///////////////////////////////////////////////////////////////////////////// // FILE : protstor.c // // DESCRIPTION : Code for storing keys in the protected store: // // AUTHOR : // // HISTORY : // // Dec 4 1996 jeffspel Created // // Apr 21 1997 jeffspel Changes for NT 5 tree // // Jul 28 1997 jeffspel Added ability to delete a persisted key // // May 5 2000 dbarlow Modify error return handling // // // // Copyright (C) 1996 - 2000, Microsoft Corporation // // All Rights Reserved // ///////////////////////////////////////////////////////////////////////////// #include "precomp.h" #include "wincrypt.h" #include "pstore.h" #include "protstor.h" #include "ntagum.h" #define CRYPTO_KEY_TYPE_STRING L"Cryptographic Keys" static GUID l_DefTypeGuid = { 0x4d1fa410, 0x6fd9, 0x11d0, { 0x8C, 0x58, 0x00, 0xC0, 0x4F, 0xD9, 0x12, 0x6B } }; #define CRYPTO_SIG_SUBTYPE_STRING L"RSA Signature Keys" static GUID l_DefSigGuid = { 0x4d1fa411, 0x6fd9, 0x11D0, { 0x8C, 0x58, 0x00, 0xC0, 0x4F, 0xD9, 0x12, 0x6B } }; #define CRYPTO_EXCH_SUBTYPE_STRING L"RSA Exchange Keys" static GUID l_DefExchGuid = { 0x4d1fa412, 0x6fd9, 0x11D0, { 0x8C, 0x58, 0x00, 0xC0, 0x4F, 0xD9, 0x12, 0x6B } }; static GUID l_SysProv = MS_BASE_PSTPROVIDER_ID; void FreePSInfo( PSTORE_INFO *pPStore) { IPStore *pIPS; if (NULL != pPStore) { pIPS = (IPStore*)pPStore->pProv; if (NULL != pIPS) pIPS->Release(); if (NULL != pPStore->hInst) FreeLibrary(pPStore->hInst); if (NULL != pPStore->szPrompt) _nt_free(pPStore->szPrompt, pPStore->cbPrompt); _nt_free(pPStore, sizeof(PSTORE_INFO)); } } BOOL CheckPStoreAvailability( PSTORE_INFO *pPStore) { BOOL fRet = FALSE; if (S_OK != PStoreCreateInstance((IPStore**)(&pPStore->pProv), &l_SysProv, NULL, 0)) goto ErrorExit; memcpy(&pPStore->SigType, &l_DefTypeGuid, sizeof(GUID)); memcpy(&pPStore->SigSubtype, &l_DefSigGuid, sizeof(GUID)); memcpy(&pPStore->ExchType, &l_DefTypeGuid, sizeof(GUID)); memcpy(&pPStore->ExchSubtype, &l_DefExchGuid, sizeof(GUID)); fRet = TRUE; ErrorExit: return fRet; } DWORD CreateNewPSKeyset( PSTORE_INFO *pPStore, DWORD dwFlags) { DWORD dwReturn = ERROR_INTERNAL_ERROR; PST_ACCESSRULE rgRules[2]; PST_ACCESSRULESET Rules; PST_TYPEINFO Info; PST_PROMPTINFO PromptInfo = {NULL, NULL}; HRESULT hr; IPStore *pIPS = (IPStore*)pPStore->pProv; DWORD dwRegLoc = PST_KEY_CURRENT_USER; if (dwFlags & CRYPT_MACHINE_KEYSET) dwRegLoc = PST_KEY_LOCAL_MACHINE; // if type is not available the create it memset(&Info, 0, sizeof(Info)); Info.cbSize = sizeof(PST_TYPEINFO); Info.szDisplayName = CRYPTO_KEY_TYPE_STRING; hr = pIPS->CreateType(dwRegLoc, &pPStore->SigType, &Info, 0); if ((S_OK != hr) && (PST_E_TYPE_EXISTS != hr)) { dwReturn = (DWORD)hr; goto ErrorExit; } // make same rules for read, write access rgRules[0].cbSize = sizeof(PST_ACCESSRULE); rgRules[0].AccessModeFlags = PST_READ; rgRules[0].cClauses = 0; rgRules[0].rgClauses = NULL; rgRules[1].cbSize = sizeof(PST_ACCESSRULE); rgRules[1].AccessModeFlags = PST_WRITE; rgRules[1].cClauses = 0; rgRules[1].rgClauses = NULL; Rules.cbSize = sizeof(PST_ACCESSRULESET); Rules.cRules = 2; Rules.rgRules = rgRules; // create the signature subtype Info.szDisplayName = CRYPTO_SIG_SUBTYPE_STRING; PromptInfo.szPrompt = L""; hr = pIPS->CreateSubtype(dwRegLoc, &pPStore->SigType, &pPStore->SigSubtype, &Info, &Rules, 0); if ((S_OK != hr) && (PST_E_TYPE_EXISTS != hr)) { dwReturn = (DWORD)hr; goto ErrorExit; } // create the exchange subtype Info.szDisplayName = CRYPTO_EXCH_SUBTYPE_STRING; hr = pIPS->CreateSubtype(dwRegLoc, &pPStore->SigType, &pPStore->ExchSubtype, &Info, &Rules, 0); if ((S_OK != hr) && (PST_E_TYPE_EXISTS != hr)) { dwReturn = (DWORD)hr; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } DWORD GetKeysetTypeAndSubType( PNTAGUserList pUser) { DWORD dwReturn = ERROR_INTERNAL_ERROR; BYTE *pb1 = NULL; DWORD cb1; BYTE *pb2 = NULL; DWORD cb2; DWORD dwSts; memcpy(&pUser->pPStore->SigType, &l_DefTypeGuid, sizeof(GUID)); memcpy(&pUser->pPStore->SigSubtype, &l_DefSigGuid, sizeof(GUID)); memcpy(&pUser->pPStore->ExchType, &l_DefTypeGuid, sizeof(GUID)); memcpy(&pUser->pPStore->ExchSubtype, &l_DefExchGuid, sizeof(GUID)); // look in registry and see if the type and subtype Guids are there dwSts = ReadRegValue(pUser->hKeys, "SigTypeSubtype", &pb1, &cb1, TRUE); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = ReadRegValue(pUser->hKeys, "ExchTypeSubtype", &pb2, &cb2, TRUE); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } if (pb1) { memcpy(&pUser->pPStore->SigType, pb1, sizeof(GUID)); memcpy(&pUser->pPStore->SigSubtype, pb1 + sizeof(GUID), sizeof(GUID)); } if (pb2) { memcpy(&pUser->pPStore->ExchType, pb2, sizeof(GUID)); memcpy(&pUser->pPStore->ExchSubtype, pb2 + sizeof(GUID), sizeof(GUID)); } dwReturn = ERROR_SUCCESS; ErrorExit: if (pb1) _nt_free(pb1, cb1); if (pb2) _nt_free(pb2, cb2); return dwReturn; } DWORD SetUIPrompt( PNTAGUserList pUser, LPWSTR szPrompt) { DWORD dwReturn = ERROR_INTERNAL_ERROR; DWORD cb = 0; LPWSTR sz = NULL; // check if sig or exch keys are loaded and if so error if (NULL == pUser->pPStore) { dwReturn = (DWORD)NTE_BAD_KEYSET; goto ErrorExit; } if (NULL != szPrompt) { cb = (lstrlenW(szPrompt) + 1) * sizeof(WCHAR); sz = (LPWSTR)_nt_malloc(cb); if (NULL == sz) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } lstrcpyW(sz, szPrompt); } if (pUser->pPStore->szPrompt) _nt_free(pUser->pPStore->szPrompt, pUser->pPStore->cbPrompt); pUser->pPStore->cbPrompt = cb; pUser->pPStore->szPrompt = sz; dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } /*static*/ DWORD PickleKey( BOOL fExportable, size_t cbPriv, PBYTE pbPriv, PBYTE *ppbData, PDWORD pcbData) { DWORD dwReturn = ERROR_INTERNAL_ERROR; if (NULL != pbPriv) { // alloc the appropriate amount of space *pcbData = cbPriv + sizeof(DWORD) + sizeof(BOOL); *ppbData = (PBYTE)_nt_malloc(*pcbData); if (NULL == *ppbData) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } // copy exportable info into buffer memcpy(*ppbData, &fExportable, sizeof(BOOL)); // copy length of keying material into buffer memcpy(*ppbData + sizeof(BOOL), &cbPriv, sizeof(DWORD)); // copy keying material into buffer memcpy(*ppbData + sizeof(DWORD) + sizeof(BOOL), pbPriv, cbPriv); } dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } /*static*/ DWORD UnpickleKey( PBYTE pbData, BOOL *pfExportable, DWORD *pcbPriv, PBYTE *ppbPriv) { DWORD dwReturn = ERROR_INTERNAL_ERROR; if (NULL != pbData) { // pull out the exportable info memcpy(pfExportable, pbData, sizeof(BOOL)); // pull out the length of the key material memcpy(pcbPriv, pbData + sizeof(BOOL), sizeof(DWORD)); // free the current key material memory if (NULL != *ppbPriv) _nt_free(*ppbPriv, *pcbPriv); // alloc new memory for the key material *ppbPriv = (PBYTE)_nt_malloc(*pcbPriv); if (NULL == *ppbPriv) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } // copy key material memcpy(*ppbPriv, pbData + sizeof(DWORD) + sizeof(BOOL), *pcbPriv); } dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } /*static*/ DWORD RestoreKeyFromProtectedStorage( PNTAGUserList pUser, LPWSTR szKeyName, BYTE **ppbKey, DWORD *pcbKey, LPWSTR szPrompt, BOOL fSigKey, BOOL fMachineKeySet, BOOL *pfUIOnKey) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HRESULT hr; DWORD cb; BYTE *pb = NULL; GUID *pType; GUID *pSubtype; BOOL *pf; PST_PROMPTINFO PromptInfo; IPStore *pIPS = (IPStore*)(pUser->pPStore->pProv); DWORD dwRegLoc = PST_KEY_CURRENT_USER; DWORD dwSts; *pfUIOnKey = FALSE; if (fMachineKeySet) dwRegLoc = PST_KEY_LOCAL_MACHINE; memset(&PromptInfo, 0, sizeof(PromptInfo)); PromptInfo.cbSize = sizeof(PST_PROMPTINFO); if (fSigKey) { if (0 == pUser->ContInfo.ContLens.cbSigPub) { dwReturn = ERROR_SUCCESS; goto ErrorExit; } pType = &pUser->pPStore->SigType; pSubtype = &pUser->pPStore->SigSubtype; pf = &pUser->ContInfo.fSigExportable; } else { if (0 == pUser->ContInfo.ContLens.cbExchPub) { dwReturn = ERROR_SUCCESS; goto ErrorExit; } pType = &pUser->pPStore->ExchType; pSubtype = &pUser->pPStore->ExchSubtype; pf = &pUser->ContInfo.fExchExportable; } // read the item from secure storage PromptInfo.hwndApp = NULL; if (NULL == pUser->pPStore->szPrompt) PromptInfo.szPrompt = szPrompt; else PromptInfo.szPrompt = pUser->pPStore->szPrompt; hr = pIPS->ReadItem(dwRegLoc, pType, pSubtype, szKeyName, &cb, &pb, &PromptInfo, PST_PROMPT_QUERY | PST_NO_UI_MIGRATION); if (S_OK != hr) { // this function returns PST_E_ITEM_EXISTS if there is UI on the item if (PST_E_ITEM_EXISTS == hr) *pfUIOnKey = TRUE; else { dwReturn = (DWORD)hr; goto ErrorExit; } } dwSts = UnpickleKey(pb, pf, pcbKey, ppbKey); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (pb) CoTaskMemFree(pb); return dwReturn; } /*static*/ DWORD MakeUnicodeKeysetName( BYTE *pszName, LPWSTR *ppszWName) { // ?BUGBUG? -- We don't really do this, do we? DWORD dwReturn = ERROR_INTERNAL_ERROR; long i; DWORD cb; cb = (DWORD)lstrlenA((LPSTR)pszName); *ppszWName = (LPWSTR)_nt_malloc((cb + 1) * sizeof(WCHAR)); if (NULL == *ppszWName) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } for (i=0;i<(long)cb;i++) (*ppszWName)[i] = (WCHAR)(pszName[i]); dwReturn = ERROR_SUCCESS; ErrorExit: return dwReturn; } DWORD RestoreKeysetFromProtectedStorage( PNTAGUserList pUser, LPWSTR szPrompt, BYTE **ppbKey, DWORD *pcbKey, BOOL fSigKey, BOOL fMachineKeySet, BOOL *pfUIOnKey) { DWORD dwReturn = ERROR_INTERNAL_ERROR; LPWSTR pszWName = NULL; DWORD dwSts; // convert the keyset name to unicode dwSts = MakeUnicodeKeysetName((BYTE*)pUser->ContInfo.pszUserName, &pszWName); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // restore the signature key dwSts = RestoreKeyFromProtectedStorage(pUser, pszWName, ppbKey, pcbKey, szPrompt, fSigKey, fMachineKeySet, pfUIOnKey); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (pszWName) _nt_free(pszWName, (wcslen(pszWName) + 1) * sizeof(WCHAR)); return dwReturn; } void RemoveKeysetFromMemory( PNTAGUserList pUser) { pUser->ContInfo.fSigExportable = FALSE; if (pUser->pSigPrivKey) { _nt_free(pUser->pSigPrivKey, pUser->SigPrivLen); pUser->SigPrivLen = 0; pUser->pSigPrivKey = NULL; } pUser->ContInfo.fExchExportable = FALSE; if (pUser->pExchPrivKey) { _nt_free(pUser->pExchPrivKey, pUser->ExchPrivLen); pUser->ExchPrivLen = 0; pUser->pExchPrivKey = NULL; } } DWORD SaveKeyToProtectedStorage( PNTAGUserList pUser, DWORD dwFlags, LPWSTR szPrompt, BOOL fSigKey, BOOL fMachineKeySet) { DWORD dwReturn = ERROR_INTERNAL_ERROR; HRESULT hr; PBYTE pb = NULL; DWORD cb; GUID *pType; GUID *pSubtype; BOOL f; BYTE *pbKey = NULL; size_t cbKey; LPWSTR pszWName = NULL; LPSTR szKeyName; PST_PROMPTINFO PromptInfo; IPStore *pIPS = (IPStore*)(pUser->pPStore->pProv); DWORD dwRegLoc = PST_KEY_CURRENT_USER; DWORD dwConfirm = PST_CF_NONE; DWORD dwSts; if (fMachineKeySet) dwRegLoc = PST_KEY_LOCAL_MACHINE; memset(&PromptInfo, 0, sizeof(PromptInfo)); PromptInfo.cbSize = sizeof(PST_PROMPTINFO); if (fSigKey) { pType = &pUser->pPStore->SigType; pSubtype = &pUser->pPStore->SigSubtype; f = pUser->ContInfo.fSigExportable; cbKey = pUser->SigPrivLen; pbKey = pUser->pSigPrivKey; szKeyName = "SPbK"; if (dwFlags & CRYPT_USER_PROTECTED) dwConfirm = PST_CF_DEFAULT; } else { pType = &pUser->pPStore->ExchType; pSubtype = &pUser->pPStore->ExchSubtype; f = pUser->ContInfo.fExchExportable; cbKey = pUser->ExchPrivLen; pbKey = pUser->pExchPrivKey; szKeyName = "EPbK"; if (dwFlags & CRYPT_USER_PROTECTED) dwConfirm = PST_CF_DEFAULT; } // format the signature key and exportable info dwSts = PickleKey(f, cbKey, pbKey, &pb, &cb); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } if (pb) { // make a unicode version of the keyset name dwSts = MakeUnicodeKeysetName((BYTE*)pUser->ContInfo.pszUserName, &pszWName); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } PromptInfo.hwndApp = NULL; if (NULL == pUser->pPStore->szPrompt) PromptInfo.szPrompt = szPrompt; else PromptInfo.szPrompt = pUser->pPStore->szPrompt; hr = pIPS->WriteItem(dwRegLoc, pType, pSubtype, pszWName, cb, pb, &PromptInfo, dwConfirm, 0); if (S_OK != hr) { dwReturn = (DWORD)hr; goto ErrorExit; } } dwReturn = ERROR_SUCCESS; ErrorExit: if (pb) _nt_free(pb, cb); if (pszWName) _nt_free(pszWName, (wcslen(pszWName) + 1) * sizeof(WCHAR)); return dwReturn; } DWORD DeleteKeyFromProtectedStorage( NTAGUserList *pUser, PCSP_STRINGS pStrings, DWORD dwKeySpec, BOOL fMachineKeySet, BOOL fMigration) { DWORD dwReturn = ERROR_INTERNAL_ERROR; LPWSTR szWUserName = NULL; PST_PROMPTINFO PromptInfo; IPStore *pIPS; DWORD dwRegLoc = PST_KEY_CURRENT_USER; DWORD dwSts; memset(&PromptInfo, 0, sizeof(PromptInfo)); PromptInfo.cbSize = sizeof(PST_PROMPTINFO); PromptInfo.hwndApp = NULL; if (fMachineKeySet) dwRegLoc = PST_KEY_LOCAL_MACHINE; // make a unicode name dwSts = MakeUnicodeKeysetName((BYTE*)pUser->ContInfo.pszUserName, &szWUserName); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } pIPS = (IPStore*)(pUser->pPStore->pProv); if (AT_SIGNATURE == dwKeySpec) { if (fMigration) PromptInfo.szPrompt = pStrings->pwszDeleteMigrSig; else PromptInfo.szPrompt = pStrings->pwszDeleteSig; pIPS->DeleteItem(dwRegLoc, &pUser->pPStore->SigType, &pUser->pPStore->SigSubtype, szWUserName, &PromptInfo, PST_NO_UI_MIGRATION); } else { if (fMigration) PromptInfo.szPrompt = pStrings->pwszDeleteMigrExch; else PromptInfo.szPrompt = pStrings->pwszDeleteExch; pIPS->DeleteItem(dwRegLoc, &pUser->pPStore->ExchType, &pUser->pPStore->ExchSubtype, szWUserName, &PromptInfo, PST_NO_UI_MIGRATION); } dwReturn = ERROR_SUCCESS; ErrorExit: if (szWUserName) _nt_free(szWUserName, (wcslen(szWUserName) + 1) * sizeof(WCHAR)); return dwReturn; } DWORD DeleteFromProtectedStorage( CONST char *pszUserID, PCSP_STRINGS pStrings, HKEY hRegKey, BOOL fMachineKeySet) { DWORD dwReturn = ERROR_INTERNAL_ERROR; NTAGUserList User; DWORD dwSts; // set up the User List structure memset(&User, 0, sizeof(User)); User.ContInfo.pszUserName = (LPSTR)pszUserID; User.hKeys = hRegKey; User.pPStore = (PSTORE_INFO*)_nt_malloc(sizeof(PSTORE_INFO)); if (NULL == User.pPStore) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } if (!CheckPStoreAvailability(User.pPStore)) { dwReturn = (DWORD)NTE_FAIL; goto ErrorExit; } // get type and subtypes dwSts = GetKeysetTypeAndSubType(&User); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } // delete each key dwSts = DeleteKeyFromProtectedStorage(&User, pStrings, AT_SIGNATURE, fMachineKeySet, FALSE); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwSts = DeleteKeyFromProtectedStorage(&User, pStrings, AT_KEYEXCHANGE, fMachineKeySet, FALSE); if (ERROR_SUCCESS != dwSts) { dwReturn = dwSts; goto ErrorExit; } dwReturn = ERROR_SUCCESS; ErrorExit: if (User.pPStore) FreePSInfo(User.pPStore); return dwReturn; }