1599 lines
40 KiB
C
1599 lines
40 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
httpauth.c
|
|
|
|
Abstract:
|
|
|
|
Handles authentication sequence ( Basic & SSPI )
|
|
|
|
History:
|
|
|
|
Created 15-Feb-1996
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
/************************************************************
|
|
* Include Headers
|
|
************************************************************/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
|
|
#include <windows.h>
|
|
#include <rpc.h>
|
|
#include <winsock2.h>
|
|
#include <lm.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <tchar.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <ntsam.h>
|
|
#include <ntlsa.h>
|
|
#include <ntmsv1_0.h>
|
|
#include <crypt.h>
|
|
#include <logonmsv.h>
|
|
|
|
#define SECURITY_WIN32
|
|
#include <sspi.h>
|
|
#include <issperr.h>
|
|
#include <md5.h>
|
|
|
|
|
|
// declaration for this module
|
|
|
|
#include "httpauth.h"
|
|
|
|
#define SEC_SUCCESS(Status) ((Status) >= 0)
|
|
#define SIZE_MD5_DIGEST 32
|
|
|
|
// Target name for the security package
|
|
|
|
#define TOKEN_SOURCE_NAME "InetSvcs"
|
|
|
|
// header fields for Digest authentication
|
|
|
|
enum MD5_AUTH_NAME
|
|
{
|
|
MD5_AUTH_USERNAME,
|
|
MD5_AUTH_URI,
|
|
MD5_AUTH_REALM,
|
|
MD5_AUTH_NONCE,
|
|
MD5_AUTH_RESPONSE,
|
|
MD5_AUTH_ALGORITHM,
|
|
MD5_AUTH_DIGEST,
|
|
MD5_AUTH_OPAQUE,
|
|
MD5_AUTH_LAST,
|
|
};
|
|
|
|
//
|
|
// value names used by MD5 authentication.
|
|
// must be in sync with MD5_AUTH_NAME
|
|
//
|
|
|
|
PSTR MD5_AUTH_NAMES[] = {
|
|
"username",
|
|
"uri",
|
|
"realm",
|
|
"nonce",
|
|
"response",
|
|
"algorithm",
|
|
"digest",
|
|
"opaque"
|
|
} ;
|
|
|
|
|
|
// general purpose dynamic buffer structure
|
|
|
|
typedef struct _BUFFER {
|
|
PBYTE pBuf;
|
|
DWORD cLen;
|
|
} BUFFER ;
|
|
|
|
|
|
// structure storing the state of the authentication sequence
|
|
|
|
typedef struct _AUTH_SEQ {
|
|
BOOL _fNewConversation;
|
|
CredHandle _hcred;
|
|
BOOL _fHaveCredHandle;
|
|
DWORD _cbMaxToken;
|
|
BOOL _fHaveCtxtHandle;
|
|
struct _SecHandle _hctxt;
|
|
BOOL _fUUEncodeData;
|
|
} AUTH_SEQ;
|
|
|
|
// entry points in the security DLL
|
|
|
|
typedef struct _SEC_FUNC {
|
|
FREE_CREDENTIALS_HANDLE_FN pFreeCredentialsHandle;
|
|
ACQUIRE_CREDENTIALS_HANDLE_FN pAcquireCredentialsHandle;
|
|
QUERY_SECURITY_PACKAGE_INFO_FN pQuerySecurityPackageInfo; // A
|
|
FREE_CONTEXT_BUFFER_FN pFreeContextBuffer;
|
|
INITIALIZE_SECURITY_CONTEXT_FN pInitializeSecurityContext; // A
|
|
COMPLETE_AUTH_TOKEN_FN pCompleteAuthToken;
|
|
ENUMERATE_SECURITY_PACKAGES_FN pEnumerateSecurityPackages; // A
|
|
} SEC_FUNC;
|
|
|
|
// local functions
|
|
|
|
BOOL CrackUserAndDomain(
|
|
CHAR * pszDomainAndUser,
|
|
CHAR * * ppszUser,
|
|
CHAR * * ppszDomain
|
|
);
|
|
|
|
BOOL AuthConverse(
|
|
AUTH_SEQ *pAS,
|
|
VOID * pBuffIn,
|
|
DWORD cbBuffIn,
|
|
BUFFER * pbuffOut,
|
|
DWORD * pcbBuffOut,
|
|
BOOL * pfNeedMoreData,
|
|
CHAR * pszPackage,
|
|
CHAR * pszUser,
|
|
CHAR * pszPassword
|
|
);
|
|
|
|
BOOL AuthInit( AUTH_SEQ *pAS );
|
|
|
|
void AuthTerminate( AUTH_SEQ *pAS );
|
|
|
|
|
|
// uuencode/decode routines declaration
|
|
// used to code the authentication blob
|
|
|
|
BOOL uudecode(char * bufcoded,
|
|
BUFFER * pbuffdecoded,
|
|
DWORD * pcbDecoded );
|
|
BOOL uuencode( BYTE * bufin,
|
|
DWORD nbytes,
|
|
BUFFER * pbuffEncoded );
|
|
|
|
|
|
/************************************************************
|
|
* Globals for this module
|
|
************************************************************/
|
|
|
|
static BOOL g_fAuth = FALSE;
|
|
static BOOL g_fBasic = FALSE;
|
|
static AUTH_SEQ g_Auth;
|
|
static HINSTANCE g_hSecLib = NULL;
|
|
static SEC_FUNC sfProcs;
|
|
|
|
|
|
/************************************************************
|
|
* Helper functions
|
|
************************************************************/
|
|
|
|
VOID
|
|
ToHex(
|
|
LPBYTE pSrc,
|
|
UINT cSrc,
|
|
LPSTR pDst
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert binary data to ASCII hex representation
|
|
|
|
Arguments:
|
|
|
|
pSrc - binary data to convert
|
|
cSrc - length of binary data
|
|
pDst - buffer receiving ASCII representation of pSrc
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
UINT x;
|
|
UINT y;
|
|
|
|
#define TOHEX(a) ((a)>=10 ? 'a'+(a)-10 : '0'+(a))
|
|
|
|
for ( x = 0, y = 0 ; x < cSrc ; ++x )
|
|
{
|
|
UINT v;
|
|
v = pSrc[x]>>4;
|
|
pDst[y++] = TOHEX( v );
|
|
v = pSrc[x]&0x0f;
|
|
pDst[y++] = TOHEX( v );
|
|
}
|
|
pDst[y] = '\0';
|
|
}
|
|
|
|
|
|
LPSTR
|
|
ParseSkipWhite(
|
|
LPSTR p
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Skip white spaces and ','
|
|
|
|
Arguments:
|
|
|
|
p - ptr to string
|
|
|
|
Return Value:
|
|
|
|
updated ptr after skiping white space
|
|
|
|
--*/
|
|
{
|
|
while ( isspace(*p) || *p == ',' )
|
|
{
|
|
++p;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
BOOL ParseForName(
|
|
PSTR pszStr,
|
|
PSTR *pNameTable,
|
|
UINT cNameTable,
|
|
PSTR *pValueTable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse list of name=value pairs for known names
|
|
|
|
Arguments:
|
|
|
|
pszStr - line to parse ( '\0' delimited )
|
|
pNameTable - table of known names
|
|
cNameTable - number of known names
|
|
pValueTable - updated with ptr to parsed value for corresponding name
|
|
|
|
Return Value:
|
|
|
|
TRUE if success, FALSE if error
|
|
|
|
--*/
|
|
{
|
|
BOOL fSt = TRUE;
|
|
PSTR pszBeginName;
|
|
PSTR pszEndName;
|
|
PSTR pszBeginVal;
|
|
PSTR pszEndVal;
|
|
UINT iN;
|
|
int ch;
|
|
|
|
|
|
for ( iN = 0 ; iN < cNameTable ; ++iN )
|
|
{
|
|
pValueTable[iN] = NULL;
|
|
}
|
|
|
|
for ( ; *pszStr && fSt ; )
|
|
{
|
|
pszStr = ParseSkipWhite( pszStr );
|
|
|
|
pszBeginName = pszStr;
|
|
|
|
for ( pszEndName = pszStr ; (ch=*pszEndName) && ch != '=' && ch != ' ' ; ++pszEndName )
|
|
{
|
|
}
|
|
|
|
if ( *pszEndName )
|
|
{
|
|
*pszEndName = '\0';
|
|
|
|
for ( pszBeginVal = ++pszEndName ; (ch=*pszBeginVal) && ch != '"' ; ++pszBeginVal )
|
|
{
|
|
}
|
|
if ( *pszBeginVal == '"' )
|
|
{
|
|
++pszBeginVal;
|
|
for ( pszEndVal = pszBeginVal ; (ch=*pszEndVal) ; ++pszEndVal )
|
|
{
|
|
if ( ch == '"' )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if ( *pszEndVal == '"' )
|
|
{
|
|
// find name in table
|
|
for ( iN = 0 ; iN < cNameTable ; ++iN )
|
|
{
|
|
if ( !_stricmp( pNameTable[iN], pszBeginName ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if ( iN < cNameTable )
|
|
{
|
|
pValueTable[iN] = pszBeginVal;
|
|
*pszEndVal = '\0';
|
|
}
|
|
pszStr = ++pszEndVal;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
fSt = FALSE;
|
|
}
|
|
|
|
return fSt;
|
|
}
|
|
|
|
|
|
BOOL BufferInit( BUFFER *pB )
|
|
{
|
|
pB->pBuf = NULL;
|
|
pB->cLen = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void BufferTerminate( BUFFER *pB )
|
|
{
|
|
if ( pB->pBuf != NULL )
|
|
{
|
|
free( pB->pBuf );
|
|
pB->pBuf = NULL;
|
|
pB->cLen = 0;
|
|
}
|
|
}
|
|
|
|
|
|
PBYTE BufferQueryPtr( BUFFER * pB )
|
|
{
|
|
return pB->pBuf;
|
|
}
|
|
|
|
|
|
BOOL BufferResize( BUFFER *pB, DWORD cNewL )
|
|
{
|
|
PBYTE pN;
|
|
if ( cNewL > pB->cLen )
|
|
{
|
|
pN = malloc( cNewL );
|
|
if ( pB->pBuf )
|
|
{
|
|
memcpy( pN, pB->pBuf, pB->cLen );
|
|
free( pB->pBuf );
|
|
}
|
|
pB->pBuf = pN;
|
|
pB->cLen = cNewL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/************************************************************
|
|
* Authentication functions
|
|
************************************************************/
|
|
|
|
|
|
BOOL
|
|
InitAuthorizationHeader(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the authentication package
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE is successful; otherwise FALSE is returned.
|
|
|
|
--*/
|
|
{
|
|
OSVERSIONINFO VerInfo;
|
|
UCHAR lpszDLL[MAX_PATH];
|
|
|
|
//
|
|
// Find out which security DLL to use, depending on
|
|
// whether we are on NT or Win95
|
|
//
|
|
VerInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
|
|
if (!GetVersionEx (&VerInfo)) // If this fails, something has gone wrong
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
|
{
|
|
strcpy (lpszDLL, "security.dll" );
|
|
}
|
|
else if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
|
|
{
|
|
strcpy (lpszDLL, "secur32.dll" );
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Load Security DLL
|
|
//
|
|
|
|
g_hSecLib = LoadLibrary (lpszDLL);
|
|
if (g_hSecLib == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Get all entry points we care about
|
|
|
|
sfProcs.pFreeCredentialsHandle
|
|
= (FREE_CREDENTIALS_HANDLE_FN) GetProcAddress(
|
|
g_hSecLib,
|
|
"FreeCredentialsHandle" );
|
|
|
|
sfProcs.pQuerySecurityPackageInfo
|
|
= (QUERY_SECURITY_PACKAGE_INFO_FN) GetProcAddress(
|
|
g_hSecLib,
|
|
"QuerySecurityPackageInfoA" );
|
|
|
|
sfProcs.pAcquireCredentialsHandle
|
|
= (ACQUIRE_CREDENTIALS_HANDLE_FN) GetProcAddress(
|
|
g_hSecLib,
|
|
"AcquireCredentialsHandleA" );
|
|
|
|
sfProcs.pFreeContextBuffer
|
|
= (FREE_CONTEXT_BUFFER_FN) GetProcAddress(
|
|
g_hSecLib,
|
|
"FreeContextBuffer" );
|
|
|
|
sfProcs.pInitializeSecurityContext
|
|
= (INITIALIZE_SECURITY_CONTEXT_FN) GetProcAddress(
|
|
g_hSecLib,
|
|
"InitializeSecurityContextA" );
|
|
|
|
sfProcs.pCompleteAuthToken
|
|
= (COMPLETE_AUTH_TOKEN_FN) GetProcAddress(
|
|
g_hSecLib,
|
|
"CompleteAuthToken" );
|
|
|
|
sfProcs.pEnumerateSecurityPackages
|
|
= (ENUMERATE_SECURITY_PACKAGES_FN) GetProcAddress(
|
|
g_hSecLib,
|
|
"EnumerateSecurityPackagesA" );
|
|
|
|
if ( sfProcs.pFreeCredentialsHandle == NULL
|
|
|| sfProcs.pQuerySecurityPackageInfo == NULL
|
|
|| sfProcs.pAcquireCredentialsHandle == NULL
|
|
|| sfProcs.pFreeContextBuffer == NULL
|
|
|| sfProcs.pInitializeSecurityContext == NULL
|
|
|| sfProcs.pEnumerateSecurityPackages == NULL )
|
|
{
|
|
FreeLibrary( g_hSecLib );
|
|
g_hSecLib = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
g_fAuth = g_fBasic = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
TerminateAuthorizationHeader(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminate the authentication package
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if ( g_fAuth )
|
|
{
|
|
AuthTerminate( &g_Auth );
|
|
g_fAuth = FALSE;
|
|
}
|
|
|
|
g_fBasic = FALSE;
|
|
|
|
if ( g_hSecLib != NULL )
|
|
{
|
|
FreeLibrary( g_hSecLib );
|
|
g_hSecLib = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsInAuthorizationSequence(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Indicates if in authentication sequence
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE is successful; otherwise FALSE is returned.
|
|
|
|
--*/
|
|
{
|
|
return g_fAuth || g_fBasic;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ValidateAuthenticationMethods(
|
|
PSTR pszMet,
|
|
PSTR pszPreferedMet
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Filter the supplied authentication list for supported
|
|
methods ( Basic and all local security packages )
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if at least one authentication method supported,
|
|
otherwise FALSE is returned.
|
|
|
|
--*/
|
|
{
|
|
PSecPkgInfo pSec;
|
|
ULONG cSec, iS;
|
|
BOOL fValid;
|
|
PSTR p,t;
|
|
SECURITY_STATUS ss;
|
|
|
|
|
|
// access local security packages list
|
|
|
|
if ( (ss = sfProcs.pEnumerateSecurityPackages( &cSec, &pSec ))
|
|
== STATUS_SUCCESS )
|
|
{
|
|
for ( t = p = pszMet ; *p ; )
|
|
{
|
|
// Valid methods are "Basic", "Digest", "NT-Digest"
|
|
// and all SSPI based security packages
|
|
|
|
if ( !_stricmp( p , "Basic" ) )
|
|
{
|
|
fValid = TRUE;
|
|
}
|
|
else if ( !_stricmp( p , "Digest" ) )
|
|
{
|
|
fValid = TRUE;
|
|
}
|
|
else if ( !_stricmp( p , "NT-Digest" ) )
|
|
{
|
|
fValid = TRUE;
|
|
}
|
|
else for ( iS = 0 ; iS < cSec ; ++iS )
|
|
{
|
|
if ( !_stricmp( pSec[iS].Name, p ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( fValid )
|
|
{
|
|
if ( t != p )
|
|
memmove( t, p, strlen(p)+1 );
|
|
p += strlen( p ) + 1;
|
|
t += strlen( t ) + 1;
|
|
}
|
|
}
|
|
*t = '\0';
|
|
}
|
|
|
|
// check for prefered method
|
|
|
|
if ( pszPreferedMet != NULL )
|
|
{
|
|
PSTR pP;
|
|
|
|
for ( pP = strtok( pszPreferedMet, "," ) ;
|
|
pP != NULL ;
|
|
pP = strtok( NULL, "," ) )
|
|
{
|
|
// scan list of validated methods for the current
|
|
// prefered method
|
|
|
|
for ( p = pszMet ; *p ; )
|
|
{
|
|
if ( !_stricmp( pP, p ) )
|
|
{
|
|
memmove( pszMet, p, strlen(p) + 1 );
|
|
return TRUE;
|
|
}
|
|
p += strlen( p ) + 1;
|
|
}
|
|
}
|
|
|
|
// no method in the prefered method list is supported
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// no prefered method list supplied
|
|
|
|
return *pszMet ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AddAuthorizationHeader(
|
|
PSTR pch,
|
|
PSTR pchSchemes,
|
|
PSTR pchAuthData,
|
|
PSTR pchMethod,
|
|
PSTR pchUri,
|
|
PSTR pchUserName,
|
|
PSTR pchPassword,
|
|
BOOL *pfNeedMoreData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates an authentication header to be sent to the server.
|
|
An authentication sequence will be initiated if none is in
|
|
use and at least one of the authentication scheme specified
|
|
in pchSchemes is recognized.
|
|
Otherwise the current authentication sequence will proceed.
|
|
|
|
Arguments:
|
|
|
|
pch where to append authentication header
|
|
pchSchemes list of null-delimited authentication methods
|
|
pchAuthData incoming blob from server
|
|
pchUserName user name ( possibly prefixed with domain )
|
|
pchPassword password
|
|
pfNeedMoreData Out: TRUE if authentication sequence to continue
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE is successful; otherwise FALSE is returned.
|
|
|
|
--*/
|
|
{
|
|
CHAR achUserAndPass[256];
|
|
LPSTR aValueTable[ MD5_AUTH_LAST ];
|
|
BUFFER buff;
|
|
BOOL fSt = TRUE;
|
|
DWORD cbOut;
|
|
LPSTR pszA1;
|
|
LPSTR pszA2;
|
|
LPSTR pszH;
|
|
MD5_CTX md5;
|
|
BOOL fNtDigest = FALSE;
|
|
UNICODE_STRING UnicodePassword;
|
|
NT_OWF_PASSWORD PasswordHash;
|
|
|
|
BufferInit( &buff );
|
|
|
|
while ( *pchSchemes )
|
|
{
|
|
if ( !_stricmp( pchSchemes, "Basic" ))
|
|
{
|
|
// if already in authentication sequence, it failed.
|
|
|
|
if ( g_fBasic )
|
|
{
|
|
SetLastError( ERROR_ACCESS_DENIED );
|
|
fSt = FALSE;
|
|
break;
|
|
}
|
|
|
|
strcpy( achUserAndPass, pchUserName );
|
|
strcat( achUserAndPass, ":" );
|
|
strcat( achUserAndPass, pchPassword );
|
|
|
|
uuencode( (BYTE *) achUserAndPass,
|
|
strlen( achUserAndPass ),
|
|
&buff );
|
|
strcat( pch, "Authorization: " );
|
|
strcat( pch, "Basic " );
|
|
strcat( pch, BufferQueryPtr( &buff ));
|
|
strcat( pch, "\r\n");
|
|
g_fBasic = TRUE;
|
|
break;
|
|
}
|
|
else if ( !_stricmp( pchSchemes, "NT-Digest" ) )
|
|
{
|
|
fNtDigest = TRUE;
|
|
goto as_digest;
|
|
}
|
|
else if ( !_stricmp( pchSchemes, "Digest" ) )
|
|
{
|
|
as_digest:
|
|
if ( !_memicmp( pchUri, "http://", sizeof("http://")-1 ) )
|
|
{
|
|
pchUri += sizeof("http://")-1;
|
|
pchUri = strchr( pchUri, '/' );
|
|
if ( !pchUri )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
fSt = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !ParseForName( pchAuthData,
|
|
MD5_AUTH_NAMES,
|
|
MD5_AUTH_LAST,
|
|
aValueTable ) ||
|
|
aValueTable[MD5_AUTH_REALM] == NULL ||
|
|
aValueTable[MD5_AUTH_NONCE] == NULL )
|
|
{
|
|
SetLastError( ERROR_ACCESS_DENIED );
|
|
fSt = FALSE;
|
|
break;
|
|
}
|
|
|
|
if ( aValueTable[MD5_AUTH_REALM] != NULL &&
|
|
!_stricmp( aValueTable[MD5_AUTH_REALM], "false" ) )
|
|
{
|
|
aValueTable[MD5_AUTH_REALM] = NULL;
|
|
}
|
|
|
|
// if already in authentication sequence, it failed.
|
|
|
|
if ( g_fBasic && aValueTable[MD5_AUTH_REALM]==NULL )
|
|
{
|
|
SetLastError( ERROR_ACCESS_DENIED );
|
|
fSt = FALSE;
|
|
break;
|
|
}
|
|
|
|
if ( (pszA1 = LocalAlloc( LMEM_FIXED,
|
|
strlen(aValueTable[MD5_AUTH_REALM]) + 1
|
|
+ strlen(pchUserName) + 1
|
|
+ strlen(pchPassword)
|
|
+ 1 + SIZE_MD5_DIGEST)) == NULL )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
fSt = FALSE;
|
|
break;
|
|
}
|
|
strcpy( pszA1, pchUserName );
|
|
strcat( pszA1, ":" );
|
|
strcat( pszA1, aValueTable[MD5_AUTH_REALM] );
|
|
strcat( pszA1, ":" );
|
|
if ( fNtDigest )
|
|
{
|
|
UnicodePassword.MaximumLength = (strlen(pchPassword)+1);
|
|
UnicodePassword.Buffer = LocalAlloc( LMEM_FIXED, UnicodePassword.MaximumLength*sizeof(WCHAR));
|
|
if ( UnicodePassword.Buffer == NULL )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
fSt = FALSE;
|
|
break;
|
|
}
|
|
UnicodePassword.Length = MultiByteToWideChar( CP_ACP,
|
|
0,
|
|
pchPassword,
|
|
strlen(pchPassword),
|
|
UnicodePassword.Buffer,
|
|
UnicodePassword.MaximumLength ) * sizeof(WCHAR);
|
|
RtlCalculateNtOwfPassword(
|
|
& UnicodePassword,
|
|
& PasswordHash );
|
|
ToHex( (LPBYTE)&PasswordHash, sizeof(PasswordHash), pszA1+strlen(pszA1) );
|
|
|
|
LocalFree( UnicodePassword.Buffer );
|
|
}
|
|
else
|
|
{
|
|
strcat( pszA1, pchPassword );
|
|
}
|
|
MD5Init( &md5 );
|
|
MD5Update( &md5, (LPBYTE)pszA1, strlen(pszA1) );
|
|
MD5Final( &md5 );
|
|
ToHex( md5.digest, sizeof(md5.digest), pszA1 );
|
|
|
|
if ( (pszA2 = LocalAlloc( LMEM_FIXED,
|
|
strlen(pchMethod) + 1
|
|
+ strlen(pchUri)
|
|
+ 1 + SIZE_MD5_DIGEST)) == NULL )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
fSt = FALSE;
|
|
break;
|
|
}
|
|
strcpy( pszA2, pchMethod );
|
|
strcat( pszA2, ":" );
|
|
strcat( pszA2, pchUri );
|
|
MD5Init( &md5 );
|
|
MD5Update( &md5, (LPBYTE)pszA2, strlen(pszA2) );
|
|
MD5Final( &md5 );
|
|
ToHex( md5.digest, sizeof(md5.digest), pszA2 );
|
|
|
|
if ( (pszH = LocalAlloc( LMEM_FIXED,
|
|
strlen(pszA1) + 1
|
|
+ strlen(aValueTable[MD5_AUTH_NONCE])
|
|
+ 1 + strlen(pszA2) + 1
|
|
+ SIZE_MD5_DIGEST)) == NULL )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
fSt = FALSE;
|
|
break;
|
|
}
|
|
strcpy( pszH, pszA1 );
|
|
strcat( pszH, ":" );
|
|
strcat( pszH, aValueTable[MD5_AUTH_NONCE] );
|
|
strcat( pszH, ":" );
|
|
strcat( pszH, pszA2 );
|
|
MD5Init( &md5 );
|
|
MD5Update( &md5, (LPBYTE)pszH, strlen(pszH) );
|
|
MD5Final( &md5 );
|
|
ToHex( md5.digest, sizeof(md5.digest), pszH );
|
|
|
|
wsprintf( pch,
|
|
"Authorization: %sDigest username=\"%s\","
|
|
" realm=\"%s\","
|
|
" nonce=\"%s\","
|
|
" uri=\"%s\","
|
|
" response=\"%s\"",
|
|
fNtDigest ? "NT-" : "",
|
|
pchUserName,
|
|
aValueTable[MD5_AUTH_REALM],
|
|
aValueTable[MD5_AUTH_NONCE],
|
|
pchUri,
|
|
pszH
|
|
);
|
|
if ( aValueTable[MD5_AUTH_OPAQUE] )
|
|
{
|
|
wsprintf( pch+strlen(pch), ", opaque=\"%s\"",
|
|
aValueTable[MD5_AUTH_OPAQUE] );
|
|
}
|
|
strcat( pch, "\r\n" );
|
|
|
|
LocalFree( pszA1 );
|
|
LocalFree( pszA2 );
|
|
LocalFree( pszH );
|
|
|
|
g_fBasic = TRUE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// SSPI package ( assuming methods list have been validated )
|
|
|
|
if ( !g_fAuth )
|
|
{
|
|
if ( !AuthInit( &g_Auth ) )
|
|
{
|
|
fSt = FALSE;
|
|
goto ex;
|
|
}
|
|
}
|
|
else if ( pchAuthData == NULL || *pchAuthData == '\0' )
|
|
{
|
|
// no blob while in authentication sequence : it failed
|
|
|
|
SetLastError( ERROR_ACCESS_DENIED );
|
|
fSt = FALSE;
|
|
break;
|
|
}
|
|
|
|
if ( !AuthConverse( &g_Auth,
|
|
(void *) pchAuthData,
|
|
0,
|
|
&buff,
|
|
&cbOut,
|
|
pfNeedMoreData,
|
|
pchSchemes,
|
|
pchUserName,
|
|
pchPassword ))
|
|
{
|
|
fSt = FALSE;
|
|
goto ex;
|
|
}
|
|
|
|
strcat( pch, "Authorization: " );
|
|
strcat( pch, pchSchemes );
|
|
strcat( pch, " " );
|
|
strcat( pch, (CHAR *) BufferQueryPtr( &buff ) );
|
|
strcat( pch, "\r\n" );
|
|
break;
|
|
}
|
|
|
|
pchSchemes += strlen(pchSchemes) + 1;
|
|
}
|
|
ex:
|
|
BufferTerminate( &buff );
|
|
|
|
return fSt;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthInit(
|
|
AUTH_SEQ *pAS )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize a SSP authentication sequence
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE is successful; otherwise FALSE is returned.
|
|
|
|
--*/
|
|
{
|
|
pAS->_fNewConversation = TRUE;
|
|
pAS->_fHaveCredHandle = FALSE;
|
|
pAS->_fHaveCtxtHandle = FALSE;
|
|
pAS->_fUUEncodeData = TRUE;
|
|
|
|
g_fAuth = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
AuthTerminate(
|
|
AUTH_SEQ *pAS )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminate a SSP authentication sequence
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if ( pAS->_fHaveCredHandle )
|
|
sfProcs.pFreeCredentialsHandle( &pAS->_hcred );
|
|
|
|
pAS->_fHaveCredHandle = FALSE;
|
|
pAS->_fHaveCtxtHandle = FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthConverse(
|
|
AUTH_SEQ *pAS,
|
|
VOID * pBuffIn,
|
|
DWORD cbBuffIn,
|
|
BUFFER * pbuffOut,
|
|
DWORD * pcbBuffOut,
|
|
BOOL * pfNeedMoreData,
|
|
CHAR * pszPackage,
|
|
CHAR * pszUser,
|
|
CHAR * pszPassword
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initiates or continues a previously initiated authentication conversation
|
|
|
|
Client calls this first to get the negotiation message which
|
|
it then sends to the server. The server calls this with the
|
|
client result and sends back the result. The conversation
|
|
continues until *pfNeedMoreData is FALSE.
|
|
|
|
On the first call, pszPackage must point to the zero terminated
|
|
authentication package name to be used and pszUser and pszPassword
|
|
should point to the user name and password to authenticated with
|
|
on the client side (server side will always be NULL).
|
|
|
|
Arguments:
|
|
|
|
pBuffIn - Points to SSP message received from the
|
|
client. If UUENCODE is used, then this must point to a
|
|
zero terminated uuencoded string (except for the first call).
|
|
cbBuffIn - Number of bytes in pBuffIn or zero if pBuffIn points to a
|
|
zero terminated, uuencoded string.
|
|
pbuffOut - If *pfDone is not set to TRUE, this buffer contains the data
|
|
that should be sent to the other side. If this is zero, then no
|
|
data needs to be sent.
|
|
pcbBuffOut - Number of bytes in pbuffOut
|
|
pfNeedMoreData - Set to TRUE while this side of the conversation is
|
|
expecting more data from the remote side.
|
|
pszPackage - On the first call points to a zero terminate string indicating
|
|
the security package to use
|
|
pszUser - Specifies user or domain\user the first time the client calls
|
|
this method (client side only)
|
|
pszPassword - Specifies the password for pszUser the first time the
|
|
client calls this method (client side only)
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise (call GetLastError). Access is
|
|
denied if FALSE is returned and GetLastError is ERROR_ACCESS_DENIED.
|
|
|
|
--*/
|
|
{
|
|
SECURITY_STATUS ss;
|
|
TimeStamp Lifetime;
|
|
SecBufferDesc OutBuffDesc;
|
|
SecBuffer OutSecBuff;
|
|
SecBufferDesc InBuffDesc;
|
|
SecBuffer InSecBuff;
|
|
ULONG ContextAttributes;
|
|
BUFFER buffData;
|
|
BUFFER buff;
|
|
BOOL fSt;
|
|
BOOL fReply;
|
|
|
|
BufferInit( &buffData );
|
|
BufferInit( &buff );
|
|
|
|
//
|
|
// Decode the data if there's something to decode
|
|
//
|
|
|
|
if ( pAS->_fUUEncodeData && pBuffIn )
|
|
{
|
|
if ( !uudecode( (CHAR *) pBuffIn,
|
|
&buffData,
|
|
&cbBuffIn ))
|
|
{
|
|
fSt = FALSE;
|
|
goto ex;
|
|
}
|
|
|
|
pBuffIn = BufferQueryPtr( &buffData );
|
|
}
|
|
|
|
//
|
|
// If this is a new conversation, then we need to get the credential
|
|
// handle and find out the maximum token size
|
|
//
|
|
|
|
if ( pAS->_fNewConversation )
|
|
{
|
|
SecPkgInfo * pspkg;
|
|
SEC_WINNT_AUTH_IDENTITY AuthIdentity;
|
|
SEC_WINNT_AUTH_IDENTITY * pAuthIdentity;
|
|
CHAR * pszDomain = NULL;
|
|
CHAR szDomainAndUser[DNLEN+UNLEN+2];
|
|
|
|
|
|
//
|
|
// fill out the authentication information
|
|
//
|
|
|
|
if ( ((pszUser != NULL) ||
|
|
(pszPassword != NULL)) )
|
|
{
|
|
pAuthIdentity = &AuthIdentity;
|
|
|
|
//
|
|
// Break out the domain from the username if one was specified
|
|
//
|
|
|
|
if ( pszUser != NULL )
|
|
{
|
|
strcpy( szDomainAndUser, pszUser );
|
|
if ( !CrackUserAndDomain( szDomainAndUser,
|
|
&pszUser,
|
|
&pszDomain ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
memset( &AuthIdentity,
|
|
0,
|
|
sizeof( AuthIdentity ));
|
|
|
|
if ( pszUser != NULL )
|
|
{
|
|
AuthIdentity.User = (unsigned char *) pszUser;
|
|
AuthIdentity.UserLength = strlen( pszUser );
|
|
}
|
|
|
|
if ( pszPassword != NULL )
|
|
{
|
|
AuthIdentity.Password = (unsigned char *) pszPassword;
|
|
AuthIdentity.PasswordLength = strlen( pszPassword );
|
|
}
|
|
|
|
if ( pszDomain != NULL )
|
|
{
|
|
AuthIdentity.Domain = (unsigned char *) pszDomain;
|
|
AuthIdentity.DomainLength = strlen( pszDomain );
|
|
}
|
|
|
|
AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
|
|
}
|
|
else
|
|
{
|
|
pAuthIdentity = NULL;
|
|
}
|
|
|
|
ss = sfProcs.pAcquireCredentialsHandle( NULL, // New principal
|
|
pszPackage, // Package name
|
|
SECPKG_CRED_OUTBOUND,
|
|
NULL, // Logon ID
|
|
pAuthIdentity, // Auth Data
|
|
NULL, // Get key func
|
|
NULL, // Get key arg
|
|
&pAS->_hcred,
|
|
&Lifetime );
|
|
|
|
//
|
|
// Need to determine the max token size for this package
|
|
//
|
|
|
|
if ( ss == STATUS_SUCCESS )
|
|
{
|
|
pAS->_fHaveCredHandle = TRUE;
|
|
ss = sfProcs.pQuerySecurityPackageInfo(
|
|
(char *) pszPackage,
|
|
&pspkg );
|
|
}
|
|
|
|
if ( ss != STATUS_SUCCESS )
|
|
{
|
|
SetLastError( ss );
|
|
return FALSE;
|
|
}
|
|
|
|
pAS->_cbMaxToken = pspkg->cbMaxToken;
|
|
|
|
sfProcs.pFreeContextBuffer( pspkg );
|
|
|
|
}
|
|
|
|
//
|
|
// Prepare our output buffer. We use a temporary buffer because
|
|
// the real output buffer will most likely need to be uuencoded
|
|
//
|
|
|
|
if ( !BufferResize( &buff, pAS->_cbMaxToken ))
|
|
{
|
|
fSt = FALSE;
|
|
goto ex;
|
|
}
|
|
|
|
OutBuffDesc.ulVersion = 0;
|
|
OutBuffDesc.cBuffers = 1;
|
|
OutBuffDesc.pBuffers = &OutSecBuff;
|
|
|
|
OutSecBuff.cbBuffer = pAS->_cbMaxToken;
|
|
OutSecBuff.BufferType = SECBUFFER_TOKEN;
|
|
OutSecBuff.pvBuffer = BufferQueryPtr( &buff );
|
|
|
|
//
|
|
// Prepare our Input buffer - Note the server is expecting the client's
|
|
// negotiation packet on the first call
|
|
//
|
|
|
|
if ( pBuffIn )
|
|
{
|
|
InBuffDesc.ulVersion = 0;
|
|
InBuffDesc.cBuffers = 1;
|
|
InBuffDesc.pBuffers = &InSecBuff;
|
|
|
|
InSecBuff.cbBuffer = cbBuffIn;
|
|
InSecBuff.BufferType = SECBUFFER_TOKEN;
|
|
InSecBuff.pvBuffer = pBuffIn;
|
|
}
|
|
|
|
{
|
|
//
|
|
// will return success when its done but we still
|
|
// need to send the out buffer if there are bytes to send
|
|
//
|
|
|
|
ss = sfProcs.pInitializeSecurityContext(
|
|
&pAS->_hcred,
|
|
pAS->_fNewConversation ? NULL :
|
|
&pAS->_hctxt,
|
|
TOKEN_SOURCE_NAME,
|
|
0,
|
|
0,
|
|
SECURITY_NATIVE_DREP,
|
|
pAS->_fNewConversation ? NULL :
|
|
&InBuffDesc,
|
|
0,
|
|
&pAS->_hctxt,
|
|
&OutBuffDesc,
|
|
&ContextAttributes,
|
|
&Lifetime );
|
|
}
|
|
|
|
if ( !SEC_SUCCESS( ss ) )
|
|
{
|
|
if ( ss == SEC_E_LOGON_DENIED )
|
|
ss = ERROR_LOGON_FAILURE;
|
|
|
|
SetLastError( ss );
|
|
fSt = FALSE;
|
|
goto ex;
|
|
}
|
|
|
|
pAS->_fHaveCtxtHandle = TRUE;
|
|
|
|
//
|
|
// Now we just need to complete the token (if requested) and prepare
|
|
// it for shipping to the other side if needed
|
|
//
|
|
|
|
fReply = !!OutSecBuff.cbBuffer;
|
|
|
|
if ( (ss == SEC_I_COMPLETE_NEEDED) ||
|
|
(ss == SEC_I_COMPLETE_AND_CONTINUE) )
|
|
{
|
|
if ( sfProcs.pCompleteAuthToken != NULL )
|
|
{
|
|
ss = sfProcs.pCompleteAuthToken( &pAS->_hctxt,
|
|
&OutBuffDesc );
|
|
|
|
if ( !SEC_SUCCESS( ss ))
|
|
{
|
|
fSt = FALSE;
|
|
goto ex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if not supported
|
|
|
|
fSt = FALSE;
|
|
goto ex;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Format or copy to the output buffer if we need to reply
|
|
//
|
|
|
|
if ( fReply )
|
|
{
|
|
if ( pAS->_fUUEncodeData )
|
|
{
|
|
if ( !uuencode( (BYTE *) OutSecBuff.pvBuffer,
|
|
OutSecBuff.cbBuffer,
|
|
pbuffOut ))
|
|
{
|
|
fSt = FALSE;
|
|
goto ex;
|
|
}
|
|
|
|
*pcbBuffOut = strlen( (CHAR *) BufferQueryPtr(pbuffOut) );
|
|
}
|
|
else
|
|
{
|
|
if ( !BufferResize( pbuffOut, OutSecBuff.cbBuffer ))
|
|
{
|
|
fSt = FALSE;
|
|
goto ex;
|
|
}
|
|
|
|
memcpy( BufferQueryPtr(pbuffOut),
|
|
OutSecBuff.pvBuffer,
|
|
OutSecBuff.cbBuffer );
|
|
|
|
*pcbBuffOut = OutSecBuff.cbBuffer;
|
|
}
|
|
}
|
|
|
|
if ( pAS->_fNewConversation )
|
|
pAS->_fNewConversation = FALSE;
|
|
|
|
*pfNeedMoreData = ((ss == SEC_I_CONTINUE_NEEDED) ||
|
|
(ss == SEC_I_COMPLETE_AND_CONTINUE));
|
|
|
|
fSt = TRUE;
|
|
|
|
ex:
|
|
BufferTerminate( &buffData );
|
|
BufferTerminate( &buff );
|
|
|
|
return fSt;
|
|
}
|
|
|
|
|
|
BOOL CrackUserAndDomain(
|
|
CHAR * pszDomainAndUser,
|
|
CHAR * * ppszUser,
|
|
CHAR * * ppszDomain
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a user name potentially in the form domain\user, zero terminates
|
|
the domain name and returns pointers to the domain name and the user name
|
|
|
|
Arguments:
|
|
|
|
pszDomainAndUser - Pointer to user name or domain and user name
|
|
ppszUser - Receives pointer to user portion of name
|
|
ppszDomain - Receives pointer to domain portion of name
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
static CHAR szDefaultDomain[MAX_COMPUTERNAME_LENGTH+1];
|
|
DWORD cbN;
|
|
|
|
//
|
|
// Crack the name into domain/user components.
|
|
//
|
|
|
|
*ppszDomain = pszDomainAndUser;
|
|
*ppszUser = strpbrk( pszDomainAndUser, "/\\" );
|
|
|
|
if( *ppszUser == NULL )
|
|
{
|
|
//
|
|
// No domain name specified, just the username so we assume the
|
|
// user is on the local machine
|
|
//
|
|
|
|
if ( !*szDefaultDomain )
|
|
{
|
|
cbN = sizeof(szDefaultDomain);
|
|
GetComputerName( szDefaultDomain, &cbN );
|
|
}
|
|
|
|
*ppszDomain = szDefaultDomain;
|
|
*ppszUser = pszDomainAndUser;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Both domain & user specified, skip delimiter.
|
|
//
|
|
|
|
**ppszUser = '\0';
|
|
(*ppszUser)++;
|
|
|
|
if( ( **ppszUser == '\0' ) ||
|
|
( **ppszUser == '\\' ) ||
|
|
( **ppszUser == '/' ) )
|
|
{
|
|
//
|
|
// Name is of one of the following (invalid) forms:
|
|
//
|
|
// "domain\"
|
|
// "domain\\..."
|
|
// "domain/..."
|
|
//
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/************************************************************
|
|
* uuencode/decode functions
|
|
************************************************************/
|
|
|
|
//
|
|
// Taken from NCSA HTTP and wwwlib.
|
|
//
|
|
// NOTE: These conform to RFC1113, which is slightly different then the Unix
|
|
// uuencode and uudecode!
|
|
//
|
|
|
|
const int pr2six[256]={
|
|
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
|
|
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
|
|
52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9,
|
|
10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
|
|
28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
|
|
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
|
|
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
|
|
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
|
|
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
|
|
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
|
|
64,64,64,64,64,64,64,64,64,64,64,64,64
|
|
};
|
|
|
|
char six2pr[64] = {
|
|
'A','B','C','D','E','F','G','H','I','J','K','L','M',
|
|
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
|
|
'a','b','c','d','e','f','g','h','i','j','k','l','m',
|
|
'n','o','p','q','r','s','t','u','v','w','x','y','z',
|
|
'0','1','2','3','4','5','6','7','8','9','+','/'
|
|
};
|
|
|
|
|
|
BOOL
|
|
uudecode(
|
|
char * bufcoded,
|
|
BUFFER * pbuffdecoded,
|
|
DWORD * pcbDecoded )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
uudecode a string of data
|
|
|
|
Arguments:
|
|
|
|
bufcoded pointer to uuencoded data
|
|
pbuffdecoded pointer to output BUFFER structure
|
|
pcbDecoded number of decode bytes
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE is successful; otherwise FALSE is returned.
|
|
|
|
--*/
|
|
{
|
|
int nbytesdecoded;
|
|
char *bufin = bufcoded;
|
|
unsigned char *bufout;
|
|
int nprbytes;
|
|
|
|
/* Strip leading whitespace. */
|
|
|
|
while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++;
|
|
|
|
/* Figure out how many characters are in the input buffer.
|
|
* If this would decode into more bytes than would fit into
|
|
* the output buffer, adjust the number of input bytes downwards.
|
|
*/
|
|
bufin = bufcoded;
|
|
while(pr2six[*(bufin++)] <= 63);
|
|
nprbytes = bufin - bufcoded - 1;
|
|
nbytesdecoded = ((nprbytes+3)/4) * 3;
|
|
|
|
if ( !BufferResize( pbuffdecoded, nbytesdecoded + 4 ))
|
|
return FALSE;
|
|
|
|
if ( pcbDecoded )
|
|
*pcbDecoded = nbytesdecoded;
|
|
|
|
bufout = (unsigned char *) BufferQueryPtr(pbuffdecoded);
|
|
|
|
bufin = bufcoded;
|
|
|
|
while (nprbytes > 0) {
|
|
*(bufout++) =
|
|
(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
|
|
*(bufout++) =
|
|
(unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
|
|
*(bufout++) =
|
|
(unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
|
|
bufin += 4;
|
|
nprbytes -= 4;
|
|
}
|
|
|
|
if(nprbytes & 03) {
|
|
if(pr2six[bufin[-2]] > 63)
|
|
nbytesdecoded -= 2;
|
|
else
|
|
nbytesdecoded -= 1;
|
|
}
|
|
|
|
((CHAR *)BufferQueryPtr(pbuffdecoded))[nbytesdecoded] = '\0';
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
uuencode(
|
|
BYTE * bufin,
|
|
DWORD nbytes,
|
|
BUFFER * pbuffEncoded )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
uuencode a string of data
|
|
|
|
Arguments:
|
|
|
|
bufin pointer to data to encode
|
|
nbytes number of bytes to encode
|
|
pbuffEncoded pointer to output BUFFER structure
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE is successful; otherwise FALSE is returned.
|
|
|
|
--*/
|
|
{
|
|
unsigned char *outptr;
|
|
unsigned int i;
|
|
|
|
//
|
|
// Resize the buffer to 133% of the incoming data
|
|
//
|
|
|
|
if ( !BufferResize( pbuffEncoded, nbytes + ((nbytes + 3) / 3) + 4))
|
|
return FALSE;
|
|
|
|
outptr = (unsigned char *) BufferQueryPtr(pbuffEncoded);
|
|
|
|
for (i=0; i<nbytes; i += 3) {
|
|
*(outptr++) = six2pr[*bufin >> 2]; /* c1 */
|
|
*(outptr++) = six2pr[((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)]; /*c2*/
|
|
*(outptr++) = six2pr[((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)];/*c3*/
|
|
*(outptr++) = six2pr[bufin[2] & 077]; /* c4 */
|
|
|
|
bufin += 3;
|
|
}
|
|
|
|
/* If nbytes was not a multiple of 3, then we have encoded too
|
|
* many characters. Adjust appropriately.
|
|
*/
|
|
if(i == nbytes+1) {
|
|
/* There were only 2 bytes in that last group */
|
|
outptr[-1] = '=';
|
|
} else if(i == nbytes+2) {
|
|
/* There was only 1 byte in that last group */
|
|
outptr[-1] = '=';
|
|
outptr[-2] = '=';
|
|
}
|
|
|
|
*outptr = '\0';
|
|
|
|
return TRUE;
|
|
}
|