/*++ Copyright (c) 1997, 1998, 1999 Microsoft Corporation Module Name: keyman.cpp Abstract: This module contains routines to read and write data (key containers) from and to files. Author: 16 Mar 98 jeffspel --*/ // Don't whine about unnamed unions #pragma warning (disable: 4201) #include #include #include #include #include #include #include // for GetUserAppDataPathW #include #include #include #include #include #include #include #include #include #include #ifdef USE_HW_RNG #ifdef _M_IX86 #include // INTEL h files for on chip RNG #include "deftypes.h" //ISD typedefs and constants #include "ioctldef.h" //ISD ioctl definitions #endif // _M_IX86 #endif // USE_HW_RNG static LPBYTE l_pbStringBlock = NULL; CSP_STRINGS g_Strings = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; typedef struct _OLD_KEY_CONTAINER_LENS_ { DWORD cbSigPub; DWORD cbSigEncPriv; DWORD cbExchPub; DWORD cbExchEncPriv; } OLD_KEY_CONTAINER_LENS, *POLD_KEY_CONTAINER_LENS; #define OLD_KEY_CONTAINER_FILE_FORMAT_VER 1 #define FAST_BUF_SIZE 256 #define ContInfoAlloc(cb) ContAlloc(cb) #define ContInfoReAlloc(pb, cb) ContRealloc(pb, cb) #define ContInfoFree(pb) ContFree(pb) #define MACHINE_KEYS_DIR L"MachineKeys" // Location of the keys in the registry (minus the logon name) // Length of the full location (including the logon name) #define RSA_REG_KEY_LOC "Software\\Microsoft\\Cryptography\\UserKeys" #define RSA_REG_KEY_LOC_LEN sizeof(RSA_REG_KEY_LOC) #define RSA_MACH_REG_KEY_LOC "Software\\Microsoft\\Cryptography\\MachineKeys" #define RSA_MACH_REG_KEY_LOC_LEN sizeof(RSA_MACH_REG_KEY_LOC) #define DSS_REG_KEY_LOC "Software\\Microsoft\\Cryptography\\DSSUserKeys" #define DSS_REG_KEY_LOC_LEN sizeof(DSS_REG_KEY_LOC) #define DSS_MACH_REG_KEY_LOC "Software\\Microsoft\\Cryptography\\DSSUserKeys" #define DSS_MACH_REG_KEY_LOC_LEN sizeof(DSS_MACH_REG_KEY_LOC) #define MAX_DPAPI_RETRY_COUNT 5 // // Memory allocation support. // #ifndef ASSERT #define ASSERT _ASSERTE #endif #ifdef _X86_ #define InterlockedAccess(pl) *(pl) #define InterlockedPointerAccess(ppv) *(ppv) #else #define InterlockedAccess(pl) InterlockedExchangeAdd((pl), 0) #define InterlockedPointerAccess(ppv) InterlockedExchangePointer((ppv), *(ppv)) #endif #define CONT_HEAP_FLAGS (HEAP_ZERO_MEMORY) // Scrub sensitive data from memory extern void memnuke( volatile BYTE *pData, DWORD dwLen); LPVOID ContAlloc( ULONG cbLen) { return HeapAlloc(GetProcessHeap(), CONT_HEAP_FLAGS, cbLen); } LPVOID ContRealloc( LPVOID pvMem, ULONG cbLen) { return HeapReAlloc(GetProcessHeap(), CONT_HEAP_FLAGS, pvMem, cbLen); } void ContFree( LPVOID pvMem) { if (NULL != pvMem) HeapFree(GetProcessHeap(), CONT_HEAP_FLAGS, pvMem); } // // Wrapper for RtlEncryptMemory, which returns an NTSTATUS. The return // value is translated to a winerror code. // DWORD MyRtlEncryptMemory( IN PVOID pvMem, IN DWORD cbMem) { NTSTATUS status = RtlEncryptMemory(pvMem, cbMem, 0); return RtlNtStatusToDosError(status); } // // Wrapper for RtlDecryptMemory, which returns an NTSTATUS. The return value // is translated to a winerror code. // DWORD MyRtlDecryptMemory( IN PVOID pvMem, IN DWORD cbMem) { NTSTATUS status = RtlDecryptMemory(pvMem, cbMem, 0); return RtlNtStatusToDosError(status); } // // Return TRUE if Force High Key Protection is set on this machine, return // FALSE otherwise. // BOOL IsForceHighProtectionEnabled( IN PKEY_CONTAINER_INFO pContInfo) { return pContInfo->fForceHighKeyProtection; } // // Retrieves the Force High Key Protection setting for this machine from the // registry. // DWORD InitializeForceHighProtection( IN OUT PKEY_CONTAINER_INFO pContInfo) { HKEY hKey = 0; DWORD dwSts = ERROR_SUCCESS; DWORD cbData = 0; DWORD dwValue = 0; pContInfo->fForceHighKeyProtection = FALSE; // // Open the Cryptography key // dwSts = RegOpenKeyEx( HKEY_LOCAL_MACHINE, szKEY_CRYPTOAPI_PRIVATE_KEY_OPTIONS, 0, KEY_READ | KEY_WOW64_64KEY, &hKey); if (ERROR_FILE_NOT_FOUND == dwSts) { // Key doesn't exist. Assume feature should remain off. dwSts = ERROR_SUCCESS; goto Ret; } if (ERROR_SUCCESS != dwSts) goto Ret; // // Find out if force high key protection is on // cbData = sizeof(DWORD); dwSts = RegQueryValueEx( hKey, szFORCE_KEY_PROTECTION, 0, NULL, (PBYTE) &dwValue, &cbData); if (ERROR_SUCCESS == dwSts && dwFORCE_KEY_PROTECTION_HIGH == dwValue) pContInfo->fForceHighKeyProtection = TRUE; else if (ERROR_FILE_NOT_FOUND == dwSts) // If the value isn't present, assume Force High is turned off. dwSts = ERROR_SUCCESS; Ret: if (hKey) RegCloseKey(hKey); return dwSts; } // // Returns True is the cached private key of the indicated type // is still valid. // // Returns False if no cached key is available, or if the available // cached key is stale. // BOOL IsCachedKeyValid( IN PKEY_CONTAINER_INFO pContInfo, IN BOOL fSigKey) { DWORD *pdwPreviousTimestamp = NULL; // If the new caching behavior isn't enabled, let the // caller proceed as before. if (FALSE == pContInfo->fCachePrivateKeys) return TRUE; if (fSigKey) pdwPreviousTimestamp = &pContInfo->dwSigKeyTimestamp; else pdwPreviousTimestamp = &pContInfo->dwKeyXKeyTimestamp; if ((GetTickCount() - *pdwPreviousTimestamp) > pContInfo->cMaxKeyLifetime) { // Cached key is stale *pdwPreviousTimestamp = 0; return FALSE; } return TRUE; } // // Updates the cache counter for the key of the indicated type. This // is called immediately after the key is read from storage, to // restart the cached key lifetime "countdown." // DWORD SetCachedKeyTimestamp( IN PKEY_CONTAINER_INFO pContInfo, IN BOOL fSigKey) { if (FALSE == pContInfo->fCachePrivateKeys) return ERROR_SUCCESS; if (fSigKey) pContInfo->dwSigKeyTimestamp = GetTickCount(); else pContInfo->dwKeyXKeyTimestamp = GetTickCount(); return ERROR_SUCCESS; } // // Reads the key cache initialization parameters from the registry. // DWORD InitializeKeyCacheInfo( IN OUT PKEY_CONTAINER_INFO pContInfo) { HKEY hKey = 0; DWORD dwSts = ERROR_SUCCESS; DWORD cbData = 0; // // Open the Cryptography key // dwSts = RegOpenKeyEx( HKEY_LOCAL_MACHINE, szKEY_CRYPTOAPI_PRIVATE_KEY_OPTIONS, 0, KEY_READ | KEY_WOW64_64KEY, &hKey); if (ERROR_FILE_NOT_FOUND == dwSts) { // Key doesn't exist. Assume feature should remain off. dwSts = ERROR_SUCCESS; goto Ret; } if (ERROR_SUCCESS != dwSts) goto Ret; // // Find out if private key caching is turned on // cbData = sizeof(DWORD); dwSts = RegQueryValueEx( hKey, szKEY_CACHE_ENABLED, 0, NULL, (PBYTE) &pContInfo->fCachePrivateKeys, &cbData); if (ERROR_FILE_NOT_FOUND == dwSts) { // Reg key enabling the new behavior isn't set, so we're done. dwSts = ERROR_SUCCESS; goto Ret; } else if (ERROR_SUCCESS != dwSts || FALSE == pContInfo->fCachePrivateKeys) goto Ret; // // Find out how long to cache private keys // cbData = sizeof(DWORD); dwSts = RegQueryValueEx( hKey, szKEY_CACHE_SECONDS, 0, NULL, (PBYTE) &pContInfo->cMaxKeyLifetime, &cbData); if (ERROR_SUCCESS != dwSts) goto Ret; // Cache lifetime value stored in registry is in seconds. We'll remember // the value in milliseconds for easy comparison. pContInfo->cMaxKeyLifetime *= 1000; Ret: if (hKey) RegCloseKey(hKey); return dwSts; } /*++ OpenCallerToken: This routine returns the caller's ID token. Arguments: dwFlags supplies the flags to use when opening the token. phToken receives the token. It must be closed via CloseHandle. Return Value: A DWORD status code. Remarks: Author: Doug Barlow (dbarlow) 5/2/2000 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ TEXT("OpenCallerToken") /*static*/ DWORD OpenCallerToken( IN DWORD dwFlags, OUT HANDLE *phToken) { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD dwSts; BOOL fSts; HANDLE hToken = NULL; fSts = OpenThreadToken(GetCurrentThread(), dwFlags, TRUE, &hToken); if (!fSts) { dwSts = GetLastError(); if (ERROR_NO_TOKEN != dwSts) { dwReturn = dwSts; goto ErrorExit; } // For Jeff, fall back and get the process token fSts = OpenProcessToken(GetCurrentProcess(), dwFlags, &hToken); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } } *phToken = hToken; return ERROR_SUCCESS; ErrorExit: return dwReturn; } DWORD MyCryptProtectData( IN DATA_BLOB *pDataIn, IN LPCWSTR szDataDescr, IN OPTIONAL DATA_BLOB *pOptionalEntropy, IN PVOID pvReserved, IN OPTIONAL CRYPTPROTECT_PROMPTSTRUCT *pPromptStruct, IN DWORD dwFlags, OUT DATA_BLOB *pDataOut) // out encr blob { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD dwRetryCount = 0; DWORD dwMilliseconds = 10; DWORD dwSts; for (;;) { if (CryptProtectData(pDataIn, szDataDescr, pOptionalEntropy, pvReserved, pPromptStruct, dwFlags, pDataOut)) { break; } dwSts = GetLastError(); switch (dwSts) { case RPC_S_SERVER_TOO_BUSY: if (MAX_DPAPI_RETRY_COUNT <= dwRetryCount) { dwReturn = dwSts; goto ErrorExit; } Sleep(dwMilliseconds); dwMilliseconds *= 2; dwRetryCount++; break; case RPC_S_UNKNOWN_IF: // Make this error code more friendly. dwReturn = ERROR_SERVICE_NOT_ACTIVE; goto ErrorExit; break; default: dwReturn = dwSts; goto ErrorExit; } } dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } DWORD MyCryptUnprotectData( IN DATA_BLOB *pDataIn, // in encr blob OUT OPTIONAL LPWSTR *ppszDataDescr, // out IN OPTIONAL DATA_BLOB *pOptionalEntropy, IN PVOID pvReserved, IN OPTIONAL CRYPTPROTECT_PROMPTSTRUCT *pPromptStruct, IN DWORD dwFlags, OUT DATA_BLOB *pDataOut, OUT LPDWORD pdwReprotectFlags) { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD dwRetryCount = 0; DWORD dwMilliseconds = 10; DWORD dwSts; BOOL fSts; if (NULL != pdwReprotectFlags) { *pdwReprotectFlags = 0; dwFlags |= (CRYPTPROTECT_VERIFY_PROTECTION | CRYPTPROTECT_UI_FORBIDDEN); } for (;;) { fSts = CryptUnprotectData(pDataIn, // in encr blob ppszDataDescr, // out pOptionalEntropy, pvReserved, pPromptStruct, dwFlags, pDataOut); if (!fSts) { dwSts = GetLastError(); if ((RPC_S_SERVER_TOO_BUSY == dwSts) && (MAX_DPAPI_RETRY_COUNT > dwRetryCount)) { Sleep(dwMilliseconds); dwMilliseconds *= 2; dwRetryCount++; } else if ((ERROR_PASSWORD_RESTRICTION == dwSts) && (NULL != pdwReprotectFlags)) { *pdwReprotectFlags |= CRYPT_USER_PROTECTED; dwFlags &= ~CRYPTPROTECT_UI_FORBIDDEN; } else { dwReturn = dwSts; break; } } else { if (NULL != pdwReprotectFlags) { dwSts = GetLastError(); if (CRYPT_I_NEW_PROTECTION_REQUIRED == dwSts) *pdwReprotectFlags |= CRYPT_UPDATE_KEY; } dwReturn = ERROR_SUCCESS; break; } } return dwReturn; } void FreeEnumOldMachKeyEntries( PKEY_CONTAINER_INFO pInfo) { if (pInfo) { if (pInfo->pchEnumOldMachKeyEntries) { ContInfoFree(pInfo->pchEnumOldMachKeyEntries); pInfo->dwiOldMachKeyEntry = 0; pInfo->cMaxOldMachKeyEntry = 0; pInfo->cbOldMachKeyEntry = 0; pInfo->pchEnumOldMachKeyEntries = NULL; } } } void FreeEnumRegEntries( PKEY_CONTAINER_INFO pInfo) { if (pInfo) { if (pInfo->pchEnumRegEntries) { ContInfoFree(pInfo->pchEnumRegEntries); pInfo->dwiRegEntry = 0; pInfo->cMaxRegEntry = 0; pInfo->cbRegEntry = 0; pInfo->pchEnumRegEntries = NULL; } } } void FreeContainerInfo( PKEY_CONTAINER_INFO pInfo) { if (NULL != pInfo) { if (NULL != pInfo->pbSigPub) { ContInfoFree(pInfo->pbSigPub); pInfo->ContLens.cbSigPub = 0; pInfo->pbSigPub = NULL; } if (NULL != pInfo->pbSigEncPriv) { memnuke(pInfo->pbSigEncPriv, pInfo->ContLens.cbSigEncPriv); ContInfoFree(pInfo->pbSigEncPriv); pInfo->ContLens.cbSigEncPriv = 0; pInfo->pbSigEncPriv = NULL; } if (NULL != pInfo->pbExchPub) { ContInfoFree(pInfo->pbExchPub); pInfo->ContLens.cbExchPub = 0; pInfo->pbExchPub = NULL; } if (NULL != pInfo->pbExchEncPriv) { memnuke(pInfo->pbExchEncPriv, pInfo->ContLens.cbExchEncPriv); ContInfoFree(pInfo->pbExchEncPriv); pInfo->ContLens.cbExchEncPriv = 0; pInfo->pbExchEncPriv = NULL; } if (NULL != pInfo->pbRandom) { ContInfoFree(pInfo->pbRandom); pInfo->ContLens.cbRandom = 0; pInfo->pbRandom = NULL; } if (NULL != pInfo->pszUserName) { ContInfoFree(pInfo->pszUserName); pInfo->ContLens.cbName = 0; pInfo->pszUserName = NULL; } FreeEnumOldMachKeyEntries(pInfo); FreeEnumRegEntries(pInfo); if (NULL != pInfo->hFind) FindClose(pInfo->hFind); } } /*static*/ DWORD GetHashOfContainer( LPCSTR pszContainer, LPWSTR pszHash) { DWORD dwReturn = ERROR_INTERNAL_ERROR; MD5_CTX MD5; LPSTR pszLowerContainer = NULL; DWORD *pdw1; DWORD *pdw2; DWORD *pdw3; DWORD *pdw4; pszLowerContainer = (LPSTR)ContInfoAlloc( strlen(pszContainer) + sizeof(CHAR)); if (NULL == pszLowerContainer) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } lstrcpy(pszLowerContainer, pszContainer); _strlwr(pszLowerContainer); MD5Init(&MD5); MD5Update(&MD5, (LPBYTE)pszLowerContainer, strlen(pszLowerContainer) + sizeof(CHAR)); MD5Final(&MD5); pdw1 = (DWORD*)&MD5.digest[0]; pdw2 = (DWORD*)&MD5.digest[4]; pdw3 = (DWORD*)&MD5.digest[8]; pdw4 = (DWORD*)&MD5.digest[12]; wsprintfW(pszHash, L"%08hx%08hx%08hx%08hx", *pdw1, *pdw2, *pdw3, *pdw4); dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pszLowerContainer) ContInfoFree(pszLowerContainer); return dwReturn; } /*static*/ DWORD GetMachineGUID( LPWSTR *ppwszUuid) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HKEY hRegKey = 0; LPSTR pszUuid = NULL; LPWSTR pwszUuid = NULL; DWORD cbUuid = sizeof(UUID); DWORD cch = 0; DWORD dwSts; *ppwszUuid = NULL; // read the GUID from the Local Machine portion of the registry dwSts = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZLOCALMACHINECRYPTO, 0, KEY_READ | KEY_WOW64_64KEY, &hRegKey); if (ERROR_FILE_NOT_FOUND == dwSts) { dwReturn = ERROR_SUCCESS; goto ErrorExit; // Return a success code, but a null GUID. } else if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; // (DWORD)NTE_FAIL goto ErrorExit; } dwSts = RegQueryValueEx(hRegKey, SZCRYPTOMACHINEGUID, 0, NULL, NULL, &cbUuid); if (ERROR_FILE_NOT_FOUND == dwSts) { dwReturn = ERROR_SUCCESS; goto ErrorExit; // Return a success code, but a null GUID. } else if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; // (DWORD)NTE_FAIL goto ErrorExit; } pszUuid = (LPSTR)ContInfoAlloc(cbUuid); if (NULL == pszUuid) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } dwSts = RegQueryValueEx(hRegKey, SZCRYPTOMACHINEGUID, 0, NULL, (LPBYTE)pszUuid, &cbUuid); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; // (DWORD)NTE_FAIL; goto ErrorExit; } // convert from ansi to unicode cch = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszUuid, -1, NULL, cch); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } pwszUuid = ContInfoAlloc((cch + 1) * sizeof(WCHAR)); if (NULL == pwszUuid) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } cch = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszUuid, -1, pwszUuid, cch); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } *ppwszUuid = pwszUuid; pwszUuid = NULL; dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pwszUuid) ContInfoFree(pwszUuid); if (NULL != pszUuid) ContInfoFree(pszUuid); if (NULL != hRegKey) RegCloseKey(hRegKey); return dwReturn; } DWORD SetMachineGUID( void) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HKEY hRegKey = 0; UUID Uuid; LPSTR pszUuid = NULL; DWORD cbUuid; LPWSTR pwszOldUuid = NULL; DWORD dwSts; DWORD dwResult; dwSts = GetMachineGUID(&pwszOldUuid); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } if (NULL != pwszOldUuid) { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } UuidCreate(&Uuid); dwSts = (DWORD) UuidToStringA(&Uuid, &pszUuid); if (RPC_S_OK != dwSts) { dwReturn = dwSts; // (DWORD)NTE_FAIL; goto ErrorExit; } // read the GUID from the Local Machine portion of the registry dwSts = RegCreateKeyEx(HKEY_LOCAL_MACHINE, SZLOCALMACHINECRYPTO, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &hRegKey, &dwResult); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; // (DWORD)NTE_FAIL; goto ErrorExit; } dwSts = RegQueryValueEx(hRegKey, SZCRYPTOMACHINEGUID, 0, NULL, NULL, &cbUuid); if (ERROR_FILE_NOT_FOUND != dwSts) { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } dwSts = RegSetValueEx(hRegKey, SZCRYPTOMACHINEGUID, 0, REG_SZ, (BYTE*)pszUuid, strlen(pszUuid) + 1); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; // (DWORD)NTE_FAIL; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (pszUuid) RpcStringFreeA(&pszUuid); if (pwszOldUuid) ContInfoFree(pwszOldUuid); if (hRegKey) RegCloseKey(hRegKey); return dwReturn; } /*static*/ DWORD AddMachineGuidToContainerName( LPSTR pszContainer, LPWSTR pwszNewContainer) { DWORD dwReturn = ERROR_INTERNAL_ERROR; WCHAR rgwszHash[33]; LPWSTR pwszUuid = NULL; DWORD dwSts; memset(rgwszHash, 0, sizeof(rgwszHash)); // get the stringized hash of the container name dwSts = GetHashOfContainer(pszContainer, rgwszHash); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // get the GUID of the machine dwSts = GetMachineGUID(&pwszUuid); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } if (NULL == pwszUuid) { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } wcscpy(pwszNewContainer, rgwszHash); wcscat(pwszNewContainer, L"_"); wcscat(pwszNewContainer, pwszUuid); dwReturn = ERROR_SUCCESS; ErrorExit: if (pwszUuid) ContInfoFree(pwszUuid); return dwReturn; } // // Just tries to use DPAPI to make sure it works before creating a key // container. // DWORD TryDPAPI( void) { DWORD dwReturn = ERROR_INTERNAL_ERROR; CRYPTPROTECT_PROMPTSTRUCT PromptStruct; CRYPT_DATA_BLOB DataIn; CRYPT_DATA_BLOB DataOut; CRYPT_DATA_BLOB ExtraEntropy; DWORD dwJunk = 0; DWORD dwSts; memset(&PromptStruct, 0, sizeof(PromptStruct)); memset(&DataIn, 0, sizeof(DataIn)); memset(&DataOut, 0, sizeof(DataOut)); PromptStruct.cbSize = sizeof(PromptStruct); DataIn.cbData = sizeof(DWORD); DataIn.pbData = (BYTE*)&dwJunk; ExtraEntropy.cbData = sizeof(STUFF_TO_GO_INTO_MIX); ExtraEntropy.pbData = (LPBYTE)STUFF_TO_GO_INTO_MIX; dwSts = MyCryptProtectData(&DataIn, L"Export Flag", &ExtraEntropy, NULL, &PromptStruct, 0, &DataOut); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != DataOut.pbData) LocalFree(DataOut.pbData); return dwReturn; } /*static*/ DWORD ProtectExportabilityFlag( IN BOOL fExportable, IN BOOL fMachineKeyset, OUT BYTE **ppbProtectedExportability, OUT DWORD *pcbProtectedExportability) { DWORD dwReturn = ERROR_INTERNAL_ERROR; CRYPTPROTECT_PROMPTSTRUCT PromptStruct; CRYPT_DATA_BLOB DataIn; CRYPT_DATA_BLOB DataOut; CRYPT_DATA_BLOB ExtraEntropy; DWORD dwProtectFlags = 0; DWORD dwSts = 0; memset(&PromptStruct, 0, sizeof(PromptStruct)); memset(&DataIn, 0, sizeof(DataIn)); memset(&DataOut, 0, sizeof(DataOut)); if (fMachineKeyset) dwProtectFlags = CRYPTPROTECT_LOCAL_MACHINE; PromptStruct.cbSize = sizeof(PromptStruct); DataIn.cbData = sizeof(BOOL); DataIn.pbData = (BYTE*)&fExportable; ExtraEntropy.cbData = sizeof(STUFF_TO_GO_INTO_MIX); ExtraEntropy.pbData = (LPBYTE)STUFF_TO_GO_INTO_MIX; dwSts = MyCryptProtectData(&DataIn, L"Export Flag", &ExtraEntropy, NULL, &PromptStruct, dwProtectFlags, &DataOut); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } *ppbProtectedExportability = ContInfoAlloc(DataOut.cbData); if (NULL == *ppbProtectedExportability) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } *pcbProtectedExportability = DataOut.cbData; memcpy(*ppbProtectedExportability, DataOut.pbData, DataOut.cbData); dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != DataOut.pbData) LocalFree(DataOut.pbData); return dwReturn; } /*static*/ DWORD UnprotectExportabilityFlag( IN BOOL fMachineKeyset, IN BYTE *pbProtectedExportability, IN DWORD cbProtectedExportability, IN BOOL *pfExportable) { DWORD dwReturn = ERROR_INTERNAL_ERROR; CRYPTPROTECT_PROMPTSTRUCT PromptStruct; CRYPT_DATA_BLOB DataIn; CRYPT_DATA_BLOB DataOut; CRYPT_DATA_BLOB ExtraEntropy; DWORD dwProtectFlags = 0; DWORD dwSts = 0; memset(&PromptStruct, 0, sizeof(PromptStruct)); memset(&DataIn, 0, sizeof(DataIn)); memset(&DataOut, 0, sizeof(DataOut)); memset(&ExtraEntropy, 0, sizeof(ExtraEntropy)); if (fMachineKeyset) dwProtectFlags = CRYPTPROTECT_LOCAL_MACHINE; PromptStruct.cbSize = sizeof(PromptStruct); DataIn.cbData = cbProtectedExportability; DataIn.pbData = pbProtectedExportability; ExtraEntropy.cbData = sizeof(STUFF_TO_GO_INTO_MIX); ExtraEntropy.pbData = (LPBYTE)STUFF_TO_GO_INTO_MIX; dwSts = MyCryptUnprotectData(&DataIn, NULL, &ExtraEntropy, NULL, &PromptStruct, dwProtectFlags, &DataOut, NULL); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; // NTE_BAD_KEYSET goto ErrorExit; } if (sizeof(BOOL) != DataOut.cbData) { dwReturn = (DWORD)NTE_BAD_KEYSET; goto ErrorExit; } *pfExportable = *((BOOL*)DataOut.pbData); dwReturn = ERROR_SUCCESS; ErrorExit: // free the DataOut struct if necessary if (NULL != DataOut.pbData) LocalFree(DataOut.pbData); return dwReturn; } /*++ Creates a DACL for the MachineKeys directory for machine keysets so that Everyone may create machine keys. --*/ /*static*/ DWORD GetMachineKeysetDirDACL( IN OUT PACL *ppDacl) { DWORD dwReturn = ERROR_INTERNAL_ERROR; SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY siaNTAuth = SECURITY_NT_AUTHORITY; PSID pEveryoneSid = NULL; PSID pAdminsSid = NULL; DWORD dwAclSize; // // prepare Sids representing the world and admins // if (!AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSid)) { dwReturn = GetLastError(); goto ErrorExit; } if (!AllocateAndInitializeSid(&siaNTAuth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminsSid)) { dwReturn = GetLastError(); goto ErrorExit; } // // compute size of new acl // dwAclSize = sizeof(ACL) + 2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetLengthSid(pEveryoneSid) + GetLengthSid(pAdminsSid); // // allocate storage for Acl // *ppDacl = (PACL)ContInfoAlloc(dwAclSize); if (NULL == *ppDacl) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } if (!InitializeAcl(*ppDacl, dwAclSize, ACL_REVISION)) { dwReturn = GetLastError(); goto ErrorExit; } if (!AddAccessAllowedAce(*ppDacl, ACL_REVISION, (FILE_GENERIC_WRITE | FILE_GENERIC_READ) & (~WRITE_DAC), pEveryoneSid)) { dwReturn = GetLastError(); goto ErrorExit; } if (!AddAccessAllowedAce(*ppDacl, ACL_REVISION, FILE_ALL_ACCESS, pAdminsSid)) { dwReturn = GetLastError(); goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pEveryoneSid) FreeSid(pEveryoneSid); if (NULL != pAdminsSid) FreeSid(pAdminsSid); return dwReturn; } DWORD CreateSystemDirectory( LPCWSTR lpPathName, SECURITY_ATTRIBUTES *pSecAttrib) { DWORD dwReturn = ERROR_INTERNAL_ERROR; NTSTATUS Status; OBJECT_ATTRIBUTES Obja; HANDLE Handle; UNICODE_STRING FileName; IO_STATUS_BLOCK IoStatusBlock; RTL_RELATIVE_NAME RelativeName; PVOID FreeBuffer; if(!RtlDosPathNameToNtPathName_U( lpPathName, &FileName, NULL, &RelativeName)) { dwReturn = ERROR_PATH_NOT_FOUND; goto ErrorExit; } FreeBuffer = FileName.Buffer; if ( RelativeName.RelativeName.Length ) { FileName = *(PUNICODE_STRING)&RelativeName.RelativeName; } else { RelativeName.ContainingDirectory = NULL; } InitializeObjectAttributes( &Obja, &FileName, OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, (NULL != pSecAttrib) ? pSecAttrib->lpSecurityDescriptor : NULL); // Creating the directory with attribute FILE_ATTRIBUTE_SYSTEM to avoid inheriting encryption // property from parent directory Status = NtCreateFile( &Handle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0L ); RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer); if(NT_SUCCESS(Status)) { NtClose(Handle); dwReturn = ERROR_SUCCESS; } else { if (STATUS_TIMEOUT == Status) dwReturn = ERROR_TIMEOUT; else dwReturn = RtlNtStatusToDosError(Status); } ErrorExit: return dwReturn; } /*++ Create all subdirectories if they do not exists starting at szCreationStartPoint. szCreationStartPoint must point to a character within the null terminated buffer specified by the szFullPath parameter. Note that szCreationStartPoint should not point at the first character of a drive root, eg: d:\foo\bar\bilge\water \\server\share\foo\bar \\?\d:\big\path\bilge\water Instead, szCreationStartPoint should point beyond these components, eg: bar\bilge\water foo\bar big\path\bilge\water This function does not implement logic for adjusting to compensate for these inputs because the environment it was design to be used in causes the input szCreationStartPoint to point well into the szFullPath input buffer. --*/ /*static*/ DWORD CreateNestedDirectories( IN LPWSTR wszFullPath, IN LPWSTR wszCreationStartPoint, // must point in null-terminated range of szFullPath IN BOOL fMachineKeyset) { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD i; DWORD dwPrevious = 0; DWORD cchRemaining; SECURITY_ATTRIBUTES SecAttrib; SECURITY_ATTRIBUTES *pSecAttrib; SECURITY_DESCRIPTOR sd; PACL pDacl = NULL; DWORD dwSts = ERROR_SUCCESS; BOOL fSts; if (wszCreationStartPoint < wszFullPath || wszCreationStartPoint > (wcslen(wszFullPath) + wszFullPath)) { dwReturn = ERROR_INVALID_PARAMETER; goto ErrorExit; } cchRemaining = wcslen(wszCreationStartPoint); // // scan from left to right in the szCreationStartPoint string // looking for directory delimiter. // for (i = 0; i < cchRemaining; i++) { WCHAR charReplaced = wszCreationStartPoint[i]; if (charReplaced == '\\' || charReplaced == '/') { wszCreationStartPoint[ i ] = '\0'; pSecAttrib = NULL; if (fMachineKeyset) { memset(&SecAttrib, 0, sizeof(SecAttrib)); SecAttrib.nLength = sizeof(SecAttrib); if (0 == wcscmp(MACHINE_KEYS_DIR, &(wszCreationStartPoint[ dwPrevious ]))) { dwSts = GetMachineKeysetDirDACL(&pDacl); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } fSts = InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } fSts = SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } SecAttrib.lpSecurityDescriptor = &sd; pSecAttrib = &SecAttrib; } } dwSts = CreateSystemDirectory(wszFullPath, pSecAttrib); dwPrevious = i + 1; wszCreationStartPoint[ i ] = charReplaced; if (ERROR_SUCCESS != dwSts) { // // continue onwards, trying to create specified // subdirectories. This is done to address the obscure // scenario where the Bypass Traverse Checking Privilege // allows the caller to create directories below an // existing path where one component denies the user // access. We just keep trying and the last // CreateDirectory() result is returned to the caller. // continue; } } } if (ERROR_ALREADY_EXISTS == dwSts) dwSts = ERROR_SUCCESS; dwReturn = dwSts; ErrorExit: if (NULL != pDacl) ContInfoFree(pDacl); return dwReturn; } #ifdef _M_IX86 BOOL WINAPI FIsWinNT( void) { static BOOL fIKnow = FALSE; static BOOL fIsWinNT = FALSE; OSVERSIONINFO osVer; if (fIKnow) return(fIsWinNT); memset(&osVer, 0, sizeof(OSVERSIONINFO)); osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx(&osVer)) fIsWinNT = (osVer.dwPlatformId == VER_PLATFORM_WIN32_NT); // even on an error, this is as good as it gets fIKnow = TRUE; return fIsWinNT; } #else // other than _M_IX86 BOOL WINAPI FIsWinNT( void) { return TRUE; } #endif // _M_IX86 /*++ This function determines if the user associated with the specified token is the Local System account. --*/ DWORD IsLocalSystem( BOOL *pfIsLocalSystem) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HANDLE hToken = 0; HANDLE hThreadToken = NULL; UCHAR InfoBuffer[1024]; DWORD dwInfoBufferSize = sizeof(InfoBuffer); PTOKEN_USER SlowBuffer = NULL; PTOKEN_USER pTokenUser = (PTOKEN_USER)InfoBuffer; PSID psidLocalSystem = NULL; SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; BOOL fSts; DWORD dwSts; *pfIsLocalSystem = FALSE; fSts = OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hThreadToken); if (fSts) { // impersonation is going on need to save handle RevertToSelf(); } fSts = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } if (NULL != hThreadToken) { // put the impersonation token back fSts = SetThreadToken(NULL, hThreadToken); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } } fSts = GetTokenInformation(hToken, TokenUser, pTokenUser, dwInfoBufferSize, &dwInfoBufferSize); if (!fSts) { dwSts = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER == dwSts) { // // if fast buffer wasn't big enough, allocate enough storage // and try again. // SlowBuffer = (PTOKEN_USER)ContInfoAlloc(dwInfoBufferSize); if (NULL == SlowBuffer) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } pTokenUser = SlowBuffer; fSts = GetTokenInformation(hToken, TokenUser, pTokenUser, dwInfoBufferSize, &dwInfoBufferSize); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } } else { dwReturn = dwSts; goto ErrorExit; } } fSts = AllocateAndInitializeSid(&siaNtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidLocalSystem); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } if (EqualSid(psidLocalSystem, pTokenUser->User.Sid)) *pfIsLocalSystem = TRUE; dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != SlowBuffer) ContInfoFree(SlowBuffer); if (NULL != psidLocalSystem) FreeSid(psidLocalSystem); if (NULL != hThreadToken) CloseHandle(hThreadToken); if (NULL != hToken) CloseHandle(hToken); return dwReturn; } /*++ This function determines if the user associated with the specified token is the Local System account. --*/ /*static*/ DWORD IsThreadLocalSystem( BOOL *pfIsLocalSystem) { DWORD dwReturn = ERROR_INTERNAL_ERROR; BOOL fSts; DWORD dwSts; HANDLE hToken = 0; UCHAR InfoBuffer[1024]; DWORD dwInfoBufferSize = sizeof(InfoBuffer); PTOKEN_USER SlowBuffer = NULL; PTOKEN_USER pTokenUser = (PTOKEN_USER)InfoBuffer; PSID psidLocalSystem = NULL; SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; *pfIsLocalSystem = FALSE; dwSts = OpenCallerToken(TOKEN_QUERY, &hToken); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } fSts = GetTokenInformation(hToken, TokenUser, pTokenUser, dwInfoBufferSize, &dwInfoBufferSize); // // if fast buffer wasn't big enough, allocate enough storage // and try again. // if (!fSts) { dwSts = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER != dwSts) { dwReturn = dwSts; goto ErrorExit; } SlowBuffer = (PTOKEN_USER)ContInfoAlloc(dwInfoBufferSize); if (NULL == SlowBuffer) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } pTokenUser = SlowBuffer; fSts = GetTokenInformation(hToken, TokenUser, pTokenUser, dwInfoBufferSize, &dwInfoBufferSize); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } } fSts = AllocateAndInitializeSid(&siaNtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidLocalSystem); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } if (EqualSid(psidLocalSystem, pTokenUser->User.Sid)) *pfIsLocalSystem = TRUE; dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != SlowBuffer) ContInfoFree(SlowBuffer); if (NULL != psidLocalSystem) FreeSid(psidLocalSystem); if (NULL != hToken) CloseHandle(hToken); return dwReturn; } /*static*/ DWORD GetTextualSidA( PSID pSid, // binary Sid LPSTR TextualSid, // buffer for Textual representaion of Sid LPDWORD dwBufferLen) // required/provided TextualSid buffersize { DWORD dwReturn = ERROR_INTERNAL_ERROR; BOOL fSts; PSID_IDENTIFIER_AUTHORITY psia; DWORD dwSubAuthorities; DWORD dwCounter; DWORD dwSidSize; fSts = IsValidSid(pSid); if (!fSts) { dwReturn = ERROR_INVALID_PARAMETER; goto ErrorExit; } // obtain SidIdentifierAuthority psia = GetSidIdentifierAuthority(pSid); // obtain sidsubauthority count dwSubAuthorities = *GetSidSubAuthorityCount(pSid); // // compute buffer length (conservative guess) // S-SID_REVISION- + identifierauthority- + subauthorities- + NULL dwSidSize = (15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(WCHAR); // // check provided buffer length. // If not large enough, indicate proper size and setlasterror if (*dwBufferLen < dwSidSize) { *dwBufferLen = dwSidSize; dwReturn = ERROR_INSUFFICIENT_BUFFER; goto ErrorExit; } // // prepare S-SID_REVISION- dwSidSize = wsprintfA(TextualSid, "S-%lu-", SID_REVISION ); // // prepare SidIdentifierAuthority if ((psia->Value[0] != 0) || (psia->Value[1] != 0)) { dwSidSize += wsprintfA(TextualSid + dwSidSize, "0x%02hx%02hx%02hx%02hx%02hx%02hx", (USHORT)psia->Value[0], (USHORT)psia->Value[1], (USHORT)psia->Value[2], (USHORT)psia->Value[3], (USHORT)psia->Value[4], (USHORT)psia->Value[5]); } else { dwSidSize += wsprintfA(TextualSid + dwSidSize, "%lu", (ULONG)(psia->Value[5]) + (ULONG)(psia->Value[4] << 8) + (ULONG)(psia->Value[3] << 16) + (ULONG)(psia->Value[2] << 24)); } // // loop through SidSubAuthorities for (dwCounter = 0; dwCounter < dwSubAuthorities; dwCounter++) { dwSidSize += wsprintfA(TextualSid + dwSidSize, "-%lu", *GetSidSubAuthority(pSid, dwCounter)); } *dwBufferLen = dwSidSize + 1; // tell caller how many chars (include NULL) dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } /*static*/ DWORD GetTextualSidW( PSID pSid, // binary Sid LPWSTR wszTextualSid, // buffer for Textual representaion of Sid LPDWORD dwBufferLen) // required/provided TextualSid buffersize { DWORD dwReturn = ERROR_INTERNAL_ERROR; BOOL fSts; PSID_IDENTIFIER_AUTHORITY psia; DWORD dwSubAuthorities; DWORD dwCounter; DWORD dwSidSize; fSts = IsValidSid(pSid); if (!fSts) { dwReturn = ERROR_INVALID_PARAMETER; goto ErrorExit; } // obtain SidIdentifierAuthority psia = GetSidIdentifierAuthority(pSid); // obtain sidsubauthority count dwSubAuthorities = *GetSidSubAuthorityCount(pSid); // // compute buffer length (conservative guess) // S-SID_REVISION- + identifierauthority- + subauthorities- + NULL dwSidSize = (15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(WCHAR); // // check provided buffer length. // If not large enough, indicate proper size and setlasterror if (*dwBufferLen < dwSidSize) { *dwBufferLen = dwSidSize; dwReturn = ERROR_INSUFFICIENT_BUFFER; goto ErrorExit; } // // prepare S-SID_REVISION- dwSidSize = wsprintfW(wszTextualSid, L"S-%lu-", SID_REVISION); // // prepare SidIdentifierAuthority if ((psia->Value[0] != 0) || (psia->Value[1] != 0)) { dwSidSize += wsprintfW(wszTextualSid + dwSidSize, L"0x%02hx%02hx%02hx%02hx%02hx%02hx", (USHORT)psia->Value[0], (USHORT)psia->Value[1], (USHORT)psia->Value[2], (USHORT)psia->Value[3], (USHORT)psia->Value[4], (USHORT)psia->Value[5]); } else { dwSidSize += wsprintfW(wszTextualSid + dwSidSize, L"%lu", (ULONG)(psia->Value[5]) + (ULONG)(psia->Value[4] << 8) + (ULONG)(psia->Value[3] << 16) + (ULONG)(psia->Value[2] << 24)); } // // loop through SidSubAuthorities for (dwCounter = 0; dwCounter < dwSubAuthorities; dwCounter++) { dwSidSize += wsprintfW(wszTextualSid + dwSidSize, L"-%lu", *GetSidSubAuthority(pSid, dwCounter)); } *dwBufferLen = dwSidSize + 1; // tell caller how many chars (include NULL) dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } /*static*/ DWORD GetUserSid( PTOKEN_USER *pptgUser, DWORD *pcbUser, BOOL *pfAlloced) { DWORD dwReturn = ERROR_INTERNAL_ERROR; BOOL fSts; DWORD dwSts; HANDLE hToken = 0; *pfAlloced = FALSE; dwSts = OpenCallerToken(TOKEN_QUERY, &hToken); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } fSts = GetTokenInformation(hToken, // identifies access token TokenUser, // TokenUser info type *pptgUser, // retrieved info buffer *pcbUser, // size of buffer passed-in pcbUser); // required buffer size if (!fSts) { dwSts = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER != dwSts) { dwReturn = dwSts; goto ErrorExit; } // // try again with the specified buffer size // *pptgUser = (PTOKEN_USER)ContInfoAlloc(*pcbUser); if (NULL == *pptgUser) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } *pfAlloced = TRUE; fSts = GetTokenInformation(hToken, // identifies access token TokenUser, // TokenUser info type *pptgUser, // retrieved info buffer *pcbUser, // size of buffer passed-in pcbUser); // required buffer size if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != hToken) CloseHandle(hToken); return dwReturn; } DWORD GetUserTextualSidA( LPSTR lpBuffer, LPDWORD nSize) { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD dwSts; BYTE FastBuffer[FAST_BUF_SIZE]; PTOKEN_USER ptgUser; DWORD cbUser; BOOL fAlloced = FALSE; ptgUser = (PTOKEN_USER)FastBuffer; // try fast buffer first cbUser = FAST_BUF_SIZE; dwSts = GetUserSid(&ptgUser, &cbUser, &fAlloced); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // // obtain the textual representaion of the Sid // dwSts = GetTextualSidA(ptgUser->User.Sid, // user binary Sid lpBuffer, // buffer for TextualSid nSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (fAlloced) { if (NULL != ptgUser) ContInfoFree(ptgUser); } return dwReturn; } DWORD GetUserTextualSidW( LPWSTR lpBuffer, LPDWORD nSize) { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD dwSts; BYTE FastBuffer[FAST_BUF_SIZE]; PTOKEN_USER ptgUser; DWORD cbUser; BOOL fAlloced = FALSE; ptgUser = (PTOKEN_USER)FastBuffer; // try fast buffer first cbUser = FAST_BUF_SIZE; dwSts = GetUserSid(&ptgUser, &cbUser, &fAlloced); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // // obtain the textual representaion of the Sid // dwSts = GetTextualSidW(ptgUser->User.Sid, // user binary Sid lpBuffer, // buffer for TextualSid nSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (fAlloced) { if (NULL != ptgUser) ContInfoFree(ptgUser); } return dwReturn; } /*static*/ DWORD GetUserDirectory( IN BOOL fMachineKeyset, OUT LPWSTR pwszUser, OUT DWORD *pcbUser) { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD dwSts; if (fMachineKeyset) { wcscpy(pwszUser, MACHINE_KEYS_DIR); *pcbUser = wcslen(pwszUser) + 1; } else { if (FIsWinNT()) { dwSts = GetUserTextualSidW(pwszUser, pcbUser); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } } else { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } } dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } #define WSZRSAPRODUCTSTRING L"\\Microsoft\\Crypto\\RSA\\" #define WSZDSSPRODUCTSTRING L"\\Microsoft\\Crypto\\DSS\\" #define PRODUCTSTRINGLEN sizeof(WSZRSAPRODUCTSTRING) - sizeof(WCHAR) typedef HRESULT (WINAPI *SHGETFOLDERPATHW)( HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPWSTR pwszPath); /*static*/ DWORD GetUserStorageArea( IN DWORD dwProvType, IN BOOL fMachineKeyset, IN BOOL fOldWin2KMachineKeyPath, OUT BOOL *pfIsLocalSystem, // used if fMachineKeyset is FALSE, in this // case TRUE is returned if running as Local // System IN OUT LPWSTR *ppwszUserStorageArea) { DWORD dwReturn = ERROR_INTERNAL_ERROR; WCHAR wszUserStorageRoot[MAX_PATH+1]; DWORD cbUserStorageRoot; WCHAR *wszProductString = NULL; WCHAR wszUser[MAX_PATH]; DWORD cbUser; DWORD cchUser = MAX_PATH; HANDLE hToken = NULL; DWORD dwTempProfileFlags = 0; DWORD dwSts; BOOL fSts; HMODULE hShell32 = NULL; PBYTE pbCurrent; *pfIsLocalSystem = FALSE; if ((PROV_RSA_SIG == dwProvType) || (PROV_RSA_FULL == dwProvType) || (PROV_RSA_SCHANNEL == dwProvType) || (PROV_RSA_AES == dwProvType)) { wszProductString = WSZRSAPRODUCTSTRING; } else if ((PROV_DSS == dwProvType) || (PROV_DSS_DH == dwProvType) || (PROV_DH_SCHANNEL == dwProvType)) { wszProductString = WSZDSSPRODUCTSTRING; } // // check if running in the LocalSystem context // if (!fMachineKeyset) { dwSts = IsThreadLocalSystem(pfIsLocalSystem); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } } // // determine path to per-user storage area, based on whether this // is a local machine disposition call or a per-user disposition call. // if (fMachineKeyset || *pfIsLocalSystem) { if (!fOldWin2KMachineKeyPath) { // Should not call SHGetFolderPathW with a caller token for // the local machine case. The COMMON_APPDATA location is // per-machine, not per-user, therefor we shouldn't be supplying // a user token. The shell team should make their own change to ignore // this, though. /* dwSts = OpenCallerToken(TOKEN_QUERY | TOKEN_IMPERSONATE, &hToken); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } */ dwSts = (DWORD) SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, 0 /*hToken*/, 0, wszUserStorageRoot); if (dwSts != ERROR_SUCCESS) { dwReturn = dwSts; goto ErrorExit; } /* CloseHandle(hToken); hToken = NULL; */ cbUserStorageRoot = wcslen( wszUserStorageRoot ) * sizeof(WCHAR); } else { cbUserStorageRoot = GetSystemDirectoryW(wszUserStorageRoot, MAX_PATH); cbUserStorageRoot *= sizeof(WCHAR); } } else { // check if the profile is temporary fSts = GetProfileType(&dwTempProfileFlags); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } if ((dwTempProfileFlags & PT_MANDATORY) || ((dwTempProfileFlags & PT_TEMPORARY) && !(dwTempProfileFlags & PT_ROAMING))) { dwReturn = (DWORD)NTE_TEMPORARY_PROFILE; goto ErrorExit; } dwSts = OpenCallerToken(TOKEN_QUERY | TOKEN_IMPERSONATE, &hToken); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // Use new private shell entry point for finding user storage path if (ERROR_SUCCESS != (dwSts = GetUserAppDataPathW(hToken, wszUserStorageRoot))) { dwReturn = dwSts; goto ErrorExit; } CloseHandle(hToken); hToken = NULL; cbUserStorageRoot = wcslen( wszUserStorageRoot ) * sizeof(WCHAR); } if (cbUserStorageRoot == 0) { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } // // get the user name associated with the call. // Note: this is the textual Sid on NT, and will fail on Win95. // dwSts = GetUserDirectory(fMachineKeyset, wszUser, &cchUser); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } cbUser = (cchUser-1) * sizeof(WCHAR); *ppwszUserStorageArea = (LPWSTR)ContInfoAlloc(cbUserStorageRoot + PRODUCTSTRINGLEN + cbUser + 2 * sizeof(WCHAR)); // trailing slash and NULL if (NULL == *ppwszUserStorageArea) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } pbCurrent = (PBYTE)*ppwszUserStorageArea; CopyMemory(pbCurrent, wszUserStorageRoot, cbUserStorageRoot); pbCurrent += cbUserStorageRoot; CopyMemory(pbCurrent, wszProductString, PRODUCTSTRINGLEN); pbCurrent += PRODUCTSTRINGLEN; CopyMemory(pbCurrent, wszUser, cbUser); pbCurrent += cbUser; // note: cbUser does not include terminal NULL ((LPSTR)pbCurrent)[0] = '\\'; ((LPSTR)pbCurrent)[1] = '\0'; dwSts = CreateNestedDirectories(*ppwszUserStorageArea, (LPWSTR)((LPBYTE)*ppwszUserStorageArea + cbUserStorageRoot + sizeof(WCHAR)), fMachineKeyset); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != hToken) CloseHandle(hToken); return dwReturn; } /*static*/ DWORD GetFilePath( IN LPCWSTR pwszUserStorageArea, IN LPCWSTR pwszFileName, IN OUT LPWSTR *ppwszFilePath) { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD cbUserStorageArea; DWORD cbFileName; cbUserStorageArea = wcslen(pwszUserStorageArea) * sizeof(WCHAR); cbFileName = wcslen(pwszFileName) * sizeof(WCHAR); *ppwszFilePath = (LPWSTR)ContInfoAlloc(cbUserStorageArea + cbFileName + sizeof(WCHAR)); if (*ppwszFilePath == NULL) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } CopyMemory(*ppwszFilePath, pwszUserStorageArea, cbUserStorageArea); CopyMemory((LPBYTE)*ppwszFilePath+cbUserStorageArea, pwszFileName, cbFileName + sizeof(WCHAR)); dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } static DWORD rgdwCreateFileRetryMilliseconds[] = { 1, 10, 100, 500, 1000, 5000 }; #define MAX_CREATE_FILE_RETRY_COUNT \ (sizeof(rgdwCreateFileRetryMilliseconds) \ / sizeof(rgdwCreateFileRetryMilliseconds[0])) /*static*/ DWORD MyCreateFile( IN BOOL fMachineKeyset, // indicates if this is a machine keyset IN LPCWSTR wszFilePath, // pointer to name of the file IN DWORD dwDesiredAccess, // access (read-write) mode IN DWORD dwShareMode, // share mode IN DWORD dwCreationDisposition, // how to create IN DWORD dwAttribs, // file attributes OUT HANDLE *phFile) // Resultant handle { DWORD dwReturn = ERROR_INTERNAL_ERROR; HANDLE hToken = 0; BYTE rgbPriv[sizeof(PRIVILEGE_SET) + sizeof(LUID_AND_ATTRIBUTES)]; PRIVILEGE_SET *pPriv = (PRIVILEGE_SET*)rgbPriv; BOOL fPrivSet = FALSE; HANDLE hFile = INVALID_HANDLE_VALUE; DWORD dwSts, dwSavedSts; BOOL fSts; hFile = CreateFileW(wszFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwAttribs, NULL); if (INVALID_HANDLE_VALUE == hFile) { dwSts = GetLastError(); // check if machine keyset if (fMachineKeyset) { dwSavedSts = dwSts; // open a token handle dwSts = OpenCallerToken(TOKEN_QUERY, &hToken); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } memset(rgbPriv, 0, sizeof(rgbPriv)); pPriv->PrivilegeCount = 1; // reading file if (dwDesiredAccess & GENERIC_READ) { fSts = LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &(pPriv->Privilege[0].Luid)); } // writing else { fSts = LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &(pPriv->Privilege[0].Luid)); } if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } // check if the BACKUP or RESTORE privileges are set pPriv->Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; fSts = PrivilegeCheck(hToken, pPriv, &fPrivSet); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } if (fPrivSet) { hFile = CreateFileW(wszFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwAttribs | FILE_FLAG_BACKUP_SEMANTICS, NULL); if (INVALID_HANDLE_VALUE == hFile) { dwReturn = GetLastError(); goto ErrorExit; } } else { dwReturn = dwSavedSts; goto ErrorExit; } } else { dwReturn = dwSts; goto ErrorExit; } } *phFile = hFile; dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != hToken) CloseHandle(hToken); return dwReturn; } /*static*/ DWORD OpenFileInStorageArea( IN BOOL fMachineKeyset, IN DWORD dwDesiredAccess, IN LPCWSTR wszUserStorageArea, IN LPCWSTR wszFileName, IN OUT HANDLE *phFile) { DWORD dwReturn = ERROR_INTERNAL_ERROR; LPWSTR wszFilePath = NULL; DWORD dwShareMode = 0; DWORD dwCreationDistribution = OPEN_EXISTING; DWORD dwRetryCount; DWORD dwAttribs = 0; DWORD dwSts; *phFile = INVALID_HANDLE_VALUE; if (dwDesiredAccess & GENERIC_READ) { dwShareMode |= FILE_SHARE_READ; dwCreationDistribution = OPEN_EXISTING; } if (dwDesiredAccess & GENERIC_WRITE) { dwShareMode = 0; dwCreationDistribution = OPEN_ALWAYS; dwAttribs = FILE_ATTRIBUTE_SYSTEM; } dwSts = GetFilePath(wszUserStorageArea, wszFileName, &wszFilePath); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwRetryCount = 0; for (;;) { dwSts = MyCreateFile(fMachineKeyset, wszFilePath, dwDesiredAccess, dwShareMode, dwCreationDistribution, dwAttribs | FILE_FLAG_SEQUENTIAL_SCAN, phFile); if (ERROR_SUCCESS == dwSts) break; if (((ERROR_SHARING_VIOLATION == dwSts) || (ERROR_ACCESS_DENIED == dwSts)) && (MAX_CREATE_FILE_RETRY_COUNT > dwRetryCount)) { Sleep(rgdwCreateFileRetryMilliseconds[dwRetryCount]); dwRetryCount++; } else { if (ERROR_FILE_NOT_FOUND == dwSts) dwReturn = (DWORD)NTE_BAD_KEYSET; else dwReturn = dwSts; goto ErrorExit; } } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != wszFilePath) ContInfoFree(wszFilePath); return dwReturn; } /*static*/ DWORD FindClosestFileInStorageArea( IN LPCWSTR pwszUserStorageArea, IN LPCSTR pszContainer, OUT LPWSTR pwszNewFileName, IN OUT HANDLE *phFile) { DWORD dwReturn = ERROR_INTERNAL_ERROR; LPWSTR pwszFilePath = NULL; WCHAR rgwszNewFileName[35]; DWORD dwShareMode = 0; DWORD dwCreationDistribution = OPEN_EXISTING; HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATAW FindData; DWORD dwSts; memset(&FindData, 0, sizeof(FindData)); memset(rgwszNewFileName, 0, sizeof(rgwszNewFileName)); *phFile = INVALID_HANDLE_VALUE; dwShareMode |= FILE_SHARE_READ; dwCreationDistribution = OPEN_EXISTING; // get the stringized hash of the container name dwSts = GetHashOfContainer(pszContainer, rgwszNewFileName); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // ContInfoAlloc zeros memory so no need to set NULL terminator rgwszNewFileName[32] = '_'; rgwszNewFileName[33] = '*'; dwSts = GetFilePath(pwszUserStorageArea, rgwszNewFileName, &pwszFilePath); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } hFind = FindFirstFileExW(pwszFilePath, FindExInfoStandard, &FindData, FindExSearchNameMatch, NULL, 0); if (hFind == INVALID_HANDLE_VALUE) { dwSts = GetLastError(); if (ERROR_FILE_NOT_FOUND == dwSts) dwReturn = (DWORD)NTE_BAD_KEYSET; else dwReturn = dwSts; goto ErrorExit; } ContInfoFree(pwszFilePath); pwszFilePath = NULL; dwSts = GetFilePath(pwszUserStorageArea, FindData.cFileName, &pwszFilePath); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } *phFile = CreateFileW(pwszFilePath, GENERIC_READ, dwShareMode, NULL, dwCreationDistribution, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (*phFile == INVALID_HANDLE_VALUE) { dwSts = GetLastError(); if (ERROR_FILE_NOT_FOUND == dwSts) dwReturn = (DWORD)NTE_BAD_KEYSET; else dwReturn = dwSts; goto ErrorExit; } // allocate and copy in the real file name to be returned wcscpy(pwszNewFileName, FindData.cFileName); dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != hFind) FindClose(hFind); if (NULL != pwszFilePath) ContInfoFree(pwszFilePath); return dwReturn; } // // This function gets the determines if the user associated with the // specified token is the Local System account. // /*static*/ DWORD ZeroizeFile( IN LPCWSTR wszFilePath) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HANDLE hFile = INVALID_HANDLE_VALUE; BYTE *pb = NULL; DWORD cb; DWORD dwBytesWritten = 0; DWORD dwSts; BOOL fSts; hFile = CreateFileW(wszFilePath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL); if (INVALID_HANDLE_VALUE == hFile) { dwSts = GetLastError(); if (ERROR_FILE_NOT_FOUND == dwSts) dwReturn = (DWORD)NTE_BAD_KEYSET; else dwReturn = dwSts; goto ErrorExit; } cb = GetFileSize(hFile, NULL); if ((DWORD)(-1) == cb) { dwReturn = GetLastError(); goto ErrorExit; } pb = ContInfoAlloc(cb); if (NULL == pb) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } fSts = WriteFile(hFile, pb, cb, &dwBytesWritten, NULL); if (!fSts) { dwReturn = GetLastError(); goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pb) ContInfoFree(pb); if (INVALID_HANDLE_VALUE != hFile) CloseHandle(hFile); return dwReturn; } /*static*/ DWORD DeleteFileInStorageArea( IN LPCWSTR wszUserStorageArea, IN LPCWSTR wszFileName) { DWORD dwReturn = ERROR_INTERNAL_ERROR; LPWSTR wszFilePath = NULL; DWORD cbUserStorageArea; DWORD cbFileName; DWORD dwSts; cbUserStorageArea = wcslen(wszUserStorageArea) * sizeof(WCHAR); cbFileName = wcslen(wszFileName) * sizeof(WCHAR); wszFilePath = (LPWSTR)ContInfoAlloc((cbUserStorageArea + cbFileName + 1) * sizeof(WCHAR)); if (wszFilePath == NULL) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } CopyMemory(wszFilePath, wszUserStorageArea, cbUserStorageArea); CopyMemory((LPBYTE)wszFilePath + cbUserStorageArea, wszFileName, cbFileName + sizeof(WCHAR)); // write a file of the same size with all zeros first dwSts = ZeroizeFile(wszFilePath); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } if (!DeleteFileW(wszFilePath)) { dwReturn = GetLastError(); goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != wszFilePath) ContInfoFree(wszFilePath); return dwReturn; } DWORD SetContainerUserName( IN LPSTR pszUserName, IN PKEY_CONTAINER_INFO pContInfo) { DWORD dwReturn = ERROR_INTERNAL_ERROR; pContInfo->pszUserName = (LPSTR)ContInfoAlloc((strlen(pszUserName) + 1) * sizeof(CHAR)); if (NULL == pContInfo->pszUserName) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } strcpy(pContInfo->pszUserName, pszUserName); dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } DWORD ReadContainerInfo( IN DWORD dwProvType, IN LPSTR pszContainerName, IN BOOL fMachineKeyset, IN DWORD dwFlags, OUT PKEY_CONTAINER_INFO pContInfo) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HANDLE hMap = NULL; BYTE *pbFile = NULL; DWORD cbFile; DWORD cb; HANDLE hFile = INVALID_HANDLE_VALUE; KEY_EXPORTABILITY_LENS Exportability; LPWSTR pwszFileName = NULL; LPWSTR pwszFilePath = NULL; WCHAR rgwszOtherMachineFileName[84]; BOOL fGetUserNameFromFile = FALSE; BOOL fIsLocalSystem = FALSE; BOOL fRetryWithHashedName = TRUE; DWORD cch = 0; DWORD dwSts; memset(&Exportability, 0, sizeof(Exportability)); // get the correct storage area (directory) dwSts = GetUserStorageArea(dwProvType, fMachineKeyset, FALSE, &fIsLocalSystem, &pwszFilePath); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // check if the length of the container name is the length of a new unique container, // then try with the container name which was passed in, if this fails // then try with the container name with the machine GUID appended if (69 == strlen(pszContainerName)) { // convert to UNICODE pszContainerName -> pwszFileName cch = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszContainerName, -1, NULL, cch); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } pwszFileName = ContInfoAlloc((cch + 1) * sizeof(WCHAR)); if (NULL == pwszFileName) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } cch = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszContainerName, -1, pwszFileName, cch); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } dwSts = OpenFileInStorageArea(fMachineKeyset, GENERIC_READ, pwszFilePath, pwszFileName, &hFile); if (ERROR_SUCCESS == dwSts) { wcscpy(pContInfo->rgwszFileName, pwszFileName); // set the flag so the name of the key container will be retrieved // from the file fGetUserNameFromFile = TRUE; fRetryWithHashedName = FALSE; } } if (fRetryWithHashedName) { dwSts = AddMachineGuidToContainerName(pszContainerName, pContInfo->rgwszFileName); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = OpenFileInStorageArea(fMachineKeyset, GENERIC_READ, pwszFilePath, pContInfo->rgwszFileName, &hFile); if (ERROR_SUCCESS != dwSts) { if ((ERROR_ACCESS_DENIED == dwSts) && (dwFlags & CRYPT_NEWKEYSET)) { dwReturn = (DWORD)NTE_EXISTS; goto ErrorExit; } if (NTE_BAD_KEYSET == dwSts) { if (fMachineKeyset || fIsLocalSystem) { dwReturn = dwSts; goto ErrorExit; } else { memset(rgwszOtherMachineFileName, 0, sizeof(rgwszOtherMachineFileName)); // try to open any file from another machine with this // container name dwSts = FindClosestFileInStorageArea(pwszFilePath, pszContainerName, rgwszOtherMachineFileName, &hFile); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } wcscpy(pContInfo->rgwszFileName, rgwszOtherMachineFileName); } } } } if (dwFlags & CRYPT_NEWKEYSET) { dwReturn = (DWORD)NTE_EXISTS; goto ErrorExit; } cbFile = GetFileSize(hFile, NULL); if ((DWORD)(-1) == cbFile) { dwReturn = GetLastError(); goto ErrorExit; } if (sizeof(KEY_CONTAINER_LENS) > cbFile) { dwSts = (DWORD)NTE_KEYSET_ENTRY_BAD; goto ErrorExit; } hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if (NULL == hMap) { dwReturn = GetLastError(); goto ErrorExit; } pbFile = (BYTE*)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); if (NULL == pbFile) { dwReturn = GetLastError(); goto ErrorExit; } // get the length information out of the file memcpy(&pContInfo->dwVersion, pbFile, sizeof(DWORD)); cb = sizeof(DWORD); if (KEY_CONTAINER_FILE_FORMAT_VER != pContInfo->dwVersion) { dwSts = (DWORD)NTE_KEYSET_ENTRY_BAD; goto ErrorExit; } memcpy(&pContInfo->ContLens, pbFile + cb, sizeof(KEY_CONTAINER_LENS)); cb += sizeof(KEY_CONTAINER_LENS); if (pContInfo->fCryptSilent && (0 != pContInfo->ContLens.dwUIOnKey)) { dwReturn = (DWORD)NTE_SILENT_CONTEXT; goto ErrorExit; } // get the private key exportability stuff memcpy(&Exportability, pbFile + cb, sizeof(KEY_EXPORTABILITY_LENS)); cb += sizeof(KEY_EXPORTABILITY_LENS); // get the user name pContInfo->pszUserName = ContInfoAlloc(pContInfo->ContLens.cbName); if (NULL == pContInfo->pszUserName) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } memcpy(pContInfo->pszUserName, pbFile + cb, pContInfo->ContLens.cbName); cb += pContInfo->ContLens.cbName; // get the random seed pContInfo->pbRandom = ContInfoAlloc(pContInfo->ContLens.cbRandom); if (NULL == pContInfo->pbRandom) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } memcpy(pContInfo->pbRandom, pbFile + cb, pContInfo->ContLens.cbRandom); cb += pContInfo->ContLens.cbRandom; // get the signature key info out of the file if (pContInfo->ContLens.cbSigPub && pContInfo->ContLens.cbSigEncPriv) { pContInfo->pbSigPub = ContInfoAlloc(pContInfo->ContLens.cbSigPub); if (NULL == pContInfo->pbSigPub) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } memcpy(pContInfo->pbSigPub, pbFile + cb, pContInfo->ContLens.cbSigPub); cb += pContInfo->ContLens.cbSigPub; pContInfo->pbSigEncPriv = ContInfoAlloc(pContInfo->ContLens.cbSigEncPriv); if (NULL == pContInfo->pbSigEncPriv) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } memcpy(pContInfo->pbSigEncPriv, pbFile + cb, pContInfo->ContLens.cbSigEncPriv); cb += pContInfo->ContLens.cbSigEncPriv; // get the exportability info for the sig key dwSts = UnprotectExportabilityFlag(fMachineKeyset, pbFile + cb, Exportability.cbSigExportability, &pContInfo->fSigExportable); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } cb += Exportability.cbSigExportability; } // get the signature key info out of the file if (pContInfo->ContLens.cbExchPub && pContInfo->ContLens.cbExchEncPriv) { pContInfo->pbExchPub = ContInfoAlloc(pContInfo->ContLens.cbExchPub); if (NULL == pContInfo->pbExchPub) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } memcpy(pContInfo->pbExchPub, pbFile + cb, pContInfo->ContLens.cbExchPub); cb += pContInfo->ContLens.cbExchPub; pContInfo->pbExchEncPriv = ContInfoAlloc(pContInfo->ContLens.cbExchEncPriv); if (NULL == pContInfo->pbExchEncPriv) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } memcpy(pContInfo->pbExchEncPriv, pbFile + cb, pContInfo->ContLens.cbExchEncPriv); cb += pContInfo->ContLens.cbExchEncPriv; // get the exportability info for the sig key dwSts = UnprotectExportabilityFlag(fMachineKeyset, pbFile + cb, Exportability.cbExchExportability, &pContInfo->fExchExportable); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } cb += Exportability.cbExchExportability; } pContInfo = NULL; dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pwszFileName) ContInfoFree(pwszFileName); if (NULL != pContInfo) FreeContainerInfo(pContInfo); if (NULL != pwszFilePath) ContInfoFree(pwszFilePath); if (NULL != pbFile) UnmapViewOfFile(pbFile); if (NULL != hMap) CloseHandle(hMap); if (INVALID_HANDLE_VALUE != hFile) CloseHandle(hFile); return dwReturn; } DWORD WriteContainerInfo( IN DWORD dwProvType, IN LPWSTR pwszFileName, IN BOOL fMachineKeyset, IN PKEY_CONTAINER_INFO pContInfo) { DWORD dwReturn = ERROR_INTERNAL_ERROR; BYTE *pbProtectedSigExportFlag = NULL; BYTE *pbProtectedExchExportFlag = NULL; KEY_EXPORTABILITY_LENS ExportabilityLens; BYTE *pb = NULL; DWORD cb; LPWSTR pwszFilePath = NULL; HANDLE hFile = 0; DWORD dwBytesWritten; BOOL fIsLocalSystem = FALSE; DWORD dwSts; BOOL fSts; memset(&ExportabilityLens, 0, sizeof(ExportabilityLens)); // protect the signature exportability flag if necessary if (pContInfo->ContLens.cbSigPub && pContInfo->ContLens.cbSigEncPriv) { dwSts = ProtectExportabilityFlag(pContInfo->fSigExportable, fMachineKeyset, &pbProtectedSigExportFlag, &ExportabilityLens.cbSigExportability); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } } // protect the key exchange exportability flag if necessary if (pContInfo->ContLens.cbExchPub && pContInfo->ContLens.cbExchEncPriv) { dwSts = ProtectExportabilityFlag(pContInfo->fExchExportable, fMachineKeyset, &pbProtectedExchExportFlag, &ExportabilityLens.cbExchExportability); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } } pContInfo->ContLens.cbName = strlen(pContInfo->pszUserName) + sizeof(CHAR); // calculate the buffer length required for the container info cb = pContInfo->ContLens.cbSigPub + pContInfo->ContLens.cbSigEncPriv + pContInfo->ContLens.cbExchPub + pContInfo->ContLens.cbExchEncPriv + ExportabilityLens.cbSigExportability + ExportabilityLens.cbExchExportability + pContInfo->ContLens.cbName + pContInfo->ContLens.cbRandom + sizeof(KEY_EXPORTABILITY_LENS) + sizeof(KEY_CONTAINER_INFO) + sizeof(DWORD); pb = (BYTE*)ContInfoAlloc(cb); if (NULL == pb) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } // copy the length information pContInfo->dwVersion = KEY_CONTAINER_FILE_FORMAT_VER; memcpy(pb, &pContInfo->dwVersion, sizeof(DWORD)); cb = sizeof(DWORD); memcpy(pb + cb, &pContInfo->ContLens, sizeof(KEY_CONTAINER_LENS)); cb += sizeof(KEY_CONTAINER_LENS); if (KEY_CONTAINER_FILE_FORMAT_VER != pContInfo->dwVersion) { dwReturn = (DWORD)NTE_KEYSET_ENTRY_BAD; goto ErrorExit; } memcpy(pb + cb, &ExportabilityLens, sizeof(KEY_EXPORTABILITY_LENS)); cb += sizeof(KEY_EXPORTABILITY_LENS); // copy the name of the container to the file memcpy(pb + cb, pContInfo->pszUserName, pContInfo->ContLens.cbName); cb += pContInfo->ContLens.cbName; // copy the random seed to the file memcpy(pb + cb, pContInfo->pbRandom, pContInfo->ContLens.cbRandom); cb += pContInfo->ContLens.cbRandom; // copy the signature key info to the file if (pContInfo->ContLens.cbSigPub || pContInfo->ContLens.cbSigEncPriv) { memcpy(pb + cb, pContInfo->pbSigPub, pContInfo->ContLens.cbSigPub); cb += pContInfo->ContLens.cbSigPub; memcpy(pb + cb, pContInfo->pbSigEncPriv, pContInfo->ContLens.cbSigEncPriv); cb += pContInfo->ContLens.cbSigEncPriv; // write the exportability info for the sig key memcpy(pb + cb, pbProtectedSigExportFlag, ExportabilityLens.cbSigExportability); cb += ExportabilityLens.cbSigExportability; } // get the signature key info out of the file if (pContInfo->ContLens.cbExchPub || pContInfo->ContLens.cbExchEncPriv) { memcpy(pb + cb, pContInfo->pbExchPub, pContInfo->ContLens.cbExchPub); cb += pContInfo->ContLens.cbExchPub; memcpy(pb + cb, pContInfo->pbExchEncPriv, pContInfo->ContLens.cbExchEncPriv); cb += pContInfo->ContLens.cbExchEncPriv; // write the exportability info for the sig key memcpy(pb + cb, pbProtectedExchExportFlag, ExportabilityLens.cbExchExportability); cb += ExportabilityLens.cbExchExportability; } // get the correct storage area (directory) dwSts = GetUserStorageArea(dwProvType, fMachineKeyset, FALSE, &fIsLocalSystem, &pwszFilePath); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // open the file to write the information to dwSts = OpenFileInStorageArea(fMachineKeyset, GENERIC_WRITE, pwszFilePath, pwszFileName, &hFile); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; // NTE_FAIL goto ErrorExit; } fSts = WriteFile(hFile, pb, cb, &dwBytesWritten, NULL); if (!fSts) { dwReturn = GetLastError(); // NTE_FAIL goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pwszFilePath) ContInfoFree(pwszFilePath); if (NULL != pbProtectedSigExportFlag) ContInfoFree(pbProtectedSigExportFlag); if (NULL != pbProtectedExchExportFlag) ContInfoFree(pbProtectedExchExportFlag); if (NULL != pb) ContInfoFree(pb); if (NULL != hFile) CloseHandle(hFile); return dwReturn; } /*static*/ DWORD DeleteKeyContainer( IN LPWSTR pwszFilePath, IN LPSTR pszContainer) { DWORD dwReturn = ERROR_INTERNAL_ERROR; LPWSTR pwszFileName = NULL; WCHAR rgwchNewFileName[80]; BOOL fRetryWithHashedName = TRUE; DWORD cch = 0; DWORD dwSts; memset(rgwchNewFileName, 0, sizeof(rgwchNewFileName)); // first try with the container name which was passed in, if this fails if (69 == strlen(pszContainer)) { // convert to UNICODE pszContainer -> pwszFileName cch = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszContainer, -1, NULL, cch); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } pwszFileName = ContInfoAlloc((cch + 1) * sizeof(WCHAR)); if (NULL == pwszFileName) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } cch = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszContainer, -1, pwszFileName, cch); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } dwSts = DeleteFileInStorageArea(pwszFilePath, pwszFileName); if (ERROR_SUCCESS == dwSts) fRetryWithHashedName = FALSE; } // then try with hash of container name and the machine GUID appended if (fRetryWithHashedName) { dwSts = AddMachineGuidToContainerName(pszContainer, rgwchNewFileName); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = DeleteFileInStorageArea(pwszFilePath, rgwchNewFileName); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pwszFileName) ContInfoFree(pwszFileName); return dwReturn; } DWORD DeleteContainerInfo( IN DWORD dwProvType, IN LPSTR pszContainer, IN BOOL fMachineKeyset) { DWORD dwReturn = ERROR_INTERNAL_ERROR; LPWSTR pwszFilePath = NULL; HANDLE hFile = INVALID_HANDLE_VALUE; BOOL fIsLocalSystem = FALSE; WCHAR rgwchNewFileName[80]; BOOL fDeleted = FALSE; DWORD dwSts; // get the correct storage area (directory) dwSts = GetUserStorageArea(dwProvType, fMachineKeyset, FALSE, &fIsLocalSystem, &pwszFilePath); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = DeleteKeyContainer(pwszFilePath, pszContainer); if (ERROR_SUCCESS != dwSts) { // for migration of machine keys from system to All Users\App Data if (fMachineKeyset) { ContInfoFree(pwszFilePath); pwszFilePath = NULL; dwSts = GetUserStorageArea(dwProvType, fMachineKeyset, TRUE, &fIsLocalSystem, &pwszFilePath); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = DeleteKeyContainer(pwszFilePath, pszContainer); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } else { fDeleted = TRUE; } } } else { fDeleted = TRUE; } // there may be other keys created with the same container name on // different machines and these also need to be deleted for (;;) { memset(rgwchNewFileName, 0, sizeof(rgwchNewFileName)); dwSts = FindClosestFileInStorageArea(pwszFilePath, pszContainer, rgwchNewFileName, &hFile); if (ERROR_SUCCESS != dwSts) break; CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; dwSts = DeleteFileInStorageArea(pwszFilePath, rgwchNewFileName); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } else fDeleted = TRUE; } if (!fDeleted) { dwReturn = (DWORD)NTE_BAD_KEYSET; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (INVALID_HANDLE_VALUE != hFile) CloseHandle(hFile); if (NULL != pwszFilePath) ContInfoFree(pwszFilePath); return dwReturn; } /*static*/ DWORD ReadContainerNameFromFile( IN BOOL fMachineKeyset, IN LPWSTR pwszFileName, IN LPWSTR pwszFilePath, OUT LPSTR pszNextContainer, IN OUT DWORD *pcbNextContainer) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HANDLE hMap = NULL; BYTE *pbFile = NULL; HANDLE hFile = INVALID_HANDLE_VALUE; DWORD cbFile = 0; DWORD *pdwVersion; PKEY_CONTAINER_LENS pContLens; DWORD dwSts; // open the file dwSts = OpenFileInStorageArea(fMachineKeyset, GENERIC_READ, pwszFilePath, pwszFileName, &hFile); if (ERROR_SUCCESS != dwSts) { if (ERROR_FILE_NOT_FOUND == dwSts) dwReturn = (DWORD)NTE_BAD_KEYSET; else dwReturn = dwSts; goto ErrorExit; } cbFile = GetFileSize(hFile, NULL); if ((DWORD)(-1) == cbFile) { dwReturn = GetLastError(); goto ErrorExit; } if ((sizeof(DWORD) + sizeof(KEY_CONTAINER_LENS)) > cbFile) { dwReturn = (DWORD)NTE_KEYSET_ENTRY_BAD; goto ErrorExit; } hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if (NULL == hMap) { dwReturn = GetLastError(); goto ErrorExit; } pbFile = (BYTE*)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0 ); if (NULL == pbFile) { dwReturn = GetLastError(); goto ErrorExit; } // get the length information out of the file pdwVersion = (DWORD*)pbFile; if (KEY_CONTAINER_FILE_FORMAT_VER != *pdwVersion) { dwReturn = (DWORD)NTE_KEYSET_ENTRY_BAD; goto ErrorExit; } pContLens = (PKEY_CONTAINER_LENS)(pbFile + sizeof(DWORD)); if (NULL == pszNextContainer) { *pcbNextContainer = MAX_PATH + 1; dwReturn = ERROR_SUCCESS; // Just tell them the length. goto ErrorExit; } if (*pcbNextContainer < pContLens->cbName) { *pcbNextContainer = MAX_PATH + 1; } else if ((sizeof(DWORD) + sizeof(KEY_CONTAINER_LENS) + sizeof(KEY_EXPORTABILITY_LENS) + pContLens->cbName) > cbFile) { dwReturn = (DWORD)NTE_KEYSET_ENTRY_BAD; goto ErrorExit; } else { // get the container name memcpy(pszNextContainer, pbFile + sizeof(DWORD) + sizeof(KEY_CONTAINER_LENS) + sizeof(KEY_EXPORTABILITY_LENS), pContLens->cbName); // *pcbNextContainer = pContLens->cbName; } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pbFile) UnmapViewOfFile(pbFile); if (NULL != hMap) CloseHandle(hMap); if (INVALID_HANDLE_VALUE != hFile) CloseHandle(hFile); return dwReturn; } DWORD GetUniqueContainerName( IN KEY_CONTAINER_INFO *pContInfo, OUT BYTE *pbData, OUT DWORD *pcbData) { DWORD dwReturn = ERROR_INTERNAL_ERROR; LPSTR pszUniqueContainer = NULL; DWORD cch; cch = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, pContInfo->rgwszFileName, -1, NULL, 0, NULL, NULL); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } pszUniqueContainer = (LPSTR)ContInfoAlloc((cch + 1) * sizeof(WCHAR)); if (NULL == pszUniqueContainer) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } cch = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, pContInfo->rgwszFileName, -1, pszUniqueContainer, cch, NULL, NULL); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } if (pbData == NULL) { *pcbData = strlen(pszUniqueContainer) + 1; } else if (*pcbData < (strlen(pszUniqueContainer) + 1)) { *pcbData = strlen(pszUniqueContainer) + 1; dwReturn = ERROR_MORE_DATA; goto ErrorExit; } else { *pcbData = strlen(pszUniqueContainer) + 1; strcpy((LPSTR)pbData, pszUniqueContainer); } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pszUniqueContainer) ContInfoFree(pszUniqueContainer); return dwReturn; } // // Function : MachineGuidInFilename // // Description : Check if the given Machine GUID is in the given filename. // Returns TRUE if it is FALSE if it is not. // /*static*/ BOOL MachineGuidInFilename( LPWSTR pwszFileName, LPWSTR pwszMachineGuid) { DWORD cbFileName; BOOL fRet = FALSE; cbFileName = wcslen(pwszFileName); // make sure the length of the filename is longer than the GUID if (cbFileName >= (DWORD)wcslen(pwszMachineGuid)) { // compare the GUID with the last 36 characters of the file name if (0 == memcmp(pwszMachineGuid, &(pwszFileName[cbFileName - 36]), 36 * sizeof(WCHAR))) fRet = TRUE; } return fRet; } DWORD GetNextContainer( IN DWORD dwProvType, IN BOOL fMachineKeyset, IN DWORD dwFlags, OUT LPSTR pszNextContainer, IN OUT DWORD *pcbNextContainer, IN OUT HANDLE *phFind) { DWORD dwReturn = ERROR_INTERNAL_ERROR; LPWSTR pwszFilePath = NULL; LPWSTR pwszEnumFilePath = NULL; WIN32_FIND_DATAW FindData; BOOL fIsLocalSystem = FALSE; LPWSTR pwszMachineGuid = NULL; DWORD dwSts; memset(&FindData, 0, sizeof(FindData)); // get the correct storage area (directory) dwSts = GetUserStorageArea(dwProvType, fMachineKeyset, FALSE, &fIsLocalSystem, &pwszFilePath); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } if (dwFlags & CRYPT_FIRST) { *phFind = INVALID_HANDLE_VALUE; pwszEnumFilePath = (LPWSTR)ContInfoAlloc((wcslen(pwszFilePath) + 2) * sizeof(WCHAR)); if (NULL == pwszEnumFilePath) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } wcscpy(pwszEnumFilePath, pwszFilePath); pwszEnumFilePath[wcslen(pwszFilePath)] = '*'; *phFind = FindFirstFileExW( pwszEnumFilePath, FindExInfoStandard, &FindData, FindExSearchNameMatch, NULL, 0); if (INVALID_HANDLE_VALUE == *phFind) { dwReturn = ERROR_NO_MORE_ITEMS; goto ErrorExit; } // skip past . and .. if (!FindNextFileW(*phFind, &FindData)) { dwSts = GetLastError(); if (ERROR_NO_MORE_FILES == dwSts) dwReturn = ERROR_NO_MORE_ITEMS; else dwReturn = dwSts; goto ErrorExit; } if (!FindNextFileW(*phFind, &FindData)) { dwSts = GetLastError(); if (ERROR_NO_MORE_FILES == dwSts) dwReturn = ERROR_NO_MORE_ITEMS; else dwReturn = dwSts; goto ErrorExit; } } else { GetNextFile: { if (!FindNextFileW(*phFind, &FindData)) { dwSts = GetLastError(); if (ERROR_NO_MORE_FILES == dwSts) dwReturn = ERROR_NO_MORE_ITEMS; else dwReturn = dwSts; goto ErrorExit; } } } // if this is a machine keyset or this is local system then we want to // ignore key containers not matching the current machine GUID if (fMachineKeyset || fIsLocalSystem) { // get the GUID of the machine dwSts = GetMachineGUID(&pwszMachineGuid); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } if (NULL == pwszMachineGuid) { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } // check if the file name has the machine GUID while (!MachineGuidInFilename(FindData.cFileName, pwszMachineGuid)) { if (!FindNextFileW(*phFind, &FindData)) { dwSts = GetLastError(); if (ERROR_NO_MORE_FILES == dwSts) dwReturn = ERROR_NO_MORE_ITEMS; else dwReturn = dwSts; goto ErrorExit; } } } // return the container name, in order to do that we need to open the // file and pull out the container name // // we try to get the next file if failure occurs in case the file was // deleted since the FindNextFile // dwSts = ReadContainerNameFromFile(fMachineKeyset, FindData.cFileName, pwszFilePath, pszNextContainer, pcbNextContainer); if (ERROR_SUCCESS != dwSts) goto GetNextFile; else if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pwszMachineGuid) ContInfoFree(pwszMachineGuid); if (NULL != pwszFilePath) ContInfoFree(pwszFilePath); if (NULL != pwszEnumFilePath) ContInfoFree(pwszEnumFilePath); return dwReturn; } // Converts to UNICODE and uses RegOpenKeyExW DWORD MyRegOpenKeyEx( IN HKEY hRegKey, IN LPSTR pszKeyName, IN DWORD dwReserved, IN REGSAM SAMDesired, OUT HKEY *phNewRegKey) { DWORD dwReturn = ERROR_INTERNAL_ERROR; WCHAR rgwchFastBuff[(MAX_PATH + 1) * 2]; LPWSTR pwsz = NULL; BOOL fAlloced = FALSE; DWORD cch; DWORD dwSts; memset(rgwchFastBuff, 0, sizeof(rgwchFastBuff)); // convert reg key name to unicode cch = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszKeyName, -1, NULL, 0); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } if ((cch + 1) > ((MAX_PATH + 1) * 2)) { pwsz = ContInfoAlloc((cch + 1) * sizeof(WCHAR)); if (NULL == pwsz) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } fAlloced = TRUE; } else { pwsz = rgwchFastBuff; } cch = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszKeyName, -1, pwsz, cch); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } dwSts = RegOpenKeyExW(hRegKey, pwsz, dwReserved, SAMDesired, phNewRegKey); if (ERROR_SUCCESS != dwSts) { if (ERROR_FILE_NOT_FOUND == dwSts) dwReturn = (DWORD)NTE_BAD_KEYSET; else dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (fAlloced && (NULL != pwsz)) ContInfoFree(pwsz); return dwReturn; } // Converts to UNICODE and uses RegDeleteKeyW DWORD MyRegDeleteKey( IN HKEY hRegKey, IN LPSTR pszKeyName) { DWORD dwReturn = ERROR_INTERNAL_ERROR; WCHAR rgwchFastBuff[(MAX_PATH + 1) * 2]; LPWSTR pwsz = NULL; BOOL fAlloced = FALSE; DWORD cch; DWORD dwSts; memset(rgwchFastBuff, 0, sizeof(rgwchFastBuff)); // convert reg key name to unicode cch = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszKeyName, -1, NULL, 0); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } if ((cch + 1) > ((MAX_PATH + 1) * 2)) { pwsz = ContInfoAlloc((cch + 1) * sizeof(WCHAR)); if (NULL == pwsz) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } fAlloced = TRUE; } else { pwsz = rgwchFastBuff; } cch = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszKeyName, -1, pwsz, cch); if (0 == cch) { dwReturn = GetLastError(); goto ErrorExit; } dwSts = RegDeleteKeyW(hRegKey, pwsz); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (fAlloced && (NULL != pwsz)) ContInfoFree(pwsz); return dwReturn; } DWORD AllocAndSetLocationBuff( BOOL fMachineKeySet, DWORD dwProvType, CONST char *pszUserID, HKEY *phTopRegKey, TCHAR **ppszLocBuff, BOOL fUserKeys, BOOL *pfLeaveOldKeys, LPDWORD pcbBuff) { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD dwSts; CHAR szSID[MAX_PATH]; DWORD cbSID = MAX_PATH; DWORD cbLocBuff = 0; DWORD cbTmp = 0; CHAR *pszTmp; BOOL fIsThreadLocalSystem = FALSE; if (fMachineKeySet) { *phTopRegKey = HKEY_LOCAL_MACHINE; if ((PROV_RSA_FULL == dwProvType) || (PROV_RSA_SCHANNEL == dwProvType) || (PROV_RSA_AES == dwProvType)) { cbTmp = RSA_MACH_REG_KEY_LOC_LEN; pszTmp = RSA_MACH_REG_KEY_LOC; } else if ((PROV_DSS == dwProvType) || (PROV_DSS_DH == dwProvType) || (PROV_DH_SCHANNEL == dwProvType)) { cbTmp = DSS_MACH_REG_KEY_LOC_LEN; pszTmp = DSS_MACH_REG_KEY_LOC; } else { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } } else { if ((PROV_RSA_FULL == dwProvType) || (PROV_RSA_SCHANNEL == dwProvType) || (PROV_RSA_AES == dwProvType)) { cbTmp = RSA_REG_KEY_LOC_LEN; pszTmp = RSA_REG_KEY_LOC; } else if ((PROV_DSS == dwProvType) || (PROV_DSS_DH == dwProvType) || (PROV_DH_SCHANNEL == dwProvType)) { cbTmp = DSS_REG_KEY_LOC_LEN; pszTmp = DSS_REG_KEY_LOC; } else { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } if (FIsWinNT()) { dwSts = IsThreadLocalSystem(&fIsThreadLocalSystem); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = GetUserTextualSidA(szSID, &cbSID); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; // NTE_BAD_KEYSET goto ErrorExit; } // this checks to see if the key to the current user may be opened if (!fMachineKeySet) { dwSts = RegOpenKeyEx(HKEY_USERS, szSID, 0, // dwOptions KEY_READ, phTopRegKey); if (ERROR_SUCCESS != dwSts) { // // if that failed, try HKEY_USERS\.Default (for services on NT). // cbSID = strlen(".DEFAULT") + 1; strcpy(szSID, ".DEFAULT"); dwSts = RegOpenKeyEx(HKEY_USERS, szSID, 0, // dwOptions KEY_READ, phTopRegKey); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } *pfLeaveOldKeys = TRUE; } } } else { *phTopRegKey = HKEY_CURRENT_USER; } } if (!fUserKeys) cbLocBuff = strlen(pszUserID); cbLocBuff = cbLocBuff + cbTmp + 2; *ppszLocBuff = (CHAR*)ContInfoAlloc(cbLocBuff); if (NULL == *ppszLocBuff) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } // Copy the location of the key groups, append the userID to it memcpy(*ppszLocBuff, pszTmp, cbTmp); if (!fUserKeys) { (*ppszLocBuff)[cbTmp-1] = '\\'; strcpy(&(*ppszLocBuff)[cbTmp], pszUserID); } if (NULL != pcbBuff) *pcbBuff = cbLocBuff; dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } // // Enumerates the old machine keys in the file system // keys were in this location in Beta 2 and Beta 3 of NT5/Win2K // DWORD EnumOldMachineKeys( IN DWORD dwProvType, IN OUT PKEY_CONTAINER_INFO pContInfo) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATAW FindData; LPWSTR pwszUserStorageArea = NULL; LPWSTR pwszTmp = NULL; BOOL fIsLocalSystem; DWORD i; LPSTR pszNextContainer; DWORD cbNextContainer; LPSTR pszTmpContainer; DWORD dwSts; // first check if the enumeration table is already set up if (NULL != pContInfo->pchEnumOldMachKeyEntries) { dwReturn = ERROR_SUCCESS; // Nothing to do! goto ErrorExit; } memset(&FindData, 0, sizeof(FindData)); dwSts = GetUserStorageArea(dwProvType, TRUE, TRUE, &fIsLocalSystem, &pwszUserStorageArea); if (ERROR_SUCCESS != dwSts) { dwReturn = ERROR_NO_MORE_ITEMS; goto ErrorExit; } // last character is backslash, so strip that off pwszTmp = (LPWSTR)ContInfoAlloc((wcslen(pwszUserStorageArea) + 3) * sizeof(WCHAR)); if (NULL == pwszTmp) { dwSts = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } wcscpy(pwszTmp, pwszUserStorageArea); wcscat(pwszTmp, L"*"); // figure out how many files are in the directroy hFind = FindFirstFileExW(pwszTmp, FindExInfoStandard, &FindData, FindExSearchNameMatch, NULL, 0); if (INVALID_HANDLE_VALUE == hFind) { dwSts = GetLastError(); if (ERROR_FILE_NOT_FOUND == dwSts) dwReturn = ERROR_NO_MORE_ITEMS; else dwReturn = dwSts; goto ErrorExit; } // skip past . and .. if (!FindNextFileW(hFind, &FindData)) { dwSts = GetLastError(); if (ERROR_NO_MORE_FILES == dwSts) dwReturn = ERROR_NO_MORE_ITEMS; else dwReturn = dwSts; goto ErrorExit; } if (!FindNextFileW(hFind, &FindData)) { dwSts = GetLastError(); if (ERROR_NO_MORE_FILES == dwSts) dwReturn = ERROR_NO_MORE_ITEMS; else dwReturn = dwSts; goto ErrorExit; } for (i = 1; ; i++) { memset(&FindData, 0, sizeof(FindData)); if (!FindNextFileW(hFind, &FindData)) { dwSts = GetLastError(); if (ERROR_NO_MORE_FILES == dwSts) break; else if (ERROR_ACCESS_DENIED != dwSts) { dwReturn = dwSts; goto ErrorExit; } } } FindClose(hFind); hFind = INVALID_HANDLE_VALUE; pContInfo->cbOldMachKeyEntry = MAX_PATH + 1; pContInfo->dwiOldMachKeyEntry = 0; pContInfo->cMaxOldMachKeyEntry = i; // allocate space for the file names pContInfo->pchEnumOldMachKeyEntries = ContInfoAlloc(i * (MAX_PATH + 1)); if (NULL == pContInfo->pchEnumOldMachKeyEntries) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } // enumerate through getting file name from each memset(&FindData, 0, sizeof(FindData)); hFind = FindFirstFileExW(pwszTmp, FindExInfoStandard, &FindData, FindExSearchNameMatch, NULL, 0); if (INVALID_HANDLE_VALUE == hFind) { dwSts = GetLastError(); if (ERROR_FILE_NOT_FOUND == dwSts) dwReturn = ERROR_NO_MORE_ITEMS; else dwReturn = dwSts; goto ErrorExit; } // skip past . and .. if (!FindNextFileW(hFind, &FindData)) { dwSts = GetLastError(); if (ERROR_NO_MORE_FILES == dwSts) dwReturn = ERROR_NO_MORE_ITEMS; else dwReturn = dwSts; goto ErrorExit; } memset(&FindData, 0, sizeof(FindData)); if (!FindNextFileW(hFind, &FindData)) { dwSts = GetLastError(); if (ERROR_NO_MORE_FILES == dwSts) dwReturn = ERROR_NO_MORE_ITEMS; else dwReturn = dwSts; goto ErrorExit; } pszNextContainer = pContInfo->pchEnumOldMachKeyEntries; for (i = 0; i < pContInfo->cMaxOldMachKeyEntry; i++) { cbNextContainer = MAX_PATH; // return the container name, in order to do that we need to open the // file and pull out the container name dwSts = ReadContainerNameFromFile(TRUE, FindData.cFileName, pwszUserStorageArea, pszNextContainer, &cbNextContainer); if (ERROR_SUCCESS != dwSts) { pszTmpContainer = pszNextContainer; } else { pszTmpContainer = pszNextContainer + MAX_PATH + 1; } memset(&FindData, 0, sizeof(FindData)); if (!FindNextFileW(hFind, &FindData)) { dwSts = GetLastError(); if (ERROR_NO_MORE_FILES == dwSts) break; else if (ERROR_ACCESS_DENIED != dwSts) { dwReturn = dwSts; goto ErrorExit; } } pszNextContainer = pszTmpContainer; } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pwszTmp) ContInfoFree(pwszTmp); if (NULL != pwszUserStorageArea) ContInfoFree(pwszUserStorageArea); if (INVALID_HANDLE_VALUE != hFind) FindClose(hFind); return dwReturn; } DWORD GetNextEnumedOldMachKeys( IN PKEY_CONTAINER_INFO pContInfo, IN BOOL fMachineKeyset, OUT BYTE *pbData, OUT DWORD *pcbData) { DWORD dwReturn = ERROR_INTERNAL_ERROR; CHAR *psz; if (!fMachineKeyset) { dwReturn = ERROR_SUCCESS; // Nothing to do! goto ErrorExit; } if ((NULL == pContInfo->pchEnumOldMachKeyEntries) || (pContInfo->dwiOldMachKeyEntry >= pContInfo->cMaxOldMachKeyEntry)) { dwReturn = ERROR_NO_MORE_ITEMS; goto ErrorExit; } if (NULL == pbData) *pcbData = pContInfo->cbRegEntry; else if (*pcbData < pContInfo->cbRegEntry) { *pcbData = pContInfo->cbRegEntry; dwReturn = ERROR_MORE_DATA; goto ErrorExit; } else { psz = pContInfo->pchEnumOldMachKeyEntries + (pContInfo->dwiOldMachKeyEntry * pContInfo->cbOldMachKeyEntry); memcpy(pbData, psz, strlen(psz) + 1); pContInfo->dwiOldMachKeyEntry++; } dwReturn = ERROR_SUCCESS; ErrorExit: if (fMachineKeyset) *pcbData = pContInfo->cbOldMachKeyEntry; return dwReturn; } // // Enumerates the keys in the registry into a list of entries // DWORD EnumRegKeys( IN OUT PKEY_CONTAINER_INFO pContInfo, IN BOOL fMachineKeySet, IN DWORD dwProvType, OUT BYTE *pbData, IN OUT DWORD *pcbData) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HKEY hTopRegKey = 0; LPSTR pszBuff = NULL; DWORD cbBuff; BOOL fLeaveOldKeys = FALSE; HKEY hKey = 0; DWORD cSubKeys; DWORD cchMaxSubkey; DWORD cchMaxClass; DWORD cValues; DWORD cchMaxValueName; DWORD cbMaxValueData; DWORD cbSecurityDesriptor; FILETIME ftLastWriteTime; CHAR *psz; DWORD i; DWORD dwSts; // first check if the enumeration table is already set up if (NULL != pContInfo->pchEnumRegEntries) { dwReturn = ERROR_SUCCESS; // Nothing to do! goto ErrorExit; } // get the path to the registry keys dwSts = AllocAndSetLocationBuff(fMachineKeySet, dwProvType, pContInfo->pszUserName, &hTopRegKey, &pszBuff, TRUE, &fLeaveOldKeys, &cbBuff); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // open the reg key dwSts = MyRegOpenKeyEx(hTopRegKey, pszBuff, 0, KEY_READ, &hKey); if (ERROR_SUCCESS != dwSts) { if (NTE_BAD_KEYSET == dwSts) dwReturn = ERROR_NO_MORE_ITEMS; else dwReturn = dwSts; goto ErrorExit; } // find out info on old key containers dwSts = RegQueryInfoKey(hKey, NULL, NULL, NULL, &cSubKeys, &cchMaxSubkey, &cchMaxClass, &cValues, &cchMaxValueName, &cbMaxValueData, &cbSecurityDesriptor, &ftLastWriteTime); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // if there are old keys then enumerate them into a table if (0 != cSubKeys) { pContInfo->cMaxRegEntry = cSubKeys; pContInfo->cbRegEntry = cchMaxSubkey + 1; pContInfo->pchEnumRegEntries = ContInfoAlloc(pContInfo->cMaxRegEntry * pContInfo->cbRegEntry * sizeof(CHAR)); if (NULL == pContInfo->pchEnumRegEntries) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } for (i = 0; i < pContInfo->cMaxRegEntry; i++) { psz = pContInfo->pchEnumRegEntries + (i * pContInfo->cbRegEntry); dwSts = RegEnumKey(hKey, i, psz, pContInfo->cbRegEntry); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } } if (NULL == pbData) *pcbData = pContInfo->cbRegEntry; else if (*pcbData < pContInfo->cbRegEntry) { *pcbData = pContInfo->cbRegEntry; dwReturn = ERROR_MORE_DATA; goto ErrorExit; } else { *pcbData = pContInfo->cbRegEntry; // ?BUGBUG? What? // CopyMemory(pbData, pContInfo->pbRegEntry, pContInfo->cbRegEntry); } } dwReturn = ERROR_SUCCESS; ErrorExit: if ((NULL != hTopRegKey) && (HKEY_CURRENT_USER != hTopRegKey) && (HKEY_LOCAL_MACHINE != hTopRegKey)) { RegCloseKey(hTopRegKey); } if (NULL != pszBuff) ContInfoFree(pszBuff); if (NULL != hKey) RegCloseKey(hKey); return dwReturn; } DWORD GetNextEnumedRegKeys( IN PKEY_CONTAINER_INFO pContInfo, OUT BYTE *pbData, OUT DWORD *pcbData) { DWORD dwReturn = ERROR_INTERNAL_ERROR; CHAR *psz; if ((NULL == pContInfo->pchEnumRegEntries) || (pContInfo->dwiRegEntry >= pContInfo->cMaxRegEntry)) { dwReturn = ERROR_NO_MORE_ITEMS; goto ErrorExit; } if (NULL == pbData) *pcbData = pContInfo->cbRegEntry; else if (*pcbData < pContInfo->cbRegEntry) { *pcbData = pContInfo->cbRegEntry; dwReturn = ERROR_MORE_DATA; goto ErrorExit; } else { psz = pContInfo->pchEnumRegEntries + (pContInfo->dwiRegEntry * pContInfo->cbRegEntry); memcpy(pbData, psz, pContInfo->cbRegEntry); pContInfo->dwiRegEntry++; } dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } //+ =========================================================================== // // The function adjusts the token priviledges so that SACL information // may be gotten and then opens the indicated registry key. If the token // priviledges may be set then the reg key is opened anyway but the // flags field will not have the PRIVILEDGE_FOR_SACL value set. // //- ============================================================================ DWORD OpenRegKeyWithTokenPriviledges( IN HKEY hTopRegKey, IN LPSTR pszRegKey, OUT HKEY *phRegKey, OUT DWORD *pdwFlags) { DWORD dwReturn = ERROR_INTERNAL_ERROR; TOKEN_PRIVILEGES tp; TOKEN_PRIVILEGES tpPrevious; DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES); LUID luid; HANDLE hToken = 0; HKEY hRegKey = 0; BOOL fSts; BOOL fImpersonating = FALSE; BOOL fAdjusted = FALSE; DWORD dwAccessFlags = 0; DWORD dwSts; // check if there is a registry key to open dwSts = MyRegOpenKeyEx(hTopRegKey, pszRegKey, 0, KEY_ALL_ACCESS, &hRegKey); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } RegCloseKey(hRegKey); hRegKey = 0; // check if there is a thread token fSts = OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hToken); if (!fSts) { if (!ImpersonateSelf(SecurityImpersonation)) { dwReturn = GetLastError(); goto ErrorExit; } fImpersonating = TRUE; // get the process token fSts = OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hToken); } // set up the new priviledge state if (fSts) { memset(&tp, 0, sizeof(tp)); memset(&tpPrevious, 0, sizeof(tpPrevious)); fSts = LookupPrivilegeValueA(NULL, SE_SECURITY_NAME, &luid); if (fSts) { // // first pass. get current privilege setting // tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // adjust privilege fSts = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious); if (fSts && (ERROR_SUCCESS == GetLastError())) { fAdjusted = TRUE; *pdwFlags |= PRIVILEDGE_FOR_SACL; dwAccessFlags = ACCESS_SYSTEM_SECURITY; } } } // open the registry key dwSts = MyRegOpenKeyEx(hTopRegKey, pszRegKey, 0, KEY_ALL_ACCESS | dwAccessFlags, phRegKey); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: // now set the privilege back if necessary if (fAdjusted) { // adjust the priviledge and with the previous state fSts = AdjustTokenPrivileges(hToken, FALSE, &tpPrevious, sizeof(TOKEN_PRIVILEGES), NULL, NULL); } if (NULL != hToken) CloseHandle(hToken); if (fImpersonating) RevertToSelf(); return dwReturn; } //+ =========================================================================== // // The function adjusts the token priviledges so that SACL information // may be set on a key container. If the token priviledges may be set // indicated by the pUser->dwOldKeyFlags having the PRIVILEDGE_FOR_SACL value set. // value set then the token privilege is adjusted before the security // descriptor is set on the container. This is needed for the key // migration case when keys are being migrated from the registry to files. //- ============================================================================ DWORD SetSecurityOnContainerWithTokenPriviledges( IN DWORD dwOldKeyFlags, IN LPCWSTR wszFileName, IN DWORD dwProvType, IN DWORD fMachineKeyset, IN SECURITY_INFORMATION SecurityInformation, IN PSECURITY_DESCRIPTOR pSecurityDescriptor) { DWORD dwReturn = ERROR_INTERNAL_ERROR; TOKEN_PRIVILEGES tp; TOKEN_PRIVILEGES tpPrevious; DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES); LUID luid; HANDLE hToken = 0; BOOL fStatus; BOOL fImpersonating = FALSE; BOOL fAdjusted = FALSE; DWORD dwSts; if (dwOldKeyFlags & PRIVILEDGE_FOR_SACL) { // check if there is a thread token fStatus = OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hToken); if (!fStatus) { if (!ImpersonateSelf(SecurityImpersonation)) { dwReturn = GetLastError(); goto ErrorExit; } fImpersonating = TRUE; // get the process token fStatus = OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hToken); } // set up the new priviledge state if (fStatus) { memset(&tp, 0, sizeof(tp)); memset(&tpPrevious, 0, sizeof(tpPrevious)); fStatus = LookupPrivilegeValueA(NULL, SE_SECURITY_NAME, &luid); if (fStatus) { // // first pass. get current privilege setting // tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // adjust privilege fAdjusted = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious); } } } dwSts = SetSecurityOnContainer(wszFileName, dwProvType, fMachineKeyset, SecurityInformation, pSecurityDescriptor); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: // now set the privilege back if necessary // now set the privilege back if necessary if (dwOldKeyFlags & PRIVILEDGE_FOR_SACL) { if (fAdjusted) { // adjust the priviledge and with the previous state fStatus = AdjustTokenPrivileges(hToken, FALSE, &tpPrevious, sizeof(TOKEN_PRIVILEGES), NULL, NULL); } } if (NULL != hToken) CloseHandle(hToken); if (fImpersonating) RevertToSelf(); return dwReturn; } // Loops through the ACEs of an ACL and checks for special access bits // for registry keys and converts the access mask so generic access // bits are used /*static*/ DWORD CheckAndChangeAccessMasks( IN PACL pAcl) { DWORD dwReturn = ERROR_INTERNAL_ERROR; ACL_SIZE_INFORMATION AclSizeInfo; DWORD i; ACCESS_ALLOWED_ACE *pAce; ACCESS_MASK NewMask; memset(&AclSizeInfo, 0, sizeof(AclSizeInfo)); // get the number of ACEs in the ACL if (!GetAclInformation(pAcl, &AclSizeInfo, sizeof(AclSizeInfo), AclSizeInformation)) { dwReturn = GetLastError(); goto ErrorExit; } // loop through the ACEs checking and changing the access bits for (i = 0; i < AclSizeInfo.AceCount; i++) { if (!GetAce(pAcl, i, &pAce)) { dwReturn = GetLastError(); goto ErrorExit; } NewMask = 0; // check if the specific access bits are set, if so convert to generic if ((pAce->Mask & KEY_QUERY_VALUE) || (pAce->Mask & GENERIC_READ)) NewMask |= GENERIC_READ; if ((pAce->Mask & KEY_SET_VALUE) || (pAce->Mask & GENERIC_ALL) || (pAce->Mask & GENERIC_WRITE)) { NewMask |= GENERIC_ALL; } pAce->Mask = NewMask; } dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } // Converts a security descriptor from special access to generic access /*static*/ DWORD ConvertContainerSecurityDescriptor( IN PSECURITY_DESCRIPTOR pSecurityDescriptor, OUT PSECURITY_DESCRIPTOR *ppNewSD, OUT DWORD *pcbNewSD) { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD cbSD; SECURITY_DESCRIPTOR_CONTROL Control; DWORD dwRevision; PACL pDacl; BOOL fDACLPresent; BOOL fDaclDefaulted; PACL pSacl; BOOL fSACLPresent; BOOL fSaclDefaulted; DWORD dwSts; // ge the control on the security descriptor to check if self relative if (!GetSecurityDescriptorControl(pSecurityDescriptor, &Control, &dwRevision)) { dwReturn = GetLastError(); goto ErrorExit; } // get the length of the security descriptor and alloc space for a copy cbSD = GetSecurityDescriptorLength(pSecurityDescriptor); *ppNewSD =(PSECURITY_DESCRIPTOR)ContInfoAlloc(cbSD); if (NULL == *ppNewSD) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } if (SE_SELF_RELATIVE & Control) { // if the Security Descriptor is self relative then make a copy memcpy(*ppNewSD, pSecurityDescriptor, cbSD); } else { // if not self relative then make a self relative copy if (!MakeSelfRelativeSD(pSecurityDescriptor, *ppNewSD, &cbSD)) { dwReturn = GetLastError(); goto ErrorExit; } } // get the DACL out of the security descriptor if (!GetSecurityDescriptorDacl(*ppNewSD, &fDACLPresent, &pDacl, &fDaclDefaulted)) { dwReturn = GetLastError(); goto ErrorExit; } if (fDACLPresent && pDacl) { dwSts = CheckAndChangeAccessMasks(pDacl); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } } // get the SACL out of the security descriptor if (!GetSecurityDescriptorSacl(*ppNewSD, &fSACLPresent, &pSacl, &fSaclDefaulted)) { dwReturn = GetLastError(); goto ErrorExit; } if (fSACLPresent && pSacl) { dwSts = CheckAndChangeAccessMasks(pSacl); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } } *pcbNewSD = cbSD; dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } DWORD SetSecurityOnContainer( IN LPCWSTR wszFileName, IN DWORD dwProvType, IN DWORD fMachineKeyset, IN SECURITY_INFORMATION SecurityInformation, IN PSECURITY_DESCRIPTOR pSecurityDescriptor) { DWORD dwReturn = ERROR_INTERNAL_ERROR; PSECURITY_DESCRIPTOR pSD = NULL; DWORD cbSD; LPWSTR wszFilePath = NULL; LPWSTR wszUserStorageArea = NULL; DWORD cbUserStorageArea; DWORD cbFileName; BOOL fIsLocalSystem = FALSE; DWORD dwSts; dwSts = ConvertContainerSecurityDescriptor(pSecurityDescriptor, &pSD, &cbSD); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // get the correct storage area (directory) dwSts = GetUserStorageArea(dwProvType, fMachineKeyset, FALSE, &fIsLocalSystem, &wszUserStorageArea); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } cbUserStorageArea = wcslen( wszUserStorageArea ) * sizeof(WCHAR); cbFileName = wcslen( wszFileName ) * sizeof(WCHAR); wszFilePath = (LPWSTR)ContInfoAlloc(cbUserStorageArea + cbFileName + sizeof(WCHAR)); if (wszFilePath == NULL) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } CopyMemory((BYTE*)wszFilePath, (BYTE*)wszUserStorageArea, cbUserStorageArea); CopyMemory((LPBYTE)wszFilePath+cbUserStorageArea, wszFileName, cbFileName + sizeof(WCHAR)); if (!SetFileSecurityW(wszFilePath, SecurityInformation, pSD)) { dwReturn = GetLastError(); goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pSD) ContInfoFree(pSD); if (NULL != wszUserStorageArea) ContInfoFree(wszUserStorageArea); if (NULL != wszFilePath) ContInfoFree(wszFilePath); return dwReturn; } DWORD GetSecurityOnContainer( IN LPCWSTR wszFileName, IN DWORD dwProvType, IN DWORD fMachineKeyset, IN SECURITY_INFORMATION RequestedInformation, OUT PSECURITY_DESCRIPTOR pSecurityDescriptor, IN OUT DWORD *pcbSecurityDescriptor) { DWORD dwReturn = ERROR_INTERNAL_ERROR; LPWSTR wszFilePath = NULL; LPWSTR wszUserStorageArea = NULL; DWORD cbUserStorageArea; DWORD cbFileName; PSECURITY_DESCRIPTOR pSD = NULL; DWORD cbSD; PSECURITY_DESCRIPTOR pNewSD = NULL; DWORD cbNewSD; BOOL fIsLocalSystem = FALSE; DWORD dwSts; // get the correct storage area (directory) dwSts = GetUserStorageArea(dwProvType, fMachineKeyset, FALSE, &fIsLocalSystem, &wszUserStorageArea); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } cbUserStorageArea = wcslen( wszUserStorageArea ) * sizeof(WCHAR); cbFileName = wcslen( wszFileName ) * sizeof(WCHAR); wszFilePath = (LPWSTR)ContInfoAlloc(cbUserStorageArea + cbFileName + sizeof(WCHAR)); if (wszFilePath == NULL) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } CopyMemory(wszFilePath, wszUserStorageArea, cbUserStorageArea); CopyMemory((LPBYTE)wszFilePath+cbUserStorageArea, wszFileName, cbFileName + sizeof(WCHAR)); // get the security descriptor on the file cbSD = sizeof(cbSD); pSD = &cbSD; if (!GetFileSecurityW(wszFilePath, RequestedInformation, pSD, cbSD, &cbSD)) { dwSts = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER != dwSts) { dwReturn = dwSts; pSD = NULL; goto ErrorExit; } } pSD = (PSECURITY_DESCRIPTOR)ContInfoAlloc(cbSD); if (NULL == pSD) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } if (!GetFileSecurityW(wszFilePath, RequestedInformation, pSD, cbSD, &cbSD)) { dwReturn = GetLastError(); goto ErrorExit; } // convert the security descriptor from specific to generic dwSts = ConvertContainerSecurityDescriptor(pSD, &pNewSD, &cbNewSD); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } if (NULL == pSecurityDescriptor) *pcbSecurityDescriptor = cbNewSD; else if (*pcbSecurityDescriptor < cbNewSD) { *pcbSecurityDescriptor = cbNewSD; dwReturn = ERROR_MORE_DATA; goto ErrorExit; } else { *pcbSecurityDescriptor = cbNewSD; memcpy(pSecurityDescriptor, pNewSD, *pcbSecurityDescriptor); } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pNewSD) ContInfoFree(pNewSD); if (NULL != pSD) ContInfoFree(pSD); if (NULL != wszUserStorageArea) ContInfoFree(wszUserStorageArea); if (NULL != wszFilePath) ContInfoFree(wszFilePath); return dwReturn; } // // Function : FreeOffloadInfo // // Description : The function takes a pointer to Offload Information as the // first parameter of the call. The function frees the // information. // void FreeOffloadInfo( IN OUT PEXPO_OFFLOAD_STRUCT pOffloadInfo) { if (NULL != pOffloadInfo) { if (NULL != pOffloadInfo->hInst) FreeLibrary(pOffloadInfo->hInst); ContInfoFree(pOffloadInfo); } } // // Function : InitExpOffloadInfo // // Description : The function takes a pointer to Offload Information as the // first parameter of the call. The function checks in the // registry to see if an offload module has been registered. // If a module is registered then it loads the module // and gets the OffloadModExpo function pointer. // BOOL InitExpOffloadInfo( IN OUT PEXPO_OFFLOAD_STRUCT *ppOffloadInfo) { BYTE rgbModule[MAX_PATH + 1]; BYTE *pbModule = NULL; DWORD cbModule; BOOL fAlloc = FALSE; PEXPO_OFFLOAD_STRUCT pTmpOffloadInfo = NULL; HKEY hOffloadRegKey = 0; DWORD dwSts; BOOL fRet = FALSE; // wrap with try/except __try { // check for registration of an offload module dwSts = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\Offload", 0, // dwOptions KEY_READ, &hOffloadRegKey); if (ERROR_SUCCESS != dwSts) goto ErrorExit; // get the name of the offload module cbModule = sizeof(rgbModule); dwSts = RegQueryValueEx(hOffloadRegKey, EXPO_OFFLOAD_REG_VALUE, 0, NULL, rgbModule, &cbModule); if (ERROR_SUCCESS != dwSts) { if (ERROR_MORE_DATA == dwSts) { pbModule = (BYTE*)ContInfoAlloc(cbModule); if (NULL == pbModule) goto ErrorExit; fAlloc = TRUE; dwSts = RegQueryValueEx(HKEY_LOCAL_MACHINE, EXPO_OFFLOAD_REG_VALUE, 0, NULL, pbModule, &cbModule); if (ERROR_SUCCESS != dwSts) goto ErrorExit; } else goto ErrorExit; } else pbModule = rgbModule; // alloc space for the offload info pTmpOffloadInfo = (PEXPO_OFFLOAD_STRUCT)ContInfoAlloc(sizeof(EXPO_OFFLOAD_STRUCT)); if (NULL == pTmpOffloadInfo) goto ErrorExit; pTmpOffloadInfo->dwVersion = sizeof(EXPO_OFFLOAD_STRUCT); // load the module and get the function pointer pTmpOffloadInfo->hInst = LoadLibraryEx((LPTSTR)pbModule, NULL, 0); if (NULL == pTmpOffloadInfo->hInst) goto ErrorExit; pTmpOffloadInfo->pExpoFunc = GetProcAddress(pTmpOffloadInfo->hInst, EXPO_OFFLOAD_FUNC_NAME); if (NULL == pTmpOffloadInfo->pExpoFunc) goto ErrorExit; *ppOffloadInfo = pTmpOffloadInfo; fRet = TRUE; } __except (EXCEPTION_EXECUTE_HANDLER) { goto ErrorExit; } ErrorExit: if (NULL != hOffloadRegKey) RegCloseKey(hOffloadRegKey); if (fAlloc && (NULL != pbModule)) ContInfoFree(pbModule); if (!fRet) FreeOffloadInfo(pTmpOffloadInfo); return fRet; } // // Function : ModularExpOffload // // Description : This function does the offloading of modular exponentiation. // The function takes a pointer to Offload Information as the // first parameter of the call. If this pointer is not NULL // then the function will use this module and call the function. // The exponentiation with MOD function will implement // Y^X MOD P where Y is the buffer pbBase, X is the buffer // pbExpo and P is the buffer pbModulus. The length of the // buffer pbExpo is cbExpo and the length of pbBase and // pbModulus is cbModulus. The resulting value is output // in the pbResult buffer and has length cbModulus. // The pReserved and dwFlags parameters are currently ignored. // If any of these functions fail then the function fails and // returns FALSE. If successful then the function returns // TRUE. If the function fails then most likely the caller // should fall back to using hard linked modular exponentiation. // BOOL ModularExpOffload( IN PEXPO_OFFLOAD_STRUCT pOffloadInfo, IN BYTE *pbBase, IN BYTE *pbExpo, IN DWORD cbExpo, IN BYTE *pbModulus, IN DWORD cbModulus, OUT BYTE *pbResult, IN VOID *pReserved, IN DWORD dwFlags) { BOOL fRet = FALSE; // wrap with try/except __try { if (NULL == pOffloadInfo) goto ErrorExit; // call the offload module if (!pOffloadInfo->pExpoFunc(pbBase, pbExpo, cbExpo, pbModulus, cbModulus, pbResult, pReserved, dwFlags)) { goto ErrorExit; } fRet = TRUE; } __except (EXCEPTION_EXECUTE_HANDLER) { goto ErrorExit; } ErrorExit: return fRet; } // // The following section of code is for the loading and unloading of // unicode string resources from a resource DLL (csprc.dll). This // allows the resources to be localize even though the CSPs // themselves are signed. // #define MAX_STRING_RSC_SIZE 512 #define GLOBAL_STRING_BUFFERSIZE_INC 1000 #define GLOBAL_STRING_BUFFERSIZE 20000 // // Function : FetchString // // Description : This function gets the specified string resource from // the resource DLL, allocates memory for it and copies // the string into that memory. // /*static*/ DWORD FetchString( HMODULE hModule, // module to get string from DWORD dwResourceId, // resource identifier LPWSTR *ppString, // target buffer for string BYTE **ppStringBlock, // string buffer block DWORD *pdwBufferSize, // size of string buffer block DWORD *pdwRemainingBufferSize) // remaining size of string buffer block { DWORD dwReturn = ERROR_INTERNAL_ERROR; WCHAR szMessage[MAX_STRING_RSC_SIZE]; DWORD cchMessage; DWORD dwOldSize; DWORD dwNewSize; LPWSTR pNewStr; if (ppStringBlock == NULL || *ppStringBlock == NULL || ppString == NULL) { dwReturn = ERROR_INVALID_PARAMETER; goto ErrorExit; } cchMessage = LoadStringW(hModule, dwResourceId, szMessage, MAX_STRING_RSC_SIZE); if (0 == cchMessage) { dwReturn = GetLastError(); goto ErrorExit; } if (*pdwRemainingBufferSize < ((cchMessage + 1) * sizeof(WCHAR))) { // // realloc buffer and update size // dwOldSize = *pdwBufferSize; dwNewSize = dwOldSize + max(GLOBAL_STRING_BUFFERSIZE_INC, (((cchMessage + 1) * sizeof(WCHAR)) - *pdwRemainingBufferSize)); *ppStringBlock = (BYTE*)ContInfoReAlloc(*ppStringBlock, dwNewSize); if (NULL == *ppStringBlock) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } *pdwBufferSize = dwNewSize; *pdwRemainingBufferSize += dwNewSize - dwOldSize; } pNewStr = (LPWSTR)(*ppStringBlock + *pdwBufferSize - *pdwRemainingBufferSize); // only store the offset just in case a realloc of the entire // string buffer needs to be performed at a later time. *ppString = (LPWSTR)((BYTE *)pNewStr - (BYTE *)*ppStringBlock); wcscpy(pNewStr, szMessage); *pdwRemainingBufferSize -= (cchMessage + 1) * sizeof(WCHAR); dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } DWORD LoadStrings( void) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HMODULE hMod = 0; DWORD dwBufferSize; DWORD dwRemainingBufferSize; DWORD dwSts; if (NULL == l_pbStringBlock) { hMod = LoadLibraryEx("crypt32.dll", NULL, LOAD_LIBRARY_AS_DATAFILE); if (NULL == hMod) { dwReturn = GetLastError(); goto ErrorExit; } // // get size of all string resources, and then allocate a single block // of memory to contain all the strings. This way, we only have to // free one block and we benefit memory wise due to locality of reference. // dwBufferSize = dwRemainingBufferSize = GLOBAL_STRING_BUFFERSIZE; l_pbStringBlock = (BYTE*)ContInfoAlloc(dwBufferSize); if (NULL == l_pbStringBlock) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_RSA_SIG_DESCR, &g_Strings.pwszRSASigDescr, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_RSA_EXCH_DESCR, &g_Strings.pwszRSAExchDescr, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_IMPORT_SIMPLE, &g_Strings.pwszImportSimple, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_SIGNING_E, &g_Strings.pwszSignWExch, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_CREATE_RSA_SIG, &g_Strings.pwszCreateRSASig, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_CREATE_RSA_EXCH, &g_Strings.pwszCreateRSAExch, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_DSS_SIG_DESCR, &g_Strings.pwszDSSSigDescr, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_DSS_EXCH_DESCR, &g_Strings.pwszDHExchDescr, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_CREATE_DSS_SIG, &g_Strings.pwszCreateDSS, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_CREATE_DH_EXCH, &g_Strings.pwszCreateDH, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_IMPORT_E_PUB, &g_Strings.pwszImportDHPub, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_MIGR, &g_Strings.pwszMigrKeys, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_DELETE_SIG, &g_Strings.pwszDeleteSig, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_DELETE_KEYX, &g_Strings.pwszDeleteExch, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_DELETE_SIG_MIGR, &g_Strings.pwszDeleteMigrSig, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_DELETE_KEYX_MIGR, &g_Strings.pwszDeleteMigrExch, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_SIGNING_S, &g_Strings.pwszSigning, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_EXPORT_E_PRIV, &g_Strings.pwszExportPrivExch, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_EXPORT_S_PRIV, &g_Strings.pwszExportPrivSig, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_IMPORT_E_PRIV, &g_Strings.pwszImportPrivExch, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_IMPORT_S_PRIV, &g_Strings.pwszImportPrivSig, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = FetchString(hMod, IDS_CSP_AUDIT_CAPI_KEY, &g_Strings.pwszAuditCapiKey, &l_pbStringBlock, &dwBufferSize, &dwRemainingBufferSize); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // Fix up all the strings to be real pointers rather than offsets. // the reason that offsets are originally stored is because we may // need to reallocate the buffer that all the strings are stored in. // So offsets are stored so that the pointers for those strings in // the buffers don't become invalid. g_Strings.pwszRSASigDescr = (LPWSTR)(((ULONG_PTR) g_Strings.pwszRSASigDescr) + l_pbStringBlock); g_Strings.pwszRSAExchDescr = (LPWSTR)(((ULONG_PTR) g_Strings.pwszRSAExchDescr) + l_pbStringBlock); g_Strings.pwszImportSimple = (LPWSTR)(((ULONG_PTR) g_Strings.pwszImportSimple) + l_pbStringBlock); g_Strings.pwszSignWExch = (LPWSTR)(((ULONG_PTR) g_Strings.pwszSignWExch) + l_pbStringBlock); g_Strings.pwszCreateRSASig = (LPWSTR)(((ULONG_PTR) g_Strings.pwszCreateRSASig) + l_pbStringBlock); g_Strings.pwszCreateRSAExch = (LPWSTR)(((ULONG_PTR) g_Strings.pwszCreateRSAExch) + l_pbStringBlock); g_Strings.pwszDSSSigDescr = (LPWSTR)(((ULONG_PTR) g_Strings.pwszDSSSigDescr) + l_pbStringBlock); g_Strings.pwszDHExchDescr = (LPWSTR)(((ULONG_PTR) g_Strings.pwszDHExchDescr) + l_pbStringBlock); g_Strings.pwszCreateDSS = (LPWSTR)(((ULONG_PTR) g_Strings.pwszCreateDSS) + l_pbStringBlock); g_Strings.pwszCreateDH = (LPWSTR)(((ULONG_PTR) g_Strings.pwszCreateDH) + l_pbStringBlock); g_Strings.pwszImportDHPub = (LPWSTR)(((ULONG_PTR) g_Strings.pwszImportDHPub) + l_pbStringBlock); g_Strings.pwszMigrKeys = (LPWSTR)(((ULONG_PTR) g_Strings.pwszMigrKeys) + l_pbStringBlock); g_Strings.pwszDeleteSig = (LPWSTR)(((ULONG_PTR) g_Strings.pwszDeleteSig) + l_pbStringBlock); g_Strings.pwszDeleteExch = (LPWSTR)(((ULONG_PTR) g_Strings.pwszDeleteExch) + l_pbStringBlock); g_Strings.pwszDeleteMigrSig = (LPWSTR)(((ULONG_PTR) g_Strings.pwszDeleteMigrSig) + l_pbStringBlock); g_Strings.pwszDeleteMigrExch = (LPWSTR)(((ULONG_PTR) g_Strings.pwszDeleteMigrExch) + l_pbStringBlock); g_Strings.pwszSigning = (LPWSTR)(((ULONG_PTR) g_Strings.pwszSigning) + l_pbStringBlock); g_Strings.pwszExportPrivExch = (LPWSTR)(((ULONG_PTR) g_Strings.pwszExportPrivExch) + l_pbStringBlock); g_Strings.pwszExportPrivSig = (LPWSTR)(((ULONG_PTR) g_Strings.pwszExportPrivSig) + l_pbStringBlock); g_Strings.pwszImportPrivExch = (LPWSTR)(((ULONG_PTR) g_Strings.pwszImportPrivExch) + l_pbStringBlock); g_Strings.pwszImportPrivSig = (LPWSTR)(((ULONG_PTR) g_Strings.pwszImportPrivSig) + l_pbStringBlock); g_Strings.pwszAuditCapiKey = (LPWSTR)(((ULONG_PTR) g_Strings.pwszAuditCapiKey) + l_pbStringBlock); FreeLibrary(hMod); hMod = NULL; } return ERROR_SUCCESS; ErrorExit: if (NULL != l_pbStringBlock) { ContInfoFree(l_pbStringBlock); l_pbStringBlock = NULL; } if (hMod) FreeLibrary(hMod); return dwReturn; } void UnloadStrings( void) { if (NULL != l_pbStringBlock) { ContInfoFree(l_pbStringBlock); l_pbStringBlock = NULL; memset(&g_Strings, 0, sizeof(g_Strings)); } } #ifdef USE_HW_RNG #ifdef _M_IX86 // stuff for INTEL RNG usage // // Function : GetRNGDriverHandle // // Description : Gets the handle to the INTEL RNG driver if available, then // checks if the chipset supports the hardware RNG. If so // the previous driver handle is closed if necessary and the // new handle is assigned to the passed in parameter. // DWORD GetRNGDriverHandle( IN OUT HANDLE *phDriver) { DWORD dwReturn = ERROR_INTERNAL_ERROR; ISD_Capability ISD_Cap; //in/out for GetCapability DWORD dwBytesReturned; char szDeviceName[80] = ""; //Name of device HANDLE hDriver = INVALID_HANDLE_VALUE; //Driver handle BOOL fReturnCode; //Return code from IOCTL call memset(&ISD_Cap, 0, sizeof(ISD_Cap)); wsprintf(szDeviceName,"\\\\.\\"DRIVER_NAME); hDriver = CreateFileA(szDeviceName, FILE_SHARE_READ | FILE_SHARE_WRITE | GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hDriver) { dwReturn = GetLastError(); goto ErrorExit; } //Get RNG Enabled ISD_Cap.uiIndex = ISD_RNG_ENABLED; //Set input member fReturnCode = DeviceIoControl(hDriver, IOCTL_ISD_GetCapability, &ISD_Cap, sizeof(ISD_Cap), &ISD_Cap, sizeof(ISD_Cap), &dwBytesReturned, NULL); if (fReturnCode == FALSE || ISD_Cap.iStatus != ISD_EOK) { dwReturn = GetLastError(); goto ErrorExit; } // close the previous handle if already there if (INVALID_HANDLE_VALUE != *phDriver) CloseHandle(*phDriver); *phDriver = hDriver; return ERROR_SUCCESS; ErrorExit: if (INVALID_HANDLE_VALUE != hDriver) CloseHandle(hDriver); return dwReturn; } // // Function : CheckIfRNGAvailable // // Description : Checks if the INTEL RNG driver is available, if so then // checks if the chipset supports the hardware RNG. // DWORD CheckIfRNGAvailable( void) { HANDLE hDriver = INVALID_HANDLE_VALUE; //Driver handle DWORD dwSts; dwSts = GetRNGDriverHandle(&hDriver); if (ERROR_SUCCESS == dwSts) CloseHandle(hDriver); return dwSts; } // // Function : HWRNGGenRandom // // Description : Uses the passed in handle to the INTEL RNG driver // to fill the buffer with random bits. Actually uses // XOR to fill the buffer so that the passed in buffer // is also mixed in. // DWORD HWRNGGenRandom( IN HANDLE hRNGDriver, IN OUT BYTE *pbBuffer, IN DWORD dwLen) { DWORD dwReturn = ERROR_INTERNAL_ERROR; ISD_RandomNumber ISD_Random; //in/out for GetRandomNumber DWORD dwBytesReturned = 0; DWORD i; DWORD *pdw; BYTE *pb; BYTE *pbRand; BOOL fReturnCode; //Return code from IOCTL call memset(&ISD_Random, 0, sizeof(ISD_Random)); for (i = 0; i < (dwLen / sizeof(DWORD)); i++) { pdw = (DWORD*)(pbBuffer + i * sizeof(DWORD)); //No input needed in the ISD_Random structure for this operation, //so just send it in as is. fReturnCode = DeviceIoControl(hRNGDriver, IOCTL_ISD_GetRandomNumber, &ISD_Random, sizeof(ISD_Random), &ISD_Random, sizeof(ISD_Random), &dwBytesReturned, NULL); if (fReturnCode == 0 || ISD_Random.iStatus != ISD_EOK) { //Error - ignore the data returned dwReturn = GetLastError(); goto ErrorExit; } *pdw = *pdw ^ ISD_Random.uiRandomNum; } pb = pbBuffer + i * sizeof(DWORD); fReturnCode = DeviceIoControl(hRNGDriver, IOCTL_ISD_GetRandomNumber, &ISD_Random, sizeof(ISD_Random), &ISD_Random, sizeof(ISD_Random), &dwBytesReturned, NULL); if (fReturnCode == 0 || ISD_Random.iStatus != ISD_EOK) { //Error - ignore the data returned dwReturn = GetLastError(); goto ErrorExit; } pbRand = (BYTE*)&ISD_Random.uiRandomNum; for (i = 0; i < (dwLen % sizeof(DWORD)); i++) pb[i] ^= pbRand[i]; dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } #ifdef TEST_HW_RNG // // Function : SetupHWRNGIfRegistered // // Description : Checks if there is a registry setting indicating the HW RNG // is to be used. If the registry entry is there then it attempts // to get the HW RNG driver handle. // DWORD SetupHWRNGIfRegistered( OUT HANDLE *phRNGDriver) { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD dwSts; HKEY hRegKey = NULL; // first check the registry entry to see if supposed to use HW RNG dwSts = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\UseHWRNG", 0, // dwOptions KEY_READ, &hRegKey); if (ERROR_SUCCESS == dwSts) { // get the driver handle dwSts = GetRNGDriverHandle(phRNGDriver); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != hRegKey) RegCloseKey(hRegKey); return dwReturn; } #endif // TEST_HW_RNG #endif // _M_IX86 #endif // USE_HW_RNG // The function MACs the given bytes. /*static*/ void MACBytes( IN DESTable *pDESKeyTable, IN BYTE *pbData, IN DWORD cbData, IN OUT BYTE *pbTmp, IN OUT DWORD *pcbTmp, IN OUT BYTE *pbMAC) { DWORD cb = cbData; DWORD cbMACed = 0; while (cb) { if ((cb + *pcbTmp) < DES_BLOCKLEN) { memcpy(pbTmp + *pcbTmp, pbData + cbMACed, cb); *pcbTmp += cb; break; } else { memcpy(pbTmp + *pcbTmp, pbData + cbMACed, DES_BLOCKLEN - *pcbTmp); CBC(des, DES_BLOCKLEN, pbMAC, pbTmp, pDESKeyTable, ENCRYPT, pbMAC); cbMACed = cbMACed + (DES_BLOCKLEN - *pcbTmp); cb = cb - (DES_BLOCKLEN - *pcbTmp); *pcbTmp = 0; } } } // Given hInst, allocs and returns pointers to MAC pulled from // resource /*static*/ DWORD GetResourcePtr( IN HMODULE hInst, IN LPSTR pszRsrcName, OUT BYTE **ppbRsrcMAC, OUT DWORD *pcbRsrcMAC) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HRSRC hRsrc; // Nab resource handle for our signature hRsrc = FindResourceA(hInst, pszRsrcName, RT_RCDATA); if (NULL == hRsrc) { dwReturn = GetLastError(); goto ErrorExit; } // get a pointer to the actual signature data *ppbRsrcMAC = (PBYTE)LoadResource(hInst, hRsrc); if (NULL == *ppbRsrcMAC) { dwReturn = GetLastError(); goto ErrorExit; } // determine the size of the resource *pcbRsrcMAC = SizeofResource(hInst, hRsrc); if (0 == *pcbRsrcMAC) { dwReturn = GetLastError(); goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } #define CSP_TO_BE_MACED_CHUNK 4096 // Given hFile, reads the specified number of bytes (cbToBeMACed) from the file // and MACs these bytes. The function does this in chunks. /*static*/ DWORD MACBytesOfFile( IN HANDLE hFile, IN DWORD cbToBeMACed, IN DESTable *pDESKeyTable, IN BYTE *pbTmp, IN DWORD *pcbTmp, IN BYTE *pbMAC) { DWORD dwReturn = ERROR_INTERNAL_ERROR; BYTE rgbChunk[CSP_TO_BE_MACED_CHUNK]; DWORD cbRemaining = cbToBeMACed; DWORD cbToRead; DWORD dwBytesRead; // // loop over the file for the specified number of bytes // updating the hash as we go. // while (cbRemaining > 0) { if (cbRemaining < CSP_TO_BE_MACED_CHUNK) cbToRead = cbRemaining; else cbToRead = CSP_TO_BE_MACED_CHUNK; if (!ReadFile(hFile, rgbChunk, cbToRead, &dwBytesRead, NULL)) { dwReturn = GetLastError(); goto ErrorExit; } MACBytes(pDESKeyTable, rgbChunk, dwBytesRead, pbTmp, pcbTmp, pbMAC); cbRemaining -= cbToRead; } dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } /*static*/ DWORD MACTheFile( LPCSTR pszImage, DWORD cbImage) { static CONST BYTE rgbMACDESKey[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; DWORD dwReturn = ERROR_INTERNAL_ERROR; HMODULE hInst; MEMORY_BASIC_INFORMATION MemInfo; BYTE *pbRsrcMAC; DWORD cbRsrcMAC; BYTE *pbRsrcSig; DWORD cbRsrcSig; BYTE *pbStart = NULL; BYTE rgbMAC[DES_BLOCKLEN]; BYTE rgbZeroMAC[DES_BLOCKLEN + sizeof(DWORD) * 2]; BYTE rgbZeroSig[144]; BYTE *pbPostCRC; // pointer to just after CRC DWORD cbCRCToRsrc1; // number of bytes from CRC to first rsrc DWORD cbRsrc1ToRsrc2; // number of bytes from first rsrc to second DWORD cbPostRsrc; // size - (already hashed + signature size) BYTE *pbRsrc1ToRsrc2; BYTE *pbPostRsrc; BYTE *pbZeroRsrc1; BYTE *pbZeroRsrc2; DWORD cbZeroRsrc1; DWORD cbZeroRsrc2; DWORD *pdwMACInFileVer; DWORD *pdwCRCOffset; DWORD dwCRCOffset; DWORD dwZeroCRC = 0; OFSTRUCT ImageInfoBuf; HFILE hFile = HFILE_ERROR; HANDLE hMapping = NULL; DESTable DESKeyTable; BYTE rgbTmp[DES_BLOCKLEN]; DWORD cbTmp = 0; DWORD dwSts; memset(&MemInfo, 0, sizeof(MemInfo)); memset(rgbMAC, 0, sizeof(rgbMAC)); memset(rgbTmp, 0, sizeof(rgbTmp)); // Load the file hFile = OpenFile(pszImage, &ImageInfoBuf, OF_READ); if (HFILE_ERROR == hFile) { dwReturn = GetLastError(); goto ErrorExit; } hMapping = CreateFileMapping((HANDLE)IntToPtr(hFile), NULL, PAGE_READONLY, 0, 0, NULL); if (hMapping == NULL) { dwReturn = GetLastError(); goto ErrorExit; } pbStart = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); if (pbStart == NULL) { dwReturn = GetLastError(); goto ErrorExit; } // Convert pointer to HMODULE, using the same scheme as // LoadLibrary (windows\base\client\module.c). hInst = (HMODULE)((ULONG_PTR)pbStart | 0x00000001); // the MAC resource dwSts = GetResourcePtr(hInst, CRYPT_MAC_RESOURCE, &pbRsrcMAC, &cbRsrcMAC); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // the SIG resource dwSts = GetResourcePtr(hInst, CRYPT_SIG_RESOURCE, &pbRsrcSig, &cbRsrcSig); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } if (cbRsrcMAC < (sizeof(DWORD) * 2)) { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } // create a zero byte MAC memset(rgbZeroMAC, 0, sizeof(rgbZeroMAC)); // check the sig in file version and get the CRC offset pdwMACInFileVer = (DWORD*)pbRsrcMAC; pdwCRCOffset = (DWORD*)(pbRsrcMAC + sizeof(DWORD)); dwCRCOffset = *pdwCRCOffset; if ((0x00000100 != *pdwMACInFileVer) || (dwCRCOffset > cbImage)) { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } if (DES_BLOCKLEN != (cbRsrcMAC - (sizeof(DWORD) * 2))) { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } // create a zero byte Sig memset(rgbZeroSig, 0, sizeof(rgbZeroSig)); // set up the pointers pbPostCRC = pbStart + *pdwCRCOffset + sizeof(DWORD); if (pbRsrcSig > pbRsrcMAC) // MAC is first Rsrc { cbCRCToRsrc1 = (DWORD)(pbRsrcMAC - pbPostCRC); pbRsrc1ToRsrc2 = pbRsrcMAC + cbRsrcMAC; cbRsrc1ToRsrc2 = (DWORD)(pbRsrcSig - pbRsrc1ToRsrc2); pbPostRsrc = pbRsrcSig + cbRsrcSig; cbPostRsrc = (cbImage - (DWORD)(pbPostRsrc - pbStart)); // zero pointers pbZeroRsrc1 = rgbZeroMAC; cbZeroRsrc1 = cbRsrcMAC; pbZeroRsrc2 = rgbZeroSig; cbZeroRsrc2 = cbRsrcSig; } else // Sig is first Rsrc { cbCRCToRsrc1 = (DWORD)(pbRsrcSig - pbPostCRC); pbRsrc1ToRsrc2 = pbRsrcSig + cbRsrcSig; cbRsrc1ToRsrc2 = (DWORD)(pbRsrcMAC - pbRsrc1ToRsrc2); pbPostRsrc = pbRsrcMAC + cbRsrcMAC; cbPostRsrc = (cbImage - (DWORD)(pbPostRsrc - pbStart)); // zero pointers pbZeroRsrc1 = rgbZeroSig; cbZeroRsrc1 = cbRsrcSig; pbZeroRsrc2 = rgbZeroMAC; cbZeroRsrc2 = cbRsrcMAC; } // init the key table deskey(&DESKeyTable, (LPBYTE)rgbMACDESKey); // MAC up to the CRC dwSts = MACBytesOfFile((HANDLE)IntToPtr(hFile), dwCRCOffset, &DESKeyTable, rgbTmp, &cbTmp, rgbMAC); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // pretend CRC is zeroed MACBytes(&DESKeyTable, (BYTE*)&dwZeroCRC, sizeof(DWORD), rgbTmp, &cbTmp, rgbMAC); if (!SetFilePointer((HANDLE)IntToPtr(hFile), sizeof(DWORD), NULL, FILE_CURRENT)) { dwReturn = GetLastError(); goto ErrorExit; } // MAC from CRC to first resource dwSts = MACBytesOfFile((HANDLE)IntToPtr(hFile), cbCRCToRsrc1, &DESKeyTable, rgbTmp, &cbTmp, rgbMAC); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // pretend image has zeroed first resource MACBytes(&DESKeyTable, (BYTE*)pbZeroRsrc1, cbZeroRsrc1, rgbTmp, &cbTmp, rgbMAC); if (!SetFilePointer((HANDLE)IntToPtr(hFile), cbZeroRsrc1, NULL, FILE_CURRENT)) { dwReturn = GetLastError(); goto ErrorExit; } // MAC from first resource to second dwSts = MACBytesOfFile((HANDLE)IntToPtr(hFile), cbRsrc1ToRsrc2, &DESKeyTable, rgbTmp, &cbTmp, rgbMAC); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // pretend image has zeroed second Resource MACBytes(&DESKeyTable, (BYTE*)pbZeroRsrc2, cbZeroRsrc2, rgbTmp, &cbTmp, rgbMAC); if (!SetFilePointer((HANDLE)IntToPtr(hFile), cbZeroRsrc2, NULL, FILE_CURRENT)) { dwReturn = GetLastError(); goto ErrorExit; } // MAC after the resource dwSts = MACBytesOfFile((HANDLE)IntToPtr(hFile), cbPostRsrc, &DESKeyTable, rgbTmp, &cbTmp, rgbMAC); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } if (0 != memcmp(rgbMAC, pbRsrcMAC + sizeof(DWORD) * 2, DES_BLOCKLEN)) { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (NULL != pbStart) UnmapViewOfFile(pbStart); if (NULL != hMapping) CloseHandle(hMapping); if (HFILE_ERROR != hFile) _lclose(hFile); return dwReturn; } // ********************************************************************** // SelfMACCheck performs a DES MAC on the binary image of this DLL // ********************************************************************** DWORD SelfMACCheck( IN LPSTR pszImage) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HFILE hFileProv = HFILE_ERROR; DWORD cbImage; OFSTRUCT ImageInfoBuf; DWORD dwSts; #ifdef _DEBUG return ERROR_SUCCESS; #endif // Check file size hFileProv = OpenFile(pszImage, &ImageInfoBuf, OF_READ); if (HFILE_ERROR == hFileProv) { dwSts = GetLastError(); if (ERROR_FILE_NOT_FOUND == dwSts) dwReturn = (DWORD)NTE_PROV_DLL_NOT_FOUND; else dwReturn = dwSts; goto ErrorExit; } cbImage = GetFileSize((HANDLE)IntToPtr(hFileProv), NULL); if ((DWORD)(-1) == cbImage) { dwReturn = GetLastError(); goto ErrorExit; } _lclose(hFileProv); hFileProv = HFILE_ERROR; dwSts = MACTheFile(pszImage, cbImage); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (HFILE_ERROR != hFileProv) _lclose(hFileProv); return dwReturn; }