|
|
/* Copyright (c) 1992-2001, Microsoft Corporation, all rights reserved
** ** pcache.c ** Remote Access Phonebook - Win9x Password cache (PWL) decrypter ** Main routines ** ** Portions of this code have been ported from: ** Win9x\proj\net\user\src\WNET\PCACHE ** ** Whistler bug: 208318 Win9x Upg: Username and Password for DUN connectoid not ** migrated from Win9x to Whistler ** ** 06/24/92 gregj ** 03/06/01 Jeff Sigman */
#include "pch.h" // Pre-compiled
#include "pcache.h" // Private pcache header
#include <rc4.h> // RSA RC4 MD5 library
#include <md5.h> // RSA RC4 MD5 library
//----------------------------------------------------------------------------
// Globals
//----------------------------------------------------------------------------
CHAR g_szPWLUsername[ UNLEN + 1 ]; CHAR g_szPWLPassword[ PWLEN + 1 ]; HANDLE g_hFile = NULL; RC4_KEYSTRUCT g_ks; NEW_PLAINTEXT_HEADER g_hdrPlaintext; NEW_ENCRYPTED_HEADER g_hdrEncrypted;
//-----------------------------------------------------------------------------
// Routines
//-----------------------------------------------------------------------------
UINT HashName ( const CHAR* pbResource, WORD cbResource ) { return cbResource ? ( ( *pbResource ) % BUCKET_COUNT ) : 0; }
VOID Encrypt ( CHAR *pbSource, WORD cbSource, CHAR *pbDest ) { if ( pbDest ) { memcpy ( pbDest, pbSource, cbSource ); pbSource = pbDest; }
rc4 ( &g_ks, cbSource, pbSource );
return; }
VOID Decrypt ( CHAR *pbSource, WORD cbSource ) { Encrypt ( pbSource, cbSource, NULL );
return; }
VOID ENCRYPTER ( const CHAR* pszUsername, const CHAR* pszPassword, UINT iBucket, DWORD dwSalt ) { UCHAR md5_hash[ 16 ]; MD5_CTX ctxBucketNumber; MD5_CTX ctx;
MD5Init ( &ctxBucketNumber );
MD5Update ( &ctxBucketNumber, (UCHAR* )&iBucket, sizeof( iBucket ) );
MD5Update ( &ctxBucketNumber, (UCHAR* )pszUsername, strlen ( pszUsername ) + 1 );
MD5Update ( &ctxBucketNumber, (UCHAR* )&dwSalt, sizeof( dwSalt ) ); MD5Final ( &ctxBucketNumber );
MD5Init ( &ctx ); MD5Update ( &ctx, (UCHAR* )pszPassword, strlen ( pszPassword ) + 1 );
MD5Update ( &ctx, (UCHAR* )ctxBucketNumber.digest, sizeof( ctxBucketNumber.digest ) );
MD5Final ( &ctx );
memcpy ( md5_hash, ctx.digest, sizeof( md5_hash ) ); memset ( (CHAR * )&ctx, '\0', sizeof( ctx )); memset ( (CHAR * )&ctxBucketNumber, '\0', sizeof( ctxBucketNumber ) );
rc4_key ( &g_ks, sizeof( md5_hash ), (UCHAR * )&md5_hash ); }
DWORD ReadData ( WORD ibSeek, PVOID pbBuffer, WORD cbBuffer ) { DWORD dwErr = ERROR_SUCCESS; DWORD cbRead = 0;
if ( ( SetFilePointer ( g_hFile, ibSeek, NULL, FILE_BEGIN ) == 0xffffffff ) || ( !ReadFile ( g_hFile, pbBuffer, cbBuffer, &cbRead, NULL ) ) ) { return GetLastError ( ); }
return ( cbRead < cbBuffer ) ? IERR_CacheCorrupt : ERROR_SUCCESS; }
VOID AssembleFindCacheName ( CHAR* pszWindir, CHAR* pszResult ) { CHAR szFind[ 6 ]; CHAR* Current = szFind; DWORD i;
strncpy ( pszResult, pszWindir, MAX_PATH - strlen(pszResult) ); strncat ( pszResult, S_PWLDIR, MAX_PATH - strlen(pszResult) ); strncat ( pszResult, "\\", MAX_PATH - strlen(pszResult) );
for ( i = 0; (i < 5) && (i < strlen(g_szPWLUsername)); i++ ) { *(Current++) = g_szPWLUsername[ i ]; *Current = '\0'; }
if ( Current != szFind ) { strncat ( pszResult, szFind, MAX_PATH - strlen(pszResult) ); }
strncat ( pszResult, S_SRCHPWL, MAX_PATH - strlen(pszResult) ); }
DWORD OpenCacheFile ( VOID ) { CHAR szFind[ MAX_PATH + 1 ]; CHAR szWindir[ MAX_PATH + 1 ]; CHAR szFilename[ MAX_PATH + 1 ]; DWORD dwErr; HANDLE hFile;
do { if ( !GetWindowsDirectoryA (szWindir, sizeof(szWindir) ) ) { dwErr = ERROR_FILE_NOT_FOUND; break; }
AssembleFindCacheName ( szWindir, szFind );
DEBUGMSGA ((S_DBG_RAS, "AssembleFindCacheName: %s", szFind));
dwErr = FindNewestFile ( szFind ); BREAK_ON_DWERR( dwErr );
strcpy ( szFilename, szWindir ); strcat ( szFilename, S_PWLDIR ); strcat ( szFilename, szFind );
DEBUGMSGA ((S_DBG_RAS, "FindNewestFile: %s", szFind));
hFile = CreateFileA ( szFilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL ); if ( hFile == INVALID_HANDLE_VALUE ) { dwErr = GetLastError ( ); break; }
DEBUGMSGA ((S_DBG_RAS, "CreateFileA: %s", szFilename));
g_hFile = hFile;
} while ( FALSE );
return dwErr; }
DWORD ReadAndDecrypt ( WORD ibSeek, PVOID pbBuffer, WORD cbBuffer ) { DWORD dwErr = ReadData ( ibSeek, pbBuffer, cbBuffer );
if ( dwErr ) { return dwErr; }
Decrypt ( (CHAR* )pbBuffer, cbBuffer );
return ERROR_SUCCESS; }
INT CompareCacheNames ( const CHAR* pbRes1, WORD cbRes1, const CHAR* pbRes2, WORD cbRes2 ) { INT nRet = memcmp ( pbRes1, pbRes2, min ( cbRes1, cbRes2 ) );
// DEBUGMSGA ((S_DBG_RAS, "CompareCacheNames"));
// DEBUGMSGA ((S_DBG_RAS, "1 - %s", pbRes1));
// DEBUGMSGA ((S_DBG_RAS, "2 - %s", pbRes2));
if (nRet != 0) { return nRet; }
return ( cbRes1 < cbRes2 ) ? -1 : ( ( cbRes1 == cbRes2 ) ? 0 : 1 ); }
DWORD LoadEncryptedHeader ( VOID ) { const UINT cbFirst = FIELDOFFSET ( NEW_ENCRYPTED_HEADER, aibBuckets ); const UINT IBUCKET_HEADER = 0xffffffff; DWORD dwErr = ERROR_SUCCESS;
do { ENCRYPTER ( g_szPWLUsername, g_szPWLPassword, IBUCKET_HEADER, g_hdrPlaintext.adwBucketSalts[ BUCKET_COUNT ] );
dwErr = ReadAndDecrypt ( (WORD )g_hdrPlaintext.cbHeader, &g_hdrEncrypted, (WORD )cbFirst ); BREAK_ON_DWERR( dwErr );
// All aibBuckets except the first and last are stored in the file
//
dwErr = ReadAndDecrypt ( (WORD )g_hdrPlaintext.cbHeader + cbFirst, (LPSTR )( &g_hdrEncrypted.aibBuckets[ 1 ] ), sizeof( g_hdrEncrypted.aibBuckets ) - ( sizeof( g_hdrEncrypted.aibBuckets[ 0 ] ) * 2) ); BREAK_ON_DWERR( dwErr );
// Generate the first and last aibBuckets values on the fly
//
g_hdrEncrypted.aibBuckets[ 0 ] = (USHORT )( g_hdrPlaintext.cbHeader + sizeof( NEW_ENCRYPTED_HEADER ) - sizeof( g_hdrEncrypted.aibBuckets[ 0 ] ) * 2 );
g_hdrEncrypted.aibBuckets[ BUCKET_COUNT ] = (USHORT )GetFileSize ( g_hFile, NULL );
} while ( FALSE );
return dwErr; }
DWORD LoadPlaintextHeader ( VOID ) { DWORD dwErr = ReadData ( 0, &g_hdrPlaintext, sizeof( g_hdrPlaintext ) );
if ( dwErr ) { return dwErr; }
if ( g_hdrPlaintext.ulSig != NEW_PLAINTEXT_SIGNATURE ) { return ERROR_SUCCESS; // no key blobs, for sure
}
// If there are any key blobs, read them all in a chunk (the remainder of
// the header) Otherwise we've already got the whole thing
//
if ( g_hdrPlaintext.cbHeader > sizeof( g_hdrPlaintext ) ) { return ReadData ( sizeof( g_hdrPlaintext ), ((CHAR* )&g_hdrPlaintext) + sizeof( g_hdrPlaintext ), ((WORD )g_hdrPlaintext.cbHeader) - sizeof( g_hdrPlaintext ) ); } else { return ERROR_SUCCESS; } }
DWORD LookupEntry ( const CHAR* pbResource, WORD cbResource, UCHAR nType, PASSWORD_CACHE_ENTRY** ppce ) { UINT iBucket = HashName ( pbResource, cbResource ); WORD ibEntry = g_hdrEncrypted.aibBuckets[ iBucket ]; // offs of 1st entry
WORD cbEntry; DWORD dwErr; PASSWORD_CACHE_ENTRY* pce = NULL;
ENCRYPTER ( g_szPWLUsername, g_szPWLPassword, iBucket, g_hdrPlaintext.adwBucketSalts[ iBucket ] );
dwErr = ReadAndDecrypt ( ibEntry, &cbEntry, sizeof( cbEntry ) ); if ( dwErr ) { return dwErr; }
ibEntry += sizeof( cbEntry );
if ( !cbEntry ) { return IERR_CacheEntryNotFound; }
pce = ( PASSWORD_CACHE_ENTRY* ) LocalAlloc ( LMEM_FIXED, MAX_ENTRY_SIZE + sizeof( cbEntry ) ); if ( pce == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; }
while ( !( cbEntry & PCE_END_MARKER ) ) {
if ( cbEntry > MAX_ENTRY_SIZE ) { dwErr = IERR_CacheCorrupt; break; }
dwErr = ReadAndDecrypt ( ibEntry, ((CHAR* )pce) + sizeof( cbEntry ), cbEntry ); BREAK_ON_DWERR( dwErr );
pce->cbEntry = cbEntry; // we read this earlier, set it manually
// DEBUGMSGA ((S_DBG_RAS, "LookupEntry: Searching for %s", pbResource));
if (nType == pce->nType && !CompareCacheNames ( pbResource, cbResource, pce->abResource, pce->cbResource )) { DEBUGMSGA ((S_DBG_RAS, "LookupEntry: Match Found")); break; // dwErr == ERROR_SUCCESS
}
ibEntry += cbEntry; cbEntry = NEXT_PCE(pce)->cbEntry; // fetch next entry's length
}
if ( ( cbEntry & PCE_END_MARKER ) || dwErr != ERROR_SUCCESS ) { LocalFree ( pce ); pce = NULL; DEBUGMSGA ((S_DBG_RAS, "LookupEntry: Nothing Found")); return ( cbEntry & PCE_END_MARKER ) ? IERR_CacheEntryNotFound : dwErr; }
*ppce = pce;
return ERROR_SUCCESS; }
DWORD ValidateEncryptedHeader ( VOID ) { MD5_CTX ctx;
MD5Init ( &ctx ); MD5Update ( &ctx, (UCHAR* )g_szPWLUsername, strlen( g_szPWLUsername ) + 1 );
MD5Update ( &ctx, (UCHAR* )g_hdrEncrypted.abRandomPadding, sizeof( g_hdrEncrypted.abRandomPadding ) );
MD5Final ( &ctx );
if ( memcmp ( ctx.digest, g_hdrEncrypted.abAuthenticationHash, sizeof( ctx.digest ) ) ) { return IERR_IncorrectUsername; }
return ERROR_SUCCESS; }
DWORD FindPWLResource ( const CHAR* pbResource, WORD cbResource, CHAR* pbBuffer, WORD cbBuffer, UCHAR nType ) { DWORD dwErr = ERROR_SUCCESS; CACHE_ENTRY_INFO* pcei = (CACHE_ENTRY_INFO* )pbBuffer; PASSWORD_CACHE_ENTRY* pce = NULL;
do { if ( cbBuffer < sizeof( CACHE_ENTRY_INFO ) ) { dwErr = ERROR_INSUFFICIENT_BUFFER; break; }
dwErr = LookupEntry ( pbResource, cbResource, nType, &pce ); BREAK_ON_DWERR( dwErr );
pcei->cbResource = pce->cbResource; pcei->cbPassword = pce->cbPassword; pcei->iEntry = pce->iEntry; pcei->nType = pce->nType; pcei->dchResource = 0; pcei->dchPassword = 0;
cbBuffer -= sizeof( CACHE_ENTRY_INFO ); if ( pce->cbResource > cbBuffer ) { dwErr = ERROR_MORE_DATA; break; }
pcei->dchResource = sizeof( CACHE_ENTRY_INFO ); memcpy ( pbBuffer + pcei->dchResource, pce->abResource, pce->cbResource );
cbBuffer -= pce->cbResource; if ( pce->cbPassword > cbBuffer ) { dwErr = ERROR_MORE_DATA; break; }
pcei->dchPassword = pcei->dchResource + pcei->cbResource; memcpy ( pbBuffer + pcei->dchPassword, pce->abResource + pce->cbResource, pce->cbPassword );
} while ( FALSE ); //
// Clean up
//
if ( pce ) { LocalFree ( pce ); pce = NULL; }
return dwErr; }
DWORD FindNewestFile ( IN OUT CHAR* SourceName ) { CHAR szCurFile[ MAX_PATH + 1 ]; HANDLE SourceHandle; LARGE_INTEGER SourceFileTime, NextFileTime; WIN32_FIND_DATAA SourceFileData;
SourceHandle = FindFirstFileA ( SourceName, &SourceFileData ); if ( INVALID_HANDLE_VALUE == SourceHandle ) { return ERROR_FILE_NOT_FOUND; }
SourceFileTime.LowPart = SourceFileData.ftLastWriteTime.dwLowDateTime; SourceFileTime.HighPart = SourceFileData.ftLastWriteTime.dwHighDateTime; strcpy ( szCurFile, SourceFileData.cFileName );
do { if ( !FindNextFileA (SourceHandle, &SourceFileData) ) { break; }
NextFileTime.LowPart = SourceFileData.ftLastWriteTime.dwLowDateTime; NextFileTime.HighPart = SourceFileData.ftLastWriteTime.dwHighDateTime;
if ( NextFileTime.QuadPart > SourceFileTime.QuadPart ) { SourceFileTime.LowPart = NextFileTime.LowPart; SourceFileTime.HighPart = NextFileTime.HighPart; strcpy ( szCurFile, SourceFileData.cFileName ); }
} while ( TRUE );
strcpy ( SourceName, "\\" ); strcat ( SourceName, szCurFile ); //
// Clean up
//
FindClose ( SourceHandle );
return ERROR_SUCCESS; }
VOID DeleteAllPwls ( VOID ) { CHAR szWindir[ MAX_PATH + 1 ]; PCSTR pszPath = NULL;
DEBUGMSGA ((S_DBG_RAS, "DeleteAllPwls"));
do { //
// Whistler bug: 427175 427176 PREFIX
//
if ( !GetWindowsDirectoryA ( szWindir, MAX_PATH ) ) {break;} DEBUGMSGA ((S_DBG_RAS, "GetWindowsDirectoryA %s", szWindir ));
pszPath = JoinPathsA (szWindir, S_PWLDIR); if (!pszPath) {break;}
if (DeleteDirectoryContentsA (pszPath)) { if (RemoveDirectoryA (pszPath)) { DEBUGMSGA ((S_DBG_RAS, "DeleteAllPwls: Success!")); } ELSE_DEBUGMSGA ((S_DBG_RAS, "Could not delete the tree %s.", pszPath)); } ELSE_DEBUGMSGA ((S_DBG_RAS, "Could not delete the contents of %s.", pszPath));
} while ( FALSE ); //
// Clean up
//
if (pszPath) { FreePathStringA (pszPath); }
return; }
//
// Whistler bug: 417745 INTL:Win9x Upg: DBCS chars cause User,Domain,
// Passwrds to not be migrated for DUN
//
BOOL StrCpyAFromWUsingAnsiEncoding( LPSTR pszDst, LPCWSTR pszSrc, DWORD dwDstChars ) { DWORD cb;
cb = WideCharToMultiByte( CP_ACP, 0, pszSrc, -1, pszDst, dwDstChars, NULL, NULL );
if (cb == 0) { DEBUGMSGA ((S_DBG_RAS, "StrCpyAFromWUsingAnsiEncoding fail")); return TRUE; }
// Success
return FALSE; }
//
// Whistler bug: 417745 INTL:Win9x Upg: DBCS chars cause User,Domain,
// Passwrds to not be migrated for DUN
//
BOOL StrCpyWFromAUsingAnsiEncoding( WCHAR* pszDst, LPCSTR pszSrc, DWORD dwDstChars ) { DWORD cb;
*pszDst = L'\0'; cb = MultiByteToWideChar( CP_ACP, 0, pszSrc, -1, pszDst, dwDstChars ); if (cb == 0) { DEBUGMSGA ((S_DBG_RAS, "StrCpyWFromAUsingAnsiEncoding fail")); return TRUE; }
// Success
return FALSE; }
VOID CopyAndTruncate ( LPSTR lpszDest, LPCSTR lpszSrc, UINT cbDest, BOOL flag ) { strncpy ( lpszDest, lpszSrc, cbDest - 1 ); //
// strncpyf() won't null-terminate if src > dest
//
lpszDest[ cbDest - 1 ] = '\0';
if ( flag ) { CharUpperBuffA ( lpszDest, cbDest - 1 ); CharToOemA ( lpszDest, lpszDest ); } }
DWORD OpenPWL ( CHAR* Username, CHAR* Password, BOOL flag ) { DWORD dwErr = ERROR_SUCCESS;
do { CopyAndTruncate ( g_szPWLUsername, Username, sizeof( g_szPWLUsername ), flag );
CopyAndTruncate ( g_szPWLPassword, Password, sizeof( g_szPWLPassword ), flag );
ZeroMemory ( &g_hdrPlaintext, sizeof(g_hdrPlaintext) ); ZeroMemory ( &g_hdrEncrypted, sizeof(g_hdrEncrypted) );
dwErr = OpenCacheFile ( ); if ( dwErr ) { DEBUGMSGA ((S_DBG_RAS, "OpenCacheFile fail")); break; }
dwErr = LoadPlaintextHeader ( ); if ( dwErr ) { DEBUGMSGA ((S_DBG_RAS, "LoadPlaintextHeader fail")); break; }
if ( g_hdrPlaintext.ulSig == PLAINTEXT_SIGNATURE ) { DEBUGMSGA ((S_DBG_RAS, "PLAINTEXT_SIGNATURE fail")); dwErr = IERR_BadSig; break; }
if ( g_hdrPlaintext.ulSig != NEW_PLAINTEXT_SIGNATURE ) { DEBUGMSGA ((S_DBG_RAS, "NEW_PLAINTEXT_SIGNATURE fail")); dwErr = IERR_BadSig; break; }
dwErr = LoadEncryptedHeader ( ); if ( dwErr ) { DEBUGMSGA ((S_DBG_RAS, "LoadEncryptedHeader fail")); break; }
dwErr = ValidateEncryptedHeader ( ); if ( dwErr ) { DEBUGMSGA ((S_DBG_RAS, "ValidateEncryptedHeader fail")); break; }
} while ( FALSE );
return dwErr; }
DWORD FindPWLString ( IN CHAR* EntryName, IN CHAR* ConnUser, IN OUT CHAR* Output ) { CHAR resource[ MAX_PATH * 2 ]; DWORD dwErr = ERROR_SUCCESS; DWORD cbCopied = 0; LPBYTE pcei = NULL;
do { // Allocate a buffer for the cache entry info
//
if ( ( pcei = (LPBYTE )LocalAlloc ( LMEM_FIXED, sizeof( CACHE_ENTRY_INFO ) + ( RAS_MaxPortName + 1 ) + ( MAX_PATH + 1 ) ) ) == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY; break; } //
// Whistler bug: 417745 INTL:Win9x Upg: DBCS chars cause User,Domain,
// Passwrds to not be migrated for DUN
//
_snprintf(resource, sizeof(resource) - 1, S_RESOURCEMASK2, EntryName, ConnUser);
DEBUGMSGA ((S_DBG_RAS, "FindPWLString: %s", resource));
dwErr = FindPWLResource ( resource, (WORD )strlen( resource ), pcei, sizeof( CACHE_ENTRY_INFO ) + ( RAS_MaxPortName + 1 ) + ( MAX_PATH + 1 ), PCE_MISC ); if ( dwErr ) { dwErr = ERROR_INVALID_PASSWORD; break; }
cbCopied = min( MAX_PATH,((CACHE_ENTRY_INFO* )pcei)->cbPassword );
// Copy a non null-terminated string for password and terminate it with
// a null character
//
if ( !cbCopied ) { dwErr = ERROR_INVALID_PASSWORD; break; }
memcpy ( Output, pcei+(((CACHE_ENTRY_INFO*)pcei)->dchPassword), cbCopied );
Output[ cbCopied ] = '\0';
} while ( FALSE );
// Clean up
//
if ( pcei ) { ZeroMemory ( pcei, sizeof( CACHE_ENTRY_INFO ) + ( RAS_MaxPortName + 1 ) + ( MAX_PATH + 1 ) ); LocalFree ( pcei ); pcei = NULL; }
return dwErr; }
BOOL MigrateEntryCreds ( IN OUT PRAS_DIALPARAMS prdp, IN PCTSTR pszEntryName, IN PCTSTR pszUserName, IN PDWORD pdwFlag ) { CHAR szEntryName[RAS_MaxPortName + 1]; CHAR szUserName[UNLEN + 1]; CHAR szConnUser[UNLEN + 1]; CHAR szPassword[MAX_PATH * 2];
do { ZeroMemory ( szEntryName, sizeof(szEntryName) ); ZeroMemory ( szUserName, sizeof(szUserName) ); ZeroMemory ( szConnUser, sizeof(szConnUser) ); ZeroMemory ( szPassword, sizeof(szPassword) ); //
// Whistler bug: 417745 INTL:Win9x Upg: DBCS chars cause User,Domain,
// Passwrds to not be migrated for DUN
//
if ( StrCpyAFromWUsingAnsiEncoding ( szEntryName, pszEntryName, sizeof (szEntryName) ) || StrCpyAFromWUsingAnsiEncoding ( szUserName, pszUserName, sizeof (szUserName) ) || StrCpyAFromWUsingAnsiEncoding ( szConnUser, prdp->DP_UserName, sizeof (szConnUser) ) ) { DEBUGMSGA ((S_DBG_RAS, "MigrateEntryCreds: Init Conversion Fail" )); break; }
if (OpenPWL ( szUserName, "", TRUE )) { DEBUGMSGA ((S_DBG_RAS, "MigrateEntryCreds: OpenPWL fail")); break; }
if (FindPWLString ( szEntryName, szConnUser, szPassword )) { DEBUGMSGA ((S_DBG_RAS, "MigrateEntryCreds: FindPWLString fail")); break; }
if (StrCpyWFromAUsingAnsiEncoding (prdp->DP_Password, szPassword, PWLEN )) { DEBUGMSGA ((S_DBG_RAS, "MigrateEntryCreds: Password Conversion Fail" )); break; }
DEBUGMSGA ((S_DBG_RAS, "MigrateEntryCreds success")); *pdwFlag |= DLPARAMS_MASK_PASSWORD;
} while ( FALSE ); //
// Clean up
//
ZeroMemory( szPassword, sizeof( szPassword ) );
if ( g_hFile ) { CloseHandle ( g_hFile ); g_hFile = NULL; }
if (*pdwFlag) { // Success
*pdwFlag |= DLPARAMS_MASK_OLDSTYLE; return FALSE; } else { return TRUE; } }
|