//+------------------------------------------------------------------------- // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1995 - 1999 // // File: newstor.cpp // // Contents: Certificate, CRL and CTL Store APIs // // Functions: CertStoreDllMain // CertOpenStore // CertDuplicateStore // CertCloseStore // CertSaveStore // CertControlStore // CertAddStoreToCollection // CertRemoveStoreFromCollection // CertSetStoreProperty // CertGetStoreProperty // CertGetSubjectCertificateFromStore // CertEnumCertificatesInStore // CertFindCertificateInStore // CertGetIssuerCertificateFromStore // CertVerifySubjectCertificateContext // CertDuplicateCertificateContext // CertCreateCertificateContext // CertFreeCertificateContext // CertSetCertificateContextProperty // CertGetCertificateContextProperty // CertEnumCertificateContextProperties // CertCreateCTLEntryFromCertificateContextProperties // CertSetCertificateContextPropertiesFromCTLEntry // CertGetCRLFromStore // CertEnumCRLsInStore // CertFindCRLInStore // CertDuplicateCRLContext // CertCreateCRLContext // CertFreeCRLContext // CertSetCRLContextProperty // CertGetCRLContextProperty // CertEnumCRLContextProperties // CertFindCertificateInCRL // CertAddEncodedCertificateToStore // CertAddCertificateContextToStore // CertSerializeCertificateStoreElement // CertDeleteCertificateFromStore // CertAddEncodedCRLToStore // CertAddCRLContextToStore // CertSerializeCRLStoreElement // CertDeleteCRLFromStore // CertAddSerializedElementToStore // // CertDuplicateCTLContext // CertCreateCTLContext // CertFreeCTLContext // CertSetCTLContextProperty // CertGetCTLContextProperty // CertEnumCTLContextProperties // CertEnumCTLsInStore // CertFindSubjectInCTL // CertFindCTLInStore // CertAddEncodedCTLToStore // CertAddCTLContextToStore // CertSerializeCTLStoreElement // CertDeleteCTLFromStore // // CertAddCertificateLinkToStore // CertAddCRLLinkToStore // CertAddCTLLinkToStore // // CertCreateContext // // I_CertAddSerializedStore // CryptAcquireCertificatePrivateKey // I_CertSyncStore // I_CertSyncStoreEx // I_CertUpdateStore // // CryptGetKeyIdentifierProperty // CryptSetKeyIdentifierProperty // CryptEnumKeyIdentifierProperties // // History: 17-Feb-96 philh created // 29-Dec-96 philh redo using provider functions // 01-May-97 philh added CTL functions // 01-Aug-97 philh NT 5.0 Changes. Support context links, // collections and external stores. //-------------------------------------------------------------------------- #include "global.hxx" #include #ifdef STATIC #undef STATIC #endif #define STATIC HMODULE hCertStoreInst; // Maximum # of verified CRLs allowed per issuer. // This array of CRLs is passed to CertHelperVerifyRevocation #define MAX_CRL_LIST 64 //+------------------------------------------------------------------------- // Store data structure definitions //-------------------------------------------------------------------------- // Assumes // 0 - Certificates // 1 - CRLs // 2 - CTLs #define CONTEXT_COUNT 3 typedef struct _CONTEXT_ELEMENT CONTEXT_ELEMENT, *PCONTEXT_ELEMENT; typedef struct _PROP_ELEMENT PROP_ELEMENT, *PPROP_ELEMENT; typedef struct _CERT_STORE CERT_STORE, *PCERT_STORE; typedef struct _SHARE_STORE SHARE_STORE, *PSHARE_STORE; typedef struct _CERT_STORE_LINK CERT_STORE_LINK, *PCERT_STORE_LINK; typedef struct _COLLECTION_STACK_ENTRY COLLECTION_STACK_ENTRY, *PCOLLECTION_STACK_ENTRY; // Used to maintain collection state across context find next calls. // // Ref count on pStoreLink. No ref count on pCollection. // pStoreLink may be NULL. struct _COLLECTION_STACK_ENTRY { PCERT_STORE pCollection; PCERT_STORE_LINK pStoreLink; PCOLLECTION_STACK_ENTRY pPrev; }; typedef struct _CONTEXT_CACHE_INFO { PPROP_ELEMENT pPropHead; } CONTEXT_CACHE_INFO; typedef struct _CONTEXT_EXTERNAL_INFO { // For ELEMENT_FIND_NEXT_FLAG void *pvProvInfo; } CONTEXT_EXTERNAL_INFO; typedef struct _CONTEXT_COLLECTION_INFO { // For Find PCOLLECTION_STACK_ENTRY pCollectionStack; } CONTEXT_COLLECTION_INFO; #define ELEMENT_DELETED_FLAG 0x00010000 // Only set for external elements #define ELEMENT_FIND_NEXT_FLAG 0x00020000 // Set during CertCloseStore if ELEMENT_FIND_NEXT_FLAG was set. #define ELEMENT_CLOSE_FIND_NEXT_FLAG 0x00040000 // Set if the element has a CERT_ARCHIVED_PROP_ID #define ELEMENT_ARCHIVED_FLAG 0x00080000 // A cache element is the actual context element. Its the only element, // where pEle points to itself. All other elements will eventually // point to a cache element. Cache elements may only reside in a cache // store. The pProvStore is the same as the pStore. Note, during a // context add, a cache element may temporarily be in a collection store // during the call to the provider's add callback. // // A link context element is a link to another element, including a link // to another link context element. Link context elements may only reside // in a cache store. The pProvStore is the same as the linked to element's // pProvStore. // // An external element is a link to the element returned by a provider // that stores elements externally. External elements may only reside in // an external store. The pProvStore is the external store's // provider. The store doesn't hold a reference on an external element, // its ELEMENT_DELETED_FLAG is always set. // // A collection element is a link to an element in a cache or external store. // Its returned when finding in or adding to a collection store. The store // doesn't hold a reference on a collection element, its // ELEMENT_DELETED_FLAG is always set. // #define ELEMENT_TYPE_CACHE 1 #define ELEMENT_TYPE_LINK_CONTEXT 2 #define ELEMENT_TYPE_EXTERNAL 3 #define ELEMENT_TYPE_COLLECTION 4 #define MAX_LINK_DEPTH 100 typedef struct _CONTEXT_NOCOPY_INFO { PFN_CRYPT_FREE pfnFree; void *pvFree; } CONTEXT_NOCOPY_INFO, *PCONTEXT_NOCOPY_INFO; // Identical contexts (having the same SHA1 hash) can share the same encoded // byte array and decoded info data structure. // // CreateShareElement() creates with dwRefCnt of 1. FindShareElement() finds // an existing and increments dwRefCnt. ReleaseShareElement() decrements // dwRefCnt and frees when 0. typedef struct _SHARE_ELEMENT SHARE_ELEMENT, *PSHARE_ELEMENT; struct _SHARE_ELEMENT { BYTE rgbSha1Hash[SHA1_HASH_LEN]; DWORD dwContextType; BYTE *pbEncoded; // allocated DWORD cbEncoded; void *pvInfo; // allocated DWORD dwRefCnt; PSHARE_ELEMENT pNext; PSHARE_ELEMENT pPrev; }; // The CONTEXT_ELEMENT is inserted before the CERT_CONTEXT, CRL_CONTEXT or // CTL_CONTEXT. The dwContextType used is 0 based and not 1 based. For // example, dwContextType = CERT_STORE_CERTIFICATE_CONTEXT - 1. struct _CONTEXT_ELEMENT { DWORD dwElementType; DWORD dwContextType; DWORD dwFlags; LONG lRefCnt; // For ELEMENT_TYPE_CACHE, pEle points to itself. Otherwise, pEle points // to the element being linked to and the pEle is addRef'ed. The // cached element is found by iterating through the pEle's until pEle // points to itself. PCONTEXT_ELEMENT pEle; PCERT_STORE pStore; PCONTEXT_ELEMENT pNext; PCONTEXT_ELEMENT pPrev; PCERT_STORE pProvStore; PCONTEXT_NOCOPY_INFO pNoCopyInfo; // When nonNULL, the context's pbEncoded and pInfo aren't allocated. // Instead, use the shared element's pbEncoded and pInfo. When // context element is freed, the pSharedEle is ReleaseShareElement()'ed. PSHARE_ELEMENT pShareEle; // RefCnt'ed union { CONTEXT_CACHE_INFO Cache; // ELEMENT_TYPE_CACHE CONTEXT_EXTERNAL_INFO External; // ELEMENT_TYPE_EXTERNAL CONTEXT_COLLECTION_INFO Collection; // ELEMENT_TYPE_COLLECTION }; }; // For CRL, follows the above CONTEXT_ELEMENT typedef struct _CRL_CONTEXT_SUFFIX { PCRL_ENTRY *ppSortedEntry; } CRL_CONTEXT_SUFFIX, *PCRL_CONTEXT_SUFFIX; typedef struct _HASH_BUCKET_ENTRY HASH_BUCKET_ENTRY, *PHASH_BUCKET_ENTRY; struct _HASH_BUCKET_ENTRY { union { DWORD dwEntryIndex; DWORD dwEntryOffset; const BYTE *pbEntry; }; union { PHASH_BUCKET_ENTRY pNext; DWORD iNext; }; }; typedef struct _SORTED_CTL_FIND_INFO { DWORD cHashBucket; BOOL fHashedIdentifier; // Encoded sequence of TrustedSubjects const BYTE *pbEncodedSubjects; // not allocated DWORD cbEncodedSubjects; // Following is NON-NULL for a szOID_SORTED_CTL extension const BYTE *pbEncodedHashBucket; // not allocated // Following are NON-NULL when there isn't a szOID_SORTED_CTL extension DWORD *pdwHashBucketHead; // allocated PHASH_BUCKET_ENTRY pHashBucketEntry; // allocated } SORTED_CTL_FIND_INFO, *PSORTED_CTL_FIND_INFO; // For CTL, follows the above CONTEXT_ELEMENT typedef struct _CTL_CONTEXT_SUFFIX { PCTL_ENTRY *ppSortedEntry; // allocated BOOL fFastCreate; // Following only applicable for a FastCreateCtlElement PCTL_ENTRY pCTLEntry; // allocated PCERT_EXTENSIONS pExtInfo; // allocated PSORTED_CTL_FIND_INFO pSortedCtlFindInfo; // not allocated } CTL_CONTEXT_SUFFIX, *PCTL_CONTEXT_SUFFIX; struct _PROP_ELEMENT { DWORD dwPropId; DWORD dwFlags; BYTE *pbData; DWORD cbData; PPROP_ELEMENT pNext; PPROP_ELEMENT pPrev; }; #define STORE_LINK_DELETED_FLAG 0x00010000 struct _CERT_STORE_LINK { DWORD dwFlags; LONG lRefCnt; // Whatever is passed to CertAddStoreToCollection DWORD dwUpdateFlags; DWORD dwPriority; PCERT_STORE pCollection; PCERT_STORE pSibling; // CertStoreDuplicate'd. PCERT_STORE_LINK pNext; PCERT_STORE_LINK pPrev; }; // Store types #define STORE_TYPE_CACHE 1 #define STORE_TYPE_EXTERNAL 2 #define STORE_TYPE_COLLECTION 3 // CACHE store may have CACHE or LINK_CONTEXT elements. Until deleted, // the store has a reference count to. // EXTERNAL store only has EXTERNAL elements. These elements are always // deleted, wherein, the store doesn't hold a refCnt. // COLLECTION store has COLLECTION elements. These elements // are always deleted, wherein, the store doesn't hold a refCnt. struct _CERT_STORE { DWORD dwStoreType; LONG lRefCnt; HCRYPTPROV hCryptProv; DWORD dwFlags; DWORD dwState; CRITICAL_SECTION CriticalSection; PCONTEXT_ELEMENT rgpContextListHead[CONTEXT_COUNT]; PCERT_STORE_LINK pStoreListHead; // COLLECTION PPROP_ELEMENT pPropHead; // properties for entire store // For CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG // Incremented for each context duplicated LONG lDeferCloseRefCnt; // Event handle set by CertControlStore(CERT_STORE_CTRL_AUTO_RESYNC) HANDLE hAutoResyncEvent; // The following is set for a shared store PSHARE_STORE pShareStore; // Store provider info LONG lStoreProvRefCnt; HANDLE hStoreProvWait; HCRYPTOIDFUNCADDR hStoreProvFuncAddr; CERT_STORE_PROV_INFO StoreProvInfo; }; //+------------------------------------------------------------------------- // Store states //-------------------------------------------------------------------------- #define STORE_STATE_DELETED 0 #define STORE_STATE_NULL 1 #define STORE_STATE_OPENING 2 #define STORE_STATE_OPEN 3 #define STORE_STATE_DEFER_CLOSING 4 #define STORE_STATE_CLOSING 5 #define STORE_STATE_CLOSED 6 // LocalMachine System stores opened for SHARE and MAXIMUM_ALLOWED can // be shared. struct _SHARE_STORE { LPWSTR pwszStore; // not a separate allocation, string // follows struct PCERT_STORE pStore; // store holds lRefCnt PSHARE_STORE pNext; PSHARE_STORE pPrev; }; //+------------------------------------------------------------------------- // Share stores. // // A shared stored is identified by its UNICODE name. Simply maintain a // linked list of share stores. // // Shared stores are restricted to LocalMachine System Stores opened // with CERT_STORE_SHARE_STORE_FLAG, CERT_STORE_SHARE_CONTEXT_FLAG and // CERT_STORE_MAXIMUM_ALLOWED_FLAG. //-------------------------------------------------------------------------- STATIC PSHARE_STORE pShareStoreHead; STATIC CRITICAL_SECTION ShareStoreCriticalSection; //+------------------------------------------------------------------------- // Key Identifier Element //-------------------------------------------------------------------------- typedef struct _KEYID_ELEMENT { CRYPT_HASH_BLOB KeyIdentifier; PPROP_ELEMENT pPropHead; } KEYID_ELEMENT, *PKEYID_ELEMENT; //+------------------------------------------------------------------------- // The "Find ANY" INFO data structure. // // 0 is the ANY dwFindType for all context types. //-------------------------------------------------------------------------- static CCERT_STORE_PROV_FIND_INFO FindAnyInfo = { sizeof(CCERT_STORE_PROV_FIND_INFO), // cbSize X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, // dwMsgAndCertEncodingType 0, // dwFindFlags 0, // dwFindType NULL // pvFindPara }; //+------------------------------------------------------------------------- // NULL Store. // // HANDLE of all CONTEXTs created by CertCreateCertificateContext or // CertCreateCRLContext. Created CONTEXTs are immediately added to the // NULL store's free list. (ie, the store doesn't have a RefCnt on the // CONTEXT.) //-------------------------------------------------------------------------- static CERT_STORE NullCertStore; //+------------------------------------------------------------------------- // Bug in rsabase.dll. Its not thread safe across multiple crypt prov // handles. //-------------------------------------------------------------------------- static CRITICAL_SECTION CryptProvCriticalSection; //+------------------------------------------------------------------------- // Store file definitions // // The file consist of the FILE_HDR followed by 1 or more FILE_ELEMENTs. // Each FILE_ELEMENT has a FILE_ELEMENT_HDR + its value. // // First the CERT elements are written. If a CERT has any properties, then, // the PROP elements immediately precede the CERT's element. Next the CRL // elements are written. If a CRL has any properties, then, the PROP elements // immediately precede the CRL's element. Likewise for CTL elements and its // properties. Finally, the END element is written. //-------------------------------------------------------------------------- typedef struct _FILE_HDR { DWORD dwVersion; DWORD dwMagic; } FILE_HDR, *PFILE_HDR; #define CERT_FILE_VERSION_0 0 #define CERT_MAGIC ((DWORD)'C'+((DWORD)'E'<<8)+((DWORD)'R'<<16)+((DWORD)'T'<<24)) // The element's data follows the HDR typedef struct _FILE_ELEMENT_HDR { DWORD dwEleType; DWORD dwEncodingType; DWORD dwLen; } FILE_ELEMENT_HDR, *PFILE_ELEMENT_HDR; #define FILE_ELEMENT_END_TYPE 0 // FILE_ELEMENT_PROP_TYPEs !(0 | CERT | CRL | CTL | KEYID) // Note CERT_KEY_CONTEXT_PROP_ID (and CERT_KEY_PROV_HANDLE_PROP_ID) // isn't written #define FILE_ELEMENT_CERT_TYPE 32 #define FILE_ELEMENT_CRL_TYPE 33 #define FILE_ELEMENT_CTL_TYPE 34 #define FILE_ELEMENT_KEYID_TYPE 35 //#define MAX_FILE_ELEMENT_DATA_LEN (4096 * 16) #define MAX_FILE_ELEMENT_DATA_LEN 0xFFFFFFFF //+------------------------------------------------------------------------- // Used when reading an element //-------------------------------------------------------------------------- #define CSError 0 #define CSContinue 1 #define CSEnd 2 //+------------------------------------------------------------------------- // Share elements. // // A share element is identifed by its sha1 hash. It contains the context's // encoded bytes and decoded info. Multiple contexts can point to the // same refcounted share element. The share elements are stored in a // hash bucket array of linked lists. The first byte of the element's sha1 // hash is used as the index into the array. // // Note, the actual index is the first byte modulus BUCKET_COUNT. //-------------------------------------------------------------------------- #define SHARE_ELEMENT_HASH_BUCKET_COUNT 64 static PSHARE_ELEMENT rgpShareElementHashBucket[SHARE_ELEMENT_HASH_BUCKET_COUNT]; static CRITICAL_SECTION ShareElementCriticalSection; //+------------------------------------------------------------------------- // Read, Write & Skip to memory/file function definitions //-------------------------------------------------------------------------- typedef BOOL (* PFNWRITE)(HANDLE h, void * p, DWORD cb); typedef BOOL (* PFNREAD)(HANDLE h, void * p, DWORD cb); typedef BOOL (* PFNSKIP)(HANDLE h, DWORD cb); //+------------------------------------------------------------------------- // Store Provider Functions //-------------------------------------------------------------------------- STATIC BOOL WINAPI OpenMsgStoreProv( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); STATIC BOOL WINAPI OpenMemoryStoreProv( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ) { pStoreProvInfo->dwStoreProvFlags |= CERT_STORE_PROV_NO_PERSIST_FLAG; return TRUE; } STATIC BOOL WINAPI OpenFileStoreProv( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); STATIC BOOL WINAPI OpenPKCS7StoreProv( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); STATIC BOOL WINAPI OpenSerializedStoreProv( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); STATIC BOOL WINAPI OpenFilenameStoreProvA( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); STATIC BOOL WINAPI OpenFilenameStoreProvW( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); STATIC BOOL WINAPI OpenCollectionStoreProv( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ) { PCERT_STORE pStore = (PCERT_STORE) hCertStore; pStore->dwStoreType = STORE_TYPE_COLLECTION; return TRUE; } // from regstor.cpp extern BOOL WINAPI I_CertDllOpenRegStoreProv( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); extern BOOL WINAPI I_CertDllOpenSystemStoreProvA( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); extern BOOL WINAPI I_CertDllOpenSystemStoreProvW( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); extern BOOL WINAPI I_CertDllOpenSystemRegistryStoreProvW( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); extern BOOL WINAPI I_CertDllOpenSystemRegistryStoreProvA( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); extern BOOL WINAPI I_CertDllOpenPhysicalStoreProvW( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ); static HCRYPTOIDFUNCSET hOpenStoreProvFuncSet; static const CRYPT_OID_FUNC_ENTRY OpenStoreProvFuncTable[] = { CERT_STORE_PROV_MSG, OpenMsgStoreProv, CERT_STORE_PROV_MEMORY, OpenMemoryStoreProv, CERT_STORE_PROV_FILE, OpenFileStoreProv, CERT_STORE_PROV_REG, I_CertDllOpenRegStoreProv, CERT_STORE_PROV_PKCS7, OpenPKCS7StoreProv, CERT_STORE_PROV_SERIALIZED, OpenSerializedStoreProv, CERT_STORE_PROV_FILENAME_A, OpenFilenameStoreProvA, CERT_STORE_PROV_FILENAME_W, OpenFilenameStoreProvW, CERT_STORE_PROV_SYSTEM_A, I_CertDllOpenSystemStoreProvA, CERT_STORE_PROV_SYSTEM_W, I_CertDllOpenSystemStoreProvW, CERT_STORE_PROV_COLLECTION, OpenCollectionStoreProv, CERT_STORE_PROV_SYSTEM_REGISTRY_A, I_CertDllOpenSystemRegistryStoreProvA, CERT_STORE_PROV_SYSTEM_REGISTRY_W, I_CertDllOpenSystemRegistryStoreProvW, CERT_STORE_PROV_PHYSICAL_W, I_CertDllOpenPhysicalStoreProvW, CERT_STORE_PROV_SMART_CARD_W, SmartCardProvOpenStore, sz_CERT_STORE_PROV_MEMORY, OpenMemoryStoreProv, sz_CERT_STORE_PROV_SYSTEM_W, I_CertDllOpenSystemStoreProvW, sz_CERT_STORE_PROV_FILENAME_W, OpenFilenameStoreProvW, sz_CERT_STORE_PROV_PKCS7, OpenPKCS7StoreProv, sz_CERT_STORE_PROV_SERIALIZED, OpenSerializedStoreProv, sz_CERT_STORE_PROV_COLLECTION, OpenCollectionStoreProv, sz_CERT_STORE_PROV_SYSTEM_REGISTRY_W, I_CertDllOpenSystemRegistryStoreProvW, sz_CERT_STORE_PROV_PHYSICAL_W, I_CertDllOpenPhysicalStoreProvW, sz_CERT_STORE_PROV_SMART_CARD_W, SmartCardProvOpenStore }; #define OPEN_STORE_PROV_FUNC_COUNT (sizeof(OpenStoreProvFuncTable) / \ sizeof(OpenStoreProvFuncTable[0])) //+------------------------------------------------------------------------- // NULL Store: initialization and free //-------------------------------------------------------------------------- STATIC BOOL InitNullCertStore() { BOOL fRet; memset(&NullCertStore, 0, sizeof(NullCertStore)); NullCertStore.dwStoreType = STORE_TYPE_CACHE; NullCertStore.lRefCnt = 1; NullCertStore.dwState = STORE_STATE_NULL; fRet = Pki_InitializeCriticalSection(&NullCertStore.CriticalSection); NullCertStore.StoreProvInfo.dwStoreProvFlags = CERT_STORE_PROV_NO_PERSIST_FLAG; return fRet; } STATIC void FreeNullCertStore() { DeleteCriticalSection(&NullCertStore.CriticalSection); } //+------------------------------------------------------------------------- // CryptProv: initialization and free //-------------------------------------------------------------------------- STATIC BOOL InitCryptProv() { return Pki_InitializeCriticalSection(&CryptProvCriticalSection); } STATIC void FreeCryptProv() { DeleteCriticalSection(&CryptProvCriticalSection); } extern BOOL WINAPI I_RegStoreDllMain( HMODULE hInst, ULONG ulReason, LPVOID lpReserved); //+------------------------------------------------------------------------- // Dll initialization //-------------------------------------------------------------------------- BOOL WINAPI CertStoreDllMain( HMODULE hInst, ULONG ulReason, LPVOID lpReserved) { BOOL fRet; if (!I_RegStoreDllMain(hInst, ulReason, lpReserved)) return FALSE; switch (ulReason) { case DLL_PROCESS_ATTACH: // Used for "root" system store's message box hCertStoreInst = hInst; if (NULL == (hOpenStoreProvFuncSet = CryptInitOIDFunctionSet( CRYPT_OID_OPEN_STORE_PROV_FUNC, 0))) goto CryptInitOIDFunctionSetError; if (!CryptInstallOIDFunctionAddress( NULL, // hModule 0, // dwEncodingType CRYPT_OID_OPEN_STORE_PROV_FUNC, OPEN_STORE_PROV_FUNC_COUNT, OpenStoreProvFuncTable, 0)) // dwFlags goto CryptInstallOIDFunctionAddressError; if (!Pki_InitializeCriticalSection(&ShareElementCriticalSection)) goto InitShareElementCritSectionError; if (!Pki_InitializeCriticalSection(&ShareStoreCriticalSection)) goto InitShareStoreCritSectionError; if (!InitNullCertStore()) goto InitNullCertStoreError; if (!InitCryptProv()) goto InitCryptProvError; break; case DLL_PROCESS_DETACH: FreeCryptProv(); FreeNullCertStore(); DeleteCriticalSection(&ShareElementCriticalSection); DeleteCriticalSection(&ShareStoreCriticalSection); break; case DLL_THREAD_DETACH: default: break; } fRet = TRUE; CommonReturn: return fRet; InitCryptProvError: FreeNullCertStore(); InitNullCertStoreError: DeleteCriticalSection(&ShareStoreCriticalSection); InitShareStoreCritSectionError: DeleteCriticalSection(&ShareElementCriticalSection); InitShareElementCritSectionError: ErrorReturn: I_RegStoreDllMain(hInst, DLL_PROCESS_DETACH, NULL); fRet = FALSE; goto CommonReturn; TRACE_ERROR(CryptInitOIDFunctionSetError) TRACE_ERROR(CryptInstallOIDFunctionAddressError) } //+========================================================================= // Context Type Tables //========================================================================== //+------------------------------------------------------------------------- // Provider callback function indices //-------------------------------------------------------------------------- static const DWORD rgdwStoreProvFindIndex[CONTEXT_COUNT] = { CERT_STORE_PROV_FIND_CERT_FUNC, CERT_STORE_PROV_FIND_CRL_FUNC, CERT_STORE_PROV_FIND_CTL_FUNC }; static const DWORD rgdwStoreProvWriteIndex[CONTEXT_COUNT] = { CERT_STORE_PROV_WRITE_CERT_FUNC, CERT_STORE_PROV_WRITE_CRL_FUNC, CERT_STORE_PROV_WRITE_CTL_FUNC }; static const DWORD rgdwStoreProvDeleteIndex[CONTEXT_COUNT] = { CERT_STORE_PROV_DELETE_CERT_FUNC, CERT_STORE_PROV_DELETE_CRL_FUNC, CERT_STORE_PROV_DELETE_CTL_FUNC }; static const DWORD rgdwStoreProvFreeFindIndex[CONTEXT_COUNT] = { CERT_STORE_PROV_FREE_FIND_CERT_FUNC, CERT_STORE_PROV_FREE_FIND_CRL_FUNC, CERT_STORE_PROV_FREE_FIND_CTL_FUNC }; static const DWORD rgdwStoreProvGetPropertyIndex[CONTEXT_COUNT] = { CERT_STORE_PROV_GET_CERT_PROPERTY_FUNC, CERT_STORE_PROV_GET_CRL_PROPERTY_FUNC, CERT_STORE_PROV_GET_CTL_PROPERTY_FUNC }; static const DWORD rgdwStoreProvSetPropertyIndex[CONTEXT_COUNT] = { CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC, CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC, CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC }; //+------------------------------------------------------------------------- // Context data structure length and field offsets //-------------------------------------------------------------------------- static const DWORD rgcbContext[CONTEXT_COUNT] = { sizeof(CERT_CONTEXT), sizeof(CRL_CONTEXT), sizeof(CTL_CONTEXT) }; static const DWORD rgOffsetofStoreHandle[CONTEXT_COUNT] = { offsetof(CERT_CONTEXT, hCertStore), offsetof(CRL_CONTEXT, hCertStore), offsetof(CTL_CONTEXT, hCertStore) }; static const DWORD rgOffsetofEncodingType[CONTEXT_COUNT] = { offsetof(CERT_CONTEXT, dwCertEncodingType), offsetof(CRL_CONTEXT, dwCertEncodingType), offsetof(CTL_CONTEXT, dwMsgAndCertEncodingType) }; static const DWORD rgOffsetofEncodedPointer[CONTEXT_COUNT] = { offsetof(CERT_CONTEXT, pbCertEncoded), offsetof(CRL_CONTEXT, pbCrlEncoded), offsetof(CTL_CONTEXT, pbCtlEncoded) }; static const DWORD rgOffsetofEncodedCount[CONTEXT_COUNT] = { offsetof(CERT_CONTEXT, cbCertEncoded), offsetof(CRL_CONTEXT, cbCrlEncoded), offsetof(CTL_CONTEXT, cbCtlEncoded) }; //+------------------------------------------------------------------------- // Find Types //-------------------------------------------------------------------------- static const DWORD rgdwFindTypeToFindExisting[CONTEXT_COUNT] = { CERT_FIND_EXISTING, CRL_FIND_EXISTING, CTL_FIND_EXISTING }; //+------------------------------------------------------------------------- // File Element Types //-------------------------------------------------------------------------- static const DWORD rgdwFileElementType[CONTEXT_COUNT] = { FILE_ELEMENT_CERT_TYPE, FILE_ELEMENT_CRL_TYPE, FILE_ELEMENT_CTL_TYPE }; //+------------------------------------------------------------------------- // Share Element Decode Struct Types //-------------------------------------------------------------------------- static const LPCSTR rgpszShareElementStructType[CONTEXT_COUNT] = { X509_CERT_TO_BE_SIGNED, X509_CERT_CRL_TO_BE_SIGNED, 0 }; //+========================================================================= // Context Type Specific Functions //========================================================================== //+------------------------------------------------------------------------- // CERT_CONTEXT Element //-------------------------------------------------------------------------- // pbCertEncoded has already been allocated STATIC PCONTEXT_ELEMENT CreateCertElement( IN PCERT_STORE pStore, IN DWORD dwCertEncodingType, IN BYTE *pbCertEncoded, IN DWORD cbCertEncoded, IN OPTIONAL PSHARE_ELEMENT pShareEle ); STATIC void FreeCertElement(IN PCONTEXT_ELEMENT pEle); STATIC BOOL IsSameCert( IN PCCERT_CONTEXT pCert, IN PCCERT_CONTEXT pNew ); STATIC BOOL CompareCertElement( IN PCONTEXT_ELEMENT pEle, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN BOOL fArchived ); STATIC BOOL IsNewerCertElement( IN PCONTEXT_ELEMENT pNewEle, IN PCONTEXT_ELEMENT pExistingEle ); static inline PCONTEXT_ELEMENT ToContextElement( IN PCCERT_CONTEXT pCertContext ) { if (pCertContext) return (PCONTEXT_ELEMENT) (((BYTE *) pCertContext) - sizeof(CONTEXT_ELEMENT)); else return NULL; } static inline PCCERT_CONTEXT ToCertContext( IN PCONTEXT_ELEMENT pEle ) { if (pEle) return (PCCERT_CONTEXT) (((BYTE *) pEle) + sizeof(CONTEXT_ELEMENT)); else return NULL; } //+------------------------------------------------------------------------- // CRL_CONTEXT Element //-------------------------------------------------------------------------- // pbCrlEncoded has already been allocated STATIC PCONTEXT_ELEMENT CreateCrlElement( IN PCERT_STORE pStore, IN DWORD dwCertEncodingType, IN BYTE *pbCrlEncoded, IN DWORD cbCrlEncoded, IN OPTIONAL PSHARE_ELEMENT pShareEle ); STATIC void FreeCrlElement(IN PCONTEXT_ELEMENT pEle); STATIC BOOL CompareCrlElement( IN PCONTEXT_ELEMENT pEle, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN BOOL fArchived ); STATIC BOOL IsNewerCrlElement( IN PCONTEXT_ELEMENT pNewEle, IN PCONTEXT_ELEMENT pExistingEle ); static inline PCONTEXT_ELEMENT ToContextElement( IN PCCRL_CONTEXT pCrlContext ) { if (pCrlContext) return (PCONTEXT_ELEMENT) (((BYTE *) pCrlContext) - sizeof(CONTEXT_ELEMENT)); else return NULL; } static inline PCCRL_CONTEXT ToCrlContext( IN PCONTEXT_ELEMENT pEle ) { if (pEle) return (PCCRL_CONTEXT) (((BYTE *) pEle) + sizeof(CONTEXT_ELEMENT)); else return NULL; } static inline PCRL_CONTEXT_SUFFIX ToCrlContextSuffix( IN PCONTEXT_ELEMENT pEle ) { if (pEle) return (PCRL_CONTEXT_SUFFIX) (((BYTE *) pEle) + sizeof(CONTEXT_ELEMENT) + sizeof(CRL_CONTEXT)); else return NULL; } //+------------------------------------------------------------------------- // CTL_CONTEXT Element //-------------------------------------------------------------------------- // pbCtlEncoded has already been allocated STATIC PCONTEXT_ELEMENT CreateCtlElement( IN PCERT_STORE pStore, IN DWORD dwMsgAndCertEncodingType, IN BYTE *pbCtlEncoded, IN DWORD cbCtlEncoded, IN OPTIONAL PSHARE_ELEMENT pShareEle ); STATIC void FreeCtlElement(IN PCONTEXT_ELEMENT pEle); STATIC BOOL CompareCtlElement( IN PCONTEXT_ELEMENT pEle, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN BOOL fArchived ); STATIC BOOL IsNewerCtlElement( IN PCONTEXT_ELEMENT pNewEle, IN PCONTEXT_ELEMENT pExistingEle ); static inline PCONTEXT_ELEMENT ToContextElement( IN PCCTL_CONTEXT pCtlContext ) { if (pCtlContext) return (PCONTEXT_ELEMENT) (((BYTE *) pCtlContext) - sizeof(CONTEXT_ELEMENT)); else return NULL; } static inline PCCTL_CONTEXT ToCtlContext( IN PCONTEXT_ELEMENT pEle ) { if (pEle) return (PCCTL_CONTEXT) (((BYTE *) pEle) + sizeof(CONTEXT_ELEMENT)); else return NULL; } static inline PCTL_CONTEXT_SUFFIX ToCtlContextSuffix( IN PCONTEXT_ELEMENT pEle ) { if (pEle) return (PCTL_CONTEXT_SUFFIX) (((BYTE *) pEle) + sizeof(CONTEXT_ELEMENT) + sizeof(CTL_CONTEXT)); else return NULL; } //+========================================================================= // Context Type Function Tables //========================================================================== typedef PCONTEXT_ELEMENT (*PFN_CREATE_ELEMENT)( IN PCERT_STORE pStore, IN DWORD dwCertEncodingType, IN BYTE *pbCertEncoded, IN DWORD cbCertEncoded, IN OPTIONAL PSHARE_ELEMENT pShareEle ); static PFN_CREATE_ELEMENT const rgpfnCreateElement[CONTEXT_COUNT] = { CreateCertElement, CreateCrlElement, CreateCtlElement }; typedef void (*PFN_FREE_ELEMENT)( IN PCONTEXT_ELEMENT pEle ); static PFN_FREE_ELEMENT const rgpfnFreeElement[CONTEXT_COUNT] = { FreeCertElement, FreeCrlElement, FreeCtlElement }; typedef BOOL (*PFN_COMPARE_ELEMENT)( IN PCONTEXT_ELEMENT pEle, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN BOOL fArchived ); static PFN_COMPARE_ELEMENT const rgpfnCompareElement[CONTEXT_COUNT] = { CompareCertElement, CompareCrlElement, CompareCtlElement }; typedef BOOL (*PFN_IS_NEWER_ELEMENT)( IN PCONTEXT_ELEMENT pNewEle, IN PCONTEXT_ELEMENT pExistingEle ); static PFN_IS_NEWER_ELEMENT const rgpfnIsNewerElement[CONTEXT_COUNT] = { IsNewerCertElement, IsNewerCrlElement, IsNewerCtlElement }; //+========================================================================= // Store Link Functions //========================================================================== STATIC PCERT_STORE_LINK CreateStoreLink( IN PCERT_STORE pCollection, IN PCERT_STORE pSibling, IN DWORD dwUpdateFlags, IN DWORD dwPriority ); STATIC void FreeStoreLink( IN PCERT_STORE_LINK pStoreLink ); STATIC void RemoveStoreLink( IN PCERT_STORE_LINK pStoreLink ); STATIC void RemoveAndFreeStoreLink( IN PCERT_STORE_LINK pStoreLink ); static inline void AddRefStoreLink( IN PCERT_STORE_LINK pStoreLink ) { InterlockedIncrement(&pStoreLink->lRefCnt); } STATIC void ReleaseStoreLink( IN PCERT_STORE_LINK pStoreLink ); //+========================================================================= // Context Element Functions //========================================================================== STATIC DWORD GetContextEncodingType( IN PCONTEXT_ELEMENT pEle ); STATIC void GetContextEncodedInfo( IN PCONTEXT_ELEMENT pEle, OUT BYTE **ppbEncoded, OUT DWORD *pcbEncoded ); STATIC PCONTEXT_ELEMENT GetCacheElement( IN PCONTEXT_ELEMENT pCacheEle ); STATIC void AddContextElement( IN PCONTEXT_ELEMENT pEle ); STATIC void RemoveContextElement( IN PCONTEXT_ELEMENT pEle ); STATIC void FreeContextElement( IN PCONTEXT_ELEMENT pEle ); STATIC void RemoveAndFreeContextElement( IN PCONTEXT_ELEMENT pEle ); STATIC void AddRefContextElement( IN PCONTEXT_ELEMENT pEle ); STATIC void AddRefDeferClose( IN PCONTEXT_ELEMENT pEle ); STATIC void ReleaseContextElement( IN PCONTEXT_ELEMENT pEle ); STATIC BOOL DeleteContextElement( IN PCONTEXT_ELEMENT pEle ); // Returns TRUE if both elements have identical SHA1 hash. STATIC BOOL IsIdenticalContextElement( IN PCONTEXT_ELEMENT pEle1, IN PCONTEXT_ELEMENT pEle2 ); STATIC BOOL SerializeStoreElement( IN HANDLE h, IN PFNWRITE pfn, IN PCONTEXT_ELEMENT pEle ); STATIC BOOL SerializeContextElement( IN PCONTEXT_ELEMENT pEle, IN DWORD dwFlags, OUT BYTE *pbElement, IN OUT DWORD *pcbElement ); STATIC PCONTEXT_ELEMENT CreateLinkElement( IN DWORD dwContextType ); static inline void FreeLinkElement( IN PCONTEXT_ELEMENT pLinkEle ) { PkiFree(pLinkEle); } STATIC void FreeLinkContextElement( IN PCONTEXT_ELEMENT pLinkEle ); // Upon entry no locks STATIC void RemoveAndFreeLinkElement( IN PCONTEXT_ELEMENT pEle ); STATIC PCONTEXT_ELEMENT FindElementInStore( IN PCERT_STORE pStore, IN DWORD dwContextType, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN OPTIONAL PCONTEXT_ELEMENT pPrevEle ); STATIC PCONTEXT_ELEMENT CheckAutoResyncAndFindElementInStore( IN PCERT_STORE pStore, IN DWORD dwContextType, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN OPTIONAL PCONTEXT_ELEMENT pPrevEle ); STATIC BOOL AddLinkContextToCacheStore( IN PCERT_STORE pStore, IN PCONTEXT_ELEMENT pEle, IN DWORD dwAddDisposition, OUT OPTIONAL PCONTEXT_ELEMENT *ppStoreEle ); // pEle is used or freed for success only, Otherwise, its left alone and // will be freed by the caller. // // This routine may be called recursively STATIC BOOL AddElementToStore( IN PCERT_STORE pStore, IN PCONTEXT_ELEMENT pEle, IN DWORD dwAddDisposition, OUT OPTIONAL PCONTEXT_ELEMENT *ppStoreEle ); STATIC BOOL AddEncodedContextToStore( IN PCERT_STORE pStore, IN DWORD dwContextType, IN DWORD dwCertEncodingType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwAddDisposition, OUT OPTIONAL PCONTEXT_ELEMENT *ppStoreEle ); STATIC BOOL AddContextToStore( IN PCERT_STORE pStore, IN PCONTEXT_ELEMENT pSrcEle, IN DWORD dwCertEncodingType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwAddDisposition, OUT OPTIONAL PCONTEXT_ELEMENT *ppStoreEle ); //+========================================================================= // PROP_ELEMENT Functions //========================================================================== // pbData has already been allocated STATIC PPROP_ELEMENT CreatePropElement( IN DWORD dwPropId, IN DWORD dwFlags, IN BYTE *pbData, IN DWORD cbData ); STATIC void FreePropElement(IN PPROP_ELEMENT pEle); // Upon entry/exit: Store/Element is locked STATIC PPROP_ELEMENT FindPropElement( IN PPROP_ELEMENT pPropEle, IN DWORD dwPropId ); STATIC PPROP_ELEMENT FindPropElement( IN PCONTEXT_ELEMENT pCacheEle, IN DWORD dwPropId ); // Upon entry/exit: Store/Element is locked STATIC void AddPropElement( IN OUT PPROP_ELEMENT *ppPropHead, IN PPROP_ELEMENT pPropEle ); STATIC void AddPropElement( IN OUT PCONTEXT_ELEMENT pCacheEle, IN PPROP_ELEMENT pPropEle ); // Upon entry/exit: Store/Element is locked STATIC void RemovePropElement( IN OUT PPROP_ELEMENT *ppPropHead, IN PPROP_ELEMENT pPropEle ); STATIC void RemovePropElement( IN OUT PCONTEXT_ELEMENT pCacheEle, IN PPROP_ELEMENT pPropEle ); //+========================================================================= // Property Functions //========================================================================== //+------------------------------------------------------------------------- // Set the property for the specified element //-------------------------------------------------------------------------- STATIC BOOL SetProperty( IN PCONTEXT_ELEMENT pEle, IN DWORD dwPropId, IN DWORD dwFlags, IN const void *pvData, IN BOOL fInhibitProvSet = FALSE ); //+------------------------------------------------------------------------- // Get the property for the specified element //-------------------------------------------------------------------------- STATIC BOOL GetProperty( IN PCONTEXT_ELEMENT pEle, IN DWORD dwPropId, OUT void *pvData, IN OUT DWORD *pcbData ); // Upon entry/exit the store is locked STATIC void DeleteProperty( IN OUT PPROP_ELEMENT *ppPropHead, IN DWORD dwPropId ); STATIC void DeleteProperty( IN OUT PCONTEXT_ELEMENT pCacheEle, IN DWORD dwPropId ); //+------------------------------------------------------------------------- // Serialize a Property //-------------------------------------------------------------------------- STATIC BOOL SerializeProperty( IN HANDLE h, IN PFNWRITE pfn, IN PCONTEXT_ELEMENT pEle ); #define COPY_PROPERTY_USE_EXISTING_FLAG 0x1 #define COPY_PROPERTY_INHIBIT_PROV_SET_FLAG 0x2 #define COPY_PROPERTY_SYNC_FLAG 0x4 STATIC BOOL CopyProperties( IN PCONTEXT_ELEMENT pSrcEle, IN PCONTEXT_ELEMENT pDstEle, IN DWORD dwFlags ); //+------------------------------------------------------------------------- // Get the first or next PropId for the specified element // // Set dwPropId = 0, to get the first. Returns 0, if no more properties. //-------------------------------------------------------------------------- STATIC DWORD EnumProperties( IN PCONTEXT_ELEMENT pEle, IN DWORD dwPropId ); //+------------------------------------------------------------------------- // Get or set the caller properties for a store or KeyId element. //-------------------------------------------------------------------------- STATIC BOOL GetCallerProperty( IN PPROP_ELEMENT pPropHead, IN DWORD dwPropId, BOOL fAlloc, OUT void *pvData, IN OUT DWORD *pcbData ); BOOL SetCallerProperty( IN OUT PPROP_ELEMENT *ppPropHead, IN DWORD dwPropId, IN DWORD dwFlags, IN const void *pvData ); //+------------------------------------------------------------------------- // CRYPT_KEY_PROV_INFO: Encode and Decode Functions //-------------------------------------------------------------------------- #define ENCODE_LEN_ALIGN(Len) ((Len + 7) & ~7) typedef struct _SERIALIZED_KEY_PROV_PARAM { DWORD dwParam; DWORD offbData; DWORD cbData; DWORD dwFlags; } SERIALIZED_KEY_PROV_PARAM, *PSERIALIZED_KEY_PROV_PARAM; typedef struct _SERIALIZED_KEY_PROV_INFO { DWORD offwszContainerName; DWORD offwszProvName; DWORD dwProvType; DWORD dwFlags; DWORD cProvParam; DWORD offrgProvParam; DWORD dwKeySpec; } SERIALIZED_KEY_PROV_INFO, *PSERIALIZED_KEY_PROV_INFO; #define MAX_PROV_PARAM 0x00000100 #define MAX_PROV_PARAM_CBDATA 0x00010000 STATIC BOOL AllocAndEncodeKeyProvInfo( IN PCRYPT_KEY_PROV_INFO pKeyProvInfo, OUT BYTE **ppbEncoded, OUT DWORD *pcbEncoded ); STATIC BOOL DecodeKeyProvInfo( IN PSERIALIZED_KEY_PROV_INFO pSerializedInfo, IN DWORD cbSerialized, OUT PCRYPT_KEY_PROV_INFO pInfo, OUT DWORD *pcbInfo ); //+========================================================================= // KEYID_ELEMENT Functions //========================================================================== // pbKeyIdEncoded has already been allocated STATIC PKEYID_ELEMENT CreateKeyIdElement( IN BYTE *pbKeyIdEncoded, IN DWORD cbKeyIdEncoded ); STATIC void FreeKeyIdElement(IN PKEYID_ELEMENT pEle); //+========================================================================= // Key Identifier Property Functions // // If dwPropId == 0, check if the element has a KEY_PROV_INFO property //========================================================================== STATIC void SetCryptKeyIdentifierKeyProvInfoProperty( IN PCONTEXT_ELEMENT pEle, IN DWORD dwPropId = 0, IN const void *pvData = NULL ); STATIC BOOL GetKeyIdProperty( IN PCONTEXT_ELEMENT pEle, IN DWORD dwPropId, OUT void *pvData, IN OUT DWORD *pcbData ); //+------------------------------------------------------------------------- // Alloc and NOCOPY Decode //-------------------------------------------------------------------------- STATIC void *AllocAndDecodeObject( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwFlags = CRYPT_DECODE_NOCOPY_FLAG ) { DWORD cbStructInfo; void *pvStructInfo; if (!CryptDecodeObjectEx( dwCertEncodingType, lpszStructType, pbEncoded, cbEncoded, dwFlags | CRYPT_DECODE_SHARE_OID_STRING_FLAG | CRYPT_DECODE_ALLOC_FLAG, &PkiDecodePara, (void *) &pvStructInfo, &cbStructInfo )) goto ErrorReturn; CommonReturn: return pvStructInfo; ErrorReturn: pvStructInfo = NULL; goto CommonReturn; } //+------------------------------------------------------------------------- // Allocates and returns the specified cryptographic message parameter. //-------------------------------------------------------------------------- STATIC void *AllocAndGetMsgParam( IN HCRYPTMSG hMsg, IN DWORD dwParamType, IN DWORD dwIndex, OUT DWORD *pcbData ) { void *pvData; DWORD cbData; if (!CryptMsgGetParam( hMsg, dwParamType, dwIndex, NULL, // pvData &cbData) || 0 == cbData) goto GetParamError; if (NULL == (pvData = PkiNonzeroAlloc(cbData))) goto OutOfMemory; if (!CryptMsgGetParam( hMsg, dwParamType, dwIndex, pvData, &cbData)) { PkiFree(pvData); goto GetParamError; } CommonReturn: *pcbData = cbData; return pvData; ErrorReturn: pvData = NULL; cbData = 0; goto CommonReturn; TRACE_ERROR(OutOfMemory) TRACE_ERROR(GetParamError) } //+------------------------------------------------------------------------- // First try to get the EncodingType from the lower 16 bits. If 0, get // from the upper 16 bits. //-------------------------------------------------------------------------- static inline DWORD GetCertEncodingType( IN DWORD dwEncodingType ) { if (0 == dwEncodingType) return X509_ASN_ENCODING; else return (dwEncodingType & CERT_ENCODING_TYPE_MASK) ? (dwEncodingType & CERT_ENCODING_TYPE_MASK) : ((dwEncodingType >> 16) & CERT_ENCODING_TYPE_MASK); } STATIC DWORD AdjustEncodedLength( IN DWORD dwCertEncodingType, IN const BYTE *pbDER, IN DWORD cbDER ) { if (X509_ASN_ENCODING == GET_CERT_ENCODING_TYPE(dwCertEncodingType)) return Asn1UtilAdjustEncodedLength(pbDER, cbDER); else return cbDER; } //+------------------------------------------------------------------------- // Read, Write and Skip file functions //-------------------------------------------------------------------------- BOOL WriteToFile(HANDLE h, void * p, DWORD cb) { DWORD cbBytesWritten; return(WriteFile(h, p, cb, &cbBytesWritten, NULL)); } BOOL ReadFromFile( IN HANDLE h, IN void * p, IN DWORD cb ) { DWORD cbBytesRead; return(ReadFile(h, p, cb, &cbBytesRead, NULL)); } BOOL SkipInFile( IN HANDLE h, IN DWORD cb ) { DWORD dwLoFilePointer; LONG lHiFilePointer; LONG lDistanceToMove; lDistanceToMove = (LONG) cb; lHiFilePointer = 0; dwLoFilePointer = SetFilePointer( h, lDistanceToMove, &lHiFilePointer, FILE_CURRENT ); if (0xFFFFFFFF == dwLoFilePointer && NO_ERROR != GetLastError()) return FALSE; else return TRUE; } //+------------------------------------------------------------------------- // Read, Write and Skip memory fucntions //-------------------------------------------------------------------------- typedef struct _MEMINFO { BYTE * pByte; DWORD cb; DWORD cbSeek; } MEMINFO, * PMEMINFO; BOOL WriteToMemory(HANDLE h, void * p, DWORD cb) { PMEMINFO pMemInfo = (PMEMINFO) h; // See if we have room. The caller will detect an error after the final // write if (pMemInfo->cbSeek + cb <= pMemInfo->cb) { // Handle MappedFile Exceptions __try { // copy the bytes memcpy(&pMemInfo->pByte[pMemInfo->cbSeek], p, cb); } __except(EXCEPTION_EXECUTE_HANDLER) { SetLastError(GetExceptionCode()); return FALSE; } } pMemInfo->cbSeek += cb; return(TRUE); } BOOL ReadFromMemory( IN HANDLE h, IN void * p, IN DWORD cb ) { PMEMINFO pMemInfo = (PMEMINFO) h; BOOL fResult; fResult = !((pMemInfo->cb - pMemInfo->cbSeek) < cb); cb = min((pMemInfo->cb - pMemInfo->cbSeek), cb); // Handle MappedFile Exceptions __try { // copy the bytes memcpy(p, &pMemInfo->pByte[pMemInfo->cbSeek], cb); } __except(EXCEPTION_EXECUTE_HANDLER) { SetLastError(GetExceptionCode()); return FALSE; } pMemInfo->cbSeek += cb; if(!fResult) SetLastError(ERROR_END_OF_MEDIA); return(fResult); } BOOL SkipInMemory( IN HANDLE h, IN DWORD cb ) { PMEMINFO pMemInfo = (PMEMINFO) h; BOOL fResult; fResult = !((pMemInfo->cb - pMemInfo->cbSeek) < cb); cb = min((pMemInfo->cb - pMemInfo->cbSeek), cb); pMemInfo->cbSeek += cb; if(!fResult) SetLastError(ERROR_END_OF_MEDIA); return(fResult); } //+------------------------------------------------------------------------- // Lock and unlock functions //-------------------------------------------------------------------------- STATIC void LockStore(IN PCERT_STORE pStore) { EnterCriticalSection(&pStore->CriticalSection); } STATIC void UnlockStore(IN PCERT_STORE pStore) { LeaveCriticalSection(&pStore->CriticalSection); } //+------------------------------------------------------------------------- // Reference count calls to provider functions. This is necessary since // the store provider functions are called without a lock on the // store. CertCloseStore waits until the provider reference count // is decremented to zero before completing the close. // // Also used to reference count use of the store's CryptProv handle when // used without a store lock. //-------------------------------------------------------------------------- // Upon entry/exit the store is locked static inline void AddRefStoreProv(IN PCERT_STORE pStore) { pStore->lStoreProvRefCnt++; } // Upon entry/exit the store is locked static inline void ReleaseStoreProv(IN PCERT_STORE pStore) { if (0 == --pStore->lStoreProvRefCnt && pStore->hStoreProvWait) SetEvent(pStore->hStoreProvWait); } //+------------------------------------------------------------------------- // Try to get the store's CryptProv handle. // If we get the store's CryptProv handle, // then, increment the provider reference count to force another // thread's CertCloseStore to wait until we make a call to ReleaseCryptProv. // // Leave while still in the CryptProvCriticalSection. // // ReleaseCryptProv() must always be called. // // Note, if returned hCryptProv is NULL, the called CertHelper functions // will acquire and use the appropriate default provider. //-------------------------------------------------------------------------- #define RELEASE_STORE_CRYPT_PROV_FLAG 0x1 STATIC HCRYPTPROV GetCryptProv( IN PCERT_STORE pStore, OUT DWORD *pdwFlags ) { HCRYPTPROV hCryptProv; LockStore(pStore); hCryptProv = pStore->hCryptProv; if (hCryptProv) { AddRefStoreProv(pStore); *pdwFlags = RELEASE_STORE_CRYPT_PROV_FLAG; } else *pdwFlags = 0; UnlockStore(pStore); EnterCriticalSection(&CryptProvCriticalSection); return hCryptProv; } STATIC void ReleaseCryptProv( IN PCERT_STORE pStore, IN DWORD dwFlags ) { LeaveCriticalSection(&CryptProvCriticalSection); if (dwFlags & RELEASE_STORE_CRYPT_PROV_FLAG) { LockStore(pStore); ReleaseStoreProv(pStore); UnlockStore(pStore); } } //+------------------------------------------------------------------------- // Forward references //-------------------------------------------------------------------------- STATIC BOOL IsEmptyStore( IN PCERT_STORE pStore ); STATIC BOOL CloseStore( IN PCERT_STORE pStore, DWORD dwFlags ); void ArchiveManifoldCertificatesInStore( IN PCERT_STORE pStore ); //+------------------------------------------------------------------------- // Share Store Functions //-------------------------------------------------------------------------- // If the sharable LocalMachine store is already open, its returned with its // RefCnt bumped STATIC PCERT_STORE FindShareStore( IN LPCWSTR pwszStore ) { PCERT_STORE pStore = NULL; PSHARE_STORE pShare; EnterCriticalSection(&ShareStoreCriticalSection); for (pShare = pShareStoreHead; pShare; pShare = pShare->pNext) { if (0 == _wcsicmp(pShare->pwszStore, pwszStore)) { pStore = pShare->pStore; InterlockedIncrement(&pStore->lRefCnt); break; } } LeaveCriticalSection(&ShareStoreCriticalSection); return pStore; } // The LocalMachine store is added to the linked list of opened, sharable // stores. STATIC void CreateShareStore( IN LPCWSTR pwszStore, IN PCERT_STORE pStore ) { PSHARE_STORE pShare; DWORD cbwszStore; cbwszStore = (wcslen(pwszStore) + 1) * sizeof(WCHAR); if (NULL == (pShare = (PSHARE_STORE) PkiZeroAlloc( sizeof(SHARE_STORE) + cbwszStore))) return; pShare->pwszStore = (LPWSTR) &pShare[1]; memcpy(pShare->pwszStore, pwszStore, cbwszStore); pShare->pStore = pStore; pStore->pShareStore = pShare; EnterCriticalSection(&ShareStoreCriticalSection); if (pShareStoreHead) { pShare->pNext = pShareStoreHead; assert(NULL == pShareStoreHead->pPrev); pShareStoreHead->pPrev = pShare; } pShareStoreHead = pShare; LeaveCriticalSection(&ShareStoreCriticalSection); } // Upon input/exit, Store is locked. // Returns TRUE if share store was closed and freed STATIC BOOL CloseShareStore( IN PCERT_STORE pStore ) { BOOL fClose; EnterCriticalSection(&ShareStoreCriticalSection); // Check if we had a FindShareStore after the store's lRefCnt // was decremented to 0. InterlockedIncrement(&pStore->lRefCnt); if (0 == InterlockedDecrement(&pStore->lRefCnt)) { PSHARE_STORE pShare; pShare = pStore->pShareStore; assert(pShare); if (pShare) { if (pShare->pNext) pShare->pNext->pPrev = pShare->pPrev; if (pShare->pPrev) pShare->pPrev->pNext = pShare->pNext; else { assert(pShareStoreHead == pShare); pShareStoreHead = pShare->pNext; } PkiFree(pShare); } pStore->pShareStore = NULL; fClose = TRUE; } else fClose = FALSE; LeaveCriticalSection(&ShareStoreCriticalSection); return fClose; } //+------------------------------------------------------------------------- // Open the cert store using the specified store provider. // // hCryptProv specifies the crypto provider to use to create the hash // properties or verify the signature of a subject certificate or CRL. // The store doesn't need to use a private // key. If the CERT_STORE_NO_CRYPT_RELEASE_FLAG isn't set, hCryptProv is // CryptReleaseContext'ed on the final CertCloseStore. // // Note, if the open fails, hCryptProv is released if it would have been // released when the store was closed. // // If hCryptProv is zero, then, the default provider and container for the // PROV_RSA_FULL provider type is CryptAcquireContext'ed with // CRYPT_VERIFYCONTEXT access. The CryptAcquireContext is deferred until // the first create hash or verify signature. In addition, once acquired, // the default provider isn't released until process exit when crypt32.dll // is unloaded. The acquired default provider is shared across all stores // and threads. // // Use of the dwEncodingType parameter is provider dependent. The type // definition for pvPara also depends on the provider. //-------------------------------------------------------------------------- HCERTSTORE WINAPI CertOpenStore( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara ) { PCERT_STORE pStore; PFN_CERT_DLL_OPEN_STORE_PROV_FUNC pfnOpenStoreProv; BOOL fShareStore = FALSE; // LocalMachine System stores opened for SHARE and MAXIMUM_ALLOWED can // be shared. if ((CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_SHARE_STORE_FLAG | CERT_STORE_SHARE_CONTEXT_FLAG | CERT_STORE_MAXIMUM_ALLOWED_FLAG ) == dwFlags && 0 == hCryptProv ) { if (0xFFFF < (DWORD_PTR) lpszStoreProvider) { if (0 == _stricmp(sz_CERT_STORE_PROV_SYSTEM_W, lpszStoreProvider)) fShareStore = TRUE; } else { if (CERT_STORE_PROV_SYSTEM_W == lpszStoreProvider) fShareStore = TRUE; } if (fShareStore) { if (pStore = FindShareStore((LPCWSTR) pvPara)) return (HCERTSTORE) pStore; } } pStore = (PCERT_STORE) PkiZeroAlloc(sizeof(*pStore)); if (pStore) { if (!Pki_InitializeCriticalSection(&pStore->CriticalSection)) { PkiFree(pStore); pStore = NULL; } } if (pStore == NULL) { if (hCryptProv && (dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG) == 0) CryptReleaseContext(hCryptProv, 0); return NULL; } CertPerfIncrementStoreTotalCount(); CertPerfIncrementStoreCurrentCount(); pStore->StoreProvInfo.cbSize = sizeof(CERT_STORE_PROV_INFO); pStore->dwStoreType = STORE_TYPE_CACHE; pStore->lRefCnt = 1; pStore->dwState = STORE_STATE_OPENING; pStore->hCryptProv = hCryptProv; pStore->dwFlags = dwFlags; if (CERT_STORE_PROV_MEMORY == lpszStoreProvider) pStore->StoreProvInfo.dwStoreProvFlags |= CERT_STORE_PROV_NO_PERSIST_FLAG; else { if (!CryptGetOIDFunctionAddress( hOpenStoreProvFuncSet, 0, // dwEncodingType, lpszStoreProvider, 0, // dwFlags (void **) &pfnOpenStoreProv, &pStore->hStoreProvFuncAddr)) goto GetOIDFuncAddrError; if (!pfnOpenStoreProv( lpszStoreProvider, dwEncodingType, hCryptProv, dwFlags & ~CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, pvPara, (HCERTSTORE) pStore, &pStore->StoreProvInfo)) { if (0 == (dwFlags & CERT_STORE_MAXIMUM_ALLOWED_FLAG)) goto OpenStoreProvError; pStore->hCryptProv = NULL; CertCloseStore((HCERTSTORE) pStore, 0); return CertOpenStore( lpszStoreProvider, dwEncodingType, hCryptProv, (dwFlags & ~CERT_STORE_MAXIMUM_ALLOWED_FLAG) | CERT_STORE_READONLY_FLAG, pvPara ); } if (pStore->StoreProvInfo.dwStoreProvFlags & CERT_STORE_PROV_EXTERNAL_FLAG) { assert(STORE_TYPE_CACHE == pStore->dwStoreType && IsEmptyStore(pStore)); pStore->dwStoreType = STORE_TYPE_EXTERNAL; } if ((dwFlags & CERT_STORE_MANIFOLD_FLAG) && STORE_TYPE_CACHE == pStore->dwStoreType) ArchiveManifoldCertificatesInStore(pStore); } if (dwFlags & CERT_STORE_DELETE_FLAG) { if (0 == (pStore->StoreProvInfo.dwStoreProvFlags & CERT_STORE_PROV_DELETED_FLAG)) goto DeleteNotSupported; CertCloseStore((HCERTSTORE) pStore, 0); pStore = NULL; SetLastError(0); } else { pStore->dwState = STORE_STATE_OPEN; if (fShareStore) CreateShareStore((LPCWSTR) pvPara, pStore); } CommonReturn: return (HCERTSTORE) pStore; ErrorReturn: CertCloseStore((HCERTSTORE) pStore, 0); pStore = NULL; if (dwFlags & CERT_STORE_DELETE_FLAG) { if (0 == GetLastError()) SetLastError((DWORD) E_UNEXPECTED); } goto CommonReturn; TRACE_ERROR(GetOIDFuncAddrError) TRACE_ERROR(OpenStoreProvError) SET_ERROR(DeleteNotSupported, ERROR_CALL_NOT_IMPLEMENTED) } //+------------------------------------------------------------------------- // Duplicate a cert store handle //-------------------------------------------------------------------------- HCERTSTORE WINAPI CertDuplicateStore( IN HCERTSTORE hCertStore ) { PCERT_STORE pStore = (PCERT_STORE) hCertStore; assert(pStore->dwState == STORE_STATE_OPEN || pStore->dwState == STORE_STATE_OPENING || pStore->dwState == STORE_STATE_DEFER_CLOSING || pStore->dwState == STORE_STATE_NULL); InterlockedIncrement(&pStore->lRefCnt); return hCertStore; } //+------------------------------------------------------------------------- // Checks if the store has any Certs, CRLs, CTLs, collection stores or // links //-------------------------------------------------------------------------- STATIC BOOL IsEmptyStore( IN PCERT_STORE pStore ) { DWORD i; // Check that all the context lists are empty for (i = 0; i < CONTEXT_COUNT; i++) { if (pStore->rgpContextListHead[i]) return FALSE; } // For collection, check that all stores have been removed if (pStore->pStoreListHead) return FALSE; return TRUE; } //+------------------------------------------------------------------------- // Free the store if empty // // Store is locked upon input and unlocked or freed upon returning //-------------------------------------------------------------------------- STATIC void FreeStore( IN PCERT_STORE pStore) { if (STORE_STATE_DEFER_CLOSING == pStore->dwState) { // Check if duplicated context reference count is zero. InterlockedIncrement(&pStore->lDeferCloseRefCnt); if (InterlockedDecrement(&pStore->lDeferCloseRefCnt) == 0) CloseStore(pStore, 0); else UnlockStore(pStore); } else if (STORE_STATE_CLOSED == pStore->dwState && IsEmptyStore(pStore)) { UnlockStore(pStore); pStore->dwState = STORE_STATE_DELETED; assert(NULL == pStore->pShareStore); if (pStore->hAutoResyncEvent) CloseHandle(pStore->hAutoResyncEvent); DeleteCriticalSection(&pStore->CriticalSection); PkiFree(pStore); } else UnlockStore(pStore); } // Store is locked upon input and unlocked or freed upon returning STATIC BOOL CloseStore( IN PCERT_STORE pStore, DWORD dwFlags ) { DWORD dwFailFlags = 0; DWORD i; PCONTEXT_ELEMENT pFreeLinkEleHead; PCERT_STORE_LINK pStoreLink; PCERT_STORE_LINK pFreeStoreLinkHead; PPROP_ELEMENT pPropEle; DWORD cStoreProvFunc; BOOL fFreeFindNext; PFN_CERT_STORE_PROV_CLOSE pfnStoreProvClose; HCRYPTPROV hCryptProv; assert(pStore); assert(NULL == pStore->pShareStore); CertPerfDecrementStoreCurrentCount(); // Assert that another thread isn't already waiting for a provider // function to complete. assert(NULL == pStore->hStoreProvWait); // Assert that another thread isn't already waiting for a provider // to return from its close callback. assert(pStore->dwState != STORE_STATE_CLOSING && pStore->dwState != STORE_STATE_CLOSED); if (pStore->hStoreProvWait || pStore->dwState == STORE_STATE_CLOSING || pStore->dwState == STORE_STATE_CLOSED) goto UnexpectedError; assert(pStore->dwState == STORE_STATE_OPEN || pStore->dwState == STORE_STATE_OPENING || pStore->dwState == STORE_STATE_DEFER_CLOSING); pStore->dwState = STORE_STATE_CLOSING; cStoreProvFunc = pStore->StoreProvInfo.cStoreProvFunc; // By setting the following to 0 inhibits anyone else from calling // the provider's functions. pStore->StoreProvInfo.cStoreProvFunc = 0; if (cStoreProvFunc > CERT_STORE_PROV_CLOSE_FUNC) pfnStoreProvClose = (PFN_CERT_STORE_PROV_CLOSE) pStore->StoreProvInfo.rgpvStoreProvFunc[ CERT_STORE_PROV_CLOSE_FUNC]; else pfnStoreProvClose = NULL; hCryptProv = pStore->hCryptProv; // By setting the following to 0 inhibits anyone else from using // the store's CryptProv handle pStore->hCryptProv = 0; fFreeFindNext = FALSE; if (STORE_TYPE_EXTERNAL == pStore->dwStoreType) { // Check if any FIND_NEXT external elements are remaining to be freed for (i = 0; i < CONTEXT_COUNT; i++) { PCONTEXT_ELEMENT pEle = pStore->rgpContextListHead[i]; for ( ; pEle; pEle = pEle->pNext) { if (pEle->dwFlags & ELEMENT_FIND_NEXT_FLAG) { pEle->dwFlags &= ~ELEMENT_FIND_NEXT_FLAG; pEle->dwFlags |= ELEMENT_CLOSE_FIND_NEXT_FLAG; AddRefContextElement(pEle); fFreeFindNext = TRUE; } } } } if (pStore->lStoreProvRefCnt) { // Wait for all the provider functions to complete and all // uses of the hCryptProv handle to finish if (NULL == (pStore->hStoreProvWait = CreateEvent( NULL, // lpsa FALSE, // fManualReset FALSE, // fInitialState NULL))) { // lpszEventName assert(pStore->hStoreProvWait); goto UnexpectedError; } while (pStore->lStoreProvRefCnt) { UnlockStore(pStore); WaitForSingleObject(pStore->hStoreProvWait, INFINITE); LockStore(pStore); } CloseHandle(pStore->hStoreProvWait); pStore->hStoreProvWait = NULL; } if (fFreeFindNext) { // Call the provider to free the FIND_NEXT element. Must call // without holding a store lock. for (i = 0; i < CONTEXT_COUNT; i++) { const DWORD dwStoreProvFreeFindIndex = rgdwStoreProvFreeFindIndex[i]; PCONTEXT_ELEMENT pEle = pStore->rgpContextListHead[i]; while (pEle) { if (pEle->dwFlags & ELEMENT_CLOSE_FIND_NEXT_FLAG) { PCONTEXT_ELEMENT pEleFree = pEle; PFN_CERT_STORE_PROV_FREE_FIND_CERT pfnStoreProvFreeFindCert; pEle = pEle->pNext; while (pEle && 0 == (pEle->dwFlags & ELEMENT_CLOSE_FIND_NEXT_FLAG)) pEle = pEle->pNext; UnlockStore(pStore); if (dwStoreProvFreeFindIndex < cStoreProvFunc && NULL != (pfnStoreProvFreeFindCert = (PFN_CERT_STORE_PROV_FREE_FIND_CERT) pStore->StoreProvInfo.rgpvStoreProvFunc[ dwStoreProvFreeFindIndex])) pfnStoreProvFreeFindCert( pStore->StoreProvInfo.hStoreProv, ToCertContext(pEleFree->pEle), pEleFree->External.pvProvInfo, 0 // dwFlags ); ReleaseContextElement(pEleFree); LockStore(pStore); } else pEle = pEle->pNext; } } } if (pfnStoreProvClose) { // To prevent any type of deadlock, call the provider functions // without a lock on the store. // // Note our state is CLOSING, not CLOSED. This prevents any other // calls to FreeStore() from prematurely deleting the store. UnlockStore(pStore); pfnStoreProvClose(pStore->StoreProvInfo.hStoreProv, dwFlags); LockStore(pStore); } if (pStore->hStoreProvFuncAddr) CryptFreeOIDFunctionAddress(pStore->hStoreProvFuncAddr, 0); if (pStore->StoreProvInfo.hStoreProvFuncAddr2) CryptFreeOIDFunctionAddress( pStore->StoreProvInfo.hStoreProvFuncAddr2, 0); // Since hCryptProv was passed to the provider it must be released // last!!! if (hCryptProv && 0 == (pStore->dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG)) CryptReleaseContext(hCryptProv, 0); // Iterate through the elements. If the element hasn't already been // deleted, remove the store's reference on the element. Remove and // free if no other references. pFreeLinkEleHead = NULL; for (i = 0; i < CONTEXT_COUNT; i++) { PCONTEXT_ELEMENT pEle = pStore->rgpContextListHead[i]; while (pEle) { PCONTEXT_ELEMENT pEleNext = pEle->pNext; if (0 == (pEle->dwFlags & ELEMENT_DELETED_FLAG)) { if (0 == InterlockedDecrement(&pEle->lRefCnt)) { if (ELEMENT_TYPE_LINK_CONTEXT == pEle->dwElementType) { // The LINK_CONTEXT can't be freed while holding // the store lock. Will free later after unlocking. RemoveContextElement(pEle); pEle->pNext = pFreeLinkEleHead; pFreeLinkEleHead = pEle; } else { assert(ELEMENT_TYPE_CACHE == pEle->dwElementType); RemoveAndFreeContextElement(pEle); } } else // Still a reference on the element pEle->dwFlags |= ELEMENT_DELETED_FLAG; } // else // A previous delete has already removed the store's reference pEle = pEleNext; } } // Iterate through the store links. If the store link hasn't already been // deleted, remove the store's reference on the link. Remove and free // if no other references. pFreeStoreLinkHead = NULL; pStoreLink = pStore->pStoreListHead; while (pStoreLink) { PCERT_STORE_LINK pStoreLinkNext = pStoreLink->pNext; if (0 == (pStoreLink->dwFlags & STORE_LINK_DELETED_FLAG)) { if (0 == InterlockedDecrement(&pStoreLink->lRefCnt)) { // The STORE_LINK can't be freed while holding // the store lock. Will free later after unlocking. RemoveStoreLink(pStoreLink); pStoreLink->pNext = pFreeStoreLinkHead; pFreeStoreLinkHead = pStoreLink; } else // Still a reference on the store link pStoreLink->dwFlags |= STORE_LINK_DELETED_FLAG; } // else // A previous delete has already removed the store's reference pStoreLink = pStoreLinkNext; } if (pFreeLinkEleHead || pFreeStoreLinkHead) { // Unlock the store before freeing links UnlockStore(pStore); while (pFreeLinkEleHead) { PCONTEXT_ELEMENT pEle = pFreeLinkEleHead; pFreeLinkEleHead = pFreeLinkEleHead->pNext; FreeLinkContextElement(pEle); } while (pFreeStoreLinkHead) { pStoreLink = pFreeStoreLinkHead; pFreeStoreLinkHead = pFreeStoreLinkHead->pNext; if (pStore->hAutoResyncEvent) { CertControlStore( (HCERTSTORE) pStoreLink->pSibling, 0, // dwFlags CERT_STORE_CTRL_CANCEL_NOTIFY, &pStore->hAutoResyncEvent ); } FreeStoreLink(pStoreLink); } LockStore(pStore); } // Free the store's property elements while (pPropEle = pStore->pPropHead) { RemovePropElement(&pStore->pPropHead, pPropEle); FreePropElement(pPropEle); } if (dwFlags & CERT_CLOSE_STORE_CHECK_FLAG) { if (!IsEmptyStore(pStore)) dwFailFlags = CERT_CLOSE_STORE_CHECK_FLAG; } if (dwFlags & CERT_CLOSE_STORE_FORCE_FLAG) { UnlockStore(pStore); for (i = 0; i < CONTEXT_COUNT; i++) { PCONTEXT_ELEMENT pEle; while (pEle = pStore->rgpContextListHead[i]) { if (ELEMENT_TYPE_CACHE == pEle->dwElementType) RemoveAndFreeContextElement(pEle); else RemoveAndFreeLinkElement(pEle); } } while (pStoreLink = pStore->pStoreListHead) RemoveAndFreeStoreLink(pStoreLink); LockStore(pStore); assert(IsEmptyStore(pStore)); } pStore->dwState = STORE_STATE_CLOSED; // Either frees or unlocks the store FreeStore(pStore); if (dwFlags & dwFailFlags) { SetLastError((DWORD) CRYPT_E_PENDING_CLOSE); return FALSE; } else return TRUE; UnexpectedError: UnlockStore(pStore); SetLastError((DWORD) E_UNEXPECTED); return FALSE; } //+------------------------------------------------------------------------- // Close a cert store handle. // // There needs to be a corresponding close for each open and duplicate. // // Even on the final close, the cert store isn't freed until all of its // certificate, CRL and CTL contexts have also been freed. // // On the final close, the hCryptProv passed to CertOpenStore is // CryptReleaseContext'ed. // // LastError is preserved unless CERT_CLOSE_STORE_CHECK_FLAG is set and FALSE // is returned. //-------------------------------------------------------------------------- BOOL WINAPI CertCloseStore( IN HCERTSTORE hCertStore, DWORD dwFlags ) { BOOL fResult; BOOL fClose; BOOL fPendingError = FALSE; PCERT_STORE pStore = (PCERT_STORE) hCertStore; DWORD dwErr = GetLastError(); // For success, don't globber LastError if (pStore == NULL) return TRUE; if (dwFlags & CERT_CLOSE_STORE_FORCE_FLAG) { LockStore(pStore); if (pStore->lRefCnt != 1) { if (dwFlags & CERT_CLOSE_STORE_CHECK_FLAG) fPendingError = TRUE; } pStore->lRefCnt = 0; } else if (InterlockedDecrement(&pStore->lRefCnt) == 0) { LockStore(pStore); if (pStore->dwFlags & CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG) { // Check if duplicated context reference count is zero. InterlockedIncrement(&pStore->lDeferCloseRefCnt); if (InterlockedDecrement(&pStore->lDeferCloseRefCnt) != 0) { assert(pStore->dwState == STORE_STATE_OPEN || pStore->dwState == STORE_STATE_OPENING || pStore->dwState == STORE_STATE_DEFER_CLOSING); pStore->dwState = STORE_STATE_DEFER_CLOSING; UnlockStore(pStore); goto PendingCloseReturn; } } } else // Still holding a reference count on the store goto PendingCloseReturn; if (pStore->pShareStore) { assert(0 == (dwFlags & CERT_CLOSE_STORE_FORCE_FLAG)); // There's a window where the shared store's RefCnt can be incremented // before being removed from the linked list of share stores. fClose = CloseShareStore(pStore); } else fClose = TRUE; // Don't allow the NULL store to be closed assert(pStore->dwState != STORE_STATE_NULL); if (pStore->dwState == STORE_STATE_NULL) { pStore->lRefCnt = 1; UnlockStore(pStore); SetLastError((DWORD) E_UNEXPECTED); return FALSE; } // CloseStore() unlocks or frees store if (fClose) fResult = CloseStore(pStore, dwFlags); else { fResult = TRUE; UnlockStore(pStore); } if (fResult) { if (fPendingError) { fResult = FALSE; SetLastError((DWORD) CRYPT_E_PENDING_CLOSE); } else SetLastError(dwErr); } return fResult; PendingCloseReturn: if (dwFlags & CERT_CLOSE_STORE_CHECK_FLAG) { SetLastError((DWORD) CRYPT_E_PENDING_CLOSE); fResult = FALSE; } else fResult = TRUE; return fResult; } //+========================================================================= // ArchiveManifoldCertificatesInStore //========================================================================== #define SORTED_MANIFOLD_ALLOC_COUNT 25 typedef struct _SORTED_MANIFOLD_ENTRY { PCCERT_CONTEXT pCert; CRYPT_OBJID_BLOB Value; } SORTED_MANIFOLD_ENTRY, *PSORTED_MANIFOLD_ENTRY; //+------------------------------------------------------------------------- // Called by qsort. // // The Manifold entries are sorted according to manifold value and // the certificate's NotAfter and NotBefore times. //-------------------------------------------------------------------------- STATIC int __cdecl CompareManifoldEntry( IN const void *pelem1, IN const void *pelem2 ) { PSORTED_MANIFOLD_ENTRY p1 = (PSORTED_MANIFOLD_ENTRY) pelem1; PSORTED_MANIFOLD_ENTRY p2 = (PSORTED_MANIFOLD_ENTRY) pelem2; DWORD cb1 = p1->Value.cbData; DWORD cb2 = p2->Value.cbData; if (cb1 == cb2) { int iCmp; if (0 == cb1) iCmp = 0; else iCmp = memcmp(p1->Value.pbData, p2->Value.pbData, cb1); if (0 != iCmp) return iCmp; // Same manifold value. Compare the certificate NotAfter and // NotBefore times. iCmp = CompareFileTime(&p1->pCert->pCertInfo->NotAfter, &p2->pCert->pCertInfo->NotAfter); if (0 == iCmp) iCmp = CompareFileTime(&p1->pCert->pCertInfo->NotBefore, &p2->pCert->pCertInfo->NotBefore); return iCmp; } else if (cb1 < cb2) return -1; else return 1; } void ArchiveManifoldCertificatesInStore( IN PCERT_STORE pStore ) { PCONTEXT_ELEMENT pEle; DWORD cAlloc = 0; DWORD cManifold = 0; PSORTED_MANIFOLD_ENTRY pManifold = NULL; assert(STORE_TYPE_CACHE == pStore->dwStoreType); LockStore(pStore); // Create an array of non-archived certificates having the Manifold // extension pEle = pStore->rgpContextListHead[CERT_STORE_CERTIFICATE_CONTEXT - 1]; for ( ; pEle; pEle = pEle->pNext) { PCCERT_CONTEXT pCert; PCERT_INFO pCertInfo; PCERT_EXTENSION pExt; assert(ELEMENT_TYPE_CACHE == pEle->dwElementType || ELEMENT_TYPE_LINK_CONTEXT == pEle->dwElementType); // Skip past deleted or archived elements if (pEle->dwFlags & (ELEMENT_DELETED_FLAG | ELEMENT_ARCHIVED_FLAG)) continue; pCert = ToCertContext(pEle); pCertInfo = pCert->pCertInfo; if (pExt = CertFindExtension( szOID_CERT_MANIFOLD, pCertInfo->cExtension, pCertInfo->rgExtension )) { if (cManifold >= cAlloc) { PSORTED_MANIFOLD_ENTRY pNewManifold; if (NULL == (pNewManifold = (PSORTED_MANIFOLD_ENTRY) PkiRealloc( pManifold, (cAlloc + SORTED_MANIFOLD_ALLOC_COUNT) * sizeof(SORTED_MANIFOLD_ENTRY)))) continue; pManifold = pNewManifold; cAlloc += SORTED_MANIFOLD_ALLOC_COUNT; } pManifold[cManifold].pCert = CertDuplicateCertificateContext(pCert); pManifold[cManifold].Value = pExt->Value; cManifold++; } } UnlockStore(pStore); if (cManifold) { const CRYPT_DATA_BLOB ManifoldBlob = { 0, NULL }; // Sort the Manifold entries according to manifold value and // the certificate's NotAfter and NotBefore times. qsort(pManifold, cManifold, sizeof(SORTED_MANIFOLD_ENTRY), CompareManifoldEntry); // Set the Archive property for previous entries having the same // manifold value. for (DWORD i = 0; i < cManifold - 1; i++) { if (pManifold[i].Value.cbData == pManifold[i+1].Value.cbData && (0 == pManifold[i].Value.cbData || 0 == memcmp(pManifold[i].Value.pbData, pManifold[i+1].Value.pbData, pManifold[i].Value.cbData))) CertSetCertificateContextProperty( pManifold[i].pCert, CERT_ARCHIVED_PROP_ID, CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, (const void *) &ManifoldBlob ); } while (cManifold--) CertFreeCertificateContext(pManifold[cManifold].pCert); PkiFree(pManifold); } } //+------------------------------------------------------------------------- // Write the CERT, CRL, CTL, PROP or END element to the file or memory // // Upon entry/exit the store is locked. //-------------------------------------------------------------------------- STATIC BOOL WriteStoreElement( IN HANDLE h, IN PFNWRITE pfnWrite, IN DWORD dwEncodingType, IN DWORD dwEleType, IN BYTE *pbData, IN DWORD cbData ) { FILE_ELEMENT_HDR EleHdr; BOOL fResult; EleHdr.dwEleType = dwEleType; EleHdr.dwEncodingType = dwEncodingType; EleHdr.dwLen = cbData; assert(cbData <= MAX_FILE_ELEMENT_DATA_LEN); fResult = pfnWrite( h, &EleHdr, sizeof(EleHdr) ); if (fResult && cbData > 0) fResult = pfnWrite( h, pbData, cbData ); return fResult; } //+------------------------------------------------------------------------- // Serialize the certs, CRLs, CTLs and properties in the store. Prepend with a // file header and append with an end element. //-------------------------------------------------------------------------- STATIC BOOL SerializeStore( IN HANDLE h, IN PFNWRITE pfnWrite, IN PCERT_STORE pStore ) { BOOL fResult; DWORD i; FILE_HDR FileHdr; FileHdr.dwVersion = CERT_FILE_VERSION_0; FileHdr.dwMagic = CERT_MAGIC; if (!pfnWrite(h, &FileHdr, sizeof(FileHdr))) goto WriteError; for (i = 0; i < CONTEXT_COUNT; i++) { PCONTEXT_ELEMENT pEle = NULL; while (pEle = FindElementInStore(pStore, i, &FindAnyInfo, pEle)) { if (!SerializeStoreElement(h, pfnWrite, pEle)) { ReleaseContextElement(pEle); goto SerializeError; } } } if (!WriteStoreElement( h, pfnWrite, 0, // dwEncodingType FILE_ELEMENT_END_TYPE, NULL, // pbData 0 // cbData )) goto WriteError; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(WriteError) TRACE_ERROR(SerializeError) } //+------------------------------------------------------------------------- // Called by CertStoreSaveEx for CERT_STORE_SAVE_AS_STORE //-------------------------------------------------------------------------- STATIC BOOL SaveAsStore( IN PCERT_STORE pStore, IN DWORD dwSaveTo, IN OUT void *pvSaveToPara, IN DWORD dwFlags ) { BOOL fResult; switch (dwSaveTo) { case CERT_STORE_SAVE_TO_FILE: fResult = SerializeStore( (HANDLE) pvSaveToPara, WriteToFile, pStore); break; case CERT_STORE_SAVE_TO_MEMORY: { PCRYPT_DATA_BLOB pData = (PCRYPT_DATA_BLOB) pvSaveToPara; MEMINFO MemInfo; MemInfo.pByte = pData->pbData; if (NULL == pData->pbData) MemInfo.cb = 0; else MemInfo.cb = pData->cbData; MemInfo.cbSeek = 0; if (fResult = SerializeStore( (HANDLE) &MemInfo, WriteToMemory, pStore)) { if (MemInfo.cbSeek > MemInfo.cb && pData->pbData) { SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; } pData->cbData = MemInfo.cbSeek; } else pData->cbData = 0; } break; default: SetLastError((DWORD) E_UNEXPECTED); fResult = FALSE; } return fResult; } //+------------------------------------------------------------------------- // Following routines support the SaveAsPKCS7 function //-------------------------------------------------------------------------- STATIC void FreeSaveAsPKCS7Info( IN PCMSG_SIGNED_ENCODE_INFO pInfo, IN PCCERT_CONTEXT *ppCert, IN PCCRL_CONTEXT *ppCrl ) { DWORD dwIndex; dwIndex = pInfo->cCertEncoded; while (dwIndex--) CertFreeCertificateContext(ppCert[dwIndex]); PkiFree(ppCert); PkiFree(pInfo->rgCertEncoded); pInfo->cCertEncoded = 0; pInfo->rgCertEncoded = NULL; dwIndex = pInfo->cCrlEncoded; while (dwIndex--) CertFreeCRLContext(ppCrl[dwIndex]); PkiFree(ppCrl); PkiFree(pInfo->rgCrlEncoded); pInfo->cCrlEncoded = 0; pInfo->rgCrlEncoded = NULL; } #define SAVE_AS_PKCS7_ALLOC_COUNT 50 // Upon entry: store is unlocked STATIC BOOL InitSaveAsPKCS7Info( IN PCERT_STORE pStore, IN OUT PCMSG_SIGNED_ENCODE_INFO pInfo, OUT PCCERT_CONTEXT **pppCert, OUT PCCRL_CONTEXT **pppCrl ) { BOOL fResult; DWORD cAlloc; DWORD dwIndex; PCRYPT_DATA_BLOB pBlob; PCCERT_CONTEXT pCert = NULL; PCCERT_CONTEXT *ppCert = NULL; PCCRL_CONTEXT pCrl = NULL; PCCRL_CONTEXT *ppCrl = NULL; memset(pInfo, 0, sizeof(CMSG_SIGNED_ENCODE_INFO)); pInfo->cbSize = sizeof(CMSG_SIGNED_ENCODE_INFO); dwIndex = 0; cAlloc = 0; pBlob = NULL; while (pCert = CertEnumCertificatesInStore((HCERTSTORE) pStore, pCert)) { if (dwIndex >= cAlloc) { PCRYPT_DATA_BLOB pNewBlob; PCCERT_CONTEXT *ppNewCert; if (NULL == (pNewBlob = (PCRYPT_DATA_BLOB) PkiRealloc( pBlob, (cAlloc + SAVE_AS_PKCS7_ALLOC_COUNT) * sizeof(CRYPT_DATA_BLOB)))) goto OutOfMemory; pBlob = pNewBlob; pInfo->rgCertEncoded = pBlob; if (NULL == (ppNewCert = (PCCERT_CONTEXT *) PkiRealloc( ppCert, (cAlloc + SAVE_AS_PKCS7_ALLOC_COUNT) * sizeof(PCCERT_CONTEXT)))) goto OutOfMemory; ppCert = ppNewCert; cAlloc += SAVE_AS_PKCS7_ALLOC_COUNT; } ppCert[dwIndex] = CertDuplicateCertificateContext(pCert); pBlob[dwIndex].pbData = pCert->pbCertEncoded; pBlob[dwIndex].cbData = pCert->cbCertEncoded; pInfo->cCertEncoded = ++dwIndex; } dwIndex = 0; cAlloc = 0; pBlob = NULL; while (pCrl = CertEnumCRLsInStore((HCERTSTORE) pStore, pCrl)) { if (dwIndex >= cAlloc) { PCRYPT_DATA_BLOB pNewBlob; PCCRL_CONTEXT *ppNewCrl; if (NULL == (pNewBlob = (PCRYPT_DATA_BLOB) PkiRealloc( pBlob, (cAlloc + SAVE_AS_PKCS7_ALLOC_COUNT) * sizeof(CRYPT_DATA_BLOB)))) goto OutOfMemory; pBlob = pNewBlob; pInfo->rgCrlEncoded = pBlob; if (NULL == (ppNewCrl = (PCCRL_CONTEXT *) PkiRealloc( ppCrl, (cAlloc + SAVE_AS_PKCS7_ALLOC_COUNT) * sizeof(PCCRL_CONTEXT)))) goto OutOfMemory; ppCrl = ppNewCrl; cAlloc += SAVE_AS_PKCS7_ALLOC_COUNT; } ppCrl[dwIndex] = CertDuplicateCRLContext(pCrl); pBlob[dwIndex].pbData = pCrl->pbCrlEncoded; pBlob[dwIndex].cbData = pCrl->cbCrlEncoded; pInfo->cCrlEncoded = ++dwIndex; } fResult = TRUE; CommonReturn: *pppCert = ppCert; *pppCrl = ppCrl; return fResult; ErrorReturn: if (pCert) CertFreeCertificateContext(pCert); if (pCrl) CertFreeCRLContext(pCrl); FreeSaveAsPKCS7Info(pInfo, ppCert, ppCrl); ppCert = NULL; ppCrl = NULL; fResult = FALSE; goto CommonReturn; TRACE_ERROR(OutOfMemory) } STATIC BOOL EncodePKCS7( IN DWORD dwEncodingType, IN PCMSG_SIGNED_ENCODE_INFO pInfo, OUT BYTE *pbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult = TRUE; DWORD cbEncoded; if (NULL == pbEncoded) cbEncoded = 0; else cbEncoded = *pcbEncoded; if (0 == cbEncoded) cbEncoded = CryptMsgCalculateEncodedLength( dwEncodingType, 0, // dwFlags CMSG_SIGNED, pInfo, NULL, // pszInnerContentObjID 0 // cbData ); else { HCRYPTMSG hMsg; if (NULL == (hMsg = CryptMsgOpenToEncode( dwEncodingType, 0, // dwFlags CMSG_SIGNED, pInfo, NULL, // pszInnerContentObjID NULL // pStreamInfo ))) cbEncoded = 0; else { if (CryptMsgUpdate( hMsg, NULL, // pbData 0, // cbData TRUE // fFinal )) fResult = CryptMsgGetParam( hMsg, CMSG_CONTENT_PARAM, 0, // dwIndex pbEncoded, &cbEncoded); else cbEncoded = 0; CryptMsgClose(hMsg); } } if (fResult) { if (0 == cbEncoded) fResult = FALSE; else if (pbEncoded && cbEncoded > *pcbEncoded) { SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; } } *pcbEncoded = cbEncoded; return fResult; } //+------------------------------------------------------------------------- // Called by CertStoreSaveEx for CERT_STORE_SAVE_AS_PKCS7 //-------------------------------------------------------------------------- STATIC BOOL SaveAsPKCS7( IN PCERT_STORE pStore, IN DWORD dwEncodingType, IN DWORD dwSaveTo, IN OUT void *pvSaveToPara, IN DWORD dwFlags ) { BOOL fResult; CMSG_SIGNED_ENCODE_INFO SignedEncodeInfo; PCCERT_CONTEXT *ppCert; PCCRL_CONTEXT *ppCrl; BYTE *pbEncoded = NULL; DWORD cbEncoded; if (0 == GET_CERT_ENCODING_TYPE(dwEncodingType) || 0 == GET_CMSG_ENCODING_TYPE(dwEncodingType)) { SetLastError((DWORD) E_INVALIDARG); return FALSE; } if (!InitSaveAsPKCS7Info( pStore, &SignedEncodeInfo, &ppCert, &ppCrl)) goto InitInfoError; switch (dwSaveTo) { case CERT_STORE_SAVE_TO_FILE: if (!EncodePKCS7( dwEncodingType, &SignedEncodeInfo, NULL, // pbEncoded &cbEncoded)) goto EncodePKCS7Error; if (NULL == (pbEncoded = (BYTE *) PkiNonzeroAlloc(cbEncoded))) goto OutOfMemory; if (!EncodePKCS7( dwEncodingType, &SignedEncodeInfo, pbEncoded, &cbEncoded)) goto EncodePKCS7Error; else { DWORD cbBytesWritten; if (!WriteFile( (HANDLE) pvSaveToPara, pbEncoded, cbEncoded, &cbBytesWritten, NULL // lpOverlapped )) goto WriteError; } break; case CERT_STORE_SAVE_TO_MEMORY: { PCRYPT_DATA_BLOB pData = (PCRYPT_DATA_BLOB) pvSaveToPara; if (!EncodePKCS7( dwEncodingType, &SignedEncodeInfo, pData->pbData, &pData->cbData)) goto EncodePKCS7Error; } break; default: goto UnexpectedError; } fResult = TRUE; CommonReturn: FreeSaveAsPKCS7Info(&SignedEncodeInfo, ppCert, ppCrl); PkiFree(pbEncoded); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(InitInfoError) TRACE_ERROR(EncodePKCS7Error) TRACE_ERROR(WriteError) TRACE_ERROR(OutOfMemory) SET_ERROR(UnexpectedError, E_UNEXPECTED) } //+------------------------------------------------------------------------- // Save the cert store. Enhanced version with lots of options. //-------------------------------------------------------------------------- BOOL WINAPI CertSaveStore( IN HCERTSTORE hCertStore, IN DWORD dwEncodingType, IN DWORD dwSaveAs, IN DWORD dwSaveTo, IN OUT void *pvSaveToPara, IN DWORD dwFlags ) { assert(pvSaveToPara); switch (dwSaveTo) { case CERT_STORE_SAVE_TO_FILENAME_A: { BOOL fResult; LPWSTR pwszFilename; if (NULL == (pwszFilename = MkWStr((LPSTR) pvSaveToPara))) return FALSE; fResult = CertSaveStore( hCertStore, dwEncodingType, dwSaveAs, CERT_STORE_SAVE_TO_FILENAME_W, (void *) pwszFilename, dwFlags); FreeWStr(pwszFilename); return fResult; } break; case CERT_STORE_SAVE_TO_FILENAME_W: { BOOL fResult; HANDLE hFile; if (INVALID_HANDLE_VALUE == (hFile = CreateFileU( (LPWSTR) pvSaveToPara, GENERIC_WRITE, 0, // fdwShareMode NULL, // lpsa CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL // hTemplateFile ))) return FALSE; fResult = CertSaveStore( hCertStore, dwEncodingType, dwSaveAs, CERT_STORE_SAVE_TO_FILE, (void *) hFile, dwFlags); CloseHandle(hFile); return fResult; } break; case CERT_STORE_SAVE_TO_FILE: case CERT_STORE_SAVE_TO_MEMORY: break; default: SetLastError((DWORD) E_INVALIDARG); return FALSE; } switch (dwSaveAs) { case CERT_STORE_SAVE_AS_STORE: return SaveAsStore( (PCERT_STORE) hCertStore, dwSaveTo, pvSaveToPara, dwFlags); break; case CERT_STORE_SAVE_AS_PKCS7: return SaveAsPKCS7( (PCERT_STORE) hCertStore, dwEncodingType, dwSaveTo, pvSaveToPara, dwFlags); break; default: SetLastError((DWORD) E_INVALIDARG); return FALSE; } } //+========================================================================= // Share Element Find, Create and Release functions //========================================================================== static inline DWORD GetShareElementHashBucketIndex( IN BYTE *pbSha1Hash ) { return pbSha1Hash[0] % SHARE_ELEMENT_HASH_BUCKET_COUNT; } // Find existing share element identified by its sha1 hash. // For a match, the returned share element is AddRef'ed. // // The dwContextType is a sanity check. Different context types should never // match. STATIC PSHARE_ELEMENT FindShareElement( IN BYTE *pbSha1Hash, IN DWORD dwContextType ) { PSHARE_ELEMENT pShareEle; DWORD dwBucketIndex = GetShareElementHashBucketIndex(pbSha1Hash); EnterCriticalSection(&ShareElementCriticalSection); for (pShareEle = rgpShareElementHashBucket[dwBucketIndex]; NULL != pShareEle; pShareEle = pShareEle->pNext) { if (0 == memcmp(pbSha1Hash, pShareEle->rgbSha1Hash, SHA1_HASH_LEN) && dwContextType == pShareEle->dwContextType) { pShareEle->dwRefCnt++; break; } } LeaveCriticalSection(&ShareElementCriticalSection); return pShareEle; } // Upon input pbEncoded has been allocated. Not freed on a NULL error // return. // // The returned share element has been AddRef'ed STATIC PSHARE_ELEMENT CreateShareElement( IN BYTE *pbSha1Hash, IN DWORD dwContextType, IN DWORD dwEncodingType, IN BYTE *pbEncoded, IN DWORD cbEncoded ) { PSHARE_ELEMENT pShareEle = NULL; DWORD dwBucketIndex = GetShareElementHashBucketIndex(pbSha1Hash); LPCSTR pszStructType; if (NULL == (pShareEle = (PSHARE_ELEMENT) PkiZeroAlloc( sizeof(SHARE_ELEMENT)))) goto OutOfMemory; memcpy(pShareEle->rgbSha1Hash, pbSha1Hash, SHA1_HASH_LEN); pShareEle->dwContextType = dwContextType; pShareEle->pbEncoded = pbEncoded; pShareEle->cbEncoded = cbEncoded; // Decode according to the context type. Note, a CTL share element // doesn't have a decoded CTL. pszStructType = rgpszShareElementStructType[dwContextType]; if (pszStructType) { if (NULL == (pShareEle->pvInfo = AllocAndDecodeObject( dwEncodingType, pszStructType, pbEncoded, cbEncoded ))) goto DecodeError; } pShareEle->dwRefCnt = 1; // Insert at beginning of share element's hash bucket list EnterCriticalSection(&ShareElementCriticalSection); if (rgpShareElementHashBucket[dwBucketIndex]) { assert(NULL == rgpShareElementHashBucket[dwBucketIndex]->pPrev); rgpShareElementHashBucket[dwBucketIndex]->pPrev = pShareEle; pShareEle->pNext = rgpShareElementHashBucket[dwBucketIndex]; } rgpShareElementHashBucket[dwBucketIndex] = pShareEle; LeaveCriticalSection(&ShareElementCriticalSection); switch (dwContextType) { case CERT_STORE_CERTIFICATE_CONTEXT - 1: CertPerfIncrementCertElementCurrentCount(); CertPerfIncrementCertElementTotalCount(); break; case CERT_STORE_CRL_CONTEXT - 1: CertPerfIncrementCrlElementCurrentCount(); CertPerfIncrementCrlElementTotalCount(); break; case CERT_STORE_CTL_CONTEXT - 1: CertPerfIncrementCtlElementCurrentCount(); CertPerfIncrementCtlElementTotalCount(); break; } CommonReturn: return pShareEle; ErrorReturn: if (pShareEle) { PkiFree(pShareEle->pvInfo); PkiFree(pShareEle); pShareEle = NULL; } goto CommonReturn; TRACE_ERROR(OutOfMemory) TRACE_ERROR(DecodeError) } STATIC void ReleaseShareElement( IN PSHARE_ELEMENT pShareEle ) { EnterCriticalSection(&ShareElementCriticalSection); if (0 == --pShareEle->dwRefCnt) { if (pShareEle->pNext) pShareEle->pNext->pPrev = pShareEle->pPrev; if (pShareEle->pPrev) pShareEle->pPrev->pNext = pShareEle->pNext; else { DWORD dwBucketIndex = GetShareElementHashBucketIndex(pShareEle->rgbSha1Hash); assert(rgpShareElementHashBucket[dwBucketIndex] == pShareEle); if (rgpShareElementHashBucket[dwBucketIndex] == pShareEle) rgpShareElementHashBucket[dwBucketIndex] = pShareEle->pNext; } switch (pShareEle->dwContextType) { case CERT_STORE_CERTIFICATE_CONTEXT - 1: CertPerfDecrementCertElementCurrentCount(); break; case CERT_STORE_CRL_CONTEXT - 1: CertPerfDecrementCrlElementCurrentCount(); break; case CERT_STORE_CTL_CONTEXT - 1: CertPerfDecrementCtlElementCurrentCount(); break; } PkiFree(pShareEle->pbEncoded); PkiFree(pShareEle->pvInfo); PkiFree(pShareEle); } LeaveCriticalSection(&ShareElementCriticalSection); } //+------------------------------------------------------------------------- // Read and allocate the store element. Possibly adjust the cbEncoded to // excluded trailing bytes. //-------------------------------------------------------------------------- STATIC ReadStoreElement( IN HANDLE h, IN PFNREAD pfnRead, IN DWORD dwEncodingType, OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; BYTE *pbEncoded = NULL; DWORD cbEncoded = *pcbEncoded; if (NULL == (pbEncoded = (BYTE *) PkiNonzeroAlloc(cbEncoded))) goto OutOfMemory; if (!pfnRead( h, pbEncoded, cbEncoded)) goto ReadError; cbEncoded = AdjustEncodedLength(dwEncodingType, pbEncoded, cbEncoded); fResult = TRUE; CommonReturn: *ppbEncoded = pbEncoded; *pcbEncoded = cbEncoded; return fResult; ErrorReturn: PkiFree(pbEncoded); pbEncoded = NULL; fResult = FALSE; goto CommonReturn; TRACE_ERROR(OutOfMemory) TRACE_ERROR(ReadError) } //+------------------------------------------------------------------------- // Create the store element. // // If CERT_STORE_SHARE_CONTEXT_FLAG is set, a share element is either found // or created. // // Normally, the sha1 hash will have been read as a serialized property // and passed in and won't need to be calculated here. Also, for a // found share element, the encoded element bytes can be skipped instead // of being allocated and read. // // In all cases, the context specific CreateElement function is called. //-------------------------------------------------------------------------- STATIC PCONTEXT_ELEMENT CreateStoreElement( IN HANDLE h, IN PFNREAD pfnRead, IN PFNSKIP pfnSkip, IN PCERT_STORE pStore, IN DWORD dwEncodingType, IN DWORD dwContextType, IN DWORD cbEncoded, IN OPTIONAL BYTE *pbSha1Hash ) { PCONTEXT_ELEMENT pEle = NULL; PSHARE_ELEMENT pShareEle = NULL; BYTE *pbEncoded = NULL; assert(pStore); if (pStore->dwFlags & CERT_STORE_SHARE_CONTEXT_FLAG) { BYTE rgbSha1Hash[SHA1_HASH_LEN]; if (NULL == pbSha1Hash) { DWORD cbData; if (!ReadStoreElement(h, pfnRead, dwEncodingType, &pbEncoded, &cbEncoded)) goto ReadError; cbData = SHA1_HASH_LEN; if (!CryptHashCertificate( 0, // hCryptProv CALG_SHA1, 0, // dwFlags pbEncoded, cbEncoded, rgbSha1Hash, &cbData) || SHA1_HASH_LEN != cbData) goto HashError; pbSha1Hash = rgbSha1Hash; } pShareEle = FindShareElement(pbSha1Hash, dwContextType); if (pShareEle) { if (NULL == pbEncoded) { if (!pfnSkip( h, cbEncoded)) goto SkipError; } else PkiFree(pbEncoded); pbEncoded = pShareEle->pbEncoded; cbEncoded = pShareEle->cbEncoded; } else { if (NULL == pbEncoded) { if (!ReadStoreElement(h, pfnRead, dwEncodingType, &pbEncoded, &cbEncoded)) goto ReadError; } if (NULL == (pShareEle = CreateShareElement( pbSha1Hash, dwContextType, dwEncodingType, pbEncoded, cbEncoded ))) goto CreateShareElementError; assert(pbEncoded == pShareEle->pbEncoded); assert(cbEncoded == pShareEle->cbEncoded); } } else { if (!ReadStoreElement(h, pfnRead, dwEncodingType, &pbEncoded, &cbEncoded)) goto ReadError; } if (NULL == (pEle = rgpfnCreateElement[dwContextType]( pStore, dwEncodingType, pbEncoded, cbEncoded, pShareEle ))) goto CreateElementError; CommonReturn: return pEle; ErrorReturn: if (pShareEle) { if (pbEncoded != pShareEle->pbEncoded) PkiFree(pbEncoded); ReleaseShareElement(pShareEle); } else { PkiFree(pbEncoded); } assert(NULL == pEle); goto CommonReturn; TRACE_ERROR(ReadError) TRACE_ERROR(HashError) TRACE_ERROR(SkipError) TRACE_ERROR(CreateShareElementError) TRACE_ERROR(CreateElementError) } //+------------------------------------------------------------------------- // Loads a serialized certificate, CRL or CTL with properties into a store. // // Also supports decoding of KeyIdentifier properties. //-------------------------------------------------------------------------- STATIC DWORD LoadStoreElement( IN HANDLE h, IN PFNREAD pfnRead, IN PFNSKIP pfnSkip, IN DWORD cbReadSize, IN OPTIONAL PCERT_STORE pStore, // NULL for fKeyIdAllowed IN DWORD dwAddDisposition, IN DWORD dwContextTypeFlags, OUT OPTIONAL DWORD *pdwContextType, OUT OPTIONAL const void **ppvContext, IN BOOL fKeyIdAllowed = FALSE ) { BYTE *pbEncoded = NULL; PCONTEXT_ELEMENT pContextEle = NULL; PCONTEXT_ELEMENT pStoreEle = NULL; PPROP_ELEMENT pPropHead = NULL; BYTE *pbSha1Hash = NULL; // not allocated FILE_ELEMENT_HDR EleHdr; BOOL fIsProp; DWORD csStatus; DWORD dwContextType; do { fIsProp = FALSE; if (!pfnRead( h, &EleHdr, sizeof(EleHdr))) goto ReadError; if (EleHdr.dwEleType == FILE_ELEMENT_END_TYPE) { if (pPropHead != NULL) goto PrematureEndError; csStatus = CSEnd; goto ZeroOutParameterReturn; } if (EleHdr.dwLen > cbReadSize) goto ExceedReadSizeError; switch (EleHdr.dwEleType) { case FILE_ELEMENT_CERT_TYPE: dwContextType = CERT_STORE_CERTIFICATE_CONTEXT; break; case FILE_ELEMENT_CRL_TYPE: dwContextType = CERT_STORE_CRL_CONTEXT; break; case FILE_ELEMENT_CTL_TYPE: dwContextType = CERT_STORE_CTL_CONTEXT; break; default: dwContextType = 0; } if (0 != dwContextType) { if (0 == (dwContextTypeFlags & (1 << dwContextType))) goto ContextNotAllowedError; if (NULL == (pContextEle = CreateStoreElement( h, pfnRead, pfnSkip, pStore, EleHdr.dwEncodingType, dwContextType - 1, EleHdr.dwLen, pbSha1Hash ))) goto CreateStoreElementError; pbEncoded = NULL; pContextEle->Cache.pPropHead = pPropHead; pPropHead = NULL; if (!AddElementToStore(pStore, pContextEle, dwAddDisposition, ppvContext ? &pStoreEle : NULL)) goto AddStoreElementError; else pContextEle = NULL; if (pdwContextType) *pdwContextType = dwContextType; if (ppvContext) *((PCCERT_CONTEXT *) ppvContext) = ToCertContext(pStoreEle); } else { // EleHdr.dwLen may be 0 for a property if (EleHdr.dwLen > 0) { if (NULL == (pbEncoded = (BYTE *) PkiNonzeroAlloc( EleHdr.dwLen))) goto OutOfMemory; if (!pfnRead( h, pbEncoded, EleHdr.dwLen)) goto ReadError; } if (EleHdr.dwEleType == FILE_ELEMENT_KEYID_TYPE) { PKEYID_ELEMENT pKeyIdEle; if (!fKeyIdAllowed) goto KeyIdNotAllowedError; if (NULL == (pKeyIdEle = CreateKeyIdElement( pbEncoded, EleHdr.dwLen ))) goto CreateKeyIdElementError; pbEncoded = NULL; pKeyIdEle->pPropHead = pPropHead; pPropHead = NULL; assert(ppvContext); if (ppvContext) *((PKEYID_ELEMENT *) ppvContext) = pKeyIdEle; } else if (EleHdr.dwEleType > CERT_LAST_USER_PROP_ID) { // Silently discard any IDs exceeding 0xFFFF. The // FIRST_USER_PROP_ID used to start at 0x10000. fIsProp = TRUE; PkiFree(pbEncoded); pbEncoded = NULL; } else if (EleHdr.dwEleType == CERT_KEY_CONTEXT_PROP_ID) { goto InvalidPropId; } else { PPROP_ELEMENT pPropEle; fIsProp = TRUE; if (NULL == (pPropEle = CreatePropElement( EleHdr.dwEleType, 0, // dwFlags pbEncoded, EleHdr.dwLen ))) goto CreatePropElementError; if (CERT_SHA1_HASH_PROP_ID == EleHdr.dwEleType && SHA1_HASH_LEN == EleHdr.dwLen) pbSha1Hash = pbEncoded; pbEncoded = NULL; AddPropElement(&pPropHead, pPropEle); } } } while (fIsProp); assert(pPropHead == NULL); assert(pbEncoded == NULL); assert(pContextEle == NULL); csStatus = CSContinue; CommonReturn: return csStatus; ErrorReturn: PkiFree(pbEncoded); if (pContextEle) FreeContextElement(pContextEle); while (pPropHead) { PPROP_ELEMENT pEle = pPropHead; pPropHead = pPropHead->pNext; FreePropElement(pEle); } csStatus = CSError; ZeroOutParameterReturn: if (pdwContextType) *pdwContextType = 0; if (ppvContext) *ppvContext = NULL; goto CommonReturn; TRACE_ERROR(ReadError) SET_ERROR(PrematureEndError, CRYPT_E_FILE_ERROR) SET_ERROR(ExceedReadSizeError, CRYPT_E_FILE_ERROR) TRACE_ERROR(OutOfMemory) SET_ERROR(ContextNotAllowedError, E_INVALIDARG) TRACE_ERROR(CreateStoreElementError) TRACE_ERROR(AddStoreElementError) TRACE_ERROR(CreatePropElementError) SET_ERROR(KeyIdNotAllowedError, E_INVALIDARG) TRACE_ERROR(CreateKeyIdElementError) SET_ERROR(InvalidPropId, CRYPT_E_FILE_ERROR) } //+------------------------------------------------------------------------- // Add the serialized certificate, CRL or CTL element to the store. //-------------------------------------------------------------------------- BOOL WINAPI CertAddSerializedElementToStore( IN HCERTSTORE hCertStore, IN const BYTE *pbElement, IN DWORD cbElement, IN DWORD dwAddDisposition, IN DWORD dwFlags, IN DWORD dwContextTypeFlags, OUT OPTIONAL DWORD *pdwContextType, OUT OPTIONAL const void **ppvContext ) { MEMINFO MemInfo; DWORD csStatus; PCERT_STORE pStore = hCertStore ? (PCERT_STORE) hCertStore : &NullCertStore; MemInfo.pByte = (BYTE*) pbElement; MemInfo.cb = cbElement; MemInfo.cbSeek = 0; csStatus = LoadStoreElement( (HANDLE) &MemInfo, ReadFromMemory, SkipInMemory, cbElement, pStore, dwAddDisposition, dwContextTypeFlags, pdwContextType, ppvContext); if (CSContinue == csStatus) return TRUE; else { if (CSEnd == csStatus) SetLastError((DWORD) CRYPT_E_NOT_FOUND); return FALSE; } } //+========================================================================= // Store Control APIs //========================================================================== STATIC BOOL EnableAutoResync( IN PCERT_STORE pStore ) { BOOL fResult; HANDLE hEvent; fResult = TRUE; hEvent = NULL; LockStore(pStore); if (NULL == pStore->hAutoResyncEvent) { // Create event to be notified if (hEvent = CreateEvent( NULL, // lpsa FALSE, // fManualReset FALSE, // fInitialState NULL)) // lpszEventName pStore->hAutoResyncEvent = hEvent; else fResult = FALSE; } UnlockStore(pStore); if (!fResult) goto CreateEventError; if (hEvent) { if (!CertControlStore( (HCERTSTORE) pStore, CERT_STORE_CTRL_INHIBIT_DUPLICATE_HANDLE_FLAG, CERT_STORE_CTRL_NOTIFY_CHANGE, &hEvent )) { DWORD dwErr = GetLastError(); // Bug 484023 Certificate store event handle closed before // being removed from list. CertControlStore( (HCERTSTORE) pStore, 0, // dwFlags CERT_STORE_CTRL_CANCEL_NOTIFY, &hEvent ); LockStore(pStore); pStore->hAutoResyncEvent = NULL; UnlockStore(pStore); CloseHandle(hEvent); SetLastError(dwErr); goto CtrlNotifyChangeError; } } CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(CreateEventError) TRACE_ERROR(CtrlNotifyChangeError) } // For a collection, iterates through all the sibling stores. For an error, // continues on to the remaining stores. LastError is updated with the // LastError of the first failing store. STATIC BOOL ControlCollectionStore( IN PCERT_STORE pCollection, IN DWORD dwFlags, IN DWORD dwCtrlType, IN void const *pvCtrlPara ) { BOOL fResult = TRUE; BOOL fOneSiblingSuccess = FALSE; DWORD dwError = ERROR_CALL_NOT_IMPLEMENTED; PCERT_STORE_LINK pStoreLink; PCERT_STORE_LINK pPrevStoreLink = NULL; // Iterate through all the siblings and call the control function LockStore(pCollection); pStoreLink = pCollection->pStoreListHead; for (; pStoreLink; pStoreLink = pStoreLink->pNext) { // Advance past deleted store link if (pStoreLink->dwFlags & STORE_LINK_DELETED_FLAG) continue; AddRefStoreLink(pStoreLink); UnlockStore(pCollection); if (pPrevStoreLink) ReleaseStoreLink(pPrevStoreLink); pPrevStoreLink = pStoreLink; if (CertControlStore( (HCERTSTORE) pStoreLink->pSibling, dwFlags, dwCtrlType, pvCtrlPara )) { fOneSiblingSuccess = TRUE; if (ERROR_CALL_NOT_IMPLEMENTED == dwError) fResult = TRUE; } else if (ERROR_CALL_NOT_IMPLEMENTED == dwError) { dwError = GetLastError(); if (!fOneSiblingSuccess || ERROR_CALL_NOT_IMPLEMENTED != dwError) fResult = FALSE; } LockStore(pCollection); } UnlockStore(pCollection); if (pPrevStoreLink) ReleaseStoreLink(pPrevStoreLink); if (!fResult) SetLastError(dwError); return fResult; } BOOL WINAPI CertControlStore( IN HCERTSTORE hCertStore, IN DWORD dwFlags, IN DWORD dwCtrlType, IN void const *pvCtrlPara ) { BOOL fResult; PCERT_STORE pStore = (PCERT_STORE) hCertStore; PFN_CERT_STORE_PROV_CONTROL pfnStoreProvControl; if (CERT_STORE_CTRL_AUTO_RESYNC == dwCtrlType) return EnableAutoResync(pStore); if (STORE_TYPE_COLLECTION == pStore->dwStoreType) return ControlCollectionStore( pStore, dwFlags, dwCtrlType, pvCtrlPara ); // Check if the store supports the control callback if (pStore->StoreProvInfo.cStoreProvFunc <= CERT_STORE_PROV_CONTROL_FUNC || NULL == (pfnStoreProvControl = (PFN_CERT_STORE_PROV_CONTROL) pStore->StoreProvInfo.rgpvStoreProvFunc[ CERT_STORE_PROV_CONTROL_FUNC])) goto ProvControlNotSupported; // The caller is holding a reference count on the store. if (!pfnStoreProvControl( pStore->StoreProvInfo.hStoreProv, dwFlags, dwCtrlType, pvCtrlPara )) goto StoreProvControlError; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(ProvControlNotSupported, ERROR_CALL_NOT_IMPLEMENTED) TRACE_ERROR(StoreProvControlError) } //+========================================================================= // Store Collection APIs //========================================================================== BOOL WINAPI CertAddStoreToCollection( IN HCERTSTORE hCollectionStore, IN OPTIONAL HCERTSTORE hSiblingStore, IN DWORD dwUpdateFlags, IN DWORD dwPriority ) { BOOL fResult; PCERT_STORE pCollection = (PCERT_STORE) hCollectionStore; PCERT_STORE pSibling = (PCERT_STORE) hSiblingStore; PCERT_STORE_LINK pAddLink = NULL; LockStore(pCollection); if (STORE_TYPE_COLLECTION == pCollection->dwStoreType) fResult = TRUE; else if (STORE_TYPE_CACHE == pCollection->dwStoreType && STORE_STATE_OPENING == pCollection->dwState && IsEmptyStore(pCollection)) { pCollection->dwStoreType = STORE_TYPE_COLLECTION; fResult = TRUE; } else fResult = FALSE; UnlockStore(pCollection); if (!fResult) goto InvalidCollectionStore; if (NULL == hSiblingStore) goto CommonReturn; // Create a link to the store to be added. It duplicates pSibling. if (NULL == (pAddLink = CreateStoreLink( pCollection, pSibling, dwUpdateFlags, dwPriority))) goto CreateStoreLinkError; LockStore(pCollection); if (NULL == pCollection->pStoreListHead) pCollection->pStoreListHead = pAddLink; else { PCERT_STORE_LINK pLink; pLink = pCollection->pStoreListHead; if (dwPriority > pLink->dwPriority) { // Insert at beginning before first link pAddLink->pNext = pLink; pLink->pPrev = pAddLink; pCollection->pStoreListHead = pAddLink; } else { // Insert after the link whose next link has // lower priority or insert after the last link while (pLink->pNext && dwPriority <= pLink->pNext->dwPriority) pLink = pLink->pNext; pAddLink->pPrev = pLink; pAddLink->pNext = pLink->pNext; if (pLink->pNext) pLink->pNext->pPrev = pAddLink; pLink->pNext = pAddLink; } } UnlockStore(pCollection); fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidCollectionStore, E_INVALIDARG) TRACE_ERROR(CreateStoreLinkError) } void WINAPI CertRemoveStoreFromCollection( IN HCERTSTORE hCollectionStore, IN HCERTSTORE hSiblingStore ) { PCERT_STORE pCollection = (PCERT_STORE) hCollectionStore; PCERT_STORE pSibling = (PCERT_STORE) hSiblingStore; PCERT_STORE_LINK pLink; LockStore(pCollection); assert(STORE_TYPE_COLLECTION == pCollection->dwStoreType); pLink = pCollection->pStoreListHead; for (; pLink; pLink = pLink->pNext) { if (pSibling == pLink->pSibling && 0 == (pLink->dwFlags & STORE_LINK_DELETED_FLAG)) { // Remove the collection's reference pLink->dwFlags |= STORE_LINK_DELETED_FLAG; UnlockStore(pCollection); ReleaseStoreLink(pLink); return; } } UnlockStore(pCollection); } //+========================================================================= // Cert Store Property Functions //========================================================================== //+------------------------------------------------------------------------- // Set a store property. //-------------------------------------------------------------------------- BOOL WINAPI CertSetStoreProperty( IN HCERTSTORE hCertStore, IN DWORD dwPropId, IN DWORD dwFlags, IN const void *pvData ) { BOOL fResult; PCERT_STORE pStore = (PCERT_STORE) hCertStore; LockStore(pStore); fResult = SetCallerProperty( &pStore->pPropHead, dwPropId, dwFlags, pvData ); UnlockStore(pStore); return fResult; } //+------------------------------------------------------------------------- // Get a store property. //-------------------------------------------------------------------------- BOOL WINAPI CertGetStoreProperty( IN HCERTSTORE hCertStore, IN DWORD dwPropId, OUT void *pvData, IN OUT DWORD *pcbData ) { BOOL fResult; PCERT_STORE pStore = (PCERT_STORE) hCertStore; if (dwPropId == CERT_ACCESS_STATE_PROP_ID) { DWORD dwAccessStateFlags; DWORD cbIn; dwAccessStateFlags = 0; if (0 == (pStore->dwFlags & CERT_STORE_READONLY_FLAG) && 0 == (pStore->StoreProvInfo.dwStoreProvFlags & CERT_STORE_PROV_NO_PERSIST_FLAG)) { if (STORE_TYPE_COLLECTION == pStore->dwStoreType) { // If all its children are READONLY, then NO WRITE_PERSIST PCERT_STORE_LINK pStoreLink; PCERT_STORE_LINK pPrevStoreLink = NULL; LockStore(pStore); for (pStoreLink = pStore->pStoreListHead; pStoreLink; pStoreLink = pStoreLink->pNext) { DWORD dwSiblingAccessStateFlags; DWORD cbSiblingData; // Advance past deleted store link if (pStoreLink->dwFlags & STORE_LINK_DELETED_FLAG) continue; AddRefStoreLink(pStoreLink); UnlockStore(pStore); if (pPrevStoreLink) ReleaseStoreLink(pPrevStoreLink); pPrevStoreLink = pStoreLink; dwSiblingAccessStateFlags = 0; cbSiblingData = sizeof(dwSiblingAccessStateFlags); CertGetStoreProperty( (HCERTSTORE) pStoreLink->pSibling, CERT_ACCESS_STATE_PROP_ID, &dwSiblingAccessStateFlags, &cbSiblingData ); LockStore(pStore); if (dwSiblingAccessStateFlags & CERT_ACCESS_STATE_WRITE_PERSIST_FLAG) { dwAccessStateFlags = CERT_ACCESS_STATE_WRITE_PERSIST_FLAG; break; } } UnlockStore(pStore); if (pPrevStoreLink) ReleaseStoreLink(pPrevStoreLink); } else dwAccessStateFlags = CERT_ACCESS_STATE_WRITE_PERSIST_FLAG; } if (pStore->StoreProvInfo.dwStoreProvFlags & CERT_STORE_PROV_SYSTEM_STORE_FLAG) dwAccessStateFlags |= CERT_ACCESS_STATE_SYSTEM_STORE_FLAG; if (pStore->StoreProvInfo.dwStoreProvFlags & CERT_STORE_PROV_LM_SYSTEM_STORE_FLAG) dwAccessStateFlags |= CERT_ACCESS_STATE_LM_SYSTEM_STORE_FLAG; fResult = TRUE; if (pvData == NULL) cbIn = 0; else cbIn = *pcbData; if (cbIn < sizeof(DWORD)) { if (pvData) { SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; } } else *((DWORD * ) pvData) = dwAccessStateFlags; *pcbData = sizeof(DWORD); return fResult; } LockStore(pStore); fResult = GetCallerProperty( pStore->pPropHead, dwPropId, FALSE, // fAlloc pvData, pcbData ); UnlockStore(pStore); return fResult; } //+========================================================================= // Certificate APIs //========================================================================== BOOL WINAPI CertAddEncodedCertificateToStore( IN HCERTSTORE hCertStore, IN DWORD dwCertEncodingType, IN const BYTE *pbCertEncoded, IN DWORD cbCertEncoded, IN DWORD dwAddDisposition, OUT OPTIONAL PCCERT_CONTEXT *ppCertContext ) { BOOL fResult; PCONTEXT_ELEMENT pStoreEle = NULL; fResult = AddEncodedContextToStore( (PCERT_STORE) hCertStore, CERT_STORE_CERTIFICATE_CONTEXT - 1, dwCertEncodingType, pbCertEncoded, cbCertEncoded, dwAddDisposition, ppCertContext ? &pStoreEle : NULL ); if (ppCertContext) *ppCertContext = ToCertContext(pStoreEle); return fResult; } BOOL WINAPI CertAddCertificateContextToStore( IN HCERTSTORE hCertStore, IN PCCERT_CONTEXT pCertContext, IN DWORD dwAddDisposition, OUT OPTIONAL PCCERT_CONTEXT *ppStoreContext ) { BOOL fResult; PCONTEXT_ELEMENT pStoreEle = NULL; fResult = AddContextToStore( (PCERT_STORE) hCertStore, ToContextElement(pCertContext), pCertContext->dwCertEncodingType, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded, dwAddDisposition, ppStoreContext ? &pStoreEle : NULL ); if (ppStoreContext) *ppStoreContext = ToCertContext(pStoreEle); return fResult; } BOOL WINAPI CertAddCertificateLinkToStore( IN HCERTSTORE hCertStore, IN PCCERT_CONTEXT pCertContext, IN DWORD dwAddDisposition, OUT OPTIONAL PCCERT_CONTEXT *ppStoreContext ) { BOOL fResult; PCONTEXT_ELEMENT pStoreEle = NULL; fResult = AddLinkContextToCacheStore( (PCERT_STORE) hCertStore, ToContextElement(pCertContext), dwAddDisposition, ppStoreContext ? &pStoreEle : NULL ); if (ppStoreContext) *ppStoreContext = ToCertContext(pStoreEle); return fResult; } //+------------------------------------------------------------------------- // Serialize the certificate context's encoded certificate and its // properties. //-------------------------------------------------------------------------- BOOL WINAPI CertSerializeCertificateStoreElement( IN PCCERT_CONTEXT pCertContext, IN DWORD dwFlags, OUT BYTE *pbElement, IN OUT DWORD *pcbElement ) { return SerializeContextElement( ToContextElement(pCertContext), dwFlags, pbElement, pcbElement ); } //+------------------------------------------------------------------------- // Delete the specified certificate from the store. // // All subsequent gets or finds for the certificate will fail. However, // memory allocated for the certificate isn't freed until all of its contexts // have also been freed. // // The pCertContext is obtained from a get, find or duplicate. // // Some store provider implementations might also delete the issuer's CRLs // if this is the last certificate for the issuer in the store. // // NOTE: the pCertContext is always CertFreeCertificateContext'ed by // this function, even for an error. //-------------------------------------------------------------------------- BOOL WINAPI CertDeleteCertificateFromStore( IN PCCERT_CONTEXT pCertContext ) { assert(NULL == pCertContext || (CERT_STORE_CERTIFICATE_CONTEXT - 1) == ToContextElement(pCertContext)->dwContextType); return DeleteContextElement(ToContextElement(pCertContext)); } //+------------------------------------------------------------------------- // Get the subject certificate context uniquely identified by its Issuer and // SerialNumber from the store. // // If the certificate isn't found, NULL is returned. Otherwise, a pointer to // a read only CERT_CONTEXT is returned. CERT_CONTEXT must be freed by calling // CertFreeCertificateContext. CertDuplicateCertificateContext can be called to make a // duplicate. // // The returned certificate might not be valid. Normally, it would be // verified when getting its issuer certificate (CertGetIssuerCertificateFromStore). //-------------------------------------------------------------------------- PCCERT_CONTEXT WINAPI CertGetSubjectCertificateFromStore( IN HCERTSTORE hCertStore, IN DWORD dwCertEncodingType, IN PCERT_INFO pCertId // Only the Issuer and SerialNumber // fields are used ) { CERT_STORE_PROV_FIND_INFO FindInfo; if (pCertId == NULL) { SetLastError((DWORD) E_INVALIDARG); return NULL; } FindInfo.cbSize = sizeof(FindInfo); FindInfo.dwMsgAndCertEncodingType = dwCertEncodingType, FindInfo.dwFindFlags = 0; FindInfo.dwFindType = CERT_FIND_SUBJECT_CERT; FindInfo.pvFindPara = pCertId; return ToCertContext(CheckAutoResyncAndFindElementInStore( (PCERT_STORE) hCertStore, CERT_STORE_CERTIFICATE_CONTEXT - 1, &FindInfo, NULL // pPrevEle )); } //+------------------------------------------------------------------------- // Enumerate the certificate contexts in the store. // // If a certificate isn't found, NULL is returned. // Otherwise, a pointer to a read only CERT_CONTEXT is returned. CERT_CONTEXT // must be freed by calling CertFreeCertificateContext or is freed when passed as the // pPrevCertContext on a subsequent call. CertDuplicateCertificateContext // can be called to make a duplicate. // // pPrevCertContext MUST BE NULL to enumerate the first // certificate in the store. Successive certificates are enumerated by setting // pPrevCertContext to the CERT_CONTEXT returned by a previous call. // // NOTE: a NON-NULL pPrevCertContext is always CertFreeCertificateContext'ed by // this function, even for an error. //-------------------------------------------------------------------------- PCCERT_CONTEXT WINAPI CertEnumCertificatesInStore( IN HCERTSTORE hCertStore, IN PCCERT_CONTEXT pPrevCertContext ) { return ToCertContext(CheckAutoResyncAndFindElementInStore( (PCERT_STORE) hCertStore, CERT_STORE_CERTIFICATE_CONTEXT - 1, &FindAnyInfo, ToContextElement(pPrevCertContext) )); } //+------------------------------------------------------------------------- // Find the first or next certificate context in the store. // // The certificate is found according to the dwFindType and its pvFindPara. // See below for a list of the find types and its parameters. // // Currently dwFindFlags is only used for CERT_FIND_SUBJECT_ATTR or // CERT_FIND_ISSUER_ATTR. Otherwise, must be set to 0. // // Usage of dwCertEncodingType depends on the dwFindType. // // If the first or next certificate isn't found, NULL is returned. // Otherwise, a pointer to a read only CERT_CONTEXT is returned. CERT_CONTEXT // must be freed by calling CertFreeCertificateContext or is freed when passed as the // pPrevCertContext on a subsequent call. CertDuplicateCertificateContext // can be called to make a duplicate. // // pPrevCertContext MUST BE NULL on the first // call to find the certificate. To find the next certificate, the // pPrevCertContext is set to the CERT_CONTEXT returned by a previous call. // // NOTE: a NON-NULL pPrevCertContext is always CertFreeCertificateContext'ed by // this function, even for an error. //-------------------------------------------------------------------------- PCCERT_CONTEXT WINAPI CertFindCertificateInStore( IN HCERTSTORE hCertStore, IN DWORD dwCertEncodingType, IN DWORD dwFindFlags, IN DWORD dwFindType, IN const void *pvFindPara, IN PCCERT_CONTEXT pPrevCertContext ) { CERT_STORE_PROV_FIND_INFO FindInfo; FindInfo.cbSize = sizeof(FindInfo); FindInfo.dwMsgAndCertEncodingType = dwCertEncodingType; FindInfo.dwFindFlags = dwFindFlags; FindInfo.dwFindType = dwFindType; FindInfo.pvFindPara = pvFindPara; return ToCertContext(CheckAutoResyncAndFindElementInStore( (PCERT_STORE) hCertStore, CERT_STORE_CERTIFICATE_CONTEXT - 1, &FindInfo, ToContextElement(pPrevCertContext) )); } //+------------------------------------------------------------------------- // Perform the revocation check on the subject certificate // using the issuer certificate and store //-------------------------------------------------------------------------- STATIC void VerifySubjectCertRevocation( IN PCCERT_CONTEXT pSubject, IN PCCERT_CONTEXT pIssuer, IN HCERTSTORE hIssuerStore, IN OUT DWORD *pdwFlags ) { PCCRL_CONTEXT rgpCrlContext[MAX_CRL_LIST]; PCRL_INFO rgpCrlInfo[MAX_CRL_LIST]; PCCRL_CONTEXT pCrlContext = NULL; DWORD cCrl = 0; assert(pIssuer && hIssuerStore); assert(*pdwFlags & CERT_STORE_REVOCATION_FLAG); while (TRUE) { DWORD dwFlags = CERT_STORE_SIGNATURE_FLAG; pCrlContext = CertGetCRLFromStore( hIssuerStore, pIssuer, pCrlContext, &dwFlags ); if (pCrlContext == NULL) break; if (cCrl == MAX_CRL_LIST) { assert(cCrl > MAX_CRL_LIST); CertFreeCRLContext(pCrlContext); break; } if (dwFlags == 0) { rgpCrlContext[cCrl] = CertDuplicateCRLContext(pCrlContext); rgpCrlInfo[cCrl] = pCrlContext->pCrlInfo; cCrl++; } else { // Need to log or remove a bad CRL from the store ; } } if (cCrl == 0) *pdwFlags |= CERT_STORE_NO_CRL_FLAG; else { if (CertVerifyCRLRevocation( pSubject->dwCertEncodingType, pSubject->pCertInfo, cCrl, rgpCrlInfo )) *pdwFlags &= ~CERT_STORE_REVOCATION_FLAG; while (cCrl--) CertFreeCRLContext(rgpCrlContext[cCrl]); } } #ifdef CMS_PKCS7 //+------------------------------------------------------------------------- // If the verify certificate signature fails with CRYPT_E_MISSING_PUBKEY_PARA, // build a certificate chain. Retry. Hopefully, the issuer's // CERT_PUBKEY_ALG_PARA_PROP_ID property gets set while building the chain. //-------------------------------------------------------------------------- STATIC BOOL VerifyCertificateSignatureWithChainPubKeyParaInheritance( IN HCRYPTPROV hCryptProv, IN DWORD dwCertEncodingType, IN DWORD dwSubjectType, IN void *pvSubject, IN PCCERT_CONTEXT pIssuer ); #endif // CMS_PKCS7 //+------------------------------------------------------------------------- // Perform the enabled verification checks on the subject certificate // using the issuer //-------------------------------------------------------------------------- STATIC void VerifySubjectCert( IN PCCERT_CONTEXT pSubject, IN OPTIONAL PCCERT_CONTEXT pIssuer, IN OUT DWORD *pdwFlags ) { if (*pdwFlags & CERT_STORE_TIME_VALIDITY_FLAG) { if (CertVerifyTimeValidity(NULL, pSubject->pCertInfo) == 0) *pdwFlags &= ~CERT_STORE_TIME_VALIDITY_FLAG; } if (pIssuer == NULL) { if (*pdwFlags & (CERT_STORE_SIGNATURE_FLAG | CERT_STORE_REVOCATION_FLAG)) *pdwFlags |= CERT_STORE_NO_ISSUER_FLAG; return; } if (*pdwFlags & CERT_STORE_SIGNATURE_FLAG) { PCERT_STORE pStore = (PCERT_STORE) pIssuer->hCertStore; HCRYPTPROV hProv; DWORD dwProvFlags; // Attempt to get the store's crypt provider. Serialize crypto // operations by entering critical section. hProv = GetCryptProv(pStore, &dwProvFlags); #if 0 // Slow down the provider while holding the provider reference // count Sleep(1700); #endif #ifdef CMS_PKCS7 if (VerifyCertificateSignatureWithChainPubKeyParaInheritance( hProv, pSubject->dwCertEncodingType, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *) pSubject, pIssuer )) #else if (CryptVerifyCertificateSignature( hProv, pSubject->dwCertEncodingType, pSubject->pbCertEncoded, pSubject->cbCertEncoded, &pIssuer->pCertInfo->SubjectPublicKeyInfo )) #endif // CMS_PKCS7 *pdwFlags &= ~CERT_STORE_SIGNATURE_FLAG; // For the store's crypt provider, release reference count. Leave // crypto operations critical section. ReleaseCryptProv(pStore, dwProvFlags); } if (*pdwFlags & CERT_STORE_REVOCATION_FLAG) { *pdwFlags &= ~CERT_STORE_NO_CRL_FLAG; VerifySubjectCertRevocation( pSubject, pIssuer, pIssuer->hCertStore, pdwFlags ); if (*pdwFlags & CERT_STORE_NO_CRL_FLAG) { PCONTEXT_ELEMENT pIssuerEle = ToContextElement(pIssuer); if (ELEMENT_TYPE_LINK_CONTEXT == pIssuerEle->dwElementType) { // Skip past the link elements. A store containing a link // may not have any CRLs. Try the store containing the // real issuer element. DWORD dwInnerDepth = 0; for ( ; ELEMENT_TYPE_LINK_CONTEXT == pIssuerEle->dwElementType; pIssuerEle = pIssuerEle->pEle) { dwInnerDepth++; assert(dwInnerDepth <= MAX_LINK_DEPTH); assert(pIssuerEle != pIssuerEle->pEle); if (dwInnerDepth > MAX_LINK_DEPTH) break; } if ((HCERTSTORE) pIssuerEle->pStore != pIssuer->hCertStore) { *pdwFlags &= ~CERT_STORE_NO_CRL_FLAG; VerifySubjectCertRevocation( pSubject, pIssuer, (HCERTSTORE) pIssuerEle->pStore, pdwFlags ); } } } } } //+------------------------------------------------------------------------- // Get the certificate context from the store for the first or next issuer // of the specified subject certificate. Perform the enabled // verification checks on the subject. (Note, the checks are on the subject // using the returned issuer certificate.) // // If the first or next issuer certificate isn't found, NULL is returned. // Otherwise, a pointer to a read only CERT_CONTEXT is returned. CERT_CONTEXT // must be freed by calling CertFreeCertificateContext or is freed when passed as the // pPrevIssuerContext on a subsequent call. CertDuplicateCertificateContext // can be called to make a duplicate. // // The pSubjectContext may have been obtained from this store, another store // or created by the caller application. When created by the caller, the // CertCreateCertificateContext function must have been called. // // An issuer may have multiple certificates. This may occur when the validity // period is about to change. pPrevIssuerContext MUST BE NULL on the first // call to get the issuer. To get the next certificate for the issuer, the // pPrevIssuerContext is set to the CERT_CONTEXT returned by a previous call. // // NOTE: a NON-NULL pPrevIssuerContext is always CertFreeCertificateContext'ed by // this function, even for an error. // // The following flags can be set in *pdwFlags to enable verification checks // on the subject certificate context: // CERT_STORE_SIGNATURE_FLAG - use the public key in the returned // issuer certificate to verify the // signature on the subject certificate. // Note, if pSubjectContext->hCertStore == // hCertStore, the store provider might // be able to eliminate a redo of // the signature verify. // CERT_STORE_TIME_VALIDITY_FLAG - get the current time and verify that // its within the subject certificate's // validity period // CERT_STORE_REVOCATION_FLAG - check if the subject certificate is on // the issuer's revocation list // // If an enabled verification check fails, then, its flag is set upon return. // If CERT_STORE_REVOCATION_FLAG was enabled and the issuer doesn't have a // CRL in the store, then, CERT_STORE_NO_CRL_FLAG is set in addition to // the CERT_STORE_REVOCATION_FLAG. // // If CERT_STORE_SIGNATURE_FLAG or CERT_STORE_REVOCATION_FLAG is set, then, // CERT_STORE_NO_ISSUER_FLAG is set if it doesn't have an issuer certificate // in the store. // // For a verification check failure, a pointer to the issuer's CERT_CONTEXT // is still returned and SetLastError isn't updated. //-------------------------------------------------------------------------- PCCERT_CONTEXT WINAPI CertGetIssuerCertificateFromStore( IN HCERTSTORE hCertStore, IN PCCERT_CONTEXT pSubjectContext, IN OPTIONAL PCCERT_CONTEXT pPrevIssuerContext, IN OUT DWORD *pdwFlags ) { PCCERT_CONTEXT pIssuerContext; if (*pdwFlags & ~(CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG | CERT_STORE_REVOCATION_FLAG)) goto InvalidArg; // Check if self signed certificate, issuer == subject if (CertCompareCertificateName( pSubjectContext->dwCertEncodingType, &pSubjectContext->pCertInfo->Subject, &pSubjectContext->pCertInfo->Issuer )) { VerifySubjectCert( pSubjectContext, pSubjectContext, pdwFlags ); SetLastError((DWORD) CRYPT_E_SELF_SIGNED); goto ErrorReturn; } else { CERT_STORE_PROV_FIND_INFO FindInfo; FindInfo.cbSize = sizeof(FindInfo); FindInfo.dwMsgAndCertEncodingType = pSubjectContext->dwCertEncodingType; FindInfo.dwFindFlags = 0; FindInfo.dwFindType = CERT_FIND_ISSUER_OF; FindInfo.pvFindPara = pSubjectContext; if (pIssuerContext = ToCertContext(CheckAutoResyncAndFindElementInStore( (PCERT_STORE) hCertStore, CERT_STORE_CERTIFICATE_CONTEXT - 1, &FindInfo, ToContextElement(pPrevIssuerContext) ))) VerifySubjectCert( pSubjectContext, pIssuerContext, pdwFlags ); } CommonReturn: return pIssuerContext; ErrorReturn: if (pPrevIssuerContext) CertFreeCertificateContext(pPrevIssuerContext); pIssuerContext = NULL; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) } //+------------------------------------------------------------------------- // Perform the enabled verification checks on the subject certificate // using the issuer. Same checks and flags definitions as for the above // CertGetIssuerCertificateFromStore. // // For a verification check failure, SUCCESS is still returned. // // pIssuer must come from a store that is still open. //-------------------------------------------------------------------------- BOOL WINAPI CertVerifySubjectCertificateContext( IN PCCERT_CONTEXT pSubject, IN OPTIONAL PCCERT_CONTEXT pIssuer, IN OUT DWORD *pdwFlags ) { if (*pdwFlags & ~(CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG | CERT_STORE_REVOCATION_FLAG)) { SetLastError((DWORD) E_INVALIDARG); return FALSE; } if (*pdwFlags & (CERT_STORE_SIGNATURE_FLAG | CERT_STORE_REVOCATION_FLAG)) { if (pIssuer == NULL) { SetLastError((DWORD) E_INVALIDARG); return FALSE; } } VerifySubjectCert( pSubject, pIssuer, pdwFlags ); return TRUE; } //+------------------------------------------------------------------------- // Duplicate a certificate context //-------------------------------------------------------------------------- PCCERT_CONTEXT WINAPI CertDuplicateCertificateContext( IN PCCERT_CONTEXT pCertContext ) { if (pCertContext) AddRefContextElement(ToContextElement(pCertContext)); return pCertContext; } //+------------------------------------------------------------------------- // Create a certificate context from the encoded certificate. The created // context isn't put in a store. // // Makes a copy of the encoded certificate in the created context. // // If unable to decode and create the certificate context, NULL is returned. // Otherwise, a pointer to a read only CERT_CONTEXT is returned. // CERT_CONTEXT must be freed by calling CertFreeCertificateContext. // CertDuplicateCertificateContext can be called to make a duplicate. // // CertSetCertificateContextProperty and CertGetCertificateContextProperty can be called // to store properties for the certificate. //-------------------------------------------------------------------------- PCCERT_CONTEXT WINAPI CertCreateCertificateContext( IN DWORD dwCertEncodingType, IN const BYTE *pbCertEncoded, IN DWORD cbCertEncoded ) { PCCERT_CONTEXT pCertContext; CertAddEncodedCertificateToStore( NULL, // hCertStore dwCertEncodingType, pbCertEncoded, cbCertEncoded, CERT_STORE_ADD_ALWAYS, &pCertContext ); return pCertContext; } //+------------------------------------------------------------------------- // Free a certificate context // // There needs to be a corresponding free for each context obtained by a // get, find, duplicate or create. //-------------------------------------------------------------------------- BOOL WINAPI CertFreeCertificateContext( IN PCCERT_CONTEXT pCertContext ) { ReleaseContextElement(ToContextElement(pCertContext)); return TRUE; } //+------------------------------------------------------------------------- // Set the property for the specified certificate context. // // If the certificate context was obtained from a store, then, the property // is added to the store. // // The type definition for pvData depends on the dwPropId value. There are // three predefined types: // CERT_KEY_PROV_HANDLE_PROP_ID - a HCRYPTPROV for the certificate's // private key is passed in pvData. If the // CERT_STORE_NO_CRYPT_RELEASE_FLAG isn't set, HCRYPTPROV is implicitly // released when either the property is set to NULL or on the final // free of the CertContext. // // CERT_KEY_PROV_INFO_PROP_ID - a PCRYPT_KEY_PROV_INFO for the certificate's // private key is passed in pvData. // // CERT_SHA1_HASH_PROP_ID - // CERT_MD5_HASH_PROP_ID - // CERT_SIGNATURE_HASH_PROP_ID - normally, a hash property is implicitly // set by doing a CertGetCertificateContextProperty. pvData points to a // CRYPT_HASH_BLOB. // // For all the other PROP_IDs: an encoded PCRYPT_DATA_BLOB is passed in pvData. // // If the property already exists, then, the old value is deleted and silently // replaced. Setting, pvData to NULL, deletes the property. //-------------------------------------------------------------------------- BOOL WINAPI CertSetCertificateContextProperty( IN PCCERT_CONTEXT pCertContext, IN DWORD dwPropId, IN DWORD dwFlags, IN const void *pvData ) { return SetProperty( ToContextElement(pCertContext), dwPropId, dwFlags, pvData ); } //+------------------------------------------------------------------------- // Get the property for the specified certificate context. // // For CERT_KEY_PROV_HANDLE_PROP_ID, pvData points to a HCRYPTPROV. // // For CERT_KEY_PROV_INFO_PROP_ID, pvData points to a CRYPT_KEY_PROV_INFO structure. // Elements pointed to by fields in the pvData structure follow the // structure. Therefore, *pcbData may exceed the size of the structure. // // For CERT_SHA1_HASH_PROP_ID or CERT_MD5_HASH_PROP_ID, if the hash // doesn't already exist, then, its computed via CryptHashCertificate() // and then set. pvData points to the computed hash. Normally, the length // is 20 bytes for SHA and 16 for MD5. // MD5. // // For CERT_SIGNATURE_HASH_PROP_ID, if the hash // doesn't already exist, then, its computed via CryptHashToBeSigned() // and then set. pvData points to the computed hash. Normally, the length // is 20 bytes for SHA and 16 for MD5. // // For all other PROP_IDs, pvData points to an encoded array of bytes. //-------------------------------------------------------------------------- BOOL WINAPI CertGetCertificateContextProperty( IN PCCERT_CONTEXT pCertContext, IN DWORD dwPropId, OUT void *pvData, IN OUT DWORD *pcbData ) { return GetProperty( ToContextElement(pCertContext), dwPropId, pvData, pcbData ); } //+------------------------------------------------------------------------- // Enumerate the properties for the specified certificate context. //-------------------------------------------------------------------------- DWORD WINAPI CertEnumCertificateContextProperties( IN PCCERT_CONTEXT pCertContext, IN DWORD dwPropId ) { return EnumProperties( ToContextElement(pCertContext), dwPropId ); } //+========================================================================= // CRL APIs //========================================================================== BOOL WINAPI CertAddEncodedCRLToStore( IN HCERTSTORE hCertStore, IN DWORD dwCertEncodingType, IN const BYTE *pbCrlEncoded, IN DWORD cbCrlEncoded, IN DWORD dwAddDisposition, OUT OPTIONAL PCCRL_CONTEXT *ppCrlContext ) { BOOL fResult; PCONTEXT_ELEMENT pStoreEle = NULL; fResult = AddEncodedContextToStore( (PCERT_STORE) hCertStore, CERT_STORE_CRL_CONTEXT - 1, dwCertEncodingType, pbCrlEncoded, cbCrlEncoded, dwAddDisposition, ppCrlContext ? &pStoreEle : NULL ); if (ppCrlContext) *ppCrlContext = ToCrlContext(pStoreEle); return fResult; } BOOL WINAPI CertAddCRLContextToStore( IN HCERTSTORE hCertStore, IN PCCRL_CONTEXT pCrlContext, IN DWORD dwAddDisposition, OUT OPTIONAL PCCRL_CONTEXT *ppStoreContext ) { BOOL fResult; PCONTEXT_ELEMENT pStoreEle = NULL; fResult = AddContextToStore( (PCERT_STORE) hCertStore, ToContextElement(pCrlContext), pCrlContext->dwCertEncodingType, pCrlContext->pbCrlEncoded, pCrlContext->cbCrlEncoded, dwAddDisposition, ppStoreContext ? &pStoreEle : NULL ); if (ppStoreContext) *ppStoreContext = ToCrlContext(pStoreEle); return fResult; } BOOL WINAPI CertAddCRLLinkToStore( IN HCERTSTORE hCertStore, IN PCCRL_CONTEXT pCrlContext, IN DWORD dwAddDisposition, OUT OPTIONAL PCCRL_CONTEXT *ppStoreContext ) { BOOL fResult; PCONTEXT_ELEMENT pStoreEle = NULL; fResult = AddLinkContextToCacheStore( (PCERT_STORE) hCertStore, ToContextElement(pCrlContext), dwAddDisposition, ppStoreContext ? &pStoreEle : NULL ); if (ppStoreContext) *ppStoreContext = ToCrlContext(pStoreEle); return fResult; } //+------------------------------------------------------------------------- // Serialize the CRL context's encoded CRL and its properties. //-------------------------------------------------------------------------- BOOL WINAPI CertSerializeCRLStoreElement( IN PCCRL_CONTEXT pCrlContext, IN DWORD dwFlags, OUT BYTE *pbElement, IN OUT DWORD *pcbElement ) { return SerializeContextElement( ToContextElement(pCrlContext), dwFlags, pbElement, pcbElement ); } //+------------------------------------------------------------------------- // Delete the specified CRL from the store. // // All subsequent gets for the CRL will fail. However, // memory allocated for the CRL isn't freed until all of its contexts // have also been freed. // // The pCrlContext is obtained from a get or duplicate. // // NOTE: the pCrlContext is always CertFreeCRLContext'ed by // this function, even for an error. //-------------------------------------------------------------------------- BOOL WINAPI CertDeleteCRLFromStore( IN PCCRL_CONTEXT pCrlContext ) { assert(NULL == pCrlContext || (CERT_STORE_CRL_CONTEXT - 1) == ToContextElement(pCrlContext)->dwContextType); return DeleteContextElement(ToContextElement(pCrlContext)); } //+------------------------------------------------------------------------- // Perform the enabled verification checks on the CRL using the issuer //-------------------------------------------------------------------------- STATIC void VerifyCrl( IN PCCRL_CONTEXT pCrl, IN OPTIONAL PCCERT_CONTEXT pIssuer, IN OUT DWORD *pdwFlags ) { if (*pdwFlags & CERT_STORE_TIME_VALIDITY_FLAG) { if (CertVerifyCRLTimeValidity(NULL, pCrl->pCrlInfo) == 0) *pdwFlags &= ~CERT_STORE_TIME_VALIDITY_FLAG; } if (*pdwFlags & (CERT_STORE_BASE_CRL_FLAG | CERT_STORE_DELTA_CRL_FLAG)) { PCERT_EXTENSION pDeltaExt; pDeltaExt = CertFindExtension( szOID_DELTA_CRL_INDICATOR, pCrl->pCrlInfo->cExtension, pCrl->pCrlInfo->rgExtension ); if (*pdwFlags & CERT_STORE_DELTA_CRL_FLAG) { if (NULL != pDeltaExt) *pdwFlags &= ~CERT_STORE_DELTA_CRL_FLAG; } if (*pdwFlags & CERT_STORE_BASE_CRL_FLAG) { if (NULL == pDeltaExt) *pdwFlags &= ~CERT_STORE_BASE_CRL_FLAG; } } if (pIssuer == NULL) { if (*pdwFlags & CERT_STORE_SIGNATURE_FLAG) *pdwFlags |= CERT_STORE_NO_ISSUER_FLAG; return; } if (*pdwFlags & CERT_STORE_SIGNATURE_FLAG) { PCERT_STORE pStore = (PCERT_STORE) pIssuer->hCertStore; HCRYPTPROV hProv; DWORD dwProvFlags; // Attempt to get the store's crypt provider. Serialize crypto // operations by entering critical section. hProv = GetCryptProv(pStore, &dwProvFlags); #ifdef CMS_PKCS7 if (VerifyCertificateSignatureWithChainPubKeyParaInheritance( hProv, pCrl->dwCertEncodingType, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL, (void *) pCrl, pIssuer )) #else if (CryptVerifyCertificateSignature( hProv, pCrl->dwCertEncodingType, pCrl->pbCrlEncoded, pCrl->cbCrlEncoded, &pIssuer->pCertInfo->SubjectPublicKeyInfo )) #endif // CMS_PKCS7 *pdwFlags &= ~CERT_STORE_SIGNATURE_FLAG; // For the store's crypt provider, release reference count. Leave // crypto operations critical section. ReleaseCryptProv(pStore, dwProvFlags); } } //+------------------------------------------------------------------------- // Get the first or next CRL context from the store for the specified // issuer certificate. Perform the enabled verification checks on the CRL. // // If the first or next CRL isn't found, NULL is returned. // Otherwise, a pointer to a read only CRL_CONTEXT is returned. CRL_CONTEXT // must be freed by calling CertFreeCRLContext or is freed when passed as the // pPrevCrlContext on a subsequent call. CertDuplicateCRLContext // can be called to make a duplicate. // // The pIssuerContext may have been obtained from this store, another store // or created by the caller application. When created by the caller, the // CertCreateCertificateContext function must have been called. // // If pIssuerContext == NULL, finds all the CRLs in the store. // // An issuer may have multiple CRLs. For example, it generates delta CRLs // using a X.509 v3 extension. pPrevCrlContext MUST BE NULL on the first // call to get the CRL. To get the next CRL for the issuer, the // pPrevCrlContext is set to the CRL_CONTEXT returned by a previous call. // // NOTE: a NON-NULL pPrevCrlContext is always CertFreeCRLContext'ed by // this function, even for an error. // // The following flags can be set in *pdwFlags to enable verification checks // on the returned CRL: // CERT_STORE_SIGNATURE_FLAG - use the public key in the // issuer's certificate to verify the // signature on the returned CRL. // Note, if pIssuerContext->hCertStore == // hCertStore, the store provider might // be able to eliminate a redo of // the signature verify. // CERT_STORE_TIME_VALIDITY_FLAG - get the current time and verify that // its within the CRL's ThisUpdate and // NextUpdate validity period. // CERT_STORE_BASE_CRL_FLAG - get base CRL. // CERT_STORE_DELTA_CRL_FLAG - get delta CRL. // // If only one of CERT_STORE_BASE_CRL_FLAG or CERT_STORE_DELTA_CRL_FLAG is // set, then, only returns either a base or delta CRL. In any case, the // appropriate base or delta flag will be cleared upon returned. If both // flags are set, then, only one of flags will be cleared. // // If an enabled verification check fails, then, its flag is set upon return. // // If pIssuerContext == NULL, then, an enabled CERT_STORE_SIGNATURE_FLAG // always fails and the CERT_STORE_NO_ISSUER_FLAG is also set. // // For a verification check failure, a pointer to the first or next // CRL_CONTEXT is still returned and SetLastError isn't updated. //-------------------------------------------------------------------------- PCCRL_CONTEXT WINAPI CertGetCRLFromStore( IN HCERTSTORE hCertStore, IN OPTIONAL PCCERT_CONTEXT pIssuerContext, IN PCCRL_CONTEXT pPrevCrlContext, IN OUT DWORD *pdwFlags ) { CERT_STORE_PROV_FIND_INFO FindInfo; DWORD dwMsgAndCertEncodingType; PCCRL_CONTEXT pCrlContext; if (*pdwFlags & ~(CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG | CERT_STORE_BASE_CRL_FLAG | CERT_STORE_DELTA_CRL_FLAG)) goto InvalidArg; if (NULL == pIssuerContext) dwMsgAndCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; else dwMsgAndCertEncodingType = pIssuerContext->dwCertEncodingType; FindInfo.cbSize = sizeof(FindInfo); FindInfo.dwMsgAndCertEncodingType = dwMsgAndCertEncodingType; FindInfo.dwFindFlags = 0; if (*pdwFlags & CERT_STORE_BASE_CRL_FLAG) FindInfo.dwFindFlags |= CRL_FIND_ISSUED_BY_BASE_FLAG; if (*pdwFlags & CERT_STORE_DELTA_CRL_FLAG) FindInfo.dwFindFlags |= CRL_FIND_ISSUED_BY_DELTA_FLAG; FindInfo.dwFindType = CRL_FIND_ISSUED_BY; FindInfo.pvFindPara = pIssuerContext; if (pCrlContext = ToCrlContext(CheckAutoResyncAndFindElementInStore( (PCERT_STORE) hCertStore, CERT_STORE_CRL_CONTEXT - 1, &FindInfo, ToContextElement(pPrevCrlContext) ))) VerifyCrl( pCrlContext, pIssuerContext, pdwFlags ); CommonReturn: return pCrlContext; ErrorReturn: if (pPrevCrlContext) CertFreeCRLContext(pPrevCrlContext); pCrlContext = NULL; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) } //+------------------------------------------------------------------------- // Enumerate the CRL contexts in the store. // // If a CRL isn't found, NULL is returned. // Otherwise, a pointer to a read only CRL_CONTEXT is returned. CRL_CONTEXT // must be freed by calling CertFreeCRLContext or is freed when passed as the // pPrevCrlContext on a subsequent call. CertDuplicateCRLContext // can be called to make a duplicate. // // pPrevCrlContext MUST BE NULL to enumerate the first // CRL in the store. Successive CRLs are enumerated by setting // pPrevCrlContext to the CRL_CONTEXT returned by a previous call. // // NOTE: a NON-NULL pPrevCrlContext is always CertFreeCRLContext'ed by // this function, even for an error. //-------------------------------------------------------------------------- PCCRL_CONTEXT WINAPI CertEnumCRLsInStore( IN HCERTSTORE hCertStore, IN PCCRL_CONTEXT pPrevCrlContext ) { return ToCrlContext(CheckAutoResyncAndFindElementInStore( (PCERT_STORE) hCertStore, CERT_STORE_CRL_CONTEXT - 1, &FindAnyInfo, ToContextElement(pPrevCrlContext) )); } //+------------------------------------------------------------------------- // Find the first or next CRL context in the store. // // The CRL is found according to the dwFindType and its pvFindPara. // See below for a list of the find types and its parameters. // // Currently dwFindFlags isn't used and must be set to 0. // // Usage of dwCertEncodingType depends on the dwFindType. // // If the first or next CRL isn't found, NULL is returned. // Otherwise, a pointer to a read only CRL_CONTEXT is returned. CRL_CONTEXT // must be freed by calling CertFreeCRLContext or is freed when passed as the // pPrevCrlContext on a subsequent call. CertDuplicateCRLContext // can be called to make a duplicate. // // pPrevCrlContext MUST BE NULL on the first // call to find the CRL. To find the next CRL, the // pPrevCrlContext is set to the CRL_CONTEXT returned by a previous call. // // NOTE: a NON-NULL pPrevCrlContext is always CertFreeCRLContext'ed by // this function, even for an error. //-------------------------------------------------------------------------- PCCRL_CONTEXT WINAPI CertFindCRLInStore( IN HCERTSTORE hCertStore, IN DWORD dwCertEncodingType, IN DWORD dwFindFlags, IN DWORD dwFindType, IN const void *pvFindPara, IN PCCRL_CONTEXT pPrevCrlContext ) { CERT_STORE_PROV_FIND_INFO FindInfo; FindInfo.cbSize = sizeof(FindInfo); FindInfo.dwMsgAndCertEncodingType = dwCertEncodingType; FindInfo.dwFindFlags = dwFindFlags; FindInfo.dwFindType = dwFindType; FindInfo.pvFindPara = pvFindPara; return ToCrlContext(CheckAutoResyncAndFindElementInStore( (PCERT_STORE) hCertStore, CERT_STORE_CRL_CONTEXT - 1, &FindInfo, ToContextElement(pPrevCrlContext) )); } //+------------------------------------------------------------------------- // Duplicate a CRL context //-------------------------------------------------------------------------- PCCRL_CONTEXT WINAPI CertDuplicateCRLContext( IN PCCRL_CONTEXT pCrlContext ) { if (pCrlContext) AddRefContextElement(ToContextElement(pCrlContext)); return pCrlContext; } //+------------------------------------------------------------------------- // Create a CRL context from the encoded CRL. The created // context isn't put in a store. // // Makes a copy of the encoded CRL in the created context. // // If unable to decode and create the CRL context, NULL is returned. // Otherwise, a pointer to a read only CRL_CONTEXT is returned. // CRL_CONTEXT must be freed by calling CertFreeCRLContext. // CertDuplicateCRLContext can be called to make a duplicate. // // CertSetCRLContextProperty and CertGetCRLContextProperty can be called // to store properties for the CRL. //-------------------------------------------------------------------------- PCCRL_CONTEXT WINAPI CertCreateCRLContext( IN DWORD dwCertEncodingType, IN const BYTE *pbCrlEncoded, IN DWORD cbCrlEncoded ) { PCCRL_CONTEXT pCrlContext; CertAddEncodedCRLToStore( NULL, // hCertStore dwCertEncodingType, pbCrlEncoded, cbCrlEncoded, CERT_STORE_ADD_ALWAYS, &pCrlContext ); return pCrlContext; } //+------------------------------------------------------------------------- // Free a CRL context // // There needs to be a corresponding free for each context obtained by a // get, duplicate or create. //-------------------------------------------------------------------------- BOOL WINAPI CertFreeCRLContext( IN PCCRL_CONTEXT pCrlContext ) { ReleaseContextElement(ToContextElement(pCrlContext)); return TRUE; } //+------------------------------------------------------------------------- // Set the property for the specified CRL context. // // Same Property Ids and semantics as CertSetCertificateContextProperty. //-------------------------------------------------------------------------- BOOL WINAPI CertSetCRLContextProperty( IN PCCRL_CONTEXT pCrlContext, IN DWORD dwPropId, IN DWORD dwFlags, IN const void *pvData ) { return SetProperty( ToContextElement(pCrlContext), dwPropId, dwFlags, pvData ); } //+------------------------------------------------------------------------- // Get the property for the specified CRL context. // // Same Property Ids and semantics as CertGetCertificateContextProperty. // // CERT_SHA1_HASH_PROP_ID, CERT_MD5_HASH_PROP_ID or // CERT_SIGNATURE_HASH_PROP_ID is the predefined property of most interest. //-------------------------------------------------------------------------- BOOL WINAPI CertGetCRLContextProperty( IN PCCRL_CONTEXT pCrlContext, IN DWORD dwPropId, OUT void *pvData, IN OUT DWORD *pcbData ) { return GetProperty( ToContextElement(pCrlContext), dwPropId, pvData, pcbData ); } //+------------------------------------------------------------------------- // Enumerate the properties for the specified CRL context. //-------------------------------------------------------------------------- DWORD WINAPI CertEnumCRLContextProperties( IN PCCRL_CONTEXT pCrlContext, IN DWORD dwPropId ) { return EnumProperties( ToContextElement(pCrlContext), dwPropId ); } //+------------------------------------------------------------------------- // Called by qsort. // // Compare's the CRL entry's serial numbers. Note, since we won't be adding // any entries, don't need to worry about leading 0's or ff's. Also, ASN.1 // decoding should have removed them. // // The elements being sorted are pointers to the CRL entries. Not the // CRL entries. //-------------------------------------------------------------------------- STATIC int __cdecl CompareCrlEntry( IN const void *pelem1, IN const void *pelem2 ) { PCRL_ENTRY p1 = *((PCRL_ENTRY *) pelem1); PCRL_ENTRY p2 = *((PCRL_ENTRY *) pelem2); DWORD cb1 = p1->SerialNumber.cbData; DWORD cb2 = p2->SerialNumber.cbData; if (cb1 == cb2) { if (0 == cb1) return 0; else return memcmp(p1->SerialNumber.pbData, p2->SerialNumber.pbData, cb1); } else if (cb1 < cb2) return -1; else return 1; } //+------------------------------------------------------------------------- // Called by bsearch. // // Compare's the key's serial number with the CRL entry's serial number // // The elements being searched are pointers to the CRL entries. Not the // CRL entries. //-------------------------------------------------------------------------- STATIC int __cdecl CompareCrlEntrySerialNumber( IN const void *pkey, IN const void *pvalue ) { PCRYPT_INTEGER_BLOB pSerialNumber = (PCRYPT_INTEGER_BLOB) pkey; PCRL_ENTRY pCrlEntry = *((PCRL_ENTRY *) pvalue); DWORD cb1 = pSerialNumber->cbData; DWORD cb2 = pCrlEntry->SerialNumber.cbData; if (cb1 == cb2) { if (0 == cb1) return 0; else return memcmp(pSerialNumber->pbData, pCrlEntry->SerialNumber.pbData, cb1); } else if (cb1 < cb2) return -1; else return 1; } //+------------------------------------------------------------------------- // Search the CRL's list of entries for the specified certificate. // // TRUE is returned if we were able to search the list. Otherwise, FALSE is // returned, // // For success, if the certificate was found in the list, *ppCrlEntry is // updated with a pointer to the entry. Otherwise, *ppCrlEntry is set to NULL. // The returned entry isn't allocated and must not be freed. // // dwFlags and pvReserved currently aren't used and must be set to 0 or NULL. //-------------------------------------------------------------------------- BOOL WINAPI CertFindCertificateInCRL( IN PCCERT_CONTEXT pCert, IN PCCRL_CONTEXT pCrlContext, IN DWORD dwFlags, IN OPTIONAL void *pvReserved, OUT PCRL_ENTRY *ppCrlEntry ) { BOOL fResult; PCRL_INFO pInfo = pCrlContext->pCrlInfo; PCONTEXT_ELEMENT pCacheEle; PCERT_STORE pCacheStore; PCRL_ENTRY *ppSortedEntry; DWORD cEntry; PCRL_ENTRY *ppFoundEntry; *ppCrlEntry = NULL; // Get qsorted pointers to the CRL Entries if (0 == (cEntry = pInfo->cCRLEntry)) goto SuccessReturn; if (NULL == (pCacheEle = GetCacheElement(ToContextElement(pCrlContext)))) goto NoCacheElementError; pCacheStore = pCacheEle->pStore; LockStore(pCacheStore); if (NULL == (ppSortedEntry = ToCrlContextSuffix(pCacheEle)->ppSortedEntry)) { if (ppSortedEntry = (PCRL_ENTRY *) PkiNonzeroAlloc( cEntry * sizeof(PCRL_ENTRY))) { // Initialize the array of entry pointers DWORD c = cEntry; PCRL_ENTRY p = pInfo->rgCRLEntry; PCRL_ENTRY *pp = ppSortedEntry; for ( ; c > 0; c--, p++, pp++) *pp = p; // Now sort the CRL entry pointers qsort(ppSortedEntry, cEntry, sizeof(PCRL_ENTRY), CompareCrlEntry); ToCrlContextSuffix(pCacheEle)->ppSortedEntry = ppSortedEntry; } } UnlockStore(pCacheStore); if (NULL == ppSortedEntry) goto OutOfMemory; // Search the sorted subject entry pointers if (ppFoundEntry = (PCRL_ENTRY *) bsearch(&pCert->pCertInfo->SerialNumber, ppSortedEntry, cEntry, sizeof(PCRL_ENTRY), CompareCrlEntrySerialNumber)) *ppCrlEntry = *ppFoundEntry; SuccessReturn: fResult = TRUE; CommonReturn: return fResult; ErrorReturn: *ppCrlEntry = (PCRL_ENTRY) 1; fResult = FALSE; goto CommonReturn; TRACE_ERROR(NoCacheElementError) TRACE_ERROR(OutOfMemory) } //+========================================================================= // CTL APIs //========================================================================== BOOL WINAPI CertAddEncodedCTLToStore( IN HCERTSTORE hCertStore, IN DWORD dwMsgAndCertEncodingType, IN const BYTE *pbCtlEncoded, IN DWORD cbCtlEncoded, IN DWORD dwAddDisposition, OUT OPTIONAL PCCTL_CONTEXT *ppCtlContext ) { BOOL fResult; PCONTEXT_ELEMENT pStoreEle = NULL; fResult = AddEncodedContextToStore( (PCERT_STORE) hCertStore, CERT_STORE_CTL_CONTEXT - 1, dwMsgAndCertEncodingType, pbCtlEncoded, cbCtlEncoded, dwAddDisposition, ppCtlContext ? &pStoreEle : NULL ); if (ppCtlContext) *ppCtlContext = ToCtlContext(pStoreEle); return fResult; } BOOL WINAPI CertAddCTLContextToStore( IN HCERTSTORE hCertStore, IN PCCTL_CONTEXT pCtlContext, IN DWORD dwAddDisposition, OUT OPTIONAL PCCTL_CONTEXT *ppStoreContext ) { BOOL fResult; PCONTEXT_ELEMENT pStoreEle = NULL; fResult = AddContextToStore( (PCERT_STORE) hCertStore, ToContextElement(pCtlContext), pCtlContext->dwMsgAndCertEncodingType, pCtlContext->pbCtlEncoded, pCtlContext->cbCtlEncoded, dwAddDisposition, ppStoreContext ? &pStoreEle : NULL ); if (ppStoreContext) *ppStoreContext = ToCtlContext(pStoreEle); return fResult; } BOOL WINAPI CertAddCTLLinkToStore( IN HCERTSTORE hCertStore, IN PCCTL_CONTEXT pCtlContext, IN DWORD dwAddDisposition, OUT OPTIONAL PCCTL_CONTEXT *ppStoreContext ) { BOOL fResult; PCONTEXT_ELEMENT pStoreEle = NULL; fResult = AddLinkContextToCacheStore( (PCERT_STORE) hCertStore, ToContextElement(pCtlContext), dwAddDisposition, ppStoreContext ? &pStoreEle : NULL ); if (ppStoreContext) *ppStoreContext = ToCtlContext(pStoreEle); return fResult; } //+------------------------------------------------------------------------- // Serialize the CTL context's encoded CTL and its properties. //-------------------------------------------------------------------------- BOOL WINAPI CertSerializeCTLStoreElement( IN PCCTL_CONTEXT pCtlContext, IN DWORD dwFlags, OUT BYTE *pbElement, IN OUT DWORD *pcbElement ) { return SerializeContextElement( ToContextElement(pCtlContext), dwFlags, pbElement, pcbElement ); } //+------------------------------------------------------------------------- // Delete the specified CTL from the store. // // All subsequent gets for the CTL will fail. However, // memory allocated for the CTL isn't freed until all of its contexts // have also been freed. // // The pCtlContext is obtained from a get or duplicate. // // NOTE: the pCtlContext is always CertFreeCTLContext'ed by // this function, even for an error. //-------------------------------------------------------------------------- BOOL WINAPI CertDeleteCTLFromStore( IN PCCTL_CONTEXT pCtlContext ) { assert(NULL == pCtlContext || (CERT_STORE_CTL_CONTEXT - 1) == ToContextElement(pCtlContext)->dwContextType); return DeleteContextElement(ToContextElement(pCtlContext)); } //+------------------------------------------------------------------------- // Duplicate a CTL context //-------------------------------------------------------------------------- PCCTL_CONTEXT WINAPI CertDuplicateCTLContext( IN PCCTL_CONTEXT pCtlContext ) { if (pCtlContext) AddRefContextElement(ToContextElement(pCtlContext)); return pCtlContext; } //+------------------------------------------------------------------------- // Create a CTL context from the encoded CTL. The created // context isn't put in a store. // // Makes a copy of the encoded CTL in the created context. // // If unable to decode and create the CTL context, NULL is returned. // Otherwise, a pointer to a read only CTL_CONTEXT is returned. // CTL_CONTEXT must be freed by calling CertFreeCTLContext. // CertDuplicateCTLContext can be called to make a duplicate. // // CertSetCTLContextProperty and CertGetCTLContextProperty can be called // to store properties for the CTL. //-------------------------------------------------------------------------- PCCTL_CONTEXT WINAPI CertCreateCTLContext( IN DWORD dwMsgAndCertEncodingType, IN const BYTE *pbCtlEncoded, IN DWORD cbCtlEncoded ) { PCCTL_CONTEXT pCtlContext; CertAddEncodedCTLToStore( NULL, // hCertStore dwMsgAndCertEncodingType, pbCtlEncoded, cbCtlEncoded, CERT_STORE_ADD_ALWAYS, &pCtlContext ); return pCtlContext; } //+------------------------------------------------------------------------- // Free a CTL context // // There needs to be a corresponding free for each context obtained by a // get, duplicate or create. //-------------------------------------------------------------------------- BOOL WINAPI CertFreeCTLContext( IN PCCTL_CONTEXT pCtlContext ) { ReleaseContextElement(ToContextElement(pCtlContext)); return TRUE; } //+------------------------------------------------------------------------- // Set the property for the specified CTL context. // // Same Property Ids and semantics as CertSetCertificateContextProperty. //-------------------------------------------------------------------------- BOOL WINAPI CertSetCTLContextProperty( IN PCCTL_CONTEXT pCtlContext, IN DWORD dwPropId, IN DWORD dwFlags, IN const void *pvData ) { return SetProperty( ToContextElement(pCtlContext), dwPropId, dwFlags, pvData ); } //+------------------------------------------------------------------------- // Get the property for the specified CTL context. // // Same Property Ids and semantics as CertGetCertificateContextProperty. // // CERT_SHA1_HASH_PROP_ID or CERT_MD5_HASH_PROP_ID is the predefined // property of most interest. //-------------------------------------------------------------------------- BOOL WINAPI CertGetCTLContextProperty( IN PCCTL_CONTEXT pCtlContext, IN DWORD dwPropId, OUT void *pvData, IN OUT DWORD *pcbData ) { return GetProperty( ToContextElement(pCtlContext), dwPropId, pvData, pcbData ); } //+------------------------------------------------------------------------- // Enumerate the properties for the specified CTL context. //-------------------------------------------------------------------------- DWORD WINAPI CertEnumCTLContextProperties( IN PCCTL_CONTEXT pCtlContext, IN DWORD dwPropId ) { return EnumProperties( ToContextElement(pCtlContext), dwPropId ); } //+------------------------------------------------------------------------- // Enumerate the CTL contexts in the store. // // If a CTL isn't found, NULL is returned. // Otherwise, a pointer to a read only CTL_CONTEXT is returned. CTL_CONTEXT // must be freed by calling CertFreeCTLContext or is freed when passed as the // pPrevCtlContext on a subsequent call. CertDuplicateCTLContext // can be called to make a duplicate. // // pPrevCtlContext MUST BE NULL to enumerate the first // CTL in the store. Successive CTLs are enumerated by setting // pPrevCtlContext to the CTL_CONTEXT returned by a previous call. // // NOTE: a NON-NULL pPrevCtlContext is always CertFreeCTLContext'ed by // this function, even for an error. //-------------------------------------------------------------------------- PCCTL_CONTEXT WINAPI CertEnumCTLsInStore( IN HCERTSTORE hCertStore, IN PCCTL_CONTEXT pPrevCtlContext ) { return ToCtlContext(CheckAutoResyncAndFindElementInStore( (PCERT_STORE) hCertStore, CERT_STORE_CTL_CONTEXT - 1, &FindAnyInfo, ToContextElement(pPrevCtlContext) )); } STATIC BOOL CompareAlgorithmIdentifier( IN DWORD dwEncodingType, IN PCRYPT_ALGORITHM_IDENTIFIER pAlg1, IN PCRYPT_ALGORITHM_IDENTIFIER pAlg2 ) { BOOL fResult = FALSE; if (NULL == pAlg1->pszObjId) { if (NULL == pAlg2->pszObjId) // Both are NULL fResult = TRUE; // else // One of the OIDs is NULL } else if (pAlg2->pszObjId) { if (0 == strcmp(pAlg1->pszObjId, pAlg2->pszObjId)) { DWORD cb1 = pAlg1->Parameters.cbData; BYTE *pb1 = pAlg1->Parameters.pbData; DWORD cb2 = pAlg2->Parameters.cbData; BYTE *pb2 = pAlg2->Parameters.pbData; if (X509_ASN_ENCODING == GET_CERT_ENCODING_TYPE(dwEncodingType)) { // Check for NULL parameters: {0x05, 0x00} if (2 == cb1 && 0x05 == pb1[0] && 0x00 == pb1[1]) cb1 = 0; if (2 == cb2 && 0x05 == pb2[0] && 0x00 == pb2[1]) cb2 = 0; } if (cb1 == cb2) { if (0 == cb1 || 0 == memcmp(pb1, pb2, cb1)) fResult = TRUE; } } } // else // One of the OIDs is NULL return fResult; } //+------------------------------------------------------------------------- // Called by qsort. Compare's the CTL entry's SubjectIdentifier. // // The elements being sorted are pointers to the CTL entries. Not the // CTL entries. //-------------------------------------------------------------------------- STATIC int __cdecl CompareCtlEntry( IN const void *pelem1, IN const void *pelem2 ) { PCTL_ENTRY p1 = *((PCTL_ENTRY *) pelem1); PCTL_ENTRY p2 = *((PCTL_ENTRY *) pelem2); DWORD cb1 = p1->SubjectIdentifier.cbData; DWORD cb2 = p2->SubjectIdentifier.cbData; if (cb1 == cb2) { if (0 == cb1) return 0; else return memcmp(p1->SubjectIdentifier.pbData, p2->SubjectIdentifier.pbData, cb1); } else if (cb1 < cb2) return -1; else return 1; } //+------------------------------------------------------------------------- // Called by bsearch. Compare's the key's SubjectIdentifier with the CTL // entry's SubjectIdentifier. // // The elements being searched are pointers to the CTL entries. Not the // CTL entries. //-------------------------------------------------------------------------- STATIC int __cdecl CompareCtlEntrySubjectIdentifier( IN const void *pkey, IN const void *pvalue ) { PCRYPT_DATA_BLOB pSubjectIdentifier = (PCRYPT_DATA_BLOB) pkey; PCTL_ENTRY pCtlEntry = *((PCTL_ENTRY *) pvalue); DWORD cb1 = pSubjectIdentifier->cbData; DWORD cb2 = pCtlEntry->SubjectIdentifier.cbData; if (cb1 == cb2) { if (0 == cb1) return 0; else return memcmp(pSubjectIdentifier->pbData, pCtlEntry->SubjectIdentifier.pbData, cb1); } else if (cb1 < cb2) return -1; else return 1; } //+------------------------------------------------------------------------- // Attempt to find the specified subject in the CTL. // // For CTL_CERT_SUBJECT_TYPE, pvSubject points to a CERT_CONTEXT. The CTL's // SubjectAlgorithm is examined to determine the representation of the // subject's identity. Initially, only SHA1 or MD5 hash will be supported. // The appropriate hash property is obtained from the CERT_CONTEXT. // // For CTL_ANY_SUBJECT_TYPE, pvSubject points to the CTL_ANY_SUBJECT_INFO // structure which contains the SubjectAlgorithm to be matched in the CTL // and the SubjectIdentifer to be matched in one of the CTL entries. // // The cetificate's hash or the CTL_ANY_SUBJECT_INFO's SubjectIdentifier // is used as the key in searching the subject entries. A binary // memory comparison is done between the key and the entry's SubjectIdentifer. // // dwEncodingType isn't used for either of the above SubjectTypes. //-------------------------------------------------------------------------- PCTL_ENTRY WINAPI CertFindSubjectInCTL( IN DWORD dwEncodingType, IN DWORD dwSubjectType, IN void *pvSubject, IN PCCTL_CONTEXT pCtlContext, IN DWORD dwFlags ) { PCTL_ENTRY *ppSubjectEntry; PCTL_ENTRY pSubjectEntry; PCTL_INFO pInfo = pCtlContext->pCtlInfo; PCONTEXT_ELEMENT pCacheEle; PCERT_STORE pCacheStore; BYTE rgbHash[MAX_HASH_LEN]; CRYPT_DATA_BLOB Key; PCTL_ENTRY *ppSortedEntry; DWORD cEntry; // Get Key to be used in bsearch switch (dwSubjectType) { case CTL_CERT_SUBJECT_TYPE: { DWORD Algid; DWORD dwPropId; if (NULL == pInfo->SubjectAlgorithm.pszObjId) goto NoSubjectAlgorithm; Algid = CertOIDToAlgId(pInfo->SubjectAlgorithm.pszObjId); switch (Algid) { case CALG_SHA1: dwPropId = CERT_SHA1_HASH_PROP_ID; break; case CALG_MD5: dwPropId = CERT_MD5_HASH_PROP_ID; break; default: goto UnknownAlgid; } Key.cbData = MAX_HASH_LEN; if (!CertGetCertificateContextProperty( (PCCERT_CONTEXT) pvSubject, dwPropId, rgbHash, &Key.cbData) || 0 == Key.cbData) goto GetHashError; Key.pbData = rgbHash; } break; case CTL_ANY_SUBJECT_TYPE: { PCTL_ANY_SUBJECT_INFO pAnyInfo = (PCTL_ANY_SUBJECT_INFO) pvSubject; if (pAnyInfo->SubjectAlgorithm.pszObjId && !CompareAlgorithmIdentifier( (pCtlContext->dwMsgAndCertEncodingType >> 16) & CERT_ENCODING_TYPE_MASK, &pAnyInfo->SubjectAlgorithm, &pInfo->SubjectAlgorithm)) goto NotFoundError; Key = pAnyInfo->SubjectIdentifier; } break; default: goto InvalidSubjectType; } // Get qsorted pointers to the Subject Entries if (0 == (cEntry = pInfo->cCTLEntry)) goto NoEntryError; if (NULL == (pCacheEle = GetCacheElement(ToContextElement(pCtlContext)))) goto NoCacheElementError; pCacheStore = pCacheEle->pStore; LockStore(pCacheStore); if (NULL == (ppSortedEntry = ToCtlContextSuffix(pCacheEle)->ppSortedEntry)) { if (ppSortedEntry = (PCTL_ENTRY *) PkiNonzeroAlloc( cEntry * sizeof(PCTL_ENTRY))) { // Initialize the array of entry pointers DWORD c = cEntry; PCTL_ENTRY p = pInfo->rgCTLEntry; PCTL_ENTRY *pp = ppSortedEntry; for ( ; c > 0; c--, p++, pp++) *pp = p; // Now sort the subject entry pointers qsort(ppSortedEntry, cEntry, sizeof(PCTL_ENTRY), CompareCtlEntry); ToCtlContextSuffix(pCacheEle)->ppSortedEntry = ppSortedEntry; } } UnlockStore(pCacheStore); if (NULL == ppSortedEntry) goto OutOfMemory; // Search the sorted subject entry pointers if (NULL == (ppSubjectEntry = (PCTL_ENTRY *) bsearch(&Key, ppSortedEntry, cEntry, sizeof(PCTL_ENTRY), CompareCtlEntrySubjectIdentifier))) goto NotFoundError; pSubjectEntry = *ppSubjectEntry; CommonReturn: return pSubjectEntry; NotFoundError: SetLastError((DWORD) CRYPT_E_NOT_FOUND); ErrorReturn: pSubjectEntry = NULL; goto CommonReturn; SET_ERROR(NoSubjectAlgorithm, CRYPT_E_NOT_FOUND) SET_ERROR(UnknownAlgid, NTE_BAD_ALGID) SET_ERROR(NoEntryError, CRYPT_E_NOT_FOUND) TRACE_ERROR(NoCacheElementError) TRACE_ERROR(GetHashError) SET_ERROR(InvalidSubjectType, E_INVALIDARG) TRACE_ERROR(OutOfMemory) } //+------------------------------------------------------------------------- // Find the first or next CTL context in the store. // // The CTL is found according to the dwFindType and its pvFindPara. // See below for a list of the find types and its parameters. // // Currently dwFindFlags isn't used and must be set to 0. // // Usage of dwMsgAndCertEncodingType depends on the dwFindType. // // If the first or next CTL isn't found, NULL is returned. // Otherwise, a pointer to a read only CTL_CONTEXT is returned. CTL_CONTEXT // must be freed by calling CertFreeCTLContext or is freed when passed as the // pPrevCtlContext on a subsequent call. CertDuplicateCTLContext // can be called to make a duplicate. // // pPrevCtlContext MUST BE NULL on the first // call to find the CTL. To find the next CTL, the // pPrevCtlContext is set to the CTL_CONTEXT returned by a previous call. // // NOTE: a NON-NULL pPrevCtlContext is always CertFreeCTLContext'ed by // this function, even for an error. //-------------------------------------------------------------------------- PCCTL_CONTEXT WINAPI CertFindCTLInStore( IN HCERTSTORE hCertStore, IN DWORD dwMsgAndCertEncodingType, IN DWORD dwFindFlags, IN DWORD dwFindType, IN const void *pvFindPara, IN PCCTL_CONTEXT pPrevCtlContext ) { CERT_STORE_PROV_FIND_INFO FindInfo; FindInfo.cbSize = sizeof(FindInfo); FindInfo.dwMsgAndCertEncodingType = dwMsgAndCertEncodingType; FindInfo.dwFindFlags = dwFindFlags; FindInfo.dwFindType = dwFindType; FindInfo.pvFindPara = pvFindPara; return ToCtlContext(CheckAutoResyncAndFindElementInStore( (PCERT_STORE) hCertStore, CERT_STORE_CTL_CONTEXT - 1, &FindInfo, ToContextElement(pPrevCtlContext) )); } //+========================================================================= // CERT_CONTEXT Functions //========================================================================== // pbCertEncoded has already been allocated STATIC PCONTEXT_ELEMENT CreateCertElement( IN PCERT_STORE pStore, IN DWORD dwCertEncodingType, IN BYTE *pbCertEncoded, IN DWORD cbCertEncoded, IN OPTIONAL PSHARE_ELEMENT pShareEle ) { PCONTEXT_ELEMENT pEle = NULL; PCERT_CONTEXT pCert; PCERT_INFO pInfo = NULL; if (0 == GET_CERT_ENCODING_TYPE(dwCertEncodingType)) { SetLastError((DWORD) E_INVALIDARG); goto ErrorReturn; } if (NULL == pShareEle) { cbCertEncoded = AdjustEncodedLength( dwCertEncodingType, pbCertEncoded, cbCertEncoded); if (NULL == (pInfo = (PCERT_INFO) AllocAndDecodeObject( dwCertEncodingType, X509_CERT_TO_BE_SIGNED, pbCertEncoded, cbCertEncoded))) goto ErrorReturn; } // Allocate and initialize the cert element structure pEle = (PCONTEXT_ELEMENT) PkiZeroAlloc(sizeof(CONTEXT_ELEMENT) + sizeof(CERT_CONTEXT)); if (pEle == NULL) goto ErrorReturn; pEle->dwElementType = ELEMENT_TYPE_CACHE; pEle->dwContextType = CERT_STORE_CERTIFICATE_CONTEXT - 1; pEle->lRefCnt = 1; pEle->pEle = pEle; pEle->pStore = pStore; pEle->pProvStore = pStore; pCert = (PCERT_CONTEXT) ToCertContext(pEle); pCert->dwCertEncodingType = dwCertEncodingType & CERT_ENCODING_TYPE_MASK; pCert->pbCertEncoded = pbCertEncoded; pCert->cbCertEncoded = cbCertEncoded; if (pShareEle) { pEle->pShareEle = pShareEle; assert(pShareEle->pvInfo); pCert->pCertInfo = (PCERT_INFO) pShareEle->pvInfo; assert(pbCertEncoded == pShareEle->pbEncoded); assert(cbCertEncoded == pShareEle->cbEncoded); } else { pCert->pCertInfo = pInfo; CertPerfIncrementCertElementCurrentCount(); CertPerfIncrementCertElementTotalCount(); } pCert->hCertStore = (HCERTSTORE) pStore; CommonReturn: return pEle; ErrorReturn: if (pEle) { PkiFree(pEle); pEle = NULL; } PkiFree(pInfo); goto CommonReturn; } STATIC void FreeCertElement(IN PCONTEXT_ELEMENT pEle) { PCCERT_CONTEXT pCert = ToCertContext(pEle); if (pEle->pShareEle) ReleaseShareElement(pEle->pShareEle); else { PkiFree(pCert->pbCertEncoded); PkiFree(pCert->pCertInfo); CertPerfDecrementCertElementCurrentCount(); } PkiFree(pEle); } STATIC BOOL CompareCertHash( IN PCCERT_CONTEXT pCert, IN DWORD dwPropId, IN PCRYPT_HASH_BLOB pHash ) { BYTE rgbHash[MAX_HASH_LEN]; DWORD cbHash = MAX_HASH_LEN; CertGetCertificateContextProperty( pCert, dwPropId, rgbHash, &cbHash ); if (cbHash == pHash->cbData && memcmp(rgbHash, pHash->pbData, cbHash) == 0) return TRUE; else return FALSE; } STATIC BOOL CompareNameStrW( IN DWORD dwCertEncodingType, IN PCERT_NAME_BLOB pName, IN LPCWSTR pwszFind ) { BOOL fResult = FALSE; DWORD cwszFind; LPWSTR pwszName = NULL; DWORD cwszName; if (pwszFind == NULL || *pwszFind == L'\0') return TRUE; cwszName = CertNameToStrW( dwCertEncodingType, pName, CERT_SIMPLE_NAME_STR, NULL, // pwsz 0 // cwsz ); if (pwszName = (LPWSTR) PkiNonzeroAlloc(cwszName * sizeof(WCHAR))) { cwszName = CertNameToStrW( dwCertEncodingType, pName, CERT_SIMPLE_NAME_STR, pwszName, cwszName) - 1; cwszFind = wcslen(pwszFind); // Start at end of the certificate's name and slide one character // to the left until a match or reach the beginning of the // certificate name. for ( ; cwszName >= cwszFind; cwszName--) { pwszName[cwszName] = L'\0'; if (CSTR_EQUAL == CompareStringU( LOCALE_USER_DEFAULT, NORM_IGNORECASE, pwszFind, -1, &pwszName[cwszName - cwszFind], -1 )) { fResult = TRUE; break; } } PkiFree(pwszName); } return fResult; } STATIC BOOL CompareNameStrA( IN DWORD dwCertEncodingType, IN PCERT_NAME_BLOB pName, IN LPCSTR pszFind ) { BOOL fResult = FALSE; DWORD cszFind; LPSTR pszName = NULL; DWORD cszName; if (pszFind == NULL || *pszFind == '\0') return TRUE; cszName = CertNameToStrA( dwCertEncodingType, pName, CERT_SIMPLE_NAME_STR, NULL, // psz 0 // csz ); if (pszName = (LPSTR) PkiNonzeroAlloc(cszName)) { cszName = CertNameToStrA( dwCertEncodingType, pName, CERT_SIMPLE_NAME_STR, pszName, cszName) - 1; cszFind = strlen(pszFind); // Start at end of the certificate's name and slide one character // to the left until a match or reach the beginning of the // certificate name. for ( ; cszName >= cszFind; cszName--) { pszName[cszName] = '\0'; if (CSTR_EQUAL == CompareStringA( LOCALE_USER_DEFAULT, NORM_IGNORECASE, pszFind, -1, &pszName[cszName - cszFind], -1 )) { fResult = TRUE; break; } } PkiFree(pszName); } return fResult; } STATIC BOOL CompareCtlUsageIdentifiers( IN PCTL_USAGE pPara, IN DWORD cUsage, IN PCTL_USAGE pUsage, IN BOOL fOrUsage ) { if (pPara && pPara->cUsageIdentifier) { DWORD cId1 = pPara->cUsageIdentifier; LPSTR *ppszId1 = pPara->rgpszUsageIdentifier; for ( ; cId1 > 0; cId1--, ppszId1++) { DWORD i; for (i = 0; i < cUsage; i++) { DWORD cId2 = pUsage[i].cUsageIdentifier; LPSTR *ppszId2 = pUsage[i].rgpszUsageIdentifier; for ( ; cId2 > 0; cId2--, ppszId2++) { if (0 == strcmp(*ppszId1, *ppszId2)) { if (fOrUsage) return TRUE; break; } } if (cId2 > 0) break; } if (i == cUsage && !fOrUsage) return FALSE; } if (fOrUsage) // For the "OR" option we're here without any match return FALSE; // else // For the "AND" option we have matched all the specified // identifiers } return TRUE; } STATIC BOOL CompareCertUsage( IN PCCERT_CONTEXT pCert, IN DWORD dwFindFlags, IN PCTL_USAGE pPara ) { BOOL fResult; PCERT_INFO pInfo = pCert->pCertInfo; PCERT_EXTENSION pExt; // not allocated DWORD cbData; PCTL_USAGE pExtUsage = NULL; PCTL_USAGE pPropUsage = NULL; BYTE *pbPropData = NULL; CTL_USAGE rgUsage[2]; // Ext and/or Prop DWORD cUsage = 0; if (CERT_FIND_VALID_CTL_USAGE_FLAG & dwFindFlags) return IFC_IsEndCertValidForUsages( pCert, pPara, 0 != (dwFindFlags & CERT_FIND_OR_CTL_USAGE_FLAG)); if (0 == (CERT_FIND_PROP_ONLY_CTL_USAGE_FLAG & dwFindFlags)) { // Is there an Enhanced Key Usage Extension ?? if (pExt = CertFindExtension( szOID_ENHANCED_KEY_USAGE, pInfo->cExtension, pInfo->rgExtension )) { if (pExtUsage = (PCTL_USAGE) AllocAndDecodeObject( pCert->dwCertEncodingType, X509_ENHANCED_KEY_USAGE, pExt->Value.pbData, pExt->Value.cbData)) rgUsage[cUsage++] = *pExtUsage; } } if (0 == (CERT_FIND_EXT_ONLY_CTL_USAGE_FLAG & dwFindFlags)) { // Is there an Enhanced Key Usage (CTL Usage) property ?? if (CertGetCertificateContextProperty( pCert, CERT_CTL_USAGE_PROP_ID, NULL, // pvData &cbData) && cbData) { if (pbPropData = (BYTE *) PkiNonzeroAlloc(cbData)) { if (CertGetCertificateContextProperty( pCert, CERT_CTL_USAGE_PROP_ID, pbPropData, &cbData)) { if (pPropUsage = (PCTL_USAGE) AllocAndDecodeObject( pCert->dwCertEncodingType, X509_ENHANCED_KEY_USAGE, pbPropData, cbData)) rgUsage[cUsage++] = *pPropUsage; } } } } if (cUsage > 0) { if (dwFindFlags & CERT_FIND_NO_CTL_USAGE_FLAG) fResult = FALSE; else fResult = CompareCtlUsageIdentifiers(pPara, cUsage, rgUsage, 0 != (dwFindFlags & CERT_FIND_OR_CTL_USAGE_FLAG)); } else if (dwFindFlags & (CERT_FIND_OPTIONAL_CTL_USAGE_FLAG | CERT_FIND_NO_CTL_USAGE_FLAG)) fResult = TRUE; else fResult = FALSE; PkiFree(pExtUsage); PkiFree(pPropUsage); PkiFree(pbPropData); return fResult; } STATIC BOOL IsSameCert( IN PCCERT_CONTEXT pCert, IN PCCERT_CONTEXT pNew ) { BYTE rgbCertHash[SHA1_HASH_LEN]; DWORD cbCertHash = SHA1_HASH_LEN; BYTE rgbNewHash[SHA1_HASH_LEN]; DWORD cbNewHash = SHA1_HASH_LEN; CertGetCertificateContextProperty( pCert, CERT_SHA1_HASH_PROP_ID, rgbCertHash, &cbCertHash ); CertGetCertificateContextProperty( pNew, CERT_SHA1_HASH_PROP_ID, rgbNewHash, &cbNewHash ); if (SHA1_HASH_LEN == cbCertHash && SHA1_HASH_LEN == cbNewHash && 0 == memcmp(rgbCertHash, rgbNewHash, SHA1_HASH_LEN)) return TRUE; else return FALSE; } STATIC BOOL CompareCertElement( IN PCONTEXT_ELEMENT pEle, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN BOOL fArchived ) { PCCERT_CONTEXT pCert = ToCertContext(pEle); DWORD dwCmp = (pFindInfo->dwFindType >> CERT_COMPARE_SHIFT) & CERT_COMPARE_MASK; const void *pvFindPara = pFindInfo->pvFindPara; if (fArchived) { switch (dwCmp) { case CERT_COMPARE_SHA1_HASH: case CERT_COMPARE_MD5_HASH: case CERT_COMPARE_SIGNATURE_HASH: case CERT_COMPARE_SUBJECT_CERT: #ifdef CMS_PKCS7 case CERT_COMPARE_CERT_ID: #endif // CMS_PKCS7 case CERT_COMPARE_PUBKEY_MD5_HASH: break; default: return FALSE; } } switch (dwCmp) { case CERT_COMPARE_ANY: return TRUE; break; case CERT_COMPARE_SHA1_HASH: case CERT_COMPARE_MD5_HASH: case CERT_COMPARE_SIGNATURE_HASH: case CERT_COMPARE_KEY_IDENTIFIER: case CERT_COMPARE_PUBKEY_MD5_HASH: { DWORD dwPropId; switch (dwCmp) { case CERT_COMPARE_SHA1_HASH: dwPropId = CERT_SHA1_HASH_PROP_ID; break; case CERT_COMPARE_SIGNATURE_HASH: dwPropId = CERT_SIGNATURE_HASH_PROP_ID; break; case CERT_COMPARE_KEY_IDENTIFIER: dwPropId = CERT_KEY_IDENTIFIER_PROP_ID; break; case CERT_COMPARE_PUBKEY_MD5_HASH: dwPropId = CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID; break; case CERT_COMPARE_MD5_HASH: default: dwPropId = CERT_MD5_HASH_PROP_ID; } return CompareCertHash(pCert, dwPropId, (PCRYPT_HASH_BLOB) pvFindPara); } break; case CERT_COMPARE_NAME: { PCERT_NAME_BLOB pName; DWORD dwInfo = pFindInfo->dwFindType & 0xFF; DWORD dwCertEncodingType = pFindInfo->dwMsgAndCertEncodingType & CERT_ENCODING_TYPE_MASK; if (dwInfo == CERT_INFO_SUBJECT_FLAG) pName = &pCert->pCertInfo->Subject; else if (dwInfo == CERT_INFO_ISSUER_FLAG) pName = &pCert->pCertInfo->Issuer; else goto BadParameter; return dwCertEncodingType == pCert->dwCertEncodingType && CertCompareCertificateName(dwCertEncodingType, pName, (PCERT_NAME_BLOB) pvFindPara); } break; case CERT_COMPARE_ATTR: { PCERT_NAME_BLOB pName; DWORD dwInfo = pFindInfo->dwFindType & 0xFF; DWORD dwCertEncodingType = pFindInfo->dwMsgAndCertEncodingType & CERT_ENCODING_TYPE_MASK; if (dwInfo == CERT_INFO_SUBJECT_FLAG) pName = &pCert->pCertInfo->Subject; else if (dwInfo == CERT_INFO_ISSUER_FLAG) pName = &pCert->pCertInfo->Issuer; else goto BadParameter; return dwCertEncodingType == pCert->dwCertEncodingType && CertIsRDNAttrsInCertificateName(dwCertEncodingType, pFindInfo->dwFindFlags, pName, (PCERT_RDN) pvFindPara); } break; case CERT_COMPARE_PROPERTY: { DWORD dwPropId = *((DWORD *) pvFindPara); DWORD cbData = 0; return CertGetCertificateContextProperty( pCert, dwPropId, NULL, //pvData &cbData); } break; case CERT_COMPARE_PUBLIC_KEY: { return CertComparePublicKeyInfo( pCert->dwCertEncodingType, &pCert->pCertInfo->SubjectPublicKeyInfo, (PCERT_PUBLIC_KEY_INFO) pvFindPara); } break; case CERT_COMPARE_NAME_STR_A: case CERT_COMPARE_NAME_STR_W: { PCERT_NAME_BLOB pName; DWORD dwInfo = pFindInfo->dwFindType & 0xFF; DWORD dwCertEncodingType = pFindInfo->dwMsgAndCertEncodingType & CERT_ENCODING_TYPE_MASK; if (dwInfo == CERT_INFO_SUBJECT_FLAG) pName = &pCert->pCertInfo->Subject; else if (dwInfo == CERT_INFO_ISSUER_FLAG) pName = &pCert->pCertInfo->Issuer; else goto BadParameter; if (dwCertEncodingType == pCert->dwCertEncodingType) { if (dwCmp == CERT_COMPARE_NAME_STR_W) return CompareNameStrW(dwCertEncodingType, pName, (LPCWSTR) pvFindPara); else return CompareNameStrA(dwCertEncodingType, pName, (LPCSTR) pvFindPara); } else return FALSE; } break; case CERT_COMPARE_KEY_SPEC: { DWORD dwKeySpec; DWORD cbData = sizeof(dwKeySpec); return CertGetCertificateContextProperty( pCert, CERT_KEY_SPEC_PROP_ID, &dwKeySpec, &cbData) && dwKeySpec == *((DWORD *) pvFindPara); } break; case CERT_COMPARE_CTL_USAGE: return CompareCertUsage(pCert, pFindInfo->dwFindFlags, (PCTL_USAGE) pvFindPara); break; case CERT_COMPARE_SUBJECT_CERT: { DWORD dwCertEncodingType = pFindInfo->dwMsgAndCertEncodingType & CERT_ENCODING_TYPE_MASK; PCERT_INFO pCertId = (PCERT_INFO) pvFindPara; CRYPT_HASH_BLOB KeyId; if (dwCertEncodingType != pCert->dwCertEncodingType) return FALSE; if (Asn1UtilExtractKeyIdFromCertInfo( pCertId, &KeyId )) return CompareCertHash(pCert, CERT_KEY_IDENTIFIER_PROP_ID, &KeyId ); else return CertCompareCertificate( dwCertEncodingType, pCertId, pCert->pCertInfo); } break; case CERT_COMPARE_ISSUER_OF: { PCCERT_CONTEXT pSubject = (PCCERT_CONTEXT) pvFindPara; return pSubject->dwCertEncodingType == pCert->dwCertEncodingType && CertCompareCertificateName( pSubject->dwCertEncodingType, &pSubject->pCertInfo->Issuer, &pCert->pCertInfo->Subject); } break; case CERT_COMPARE_EXISTING: return IsSameCert((PCCERT_CONTEXT) pvFindPara, pCert); break; #ifdef CMS_PKCS7 case CERT_COMPARE_CERT_ID: { PCERT_ID pCertId = (PCERT_ID) pvFindPara; switch (pCertId->dwIdChoice) { case CERT_ID_ISSUER_SERIAL_NUMBER: { PCRYPT_INTEGER_BLOB pCertSerialNumber = &pCert->pCertInfo->SerialNumber; PCERT_NAME_BLOB pCertIssuer = &pCert->pCertInfo->Issuer; PCRYPT_INTEGER_BLOB pParaSerialNumber = &pCertId->IssuerSerialNumber.SerialNumber; PCERT_NAME_BLOB pParaIssuer = &pCertId->IssuerSerialNumber.Issuer; if (CertCompareIntegerBlob(pCertSerialNumber, pParaSerialNumber) && pCertIssuer->cbData == pParaIssuer->cbData && memcmp(pCertIssuer->pbData, pParaIssuer->pbData, pCertIssuer->cbData) == 0) return TRUE; else return FALSE; } break; case CERT_ID_KEY_IDENTIFIER: return CompareCertHash(pCert, CERT_KEY_IDENTIFIER_PROP_ID, &pCertId->KeyId ); break; case CERT_ID_SHA1_HASH: return CompareCertHash(pCert, CERT_SHA1_HASH_PROP_ID, &pCertId->HashId ); break; default: goto BadParameter; } } break; #endif // CMS_PKCS7 case CERT_COMPARE_CROSS_CERT_DIST_POINTS: { DWORD cbData = 0; if (CertFindExtension( szOID_CROSS_CERT_DIST_POINTS, pCert->pCertInfo->cExtension, pCert->pCertInfo->rgExtension) || CertGetCertificateContextProperty( pCert, CERT_CROSS_CERT_DIST_POINTS_PROP_ID, NULL, // pvData &cbData)) return TRUE; else return FALSE; } break; default: goto BadParameter; } BadParameter: SetLastError((DWORD) E_INVALIDARG); return FALSE; } STATIC BOOL IsNewerCertElement( IN PCONTEXT_ELEMENT pNewEle, IN PCONTEXT_ELEMENT pExistingEle ) { PCCERT_CONTEXT pNewCert = ToCertContext(pNewEle); PCCERT_CONTEXT pExistingCert = ToCertContext(pExistingEle); // CompareFileTime returns +1 if first time > second time return (0 < CompareFileTime( &pNewCert->pCertInfo->NotBefore, &pExistingCert->pCertInfo->NotBefore )); } //+========================================================================= // CRL_CONTEXT Functions //========================================================================== // pbCrlEncoded has already been allocated STATIC PCONTEXT_ELEMENT CreateCrlElement( IN PCERT_STORE pStore, IN DWORD dwCertEncodingType, IN BYTE *pbCrlEncoded, IN DWORD cbCrlEncoded, IN OPTIONAL PSHARE_ELEMENT pShareEle ) { PCONTEXT_ELEMENT pEle = NULL; PCRL_CONTEXT pCrl; PCRL_CONTEXT_SUFFIX pCrlSuffix; PCRL_INFO pInfo = NULL; if (0 == GET_CERT_ENCODING_TYPE(dwCertEncodingType)) { SetLastError((DWORD) E_INVALIDARG); goto ErrorReturn; } if (NULL == pShareEle) { cbCrlEncoded = AdjustEncodedLength( dwCertEncodingType, pbCrlEncoded, cbCrlEncoded); if (NULL == (pInfo = (PCRL_INFO) AllocAndDecodeObject( dwCertEncodingType, X509_CERT_CRL_TO_BE_SIGNED, pbCrlEncoded, cbCrlEncoded))) goto ErrorReturn; } // Allocate and initialize the CRL element structure pEle = (PCONTEXT_ELEMENT) PkiZeroAlloc(sizeof(CONTEXT_ELEMENT) + sizeof(CRL_CONTEXT) + sizeof(CRL_CONTEXT_SUFFIX)); if (pEle == NULL) goto ErrorReturn; pEle->dwElementType = ELEMENT_TYPE_CACHE; pEle->dwContextType = CERT_STORE_CRL_CONTEXT - 1; pEle->lRefCnt = 1; pEle->pEle = pEle; pEle->pStore = pStore; pEle->pProvStore = pStore; pCrl = (PCRL_CONTEXT) ToCrlContext(pEle); pCrl->dwCertEncodingType = dwCertEncodingType & CERT_ENCODING_TYPE_MASK; pCrl->pbCrlEncoded = pbCrlEncoded; pCrl->cbCrlEncoded = cbCrlEncoded; if (pShareEle) { pEle->pShareEle = pShareEle; assert(pShareEle->pvInfo); pCrl->pCrlInfo = (PCRL_INFO) pShareEle->pvInfo; assert(pbCrlEncoded == pShareEle->pbEncoded); assert(cbCrlEncoded == pShareEle->cbEncoded); } else { pCrl->pCrlInfo = pInfo; CertPerfIncrementCrlElementCurrentCount(); CertPerfIncrementCrlElementTotalCount(); } pCrl->hCertStore = (HCERTSTORE) pStore; pCrlSuffix = ToCrlContextSuffix(pEle); pCrlSuffix->ppSortedEntry = NULL; CommonReturn: return pEle; ErrorReturn: if (pEle) { PkiFree(pEle); pEle = NULL; } PkiFree(pInfo); goto CommonReturn; } STATIC void FreeCrlElement(IN PCONTEXT_ELEMENT pEle) { PCCRL_CONTEXT pCrl = ToCrlContext(pEle); PCRL_CONTEXT_SUFFIX pCrlSuffix = ToCrlContextSuffix(pEle); if (pEle->pShareEle) ReleaseShareElement(pEle->pShareEle); else { PkiFree(pCrl->pbCrlEncoded); PkiFree(pCrl->pCrlInfo); CertPerfDecrementCrlElementCurrentCount(); } PkiFree(pCrlSuffix->ppSortedEntry); PkiFree(pEle); } STATIC BOOL IsSameEncodedCrlExtension( IN LPCSTR pszObjId, IN PCCRL_CONTEXT pCrl, IN PCCRL_CONTEXT pNew ) { PCERT_EXTENSION pCrlExt; PCERT_EXTENSION pNewExt; // If they exist, compare the encoded extensions. pNewExt = CertFindExtension( pszObjId, pNew->pCrlInfo->cExtension, pNew->pCrlInfo->rgExtension ); pCrlExt = CertFindExtension( pszObjId, pCrl->pCrlInfo->cExtension, pCrl->pCrlInfo->rgExtension ); if (pNewExt) { if (pCrlExt) { DWORD dwCertEncodingType = pCrl->dwCertEncodingType; DWORD cbNewExt = pNewExt->Value.cbData; BYTE *pbNewExt = pNewExt->Value.pbData; DWORD cbCrlExt = pCrlExt->Value.cbData; BYTE *pbCrlExt = pCrlExt->Value.pbData; // Before comparing, adjust lengths to only include the // encoded bytes cbNewExt = AdjustEncodedLength(dwCertEncodingType, pbNewExt, cbNewExt); cbCrlExt = AdjustEncodedLength(dwCertEncodingType, pbCrlExt, cbCrlExt); if (cbNewExt != cbCrlExt || 0 != memcmp(pbNewExt, pbCrlExt, cbNewExt)) return FALSE; } else // Only one has the extension return FALSE; } else if (pCrlExt) // Only one has the extension return FALSE; // else // Neither has the extension return TRUE; } STATIC BOOL IsSameCrl( IN PCCRL_CONTEXT pCrl, IN PCCRL_CONTEXT pNew ) { DWORD dwCertEncodingType; PCERT_EXTENSION pCrlDeltaExt; PCERT_EXTENSION pNewDeltaExt; // Check: encoding type and issuer name dwCertEncodingType = pNew->dwCertEncodingType; if (dwCertEncodingType != pCrl->dwCertEncodingType || !CertCompareCertificateName( dwCertEncodingType, &pCrl->pCrlInfo->Issuer, &pNew->pCrlInfo->Issuer)) return FALSE; // Check that both are either base or delta CRLs pNewDeltaExt = CertFindExtension( szOID_DELTA_CRL_INDICATOR, pNew->pCrlInfo->cExtension, pNew->pCrlInfo->rgExtension ); pCrlDeltaExt = CertFindExtension( szOID_DELTA_CRL_INDICATOR, pCrl->pCrlInfo->cExtension, pCrl->pCrlInfo->rgExtension ); if (pNewDeltaExt) { if (NULL == pCrlDeltaExt) // Only one has a Delta extension return FALSE; // else // Both have a Delta extension } else if (pCrlDeltaExt) // Only one has a Delta extension return FALSE; // else // Neither has a Delta extension // If they exist, compare encoded AuthorityKeyIdentifier and // IssuingDistributionPoint extensions. if (!IsSameEncodedCrlExtension( szOID_AUTHORITY_KEY_IDENTIFIER2, pCrl, pNew )) return FALSE; if (!IsSameEncodedCrlExtension( szOID_ISSUING_DIST_POINT, pCrl, pNew )) return FALSE; return TRUE; } STATIC BOOL IsIssuedByCrl( IN PCCRL_CONTEXT pCrl, IN PCCERT_CONTEXT pIssuer, IN PCERT_NAME_BLOB pIssuerName, IN DWORD dwFindFlags ) { DWORD dwCertEncodingType; if (dwFindFlags & (CRL_FIND_ISSUED_BY_DELTA_FLAG | CRL_FIND_ISSUED_BY_BASE_FLAG)) { PCERT_EXTENSION pDeltaExt; pDeltaExt = CertFindExtension( szOID_DELTA_CRL_INDICATOR, pCrl->pCrlInfo->cExtension, pCrl->pCrlInfo->rgExtension ); if (pDeltaExt) { if (0 == (dwFindFlags & CRL_FIND_ISSUED_BY_DELTA_FLAG)) return FALSE; } else { if (0 == (dwFindFlags & CRL_FIND_ISSUED_BY_BASE_FLAG)) return FALSE; } } if (NULL == pIssuer) return TRUE; dwCertEncodingType = pIssuer->dwCertEncodingType; if (dwCertEncodingType != pCrl->dwCertEncodingType || !CertCompareCertificateName( dwCertEncodingType, &pCrl->pCrlInfo->Issuer, pIssuerName)) return FALSE; if (dwFindFlags & CRL_FIND_ISSUED_BY_AKI_FLAG) { PCERT_EXTENSION pCrlAKIExt; pCrlAKIExt = CertFindExtension( szOID_AUTHORITY_KEY_IDENTIFIER2, pCrl->pCrlInfo->cExtension, pCrl->pCrlInfo->rgExtension ); if (pCrlAKIExt) { PCERT_AUTHORITY_KEY_ID2_INFO pInfo; BOOL fResult; if (NULL == (pInfo = (PCERT_AUTHORITY_KEY_ID2_INFO) AllocAndDecodeObject( dwCertEncodingType, X509_AUTHORITY_KEY_ID2, pCrlAKIExt->Value.pbData, pCrlAKIExt->Value.cbData ))) return FALSE; if (pInfo->KeyId.cbData) fResult = CompareCertHash( pIssuer, CERT_KEY_IDENTIFIER_PROP_ID, &pInfo->KeyId ); else fResult = TRUE; PkiFree(pInfo); if (!fResult) return FALSE; } } if (dwFindFlags & CRL_FIND_ISSUED_BY_SIGNATURE_FLAG) { DWORD dwFlags; dwFlags = CERT_STORE_SIGNATURE_FLAG; VerifyCrl(pCrl, pIssuer, &dwFlags); if (0 != dwFlags) return FALSE; } return TRUE; } STATIC BOOL CompareCrlElement( IN PCONTEXT_ELEMENT pEle, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN BOOL fArchived ) { PCCRL_CONTEXT pCrl = ToCrlContext(pEle); DWORD dwFindType = pFindInfo->dwFindType; const void *pvFindPara = pFindInfo->pvFindPara; if (fArchived) return FALSE; switch (dwFindType) { case CRL_FIND_ANY: return TRUE; break; case CRL_FIND_ISSUED_BY: { PCCERT_CONTEXT pIssuer = (PCCERT_CONTEXT) pvFindPara; return IsIssuedByCrl( pCrl, pIssuer, (NULL != pIssuer) ? &pIssuer->pCertInfo->Subject : NULL, pFindInfo->dwFindFlags ); } break; case CRL_FIND_ISSUED_FOR: { PCRL_FIND_ISSUED_FOR_PARA pPara = (PCRL_FIND_ISSUED_FOR_PARA) pvFindPara; return IsIssuedByCrl( pCrl, pPara->pIssuerCert, &pPara->pSubjectCert->pCertInfo->Issuer, pFindInfo->dwFindFlags ); } break; case CRL_FIND_EXISTING: return IsSameCrl(pCrl, (PCCRL_CONTEXT) pvFindPara); break; default: goto BadParameter; } BadParameter: SetLastError((DWORD) E_INVALIDARG); return FALSE; } STATIC BOOL IsNewerCrlElement( IN PCONTEXT_ELEMENT pNewEle, IN PCONTEXT_ELEMENT pExistingEle ) { PCCRL_CONTEXT pNewCrl = ToCrlContext(pNewEle); PCCRL_CONTEXT pExistingCrl = ToCrlContext(pExistingEle); // CompareFileTime returns +1 if first time > second time return (0 < CompareFileTime( &pNewCrl->pCrlInfo->ThisUpdate, &pExistingCrl->pCrlInfo->ThisUpdate )); } STATIC BOOL IsSameAltNameEntry( IN PCERT_ALT_NAME_ENTRY pE1, IN PCERT_ALT_NAME_ENTRY pE2 ) { DWORD dwAltNameChoice; dwAltNameChoice = pE1->dwAltNameChoice; if (dwAltNameChoice != pE2->dwAltNameChoice) return FALSE; switch (dwAltNameChoice) { case CERT_ALT_NAME_OTHER_NAME: if (0 == strcmp(pE1->pOtherName->pszObjId, pE2->pOtherName->pszObjId) && pE1->pOtherName->Value.cbData == pE2->pOtherName->Value.cbData && 0 == memcmp(pE1->pOtherName->Value.pbData, pE2->pOtherName->Value.pbData, pE1->pOtherName->Value.cbData)) return TRUE; break; case CERT_ALT_NAME_RFC822_NAME: case CERT_ALT_NAME_DNS_NAME: case CERT_ALT_NAME_URL: if (0 == _wcsicmp(pE1->pwszRfc822Name, pE2->pwszRfc822Name)) return TRUE; break; case CERT_ALT_NAME_X400_ADDRESS: case CERT_ALT_NAME_EDI_PARTY_NAME: // Not implemented break; case CERT_ALT_NAME_DIRECTORY_NAME: case CERT_ALT_NAME_IP_ADDRESS: if (pE1->DirectoryName.cbData == pE2->DirectoryName.cbData && 0 == memcmp(pE1->DirectoryName.pbData, pE2->DirectoryName.pbData, pE1->DirectoryName.cbData)) return TRUE; break; case CERT_ALT_NAME_REGISTERED_ID: if (0 == strcmp(pE1->pszRegisteredID, pE2->pszRegisteredID)) return TRUE; break; default: break; } return FALSE; } //+------------------------------------------------------------------------- // Is the specified CRL valid for the certificate. // // Returns TRUE if the CRL's list of entries would contain the certificate // if it was revoked. Note, doesn't check that the certificate is in the // list of entries. // // If the CRL has an Issuing Distribution Point (IDP) extension, checks // that it's valid for the subject certificate. // // dwFlags and pvReserved currently aren't used and must be set to 0 and NULL. //-------------------------------------------------------------------------- WINCRYPT32API BOOL WINAPI CertIsValidCRLForCertificate( IN PCCERT_CONTEXT pCert, IN PCCRL_CONTEXT pCrl, IN DWORD dwFlags, IN void *pvReserved ) { BOOL fResult; PCERT_EXTENSION pIDPExt; // not allocated PCERT_EXTENSION pCDPExt; // not allocated PCERT_EXTENSION pBasicConstraintsExt; // not allocated PCRL_ISSUING_DIST_POINT pIDPInfo = NULL; PCRL_DIST_POINTS_INFO pCDPInfo = NULL; PCERT_BASIC_CONSTRAINTS2_INFO pBasicConstraintsInfo = NULL; DWORD cIDPAltEntry; PCERT_ALT_NAME_ENTRY pIDPAltEntry; // not allocated pIDPExt = CertFindExtension( szOID_ISSUING_DIST_POINT, pCrl->pCrlInfo->cExtension, pCrl->pCrlInfo->rgExtension ); if (NULL == pIDPExt) return TRUE; if (NULL == (pIDPInfo = (PCRL_ISSUING_DIST_POINT) AllocAndDecodeObject( pCrl->dwCertEncodingType, X509_ISSUING_DIST_POINT, pIDPExt->Value.pbData, pIDPExt->Value.cbData ))) goto IDPDecodeError; // Ignore IDPs having OnlySomeReasonFlags or an indirect CRL if (0 != pIDPInfo->OnlySomeReasonFlags.cbData || pIDPInfo->fIndirectCRL) goto UnsupportedIDPError; if (!(CRL_DIST_POINT_NO_NAME == pIDPInfo->DistPointName.dwDistPointNameChoice || CRL_DIST_POINT_FULL_NAME == pIDPInfo->DistPointName.dwDistPointNameChoice)) goto UnsupportedIDPError; if (pIDPInfo->fOnlyContainsUserCerts || pIDPInfo->fOnlyContainsCACerts) { // Determine if the cert is an end entity or a CA. // Default to an end entity BOOL fCA = FALSE; pBasicConstraintsExt = CertFindExtension( szOID_BASIC_CONSTRAINTS2, pCert->pCertInfo->cExtension, pCert->pCertInfo->rgExtension ); if (pBasicConstraintsExt) { if (NULL == (pBasicConstraintsInfo = (PCERT_BASIC_CONSTRAINTS2_INFO) AllocAndDecodeObject( pCert->dwCertEncodingType, X509_BASIC_CONSTRAINTS2, pBasicConstraintsExt->Value.pbData, pBasicConstraintsExt->Value.cbData ))) goto BasicConstraintsDecodeError; fCA = pBasicConstraintsInfo->fCA; } if (pIDPInfo->fOnlyContainsUserCerts && fCA) goto OnlyContainsUserCertsError; if (pIDPInfo->fOnlyContainsCACerts && !fCA) goto OnlyContainsCACertsError; } if (CRL_DIST_POINT_FULL_NAME != pIDPInfo->DistPointName.dwDistPointNameChoice) // The IDP doesn't have any name choice to check goto SuccessReturn; cIDPAltEntry = pIDPInfo->DistPointName.FullName.cAltEntry; if (0 == cIDPAltEntry) // The IDP doesn't have any DistPoint entries to check goto SuccessReturn; pIDPAltEntry = pIDPInfo->DistPointName.FullName.rgAltEntry; pCDPExt = CertFindExtension( szOID_CRL_DIST_POINTS, pCert->pCertInfo->cExtension, pCert->pCertInfo->rgExtension ); if (NULL == pCDPExt) goto NoCDPError; if (NULL == (pCDPInfo = (PCRL_DIST_POINTS_INFO) AllocAndDecodeObject( pCert->dwCertEncodingType, X509_CRL_DIST_POINTS, pCDPExt->Value.pbData, pCDPExt->Value.cbData ))) goto CDPDecodeError; for ( ; 0 < cIDPAltEntry; pIDPAltEntry++, cIDPAltEntry--) { DWORD cCDPDistPoint; PCRL_DIST_POINT pCDPDistPoint; cCDPDistPoint = pCDPInfo->cDistPoint; pCDPDistPoint = pCDPInfo->rgDistPoint; for ( ; 0 < cCDPDistPoint; pCDPDistPoint++, cCDPDistPoint--) { DWORD cCDPAltEntry; PCERT_ALT_NAME_ENTRY pCDPAltEntry; if (0 != pCDPDistPoint->ReasonFlags.cbData) continue; if (0 != pCDPDistPoint->CRLIssuer.cAltEntry) continue; if (CRL_DIST_POINT_FULL_NAME != pCDPDistPoint->DistPointName.dwDistPointNameChoice) continue; cCDPAltEntry = pCDPDistPoint->DistPointName.FullName.cAltEntry; pCDPAltEntry = pCDPDistPoint->DistPointName.FullName.rgAltEntry; for ( ; 0 < cCDPAltEntry; pCDPAltEntry++, cCDPAltEntry--) { if (IsSameAltNameEntry(pIDPAltEntry, pCDPAltEntry)) goto SuccessReturn; } } } goto NoAltNameMatchError; SuccessReturn: fResult = TRUE; CommonReturn: PkiFree(pIDPInfo); PkiFree(pCDPInfo); PkiFree(pBasicConstraintsInfo); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(NoCDPError, CRYPT_E_NO_MATCH) TRACE_ERROR(IDPDecodeError) SET_ERROR(UnsupportedIDPError, E_NOTIMPL) TRACE_ERROR(BasicConstraintsDecodeError) SET_ERROR(OnlyContainsUserCertsError, CRYPT_E_NO_MATCH) SET_ERROR(OnlyContainsCACertsError, CRYPT_E_NO_MATCH) TRACE_ERROR(CDPDecodeError) SET_ERROR(NoAltNameMatchError, CRYPT_E_NO_MATCH) } //+========================================================================= // CTL_CONTEXT Functions //========================================================================== //+------------------------------------------------------------------------- // If both msg and cert encoding types are present, or neither are present, // return without any changes. Otherwise, set the missing encoding type // with the encoding type that is present. //-------------------------------------------------------------------------- STATIC DWORD GetCtlEncodingType(IN DWORD dwMsgAndCertEncodingType) { if (0 == dwMsgAndCertEncodingType) return 0; else if (0 == (dwMsgAndCertEncodingType & CMSG_ENCODING_TYPE_MASK)) return dwMsgAndCertEncodingType | ((dwMsgAndCertEncodingType << 16) & CMSG_ENCODING_TYPE_MASK); else if (0 == (dwMsgAndCertEncodingType & CERT_ENCODING_TYPE_MASK)) return dwMsgAndCertEncodingType | ((dwMsgAndCertEncodingType >> 16) & CERT_ENCODING_TYPE_MASK); else // Both specified return dwMsgAndCertEncodingType; } #if 0 // pbCtlEncoded has already been allocated STATIC PCONTEXT_ELEMENT SlowCreateCtlElement( IN PCERT_STORE pStore, IN DWORD dwMsgAndCertEncodingType, IN BYTE *pbCtlEncoded, IN DWORD cbCtlEncoded ) { DWORD dwEncodingType; HCRYPTPROV hProv = 0; DWORD dwProvFlags = 0; HCRYPTMSG hMsg = NULL; BYTE *pbContent = NULL; DWORD cbContent; PCTL_INFO pInfo = NULL; PCONTEXT_ELEMENT pEle = NULL; PCTL_CONTEXT pCtl; PCTL_CONTEXT_SUFFIX pCtlSuffix; // Attempt to get the store's crypt provider. Serialize crypto // operations. hProv = GetCryptProv(pStore, &dwProvFlags); if (0 == (dwMsgAndCertEncodingType = GetCtlEncodingType( dwMsgAndCertEncodingType))) goto InvalidArg; // The message encoding type takes precedence dwEncodingType = (dwMsgAndCertEncodingType >> 16) & CERT_ENCODING_TYPE_MASK; // Assumption:: only definite length encoded PKCS #7 cbCtlEncoded = AdjustEncodedLength( dwEncodingType, pbCtlEncoded, cbCtlEncoded); // First decode as a PKCS#7 SignedData message if (NULL == (hMsg = CryptMsgOpenToDecode( dwMsgAndCertEncodingType, 0, // dwFlags 0, // dwMsgType hProv, NULL, // pRecipientInfo NULL // pStreamInfo ))) goto MsgOpenToDecodeError; if (!CryptMsgUpdate( hMsg, pbCtlEncoded, cbCtlEncoded, TRUE // fFinal )) goto MsgUpdateError; else { // Verify that the outer ContentType is SignedData and the inner // ContentType is a CertificateTrustList DWORD dwMsgType = 0; DWORD cbData; char szInnerContentType[64]; assert(sizeof(szInnerContentType) > strlen(szOID_CTL)); cbData = sizeof(dwMsgType); if (!CryptMsgGetParam( hMsg, CMSG_TYPE_PARAM, 0, // dwIndex &dwMsgType, &cbData )) goto GetTypeError; if (CMSG_SIGNED != dwMsgType) goto UnexpectedMsgTypeError; cbData = sizeof(szInnerContentType); if (!CryptMsgGetParam( hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, // dwIndex szInnerContentType, &cbData )) goto GetInnerContentTypeError; if (0 != strcmp(szInnerContentType, szOID_CTL)) goto UnexpectedInnerContentTypeError; } // Get the inner content. if (NULL == (pbContent = (BYTE *) AllocAndGetMsgParam( hMsg, CMSG_CONTENT_PARAM, 0, // dwIndex &cbContent))) goto GetContentError; // Decode inner content if (NULL == (pInfo = (PCTL_INFO) AllocAndDecodeObject( dwEncodingType, PKCS_CTL, pbContent, cbContent))) goto DecodeError; // Allocate and initialize the CTL element structure if (NULL == (pEle = (PCONTEXT_ELEMENT) PkiZeroAlloc( sizeof(CONTEXT_ELEMENT) + sizeof(CTL_CONTEXT) + sizeof(CTL_CONTEXT_SUFFIX)))) goto OutOfMemory; pEle->dwElementType = ELEMENT_TYPE_CACHE; pEle->dwContextType = CERT_STORE_CTL_CONTEXT - 1; pEle->lRefCnt = 1; pEle->pEle = pEle; pEle->pStore = pStore; pEle->pProvStore = pStore; pCtl = (PCTL_CONTEXT) ToCtlContext(pEle); pCtl->dwMsgAndCertEncodingType = dwMsgAndCertEncodingType; pCtl->pbCtlEncoded = pbCtlEncoded; pCtl->cbCtlEncoded = cbCtlEncoded; pCtl->pCtlInfo = pInfo; pCtl->hCertStore = (HCERTSTORE) pStore; pCtl->hCryptMsg = hMsg; pCtl->pbCtlContent = pbContent; pCtl->cbCtlContent = cbContent; pCtlSuffix = ToCtlContextSuffix(pEle); pCtlSuffix->ppSortedEntry = NULL; pCtlSuffix->pSortedCtlFindInfo = NULL; CommonReturn: // For the store's crypt provider, release reference count. Leave // crypto operations critical section. // // Also, any subsequent CryptMsg cryptographic operations will // be done outside of critical section. This critical section is needed // because CAPI 1.0 isn't thread safe. This should be fixed!!!! ????? ReleaseCryptProv(pStore, dwProvFlags); return pEle; ErrorReturn: if (hMsg) CryptMsgClose(hMsg); PkiFree(pInfo); PkiFree(pbContent); if (pEle) { PkiFree(pEle); pEle = NULL; } goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(MsgOpenToDecodeError) TRACE_ERROR(MsgUpdateError) TRACE_ERROR(GetTypeError) SET_ERROR(UnexpectedMsgTypeError, CRYPT_E_UNEXPECTED_MSG_TYPE) TRACE_ERROR(GetInnerContentTypeError) SET_ERROR(UnexpectedInnerContentTypeError, CRYPT_E_UNEXPECTED_MSG_TYPE) TRACE_ERROR(GetContentError) TRACE_ERROR(OutOfMemory) TRACE_ERROR(DecodeError) } void StoreMessageBox( IN LPSTR pszText ) { MessageBoxA( NULL, // hwndOwner pszText, "Check FastCreateCtlElement", MB_TOPMOST | MB_OK | MB_ICONQUESTION | MB_SERVICE_NOTIFICATION ); } #endif // pbCtlEncoded has already been allocated STATIC PCONTEXT_ELEMENT FastCreateCtlElement( IN PCERT_STORE pStore, IN DWORD dwMsgAndCertEncodingType, IN const BYTE *pbCtlEncoded, IN DWORD cbCtlEncoded, IN OPTIONAL PSHARE_ELEMENT pShareEle, IN DWORD dwFlags ); // pbCtlEncoded has already been allocated STATIC PCONTEXT_ELEMENT CreateCtlElement( IN PCERT_STORE pStore, IN DWORD dwMsgAndCertEncodingType, IN BYTE *pbCtlEncoded, IN DWORD cbCtlEncoded, IN OPTIONAL PSHARE_ELEMENT pShareEle ) { #if 1 return FastCreateCtlElement( pStore, dwMsgAndCertEncodingType, pbCtlEncoded, cbCtlEncoded, pShareEle, 0 // dwFlags ); #else PCONTEXT_ELEMENT pSlowEle; PCONTEXT_ELEMENT pFastEle; pFastEle = FastCreateCtlElement( pStore, dwMsgAndCertEncodingType, pbCtlEncoded, cbCtlEncoded, pShareEle, 0 // dwFlags ); pSlowEle = NULL; if (cbCtlEncoded) { BYTE *pbSlowCtlEncoded = NULL; pbSlowCtlEncoded = (BYTE *) PkiNonzeroAlloc(cbCtlEncoded); if (pbSlowCtlEncoded) { memcpy(pbSlowCtlEncoded, pbCtlEncoded, cbCtlEncoded); pSlowEle = SlowCreateCtlElement( &NullCertStore, dwMsgAndCertEncodingType, pbSlowCtlEncoded, cbCtlEncoded ); if (NULL == pSlowEle) PkiFree(pbSlowCtlEncoded); } } if (NULL == pFastEle) { if (pSlowEle) StoreMessageBox("fast failed, slow succeeded"); } else if (NULL == pSlowEle) { StoreMessageBox("fast succeeded, slow failed"); } else { PCTL_INFO pFastInfo = ToCtlContext(pFastEle)->pCtlInfo; PCTL_INFO pSlowInfo = ToCtlContext(pSlowEle)->pCtlInfo; // Check that headers match if (pFastInfo->dwVersion != pSlowInfo->dwVersion || pFastInfo->SubjectUsage.cUsageIdentifier != pSlowInfo->SubjectUsage.cUsageIdentifier || 0 != CompareFileTime(&pFastInfo->ThisUpdate, &pSlowInfo->ThisUpdate) || 0 != CompareFileTime(&pFastInfo->NextUpdate, &pSlowInfo->NextUpdate) || 0 != strcmp(pFastInfo->SubjectAlgorithm.pszObjId, pSlowInfo->SubjectAlgorithm.pszObjId) || pFastInfo->SubjectAlgorithm.Parameters.cbData != pSlowInfo->SubjectAlgorithm.Parameters.cbData) StoreMessageBox("fast and slow info doesn't match\n"); else { // Check that the extensions match DWORD cFastExt = pFastInfo->cExtension; PCERT_EXTENSION pFastExt = pFastInfo->rgExtension; DWORD cSlowExt = pSlowInfo->cExtension; PCERT_EXTENSION pSlowExt = pSlowInfo->rgExtension; if (cFastExt != cSlowExt) StoreMessageBox("fast and slow extension count doesn't match"); else { for ( ; cFastExt; cFastExt--, pFastExt++, pSlowExt++) { if (0 != strcmp(pFastExt->pszObjId, pSlowExt->pszObjId) || pFastExt->fCritical != pSlowExt->fCritical || pFastExt->Value.cbData != pSlowExt->Value.cbData) { StoreMessageBox( "fast and slow extension doesn't match"); goto Done; } if (pFastExt->Value.cbData && 0 != memcmp( pFastExt->Value.pbData, pSlowExt->Value.pbData, pFastExt->Value.cbData)) { StoreMessageBox( "fast and slow extension doesn't match"); goto Done; } } } } if (pFastInfo->cCTLEntry != pSlowInfo->cCTLEntry) StoreMessageBox("fast and slow entry count doesn't match"); else { DWORD cEntry = pFastInfo->cCTLEntry; PCTL_ENTRY pFastEntry = pFastInfo->rgCTLEntry; PCTL_ENTRY pSlowEntry = pSlowInfo->rgCTLEntry; for ( ; cEntry; cEntry--, pFastEntry++, pSlowEntry++) { if (pFastEntry->SubjectIdentifier.cbData != pSlowEntry->SubjectIdentifier.cbData || 0 != memcmp(pFastEntry->SubjectIdentifier.pbData, pSlowEntry->SubjectIdentifier.pbData, pFastEntry->SubjectIdentifier.cbData)) { StoreMessageBox( "fast and slow SubjectIdentifier doesn't match"); goto Done; } if (pFastEntry->cAttribute != pSlowEntry->cAttribute) { StoreMessageBox( "fast and slow Attribute Count doesn't match"); goto Done; } else if (0 < pFastEntry->cAttribute) { DWORD cAttr = pFastEntry->cAttribute; PCRYPT_ATTRIBUTE pFastAttr = pFastEntry->rgAttribute; PCRYPT_ATTRIBUTE pSlowAttr = pSlowEntry->rgAttribute; for ( ; cAttr; cAttr--, pFastAttr++, pSlowAttr++) { if (0 != strcmp(pFastAttr->pszObjId, pSlowAttr->pszObjId)) { StoreMessageBox( "fast and slow Attribute OID doesn't match"); goto Done; } if (pFastAttr->cValue != pSlowAttr->cValue) { StoreMessageBox( "fast and slow Value Count doesn't match"); goto Done; } if (0 < pFastAttr->cValue) { DWORD cValue = pFastAttr->cValue; PCRYPT_ATTR_BLOB pFastValue = pFastAttr->rgValue; PCRYPT_ATTR_BLOB pSlowValue = pSlowAttr->rgValue; for ( ; cValue; cValue--, pFastValue++, pSlowValue++) { if (pFastValue->cbData != pSlowValue->cbData || 0 != memcmp(pFastValue->pbData, pSlowValue->pbData, pFastValue->cbData)) { StoreMessageBox( "fast and slow Value doesn't match"); goto Done; } } } } } } } } Done: if (pSlowEle) FreeContextElement(pSlowEle); return pFastEle; #endif } STATIC void FreeCtlElement(IN PCONTEXT_ELEMENT pEle) { PCCTL_CONTEXT pCtl = ToCtlContext(pEle); PCTL_CONTEXT_SUFFIX pCtlSuffix = ToCtlContextSuffix(pEle); if (pEle->pShareEle) ReleaseShareElement(pEle->pShareEle); else { PkiFree(pCtl->pbCtlEncoded); CertPerfDecrementCtlElementCurrentCount(); } PkiFree(pCtl->pCtlInfo); PkiFree(pCtlSuffix->ppSortedEntry); CryptMsgClose(pCtl->hCryptMsg); if (pCtlSuffix->fFastCreate) { PSORTED_CTL_FIND_INFO pSortedCtlFindInfo = pCtlSuffix->pSortedCtlFindInfo; PkiFree(pCtlSuffix->pCTLEntry); PkiFree(pCtlSuffix->pExtInfo); if (pSortedCtlFindInfo) { PkiFree(pSortedCtlFindInfo->pdwHashBucketHead); PkiFree(pSortedCtlFindInfo->pHashBucketEntry); } } else PkiFree(pCtl->pbCtlContent); PkiFree(pEle); } STATIC BOOL CompareCtlHash( IN PCCTL_CONTEXT pCtl, IN DWORD dwPropId, IN PCRYPT_HASH_BLOB pHash ) { BYTE rgbHash[MAX_HASH_LEN]; DWORD cbHash = MAX_HASH_LEN; CertGetCTLContextProperty( pCtl, dwPropId, rgbHash, &cbHash ); if (cbHash == pHash->cbData && memcmp(rgbHash, pHash->pbData, cbHash) == 0) return TRUE; else return FALSE; } STATIC BOOL CompareCtlUsage( IN PCCTL_CONTEXT pCtl, IN DWORD dwMsgAndCertEncodingType, IN DWORD dwFindFlags, IN PCTL_FIND_USAGE_PARA pPara ) { PCTL_INFO pInfo = pCtl->pCtlInfo; if (NULL == pPara || pPara->cbSize < (offsetof(CTL_FIND_USAGE_PARA, SubjectUsage) + sizeof(pPara->SubjectUsage))) return TRUE; if ((CTL_FIND_SAME_USAGE_FLAG & dwFindFlags) && pPara->SubjectUsage.cUsageIdentifier != pInfo->SubjectUsage.cUsageIdentifier) return FALSE; if (!CompareCtlUsageIdentifiers(&pPara->SubjectUsage, 1, &pInfo->SubjectUsage, FALSE)) return FALSE; assert(offsetof(CTL_FIND_USAGE_PARA, ListIdentifier) > offsetof(CTL_FIND_USAGE_PARA, SubjectUsage)); if (pPara->cbSize < offsetof(CTL_FIND_USAGE_PARA, ListIdentifier) + sizeof(pPara->ListIdentifier)) return TRUE; if (pPara->ListIdentifier.cbData) { DWORD cb = pPara->ListIdentifier.cbData; if (CTL_FIND_NO_LIST_ID_CBDATA == cb) cb = 0; if (cb != pInfo->ListIdentifier.cbData) return FALSE; if (0 != cb && 0 != memcmp(pPara->ListIdentifier.pbData, pInfo->ListIdentifier.pbData, cb)) return FALSE; } assert(offsetof(CTL_FIND_USAGE_PARA, pSigner) > offsetof(CTL_FIND_USAGE_PARA, ListIdentifier)); if (pPara->cbSize < offsetof(CTL_FIND_USAGE_PARA, pSigner) + sizeof(pPara->pSigner)) return TRUE; if (CTL_FIND_NO_SIGNER_PTR == pPara->pSigner) { DWORD cbData; DWORD dwSignerCount; cbData = sizeof(dwSignerCount); if (!CryptMsgGetParam( pCtl->hCryptMsg, CMSG_SIGNER_COUNT_PARAM, 0, // dwIndex &dwSignerCount, &cbData) || 0 != dwSignerCount) return FALSE; } else if (pPara->pSigner) { DWORD dwCertEncodingType; PCERT_INFO pCertId1 = pPara->pSigner; HCRYPTMSG hMsg = pCtl->hCryptMsg; DWORD cbData; DWORD dwSignerCount; DWORD i; dwCertEncodingType = GetCertEncodingType(dwMsgAndCertEncodingType); if (dwCertEncodingType != GET_CERT_ENCODING_TYPE( pCtl->dwMsgAndCertEncodingType)) return FALSE; cbData = sizeof(dwSignerCount); if (!CryptMsgGetParam( hMsg, CMSG_SIGNER_COUNT_PARAM, 0, // dwIndex &dwSignerCount, &cbData) || 0 == dwSignerCount) return FALSE; for (i = 0; i < dwSignerCount; i++) { BOOL fResult; PCERT_INFO pCertId2; if (NULL == (pCertId2 = (PCERT_INFO) AllocAndGetMsgParam( hMsg, CMSG_SIGNER_CERT_INFO_PARAM, i, &cbData))) continue; fResult = CertCompareCertificate( dwCertEncodingType, pCertId1, pCertId2); PkiFree(pCertId2); if (fResult) break; } if (i == dwSignerCount) return FALSE; } return TRUE; } STATIC BOOL CompareCtlSubject( IN PCCTL_CONTEXT pCtl, IN DWORD dwMsgAndCertEncodingType, IN DWORD dwFindFlags, IN PCTL_FIND_SUBJECT_PARA pPara ) { if (NULL == pPara || pPara->cbSize < (offsetof(CTL_FIND_SUBJECT_PARA, pUsagePara) + sizeof(pPara->pUsagePara))) return TRUE; if (pPara->pUsagePara && !CompareCtlUsage(pCtl, dwMsgAndCertEncodingType, dwFindFlags, pPara->pUsagePara)) return FALSE; assert(offsetof(CTL_FIND_SUBJECT_PARA, pvSubject) > offsetof(CTL_FIND_SUBJECT_PARA, pUsagePara)); if (pPara->cbSize < offsetof(CTL_FIND_SUBJECT_PARA, pvSubject) + sizeof(pPara->pvSubject)) return TRUE; if (pPara->pvSubject && NULL == CertFindSubjectInCTL( dwMsgAndCertEncodingType, pPara->dwSubjectType, pPara->pvSubject, pCtl, 0)) // dwFlags return FALSE; return TRUE; } STATIC BOOL IsSameCtl( IN PCCTL_CONTEXT pCtl, IN PCCTL_CONTEXT pNew ) { PCTL_INFO pInfo = pNew->pCtlInfo; HCRYPTMSG hMsg = pNew->hCryptMsg; CTL_FIND_USAGE_PARA FindPara; DWORD dwFindFlags; DWORD cbData; DWORD dwSignerCount; DWORD i; cbData = sizeof(dwSignerCount); if (!CryptMsgGetParam( hMsg, CMSG_SIGNER_COUNT_PARAM, 0, // dwIndex &dwSignerCount, &cbData)) return FALSE; memset(&FindPara, 0, sizeof(FindPara)); FindPara.cbSize = sizeof(FindPara); FindPara.SubjectUsage = pInfo->SubjectUsage; FindPara.ListIdentifier = pInfo->ListIdentifier; if (0 == FindPara.ListIdentifier.cbData) FindPara.ListIdentifier.cbData = CTL_FIND_NO_LIST_ID_CBDATA; dwFindFlags = CTL_FIND_SAME_USAGE_FLAG; if (0 == dwSignerCount) { FindPara.pSigner = CTL_FIND_NO_SIGNER_PTR; return CompareCtlUsage( pCtl, pNew->dwMsgAndCertEncodingType, dwFindFlags, &FindPara ); } else { for (i = 0; i < dwSignerCount; i++) { BOOL fResult; PCERT_INFO pSigner; if (NULL == (pSigner = (PCERT_INFO) AllocAndGetMsgParam( hMsg, CMSG_SIGNER_CERT_INFO_PARAM, i, &cbData))) continue; FindPara.pSigner = pSigner; fResult = CompareCtlUsage( pCtl, pNew->dwMsgAndCertEncodingType, dwFindFlags, &FindPara ); PkiFree(pSigner); if (fResult) return TRUE; } } return FALSE; } STATIC BOOL CompareCtlElement( IN PCONTEXT_ELEMENT pEle, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN BOOL fArchived ) { PCCTL_CONTEXT pCtl = ToCtlContext(pEle); DWORD dwFindType = pFindInfo->dwFindType; const void *pvFindPara = pFindInfo->pvFindPara; if (fArchived) { switch (dwFindType) { case CTL_FIND_SHA1_HASH: case CTL_FIND_MD5_HASH: break; default: return FALSE; } } switch (dwFindType) { case CTL_FIND_ANY: return TRUE; break; case CTL_FIND_SHA1_HASH: case CTL_FIND_MD5_HASH: { DWORD dwPropId; if (dwFindType == CTL_FIND_SHA1_HASH) dwPropId = CERT_SHA1_HASH_PROP_ID; else dwPropId = CERT_MD5_HASH_PROP_ID; return CompareCtlHash(pCtl, dwPropId, (PCRYPT_HASH_BLOB) pvFindPara); } break; case CTL_FIND_USAGE: return CompareCtlUsage(pCtl, pFindInfo->dwMsgAndCertEncodingType, pFindInfo->dwFindFlags, (PCTL_FIND_USAGE_PARA) pvFindPara); break; case CTL_FIND_SUBJECT: return CompareCtlSubject(pCtl, pFindInfo->dwMsgAndCertEncodingType, pFindInfo->dwFindFlags, (PCTL_FIND_SUBJECT_PARA) pvFindPara); break; case CTL_FIND_EXISTING: { PCCTL_CONTEXT pNew = (PCCTL_CONTEXT) pFindInfo->pvFindPara; return IsSameCtl(pCtl, pNew); } break; default: goto BadParameter; } BadParameter: SetLastError((DWORD) E_INVALIDARG); return FALSE; } STATIC BOOL IsNewerCtlElement( IN PCONTEXT_ELEMENT pNewEle, IN PCONTEXT_ELEMENT pExistingEle ) { PCCTL_CONTEXT pNewCtl = ToCtlContext(pNewEle); PCCTL_CONTEXT pExistingCtl = ToCtlContext(pExistingEle); // CompareFileTime returns +1 if first time > second time return (0 < CompareFileTime( &pNewCtl->pCtlInfo->ThisUpdate, &pExistingCtl->pCtlInfo->ThisUpdate )); } //+========================================================================= // Store Link Functions //========================================================================== STATIC PCERT_STORE_LINK CreateStoreLink( IN PCERT_STORE pCollection, IN PCERT_STORE pSibling, IN DWORD dwUpdateFlags, IN DWORD dwPriority ) { PCERT_STORE_LINK pLink; if (NULL == (pLink = (PCERT_STORE_LINK) PkiZeroAlloc( sizeof(CERT_STORE_LINK)))) return NULL; pLink->lRefCnt = 1; pLink->dwUpdateFlags = dwUpdateFlags; pLink->dwPriority = dwPriority; pLink->pCollection = pCollection; pLink->pSibling = (PCERT_STORE) CertDuplicateStore((HCERTSTORE) pSibling); if (pSibling->dwFlags & CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG) InterlockedIncrement(&pSibling->lDeferCloseRefCnt); return pLink; } // Not locked upon entry STATIC void FreeStoreLink( IN PCERT_STORE_LINK pStoreLink ) { PCERT_STORE pSibling = pStoreLink->pSibling; if (pSibling->dwFlags & CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG) InterlockedDecrement(&pSibling->lDeferCloseRefCnt); CertCloseStore((HCERTSTORE) pSibling, 0); PkiFree(pStoreLink); } STATIC void RemoveStoreLink( IN PCERT_STORE_LINK pStoreLink ) { PCERT_STORE pCollection = pStoreLink->pCollection; LockStore(pCollection); // Remove store link from the store's collection if (pStoreLink->pNext) pStoreLink->pNext->pPrev = pStoreLink->pPrev; if (pStoreLink->pPrev) pStoreLink->pPrev->pNext = pStoreLink->pNext; else if (pStoreLink == pCollection->pStoreListHead) pCollection->pStoreListHead = pStoreLink->pNext; // else // Not on any list // Unlocks the store or deletes the store if this was the // last link in a closed store FreeStore(pCollection); } STATIC void RemoveAndFreeStoreLink( IN PCERT_STORE_LINK pStoreLink ) { RemoveStoreLink(pStoreLink); FreeStoreLink(pStoreLink); } STATIC void ReleaseStoreLink( IN PCERT_STORE_LINK pStoreLink ) { if (0 == InterlockedDecrement(&pStoreLink->lRefCnt)) { assert(pStoreLink->dwFlags & STORE_LINK_DELETED_FLAG); assert(pStoreLink->pSibling); RemoveAndFreeStoreLink(pStoreLink); } } //+========================================================================= // Context Element Functions //========================================================================== STATIC DWORD GetContextEncodingType( IN PCONTEXT_ELEMENT pEle ) { DWORD dwContextType = pEle->dwContextType; DWORD *pdwEncodingType; pdwEncodingType = (DWORD *) ((BYTE *) pEle + sizeof(CONTEXT_ELEMENT) + rgOffsetofEncodingType[dwContextType]); return *pdwEncodingType; } STATIC void GetContextEncodedInfo( IN PCONTEXT_ELEMENT pEle, OUT BYTE **ppbEncoded, OUT DWORD *pcbEncoded ) { DWORD dwContextType = pEle->dwContextType; BYTE **ppbSrcEncoded; DWORD *pcbSrcEncoded; ppbSrcEncoded = (BYTE **) ((BYTE *) pEle + sizeof(CONTEXT_ELEMENT) + rgOffsetofEncodedPointer[dwContextType]); *ppbEncoded = *ppbSrcEncoded; pcbSrcEncoded = (DWORD *) ((BYTE *) pEle + sizeof(CONTEXT_ELEMENT) + rgOffsetofEncodedCount[dwContextType]); *pcbEncoded = *pcbSrcEncoded; } STATIC PCONTEXT_ELEMENT GetCacheElement( IN PCONTEXT_ELEMENT pCacheEle ) { DWORD dwInnerDepth; // Skip past any links to get to the cache element dwInnerDepth = 0; for ( ; pCacheEle != pCacheEle->pEle; pCacheEle = pCacheEle->pEle) { dwInnerDepth++; assert(dwInnerDepth <= MAX_LINK_DEPTH); assert(ELEMENT_TYPE_CACHE != pCacheEle->dwElementType); if (dwInnerDepth > MAX_LINK_DEPTH) goto ExceededMaxLinkDepth; } assert(pCacheEle); assert(ELEMENT_TYPE_CACHE == pCacheEle->dwElementType); CommonReturn: return pCacheEle; ErrorReturn: pCacheEle = NULL; goto CommonReturn; SET_ERROR(ExceededMaxLinkDepth, E_UNEXPECTED) } STATIC void AddContextElement( IN PCONTEXT_ELEMENT pEle ) { PCERT_STORE pStore = pEle->pStore; DWORD dwContextType = pEle->dwContextType; LockStore(pStore); pEle->pNext = pStore->rgpContextListHead[dwContextType]; pEle->pPrev = NULL; if (pStore->rgpContextListHead[dwContextType]) pStore->rgpContextListHead[dwContextType]->pPrev = pEle; pStore->rgpContextListHead[dwContextType] = pEle; UnlockStore(pStore); } STATIC void RemoveContextElement( IN PCONTEXT_ELEMENT pEle ) { PCERT_STORE pStore = pEle->pStore; DWORD dwContextType = pEle->dwContextType; LockStore(pStore); // Remove context from the store's list if (pEle->pNext) pEle->pNext->pPrev = pEle->pPrev; if (pEle->pPrev) pEle->pPrev->pNext = pEle->pNext; else if (pEle == pStore->rgpContextListHead[dwContextType]) pStore->rgpContextListHead[dwContextType] = pEle->pNext; // else // Not on any list // Unlocks the store or deletes the store if this was the // last context in a closed store FreeStore(pStore); } STATIC void FreeContextElement( IN PCONTEXT_ELEMENT pEle ) { PPROP_ELEMENT pPropEle; PCONTEXT_NOCOPY_INFO pNoCopyInfo; // Free its property elements while ((pPropEle = pEle->Cache.pPropHead) != NULL) { RemovePropElement(pEle, pPropEle); FreePropElement(pPropEle); } // For NOCOPY of the pbEncoded, call the NOCOPY pfnFree callback if (pNoCopyInfo = pEle->pNoCopyInfo) { PFN_CRYPT_FREE pfnFree; BYTE **ppbEncoded; if (pfnFree = pNoCopyInfo->pfnFree) { assert(pNoCopyInfo->pvFree); pfnFree(pNoCopyInfo->pvFree); } // Inhibit following rgpfnFreeElement[] from freeing pbEncoded ppbEncoded = (BYTE **) ((BYTE *) pEle + sizeof(CONTEXT_ELEMENT) + rgOffsetofEncodedPointer[pEle->dwContextType]); *ppbEncoded = NULL; PkiFree(pNoCopyInfo); } rgpfnFreeElement[pEle->dwContextType](pEle); } STATIC void RemoveAndFreeContextElement( IN PCONTEXT_ELEMENT pEle ) { RemoveContextElement(pEle); FreeContextElement(pEle); } STATIC void AddRefContextElement( IN PCONTEXT_ELEMENT pEle ) { InterlockedIncrement(&pEle->lRefCnt); if (pEle->pStore->dwFlags & CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG) InterlockedIncrement(&pEle->pStore->lDeferCloseRefCnt); } STATIC void AddRefDeferClose( IN PCONTEXT_ELEMENT pEle ) { if (pEle->pStore->dwFlags & CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG) InterlockedIncrement(&pEle->pStore->lDeferCloseRefCnt); } STATIC void ReleaseContextElement( IN PCONTEXT_ELEMENT pEle ) { DWORD dwErr; PCERT_STORE pStore; DWORD dwStoreFlags; if (pEle == NULL) return; pStore = pEle->pStore; dwStoreFlags = pStore->dwFlags; if (0 == InterlockedDecrement(&pEle->lRefCnt)) { // Check that the store still doesn't hold a reference assert(pEle->dwFlags & ELEMENT_DELETED_FLAG); if (dwStoreFlags & CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG) InterlockedDecrement(&pStore->lDeferCloseRefCnt); dwErr = GetLastError(); if (ELEMENT_TYPE_CACHE == pEle->dwElementType) RemoveAndFreeContextElement(pEle); else RemoveAndFreeLinkElement(pEle); SetLastError(dwErr); } else if (dwStoreFlags & CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG) { LockStore(pStore); if (0 == InterlockedDecrement(&pStore->lDeferCloseRefCnt)) { if (STORE_STATE_DEFER_CLOSING == pStore->dwState) { dwErr = GetLastError(); CloseStore(pStore, 0); SetLastError(dwErr); return; } } UnlockStore(pStore); } } STATIC BOOL DeleteContextElement( IN PCONTEXT_ELEMENT pEle ) { BOOL fResult; if (NULL == pEle) { SetLastError((DWORD) E_INVALIDARG); return FALSE; } switch (pEle->dwElementType) { case ELEMENT_TYPE_LINK_CONTEXT: // Only delete the link itself break; case ELEMENT_TYPE_COLLECTION: // Delete element pointed to assert(pEle != pEle->pEle); if (pEle != pEle->pEle) { // Since delete releases refCnt, need to do an extra // addRef here. AddRefContextElement(pEle->pEle); if (!DeleteContextElement(pEle->pEle)) goto DeleteCacheCollectionError; } else goto InvalidElement; break; case ELEMENT_TYPE_CACHE: case ELEMENT_TYPE_EXTERNAL: { PCERT_STORE pProvStore = pEle->pProvStore; const DWORD dwStoreProvDeleteIndex = rgdwStoreProvDeleteIndex[pEle->dwContextType]; PFN_CERT_STORE_PROV_DELETE_CERT pfnStoreProvDeleteCert; assert(STORE_TYPE_CACHE == pProvStore->dwStoreType || STORE_TYPE_EXTERNAL == pProvStore->dwStoreType); fResult = TRUE; LockStore(pProvStore); // Check if we need to call the store provider's writethru // function. if (dwStoreProvDeleteIndex < pProvStore->StoreProvInfo.cStoreProvFunc && NULL != (pfnStoreProvDeleteCert = (PFN_CERT_STORE_PROV_DELETE_CERT) pProvStore->StoreProvInfo.rgpvStoreProvFunc[ dwStoreProvDeleteIndex])) { // Since we can't hold a lock while calling the provider // function, bump the store's provider reference count // to inhibit the closing of the store and freeing of // the provider functions. // // When the store is closed, // pProvStore->StoreProvInfo.cStoreProvFunc is set to 0. AddRefStoreProv(pProvStore); UnlockStore(pProvStore); // Check if its OK to delete from the store. fResult = pfnStoreProvDeleteCert( pProvStore->StoreProvInfo.hStoreProv, ToCertContext(pEle->pEle), 0 // dwFlags ); LockStore(pProvStore); ReleaseStoreProv(pProvStore); } UnlockStore(pProvStore); if (!fResult) goto StoreProvDeleteError; } break; default: goto InvalidElementType; } LockStore(pEle->pStore); if (0 == (pEle->dwFlags & ELEMENT_DELETED_FLAG)) { // On the store's list. There should be at least two reference // counts on the context, the store's and the caller's. assert(pEle->pStore->dwState == STORE_STATE_OPEN || pEle->pStore->dwState == STORE_STATE_OPENING || pEle->pStore->dwState == STORE_STATE_DEFER_CLOSING || pEle->pStore->dwState == STORE_STATE_CLOSING); // Remove the store's reference if (0 == InterlockedDecrement(&pEle->lRefCnt)) { assert(pEle->lRefCnt > 0); // Put back the reference to allow the ReleaseContextElement // to do the context remove and free pEle->lRefCnt = 1; } pEle->dwFlags |= ELEMENT_DELETED_FLAG; } UnlockStore(pEle->pStore); fResult = TRUE; CommonReturn: // Release the caller's reference on the context ReleaseContextElement(pEle); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(DeleteCacheCollectionError) SET_ERROR(InvalidElement, E_INVALIDARG) TRACE_ERROR(StoreProvDeleteError) SET_ERROR(InvalidElementType, E_INVALIDARG) } // Returns TRUE if both elements have identical SHA1 hash. STATIC BOOL IsIdenticalContextElement( IN PCONTEXT_ELEMENT pEle1, IN PCONTEXT_ELEMENT pEle2 ) { BYTE rgbHash1[SHA1_HASH_LEN]; BYTE rgbHash2[SHA1_HASH_LEN]; DWORD cbHash; cbHash = SHA1_HASH_LEN; if (!GetProperty( pEle1, CERT_SHA1_HASH_PROP_ID, rgbHash1, &cbHash ) || SHA1_HASH_LEN != cbHash) return FALSE; if (!GetProperty( pEle2, CERT_SHA1_HASH_PROP_ID, rgbHash2, &cbHash ) || SHA1_HASH_LEN != cbHash) return FALSE; if (0 == memcmp(rgbHash1, rgbHash2, SHA1_HASH_LEN)) return TRUE; else return FALSE; } //+------------------------------------------------------------------------- // Serialize a store element and its properties //-------------------------------------------------------------------------- STATIC BOOL SerializeStoreElement( IN HANDLE h, IN PFNWRITE pfn, IN PCONTEXT_ELEMENT pEle ) { BOOL fResult; BYTE *pbEncoded; DWORD cbEncoded; if (!SerializeProperty( h, pfn, pEle )) goto SerializePropertyError; GetContextEncodedInfo(pEle, &pbEncoded, &cbEncoded); if (!WriteStoreElement( h, pfn, GetContextEncodingType(pEle), rgdwFileElementType[pEle->dwContextType], pbEncoded, cbEncoded )) goto WriteElementError; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(SerializePropertyError); TRACE_ERROR(WriteElementError); } //+------------------------------------------------------------------------- // Serialize the context's encoded data and its properties. //-------------------------------------------------------------------------- STATIC BOOL SerializeContextElement( IN PCONTEXT_ELEMENT pEle, IN DWORD dwFlags, OUT BYTE *pbElement, IN OUT DWORD *pcbElement ) { BOOL fResult; MEMINFO MemInfo; MemInfo.pByte = pbElement; if (pbElement == NULL) MemInfo.cb = 0; else MemInfo.cb = *pcbElement; MemInfo.cbSeek = 0; if (fResult = SerializeStoreElement( (HANDLE) &MemInfo, WriteToMemory, pEle)) { if (MemInfo.cbSeek > MemInfo.cb && pbElement) { SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; } *pcbElement = MemInfo.cbSeek; } else *pcbElement = 0; return fResult; } //+========================================================================= // Collection Stack Functions //========================================================================== // No locks upon entry STATIC BOOL PushCollectionStack( IN OUT PCOLLECTION_STACK_ENTRY *ppStack, IN PCERT_STORE pCollection ) { PCOLLECTION_STACK_ENTRY pNew; PCERT_STORE_LINK pStoreLink; if (NULL == (pNew = (PCOLLECTION_STACK_ENTRY) PkiZeroAlloc( sizeof(COLLECTION_STACK_ENTRY)))) return FALSE; LockStore(pCollection); pStoreLink = pCollection->pStoreListHead; // Advance past deleted store links while (pStoreLink && (pStoreLink->dwFlags & STORE_LINK_DELETED_FLAG)) pStoreLink = pStoreLink->pNext; if (pStoreLink) AddRefStoreLink(pStoreLink); UnlockStore(pCollection); pNew->pCollection = pCollection; pNew->pStoreLink = pStoreLink; pNew->pPrev = *ppStack; *ppStack = pNew; return TRUE; }; // No locks upon entry STATIC void AdvanceToNextStackStoreLink( IN PCOLLECTION_STACK_ENTRY pStack ) { PCERT_STORE pStackCollectionStore; PCERT_STORE_LINK pStoreLink; if (NULL == pStack) return; pStoreLink = pStack->pStoreLink; if (NULL == pStoreLink) return; pStackCollectionStore = pStack->pCollection; assert(pStackCollectionStore); LockStore(pStackCollectionStore); pStoreLink = pStoreLink->pNext; // Advance past deleted store links while (pStoreLink && (pStoreLink->dwFlags & STORE_LINK_DELETED_FLAG)) pStoreLink = pStoreLink->pNext; if (pStoreLink) AddRefStoreLink(pStoreLink); UnlockStore(pStackCollectionStore); ReleaseStoreLink(pStack->pStoreLink); pStack->pStoreLink = pStoreLink; } // No locks upon entry STATIC void PopCollectionStack( IN OUT PCOLLECTION_STACK_ENTRY *ppStack ) { PCOLLECTION_STACK_ENTRY pStack = *ppStack; if (pStack) { PCOLLECTION_STACK_ENTRY pPrevStack; if (pStack->pStoreLink) ReleaseStoreLink(pStack->pStoreLink); pPrevStack = pStack->pPrev; *ppStack = pPrevStack; PkiFree(pStack); if (pPrevStack) AdvanceToNextStackStoreLink(pPrevStack); } } // No locks upon entry STATIC void ClearCollectionStack( IN PCOLLECTION_STACK_ENTRY pStack ) { while (pStack) { PCOLLECTION_STACK_ENTRY pFreeStack; if (pStack->pStoreLink) ReleaseStoreLink(pStack->pStoreLink); pFreeStack = pStack; pStack = pStack->pPrev; PkiFree(pFreeStack); } } //+========================================================================= // Link Element Functions //========================================================================== STATIC PCONTEXT_ELEMENT CreateLinkElement( IN DWORD dwContextType ) { PCONTEXT_ELEMENT pLinkEle; const DWORD cbContext = rgcbContext[dwContextType]; if (NULL == (pLinkEle = (PCONTEXT_ELEMENT) PkiZeroAlloc( sizeof(CONTEXT_ELEMENT) + cbContext))) return NULL; pLinkEle->dwContextType = dwContextType; return pLinkEle; } static inline void SetStoreHandle( IN PCONTEXT_ELEMENT pEle ) { HCERTSTORE *phStore; phStore = (HCERTSTORE *) ((BYTE *) pEle + sizeof(CONTEXT_ELEMENT) + rgOffsetofStoreHandle[pEle->dwContextType]); *phStore = (HCERTSTORE) pEle->pStore; } // The store doesn't hold a reference on the external element. // Therefore, its lRefCnt is 1 and the ELEMENT_DELETED_FLAG is set. STATIC void InitAndAddExternalElement( IN PCONTEXT_ELEMENT pLinkEle, IN PCERT_STORE pStore, // EXTERNAL IN PCONTEXT_ELEMENT pProvEle, // already AddRef'ed IN DWORD dwFlags, IN OPTIONAL void *pvProvInfo ) { const DWORD cbContext = rgcbContext[pLinkEle->dwContextType]; assert(STORE_TYPE_EXTERNAL == pStore->dwStoreType); pLinkEle->dwElementType = ELEMENT_TYPE_EXTERNAL; pLinkEle->dwFlags = dwFlags | ELEMENT_DELETED_FLAG; pLinkEle->lRefCnt = 1; pLinkEle->pEle = pProvEle; pLinkEle->pStore = pStore; pLinkEle->pProvStore = pStore; pLinkEle->External.pvProvInfo = pvProvInfo; memcpy(((BYTE *) pLinkEle) + sizeof(CONTEXT_ELEMENT), ((BYTE *) pProvEle) + sizeof(CONTEXT_ELEMENT), cbContext); SetStoreHandle(pLinkEle); AddContextElement(pLinkEle); AddRefDeferClose(pLinkEle); } // The store doesn't hold a reference on the collection element. // Therefore, its RefCount is 1 and the ELEMENT_DELETED_FLAG is set. STATIC void InitAndAddCollectionElement( IN PCONTEXT_ELEMENT pLinkEle, IN PCERT_STORE pStore, // COLLECTION IN PCONTEXT_ELEMENT pSiblingEle, // already AddRef'ed IN OPTIONAL PCOLLECTION_STACK_ENTRY pCollectionStack ) { const DWORD cbContext = rgcbContext[pLinkEle->dwContextType]; assert(STORE_TYPE_COLLECTION == pStore->dwStoreType); pLinkEle->dwElementType = ELEMENT_TYPE_COLLECTION; pLinkEle->dwFlags = ELEMENT_DELETED_FLAG; pLinkEle->lRefCnt = 1; pLinkEle->pEle = pSiblingEle; pLinkEle->pStore = pStore; pLinkEle->pProvStore = pSiblingEle->pProvStore; pLinkEle->Collection.pCollectionStack = pCollectionStack; memcpy(((BYTE *) pLinkEle) + sizeof(CONTEXT_ELEMENT), ((BYTE *) pSiblingEle) + sizeof(CONTEXT_ELEMENT), cbContext); SetStoreHandle(pLinkEle); AddContextElement(pLinkEle); AddRefDeferClose(pLinkEle); } // The store holds a reference on the link context element. // Therefore, the ELEMENT_DELETED_FLAG is clear. STATIC void InitAndAddLinkContextElement( IN PCONTEXT_ELEMENT pLinkEle, IN PCERT_STORE pStore, // CACHE IN PCONTEXT_ELEMENT pContextEle // already AddRef'ed ) { const DWORD cbContext = rgcbContext[pLinkEle->dwContextType]; assert(STORE_TYPE_CACHE == pStore->dwStoreType); pLinkEle->dwElementType = ELEMENT_TYPE_LINK_CONTEXT; pLinkEle->lRefCnt = 1; pLinkEle->pEle = pContextEle; pLinkEle->pStore = pStore; pLinkEle->pProvStore = pContextEle->pProvStore; memcpy(((BYTE *) pLinkEle) + sizeof(CONTEXT_ELEMENT), ((BYTE *) pContextEle) + sizeof(CONTEXT_ELEMENT), cbContext); SetStoreHandle(pLinkEle); AddContextElement(pLinkEle); } // Upon entry no locks STATIC void RemoveAndFreeLinkElement( IN PCONTEXT_ELEMENT pEle ) { if (ELEMENT_TYPE_EXTERNAL == pEle->dwElementType) { PCERT_STORE pProvStore = pEle->pProvStore; const DWORD dwStoreProvFreeFindIndex = rgdwStoreProvFreeFindIndex[pEle->dwContextType]; PFN_CERT_STORE_PROV_FREE_FIND_CERT pfnStoreProvFreeFindCert; assert(pEle->pStore == pEle->pProvStore); assert(STORE_TYPE_EXTERNAL == pProvStore->dwStoreType); LockStore(pProvStore); // Check if we need to call the store provider's free find cert // function. if (pEle->dwFlags & ELEMENT_FIND_NEXT_FLAG) { pEle->dwFlags &= ~ELEMENT_FIND_NEXT_FLAG; if (dwStoreProvFreeFindIndex < pProvStore->StoreProvInfo.cStoreProvFunc && NULL != (pfnStoreProvFreeFindCert = (PFN_CERT_STORE_PROV_FREE_FIND_CERT) pProvStore->StoreProvInfo.rgpvStoreProvFunc[ dwStoreProvFreeFindIndex])) { // Since we can't hold a lock while calling the provider // function, bump the store's provider reference count // to inhibit the closing of the store and freeing of // the provider functions. // // When the store is closed, // pProvStore->StoreProvInfo.cStoreProvFunc is set to 0. AddRefStoreProv(pProvStore); UnlockStore(pProvStore); pfnStoreProvFreeFindCert( pProvStore->StoreProvInfo.hStoreProv, ToCertContext(pEle->pEle), pEle->External.pvProvInfo, 0 // dwFlags ); LockStore(pProvStore); ReleaseStoreProv(pProvStore); } } UnlockStore(pProvStore); } else if (ELEMENT_TYPE_COLLECTION == pEle->dwElementType) { if (pEle->Collection.pCollectionStack) ClearCollectionStack(pEle->Collection.pCollectionStack); } ReleaseContextElement(pEle->pEle); // Remove from store RemoveContextElement(pEle); FreeLinkElement(pEle); } STATIC void FreeLinkContextElement( IN PCONTEXT_ELEMENT pLinkEle ) { ReleaseContextElement(pLinkEle->pEle); FreeLinkElement(pLinkEle); } //+========================================================================= // Find Element Functions //========================================================================== // For Add, called with store already locked and store remains locked. The // find type for the Add is FIND_EXISTING. // CacheStore may contain either cache or context link elements STATIC PCONTEXT_ELEMENT FindElementInCacheStore( IN PCERT_STORE pStore, IN DWORD dwContextType, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN OPTIONAL PCONTEXT_ELEMENT pPrevEle, IN BOOL fForceEnumArchived = FALSE ) { PCONTEXT_ELEMENT pEle; assert(STORE_TYPE_CACHE == pStore->dwStoreType); LockStore(pStore); if (pPrevEle) { if (pPrevEle->pStore != pStore || pPrevEle->dwContextType != dwContextType) { UnlockStore(pStore); goto InvalidPreviousContext; } pEle = pPrevEle->pNext; } else if (STORE_STATE_NULL == pStore->dwState) // For NULL store, all elements are already deleted pEle = NULL; else pEle = pStore->rgpContextListHead[dwContextType]; for ( ; pEle; pEle = pEle->pNext) { PCONTEXT_ELEMENT pCacheEle; BOOL fArchived; assert(ELEMENT_TYPE_CACHE == pEle->dwElementType || ELEMENT_TYPE_LINK_CONTEXT == pEle->dwElementType); // Skip past deleted elements if (pEle->dwFlags & ELEMENT_DELETED_FLAG) continue; if (ELEMENT_TYPE_CACHE == pEle->dwElementType) pCacheEle = pEle; else { pCacheEle = GetCacheElement(pEle); if (NULL == pCacheEle) pCacheEle = pEle; } fArchived = ((pCacheEle->dwFlags & ELEMENT_ARCHIVED_FLAG) && 0 == (pStore->dwFlags & CERT_STORE_ENUM_ARCHIVED_FLAG) && !fForceEnumArchived); AddRefContextElement(pEle); UnlockStore(pStore); // Handle MappedFile Exceptions __try { if (rgpfnCompareElement[dwContextType](pEle, pFindInfo, fArchived)) goto CommonReturn; } __except(EXCEPTION_EXECUTE_HANDLER) { SetLastError(GetExceptionCode()); goto ErrorReturn; } if (pPrevEle) ReleaseContextElement(pPrevEle); pPrevEle = pEle; LockStore(pStore); } UnlockStore(pStore); SetLastError((DWORD) CRYPT_E_NOT_FOUND); CommonReturn: if (pPrevEle) ReleaseContextElement(pPrevEle); return pEle; ErrorReturn: pEle = NULL; goto CommonReturn; SET_ERROR(InvalidPreviousContext, E_INVALIDARG) } STATIC PCONTEXT_ELEMENT FindElementInExternalStore( IN PCERT_STORE pStore, // EXTERNAL IN DWORD dwContextType, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN OPTIONAL PCONTEXT_ELEMENT pPrevEle ) { PCONTEXT_ELEMENT pEle = NULL; const DWORD dwStoreProvFindIndex = rgdwStoreProvFindIndex[dwContextType]; // All the context types have the same find callback signature. PFN_CERT_STORE_PROV_FIND_CERT pfnStoreProvFind; void *pvProvInfo; PCONTEXT_ELEMENT pPrevProvEle = NULL; PCCERT_CONTEXT pProvCertContext; assert(STORE_TYPE_EXTERNAL == pStore->dwStoreType); if (NULL == (pEle = CreateLinkElement(dwContextType))) goto CreateLinkElementError; if (pPrevEle) { BOOL fResult; if (pPrevEle->pStore != pStore || pPrevEle->dwContextType != dwContextType) goto InvalidPreviousContext; assert(ELEMENT_TYPE_EXTERNAL == pPrevEle->dwElementType); LockStore(pStore); fResult = pPrevEle->dwFlags & ELEMENT_FIND_NEXT_FLAG; if (fResult) { assert(dwStoreProvFindIndex < pStore->StoreProvInfo.cStoreProvFunc && pStore->StoreProvInfo.rgpvStoreProvFunc[dwStoreProvFindIndex]); pvProvInfo = pPrevEle->External.pvProvInfo; pPrevEle->External.pvProvInfo = NULL; pPrevEle->dwFlags &= ~ELEMENT_FIND_NEXT_FLAG; pPrevProvEle = pPrevEle->pEle; assert(pPrevProvEle); } UnlockStore(pStore); if (!fResult) goto InvalidExternalFindNext; } else { pvProvInfo = NULL; pPrevProvEle = NULL; } // Check if external store supports the context type if (dwStoreProvFindIndex >= pStore->StoreProvInfo.cStoreProvFunc || NULL == (pfnStoreProvFind = (PFN_CERT_STORE_PROV_FIND_CERT) pStore->StoreProvInfo.rgpvStoreProvFunc[dwStoreProvFindIndex])) goto ProvFindNotSupported; pProvCertContext = NULL; if (!pfnStoreProvFind( pStore->StoreProvInfo.hStoreProv, pFindInfo, ToCertContext(pPrevProvEle), 0, // dwFlags &pvProvInfo, &pProvCertContext) || NULL == pProvCertContext) goto ErrorReturn; InitAndAddExternalElement( pEle, pStore, ToContextElement(pProvCertContext), ELEMENT_FIND_NEXT_FLAG, pvProvInfo ); CommonReturn: if (pPrevEle) ReleaseContextElement(pPrevEle); return pEle; ErrorReturn: if (pEle) { FreeLinkElement(pEle); pEle = NULL; } goto CommonReturn; SET_ERROR(InvalidPreviousContext, E_INVALIDARG) SET_ERROR(InvalidExternalFindNext, E_INVALIDARG) SET_ERROR(ProvFindNotSupported, ERROR_CALL_NOT_IMPLEMENTED) TRACE_ERROR(CreateLinkElementError) } STATIC PCONTEXT_ELEMENT FindElementInCollectionStore( IN PCERT_STORE pCollection, IN DWORD dwContextType, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN OPTIONAL PCONTEXT_ELEMENT pPrevEle, IN BOOL fFindForAdd = FALSE ) { PCONTEXT_ELEMENT pEle = NULL; PCOLLECTION_STACK_ENTRY pStack = NULL; if (pPrevEle) { // Get previous element's collection stack if (pPrevEle->pStore != pCollection || pPrevEle->dwContextType != dwContextType) goto InvalidPreviousContext; LockStore(pCollection); pStack = pPrevEle->Collection.pCollectionStack; pPrevEle->Collection.pCollectionStack = NULL; UnlockStore(pCollection); if (NULL == pStack) goto InvalidCollectionFindNext; // Note, pStack->pCollection is only equal to pCollection // for a single level collection. assert(pStack->pStoreLink); assert(ELEMENT_TYPE_EXTERNAL == pPrevEle->dwElementType || ELEMENT_TYPE_COLLECTION == pPrevEle->dwElementType); } else { // Initialize collection stack with the collection store's // first link if (!PushCollectionStack(&pStack, pCollection)) goto PushStackError; } while (pStack) { PCERT_STORE pStackCollectionStore; PCERT_STORE_LINK pStoreLink; PCERT_STORE pFindStore; pStackCollectionStore = pStack->pCollection; pStoreLink = pStack->pStoreLink; // may be NULL if (NULL == pPrevEle) { LockStore(pStackCollectionStore); // Advance past any deleted store links // // Also if doing a find before doing an add to a collection, // check that the store link allows an ADD while (pStoreLink && ((pStoreLink->dwFlags & STORE_LINK_DELETED_FLAG) || (fFindForAdd && 0 == (pStoreLink->dwUpdateFlags & CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG)))) pStoreLink = pStoreLink->pNext; if (pStoreLink && pStoreLink != pStack->pStoreLink) AddRefStoreLink(pStoreLink); UnlockStore(pStackCollectionStore); if (NULL == pStoreLink) { // Reached end of collection's store links PopCollectionStack(&pStack); continue; } else if (pStoreLink != pStack->pStoreLink) { ReleaseStoreLink(pStack->pStoreLink); pStack->pStoreLink = pStoreLink; } } assert(pStoreLink); pFindStore = pStoreLink->pSibling; if (STORE_TYPE_COLLECTION == pFindStore->dwStoreType) { assert(NULL == pPrevEle); // Add inner collection store to stack if (!PushCollectionStack(&pStack, pFindStore)) goto PushStackError; } else if (STORE_TYPE_CACHE == pFindStore->dwStoreType || STORE_TYPE_EXTERNAL == pFindStore->dwStoreType) { PCONTEXT_ELEMENT pPrevSiblingEle; PCONTEXT_ELEMENT pSiblingEle; if (pPrevEle) { assert(ELEMENT_TYPE_COLLECTION == pPrevEle->dwElementType); pPrevSiblingEle = pPrevEle->pEle; // FindElementInCacheStore or FindElementInExternalStore // does an implicit Free AddRefContextElement(pPrevSiblingEle); } else pPrevSiblingEle = NULL; if (pSiblingEle = FindElementInStore( pFindStore, dwContextType, pFindInfo, pPrevSiblingEle )) { if (NULL == (pEle = CreateLinkElement(dwContextType))) { ReleaseContextElement(pSiblingEle); goto CreateLinkElementError; } InitAndAddCollectionElement( pEle, pCollection, pSiblingEle, pStack ); goto CommonReturn; } if (pPrevEle) { ReleaseContextElement(pPrevEle); pPrevEle = NULL; } // Advance to the next store link in the collection AdvanceToNextStackStoreLink(pStack); } else goto InvalidStoreType; } SetLastError((DWORD) CRYPT_E_NOT_FOUND); CommonReturn: if (pPrevEle) ReleaseContextElement(pPrevEle); return pEle; ErrorReturn: ClearCollectionStack(pStack); pEle = NULL; goto CommonReturn; SET_ERROR(InvalidPreviousContext, E_INVALIDARG) SET_ERROR(InvalidCollectionFindNext, E_INVALIDARG) TRACE_ERROR(PushStackError) TRACE_ERROR(CreateLinkElementError) SET_ERROR(InvalidStoreType, E_INVALIDARG) } STATIC PCONTEXT_ELEMENT FindElementInStore( IN PCERT_STORE pStore, IN DWORD dwContextType, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN OPTIONAL PCONTEXT_ELEMENT pPrevEle ) { assert(pStore->dwState == STORE_STATE_OPEN || pStore->dwState == STORE_STATE_OPENING || pStore->dwState == STORE_STATE_DEFER_CLOSING || pStore->dwState == STORE_STATE_CLOSING || pStore->dwState == STORE_STATE_NULL); switch (pStore->dwStoreType) { case STORE_TYPE_CACHE: return FindElementInCacheStore( pStore, dwContextType, pFindInfo, pPrevEle ); break; case STORE_TYPE_EXTERNAL: return FindElementInExternalStore( pStore, dwContextType, pFindInfo, pPrevEle ); break; case STORE_TYPE_COLLECTION: return FindElementInCollectionStore( pStore, dwContextType, pFindInfo, pPrevEle ); break; default: goto InvalidStoreType; } ErrorReturn: return NULL; SET_ERROR(InvalidStoreType, E_INVALIDARG) } STATIC void AutoResyncStore( IN PCERT_STORE pStore ) { if (pStore->hAutoResyncEvent) { if (WAIT_OBJECT_0 == WaitForSingleObjectEx( pStore->hAutoResyncEvent, 0, // dwMilliseconds FALSE // bAlertable )) CertControlStore( (HCERTSTORE) pStore, CERT_STORE_CTRL_INHIBIT_DUPLICATE_HANDLE_FLAG, CERT_STORE_CTRL_RESYNC, &pStore->hAutoResyncEvent ); } else if (STORE_TYPE_COLLECTION == pStore->dwStoreType) { // Iterate through all the siblings and attempt to AutoResync PCERT_STORE_LINK pStoreLink; PCERT_STORE_LINK pPrevStoreLink = NULL; LockStore(pStore); pStoreLink = pStore->pStoreListHead; for (; pStoreLink; pStoreLink = pStoreLink->pNext) { // Advance past deleted store link if (pStoreLink->dwFlags & STORE_LINK_DELETED_FLAG) continue; AddRefStoreLink(pStoreLink); UnlockStore(pStore); if (pPrevStoreLink) ReleaseStoreLink(pPrevStoreLink); pPrevStoreLink = pStoreLink; AutoResyncStore(pStoreLink->pSibling); LockStore(pStore); } UnlockStore(pStore); if (pPrevStoreLink) ReleaseStoreLink(pPrevStoreLink); } } STATIC PCONTEXT_ELEMENT CheckAutoResyncAndFindElementInStore( IN PCERT_STORE pStore, IN DWORD dwContextType, IN PCCERT_STORE_PROV_FIND_INFO pFindInfo, IN OPTIONAL PCONTEXT_ELEMENT pPrevEle ) { if (NULL == pPrevEle) AutoResyncStore(pStore); return FindElementInStore( pStore, dwContextType, pFindInfo, pPrevEle ); } //+========================================================================= // Add Element Functions //========================================================================== STATIC void SetFindInfoToFindExisting( IN PCONTEXT_ELEMENT pEle, IN OUT PCERT_STORE_PROV_FIND_INFO pFindInfo ) { memset(pFindInfo, 0, sizeof(*pFindInfo)); pFindInfo->cbSize = sizeof(*pFindInfo); pFindInfo->dwFindType = rgdwFindTypeToFindExisting[pEle->dwContextType]; pFindInfo->pvFindPara = ToCertContext(pEle); } STATIC BOOL AddLinkContextToCacheStore( IN PCERT_STORE pStore, IN PCONTEXT_ELEMENT pEle, IN DWORD dwAddDisposition, OUT OPTIONAL PCONTEXT_ELEMENT *ppStoreEle ) { BOOL fResult; PCONTEXT_ELEMENT pLinkEle = NULL; PCONTEXT_ELEMENT pGetEle = NULL; if (STORE_TYPE_CACHE != pStore->dwStoreType) goto InvalidStoreType; // Note, CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES or // CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES // isn't allowed for adding links if (!(CERT_STORE_ADD_NEW == dwAddDisposition || CERT_STORE_ADD_USE_EXISTING == dwAddDisposition || CERT_STORE_ADD_REPLACE_EXISTING == dwAddDisposition || CERT_STORE_ADD_ALWAYS == dwAddDisposition || CERT_STORE_ADD_NEWER == dwAddDisposition)) goto InvalidAddDisposition; LockStore(pStore); if (CERT_STORE_ADD_ALWAYS != dwAddDisposition) { // Check if the context is already in the store. CERT_STORE_PROV_FIND_INFO FindInfo; // Check if the context element is already in the store SetFindInfoToFindExisting(pEle, &FindInfo); if (pGetEle = FindElementInCacheStore( pStore, pEle->dwContextType, &FindInfo, NULL // pPrevEle )) { UnlockStore(pStore); switch (dwAddDisposition) { case CERT_STORE_ADD_NEW: goto NotNewError; break; case CERT_STORE_ADD_NEWER: if (!rgpfnIsNewerElement[pEle->dwContextType](pEle, pGetEle)) goto NotNewError; // fall through case CERT_STORE_ADD_REPLACE_EXISTING: if (DeleteContextElement(pGetEle)) // Try again. It shouldn't be in the store. return AddLinkContextToCacheStore( pStore, pEle, dwAddDisposition, ppStoreEle); else { // Provider didn't allow the delete pGetEle = NULL; goto DeleteError; } break; case CERT_STORE_ADD_USE_EXISTING: if (ppStoreEle) *ppStoreEle = pGetEle; else ReleaseContextElement(pGetEle); return TRUE; break; default: goto InvalidArg; break; } } } if (NULL == (pLinkEle = CreateLinkElement(pEle->dwContextType))) { UnlockStore(pStore); goto CreateLinkElementError; } AddRefContextElement(pEle); InitAndAddLinkContextElement( pLinkEle, pStore, pEle ); if (ppStoreEle) { AddRefContextElement(pLinkEle); *ppStoreEle = pLinkEle; } UnlockStore(pStore); fResult = TRUE; CommonReturn: return fResult; ErrorReturn: if (pGetEle) ReleaseContextElement(pGetEle); if (ppStoreEle) *ppStoreEle = NULL; fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidStoreType, E_INVALIDARG) SET_ERROR(InvalidAddDisposition, E_INVALIDARG) SET_ERROR(NotNewError, CRYPT_E_EXISTS) SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(DeleteError) TRACE_ERROR(CreateLinkElementError) } // pEle is used or freed for success only, Otherwise, its left alone and // will be freed by the caller. // // This routine may be called recursively // // For pStore != pEle->pStore, pEle->pStore is the outer collection store. // pStore is the cache store. STATIC BOOL AddElementToCacheStore( IN PCERT_STORE pStore, IN PCONTEXT_ELEMENT pEle, IN DWORD dwAddDisposition, OUT OPTIONAL PCONTEXT_ELEMENT *ppStoreEle ) { BOOL fResult; PCONTEXT_ELEMENT pGetEle = NULL; PCONTEXT_ELEMENT pCollectionEle; PCERT_STORE pCollectionStore = NULL; const DWORD dwStoreProvWriteIndex = rgdwStoreProvWriteIndex[pEle->dwContextType]; PFN_CERT_STORE_PROV_WRITE_CERT pfnStoreProvWriteCert; BOOL fUpdateKeyId; LockStore(pStore); assert(STORE_STATE_DELETED != pStore->dwState && STORE_STATE_CLOSED != pStore->dwState); if (STORE_STATE_NULL == pStore->dwState) { // CertCreate*Context, CertAddSerializedElementToStore // or CertAddEncoded*ToStore with hCertStore == NULL. pEle->dwFlags |= ELEMENT_DELETED_FLAG; dwAddDisposition = CERT_STORE_ADD_ALWAYS; } assert(CERT_STORE_ADD_NEW == dwAddDisposition || CERT_STORE_ADD_USE_EXISTING == dwAddDisposition || CERT_STORE_ADD_REPLACE_EXISTING == dwAddDisposition || CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES == dwAddDisposition || CERT_STORE_ADD_ALWAYS == dwAddDisposition || CERT_STORE_ADD_NEWER == dwAddDisposition || CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES == dwAddDisposition); if (CERT_STORE_ADD_ALWAYS != dwAddDisposition) { // Check if the context is already in the store. CERT_STORE_PROV_FIND_INFO FindInfo; // Check if the context element is already in the store SetFindInfoToFindExisting(pEle, &FindInfo); if (pGetEle = FindElementInCacheStore( pStore, pEle->dwContextType, &FindInfo, NULL // pPrevEle )) { UnlockStore(pStore); if (CERT_STORE_ADD_NEWER == dwAddDisposition || CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES == dwAddDisposition) { if (!rgpfnIsNewerElement[pEle->dwContextType](pEle, pGetEle)) goto NotNewError; } if (CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES == dwAddDisposition || CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES == dwAddDisposition) { // If element in the store isn't identical to the one being // added, then, inherit properties from and delete the // element in the store. if (!IsIdenticalContextElement(pEle, pGetEle)) { if (!CopyProperties( pGetEle, pEle, COPY_PROPERTY_USE_EXISTING_FLAG | COPY_PROPERTY_INHIBIT_PROV_SET_FLAG )) goto CopyPropertiesError; dwAddDisposition = CERT_STORE_ADD_REPLACE_EXISTING; } } switch (dwAddDisposition) { case CERT_STORE_ADD_NEW: goto NotNewError; break; case CERT_STORE_ADD_REPLACE_EXISTING: case CERT_STORE_ADD_NEWER: if (DeleteContextElement(pGetEle)) // Try again. It shouldn't be in the store. return AddElementToCacheStore( pStore, pEle, dwAddDisposition, ppStoreEle); else { // Provider didn't allow the delete pGetEle = NULL; goto DeleteError; } break; case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES: case CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES: case CERT_STORE_ADD_USE_EXISTING: // For USE_EXISTING, copy any new non-existing properties. // Otherwise, copy all properties, replacing existing // properties. if (!CopyProperties( pEle, pGetEle, CERT_STORE_ADD_USE_EXISTING == dwAddDisposition ? COPY_PROPERTY_USE_EXISTING_FLAG : 0 )) goto CopyPropertiesError; if (ppStoreEle) { if (pStore != pEle->pStore) { assert(STORE_TYPE_COLLECTION == pEle->pStore->dwStoreType); if (NULL == (*ppStoreEle = CreateLinkElement( pEle->dwContextType))) goto CreateLinkElementError; InitAndAddCollectionElement( *ppStoreEle, pEle->pStore, pGetEle, NULL // pCollectionStack ); } else *ppStoreEle = pGetEle; } else ReleaseContextElement(pGetEle); FreeContextElement(pEle); return TRUE; break; default: goto InvalidArg; break; } } } // The element doesn't exist in the store. // Check if we need to write through to the provider. if (pStore->StoreProvInfo.cStoreProvFunc > dwStoreProvWriteIndex && NULL != (pfnStoreProvWriteCert = (PFN_CERT_STORE_PROV_WRITE_CERT) pStore->StoreProvInfo.rgpvStoreProvFunc[ dwStoreProvWriteIndex])) { // Don't ever call the provider holding a lock!! // Also, the caller is holding a reference count on the store. UnlockStore(pStore); if (!pfnStoreProvWriteCert( pStore->StoreProvInfo.hStoreProv, ToCertContext(pEle), (dwAddDisposition << 16) | CERT_STORE_PROV_WRITE_ADD_FLAG)) goto StoreProvWriteError; LockStore(pStore); if (CERT_STORE_ADD_ALWAYS != dwAddDisposition) { // Check if the certificate was added while the store was unlocked CERT_STORE_PROV_FIND_INFO FindInfo; // Check if the context element is already in the store SetFindInfoToFindExisting(pEle, &FindInfo); if (pGetEle = FindElementInCacheStore( pStore, pEle->dwContextType, &FindInfo, NULL // pPrevEle )) { // Try again UnlockStore(pStore); ReleaseContextElement(pGetEle); return AddElementToCacheStore(pStore, pEle, dwAddDisposition, ppStoreEle); } } } pCollectionEle = NULL; fUpdateKeyId = (pStore->dwFlags & CERT_STORE_UPDATE_KEYID_FLAG) && STORE_STATE_OPENING != pStore->dwState; if (pStore != pEle->pStore) { assert(STORE_TYPE_COLLECTION == pEle->pStore->dwStoreType); if (ppStoreEle) { if (NULL == (pCollectionEle = CreateLinkElement(pEle->dwContextType))) { UnlockStore(pStore); goto CreateLinkElementError; } pCollectionStore = pEle->pStore; } // Update the element's store. This is needed when adding to a store in // a collection pEle->pProvStore = pStore; pEle->pStore = pStore; SetStoreHandle(pEle); } if (FindPropElement(pEle, CERT_ARCHIVED_PROP_ID)) pEle->dwFlags |= ELEMENT_ARCHIVED_FLAG; // Finally, add the element to the store. AddContextElement(pEle); AddRefContextElement(pEle); // needed for fUpdateKeyId if (pCollectionEle) { assert(pCollectionStore && ppStoreEle); AddRefContextElement(pEle); UnlockStore(pStore); InitAndAddCollectionElement( pCollectionEle, pCollectionStore, pEle, NULL // pCollectionStack ); *ppStoreEle = pCollectionEle; } else { if (STORE_STATE_NULL == pStore->dwState) { if (ppStoreEle) // Since the NULL store doesn't hold a reference, use it. *ppStoreEle = pEle; else ReleaseContextElement(pEle); } else if (ppStoreEle) { AddRefContextElement(pEle); *ppStoreEle = pEle; } UnlockStore(pStore); } fResult = TRUE; if (fUpdateKeyId) SetCryptKeyIdentifierKeyProvInfoProperty(pEle); ReleaseContextElement(pEle); CommonReturn: return fResult; ErrorReturn: if (pGetEle) ReleaseContextElement(pGetEle); if (ppStoreEle) *ppStoreEle = NULL; fResult = FALSE; goto CommonReturn; SET_ERROR(NotNewError, CRYPT_E_EXISTS) SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(DeleteError) TRACE_ERROR(CopyPropertiesError) TRACE_ERROR(CreateLinkElementError) TRACE_ERROR(StoreProvWriteError) } // pEle is used or freed for success only, Otherwise, its left alone and // will be freed by the caller. // // This routine may be called recursively // // The caller is holding a reference count on the store // // For pStore != pEle->pStore, pEle->pStore is the outer collection store. // pStore is the external store. STATIC BOOL AddElementToExternalStore( IN PCERT_STORE pStore, IN PCONTEXT_ELEMENT pEle, IN DWORD dwAddDisposition, OUT OPTIONAL PCONTEXT_ELEMENT *ppStoreEle ) { BOOL fResult; const DWORD dwStoreProvWriteIndex = rgdwStoreProvWriteIndex[pEle->dwContextType]; PFN_CERT_STORE_PROV_WRITE_CERT pfnStoreProvWriteCert; PCONTEXT_ELEMENT pExternalEle = NULL; PCONTEXT_ELEMENT pCollectionEle = NULL; PCERT_STORE pEleStore; BOOL fUpdateKeyId; // Check if the store supports the write callback if (pStore->StoreProvInfo.cStoreProvFunc <= dwStoreProvWriteIndex || NULL == (pfnStoreProvWriteCert = (PFN_CERT_STORE_PROV_WRITE_CERT) pStore->StoreProvInfo.rgpvStoreProvFunc[ dwStoreProvWriteIndex])) goto ProvWriteNotSupported; // Remember the Element's store. pEleStore = pEle->pStore; fUpdateKeyId = pStore->dwFlags & CERT_STORE_UPDATE_KEYID_FLAG; if (ppStoreEle) { if (NULL == (pExternalEle = CreateLinkElement(pEle->dwContextType))) goto CreateLinkElementError; if (pStore != pEleStore) { assert(STORE_TYPE_COLLECTION == pEleStore->dwStoreType); if (NULL == (pCollectionEle = CreateLinkElement(pEle->dwContextType))) goto CreateLinkElementError; } } // Update the Element to use the NULL store. pEle->pProvStore = &NullCertStore; pEle->pStore = &NullCertStore; SetStoreHandle(pEle); // Also, the caller is holding a reference count on the store. if (!pfnStoreProvWriteCert( pStore->StoreProvInfo.hStoreProv, ToCertContext(pEle), (dwAddDisposition << 16) | CERT_STORE_PROV_WRITE_ADD_FLAG)) { // Restore the Element's store pEle->pProvStore = pEleStore; pEle->pStore = pEleStore; SetStoreHandle(pEle); goto StoreProvWriteError; } // Add to the NULL store pEle->dwFlags |= ELEMENT_DELETED_FLAG; AddContextElement(pEle); AddRefContextElement(pEle); // needed for fUpdateKeyId if (ppStoreEle) { InitAndAddExternalElement( pExternalEle, pStore, // pProvStore pEle, 0, // dwFlags NULL // pvProvInfo ); if (pStore != pEleStore) { InitAndAddCollectionElement( pCollectionEle, pEleStore, pExternalEle, NULL // pCollectionStack ); *ppStoreEle = pCollectionEle; } else *ppStoreEle = pExternalEle; } else ReleaseContextElement(pEle); if (fUpdateKeyId) SetCryptKeyIdentifierKeyProvInfoProperty(pEle); ReleaseContextElement(pEle); fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; if (pExternalEle) FreeLinkElement(pExternalEle); if (pCollectionEle) FreeLinkElement(pCollectionEle); if (ppStoreEle) *ppStoreEle = NULL; goto CommonReturn; TRACE_ERROR(CreateLinkElementError) TRACE_ERROR(StoreProvWriteError) SET_ERROR(ProvWriteNotSupported, E_NOTIMPL) } // pEle is used or freed for success only, Otherwise, its left alone and // will be freed by the caller. // // This routine may be called recursively STATIC BOOL AddElementToCollectionStore( IN PCERT_STORE pCollection, IN PCONTEXT_ELEMENT pEle, IN DWORD dwAddDisposition, OUT OPTIONAL PCONTEXT_ELEMENT *ppStoreEle ) { BOOL fResult; PCERT_STORE pOuterCollection; PCONTEXT_ELEMENT pGetEle = NULL; PCERT_STORE_LINK pStoreLink; PCERT_STORE_LINK pPrevStoreLink = NULL; DWORD dwAddErr; pOuterCollection = pEle->pStore; // Only need to do the find once for the outer most collection if (pOuterCollection == pCollection && CERT_STORE_ADD_ALWAYS != dwAddDisposition) { CERT_STORE_PROV_FIND_INFO FindInfo; // Check if the context element is already in any of the collection's // stores SetFindInfoToFindExisting(pEle, &FindInfo); if (pGetEle = FindElementInCollectionStore( pCollection, pEle->dwContextType, &FindInfo, NULL, // pPrevEle FALSE // fFindForAdd )) { if (CERT_STORE_ADD_NEWER == dwAddDisposition || CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES == dwAddDisposition) { if (!rgpfnIsNewerElement[pEle->dwContextType](pEle, pGetEle)) goto NotNewError; } switch (dwAddDisposition) { case CERT_STORE_ADD_NEW: goto NotNewError; break; case CERT_STORE_ADD_REPLACE_EXISTING: case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES: case CERT_STORE_ADD_USE_EXISTING: case CERT_STORE_ADD_NEWER: case CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES: // For success pEle will be used or freed by the called function // Add to either the cache or external store assert(STORE_TYPE_CACHE == pGetEle->pProvStore->dwStoreType || STORE_TYPE_EXTERNAL == pGetEle->pProvStore->dwStoreType); fResult = AddElementToStore( pGetEle->pProvStore, pEle, dwAddDisposition, ppStoreEle ); goto CommonReturn; default: goto InvalidArg; break; } } } // The element doesn't exist in any of the collection's stores. // Iterate and try to add to first where adding is allowed LockStore(pCollection); dwAddErr = (DWORD) E_ACCESSDENIED; pStoreLink = pCollection->pStoreListHead; for (; pStoreLink; pStoreLink = pStoreLink->pNext) { // Advance past deleted store links and links not enabling adds if ((pStoreLink->dwFlags & STORE_LINK_DELETED_FLAG) || 0 == (pStoreLink->dwUpdateFlags & CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG)) continue; AddRefStoreLink(pStoreLink); UnlockStore(pCollection); if (pPrevStoreLink) ReleaseStoreLink(pPrevStoreLink); pPrevStoreLink = pStoreLink; if (AddElementToStore( pStoreLink->pSibling, pEle, dwAddDisposition, ppStoreEle )) { fResult = TRUE; goto CommonReturn; } else if (E_ACCESSDENIED == dwAddErr) { DWORD dwErr = GetLastError(); if (0 != dwErr) dwAddErr = dwErr; } LockStore(pCollection); } UnlockStore(pCollection); goto NoAddEnabledStore; CommonReturn: if (pGetEle) ReleaseContextElement(pGetEle); if (pPrevStoreLink) ReleaseStoreLink(pPrevStoreLink); return fResult; ErrorReturn: fResult = FALSE; if (ppStoreEle) *ppStoreEle = NULL; goto CommonReturn; SET_ERROR(NotNewError, CRYPT_E_EXISTS) SET_ERROR(InvalidArg, E_INVALIDARG) SET_ERROR_VAR(NoAddEnabledStore, dwAddErr) } // pEle is used or freed for success only, Otherwise, its left alone and // will be freed by the caller. // // This routine may be called recursively // // For pStore != pEle->pStore, pEle->pStore is the outer collection store. // pStore is the inner store STATIC BOOL AddElementToStore( IN PCERT_STORE pStore, IN PCONTEXT_ELEMENT pEle, IN DWORD dwAddDisposition, OUT OPTIONAL PCONTEXT_ELEMENT *ppStoreEle ) { // Check for valid disposition values if (!(CERT_STORE_ADD_NEW == dwAddDisposition || CERT_STORE_ADD_USE_EXISTING == dwAddDisposition || CERT_STORE_ADD_REPLACE_EXISTING == dwAddDisposition || CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES == dwAddDisposition || CERT_STORE_ADD_ALWAYS == dwAddDisposition || CERT_STORE_ADD_NEWER == dwAddDisposition || CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES == dwAddDisposition)) { *ppStoreEle = NULL; SetLastError((DWORD) E_INVALIDARG); return FALSE; } switch (pStore->dwStoreType) { case STORE_TYPE_CACHE: return AddElementToCacheStore( pStore, pEle, dwAddDisposition, ppStoreEle ); break; case STORE_TYPE_EXTERNAL: return AddElementToExternalStore( pStore, pEle, dwAddDisposition, ppStoreEle ); break; case STORE_TYPE_COLLECTION: return AddElementToCollectionStore( pStore, pEle, dwAddDisposition, ppStoreEle ); break; default: goto InvalidStoreType; } ErrorReturn: return NULL; SET_ERROR(InvalidStoreType, E_INVALIDARG) } STATIC BOOL AddEncodedContextToStore( IN PCERT_STORE pStore, IN DWORD dwContextType, IN DWORD dwCertEncodingType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwAddDisposition, OUT OPTIONAL PCONTEXT_ELEMENT *ppStoreEle ) { BOOL fResult; BYTE *pbAllocEncoded = NULL; PCONTEXT_ELEMENT pEle = NULL; DWORD dwExceptionCode; // Handle MappedFile Exceptions __try { if (NULL == pStore) pStore = &NullCertStore; if (NULL == (pbAllocEncoded = (BYTE *) PkiNonzeroAlloc(cbEncoded))) goto OutOfMemory; // If pbEncoded is a MappedFile, the following copy may raise // an exception memcpy(pbAllocEncoded, pbEncoded, cbEncoded); if (NULL == (pEle = rgpfnCreateElement[dwContextType]( pStore, dwCertEncodingType, pbAllocEncoded, cbEncoded, NULL // pShareEle ))) goto CreateElementError; if (!AddElementToStore( pStore, pEle, dwAddDisposition, ppStoreEle )) goto AddElementError; fResult = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; } CommonReturn: return fResult; ErrorReturn: if (pEle) FreeContextElement(pEle); else PkiFree(pbAllocEncoded); if (ppStoreEle) *ppStoreEle = NULL; fResult = FALSE; goto CommonReturn; TRACE_ERROR(OutOfMemory) TRACE_ERROR(CreateElementError) TRACE_ERROR(AddElementError) SET_ERROR_VAR(ExceptionError, dwExceptionCode) } STATIC BOOL AddContextToStore( IN PCERT_STORE pStore, IN PCONTEXT_ELEMENT pSrcEle, IN DWORD dwCertEncodingType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwAddDisposition, OUT OPTIONAL PCONTEXT_ELEMENT *ppStoreEle ) { BOOL fResult; BYTE *pbAllocEncoded = NULL; PCONTEXT_ELEMENT pEle = NULL; DWORD dwExceptionCode; // Handle MappedFile Exceptions __try { if (NULL == pStore) pStore = &NullCertStore; if (NULL == (pbAllocEncoded = (BYTE *) PkiNonzeroAlloc(cbEncoded))) goto OutOfMemory; // If pbEncoded is a MappedFile, the following copy may raise // an exception memcpy(pbAllocEncoded, pbEncoded, cbEncoded); if (NULL == (pEle = rgpfnCreateElement[pSrcEle->dwContextType]( pStore, dwCertEncodingType, pbAllocEncoded, cbEncoded, NULL // pShareEle ))) goto CreateElementError; if (!CopyProperties( pSrcEle, pEle, COPY_PROPERTY_INHIBIT_PROV_SET_FLAG )) goto CopyPropertiesError; if (!AddElementToStore( pStore, pEle, dwAddDisposition, ppStoreEle )) goto AddElementError; fResult = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; } CommonReturn: return fResult; ErrorReturn: if (pEle) FreeContextElement(pEle); else PkiFree(pbAllocEncoded); if (ppStoreEle) *ppStoreEle = NULL; fResult = FALSE; goto CommonReturn; TRACE_ERROR(OutOfMemory) TRACE_ERROR(CreateElementError) TRACE_ERROR(CopyPropertiesError) TRACE_ERROR(AddElementError) SET_ERROR_VAR(ExceptionError, dwExceptionCode) } //+========================================================================= // PROP_ELEMENT Functions //========================================================================== // pbData has already been allocated STATIC PPROP_ELEMENT CreatePropElement( IN DWORD dwPropId, IN DWORD dwFlags, IN BYTE *pbData, IN DWORD cbData ) { PPROP_ELEMENT pEle = NULL; // Allocate and initialize the prop element structure pEle = (PPROP_ELEMENT) PkiZeroAlloc(sizeof(PROP_ELEMENT)); if (pEle == NULL) return NULL; pEle->dwPropId = dwPropId; pEle->dwFlags = dwFlags; pEle->pbData = pbData; pEle->cbData = cbData; pEle->pNext = NULL; pEle->pPrev = NULL; return pEle; } STATIC void FreePropElement(IN PPROP_ELEMENT pEle) { if (pEle->dwPropId == CERT_KEY_CONTEXT_PROP_ID) { HCRYPTPROV hProv = ((PCERT_KEY_CONTEXT) pEle->pbData)->hCryptProv; if (hProv && (pEle->dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG) == 0) { DWORD dwErr = GetLastError(); CryptReleaseContext(hProv, 0); SetLastError(dwErr); } } PkiFree(pEle->pbData); PkiFree(pEle); } // Upon entry/exit: Store/Element is locked STATIC PPROP_ELEMENT FindPropElement( IN PPROP_ELEMENT pPropEle, IN DWORD dwPropId ) { while (pPropEle) { if (pPropEle->dwPropId == dwPropId) return pPropEle; pPropEle = pPropEle->pNext; } return NULL; } STATIC PPROP_ELEMENT FindPropElement( IN PCONTEXT_ELEMENT pCacheEle, IN DWORD dwPropId ) { assert(ELEMENT_TYPE_CACHE == pCacheEle->dwElementType); return FindPropElement(pCacheEle->Cache.pPropHead, dwPropId); } // Upon entry/exit: Store/Element is locked STATIC void AddPropElement( IN OUT PPROP_ELEMENT *ppPropHead, IN PPROP_ELEMENT pPropEle ) { // Insert property in the certificate/CRL/CTL's list of properties pPropEle->pNext = *ppPropHead; pPropEle->pPrev = NULL; if (*ppPropHead) (*ppPropHead)->pPrev = pPropEle; *ppPropHead = pPropEle; } STATIC void AddPropElement( IN OUT PCONTEXT_ELEMENT pCacheEle, IN PPROP_ELEMENT pPropEle ) { assert(ELEMENT_TYPE_CACHE == pCacheEle->dwElementType); AddPropElement(&pCacheEle->Cache.pPropHead, pPropEle); } // Upon entry/exit: Store/Element is locked STATIC void RemovePropElement( IN OUT PPROP_ELEMENT *ppPropHead, IN PPROP_ELEMENT pPropEle ) { if (pPropEle->pNext) pPropEle->pNext->pPrev = pPropEle->pPrev; if (pPropEle->pPrev) pPropEle->pPrev->pNext = pPropEle->pNext; else if (pPropEle == *ppPropHead) *ppPropHead = pPropEle->pNext; // else // Not on any list } STATIC void RemovePropElement( IN OUT PCONTEXT_ELEMENT pCacheEle, IN PPROP_ELEMENT pPropEle ) { assert(ELEMENT_TYPE_CACHE == pCacheEle->dwElementType); RemovePropElement(&pCacheEle->Cache.pPropHead, pPropEle); } //+========================================================================= // Property Functions //========================================================================== // Upon entry/exit the store is locked STATIC void DeleteProperty( IN OUT PPROP_ELEMENT *ppPropHead, IN DWORD dwPropId ) { PPROP_ELEMENT pPropEle; // Delete the property pPropEle = FindPropElement(*ppPropHead, dwPropId); if (pPropEle) { RemovePropElement(ppPropHead, pPropEle); FreePropElement(pPropEle); } } STATIC void DeleteProperty( IN OUT PCONTEXT_ELEMENT pCacheEle, IN DWORD dwPropId ) { DeleteProperty(&pCacheEle->Cache.pPropHead, dwPropId); } //+------------------------------------------------------------------------- // Set the property for the specified element //-------------------------------------------------------------------------- STATIC BOOL SetProperty( IN PCONTEXT_ELEMENT pEle, IN DWORD dwPropId, IN DWORD dwFlags, IN const void *pvData, IN BOOL fInhibitProvSet ) { BOOL fResult; PCONTEXT_ELEMENT pCacheEle; PCERT_STORE pCacheStore; PPROP_ELEMENT pPropEle; BYTE *pbEncoded = NULL; DWORD cbEncoded; CERT_KEY_CONTEXT KeyContext; if (dwPropId == 0 || dwPropId > CERT_LAST_USER_PROP_ID) { SetLastError((DWORD) E_INVALIDARG); return FALSE; } if (dwFlags & CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAG) fInhibitProvSet = TRUE; if (NULL == (pCacheEle = GetCacheElement(pEle))) return FALSE; pCacheStore = pCacheEle->pStore; LockStore(pCacheStore); if (dwPropId == CERT_KEY_PROV_HANDLE_PROP_ID || dwPropId == CERT_KEY_SPEC_PROP_ID) { // Map to the CERT_KEY_CONTEXT_PROP_ID and update its // hCryptProv and/or dwKeySpec field(s). DWORD cbData = sizeof(KeyContext); if ((fResult = GetProperty( pCacheEle, CERT_KEY_CONTEXT_PROP_ID, &KeyContext, &cbData))) { if (dwPropId == CERT_KEY_SPEC_PROP_ID) { // Inhibit hCryptProv from being closed by the subsequent // DeleteProperty. Also, use the existing dwFlags. pPropEle = FindPropElement(pCacheEle, CERT_KEY_CONTEXT_PROP_ID); assert(pPropEle); if (pPropEle) { dwFlags = pPropEle->dwFlags; pPropEle->dwFlags = CERT_STORE_NO_CRYPT_RELEASE_FLAG; } } } else { memset(&KeyContext, 0, sizeof(KeyContext)); KeyContext.cbSize = sizeof(KeyContext); if (pvData && dwPropId != CERT_KEY_SPEC_PROP_ID) { // Try to get the KeySpec from a CERT_KEY_PROV_INFO_PROP_ID. // Need to do without any locks. UnlockStore(pCacheStore); cbData = sizeof(DWORD); GetProperty( pEle, CERT_KEY_SPEC_PROP_ID, &KeyContext.dwKeySpec, &cbData); LockStore(pCacheStore); // Check if CERT_KEY_CONTEXT_PROP_ID was added while store // was unlocked. if (FindPropElement(pCacheEle, CERT_KEY_CONTEXT_PROP_ID)) { // We now have a CERT_KEY_CONTEXT_PROP_ID property. // Try again UnlockStore(pCacheStore); return SetProperty( pEle, dwPropId, dwFlags, pvData, fInhibitProvSet ); } } } if (dwPropId == CERT_KEY_PROV_HANDLE_PROP_ID) { KeyContext.hCryptProv = (HCRYPTPROV) pvData; } else { if (pvData) KeyContext.dwKeySpec = *((DWORD *) pvData); else KeyContext.dwKeySpec = 0; } if (fResult || pvData) // CERT_KEY_CONTEXT_PROP_ID exists or we are creating a // new CERT_KEY_CONTEXT_PROP_ID pvData = &KeyContext; dwPropId = CERT_KEY_CONTEXT_PROP_ID; } else if (dwPropId == CERT_KEY_CONTEXT_PROP_ID) { if (pvData) { PCERT_KEY_CONTEXT pKeyContext = (PCERT_KEY_CONTEXT) pvData; if (pKeyContext->cbSize != sizeof(CERT_KEY_CONTEXT)) goto InvalidArg; } } else if (!fInhibitProvSet) { // Check if we need to call the store provider's writethru function. // Note, since the above properties aren't persisted they don't // need to be checked. const DWORD dwStoreProvSetPropertyIndex = rgdwStoreProvSetPropertyIndex[pEle->dwContextType]; PCERT_STORE pProvStore = pEle->pProvStore; PFN_CERT_STORE_PROV_SET_CERT_PROPERTY pfnStoreProvSetProperty; // Use provider store. May be in a collection. UnlockStore(pCacheStore); LockStore(pProvStore); if (dwStoreProvSetPropertyIndex < pProvStore->StoreProvInfo.cStoreProvFunc && NULL != (pfnStoreProvSetProperty = (PFN_CERT_STORE_PROV_SET_CERT_PROPERTY) pProvStore->StoreProvInfo.rgpvStoreProvFunc[ dwStoreProvSetPropertyIndex])) { // Since we can't hold a lock while calling the provider function, // bump the store's provider reference count to inhibit the closing // of the store and freeing of the provider functions. // // When the store is closed, pStore->StoreProvInfo.cStoreProvFunc // is set to 0. AddRefStoreProv(pProvStore); UnlockStore(pProvStore); #if 0 // Slow down the provider while holding the provider reference // count. Sleep(1500); #endif // Note: PFN_CERT_STORE_PROV_SET_CRL_PROPERTY has the same signature // except, PCCRL_CONTEXT replaces the PCCERT_CONTEXT parameter. fResult = pfnStoreProvSetProperty( pProvStore->StoreProvInfo.hStoreProv, ToCertContext(pEle->pEle), dwPropId, dwFlags, pvData); LockStore(pProvStore); ReleaseStoreProv(pProvStore); UnlockStore(pProvStore); LockStore(pCacheStore); if (!fResult && !IS_CERT_HASH_PROP_ID(dwPropId) && !IS_CHAIN_HASH_PROP_ID(dwPropId) && 0 == (dwFlags & CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG)) goto StoreProvSetCertPropertyError; // else // Silently ignore any complaints about setting the // property. } else { UnlockStore(pProvStore); LockStore(pCacheStore); } } if (pvData != NULL) { // First, delete the property DeleteProperty(pCacheEle, dwPropId); if (dwPropId == CERT_KEY_CONTEXT_PROP_ID) { cbEncoded = sizeof(CERT_KEY_CONTEXT); if (NULL == (pbEncoded = (BYTE *) PkiNonzeroAlloc(cbEncoded))) goto OutOfMemory; memcpy(pbEncoded, (BYTE *) pvData, cbEncoded); } else if (dwPropId == CERT_KEY_PROV_INFO_PROP_ID) { if (!AllocAndEncodeKeyProvInfo( (PCRYPT_KEY_PROV_INFO) pvData, &pbEncoded, &cbEncoded )) goto AllocAndEncodeKeyProvInfoError; } else { PCRYPT_DATA_BLOB pDataBlob = (PCRYPT_DATA_BLOB) pvData; cbEncoded = pDataBlob->cbData; if (cbEncoded) { if (NULL == (pbEncoded = (BYTE *) PkiNonzeroAlloc(cbEncoded))) goto OutOfMemory; memcpy(pbEncoded, pDataBlob->pbData, cbEncoded); } } if (NULL == (pPropEle = CreatePropElement( dwPropId, dwFlags, pbEncoded, cbEncoded))) goto CreatePropElementError; AddPropElement(pCacheEle, pPropEle); if (CERT_ARCHIVED_PROP_ID == dwPropId) pCacheEle->dwFlags |= ELEMENT_ARCHIVED_FLAG; } else { // Delete the property DeleteProperty(pCacheEle, dwPropId); if (CERT_ARCHIVED_PROP_ID == dwPropId) pCacheEle->dwFlags &= ~ELEMENT_ARCHIVED_FLAG; } fResult = TRUE; CommonReturn: UnlockStore(pCacheStore); if (fResult && pvData && !fInhibitProvSet && ((pCacheStore->dwFlags & CERT_STORE_UPDATE_KEYID_FLAG) || (pEle->pStore->dwFlags & CERT_STORE_UPDATE_KEYID_FLAG))) SetCryptKeyIdentifierKeyProvInfoProperty( pEle, dwPropId, pvData ); return fResult; ErrorReturn: PkiFree(pbEncoded); fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(StoreProvSetCertPropertyError) TRACE_ERROR(OutOfMemory) TRACE_ERROR(AllocAndEncodeKeyProvInfoError) TRACE_ERROR(CreatePropElementError) } STATIC BOOL AllocAndGetProperty( IN PCONTEXT_ELEMENT pEle, IN DWORD dwPropId, OUT void **ppvData, OUT DWORD *pcbData ) { BOOL fResult; void *pvData = NULL; DWORD cbData; if (!GetProperty( pEle, dwPropId, NULL, // pvData &cbData)) goto GetPropertyError; if (cbData) { if (NULL == (pvData = PkiNonzeroAlloc(cbData))) goto OutOfMemory; if (!GetProperty( pEle, dwPropId, pvData, &cbData)) goto GetPropertyError; } fResult = TRUE; CommonReturn: *ppvData = pvData; *pcbData = cbData; return fResult; ErrorReturn: PkiFree(pvData); pvData = NULL; cbData = 0; fResult = FALSE; goto CommonReturn; TRACE_ERROR(GetPropertyError) TRACE_ERROR(OutOfMemory) } //+------------------------------------------------------------------------- // Get the property for the specified element // // Note the pEle's cache store may be locked on entry by the above // SetProperty for a CERT_KEY_CONTEXT_PROP_ID. //-------------------------------------------------------------------------- STATIC BOOL GetProperty( IN PCONTEXT_ELEMENT pEle, IN DWORD dwPropId, OUT void *pvData, IN OUT DWORD *pcbData ) { PCONTEXT_ELEMENT pCacheEle; PCERT_STORE pCacheStore; PCERT_STORE pProvStore; DWORD cbIn; if (pvData == NULL) cbIn = 0; else cbIn = *pcbData; *pcbData = 0; if (dwPropId == CERT_KEY_PROV_HANDLE_PROP_ID || dwPropId == CERT_KEY_SPEC_PROP_ID) { // These two properties are fields within CERT_KEY_CONTEXT_PROP_ID. BOOL fResult; CERT_KEY_CONTEXT KeyContext; DWORD cbData; BYTE *pbData; cbData = sizeof(KeyContext); fResult = GetProperty( pEle, CERT_KEY_CONTEXT_PROP_ID, &KeyContext, &cbData ); if (fResult && sizeof(KeyContext) != cbData) { SetLastError((DWORD) CRYPT_E_NOT_FOUND); fResult = FALSE; } if (dwPropId == CERT_KEY_PROV_HANDLE_PROP_ID) { cbData = sizeof(HCRYPTPROV); pbData = (BYTE *) &KeyContext.hCryptProv; } else { if (!fResult) { // Try to get the dwKeySpec from the CERT_KEY_PROV_INFO_PROP_ID PCRYPT_KEY_PROV_INFO pInfo; if (fResult = AllocAndGetProperty( pEle, CERT_KEY_PROV_INFO_PROP_ID, (void **) &pInfo, &cbData)) { KeyContext.dwKeySpec = pInfo->dwKeySpec; PkiFree(pInfo); } } cbData = sizeof(DWORD); pbData = (BYTE *) &KeyContext.dwKeySpec; } if (fResult) { *pcbData = cbData; if (cbIn < cbData) { if (pvData) { SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; } } else if (cbData) memcpy((BYTE *) pvData, pbData, cbData); } return fResult; } else if (dwPropId == CERT_ACCESS_STATE_PROP_ID) { DWORD dwAccessStateFlags; pProvStore = pEle->pProvStore; if ((pProvStore->dwFlags & CERT_STORE_READONLY_FLAG) || (pProvStore->StoreProvInfo.dwStoreProvFlags & CERT_STORE_PROV_NO_PERSIST_FLAG)) dwAccessStateFlags = 0; else dwAccessStateFlags = CERT_ACCESS_STATE_WRITE_PERSIST_FLAG; if ((pEle->pStore->StoreProvInfo.dwStoreProvFlags & CERT_STORE_PROV_SYSTEM_STORE_FLAG) || (pProvStore->StoreProvInfo.dwStoreProvFlags & CERT_STORE_PROV_SYSTEM_STORE_FLAG)) dwAccessStateFlags |= CERT_ACCESS_STATE_SYSTEM_STORE_FLAG; if ((pEle->pStore->StoreProvInfo.dwStoreProvFlags & CERT_STORE_PROV_LM_SYSTEM_STORE_FLAG) || (pProvStore->StoreProvInfo.dwStoreProvFlags & CERT_STORE_PROV_LM_SYSTEM_STORE_FLAG)) dwAccessStateFlags |= CERT_ACCESS_STATE_LM_SYSTEM_STORE_FLAG; *pcbData = sizeof(DWORD); if (cbIn < sizeof(DWORD)) { if (pvData) { SetLastError((DWORD) ERROR_MORE_DATA); return FALSE; } } else *((DWORD * ) pvData) = dwAccessStateFlags; return TRUE; } if (NULL == (pCacheEle = GetCacheElement(pEle))) return FALSE; pCacheStore = pCacheEle->pStore; LockStore(pCacheStore); PPROP_ELEMENT pPropEle = FindPropElement(pCacheEle, dwPropId); if (pPropEle) { BOOL fResult; DWORD cbData = pPropEle->cbData; if (dwPropId == CERT_KEY_PROV_INFO_PROP_ID) { *pcbData = cbIn; fResult = DecodeKeyProvInfo( (PSERIALIZED_KEY_PROV_INFO) pPropEle->pbData, cbData, (PCRYPT_KEY_PROV_INFO) pvData, pcbData ); } else { fResult = TRUE; if (cbIn < cbData) { if (pvData) { SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; } } else if (cbData) memcpy((BYTE *) pvData, pPropEle->pbData, cbData); *pcbData = cbData; } UnlockStore(pCacheStore); return fResult; } else UnlockStore(pCacheStore); // We're here with property not found and store unlocked. // For CERT_*_HASH_PROP_ID: compute its hash and do a SetProperty // Also, compute the MD5 hash of the public key bits. if (IS_CERT_HASH_PROP_ID(dwPropId) || ((CERT_STORE_CERTIFICATE_CONTEXT - 1) == pEle->dwContextType && (CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID == dwPropId || CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID == dwPropId || CERT_SUBJECT_NAME_MD5_HASH_PROP_ID == dwPropId))) { BOOL fResult; PCERT_STORE pEleStore; BYTE *pbEncoded; DWORD cbEncoded; BYTE Hash[MAX_HASH_LEN]; CRYPT_DATA_BLOB HashBlob; BYTE *pbAlloc = NULL; switch (dwPropId) { case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID: { PCRYPT_BIT_BLOB pPublicKey; assert((CERT_STORE_CERTIFICATE_CONTEXT - 1) == pEle->dwContextType); pPublicKey = &(ToCertContext(pEle)->pCertInfo->SubjectPublicKeyInfo.PublicKey); pbEncoded = pPublicKey->pbData; cbEncoded = pPublicKey->cbData; } break; case CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID: { PCERT_NAME_BLOB pIssuer; PCRYPT_INTEGER_BLOB pSerialNumber; assert((CERT_STORE_CERTIFICATE_CONTEXT - 1) == pEle->dwContextType); pIssuer = &(ToCertContext(pEle)->pCertInfo->Issuer); pSerialNumber = &(ToCertContext(pEle)->pCertInfo->SerialNumber); cbEncoded = pIssuer->cbData + pSerialNumber->cbData; if (0 == cbEncoded) pbAlloc = NULL; else { if (NULL == (pbAlloc = (BYTE *) PkiNonzeroAlloc( cbEncoded))) return FALSE; if (pIssuer->cbData) memcpy(pbAlloc, pIssuer->pbData, pIssuer->cbData); if (pSerialNumber->cbData) memcpy(pbAlloc + pIssuer->cbData, pSerialNumber->pbData, pSerialNumber->cbData); } pbEncoded = pbAlloc; } break; case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID: { PCERT_NAME_BLOB pSubject; assert((CERT_STORE_CERTIFICATE_CONTEXT - 1) == pEle->dwContextType); pSubject = &(ToCertContext(pEle)->pCertInfo->Subject); pbEncoded = pSubject->pbData; cbEncoded = pSubject->cbData; } break; default: GetContextEncodedInfo( pEle, &pbEncoded, &cbEncoded ); } pEleStore = pEle->pStore; HashBlob.pbData = Hash; HashBlob.cbData = sizeof(Hash); if (CERT_SIGNATURE_HASH_PROP_ID == dwPropId) fResult = CryptHashToBeSigned( 0, // hCryptProv GetContextEncodingType(pEle), pbEncoded, cbEncoded, Hash, &HashBlob.cbData); else fResult = CryptHashCertificate( 0, // hCryptProv dwPropId == CERT_SHA1_HASH_PROP_ID ? CALG_SHA1 : CALG_MD5, 0, //dwFlags pbEncoded, cbEncoded, Hash, &HashBlob.cbData); if (pbAlloc) PkiFree(pbAlloc); if (!fResult) { assert(HashBlob.cbData <= MAX_HASH_LEN); return FALSE; } assert(HashBlob.cbData); if (HashBlob.cbData == 0) return FALSE; if (!SetProperty( pEle, dwPropId, 0, // dwFlags &HashBlob )) return FALSE; *pcbData = cbIn; return GetProperty( pEle, dwPropId, pvData, pcbData); } else if (CERT_KEY_IDENTIFIER_PROP_ID == dwPropId) { *pcbData = cbIn; return GetKeyIdProperty( pEle, dwPropId, pvData, pcbData); } // We're here with property not found and not a hash or KeyId property // Since the cache store may be locked when called from SetProperty for // a CERT_KEY_CONTEXT_PROP_ID and since this property isn't persisted, // don't look in the external store for this property. pProvStore = pEle->pProvStore; if (STORE_TYPE_EXTERNAL == pProvStore->dwStoreType && CERT_KEY_CONTEXT_PROP_ID != dwPropId) { // Check if provider supports getting a non-cached property const DWORD dwStoreProvGetPropertyIndex = rgdwStoreProvGetPropertyIndex[pEle->dwContextType]; PFN_CERT_STORE_PROV_GET_CERT_PROPERTY pfnStoreProvGetProperty; LockStore(pProvStore); if (dwStoreProvGetPropertyIndex < pProvStore->StoreProvInfo.cStoreProvFunc && NULL != (pfnStoreProvGetProperty = (PFN_CERT_STORE_PROV_GET_CERT_PROPERTY) pProvStore->StoreProvInfo.rgpvStoreProvFunc[ dwStoreProvGetPropertyIndex])) { BOOL fResult; // Since we can't hold a lock while calling the provider // function, bump the store's provider reference count // to inhibit the closing of the store and freeing of // the provider functions. // // When the store is closed, // pProvStore->StoreProvInfo.cStoreProvFunc is set to 0. AddRefStoreProv(pProvStore); UnlockStore(pProvStore); *pcbData = cbIn; fResult = pfnStoreProvGetProperty( pProvStore->StoreProvInfo.hStoreProv, ToCertContext(pEle->pEle), dwPropId, 0, // dwFlags pvData, pcbData ); LockStore(pProvStore); ReleaseStoreProv(pProvStore); UnlockStore(pProvStore); return fResult; } else UnlockStore(pProvStore); } SetLastError((DWORD) CRYPT_E_NOT_FOUND); return FALSE; } //+------------------------------------------------------------------------- // Serialize a Property //-------------------------------------------------------------------------- STATIC BOOL SerializeProperty( IN HANDLE h, IN PFNWRITE pfn, IN PCONTEXT_ELEMENT pEle ) { BOOL fResult; PCONTEXT_ELEMENT pCacheEle; PCERT_STORE pCacheStore; PPROP_ELEMENT pPropEle; if (NULL == (pCacheEle = GetCacheElement(pEle))) return FALSE; pCacheStore = pCacheEle->pStore; LockStore(pCacheStore); fResult = TRUE; for (pPropEle = pCacheEle->Cache.pPropHead; pPropEle; pPropEle = pPropEle->pNext) { if (pPropEle->dwPropId != CERT_KEY_CONTEXT_PROP_ID) { if(!WriteStoreElement( h, pfn, GetContextEncodingType(pCacheEle), pPropEle->dwPropId, pPropEle->pbData, pPropEle->cbData )) { fResult = FALSE; break; } } } UnlockStore(pCacheStore); return(fResult); } //+------------------------------------------------------------------------- // Get the first or next PropId for the specified element. // // Only enumerates cached properties. Doesn't try to enumerate any external // properties. // // Set dwPropId = 0, to get the first. Returns 0, if no more properties. //-------------------------------------------------------------------------- STATIC DWORD EnumProperties( IN PCONTEXT_ELEMENT pEle, IN DWORD dwPropId ) { PPROP_ELEMENT pPropEle; PCONTEXT_ELEMENT pCacheEle; PCERT_STORE pCacheStore; if (NULL == (pCacheEle = GetCacheElement(pEle))) return 0; pCacheStore = pCacheEle->pStore; LockStore(pCacheStore); if (0 == dwPropId) pPropEle = pCacheEle->Cache.pPropHead; else { pPropEle = FindPropElement(pCacheEle, dwPropId); if (pPropEle) pPropEle = pPropEle->pNext; } if (pPropEle) dwPropId = pPropEle->dwPropId; else dwPropId = 0; UnlockStore(pCacheStore); return dwPropId; } STATIC BOOL CopyProperties( IN PCONTEXT_ELEMENT pSrcEle, IN PCONTEXT_ELEMENT pDstEle, IN DWORD dwFlags ) { BOOL fResult; DWORD dwPropId; if (dwFlags & COPY_PROPERTY_SYNC_FLAG) { // Delete any properties from the Dst element that don't exist // in the Src element. DWORD dwNextPropId; dwNextPropId = EnumProperties(pDstEle, 0); while (dwNextPropId) { PPROP_ELEMENT pPropEle; PCONTEXT_ELEMENT pSrcCacheEle; PCERT_STORE pSrcCacheStore; PCONTEXT_ELEMENT pDstCacheEle; PCERT_STORE pDstCacheStore; dwPropId = dwNextPropId; dwNextPropId = EnumProperties(pDstEle, dwNextPropId); // Don't delete hCryptProv or KeySpec or hash properties if (CERT_KEY_CONTEXT_PROP_ID == dwPropId || IS_CERT_HASH_PROP_ID(dwPropId) || IS_CHAIN_HASH_PROP_ID(dwPropId)) continue; #ifdef CMS_PKCS7 if (CERT_PUBKEY_ALG_PARA_PROP_ID == dwPropId) continue; #endif // CMS_PKCS7 if (NULL == (pSrcCacheEle = GetCacheElement(pSrcEle))) continue; pSrcCacheStore = pSrcCacheEle->pStore; // Don't delete if the src also has the property LockStore(pSrcCacheStore); pPropEle = FindPropElement(pSrcCacheEle, dwPropId); UnlockStore(pSrcCacheStore); if (pPropEle) continue; // Don't delete any non persisted properties if (NULL == (pDstCacheEle = GetCacheElement(pDstEle))) continue; pDstCacheStore = pDstCacheEle->pStore; LockStore(pDstCacheStore); pPropEle = FindPropElement(pDstCacheEle, dwPropId); UnlockStore(pDstCacheStore); if (NULL == pPropEle || 0 != (pPropEle->dwFlags & CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAG)) continue; SetProperty( pDstEle, dwPropId, 0, // dwFlags NULL, // NULL deletes dwFlags & COPY_PROPERTY_INHIBIT_PROV_SET_FLAG // fInhibitProvSet ); } } fResult = TRUE; dwPropId = 0; while (dwPropId = EnumProperties(pSrcEle, dwPropId)) { void *pvData; DWORD cbData; // Don't copy hCryptProv or KeySpec if (CERT_KEY_CONTEXT_PROP_ID == dwPropId) continue; if (dwFlags & COPY_PROPERTY_USE_EXISTING_FLAG) { PPROP_ELEMENT pPropEle; PCONTEXT_ELEMENT pDstCacheEle; PCERT_STORE pDstCacheStore; // For existing, don't copy any hash properties if (IS_CERT_HASH_PROP_ID(dwPropId) || IS_CHAIN_HASH_PROP_ID(dwPropId)) continue; if (NULL == (pDstCacheEle = GetCacheElement(pDstEle))) continue; pDstCacheStore = pDstCacheEle->pStore; // Don't copy if the destination already has the property LockStore(pDstCacheStore); pPropEle = FindPropElement(pDstCacheEle, dwPropId); UnlockStore(pDstCacheStore); if (pPropEle) continue; } if (!AllocAndGetProperty( pSrcEle, dwPropId, &pvData, &cbData)) { if (CRYPT_E_NOT_FOUND == GetLastError()) { // Its been deleted after we did the Enum. Start over // from the beginning. dwPropId = 0; continue; } else { fResult = FALSE; break; } } else { CRYPT_DATA_BLOB DataBlob; void *pvSetData; if (CERT_KEY_PROV_INFO_PROP_ID == dwPropId) pvSetData = pvData; else { DataBlob.pbData = (BYTE *) pvData; DataBlob.cbData = cbData; pvSetData = &DataBlob; } fResult = SetProperty( pDstEle, dwPropId, 0, // dwFlags pvSetData, dwFlags & COPY_PROPERTY_INHIBIT_PROV_SET_FLAG // fInhibitProvSet ); if (pvData) PkiFree(pvData); if (!fResult) break; } } return fResult; } //+------------------------------------------------------------------------- // Get or set the caller properties for a store or KeyId element. // // Upon entry/exit, properties are locked by caller. //-------------------------------------------------------------------------- STATIC BOOL GetCallerProperty( IN PPROP_ELEMENT pPropHead, IN DWORD dwPropId, BOOL fAlloc, OUT void *pvData, IN OUT DWORD *pcbData ) { BOOL fResult; PPROP_ELEMENT pPropEle; DWORD cbData; void *pvDstData = NULL; DWORD cbDstData; if (NULL == (pPropEle = FindPropElement(pPropHead, dwPropId))) goto PropertyNotFound; if (dwPropId == CERT_KEY_CONTEXT_PROP_ID || dwPropId == CERT_KEY_PROV_HANDLE_PROP_ID) goto InvalidPropId; cbData = pPropEle->cbData; if (fAlloc) { if (dwPropId == CERT_KEY_PROV_INFO_PROP_ID) { if (!DecodeKeyProvInfo( (PSERIALIZED_KEY_PROV_INFO) pPropEle->pbData, cbData, NULL, // pInfo &cbDstData )) goto DecodeKeyProvInfoError; } else cbDstData = cbData; if (cbDstData) { if (NULL == (pvDstData = PkiDefaultCryptAlloc(cbDstData))) goto OutOfMemory; } *((void **) pvData) = pvDstData; } else { pvDstData = pvData; if (NULL == pvData) cbDstData = 0; else cbDstData = *pcbData; } if (dwPropId == CERT_KEY_PROV_INFO_PROP_ID) { fResult = DecodeKeyProvInfo( (PSERIALIZED_KEY_PROV_INFO) pPropEle->pbData, cbData, (PCRYPT_KEY_PROV_INFO) pvDstData, &cbDstData ); } else { fResult = TRUE; if (pvDstData) { if (cbDstData < cbData) { SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; } else if (cbData) { memcpy((BYTE *) pvDstData, pPropEle->pbData, cbData); } } cbDstData = cbData; } if (!fResult && fAlloc) goto UnexpectedError; CommonReturn: *pcbData = cbDstData; return fResult; ErrorReturn: if (fAlloc) { *((void **) pvData) = NULL; PkiDefaultCryptFree(pvDstData); } cbDstData = 0; fResult = FALSE; goto CommonReturn; SET_ERROR(PropertyNotFound, CRYPT_E_NOT_FOUND) SET_ERROR(InvalidPropId, E_INVALIDARG) TRACE_ERROR(OutOfMemory) TRACE_ERROR(DecodeKeyProvInfoError) SET_ERROR(UnexpectedError, E_UNEXPECTED) } BOOL SetCallerProperty( IN OUT PPROP_ELEMENT *ppPropHead, IN DWORD dwPropId, IN DWORD dwFlags, IN const void *pvData ) { BOOL fResult; if (pvData != NULL) { DWORD cbEncoded = 0; BYTE *pbEncoded = NULL; PPROP_ELEMENT pPropEle; // First, delete the property DeleteProperty(ppPropHead, dwPropId); if (dwPropId == CERT_KEY_CONTEXT_PROP_ID || dwPropId == CERT_KEY_PROV_HANDLE_PROP_ID) { goto InvalidPropId; } else if (dwPropId == CERT_KEY_PROV_INFO_PROP_ID) { if (!AllocAndEncodeKeyProvInfo( (PCRYPT_KEY_PROV_INFO) pvData, &pbEncoded, &cbEncoded )) goto AllocAndEncodeKeyProvInfoError; } else { PCRYPT_DATA_BLOB pDataBlob = (PCRYPT_DATA_BLOB) pvData; cbEncoded = pDataBlob->cbData; if (cbEncoded) { if (NULL == (pbEncoded = (BYTE *) PkiNonzeroAlloc(cbEncoded))) goto OutOfMemory; memcpy(pbEncoded, pDataBlob->pbData, cbEncoded); } } if (NULL == (pPropEle = CreatePropElement( dwPropId, dwFlags, pbEncoded, cbEncoded))) { PkiFree(pbEncoded); goto CreatePropElementError; } AddPropElement(ppPropHead, pPropEle); } else // Delete the property DeleteProperty(ppPropHead, dwPropId); fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidPropId, E_INVALIDARG) TRACE_ERROR(AllocAndEncodeKeyProvInfoError) TRACE_ERROR(OutOfMemory) TRACE_ERROR(CreatePropElementError) } //+------------------------------------------------------------------------- // CRYPT_KEY_PROV_INFO: Encode and Decode Functions //-------------------------------------------------------------------------- STATIC BOOL AllocAndEncodeKeyProvInfo( IN PCRYPT_KEY_PROV_INFO pKeyProvInfo, OUT BYTE **ppbEncoded, OUT DWORD *pcbEncoded ) { BYTE *pbEncoded; DWORD cbEncoded; DWORD cbContainerName; DWORD cbProvName; PCRYPT_KEY_PROV_PARAM pParam; PSERIALIZED_KEY_PROV_INFO pDstInfo; DWORD Off; DWORD cParam; // Get overall length cbEncoded = sizeof(SERIALIZED_KEY_PROV_INFO) + pKeyProvInfo->cProvParam * sizeof(SERIALIZED_KEY_PROV_PARAM); for (cParam = pKeyProvInfo->cProvParam, pParam = pKeyProvInfo->rgProvParam; cParam > 0; cParam--, pParam++) { if (pParam->cbData) cbEncoded += ENCODE_LEN_ALIGN(pParam->cbData); } if (pKeyProvInfo->pwszContainerName) { cbContainerName = (wcslen(pKeyProvInfo->pwszContainerName) + 1) * sizeof(WCHAR); cbEncoded += ENCODE_LEN_ALIGN(cbContainerName); } else cbContainerName = 0; if (pKeyProvInfo->pwszProvName) { cbProvName = (wcslen(pKeyProvInfo->pwszProvName) + 1) * sizeof(WCHAR); cbEncoded += ENCODE_LEN_ALIGN(cbProvName); } else cbProvName = 0; assert(cbEncoded <= MAX_FILE_ELEMENT_DATA_LEN); // Allocate pbEncoded = (BYTE *) PkiZeroAlloc(cbEncoded); if (pbEncoded == NULL) { *ppbEncoded = NULL; *pcbEncoded = 0; return FALSE; } Off = sizeof(SERIALIZED_KEY_PROV_INFO); pDstInfo = (PSERIALIZED_KEY_PROV_INFO) pbEncoded; // pDstInfo->offwszContainerName // pDstInfo->offwszProvName; pDstInfo->dwProvType = pKeyProvInfo->dwProvType; pDstInfo->dwFlags = pKeyProvInfo->dwFlags; pDstInfo->cProvParam = pKeyProvInfo->cProvParam; // pDstInfo->offrgProvParam; pDstInfo->dwKeySpec = pKeyProvInfo->dwKeySpec; if (pKeyProvInfo->cProvParam) { PSERIALIZED_KEY_PROV_PARAM pDstParam; pDstParam = (PSERIALIZED_KEY_PROV_PARAM) (pbEncoded + Off); pDstInfo->offrgProvParam = Off; Off += pKeyProvInfo->cProvParam * sizeof(SERIALIZED_KEY_PROV_PARAM); for (cParam = pKeyProvInfo->cProvParam, pParam = pKeyProvInfo->rgProvParam; cParam > 0; cParam--, pParam++, pDstParam++) { pDstParam->dwParam = pParam->dwParam; // pDstParam->offbData pDstParam->cbData = pParam->cbData; pDstParam->dwFlags = pParam->dwFlags; if (pParam->cbData) { memcpy(pbEncoded + Off, pParam->pbData, pParam->cbData); pDstParam->offbData = Off; Off += ENCODE_LEN_ALIGN(pParam->cbData); } } } if (cbContainerName) { memcpy(pbEncoded + Off, (BYTE *) pKeyProvInfo->pwszContainerName, cbContainerName); pDstInfo->offwszContainerName = Off; Off += ENCODE_LEN_ALIGN(cbContainerName); } if (cbProvName) { memcpy(pbEncoded + Off, (BYTE *) pKeyProvInfo->pwszProvName, cbProvName); pDstInfo->offwszProvName = Off; Off += ENCODE_LEN_ALIGN(cbProvName); } assert(Off == cbEncoded); *ppbEncoded = pbEncoded; *pcbEncoded = cbEncoded; return TRUE; } STATIC BOOL DecodeKeyProvInfoString( IN BYTE *pbSerialized, IN DWORD cbSerialized, IN DWORD off, OUT LPWSTR *ppwsz, IN OUT BYTE **ppbExtra, IN OUT LONG *plRemain ) { BOOL fResult; LONG lRemain = *plRemain; LPWSTR pwszDst = (LPWSTR) *ppbExtra; if (0 != off) { LPWSTR pwszSrc = (LPWSTR) (pbSerialized + off); LPWSTR pwszEnd = (LPWSTR) (pbSerialized + cbSerialized); if (0 <= lRemain) *ppwsz = pwszDst; while (TRUE) { if (pwszSrc + 1 > pwszEnd) goto InvalidData; lRemain -= sizeof(WCHAR); if (0 <= lRemain) *pwszDst++ = *pwszSrc; if (L'\0' == *pwszSrc++) break; } } else if (0 <= lRemain) *ppwsz = NULL; fResult = TRUE; CommonReturn: *ppbExtra = (BYTE *) pwszDst; *plRemain = lRemain; return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidData, ERROR_INVALID_DATA) } STATIC BOOL DecodeKeyProvInfo( IN PSERIALIZED_KEY_PROV_INFO pSerializedInfo, IN DWORD cbSerialized, OUT PCRYPT_KEY_PROV_INFO pInfo, OUT DWORD *pcbInfo ) { BOOL fResult; DWORD cParam; DWORD cbInfo; BYTE *pbSerialized; LONG lRemain; BYTE *pbExtra; DWORD dwExceptionCode; __try { if (sizeof(SERIALIZED_KEY_PROV_INFO) > cbSerialized) goto InvalidData; if (NULL == pInfo) cbInfo = 0; else cbInfo = *pcbInfo; lRemain = cbInfo; cParam = pSerializedInfo->cProvParam; pbSerialized = (BYTE *) pSerializedInfo; lRemain -= sizeof(CRYPT_KEY_PROV_INFO); if (0 <= lRemain) { pbExtra = (BYTE *) pInfo + sizeof(CRYPT_KEY_PROV_INFO); memset(pInfo, 0, sizeof(CRYPT_KEY_PROV_INFO)); // pInfo->pwszContainerName // pInfo->pwszProvName pInfo->dwProvType = pSerializedInfo->dwProvType; pInfo->dwFlags = pSerializedInfo->dwFlags; pInfo->cProvParam = cParam; // pInfo->rgProvParam pInfo->dwKeySpec = pSerializedInfo->dwKeySpec; } else pbExtra = NULL; if (0 < cParam) { DWORD off; PCRYPT_KEY_PROV_PARAM pParam; PSERIALIZED_KEY_PROV_PARAM pSerializedParam; off = pSerializedInfo->offrgProvParam; if (MAX_PROV_PARAM < cParam || off > cbSerialized || (off + cParam * sizeof(SERIALIZED_KEY_PROV_PARAM)) > cbSerialized) goto InvalidData; lRemain -= cParam * sizeof(CRYPT_KEY_PROV_PARAM); if (0 <= lRemain) { pParam = (PCRYPT_KEY_PROV_PARAM) pbExtra; pInfo->rgProvParam = pParam; pbExtra += cParam * sizeof(CRYPT_KEY_PROV_PARAM); } else pParam = NULL; pSerializedParam = (PSERIALIZED_KEY_PROV_PARAM) (pbSerialized + off); for (; 0 < cParam; cParam--, pParam++, pSerializedParam++) { DWORD cbParamData = pSerializedParam->cbData; if (0 <= lRemain) { pParam->dwParam = pSerializedParam->dwParam; pParam->pbData = NULL; pParam->cbData = cbParamData; pParam->dwFlags = pSerializedParam->dwFlags; } if (0 < cbParamData) { LONG lAlignExtra; off = pSerializedParam->offbData; if (MAX_PROV_PARAM_CBDATA < cbParamData || off > cbSerialized || (off + cbParamData) > cbSerialized) goto InvalidData; lAlignExtra = ENCODE_LEN_ALIGN(cbParamData); lRemain -= lAlignExtra; if (0 <= lRemain) { pParam->pbData = pbExtra; memcpy(pbExtra, pbSerialized + off, cbParamData); pbExtra += lAlignExtra; } } } } if (!DecodeKeyProvInfoString( pbSerialized, cbSerialized, pSerializedInfo->offwszContainerName, &pInfo->pwszContainerName, &pbExtra, &lRemain )) goto InvalidData; if (!DecodeKeyProvInfoString( pbSerialized, cbSerialized, pSerializedInfo->offwszProvName, &pInfo->pwszProvName, &pbExtra, &lRemain )) goto InvalidData; if (0 > lRemain && pInfo) { SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; } else fResult = TRUE; cbInfo = (DWORD) ((LONG) cbInfo - lRemain); } __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; } CommonReturn: *pcbInfo = cbInfo; return fResult; ErrorReturn: cbInfo = 0; fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidData, ERROR_INVALID_DATA) SET_ERROR_VAR(ExceptionError, dwExceptionCode) } //+------------------------------------------------------------------------- // Creates a CTL entry whose attributes are the certificate context's // properties. // // The SubjectIdentifier in the CTL entry is the SHA1 hash of the certificate. // // The certificate properties are added as attributes. The property attribute // OID is the decimal PROP_ID preceded by szOID_CERT_PROP_ID_PREFIX. Each // property value is copied as a single attribute value. // // Any additional attributes to be included in the CTL entry can be passed // in via the cOptAttr and pOptAttr parameters. // // CTL_ENTRY_FROM_PROP_CHAIN_FLAG can be set in dwFlags, to force the // inclusion of the chain building hash properties as attributes. //-------------------------------------------------------------------------- BOOL WINAPI CertCreateCTLEntryFromCertificateContextProperties( IN PCCERT_CONTEXT pCertContext, IN DWORD cOptAttr, IN OPTIONAL PCRYPT_ATTRIBUTE rgOptAttr, IN DWORD dwFlags, IN OPTIONAL void *pvReserved, OUT OPTIONAL PCTL_ENTRY pCtlEntry, IN OUT DWORD *pcbCtlEntry ) { BOOL fResult; DWORD cbCtlEntry; LONG lRemainExtra; BYTE *pbExtra; DWORD cbData; DWORD dwPropId; DWORD cProp; DWORD cAttr; DWORD cValue; DWORD cOptValue; DWORD iAttr; PCRYPT_ATTRIBUTE pAttr; PCRYPT_ATTR_BLOB pValue; DWORD rgdwChainHashPropId[] = { CERT_KEY_IDENTIFIER_PROP_ID, // CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID, // CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID, CERT_SUBJECT_NAME_MD5_HASH_PROP_ID, }; #define CHAIN_HASH_PROP_CNT (sizeof(rgdwChainHashPropId) / \ sizeof(rgdwChainHashPropId[0])) if (NULL == pCtlEntry) { cbCtlEntry = 0; lRemainExtra = 0; } else { cbCtlEntry = *pcbCtlEntry; lRemainExtra = (LONG) cbCtlEntry; } // Ensure the certificate has the SHA1 hash property if (!CertGetCertificateContextProperty( pCertContext, CERT_SHA1_HASH_PROP_ID, NULL, // pvData &cbData ) || SHA1_HASH_LEN != cbData) goto GetSha1HashPropError; if (dwFlags & CTL_ENTRY_FROM_PROP_CHAIN_FLAG) { DWORD i; // Ensure the certificate has all of the properties needed for // chain building for (i = 0; i < CHAIN_HASH_PROP_CNT; i++) { if (!CertGetCertificateContextProperty( pCertContext, rgdwChainHashPropId[i], NULL, // pvData &cbData )) goto GetChainHashPropError; } } // Get the property count cProp = 0; dwPropId = 0; while (dwPropId = CertEnumCertificateContextProperties( pCertContext, dwPropId)) { // We won't copy the hCryptProv, KeySpec or SHA1 hash properties to // the attributes if (CERT_KEY_CONTEXT_PROP_ID == dwPropId || CERT_SHA1_HASH_PROP_ID == dwPropId) continue; cProp++; } // Get the optional value count cOptValue = 0; for (iAttr = 0; iAttr < cOptAttr; iAttr++) { PCRYPT_ATTRIBUTE pOptAttr = &rgOptAttr[iAttr]; cOptValue += pOptAttr->cValue; } // Calculate total attribute count. One attribute per property. Include // optional attributes passed in. cAttr = cOptAttr + cProp; // Calculate total value count. One value per property. Include optional // attribute values passed in. cValue = cOptValue + cProp; // Allocate memory for the CTL_ENTRY. array of attributes, all of the // attribute value blobs and the SubjectIdentifier hash. lRemainExtra -= sizeof(CTL_ENTRY) + cAttr * sizeof(CRYPT_ATTRIBUTE) + cValue * sizeof(CRYPT_ATTR_BLOB) + SHA1_HASH_LEN; if (0 <= lRemainExtra) { // Initialize the attribute, value and byte pointers pAttr = (PCRYPT_ATTRIBUTE) &pCtlEntry[1]; pValue = (PCRYPT_ATTR_BLOB) &pAttr[cAttr]; pbExtra = (BYTE *) &pValue[cValue]; // Update the CTL_ENTRY fields pCtlEntry->SubjectIdentifier.cbData = SHA1_HASH_LEN; pCtlEntry->SubjectIdentifier.pbData = pbExtra; if (!CertGetCertificateContextProperty( pCertContext, CERT_SHA1_HASH_PROP_ID, pCtlEntry->SubjectIdentifier.pbData, &pCtlEntry->SubjectIdentifier.cbData ) || SHA1_HASH_LEN != pCtlEntry->SubjectIdentifier.cbData) goto GetSha1HashPropError; pbExtra += SHA1_HASH_LEN; pCtlEntry->cAttribute = cAttr; pCtlEntry->rgAttribute = pAttr; } else { pAttr = NULL; pValue = NULL; pbExtra = NULL; } // Copy over the optional attributes and attribute values for (iAttr = 0; iAttr < cOptAttr; iAttr++, pAttr++) { PCRYPT_ATTRIBUTE pOptAttr = &rgOptAttr[iAttr]; DWORD cbOID = strlen(pOptAttr->pszObjId) + 1; DWORD iValue; lRemainExtra -= cbOID; if (0 <= lRemainExtra) { memcpy(pbExtra, pOptAttr->pszObjId, cbOID); pAttr->pszObjId = (LPSTR) pbExtra; pbExtra += cbOID; pAttr->cValue = pOptAttr->cValue; pAttr->rgValue = pValue; } for (iValue = 0; iValue < pOptAttr->cValue; iValue++, pValue++) { PCRYPT_ATTR_BLOB pOptValue = &pOptAttr->rgValue[iValue]; assert(0 < cOptValue); if (0 == cOptValue) goto UnexpectedError; cOptValue--; lRemainExtra -= pOptValue->cbData; if (0 <= lRemainExtra) { pValue->cbData = pOptValue->cbData; pValue->pbData = pbExtra; if (0 < pValue->cbData) memcpy(pValue->pbData, pOptValue->pbData, pValue->cbData); pbExtra += pValue->cbData; } } } assert(0 == cOptValue); if (0 != cOptValue) goto UnexpectedError; // Iterate through the properties and create an attribute and attribute // value for each dwPropId = 0; while (dwPropId = CertEnumCertificateContextProperties( pCertContext, dwPropId)) { CRYPT_DATA_BLOB OctetBlob; BYTE *pbEncoded = NULL; DWORD cbEncoded; char szPropId[33]; DWORD cbPrefixOID; DWORD cbPropOID; DWORD cbOID; // We won't copy the hCryptProv, KeySpec or SHA1 hash properties to // the attributes if (CERT_KEY_CONTEXT_PROP_ID == dwPropId || CERT_SHA1_HASH_PROP_ID == dwPropId) continue; assert(0 < cProp); if (0 == cProp) goto UnexpectedError; cProp--; OctetBlob.cbData = 0; OctetBlob.pbData = NULL; if (!CertGetCertificateContextProperty( pCertContext, dwPropId, NULL, // pvData &OctetBlob.cbData )) goto GetPropError; if (OctetBlob.cbData) { if (NULL == (OctetBlob.pbData = (BYTE *) PkiNonzeroAlloc(OctetBlob.cbData))) goto OutOfMemory; if (!CertGetCertificateContextProperty( pCertContext, dwPropId, OctetBlob.pbData, &OctetBlob.cbData )) { PkiFree(OctetBlob.pbData); goto GetPropError; } if (CERT_KEY_PROV_INFO_PROP_ID == dwPropId) { // Need to serialize the KeyProvInfo data structure BYTE *pbEncodedKeyProvInfo; DWORD cbEncodedKeyProvInfo; fResult = AllocAndEncodeKeyProvInfo( (PCRYPT_KEY_PROV_INFO) OctetBlob.pbData, &pbEncodedKeyProvInfo, &cbEncodedKeyProvInfo ); PkiFree(OctetBlob.pbData); if (!fResult) goto SerializeKeyProvInfoError; OctetBlob.pbData = pbEncodedKeyProvInfo; OctetBlob.cbData = cbEncodedKeyProvInfo; } } // Encode the property as an octet string fResult = CryptEncodeObjectEx( pCertContext->dwCertEncodingType, X509_OCTET_STRING, &OctetBlob, CRYPT_ENCODE_ALLOC_FLAG, &PkiEncodePara, (void *) &pbEncoded, &cbEncoded ); PkiFree(OctetBlob.pbData); if (!fResult) goto EncodeError; // Convert PropId to OID _ltoa(dwPropId, szPropId, 10); cbPropOID = strlen(szPropId) + 1; cbPrefixOID = strlen(szOID_CERT_PROP_ID_PREFIX); // Total length of attribute OID cbOID = cbPrefixOID + cbPropOID; lRemainExtra -= cbOID + cbEncoded; if (0 <= lRemainExtra) { // Update the attribute and value pAttr->pszObjId = (LPSTR) pbExtra; memcpy(pbExtra, szOID_CERT_PROP_ID_PREFIX, cbPrefixOID); memcpy(pbExtra + cbPrefixOID, szPropId, cbPropOID); pbExtra += cbOID; assert(0 != cbEncoded); pAttr->cValue = 1; pAttr->rgValue = pValue; pValue->cbData = cbEncoded; pValue->pbData = pbExtra; memcpy(pbExtra, pbEncoded, cbEncoded); pbExtra += cbEncoded; pAttr++; pValue++; } PkiFree(pbEncoded); } assert(0 == cProp); if (0 != cProp) goto UnexpectedError; if (0 <= lRemainExtra) { cbCtlEntry = cbCtlEntry - (DWORD) lRemainExtra; } else { cbCtlEntry = cbCtlEntry + (DWORD) -lRemainExtra; if (pCtlEntry) { SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; goto CommonReturn; } } fResult = TRUE; CommonReturn: *pcbCtlEntry = cbCtlEntry; return fResult; ErrorReturn: cbCtlEntry = 0; fResult = FALSE; goto CommonReturn; TRACE_ERROR(GetSha1HashPropError) TRACE_ERROR(GetChainHashPropError) SET_ERROR(UnexpectedError, E_UNEXPECTED) TRACE_ERROR(GetPropError) TRACE_ERROR(OutOfMemory) TRACE_ERROR(SerializeKeyProvInfoError) TRACE_ERROR(EncodeError) } //+------------------------------------------------------------------------- // Sets properties on the certificate context using the attributes in // the CTL entry. // // The property attribute OID is the decimal PROP_ID preceded by // szOID_CERT_PROP_ID_PREFIX. Only attributes containing such an OID are // copied. // // CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG may be set in dwFlags. //-------------------------------------------------------------------------- BOOL WINAPI CertSetCertificateContextPropertiesFromCTLEntry( IN PCCERT_CONTEXT pCertContext, IN PCTL_ENTRY pCtlEntry, IN DWORD dwFlags ) { BOOL fResult; BOOL fValidPropData; DWORD cAttr; PCRYPT_ATTRIBUTE pAttr; size_t cchPropPrefix; if (SHA1_HASH_LEN != pCtlEntry->SubjectIdentifier.cbData) goto InvalidCtlEntry; if (!CertSetCertificateContextProperty( pCertContext, CERT_SHA1_HASH_PROP_ID, dwFlags, &pCtlEntry->SubjectIdentifier )) goto SetSha1HashPropError; cchPropPrefix = strlen(szOID_CERT_PROP_ID_PREFIX); fValidPropData = TRUE; // Loop through the attributes. for (cAttr = pCtlEntry->cAttribute, pAttr = pCtlEntry->rgAttribute; cAttr > 0; cAttr--, pAttr++) { DWORD dwPropId; PCRYPT_ATTR_BLOB pValue; CRYPT_DATA_BLOB PropBlob; PCRYPT_KEY_PROV_INFO pKeyProvInfo = NULL; void *pvData; // Skip any non-property attributes if (0 != strncmp(pAttr->pszObjId, szOID_CERT_PROP_ID_PREFIX, cchPropPrefix)) continue; dwPropId = (DWORD) strtoul(pAttr->pszObjId + cchPropPrefix, NULL, 10); if (0 == dwPropId) continue; // Check that we have a single valued attribute encoded as an // OCTET STRING if (1 != pAttr->cValue) { fValidPropData = FALSE; continue; } pValue = pAttr->rgValue; if (2 > pValue->cbData || ASN1UTIL_TAG_OCTETSTRING != pValue->pbData[0]) { fValidPropData = FALSE; continue; } // Extract the property bytes from the encoded OCTET STRING if (0 >= Asn1UtilExtractContent( pValue->pbData, pValue->cbData, &PropBlob.cbData, (const BYTE **) &PropBlob.pbData ) || CMSG_INDEFINITE_LENGTH == PropBlob.cbData) { fValidPropData = FALSE; continue; } if (CERT_KEY_PROV_INFO_PROP_ID == dwPropId) { BYTE *pbAlignedData = NULL; DWORD cbData; DWORD cbInfo; cbData = PropBlob.cbData; if (0 == cbData) { fValidPropData = FALSE; continue; } if (NULL == (pbAlignedData = (BYTE *) PkiNonzeroAlloc(cbData))) goto OutOfMemory; memcpy(pbAlignedData, PropBlob.pbData, cbData); if (!DecodeKeyProvInfo( (PSERIALIZED_KEY_PROV_INFO) pbAlignedData, cbData, NULL, // pInfo &cbInfo )) { PkiFree(pbAlignedData); fValidPropData = FALSE; continue; } if (NULL == (pKeyProvInfo = (PCRYPT_KEY_PROV_INFO) PkiNonzeroAlloc(cbInfo))) { PkiFree(pbAlignedData); goto OutOfMemory; } if (!DecodeKeyProvInfo( (PSERIALIZED_KEY_PROV_INFO) pbAlignedData, cbData, pKeyProvInfo, &cbInfo )) { PkiFree(pbAlignedData); PkiFree(pKeyProvInfo); fValidPropData = FALSE; continue; } PkiFree(pbAlignedData); pvData = (void *) pKeyProvInfo; } else pvData = (void *) &PropBlob; fResult = CertSetCertificateContextProperty( pCertContext, dwPropId, dwFlags, pvData ); if (pKeyProvInfo) PkiFree(pKeyProvInfo); if (!fResult) goto SetPropError; } if (!fValidPropData) goto InvalidPropData; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidCtlEntry, E_INVALIDARG) TRACE_ERROR(SetSha1HashPropError) TRACE_ERROR(OutOfMemory) TRACE_ERROR(SetPropError) SET_ERROR(InvalidPropData, ERROR_INVALID_DATA) } //+========================================================================= // KEYID_ELEMENT Functions //========================================================================== // pbKeyIdEncoded has already been allocated STATIC PKEYID_ELEMENT CreateKeyIdElement( IN BYTE *pbKeyIdEncoded, IN DWORD cbKeyIdEncoded ) { PKEYID_ELEMENT pEle = NULL; // Allocate and initialize the prop element structure pEle = (PKEYID_ELEMENT) PkiZeroAlloc(sizeof(KEYID_ELEMENT)); if (pEle == NULL) return NULL; pEle->KeyIdentifier.pbData = pbKeyIdEncoded; pEle->KeyIdentifier.cbData = cbKeyIdEncoded; return pEle; } STATIC void FreeKeyIdElement(IN PKEYID_ELEMENT pEle) { PPROP_ELEMENT pPropEle; if (NULL == pEle) return; PkiFree(pEle->KeyIdentifier.pbData); // Free the Key Identifier's property elements while (pPropEle = pEle->pPropHead) { RemovePropElement(&pEle->pPropHead, pPropEle); FreePropElement(pPropEle); } PkiFree(pEle); } //+------------------------------------------------------------------------- // Open Message Store Provider // // Get Certs and CRLs from the message. pvPara contains the HCRYPTMSG // to read. // // Note for an error return, the caller will free any certs or CRLs // successfully added to the store. //-------------------------------------------------------------------------- STATIC BOOL WINAPI OpenMsgStoreProv( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ) { PCERT_STORE pStore = (PCERT_STORE) hCertStore; HCRYPTMSG hCryptMsg = (HCRYPTMSG) pvPara; BOOL fResult; BYTE *pbEncoded = NULL; DWORD cCert; DWORD cCrl; DWORD cbData; DWORD dwIndex; PCONTEXT_ELEMENT pCertEle; PCONTEXT_ELEMENT pCrlEle; if (dwFlags & CERT_STORE_UNSAFE_PHYSICAL_FLAG) { SetLastError((DWORD) E_INVALIDARG); goto ErrorReturn; } if (0 == GET_CERT_ENCODING_TYPE(dwEncodingType)) dwEncodingType |= X509_ASN_ENCODING; // Get count of certificates and CRLs in the message cCert = 0; cbData = sizeof(cCert); fResult = CryptMsgGetParam( hCryptMsg, CMSG_CERT_COUNT_PARAM, 0, // dwIndex &cCert, &cbData ); if (!fResult) goto ErrorReturn; cCrl = 0; cbData = sizeof(cCrl); fResult = CryptMsgGetParam( hCryptMsg, CMSG_CRL_COUNT_PARAM, 0, // dwIndex &cCrl, &cbData ); if (!fResult) goto ErrorReturn; for (dwIndex = 0; dwIndex < cCert; dwIndex++) { if (NULL == (pbEncoded = (BYTE *) AllocAndGetMsgParam( hCryptMsg, CMSG_CERT_PARAM, dwIndex, &cbData))) goto ErrorReturn; pCertEle = CreateCertElement( pStore, dwEncodingType, pbEncoded, cbData, NULL // pShareEle ); if (pCertEle == NULL) goto ErrorReturn; else { pbEncoded = NULL; AddContextElement(pCertEle); } } for (dwIndex = 0; dwIndex < cCrl; dwIndex++) { if (NULL == (pbEncoded = (BYTE *) AllocAndGetMsgParam( hCryptMsg, CMSG_CRL_PARAM, dwIndex, &cbData))) goto ErrorReturn; pCrlEle = CreateCrlElement( pStore, dwEncodingType, pbEncoded, cbData, NULL // pShareEle ); if (pCrlEle == NULL) goto ErrorReturn; else { pbEncoded = NULL; AddContextElement(pCrlEle); } } pStoreProvInfo->dwStoreProvFlags |= CERT_STORE_PROV_NO_PERSIST_FLAG; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: PkiFree(pbEncoded); fResult = FALSE; goto CommonReturn; } //+------------------------------------------------------------------------- // Open PKCS #7 Signed Message Store Provider // // Get Certs and CRLs from the message. pvPara points to a CRYPT_DATA_BLOB // containing the signed message. // // Note for an error return, the caller will free any certs or CRLs // successfully added to the store. //-------------------------------------------------------------------------- STATIC BOOL WINAPI OpenPKCS7StoreProv( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ) { BOOL fResult; PCRYPT_DATA_BLOB pMsg = (PCRYPT_DATA_BLOB) pvPara; HCRYPTMSG hMsg = NULL; DWORD dwMsgType; if (dwFlags & CERT_STORE_UNSAFE_PHYSICAL_FLAG) goto UnsafeOpenPKCS7Error; if (0 == GET_CERT_ENCODING_TYPE(dwEncodingType)) dwEncodingType |= X509_ASN_ENCODING; if (0 == GET_CMSG_ENCODING_TYPE(dwEncodingType)) dwEncodingType |= PKCS_7_ASN_ENCODING; if (Asn1UtilIsPKCS7WithoutContentType(pMsg->pbData, pMsg->cbData)) dwMsgType = CMSG_SIGNED; else dwMsgType = 0; if (NULL == (hMsg = CryptMsgOpenToDecode( dwEncodingType, 0, // dwFlags dwMsgType, 0, // hCryptProv, NULL, // pRecipientInfo NULL // pStreamInfo ))) goto MsgOpenToDecodeError; if (!CryptMsgUpdate( hMsg, pMsg->pbData, pMsg->cbData, TRUE // fFinal )) goto MsgUpdateError; fResult = OpenMsgStoreProv( lpszStoreProvider, dwEncodingType, hCryptProv, dwFlags, (const void *) hMsg, hCertStore, pStoreProvInfo ); // Set in above call // pStoreProvInfo->dwStoreProvFlags |= CERT_STORE_PROV_NO_PERSIST_FLAG; CommonReturn: if (hMsg) CryptMsgClose(hMsg); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(UnsafeOpenPKCS7Error, E_INVALIDARG) TRACE_ERROR(MsgOpenToDecodeError) TRACE_ERROR(MsgUpdateError) } STATIC BOOL LoadSerializedStore( IN HANDLE h, IN PFNREAD pfnRead, IN PFNSKIP pfnSkip, IN DWORD cbReadSize, IN PCERT_STORE pStore ) { FILE_HDR FileHdr; DWORD csStatus; if (!pfnRead( h, &FileHdr, sizeof(FileHdr))) return FALSE; if (FileHdr.dwVersion != CERT_FILE_VERSION_0 || FileHdr.dwMagic != CERT_MAGIC) { SetLastError((DWORD) CRYPT_E_FILE_ERROR); return(FALSE); } while (CSContinue == (csStatus = LoadStoreElement( h, pfnRead, pfnSkip, cbReadSize, pStore, CERT_STORE_ADD_ALWAYS, CERT_STORE_ALL_CONTEXT_FLAG, NULL, // pdwContextType NULL))) // ppvContext ; if(csStatus == CSError) return(FALSE); return(TRUE); } //+------------------------------------------------------------------------- // Add the serialized store to the store. // // Called from logstor.cpp for serialized registry stores //-------------------------------------------------------------------------- BOOL WINAPI I_CertAddSerializedStore( IN HCERTSTORE hCertStore, IN BYTE *pbStore, IN DWORD cbStore ) { MEMINFO MemInfo; MemInfo.pByte = pbStore; MemInfo.cb = cbStore; MemInfo.cbSeek = 0; return LoadSerializedStore( (HANDLE) &MemInfo, ReadFromMemory, SkipInMemory, cbStore, (PCERT_STORE) hCertStore ); } //+------------------------------------------------------------------------- // Open Serialized Store Provider // // pvPara points to a CRYPT_DATA_BLOB containing an in memory serialized // Store. // // Note for an error return, the caller will free any certs or CRLs // successfully added to the store. //-------------------------------------------------------------------------- STATIC BOOL WINAPI OpenSerializedStoreProv( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ) { PCRYPT_DATA_BLOB pData = (PCRYPT_DATA_BLOB) pvPara; if (dwFlags & CERT_STORE_UNSAFE_PHYSICAL_FLAG) { SetLastError((DWORD) E_INVALIDARG); return FALSE; } pStoreProvInfo->dwStoreProvFlags |= CERT_STORE_PROV_NO_PERSIST_FLAG; assert(pData); return I_CertAddSerializedStore( hCertStore, pData->pbData, pData->cbData ); } //+========================================================================= // File Store Provider Functions //========================================================================== #define OPEN_FILE_FLAGS_MASK (CERT_STORE_CREATE_NEW_FLAG | \ CERT_STORE_OPEN_EXISTING_FLAG | \ CERT_STORE_MAXIMUM_ALLOWED_FLAG | \ CERT_STORE_SHARE_CONTEXT_FLAG | \ CERT_STORE_SHARE_STORE_FLAG | \ CERT_STORE_BACKUP_RESTORE_FLAG | \ CERT_STORE_READONLY_FLAG | \ CERT_STORE_MANIFOLD_FLAG | \ CERT_STORE_UPDATE_KEYID_FLAG | \ CERT_STORE_ENUM_ARCHIVED_FLAG | \ CERT_STORE_NO_CRYPT_RELEASE_FLAG | \ CERT_STORE_SET_LOCALIZED_NAME_FLAG | \ CERT_FILE_STORE_COMMIT_ENABLE_FLAG) //+------------------------------------------------------------------------- // File Store Provider handle information. Only applicable when the store // was opened with CERT_FILE_STORE_COMMIT_ENABLE_FLAG set in dwFlags. //-------------------------------------------------------------------------- typedef struct _FILE_STORE { HCERTSTORE hCertStore; // not duplicated CRITICAL_SECTION CriticalSection; HANDLE hFile; DWORD dwLoFilePointer; LONG lHiFilePointer; DWORD dwEncodingType; DWORD dwSaveAs; BOOL fTouched; // set for write, delete or set property } FILE_STORE, *PFILE_STORE; //+------------------------------------------------------------------------- // Lock and unlock file functions //-------------------------------------------------------------------------- static inline void LockFileStore(IN PFILE_STORE pFileStore) { EnterCriticalSection(&pFileStore->CriticalSection); } static inline void UnlockFileStore(IN PFILE_STORE pFileStore) { LeaveCriticalSection(&pFileStore->CriticalSection); } STATIC BOOL CommitFile( IN PFILE_STORE pFileStore, IN DWORD dwFlags ) { BOOL fResult; BOOL fTouched; assert(pFileStore); LockFileStore(pFileStore); if (dwFlags & CERT_STORE_CTRL_COMMIT_FORCE_FLAG) fTouched = TRUE; else if (dwFlags & CERT_STORE_CTRL_COMMIT_CLEAR_FLAG) fTouched = FALSE; else fTouched = pFileStore->fTouched; if (fTouched) { HANDLE hFile = pFileStore->hFile; DWORD dwLoFilePointer; LONG lHiFilePointer = pFileStore->lHiFilePointer; // Start the file overwrite at the same location as we started // the store read from the file. assert(hFile); dwLoFilePointer = SetFilePointer( hFile, (LONG) pFileStore->dwLoFilePointer, &lHiFilePointer, FILE_BEGIN ); if (0xFFFFFFFF == dwLoFilePointer && NO_ERROR != GetLastError()) goto SetFilePointerError; if (!CertSaveStore( pFileStore->hCertStore, pFileStore->dwEncodingType, pFileStore->dwSaveAs, CERT_STORE_SAVE_TO_FILE, (void *) hFile, 0)) // dwFlags goto SaveStoreError; if (!SetEndOfFile(hFile)) goto SetEndOfFileError; } pFileStore->fTouched = FALSE; fResult = TRUE; CommonReturn: UnlockFileStore(pFileStore); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(SetFilePointerError) TRACE_ERROR(SaveStoreError) TRACE_ERROR(SetEndOfFileError) } //+------------------------------------------------------------------------- // File Store Provider Functions for stores opened with // CERT_FILE_STORE_COMMIT_ENABLE_FLAG set in dwFlags. // // Note, since the CRL and CTL callbacks have the same signature as the // certificate callbacks and since we don't need to access the context // information, we can also use the certificate callbacks for CRLs and // CTLs. //-------------------------------------------------------------------------- STATIC void WINAPI FileStoreProvClose( IN HCERTSTOREPROV hStoreProv, IN DWORD dwFlags ) { PFILE_STORE pFileStore = (PFILE_STORE) hStoreProv; if (pFileStore) { if (pFileStore->fTouched) CommitFile( pFileStore, 0 // dwFlags ); if (pFileStore->hFile) CloseHandle(pFileStore->hFile); DeleteCriticalSection(&pFileStore->CriticalSection); PkiFree(pFileStore); } } STATIC BOOL WINAPI FileStoreProvWriteCert( IN HCERTSTOREPROV hStoreProv, IN PCCERT_CONTEXT pCertContext, IN DWORD dwFlags ) { PFILE_STORE pFileStore = (PFILE_STORE) hStoreProv; assert(pFileStore); pFileStore->fTouched = TRUE; return TRUE; } STATIC BOOL WINAPI FileStoreProvDeleteCert( IN HCERTSTOREPROV hStoreProv, IN PCCERT_CONTEXT pCertContext, IN DWORD dwFlags ) { PFILE_STORE pFileStore = (PFILE_STORE) hStoreProv; assert(pFileStore); pFileStore->fTouched = TRUE; return TRUE; } STATIC BOOL WINAPI FileStoreProvSetCertProperty( IN HCERTSTOREPROV hStoreProv, IN PCCERT_CONTEXT pCertContext, IN DWORD dwPropId, IN DWORD dwFlags, IN const void *pvData ) { PFILE_STORE pFileStore = (PFILE_STORE) hStoreProv; assert(pFileStore); pFileStore->fTouched = TRUE; return TRUE; } STATIC BOOL WINAPI FileStoreProvControl( IN HCERTSTOREPROV hStoreProv, IN DWORD dwFlags, IN DWORD dwCtrlType, IN void const *pvCtrlPara ) { BOOL fResult; PFILE_STORE pFileStore = (PFILE_STORE) hStoreProv; switch (dwCtrlType) { case CERT_STORE_CTRL_COMMIT: fResult = CommitFile(pFileStore, dwFlags); break; default: goto NotSupported; } CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(NotSupported, ERROR_CALL_NOT_IMPLEMENTED) } static void * const rgpvFileStoreProvFunc[] = { // CERT_STORE_PROV_CLOSE_FUNC 0 FileStoreProvClose, // CERT_STORE_PROV_READ_CERT_FUNC 1 NULL, // CERT_STORE_PROV_WRITE_CERT_FUNC 2 FileStoreProvWriteCert, // CERT_STORE_PROV_DELETE_CERT_FUNC 3 FileStoreProvDeleteCert, // CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC 4 FileStoreProvSetCertProperty, // CERT_STORE_PROV_READ_CRL_FUNC 5 NULL, // CERT_STORE_PROV_WRITE_CRL_FUNC 6 FileStoreProvWriteCert, // CERT_STORE_PROV_DELETE_CRL_FUNC 7 FileStoreProvDeleteCert, // CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC 8 FileStoreProvSetCertProperty, // CERT_STORE_PROV_READ_CTL_FUNC 9 NULL, // CERT_STORE_PROV_WRITE_CTL_FUNC 10 FileStoreProvWriteCert, // CERT_STORE_PROV_DELETE_CTL_FUNC 11 FileStoreProvDeleteCert, // CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC 12 FileStoreProvSetCertProperty, // CERT_STORE_PROV_CONTROL_FUNC 13 FileStoreProvControl }; #define FILE_STORE_PROV_FUNC_COUNT (sizeof(rgpvFileStoreProvFunc) / \ sizeof(rgpvFileStoreProvFunc[0])) STATIC BOOL OpenFileForCommit( IN HANDLE hFile, IN DWORD dwLoFilePointer, IN LONG lHiFilePointer, IN HCERTSTORE hCertStore, IN DWORD dwEncodingType, IN DWORD dwSaveAs, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ) { BOOL fResult; PFILE_STORE pFileStore; if (NULL == (pFileStore = (PFILE_STORE) PkiZeroAlloc(sizeof(FILE_STORE)))) return FALSE; if (!Pki_InitializeCriticalSection(&pFileStore->CriticalSection)) { PkiFree(pFileStore); return FALSE; } // Duplicate the file HANDLE if (!DuplicateHandle( GetCurrentProcess(), hFile, GetCurrentProcess(), &pFileStore->hFile, GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess FALSE, // bInheritHandle 0 // dwOptions ) || NULL == pFileStore->hFile) goto DuplicateFileError; pFileStore->hCertStore = hCertStore; pFileStore->dwLoFilePointer = dwLoFilePointer; pFileStore->lHiFilePointer = lHiFilePointer; pFileStore->dwEncodingType = dwEncodingType; pFileStore->dwSaveAs = dwSaveAs; pStoreProvInfo->cStoreProvFunc = FILE_STORE_PROV_FUNC_COUNT; pStoreProvInfo->rgpvStoreProvFunc = (void **) rgpvFileStoreProvFunc; pStoreProvInfo->hStoreProv = (HCERTSTOREPROV) pFileStore; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: PkiFree(pFileStore); fResult = FALSE; goto CommonReturn; TRACE_ERROR(DuplicateFileError) } //+------------------------------------------------------------------------- // Open File Store Provider // // Get Certs and CRLs from the opened file. pvPara contains the opened // HANDLE of the file to read. // // Note for an error return, the caller will free any certs or CRLs // successfully added to the store. // // Opening an empty file is tolerated. //-------------------------------------------------------------------------- STATIC BOOL WINAPI OpenFileStoreProv( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ) { BOOL fResult; HANDLE hFile = (HANDLE) pvPara; DWORD dwLoFilePointer = 0; LONG lHiFilePointer = 0; DWORD cbReadSize; if (dwFlags & ~OPEN_FILE_FLAGS_MASK) goto InvalidArg; if (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG) { if (dwFlags & CERT_STORE_READONLY_FLAG) goto InvalidArg; // Get current file location. This is where we will start the // commits. lHiFilePointer = 0; dwLoFilePointer = SetFilePointer( hFile, 0, // lDistanceToMove &lHiFilePointer, FILE_CURRENT ); if (0xFFFFFFFF == dwLoFilePointer && NO_ERROR != GetLastError()) goto SetFilePointerError; } cbReadSize = GetFileSize(hFile, NULL); if (0xFFFFFFFF == cbReadSize) goto FileError; fResult = LoadSerializedStore( hFile, ReadFromFile, SkipInFile, cbReadSize, (PCERT_STORE) hCertStore ); if (!fResult) { if (0 == GetFileSize(hFile, NULL)) // Empty file fResult = TRUE; } if (fResult && (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG)) fResult = OpenFileForCommit( hFile, dwLoFilePointer, lHiFilePointer, hCertStore, dwEncodingType, CERT_STORE_SAVE_AS_STORE, pStoreProvInfo ); else pStoreProvInfo->dwStoreProvFlags |= CERT_STORE_PROV_NO_PERSIST_FLAG; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(SetFilePointerError) TRACE_ERROR(FileError) } //+------------------------------------------------------------------------- // Open Filename Store Provider (Unicode version) // // Attempt to open a file containing a Store, a PKCS #7 signed // message or a single encoded certificate. // // pvPara contains a LPCWSTR of the Filename. // // Note for an error return, the caller will free any certs or CRLs // successfully added to the store. // // Opening an empty file is tolerated. //-------------------------------------------------------------------------- STATIC BOOL WINAPI OpenFilenameStoreProvW( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ) { BOOL fResult; LPWSTR pwszFile = (LPWSTR) pvPara; HANDLE hFile = INVALID_HANDLE_VALUE; CRYPT_DATA_BLOB FileData; memset(&FileData, 0, sizeof(FileData)); DWORD cbBytesRead; DWORD dwSaveAs = 0; HCERTSTORE hSpecialCertStore = NULL; assert(pwszFile); dwFlags &= ~CERT_STORE_UNSAFE_PHYSICAL_FLAG; if (dwFlags & ~OPEN_FILE_FLAGS_MASK) goto InvalidArg; if (dwFlags & CERT_STORE_BACKUP_RESTORE_FLAG) ILS_EnableBackupRestorePrivileges(); if (0 == GET_CERT_ENCODING_TYPE(dwEncodingType)) dwEncodingType |= X509_ASN_ENCODING; if (0 == GET_CMSG_ENCODING_TYPE(dwEncodingType)) dwEncodingType |= PKCS_7_ASN_ENCODING; if (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG) { DWORD dwCreate; if (dwFlags & CERT_STORE_READONLY_FLAG) goto InvalidArg; if (dwFlags & CERT_STORE_CREATE_NEW_FLAG) dwCreate = CREATE_NEW; else if (dwFlags & CERT_STORE_OPEN_EXISTING_FLAG) dwCreate = OPEN_EXISTING; else dwCreate = OPEN_ALWAYS; if (INVALID_HANDLE_VALUE == (hFile = CreateFileU( pwszFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, // lpsa dwCreate, FILE_ATTRIBUTE_NORMAL | ((dwFlags & CERT_STORE_BACKUP_RESTORE_FLAG) ? FILE_FLAG_BACKUP_SEMANTICS : 0), NULL // hTemplateFile ))) goto CreateFileError; // Default to saving as a serialized store dwSaveAs = CERT_STORE_SAVE_AS_STORE; if (0 == GetFileSize(hFile, NULL)) { // Use file extension to determine dwSaveAs LPWSTR pwszExt; pwszExt = pwszFile + wcslen(pwszFile); while (pwszExt-- > pwszFile) { if (L'.' == *pwszExt) { pwszExt++; if (0 == _wcsicmp(pwszExt, L"p7c") || 0 == _wcsicmp(pwszExt, L"spc")) dwSaveAs = CERT_STORE_SAVE_AS_PKCS7; break; } } goto CommitReturn; } } else { WIN32_FILE_ATTRIBUTE_DATA FileAttr; if (!GetFileAttributesExW( pwszFile, GetFileExInfoStandard, &FileAttr )) goto GetFileAttributesError; if (INVALID_HANDLE_VALUE == (hFile = CreateFileU( pwszFile, GENERIC_READ, FILE_SHARE_READ, NULL, // lpsa OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | ((dwFlags & CERT_STORE_BACKUP_RESTORE_FLAG) ? FILE_FLAG_BACKUP_SEMANTICS : 0), NULL // hTemplateFile ))) goto CreateFileError; } if (OpenFileStoreProv( lpszStoreProvider, dwEncodingType, hCryptProv, dwFlags, (const void *) hFile, hCertStore, pStoreProvInfo)) { // For commit, we have already called OpenFileForCommit fResult = TRUE; goto OpenReturn; } // Read the entire file. Will attempt to process as either a // PKCS #7 or as a single cert. // // Will first try as binary. If that fails will try as base64 encoded. if (0 != SetFilePointer(hFile, 0, NULL, FILE_BEGIN)) goto FileError; FileData.cbData = GetFileSize(hFile, NULL); if (0xFFFFFFFF == FileData.cbData) goto FileError; if (0 == FileData.cbData) // Empty file goto CommitReturn; if (NULL == (FileData.pbData = (BYTE *) PkiNonzeroAlloc(FileData.cbData))) goto OutOfMemory; if (!ReadFile( hFile, FileData.pbData, FileData.cbData, &cbBytesRead, NULL // lpOverlapped )) goto FileError; if (OpenPKCS7StoreProv( lpszStoreProvider, dwEncodingType, hCryptProv, dwFlags, (const void *) &FileData, hCertStore, pStoreProvInfo)) { dwSaveAs = CERT_STORE_SAVE_AS_PKCS7; goto CommitReturn; } // Try to process as a single encoded certificate if (CertAddEncodedCertificateToStore( hCertStore, dwEncodingType, FileData.pbData, FileData.cbData, CERT_STORE_ADD_USE_EXISTING, NULL)) { if (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG) goto CanNotCommitX509CertFileError; else goto CommitReturn; } // Try to process as an encoded PKCS7, X509 or CERT_PAIR in any // format if (CryptQueryObject( CERT_QUERY_OBJECT_BLOB, &FileData, CERT_QUERY_CONTENT_FLAG_CERT | CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED | CERT_QUERY_CONTENT_FLAG_CERT_PAIR, CERT_QUERY_FORMAT_FLAG_ALL, 0, // dwFlags NULL, // pdwMsgAndCertEncodingType NULL, // pdwContentType NULL, // pdwFormatType &hSpecialCertStore, NULL, // phMsg NULL // ppvContext )) { fResult = I_CertUpdateStore(hCertStore, hSpecialCertStore, 0, NULL); CertCloseStore(hSpecialCertStore, 0); if (!fResult) goto UpdateStoreError; if (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG) goto CanNotCommitSpecialFileError; else goto CommitReturn; } goto NoStoreOrPKCS7OrCertFileError; CommitReturn: if (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG) fResult = OpenFileForCommit( hFile, 0, // dwLoFilePointer 0, // lHiFilePointer hCertStore, dwEncodingType, dwSaveAs, pStoreProvInfo ); else { pStoreProvInfo->dwStoreProvFlags |= CERT_STORE_PROV_NO_PERSIST_FLAG; fResult = TRUE; } OpenReturn: if (dwFlags & CERT_STORE_SET_LOCALIZED_NAME_FLAG) { CRYPT_DATA_BLOB Property; Property.pbData = (BYTE *) pwszFile; Property.cbData = (wcslen(pwszFile) + 1) * sizeof(WCHAR); CertSetStoreProperty( hCertStore, CERT_STORE_LOCALIZED_NAME_PROP_ID, 0, // dwFlags (const void *) &Property ); } CommonReturn: if (INVALID_HANDLE_VALUE != hFile) CloseHandle(hFile); if (FileData.pbData) PkiFree(FileData.pbData); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(GetFileAttributesError) TRACE_ERROR(CreateFileError) TRACE_ERROR(FileError) TRACE_ERROR(OutOfMemory) SET_ERROR(CanNotCommitX509CertFileError, ERROR_ACCESS_DENIED) SET_ERROR(CanNotCommitSpecialFileError, ERROR_ACCESS_DENIED) SET_ERROR(NoStoreOrPKCS7OrCertFileError, CRYPT_E_FILE_ERROR) TRACE_ERROR(UpdateStoreError) } //+------------------------------------------------------------------------- // Open Filename Store Provider (ASCII version) // // Attempt to open a file containing a Store, a PKCS #7 signed // message or a single encoded certificate. // // pvPara contains a LPCWSTR of the Filename. // // Note for an error return, the caller will free any certs or CRLs // successfully added to the store. // // Opening an empty file is tolerated. //-------------------------------------------------------------------------- STATIC BOOL WINAPI OpenFilenameStoreProvA( IN LPCSTR lpszStoreProvider, IN DWORD dwEncodingType, IN HCRYPTPROV hCryptProv, IN DWORD dwFlags, IN const void *pvPara, IN HCERTSTORE hCertStore, IN OUT PCERT_STORE_PROV_INFO pStoreProvInfo ) { BOOL fResult; LPWSTR pwszFilename; assert(pvPara); if (NULL == (pwszFilename = MkWStr((LPSTR) pvPara))) fResult = FALSE; else { fResult = OpenFilenameStoreProvW( lpszStoreProvider, dwEncodingType, hCryptProv, dwFlags, (const void *) pwszFilename, hCertStore, pStoreProvInfo ); FreeWStr(pwszFilename); } return fResult; } //+========================================================================= // CryptAcquireCertificatePrivateKey Support Functions //========================================================================== // Upon entry/exit, the Cache Store is locked. // // OUTs are only updated for success. STATIC BOOL GetCacheKeyContext( IN PCONTEXT_ELEMENT pCacheEle, OUT HCRYPTPROV *phCryptProv, OUT OPTIONAL DWORD *pdwKeySpec ) { BOOL fResult = FALSE; PPROP_ELEMENT pPropEle; if (pPropEle = FindPropElement(pCacheEle, CERT_KEY_CONTEXT_PROP_ID)) { PCERT_KEY_CONTEXT pKeyContext = (PCERT_KEY_CONTEXT) pPropEle->pbData; assert(pKeyContext); assert(pPropEle->cbData >= sizeof(CERT_KEY_CONTEXT)); if (pKeyContext->hCryptProv) { *phCryptProv = pKeyContext->hCryptProv; if (pdwKeySpec) *pdwKeySpec = pKeyContext->dwKeySpec; fResult = TRUE; } } return fResult; } STATIC PCRYPT_KEY_PROV_INFO GetKeyIdentifierKeyProvInfo( IN PCONTEXT_ELEMENT pCacheEle ) { PCRYPT_KEY_PROV_INFO pKeyProvInfo = NULL; DWORD cbKeyProvInfo; BYTE rgbKeyId[MAX_HASH_LEN]; DWORD cbKeyId; CRYPT_HASH_BLOB KeyIdentifier; cbKeyId = sizeof(rgbKeyId); if(!GetProperty( pCacheEle, CERT_KEY_IDENTIFIER_PROP_ID, rgbKeyId, &cbKeyId )) return NULL; KeyIdentifier.pbData = rgbKeyId; KeyIdentifier.cbData = cbKeyId; if (CryptGetKeyIdentifierProperty( &KeyIdentifier, CERT_KEY_PROV_INFO_PROP_ID, CRYPT_KEYID_ALLOC_FLAG, NULL, // pwszComputerName NULL, // pvReserved (void *) &pKeyProvInfo, &cbKeyProvInfo )) return pKeyProvInfo; // Try again, searching LocalMachine if (CryptGetKeyIdentifierProperty( &KeyIdentifier, CERT_KEY_PROV_INFO_PROP_ID, CRYPT_KEYID_ALLOC_FLAG | CRYPT_KEYID_MACHINE_FLAG, NULL, // pwszComputerName NULL, // pvReserved (void *) &pKeyProvInfo, &cbKeyProvInfo )) return pKeyProvInfo; else return NULL; } STATIC BOOL AcquireKeyContext( IN PCCERT_CONTEXT pCert, IN DWORD dwFlags, IN PCRYPT_KEY_PROV_INFO pKeyProvInfo, IN OUT PCERT_KEY_CONTEXT pKeyContext, IN OUT BOOL *pfBadPubKey ) { BOOL fResult; DWORD dwAcquireFlags; DWORD dwIdx; dwAcquireFlags = pKeyProvInfo->dwFlags & ~CERT_SET_KEY_CONTEXT_PROP_ID; if (dwFlags & CRYPT_ACQUIRE_SILENT_FLAG) dwAcquireFlags |= CRYPT_SILENT; pKeyContext->dwKeySpec = pKeyProvInfo->dwKeySpec; if (PROV_RSA_FULL == pKeyProvInfo->dwProvType && (NULL == pKeyProvInfo->pwszProvName || L'\0' == *pKeyProvInfo->pwszProvName || 0 == _wcsicmp(pKeyProvInfo->pwszProvName, MS_DEF_PROV_W))) fResult = CryptAcquireContextU( &pKeyContext->hCryptProv, pKeyProvInfo->pwszContainerName, MS_ENHANCED_PROV_W, PROV_RSA_FULL, dwAcquireFlags ); else if (PROV_DSS_DH == pKeyProvInfo->dwProvType && (NULL == pKeyProvInfo->pwszProvName || L'\0' == *pKeyProvInfo->pwszProvName || 0 == _wcsicmp(pKeyProvInfo->pwszProvName, MS_DEF_DSS_DH_PROV_W))) fResult = CryptAcquireContextU( &pKeyContext->hCryptProv, pKeyProvInfo->pwszContainerName, MS_ENH_DSS_DH_PROV_W, PROV_DSS_DH, dwAcquireFlags ); else fResult = FALSE; if (!fResult) { if (!CryptAcquireContextU( &pKeyContext->hCryptProv, pKeyProvInfo->pwszContainerName, pKeyProvInfo->pwszProvName, pKeyProvInfo->dwProvType, dwAcquireFlags )) { pKeyContext->hCryptProv = 0; goto AcquireContextError; } } for (dwIdx = 0; dwIdx < pKeyProvInfo->cProvParam; dwIdx++) { PCRYPT_KEY_PROV_PARAM pKeyProvParam = &pKeyProvInfo->rgProvParam[dwIdx]; if (!CryptSetProvParam( pKeyContext->hCryptProv, pKeyProvParam->dwParam, pKeyProvParam->pbData, pKeyProvParam->dwFlags )) goto SetProvParamError; } if (dwFlags & CRYPT_ACQUIRE_COMPARE_KEY_FLAG) { if (!I_CertCompareCertAndProviderPublicKey( pCert, pKeyContext->hCryptProv, pKeyContext->dwKeySpec )) { *pfBadPubKey = TRUE; goto BadPublicKey; } } fResult = TRUE; CommonReturn: return fResult; ErrorReturn: if (pKeyContext->hCryptProv) { DWORD dwErr = GetLastError(); CryptReleaseContext(pKeyContext->hCryptProv, 0); SetLastError(dwErr); pKeyContext->hCryptProv = 0; } fResult = FALSE; goto CommonReturn; TRACE_ERROR(AcquireContextError) TRACE_ERROR(SetProvParamError) SET_ERROR(BadPublicKey, NTE_BAD_PUBLIC_KEY) } //+------------------------------------------------------------------------- // Acquire a HCRYPTPROV handle and dwKeySpec for the specified certificate // context. Uses the certificate's CERT_KEY_PROV_INFO_PROP_ID property. // The returned HCRYPTPROV handle may optionally be cached using the // certificate's CERT_KEY_CONTEXT_PROP_ID property. // // If CRYPT_ACQUIRE_CACHE_FLAG is set, then, if an already acquired and // cached HCRYPTPROV exists for the certificate, its returned. Otherwise, // a HCRYPTPROV is acquired and then cached via the certificate's // CERT_KEY_CONTEXT_PROP_ID. // // The CRYPT_ACQUIRE_USE_PROV_INFO_FLAG can be set to use the dwFlags field of // the certificate's CERT_KEY_PROV_INFO_PROP_ID property's CRYPT_KEY_PROV_INFO // data structure to determine if the returned HCRYPTPROV should be cached. // HCRYPTPROV caching is enabled if the CERT_SET_KEY_CONTEXT_PROP_ID flag was // set. // // If CRYPT_ACQUIRE_COMPARE_KEY_FLAG is set, then, // the public key in the certificate is compared with the public // key returned by the cryptographic provider. If the keys don't match, the // acquire fails and LastError is set to NTE_BAD_PUBLIC_KEY. Note, if // a cached HCRYPTPROV is returned, the comparison isn't done. We assume the // comparison was done on the initial acquire. // // The CRYPT_ACQUIRE_SILENT_FLAG can be set to suppress any UI by the CSP. // See CryptAcquireContext's CRYPT_SILENT flag for more details. // // *pfCallerFreeProv is returned set to FALSE for: // - Acquire or public key comparison fails. // - CRYPT_ACQUIRE_CACHE_FLAG is set. // - CRYPT_ACQUIRE_USE_PROV_INFO_FLAG is set AND // CERT_SET_KEY_CONTEXT_PROP_ID flag is set in the dwFlags field of the // certificate's CERT_KEY_PROV_INFO_PROP_ID property's // CRYPT_KEY_PROV_INFO data structure. // When *pfCallerFreeProv is FALSE, the caller must not release. The // returned HCRYPTPROV will be released on the last free of the certificate // context. // // Otherwise, *pfCallerFreeProv is TRUE and the returned HCRYPTPROV must // be released by the caller by calling CryptReleaseContext. //-------------------------------------------------------------------------- BOOL WINAPI CryptAcquireCertificatePrivateKey( IN PCCERT_CONTEXT pCert, IN DWORD dwFlags, IN void *pvReserved, OUT HCRYPTPROV *phCryptProv, OUT OPTIONAL DWORD *pdwKeySpec, OUT OPTIONAL BOOL *pfCallerFreeProv ) { BOOL fResult; BOOL fCallerFreeProv; PCONTEXT_ELEMENT pCacheEle; PCERT_STORE pCacheStore; CERT_KEY_CONTEXT KeyContext; memset(&KeyContext, 0, sizeof(KeyContext)); PCRYPT_KEY_PROV_INFO pKeyProvInfo = NULL; DWORD cbData; BOOL fKeyIdentifier = FALSE; BOOL fBadPubKey = FALSE; if (NULL == (pCacheEle = GetCacheElement(ToContextElement(pCert)))) goto InvalidCert; pCacheStore = pCacheEle->pStore; if (dwFlags & (CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_USE_PROV_INFO_FLAG)) { // Attempt to use existing CERT_KEY_CONTEXT_PROP_ID property LockStore(pCacheStore); if (GetCacheKeyContext( pCacheEle, phCryptProv, pdwKeySpec )) { if (pfCallerFreeProv) *pfCallerFreeProv = FALSE; UnlockStore(pCacheStore); return TRUE; } UnlockStore(pCacheStore); } if (!AllocAndGetProperty( pCacheEle, CERT_KEY_PROV_INFO_PROP_ID, (void **) &pKeyProvInfo, &cbData)) { fKeyIdentifier = TRUE; if (NULL == (pKeyProvInfo = GetKeyIdentifierKeyProvInfo(pCacheEle))) goto NoKeyProperty; } if (!AcquireKeyContext( pCert, dwFlags, pKeyProvInfo, &KeyContext, &fBadPubKey )) { DWORD dwLastErr; if (fKeyIdentifier) goto AcquireKeyContextError; dwLastErr = GetLastError(); if (ERROR_CANCELLED == dwLastErr || SCARD_W_CANCELLED_BY_USER == dwLastErr || HRESULT_FROM_WIN32(ERROR_CANCELLED) == dwLastErr) goto AcquireKeyContextError; PkiFree(pKeyProvInfo); fKeyIdentifier = TRUE; if (NULL == (pKeyProvInfo = GetKeyIdentifierKeyProvInfo(pCacheEle))) goto NoKeyProperty; if (!AcquireKeyContext( pCert, dwFlags, pKeyProvInfo, &KeyContext, &fBadPubKey )) goto AcquireKeyContextError; } fResult = TRUE; if ((dwFlags & CRYPT_ACQUIRE_CACHE_FLAG) || ((dwFlags & CRYPT_ACQUIRE_USE_PROV_INFO_FLAG) && (pKeyProvInfo->dwFlags & CERT_SET_KEY_CONTEXT_PROP_ID))) { // Cache the context. HCRYPTPROV hCryptProv; DWORD dwKeySpec; LockStore(pCacheStore); // First check that another thread hasn't already cached the context. if (GetCacheKeyContext( pCacheEle, &hCryptProv, &dwKeySpec )) { CryptReleaseContext(KeyContext.hCryptProv, 0); KeyContext.hCryptProv = hCryptProv; KeyContext.dwKeySpec = dwKeySpec; } else { KeyContext.cbSize = sizeof(KeyContext); fResult = SetProperty( pCacheEle, CERT_KEY_CONTEXT_PROP_ID, 0, // dwFlags (void *) &KeyContext, TRUE // fInhibitProvSet ); } UnlockStore(pCacheStore); if (!fResult) goto SetKeyContextPropertyError; fCallerFreeProv = FALSE; } else fCallerFreeProv = TRUE; CommonReturn: if (pKeyProvInfo) { if (fKeyIdentifier) PkiDefaultCryptFree(pKeyProvInfo); else PkiFree(pKeyProvInfo); } *phCryptProv = KeyContext.hCryptProv; if (pdwKeySpec) *pdwKeySpec = KeyContext.dwKeySpec; if (pfCallerFreeProv) *pfCallerFreeProv = fCallerFreeProv; return fResult; ErrorReturn: if (fBadPubKey) SetLastError((DWORD) NTE_BAD_PUBLIC_KEY); if (KeyContext.hCryptProv) { DWORD dwErr = GetLastError(); CryptReleaseContext(KeyContext.hCryptProv, 0); SetLastError(dwErr); KeyContext.hCryptProv = 0; } fResult = FALSE; fCallerFreeProv = FALSE; goto CommonReturn; SET_ERROR(InvalidCert, E_INVALIDARG) SET_ERROR(NoKeyProperty, CRYPT_E_NO_KEY_PROPERTY) TRACE_ERROR(AcquireKeyContextError) TRACE_ERROR(SetKeyContextPropertyError) } //+========================================================================= // I_CertSyncStore and I_CertSyncStoreEx Support Functions //========================================================================== // Returns FALSE if unable to do the find. For instance, OutOfMemory error. STATIC BOOL FindElementInOtherStore( IN PCERT_STORE pOtherStore, IN DWORD dwContextType, IN PCONTEXT_ELEMENT pEle, OUT PCONTEXT_ELEMENT *ppOtherEle ) { PCONTEXT_ELEMENT pOtherEle; BYTE rgbHash[SHA1_HASH_LEN]; DWORD cbHash; *ppOtherEle = NULL; cbHash = SHA1_HASH_LEN; if (!GetProperty( pEle, CERT_SHA1_HASH_PROP_ID, rgbHash, &cbHash ) || SHA1_HASH_LEN != cbHash) return FALSE; assert(STORE_TYPE_CACHE == pOtherStore->dwStoreType); pOtherEle = NULL; // Enable fForceEnumArchived while (pOtherEle = FindElementInCacheStore(pOtherStore, dwContextType, &FindAnyInfo, pOtherEle, TRUE)) { BYTE rgbOtherHash[SHA1_HASH_LEN]; DWORD cbOtherHash; cbOtherHash = SHA1_HASH_LEN; if (!GetProperty( pOtherEle, CERT_SHA1_HASH_PROP_ID, rgbOtherHash, &cbOtherHash ) || SHA1_HASH_LEN != cbOtherHash) return FALSE; if (0 == memcmp(rgbOtherHash, rgbHash, SHA1_HASH_LEN)) { *ppOtherEle = pOtherEle; return TRUE; } } return TRUE; } STATIC void AppendElementToDeleteList( IN PCONTEXT_ELEMENT pEle, IN OUT DWORD *pcDeleteList, IN OUT PCONTEXT_ELEMENT **pppDeleteList ) { DWORD cDeleteList = *pcDeleteList; PCONTEXT_ELEMENT *ppDeleteList = *pppDeleteList; if (ppDeleteList = (PCONTEXT_ELEMENT *) PkiRealloc(ppDeleteList, (cDeleteList + 1) * sizeof(PCONTEXT_ELEMENT))) { AddRefContextElement(pEle); ppDeleteList[cDeleteList] = pEle; *pcDeleteList = cDeleteList + 1; *pppDeleteList = ppDeleteList; } } //+------------------------------------------------------------------------- // Synchronize the original store with the new store. // // Assumptions: Both are cache stores. The new store is temporary // and local to the caller. The new store's contexts can be deleted or // moved to the original store. // // Setting ICERT_SYNC_STORE_INHIBIT_SYNC_PROPERTY_IN_FLAG in dwInFlags // inhibits the syncing of properties. // // ICERT_SYNC_STORE_CHANGED_OUT_FLAG is returned and set in *pdwOutFlags // if any contexts were added or deleted from the original store. //-------------------------------------------------------------------------- BOOL WINAPI I_CertSyncStoreEx( IN OUT HCERTSTORE hOriginalStore, IN OUT HCERTSTORE hNewStore, IN DWORD dwInFlags, OUT OPTIONAL DWORD *pdwOutFlags, IN OUT OPTIONAL void *pvReserved ) { PCERT_STORE pOrigStore = (PCERT_STORE) hOriginalStore; PCERT_STORE pNewStore = (PCERT_STORE) hNewStore; DWORD dwOutFlags = 0; DWORD cDeleteList = 0; PCONTEXT_ELEMENT *ppDeleteList = NULL; DWORD i; assert(STORE_TYPE_CACHE == pOrigStore->dwStoreType && STORE_TYPE_CACHE == pNewStore->dwStoreType); if (STORE_TYPE_CACHE != pOrigStore->dwStoreType || STORE_TYPE_CACHE != pNewStore->dwStoreType) { SetLastError((DWORD) E_INVALIDARG); return FALSE; } if (pOrigStore->dwFlags & CERT_STORE_MANIFOLD_FLAG) ArchiveManifoldCertificatesInStore(pNewStore); // Loop through the original store's elements. If the context exists // in the new store, copy the new store's properties and delete from // the new store. Otherwise, put the original store's context on a // deferred delete list. for (i = 0; i < CONTEXT_COUNT; i++) { PCONTEXT_ELEMENT pOrigEle = NULL; // Enable fForceEnumArchived while (pOrigEle = FindElementInCacheStore(pOrigStore, i, &FindAnyInfo, pOrigEle, TRUE)) { PCONTEXT_ELEMENT pNewEle; if (FindElementInOtherStore(pNewStore, i, pOrigEle, &pNewEle)) { if (pNewEle) { if (0 == (dwInFlags & ICERT_SYNC_STORE_INHIBIT_SYNC_PROPERTY_IN_FLAG)) CopyProperties( pNewEle, pOrigEle, COPY_PROPERTY_INHIBIT_PROV_SET_FLAG | COPY_PROPERTY_SYNC_FLAG ); DeleteContextElement(pNewEle); } else { dwOutFlags |= ICERT_SYNC_STORE_CHANGED_OUT_FLAG; AppendElementToDeleteList(pOrigEle, &cDeleteList, &ppDeleteList); } } // // else // Find failed due to OutOfMemory } } LockStore(pOrigStore); // Move any remaining contexts in the new store to the original store. // Note, append at the end of list and not at the beginning. Another // thread might have been enumerating the store. Its better to find // 2 copies of a renewed context instead of none. for (i = 0; i < CONTEXT_COUNT; i++) { PCONTEXT_ELEMENT pNewEle; if (pNewEle = pNewStore->rgpContextListHead[i]) { PCONTEXT_ELEMENT pOrigEle; dwOutFlags |= ICERT_SYNC_STORE_CHANGED_OUT_FLAG; if (pOrigEle = pOrigStore->rgpContextListHead[i]) { // Append at end of original store while (pOrigEle->pNext) pOrigEle = pOrigEle->pNext; pOrigEle->pNext = pNewEle; pNewEle->pPrev = pOrigEle; } else { // New entries in original store pOrigStore->rgpContextListHead[i] = pNewEle; pNewEle->pPrev = NULL; } for ( ; pNewEle; pNewEle = pNewEle->pNext) { // Update the elements obtained from the new store to // point to the original store pNewEle->pStore = pOrigStore; pNewEle->pProvStore = pOrigStore; SetStoreHandle(pNewEle); } // No contexts remain in new store pNewStore->rgpContextListHead[i] = NULL; } } UnlockStore(pOrigStore); // Delete any contexts in the deferred delete list from the original store while (cDeleteList--) DeleteContextElement(ppDeleteList[cDeleteList]); PkiFree(ppDeleteList); if (pdwOutFlags) *pdwOutFlags = dwOutFlags; return TRUE; } //+------------------------------------------------------------------------- // Synchronize the original store with the new store. // // Assumptions: Both are cache stores. The new store is temporary // and local to the caller. The new store's contexts can be deleted or // moved to the original store. //-------------------------------------------------------------------------- BOOL WINAPI I_CertSyncStore( IN OUT HCERTSTORE hOriginalStore, IN OUT HCERTSTORE hNewStore ) { return I_CertSyncStoreEx( hOriginalStore, hNewStore, 0, // dwInFlags NULL, // pdwOutFlags NULL // pvReserved ); } //+------------------------------------------------------------------------- // Update the original store with contexts from the new store. // // Assumptions: Both are cache stores. The new store is temporary // and local to the caller. The new store's contexts can be deleted or // moved to the original store. //-------------------------------------------------------------------------- BOOL WINAPI I_CertUpdateStore( IN OUT HCERTSTORE hOriginalStore, IN OUT HCERTSTORE hNewStore, IN DWORD dwReserved, IN OUT void *pvReserved ) { PCERT_STORE pOrigStore = (PCERT_STORE) hOriginalStore; PCERT_STORE pNewStore = (PCERT_STORE) hNewStore; DWORD i; assert(STORE_TYPE_CACHE == pOrigStore->dwStoreType && STORE_TYPE_CACHE == pNewStore->dwStoreType); if (STORE_TYPE_CACHE != pOrigStore->dwStoreType || STORE_TYPE_CACHE != pNewStore->dwStoreType) { SetLastError((DWORD) E_INVALIDARG); return FALSE; } LockStore(pOrigStore); // Move contexts in the new store to the original store. for (i = 0; i < CONTEXT_COUNT; i++) { PCONTEXT_ELEMENT pNewEle; if (pNewEle = pNewStore->rgpContextListHead[i]) { PCONTEXT_ELEMENT pNewTailEle = NULL; PCONTEXT_ELEMENT pOrigEle; PCONTEXT_ELEMENT pEle; for (pEle = pNewEle ; pEle; pEle = pEle->pNext) { // Update the elements obtained from the new store to // point to the original store pEle->pStore = pOrigStore; pEle->pProvStore = pOrigStore; SetStoreHandle(pEle); // Remember the last element in the linked list pNewTailEle = pEle; } assert(pNewTailEle); assert(NULL == pNewEle->pPrev); assert(NULL == pNewTailEle->pNext); // Insert new store's linked list of contexts at the // beginning of the original store if (pOrigEle = pOrigStore->rgpContextListHead[i]) { assert(NULL == pOrigEle->pPrev); pOrigEle->pPrev = pNewTailEle; pNewTailEle->pNext = pOrigEle; } pOrigStore->rgpContextListHead[i] = pNewEle; // No contexts remain in new store pNewStore->rgpContextListHead[i] = NULL; } } UnlockStore(pOrigStore); return TRUE; } //+========================================================================= // SortedCTL APIs. //========================================================================== static const BYTE rgbSeqTag[] = {ASN1UTIL_TAG_SEQ, 0}; static const BYTE rgbSetTag[] = {ASN1UTIL_TAG_SET, 0}; static const BYTE rgbOIDTag[] = {ASN1UTIL_TAG_OID, 0}; static const BYTE rgbIntegerTag[] = {ASN1UTIL_TAG_INTEGER, 0}; static const BYTE rgbBooleanTag[] = {ASN1UTIL_TAG_BOOLEAN, 0}; static const BYTE rgbOctetStringTag[] = {ASN1UTIL_TAG_OCTETSTRING, 0}; static const BYTE rgbConstructedContext0Tag[] = {ASN1UTIL_TAG_CONSTRUCTED_CONTEXT_0, 0}; static const BYTE rgbChoiceOfTimeTag[] = {ASN1UTIL_TAG_UTC_TIME, ASN1UTIL_TAG_GENERALIZED_TIME, 0}; static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractCtlPara[] = { // 0 - CertificateTrustList ::= SEQUENCE { ASN1UTIL_RETURN_CONTENT_BLOB_FLAG | ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag, // 1 - version CTLVersion DEFAULT v1, ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP, rgbIntegerTag, // 2 - subjectUsage SubjectUsage, ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag, // 3 - listIdentifier ListIdentifier OPTIONAL, ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP, rgbOctetStringTag, // 4 - sequenceNumber HUGEINTEGER OPTIONAL, ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP, rgbIntegerTag, // 5 - ctlThisUpdate ChoiceOfTime, ASN1UTIL_STEP_OVER_VALUE_OP, rgbChoiceOfTimeTag, // 6 - ctlNextUpdate ChoiceOfTime OPTIONAL, ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP, rgbChoiceOfTimeTag, // 7 - subjectAlgorithm AlgorithmIdentifier, ASN1UTIL_RETURN_VALUE_BLOB_FLAG | ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag, // 8 - trustedSubjects TrustedSubjects OPTIONAL, ASN1UTIL_RETURN_CONTENT_BLOB_FLAG | ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP, rgbSeqTag, // 9 - ctlExtensions [0] EXPLICIT Extensions OPTIONAL ASN1UTIL_RETURN_CONTENT_BLOB_FLAG | ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP, rgbConstructedContext0Tag, }; #define CTL_SEQ_VALUE_INDEX 0 #define CTL_SUBJECT_ALG_VALUE_INDEX 7 #define CTL_SUBJECTS_VALUE_INDEX 8 #define CTL_EXTENSIONS_VALUE_INDEX 9 #define CTL_VALUE_COUNT \ (sizeof(rgExtractCtlPara) / sizeof(rgExtractCtlPara[0])) static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractExtPara[] = { // 0 - Extension ::= SEQUENCE { ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag, // 1 - extnId OBJECT IDENTIFIER, ASN1UTIL_RETURN_CONTENT_BLOB_FLAG | ASN1UTIL_STEP_OVER_VALUE_OP, rgbOIDTag, // 2 - critical BOOLEAN DEFAULT FALSE, ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP, rgbBooleanTag, // 3 - extnValue OCTETSTRING ASN1UTIL_RETURN_CONTENT_BLOB_FLAG | ASN1UTIL_STEP_OVER_VALUE_OP, rgbOctetStringTag, }; #define EXT_OID_VALUE_INDEX 1 #define EXT_OCTETS_VALUE_INDEX 3 #define EXT_VALUE_COUNT \ (sizeof(rgExtractExtPara) / sizeof(rgExtractExtPara[0])) static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractTrustedSubjectPara[] = { // 0 - TrustedSubject ::= SEQUENCE { ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag, // 1 - subjectIdentifier SubjectIdentifier, ASN1UTIL_RETURN_CONTENT_BLOB_FLAG | ASN1UTIL_STEP_OVER_VALUE_OP, rgbOctetStringTag, // 2 - subjectAttributes Attributes OPTIONAL ASN1UTIL_RETURN_VALUE_BLOB_FLAG | ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP, rgbSetTag, }; #define TRUSTED_SUBJECT_IDENTIFIER_VALUE_INDEX 1 #define TRUSTED_SUBJECT_ATTRIBUTES_VALUE_INDEX 2 #define TRUSTED_SUBJECT_VALUE_COUNT \ (sizeof(rgExtractTrustedSubjectPara) / \ sizeof(rgExtractTrustedSubjectPara[0])) // same as above, however, return content blob for subjectAttributes instead // of its value blob static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractTrustedSubjectPara2[] = { // 0 - TrustedSubject ::= SEQUENCE { ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag, // 1 - subjectIdentifier SubjectIdentifier, ASN1UTIL_RETURN_CONTENT_BLOB_FLAG | ASN1UTIL_STEP_OVER_VALUE_OP, rgbOctetStringTag, // 2 - subjectAttributes Attributes OPTIONAL ASN1UTIL_RETURN_CONTENT_BLOB_FLAG | ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP, rgbSetTag, }; static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractAttributePara[] = { // 0 - Attribute ::= SEQUENCE { ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag, // 1 - type ASN1UTIL_RETURN_CONTENT_BLOB_FLAG | ASN1UTIL_STEP_OVER_VALUE_OP, rgbOIDTag, // 2 - values AttributeSetValue ASN1UTIL_RETURN_CONTENT_BLOB_FLAG | ASN1UTIL_STEP_OVER_VALUE_OP, rgbSetTag, }; #define ATTRIBUTE_OID_VALUE_INDEX 1 #define ATTRIBUTE_VALUES_VALUE_INDEX 2 #define ATTRIBUTE_VALUE_COUNT \ (sizeof(rgExtractAttributePara) / sizeof(rgExtractAttributePara[0])) static const DWORD rgdwPrime[] = { // Bits - cHashBucket 1, // 0 - 0x00001 (1) 2, // 1 - 0x00002 (2) 3, // 2 - 0x00004 (4) 7, // 3 - 0x00008 (8) 13, // 4 - 0x00010 (16) 31, // 5 - 0x00020 (32) 61, // 6 - 0x00040 (64) 127, // 7 - 0x00080 (128) 251, // 8 - 0x00100 (256) 509, // 9 - 0x00200 (512) 1021, // 10 - 0x00400 (1024) 2039, // 11 - 0x00800 (2048) 4093, // 12 - 0x01000 (4096) 8191, // 13 - 0x02000 (8192) 16381, // 14 - 0x04000 (16384) 32749, // 15 - 0x08000 (32768) 65521, // 16 - 0x10000 (65536) }; #define MIN_HASH_BUCKET_BITS 6 #define MIN_HASH_BUCKET_COUNT (1 << MIN_HASH_BUCKET_BITS) #define MAX_HASH_BUCKET_BITS 16 #define MAX_HASH_BUCKET_COUNT (1 << MAX_HASH_BUCKET_BITS) #define DEFAULT_BYTES_PER_CTL_ENTRY 100 #define DEFAULT_CTL_ENTRY_COUNT 256 STATIC DWORD GetHashBucketCount( IN DWORD cCtlEntry ) { DWORD cBits; if (MAX_HASH_BUCKET_COUNT <= cCtlEntry) cBits = MAX_HASH_BUCKET_BITS; else { DWORD cHashBucket = MIN_HASH_BUCKET_COUNT; cBits = MIN_HASH_BUCKET_BITS; while (cCtlEntry > cHashBucket) { cHashBucket = cHashBucket << 1; cBits++; } assert(cBits <= MAX_HASH_BUCKET_BITS); } return rgdwPrime[cBits]; } STATIC DWORD GetHashBucketIndex( IN DWORD cHashBucket, IN BOOL fHashedIdentifier, IN const CRYPT_DATA_BLOB *pIdentifier ) { DWORD dwIndex; const BYTE *pb = pIdentifier->pbData; DWORD cb = pIdentifier->cbData; if (fHashedIdentifier) { if (4 <= cb) memcpy(&dwIndex, pb, 4); else dwIndex = 0; } else { dwIndex = 0; while (cb--) { if (dwIndex & 0x80000000) dwIndex = (dwIndex << 1) | 1; else dwIndex = dwIndex << 1; dwIndex += *pb++; } } if (0 == cHashBucket) return 0; else return dwIndex % cHashBucket; } // #define szOID_CTL "1.3.6.1.4.1.311.10.1" static const BYTE rgbOIDCtl[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0A, 0x01}; static const CRYPT_DER_BLOB EncodedOIDCtl = { sizeof(rgbOIDCtl), (BYTE *) rgbOIDCtl }; // #define szOID_SORTED_CTL "1.3.6.1.4.1.311.10.1.1" static const BYTE rgbOIDSortedCtlExt[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0A, 0x01, 0x01}; static const CRYPT_DER_BLOB EncodedOIDSortedCtlExt = { sizeof(rgbOIDSortedCtlExt), (BYTE *) rgbOIDSortedCtlExt }; // The encoded OID only includes the content octets. Excludes the tag and // length octets. STATIC BOOL CompareEncodedOID( IN const CRYPT_DER_BLOB *pEncodedOID1, IN const CRYPT_DER_BLOB *pEncodedOID2 ) { if (pEncodedOID1->cbData == pEncodedOID2->cbData && 0 == memcmp(pEncodedOID1->pbData, pEncodedOID2->pbData, pEncodedOID1->cbData)) return TRUE; else return FALSE; } STATIC BOOL ExtractSortedCtlExtValue( IN const CRYPT_DER_BLOB rgCtlValueBlob[CTL_VALUE_COUNT], OUT const BYTE **ppbSortedCtlExtValue, OUT DWORD *pcbSortedCtlExtValue, OUT const BYTE **ppbRemainExt, OUT DWORD *pcbRemainExt ) { BOOL fResult; const BYTE *pbEncodedExtensions; DWORD cbEncodedExtensions; const BYTE *pbEncodedSortedCtlExt; DWORD cbEncodedSortedCtlExt; DWORD cValue; CRYPT_DER_BLOB rgValueBlob[EXT_VALUE_COUNT]; LONG lSkipped; // Following points to the outer Extensions sequence pbEncodedExtensions = rgCtlValueBlob[CTL_EXTENSIONS_VALUE_INDEX].pbData; cbEncodedExtensions = rgCtlValueBlob[CTL_EXTENSIONS_VALUE_INDEX].cbData; if (0 == cbEncodedExtensions) goto NoExtensions; // Step into the Extension sequence and get pointer to the first extension. // The returned cbEncodedSortedCtlExt includes all of the // extensions in the sequence. if (0 >= (lSkipped = Asn1UtilExtractContent( pbEncodedExtensions, cbEncodedExtensions, &cbEncodedSortedCtlExt, &pbEncodedSortedCtlExt )) || CMSG_INDEFINITE_LENGTH == cbEncodedSortedCtlExt || (DWORD) lSkipped + cbEncodedSortedCtlExt != cbEncodedExtensions) goto InvalidExtensions; // Decode the first extension cValue = EXT_VALUE_COUNT; if (0 >= (lSkipped = Asn1UtilExtractValues( pbEncodedSortedCtlExt, cbEncodedSortedCtlExt, ASN1UTIL_DEFINITE_LENGTH_FLAG, &cValue, rgExtractExtPara, rgValueBlob ))) goto ExtractValuesError; // Check that the first extension is the SortedCtl extension if (!CompareEncodedOID( &rgValueBlob[EXT_OID_VALUE_INDEX], &EncodedOIDSortedCtlExt )) goto NoSortedCtlExtension; *ppbSortedCtlExtValue = rgValueBlob[EXT_OCTETS_VALUE_INDEX].pbData; *pcbSortedCtlExtValue = rgValueBlob[EXT_OCTETS_VALUE_INDEX].cbData; *ppbRemainExt = pbEncodedSortedCtlExt + lSkipped; *pcbRemainExt = cbEncodedSortedCtlExt - lSkipped; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: *ppbSortedCtlExtValue = NULL; *pcbSortedCtlExtValue = 0; *ppbRemainExt = NULL; *pcbRemainExt = 0; fResult = FALSE; goto CommonReturn; SET_ERROR(NoExtensions, ERROR_INVALID_DATA) SET_ERROR(InvalidExtensions, ERROR_INVALID_DATA) TRACE_ERROR(ExtractValuesError) SET_ERROR(NoSortedCtlExtension, ERROR_INVALID_DATA) } BOOL WINAPI SortedCtlInfoEncodeEx( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN PCTL_INFO pOrigCtlInfo, IN DWORD dwFlags, IN OPTIONAL PCRYPT_ENCODE_PARA pEncodePara, OUT OPTIONAL void *pvEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; PCTL_INFO pSortedCtlInfo = NULL; BYTE *pbEncoded = NULL; DWORD cbEncoded; DWORD cCtlEntry; PCTL_ENTRY pSortedCtlEntry = NULL; DWORD cHashBucket = 0; PHASH_BUCKET_ENTRY *ppHashBucketHead = NULL; PHASH_BUCKET_ENTRY pHashBucketEntry = NULL; DWORD cSortedExtension; PCERT_EXTENSION pSortedExtension = NULL; BYTE *pbSortedCtlExtValue = NULL; DWORD cbSortedCtlExtValue = 0; if (0 == (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)) goto InvalidArg; // Make a copy of the CtlInfo. We're going to re-order the CTL entries // and insert a szOID_SORTED_CTL extension. if (NULL == (pSortedCtlInfo = (PCTL_INFO) PkiNonzeroAlloc( sizeof(CTL_INFO)))) goto OutOfMemory; memcpy(pSortedCtlInfo, pOrigCtlInfo, sizeof(CTL_INFO)); cCtlEntry = pSortedCtlInfo->cCTLEntry; if (0 < cCtlEntry) { DWORD i; DWORD j; PCTL_ENTRY pCtlEntry; DWORD cOrigExtension; PCERT_EXTENSION pOrigExtension; BOOL fHashedIdentifier = dwFlags & CRYPT_SORTED_CTL_ENCODE_HASHED_SUBJECT_IDENTIFIER_FLAG; DWORD dwSortedCtlExtFlags = fHashedIdentifier ? SORTED_CTL_EXT_HASHED_SUBJECT_IDENTIFIER_FLAG : 0; DWORD dwMaxCollision = 0; cHashBucket = GetHashBucketCount(cCtlEntry); if (NULL == (ppHashBucketHead = (PHASH_BUCKET_ENTRY *) PkiZeroAlloc( sizeof(PHASH_BUCKET_ENTRY) * cHashBucket))) goto OutOfMemory; if (NULL == (pHashBucketEntry = (PHASH_BUCKET_ENTRY) PkiNonzeroAlloc( sizeof(HASH_BUCKET_ENTRY) * cCtlEntry))) goto OutOfMemory; // Iterate through the CTL entries and add to the appropriate // hash bucket. pCtlEntry = pSortedCtlInfo->rgCTLEntry; for (i = 0; i < cCtlEntry; i++) { DWORD HashBucketIndex; HashBucketIndex = GetHashBucketIndex( cHashBucket, fHashedIdentifier, &pCtlEntry[i].SubjectIdentifier ); pHashBucketEntry[i].dwEntryIndex = i; pHashBucketEntry[i].pNext = ppHashBucketHead[HashBucketIndex]; ppHashBucketHead[HashBucketIndex] = &pHashBucketEntry[i]; } // Sort the entries according to the HashBucket order if (NULL == (pSortedCtlEntry = (PCTL_ENTRY) PkiNonzeroAlloc( sizeof(CTL_ENTRY) * cCtlEntry))) goto OutOfMemory; j = 0; for (i = 0; i < cHashBucket; i++) { DWORD dwCollision = 0; PHASH_BUCKET_ENTRY p; for (p = ppHashBucketHead[i]; p; p = p->pNext) { pSortedCtlEntry[j++] = pCtlEntry[p->dwEntryIndex]; dwCollision++; } if (dwCollision > dwMaxCollision) dwMaxCollision = dwCollision; } #if DBG DbgPrintf(DBG_SS_CRYPT32, "SortedCtlInfoEncodeEx:: cHashBucket: %d MaxCollision: %d Flags:: 0x%x\n", cHashBucket, dwMaxCollision, dwSortedCtlExtFlags); #endif assert(j == cCtlEntry); pSortedCtlInfo->rgCTLEntry = pSortedCtlEntry; // Insert a SortedCtl extension cOrigExtension = pSortedCtlInfo->cExtension; pOrigExtension = pSortedCtlInfo->rgExtension; // Check if the first extension is the SortedCtl extension if (cOrigExtension && 0 == strcmp(pOrigExtension[0].pszObjId, szOID_SORTED_CTL)) { cOrigExtension--; pOrigExtension++; } cSortedExtension = cOrigExtension + 1; if (NULL == (pSortedExtension = (PCERT_EXTENSION) PkiNonzeroAlloc( sizeof(CERT_EXTENSION) * cSortedExtension))) goto OutOfMemory; if (cOrigExtension) memcpy(&pSortedExtension[1], pOrigExtension, sizeof(CERT_EXTENSION) * cOrigExtension); cbSortedCtlExtValue = SORTED_CTL_EXT_HASH_BUCKET_OFFSET + sizeof(DWORD) * (cHashBucket + 1); if (NULL == (pbSortedCtlExtValue = (BYTE *) PkiNonzeroAlloc( cbSortedCtlExtValue))) goto OutOfMemory; memcpy(pbSortedCtlExtValue + SORTED_CTL_EXT_FLAGS_OFFSET, &dwSortedCtlExtFlags, sizeof(DWORD)); memcpy(pbSortedCtlExtValue + SORTED_CTL_EXT_COUNT_OFFSET, &cHashBucket, sizeof(DWORD)); memcpy(pbSortedCtlExtValue + SORTED_CTL_EXT_MAX_COLLISION_OFFSET, &dwMaxCollision, sizeof(DWORD)); pSortedExtension[0].pszObjId = szOID_SORTED_CTL; pSortedExtension[0].fCritical = FALSE; pSortedExtension[0].Value.pbData = pbSortedCtlExtValue; pSortedExtension[0].Value.cbData = cbSortedCtlExtValue; pSortedCtlInfo->cExtension = cSortedExtension; pSortedCtlInfo->rgExtension = pSortedExtension; } if (!CryptEncodeObjectEx( dwCertEncodingType, PKCS_CTL, pSortedCtlInfo, dwFlags, pEncodePara, (void *) &pbEncoded, &cbEncoded )) goto CtlInfoEncodeError; if (0 < cCtlEntry) { // Update the SortedCtl extension's array of hash bucket offsets // First, extract values for the encoded sequence of subjects // and extensions. DWORD i; DWORD cCtlValue; CRYPT_DER_BLOB rgCtlValueBlob[CTL_VALUE_COUNT]; const BYTE *pbEncodedSubject; DWORD cbEncodedSubject; const BYTE *pbEncodedSortedCtlExtValue; DWORD cbEncodedSortedCtlExtValue; const BYTE *pbRemainExt; DWORD cbRemainExt; BYTE *pbEncodedHashBucketOffset; DWORD dwEncodedHashBucketOffset; cCtlValue = CTL_VALUE_COUNT; if (0 >= Asn1UtilExtractValues( pbEncoded, cbEncoded, ASN1UTIL_DEFINITE_LENGTH_FLAG, &cCtlValue, rgExtractCtlPara, rgCtlValueBlob )) goto ExtractCtlValuesError; pbEncodedSubject = rgCtlValueBlob[CTL_SUBJECTS_VALUE_INDEX].pbData; cbEncodedSubject = rgCtlValueBlob[CTL_SUBJECTS_VALUE_INDEX].cbData; assert(pbEncodedSubject > pbEncoded); assert(cbEncodedSubject); assert(rgCtlValueBlob[CTL_EXTENSIONS_VALUE_INDEX].pbData); if (!ExtractSortedCtlExtValue( rgCtlValueBlob, &pbEncodedSortedCtlExtValue, &cbEncodedSortedCtlExtValue, &pbRemainExt, &cbRemainExt )) goto ExtractSortedCtlExtValueError; assert(cbEncodedSortedCtlExtValue == cbSortedCtlExtValue); pbEncodedHashBucketOffset = (BYTE *) pbEncodedSortedCtlExtValue + SORTED_CTL_EXT_HASH_BUCKET_OFFSET; for (i = 0; i < cHashBucket; i++) { PHASH_BUCKET_ENTRY p; dwEncodedHashBucketOffset = (DWORD)(pbEncodedSubject - pbEncoded); memcpy(pbEncodedHashBucketOffset, &dwEncodedHashBucketOffset, sizeof(DWORD)); pbEncodedHashBucketOffset += sizeof(DWORD); // Advance through the encoded subjects for the current // hash bucket index for (p = ppHashBucketHead[i]; p; p = p->pNext) { LONG lTagLength; DWORD cbContent; const BYTE *pbContent; DWORD cbSubject; lTagLength = Asn1UtilExtractContent( pbEncodedSubject, cbEncodedSubject, &cbContent, &pbContent ); assert(lTagLength > 0 && CMSG_INDEFINITE_LENGTH != cbContent); cbSubject = cbContent + lTagLength; assert(cbEncodedSubject >= cbSubject); pbEncodedSubject += cbSubject; cbEncodedSubject -= cbSubject; } } assert(0 == cbEncodedSubject); assert(pbEncodedSubject == rgCtlValueBlob[CTL_SUBJECTS_VALUE_INDEX].pbData + rgCtlValueBlob[CTL_SUBJECTS_VALUE_INDEX].cbData); assert(pbEncodedHashBucketOffset + sizeof(DWORD) == pbEncodedSortedCtlExtValue + cbEncodedSortedCtlExtValue); dwEncodedHashBucketOffset = (DWORD)(pbEncodedSubject - pbEncoded); memcpy(pbEncodedHashBucketOffset, &dwEncodedHashBucketOffset, sizeof(DWORD)); } *((BYTE **) pvEncoded) = pbEncoded; *pcbEncoded = cbEncoded; fResult = TRUE; CommonReturn: PkiFree(pSortedCtlInfo); PkiFree(pSortedCtlEntry); PkiFree(ppHashBucketHead); PkiFree(pHashBucketEntry); PkiFree(pSortedExtension); PkiFree(pbSortedCtlExtValue); return fResult; ErrorReturn: if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) { if (pbEncoded) { PFN_CRYPT_FREE pfnFree = PkiGetEncodeFreeFunction(pEncodePara); pfnFree(pbEncoded); } *((BYTE **) pvEncoded) = NULL; } *pcbEncoded = 0; fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(OutOfMemory) TRACE_ERROR(CtlInfoEncodeError) TRACE_ERROR(ExtractCtlValuesError) TRACE_ERROR(ExtractSortedCtlExtValueError) } STATIC BOOL CreateSortedCtlHashBuckets( IN OUT PSORTED_CTL_FIND_INFO pSortedCtlFindInfo ) { BOOL fResult; DWORD cHashBucket; DWORD *pdwHashBucketHead = NULL; PHASH_BUCKET_ENTRY pHashBucketEntry = NULL; DWORD cAllocEntry = 0; DWORD cEntry = 0; const BYTE *pbEncoded; DWORD cbEncoded; #if DBG DWORD dwMaxCollision = 0; #endif DWORD dwExceptionCode; // Handle MappedFile Exceptions __try { pbEncoded = pSortedCtlFindInfo->pbEncodedSubjects; cbEncoded = pSortedCtlFindInfo->cbEncodedSubjects; cHashBucket = GetHashBucketCount(cbEncoded / DEFAULT_BYTES_PER_CTL_ENTRY); if (NULL == (pdwHashBucketHead = (DWORD *) PkiZeroAlloc( sizeof(DWORD) * cHashBucket))) goto OutOfMemory; // Loop through the encoded trusted subjects. For each subject, create // hash bucket entry, calculate hash bucket index and insert. while (cbEncoded) { DWORD cValue; LONG lAllValues; DWORD HashBucketIndex; CRYPT_DER_BLOB rgValueBlob[TRUSTED_SUBJECT_VALUE_COUNT]; cValue = TRUSTED_SUBJECT_VALUE_COUNT; if (0 >= (lAllValues = Asn1UtilExtractValues( pbEncoded, cbEncoded, ASN1UTIL_DEFINITE_LENGTH_FLAG, &cValue, rgExtractTrustedSubjectPara, rgValueBlob ))) goto ExtractValuesError; if (cEntry == cAllocEntry) { PHASH_BUCKET_ENTRY pNewHashBucketEntry; cAllocEntry += DEFAULT_CTL_ENTRY_COUNT; if (NULL == (pNewHashBucketEntry = (PHASH_BUCKET_ENTRY) PkiRealloc( pHashBucketEntry, sizeof(HASH_BUCKET_ENTRY) * cAllocEntry))) goto OutOfMemory; pHashBucketEntry = pNewHashBucketEntry; } if (0 == cEntry) // Entry[0] is used to indicate no entries for the indexed // HashBucket cEntry++; HashBucketIndex = GetHashBucketIndex( cHashBucket, FALSE, // fHashedIdentifier, &rgValueBlob[TRUSTED_SUBJECT_IDENTIFIER_VALUE_INDEX] ); #if DBG { DWORD dwEntryIndex = pdwHashBucketHead[HashBucketIndex]; DWORD dwCollision = 1; while (dwEntryIndex) { dwCollision++; dwEntryIndex = pHashBucketEntry[dwEntryIndex].iNext; } if (dwCollision > dwMaxCollision) dwMaxCollision = dwCollision; } #endif pHashBucketEntry[cEntry].iNext = pdwHashBucketHead[HashBucketIndex]; pHashBucketEntry[cEntry].pbEntry = pbEncoded; pdwHashBucketHead[HashBucketIndex] = cEntry; cEntry++; cbEncoded -= lAllValues; pbEncoded += lAllValues; } } __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; } #if DBG DbgPrintf(DBG_SS_CRYPT32, "CreateSortedCtlHashBuckets:: cEntry: %d cHashBucket: %d MaxCollision: %d\n", cEntry, cHashBucket, dwMaxCollision); #endif pSortedCtlFindInfo->cHashBucket = cHashBucket; pSortedCtlFindInfo->pdwHashBucketHead = pdwHashBucketHead; pSortedCtlFindInfo->pHashBucketEntry = pHashBucketEntry; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: PkiFree(pdwHashBucketHead); PkiFree(pHashBucketEntry); fResult = FALSE; goto CommonReturn; TRACE_ERROR(ExtractValuesError) TRACE_ERROR(OutOfMemory) SET_ERROR_VAR(ExceptionError, dwExceptionCode) } STATIC BOOL FastDecodeCtlSubjects( IN const BYTE *pbEncodedSubjects, IN DWORD cbEncodedSubjects, OUT DWORD *pcCTLEntry, OUT PCTL_ENTRY *ppCTLEntry ) { BOOL fResult; PCTL_ENTRY pAllocEntry = NULL; DWORD cAllocEntry; DWORD cbAllocEntry; DWORD cEntry = 0; PCTL_ENTRY pEntry; DWORD cbEntryEncoded; const BYTE *pbEntryEncoded; DWORD cAttr = 0; PCRYPT_ATTRIBUTE pAttr; DWORD cAttrValue = 0; PCRYPT_ATTR_BLOB pAttrValue; DWORD cValue; LONG lAllValues; DWORD dwExceptionCode; // Handle MappedFile Exceptions __try { // First: Loop through the encoded trusted subjects. Get total count of // Entries, Attributes and Values. cbEntryEncoded = cbEncodedSubjects; pbEntryEncoded = pbEncodedSubjects; while (cbEntryEncoded) { CRYPT_DER_BLOB rgEntryValueBlob[TRUSTED_SUBJECT_VALUE_COUNT]; DWORD cbAttrEncoded; const BYTE *pbAttrEncoded; cValue = TRUSTED_SUBJECT_VALUE_COUNT; if (0 >= (lAllValues = Asn1UtilExtractValues( pbEntryEncoded, cbEntryEncoded, ASN1UTIL_DEFINITE_LENGTH_FLAG, &cValue, rgExtractTrustedSubjectPara2, rgEntryValueBlob ))) goto ExtractEntryError; cEntry++; cbEntryEncoded -= lAllValues; pbEntryEncoded += lAllValues; cbAttrEncoded = rgEntryValueBlob[TRUSTED_SUBJECT_ATTRIBUTES_VALUE_INDEX].cbData; pbAttrEncoded = rgEntryValueBlob[TRUSTED_SUBJECT_ATTRIBUTES_VALUE_INDEX].pbData; while (cbAttrEncoded) { CRYPT_DER_BLOB rgAttrValueBlob[ATTRIBUTE_VALUE_COUNT]; DWORD cbAttrValueEncoded; const BYTE *pbAttrValueEncoded; cValue = ATTRIBUTE_VALUE_COUNT; if (0 >= (lAllValues = Asn1UtilExtractValues( pbAttrEncoded, cbAttrEncoded, ASN1UTIL_DEFINITE_LENGTH_FLAG, &cValue, rgExtractAttributePara, rgAttrValueBlob ))) goto ExtractAttrError; cAttr++; cbAttrEncoded -= lAllValues; pbAttrEncoded += lAllValues; cbAttrValueEncoded = rgAttrValueBlob[ATTRIBUTE_VALUES_VALUE_INDEX].cbData; pbAttrValueEncoded = rgAttrValueBlob[ATTRIBUTE_VALUES_VALUE_INDEX].pbData; while (cbAttrValueEncoded) { LONG lTagLength; DWORD cbAttrValue; const BYTE *pbContent; lTagLength = Asn1UtilExtractContent( pbAttrValueEncoded, cbAttrValueEncoded, &cbAttrValue, &pbContent ); if (0 >= lTagLength || CMSG_INDEFINITE_LENGTH == cbAttrValue) goto ExtractValueError; cbAttrValue += (DWORD) lTagLength; if (cbAttrValue > cbAttrValueEncoded) goto ExtractValueError; cAttrValue++; cbAttrValueEncoded -= cbAttrValue; pbAttrValueEncoded += cbAttrValue; } } } cAllocEntry = cEntry; if (0 == cEntry) goto SuccessReturn; cbAllocEntry = cEntry * sizeof(CTL_ENTRY) + cAttr * sizeof(CRYPT_ATTRIBUTE) + cAttrValue * sizeof(CRYPT_ATTR_BLOB); if (NULL == (pAllocEntry = (PCTL_ENTRY) PkiZeroAlloc(cbAllocEntry))) goto OutOfMemory; pEntry = pAllocEntry; pAttr = (PCRYPT_ATTRIBUTE) (pEntry + cEntry); pAttrValue = (PCRYPT_ATTR_BLOB) (pAttr + cAttr); // Second: Loop through the encoded trusted subjects. Update the // allocated Entries, Attributes and Values data structures cbEntryEncoded = cbEncodedSubjects; pbEntryEncoded = pbEncodedSubjects; while (cbEntryEncoded) { CRYPT_DER_BLOB rgEntryValueBlob[TRUSTED_SUBJECT_VALUE_COUNT]; DWORD cbAttrEncoded; const BYTE *pbAttrEncoded; cValue = TRUSTED_SUBJECT_VALUE_COUNT; if (0 >= (lAllValues = Asn1UtilExtractValues( pbEntryEncoded, cbEntryEncoded, ASN1UTIL_DEFINITE_LENGTH_FLAG, &cValue, rgExtractTrustedSubjectPara2, rgEntryValueBlob ))) goto ExtractEntryError; cbEntryEncoded -= lAllValues; pbEntryEncoded += lAllValues; assert(0 != cEntry); if (0 == cEntry--) goto InvalidCountError; pEntry->SubjectIdentifier = rgEntryValueBlob[TRUSTED_SUBJECT_IDENTIFIER_VALUE_INDEX]; cbAttrEncoded = rgEntryValueBlob[TRUSTED_SUBJECT_ATTRIBUTES_VALUE_INDEX].cbData; pbAttrEncoded = rgEntryValueBlob[TRUSTED_SUBJECT_ATTRIBUTES_VALUE_INDEX].pbData; while (cbAttrEncoded) { CRYPT_DER_BLOB rgAttrValueBlob[ATTRIBUTE_VALUE_COUNT]; DWORD cbAttrValueEncoded; const BYTE *pbAttrValueEncoded; ASN1encodedOID_t EncodedOid; BYTE *pbExtra; LONG lRemainExtra; cValue = ATTRIBUTE_VALUE_COUNT; if (0 >= (lAllValues = Asn1UtilExtractValues( pbAttrEncoded, cbAttrEncoded, ASN1UTIL_DEFINITE_LENGTH_FLAG, &cValue, rgExtractAttributePara, rgAttrValueBlob ))) goto ExtractAttrError; cbAttrEncoded -= lAllValues; pbAttrEncoded += lAllValues; assert(0 != cAttr); if (0 == cAttr--) goto InvalidCountError; if (0 == pEntry->cAttribute) { pEntry->cAttribute = 1; pEntry->rgAttribute = pAttr; } else pEntry->cAttribute++; EncodedOid.length = (ASN1uint16_t) rgAttrValueBlob[ATTRIBUTE_OID_VALUE_INDEX].cbData; EncodedOid.value = rgAttrValueBlob[ATTRIBUTE_OID_VALUE_INDEX].pbData; pbExtra = NULL; lRemainExtra = 0; I_CryptGetEncodedOID( &EncodedOid, CRYPT_DECODE_SHARE_OID_STRING_FLAG, &pAttr->pszObjId, &pbExtra, &lRemainExtra ); cbAttrValueEncoded = rgAttrValueBlob[ATTRIBUTE_VALUES_VALUE_INDEX].cbData; pbAttrValueEncoded = rgAttrValueBlob[ATTRIBUTE_VALUES_VALUE_INDEX].pbData; while (cbAttrValueEncoded) { LONG lTagLength; DWORD cbAttrValue; const BYTE *pbContent; lTagLength = Asn1UtilExtractContent( pbAttrValueEncoded, cbAttrValueEncoded, &cbAttrValue, &pbContent ); if (0 >= lTagLength || CMSG_INDEFINITE_LENGTH == cbAttrValue) goto ExtractValueError; cbAttrValue += (DWORD) lTagLength; if (cbAttrValue > cbAttrValueEncoded) goto ExtractValueError; assert(0 != cAttrValue); if (0 == cAttrValue--) goto InvalidCountError; if (0 == pAttr->cValue) { pAttr->cValue = 1; pAttr->rgValue = pAttrValue; } else pAttr->cValue++; pAttrValue->cbData = cbAttrValue; pAttrValue->pbData = (BYTE *) pbAttrValueEncoded; pAttrValue++; cbAttrValueEncoded -= cbAttrValue; pbAttrValueEncoded += cbAttrValue; } pAttr++; } pEntry++; } assert(0 == cEntry && 0 == cAttr && 0 == cAttrValue); } __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; } SuccessReturn: fResult = TRUE; CommonReturn: *pcCTLEntry = cAllocEntry; *ppCTLEntry = pAllocEntry; return fResult; ErrorReturn: PkiFree(pAllocEntry); pAllocEntry = NULL; cAllocEntry = 0; fResult = FALSE; goto CommonReturn; SET_ERROR(ExtractEntryError, ERROR_INVALID_DATA) SET_ERROR(ExtractAttrError, ERROR_INVALID_DATA) SET_ERROR(ExtractValueError, ERROR_INVALID_DATA) SET_ERROR(InvalidCountError, ERROR_INVALID_DATA) TRACE_ERROR(OutOfMemory) SET_ERROR_VAR(ExceptionError, dwExceptionCode) } // pbCtlEncoded has already been allocated STATIC PCONTEXT_ELEMENT FastCreateCtlElement( IN PCERT_STORE pStore, IN DWORD dwMsgAndCertEncodingType, IN const BYTE *pbCtlEncoded, IN DWORD cbCtlEncoded, IN OPTIONAL PSHARE_ELEMENT pShareEle, IN DWORD dwFlags ) { DWORD dwEncodingType; const BYTE *pbContent; // not allocated DWORD cbContent; PCTL_INFO pInfo = NULL; PCONTEXT_ELEMENT pEle = NULL; PCTL_CONTEXT pCtl; PCTL_CONTEXT_SUFFIX pCtlSuffix; // not allocated PSORTED_CTL_FIND_INFO pSortedCtlFindInfo; // not allocated const BYTE *pbCtlEncodedHdr; // not allocated DWORD cbCtlEncodedHdr; BYTE *pbCtlReencodedHdr = NULL; DWORD cbCtlReencodedHdr; DWORD cCtlValue; CRYPT_DER_BLOB rgCtlValueBlob[CTL_VALUE_COUNT]; const BYTE *pbEncodedSubjects; // not allocated DWORD cbEncodedSubjects; const BYTE *pbSortedCtlExtValue; // not allocated DWORD cbSortedCtlExtValue; const BYTE *pbRemainExt; // not allocated DWORD cbRemainExt; PCERT_EXTENSIONS pExtInfo = NULL; BYTE *pbAllocReencodedExt = NULL; BYTE *pbReencodedExt = NULL; // not allocated DWORD cbReencodedExt; HCRYPTMSG hMsg = NULL; PCTL_ENTRY pCTLEntry = NULL; DWORD cCTLEntry; DWORD dwExceptionCode; // Handle MappedFile Exceptions __try { if (0 == (dwMsgAndCertEncodingType = GetCtlEncodingType( dwMsgAndCertEncodingType))) goto InvalidArg; // The message encoding type takes precedence dwEncodingType = (dwMsgAndCertEncodingType >> 16) & CERT_ENCODING_TYPE_MASK; // Advance to the encoded CTL_INFO if (0 >= Asn1UtilExtractPKCS7SignedDataContent( pbCtlEncoded, cbCtlEncoded, &EncodedOIDCtl, &cbContent, &pbContent )) goto ExtractSignedDataContentError; if (CMSG_INDEFINITE_LENGTH == cbContent) goto UnsupportedIndefiniteLength; // Get pointers to the encoded CTL_INFO values cCtlValue = CTL_VALUE_COUNT; if (0 >= Asn1UtilExtractValues( pbContent, cbContent, ASN1UTIL_DEFINITE_LENGTH_FLAG, &cCtlValue, rgExtractCtlPara, rgCtlValueBlob )) goto ExtractCtlValuesError; pbEncodedSubjects = rgCtlValueBlob[CTL_SUBJECTS_VALUE_INDEX].pbData; cbEncodedSubjects = rgCtlValueBlob[CTL_SUBJECTS_VALUE_INDEX].cbData; // Initialize pointer to and length of the Extensions sequence pbReencodedExt = rgCtlValueBlob[CTL_EXTENSIONS_VALUE_INDEX].pbData; cbReencodedExt = rgCtlValueBlob[CTL_EXTENSIONS_VALUE_INDEX].cbData; // Get pointer to the first value in the CTL sequence. Get length // through the subjectAlgorithm value. Don't include the TrustedSubjects // or Extensions. pbCtlEncodedHdr = rgCtlValueBlob[CTL_SEQ_VALUE_INDEX].pbData; cbCtlEncodedHdr = (DWORD)(rgCtlValueBlob[CTL_SUBJECT_ALG_VALUE_INDEX].pbData + rgCtlValueBlob[CTL_SUBJECT_ALG_VALUE_INDEX].cbData - pbCtlEncodedHdr); // Re-encode the CTL excluding the TrustedSubjects and Extensions. // Re-encode the CTL sequence to have an indefinite length and terminated // with a NULL tag and length. cbCtlReencodedHdr = cbCtlEncodedHdr + 2 + 2; if (NULL == (pbCtlReencodedHdr = (BYTE *) PkiNonzeroAlloc( cbCtlReencodedHdr))) goto OutOfMemory; pbCtlReencodedHdr[0] = ASN1UTIL_TAG_SEQ; pbCtlReencodedHdr[1] = ASN1UTIL_LENGTH_INDEFINITE; memcpy(pbCtlReencodedHdr + 2, pbCtlEncodedHdr, cbCtlEncodedHdr); pbCtlReencodedHdr[cbCtlEncodedHdr + 2] = ASN1UTIL_TAG_NULL; pbCtlReencodedHdr[cbCtlEncodedHdr + 3] = ASN1UTIL_LENGTH_NULL; // Decode CTL_INFO excluding the TrustedSubjects and Extensions if (NULL == (pInfo = (PCTL_INFO) AllocAndDecodeObject( dwEncodingType, PKCS_CTL, pbCtlReencodedHdr, cbCtlReencodedHdr, 0 // dwFlags ))) goto DecodeCtlError; // Allocate and initialize the CTL element structure if (NULL == (pEle = (PCONTEXT_ELEMENT) PkiZeroAlloc( sizeof(CONTEXT_ELEMENT) + sizeof(CTL_CONTEXT) + sizeof(CTL_CONTEXT_SUFFIX) + sizeof(SORTED_CTL_FIND_INFO)))) goto OutOfMemory; pEle->dwElementType = ELEMENT_TYPE_CACHE; pEle->dwContextType = CERT_STORE_CTL_CONTEXT - 1; pEle->lRefCnt = 1; pEle->pEle = pEle; pEle->pStore = pStore; pEle->pProvStore = pStore; pEle->pShareEle = pShareEle; pCtl = (PCTL_CONTEXT) ToCtlContext(pEle); pCtl->dwMsgAndCertEncodingType = dwMsgAndCertEncodingType; pCtl->pbCtlEncoded = (BYTE *) pbCtlEncoded; pCtl->cbCtlEncoded = cbCtlEncoded; pCtl->pCtlInfo = pInfo; pCtl->hCertStore = (HCERTSTORE) pStore; // pCtl->hCryptMsg = NULL; pCtl->pbCtlContent = (BYTE *) pbContent; pCtl->cbCtlContent = cbContent; pCtlSuffix = ToCtlContextSuffix(pEle); // pCtlSuffix->ppSortedEntry = NULL; pCtlSuffix->fFastCreate = TRUE; if (0 == (dwFlags & CERT_CREATE_CONTEXT_SORTED_FLAG)) { if (0 == (dwFlags & CERT_CREATE_CONTEXT_NO_ENTRY_FLAG)) { if (!FastDecodeCtlSubjects( pbEncodedSubjects, cbEncodedSubjects, &cCTLEntry, &pCTLEntry )) goto FastDecodeCtlSubjectsError; pInfo->cCTLEntry = cCTLEntry; pInfo->rgCTLEntry = pCTLEntry; pCtlSuffix->pCTLEntry = pCTLEntry; } if (0 == (dwFlags & CERT_CREATE_CONTEXT_NO_HCRYPTMSG_FLAG)) { BOOL fResult; DWORD dwLastErr = 0; HCRYPTPROV hProv = 0; DWORD dwProvFlags = 0; // Attempt to get the store's crypt provider. Serialize crypto // operations. hProv = GetCryptProv(pStore, &dwProvFlags); hMsg = CryptMsgOpenToDecode( dwMsgAndCertEncodingType, 0, // dwFlags 0, // dwMsgType hProv, NULL, // pRecipientInfo NULL // pStreamInfo ); if (hMsg && CryptMsgUpdate( hMsg, pbCtlEncoded, cbCtlEncoded, TRUE // fFinal )) fResult = TRUE; else { fResult = FALSE; dwLastErr = GetLastError(); } // For the store's crypt provider, release reference count. Leave // crypto operations critical section. ReleaseCryptProv(pStore, dwProvFlags); if (!fResult) { SetLastError(dwLastErr); goto MsgError; } pCtl->hCryptMsg = hMsg; } } else { pSortedCtlFindInfo = (PSORTED_CTL_FIND_INFO) ((BYTE *) pCtlSuffix + sizeof(CTL_CONTEXT_SUFFIX)); pCtlSuffix->pSortedCtlFindInfo = pSortedCtlFindInfo; pSortedCtlFindInfo->pbEncodedSubjects = pbEncodedSubjects; pSortedCtlFindInfo->cbEncodedSubjects = cbEncodedSubjects; // Check if the CTL had the SORTED_CTL extension. If it does, update // the find info to point to the extension's hash bucket entry // offsets. if (ExtractSortedCtlExtValue( rgCtlValueBlob, &pbSortedCtlExtValue, &cbSortedCtlExtValue, &pbRemainExt, &cbRemainExt )) { DWORD dwCtlExtFlags; if (SORTED_CTL_EXT_HASH_BUCKET_OFFSET > cbSortedCtlExtValue) goto InvalidSortedCtlExtension; memcpy(&dwCtlExtFlags, pbSortedCtlExtValue + SORTED_CTL_EXT_FLAGS_OFFSET, sizeof(DWORD)); pSortedCtlFindInfo->fHashedIdentifier = dwCtlExtFlags & SORTED_CTL_EXT_HASHED_SUBJECT_IDENTIFIER_FLAG; memcpy(&pSortedCtlFindInfo->cHashBucket, pbSortedCtlExtValue + SORTED_CTL_EXT_COUNT_OFFSET, sizeof(DWORD)); pSortedCtlFindInfo->pbEncodedHashBucket = pbSortedCtlExtValue + SORTED_CTL_EXT_HASH_BUCKET_OFFSET; if (MAX_HASH_BUCKET_COUNT < pSortedCtlFindInfo->cHashBucket || SORTED_CTL_EXT_HASH_BUCKET_OFFSET + (pSortedCtlFindInfo->cHashBucket + 1) * sizeof(DWORD) > cbSortedCtlExtValue) goto InvalidSortedCtlExtension; if (0 == cbRemainExt) cbReencodedExt = 0; else { // Reencode the remaining extensions. // Re-encode the Extensions sequence to have an indefinite // length and terminated with a NULL tag and length. cbReencodedExt = cbRemainExt + 2 + 2; if (NULL == (pbAllocReencodedExt = (BYTE *) PkiNonzeroAlloc(cbReencodedExt))) goto OutOfMemory; pbReencodedExt = pbAllocReencodedExt; pbReencodedExt[0] = ASN1UTIL_TAG_SEQ; pbReencodedExt[1] = ASN1UTIL_LENGTH_INDEFINITE; memcpy(pbReencodedExt + 2, pbRemainExt, cbRemainExt); pbReencodedExt[cbRemainExt + 2] = ASN1UTIL_TAG_NULL; pbReencodedExt[cbRemainExt + 3] = ASN1UTIL_LENGTH_NULL; } } else if (cbEncodedSubjects) { if (!CreateSortedCtlHashBuckets(pSortedCtlFindInfo)) goto CreateSortedCtlHashBucketsError; } } if (cbReencodedExt) { if (NULL == (pExtInfo = (PCERT_EXTENSIONS) AllocAndDecodeObject( dwEncodingType, X509_EXTENSIONS, pbReencodedExt, cbReencodedExt, 0 // dwFlags ))) goto DecodeExtError; pInfo->cExtension = pExtInfo->cExtension; pInfo->rgExtension = pExtInfo->rgExtension; pCtlSuffix->pExtInfo = pExtInfo; } } __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; } if (NULL == pShareEle) { CertPerfIncrementCtlElementCurrentCount(); CertPerfIncrementCtlElementTotalCount(); } CommonReturn: PkiFree(pbCtlReencodedHdr); PkiFree(pbAllocReencodedExt); return pEle; ErrorReturn: if (hMsg) CryptMsgClose(hMsg); PkiFree(pInfo); PkiFree(pExtInfo); PkiFree(pCTLEntry); if (pEle) { PkiFree(pEle); pEle = NULL; } goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(ExtractSignedDataContentError) SET_ERROR(UnsupportedIndefiniteLength, ERROR_INVALID_DATA) TRACE_ERROR(ExtractCtlValuesError) TRACE_ERROR(OutOfMemory) TRACE_ERROR(DecodeCtlError) TRACE_ERROR(DecodeExtError) SET_ERROR(InvalidSortedCtlExtension, ERROR_INVALID_DATA) TRACE_ERROR(CreateSortedCtlHashBucketsError) TRACE_ERROR(FastDecodeCtlSubjectsError) TRACE_ERROR(MsgError) SET_ERROR_VAR(ExceptionError, dwExceptionCode) } //+------------------------------------------------------------------------- // Creates the specified context from the encoded bytes. The created // context isn't put in a store. // // dwContextType values: // CERT_STORE_CERTIFICATE_CONTEXT // CERT_STORE_CRL_CONTEXT // CERT_STORE_CTL_CONTEXT // // If CERT_CREATE_CONTEXT_NOCOPY_FLAG is set, the created context points // directly to the pbEncoded instead of an allocated copy. See flag // definition for more details. // // If CERT_CREATE_CONTEXT_SORTED_FLAG is set, the context is created // with sorted entries. This flag may only be set for CERT_STORE_CTL_CONTEXT. // Setting this flag implicitly sets CERT_CREATE_CONTEXT_NO_HCRYPTMSG_FLAG and // CERT_CREATE_CONTEXT_NO_ENTRY_FLAG. See flag definition for // more details. // // If CERT_CREATE_CONTEXT_NO_HCRYPTMSG_FLAG is set, the context is created // without creating a HCRYPTMSG handle for the context. This flag may only be // set for CERT_STORE_CTL_CONTEXT. See flag definition for more details. // // If CERT_CREATE_CONTEXT_NO_ENTRY_FLAG is set, the context is created // without decoding the entries. This flag may only be set for // CERT_STORE_CTL_CONTEXT. See flag definition for more details. // // If unable to decode and create the context, NULL is returned. // Otherwise, a pointer to a read only CERT_CONTEXT, CRL_CONTEXT or // CTL_CONTEXT is returned. The context must be freed by the appropriate // free context API. The context can be duplicated by calling the // appropriate duplicate context API. //-------------------------------------------------------------------------- const void * WINAPI CertCreateContext( IN DWORD dwContextType, IN DWORD dwEncodingType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwFlags, IN OPTIONAL PCERT_CREATE_CONTEXT_PARA pCreatePara ) { BYTE *pbAllocEncoded = NULL; PCONTEXT_ELEMENT pEle = NULL; PCONTEXT_NOCOPY_INFO pNoCopyInfo = NULL; PCONTEXT_ELEMENT pStoreEle; // not allocated DWORD dwExceptionCode; // Handle MappedFile Exceptions __try { dwContextType--; if (CONTEXT_COUNT <= dwContextType) goto InvalidContextType; if (dwFlags & CERT_CREATE_CONTEXT_NOCOPY_FLAG) { if (NULL == (pNoCopyInfo = (PCONTEXT_NOCOPY_INFO) PkiZeroAlloc( sizeof(CONTEXT_NOCOPY_INFO)))) goto OutOfMemory; if (pCreatePara && pCreatePara->cbSize >= offsetof(CERT_CREATE_CONTEXT_PARA, pfnFree) + sizeof(pCreatePara->pfnFree)) { pNoCopyInfo->pfnFree = pCreatePara->pfnFree; if (pCreatePara->cbSize >= offsetof(CERT_CREATE_CONTEXT_PARA, pvFree) + sizeof(pCreatePara->pvFree) && pCreatePara->pvFree) pNoCopyInfo->pvFree = pCreatePara->pvFree; else pNoCopyInfo->pvFree = (void *) pbEncoded; } } else { if (NULL == (pbAllocEncoded = (BYTE *) PkiNonzeroAlloc(cbEncoded))) goto OutOfMemory; memcpy(pbAllocEncoded, pbEncoded, cbEncoded); pbEncoded = pbAllocEncoded; } if (CERT_STORE_CTL_CONTEXT - 1 == dwContextType) pEle = FastCreateCtlElement( &NullCertStore, dwEncodingType, pbEncoded, cbEncoded, NULL, // pShareEle dwFlags ); else pEle = rgpfnCreateElement[dwContextType]( &NullCertStore, dwEncodingType, (BYTE *) pbEncoded, cbEncoded, NULL // pShareEle ); if (NULL == pEle) goto CreateElementError; pEle->pNoCopyInfo = pNoCopyInfo; if (!AddElementToStore( &NullCertStore, pEle, CERT_STORE_ADD_ALWAYS, &pStoreEle )) goto AddElementError; } __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; } CommonReturn: // Any To*Context would work return ToCertContext(pStoreEle); ErrorReturn: if (pEle) FreeContextElement(pEle); else if (pNoCopyInfo) { if (pNoCopyInfo->pfnFree) pNoCopyInfo->pfnFree(pNoCopyInfo->pvFree); PkiFree(pNoCopyInfo); } else PkiFree(pbAllocEncoded); pStoreEle = NULL; goto CommonReturn; SET_ERROR(InvalidContextType, E_INVALIDARG) TRACE_ERROR(OutOfMemory) TRACE_ERROR(CreateElementError) TRACE_ERROR(AddElementError) SET_ERROR_VAR(ExceptionError, dwExceptionCode) } STATIC BOOL IsTrustedSubject( IN PCRYPT_DATA_BLOB pSubjectIdentifier, IN OUT const BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded, OUT BOOL *pfTrusted, OUT OPTIONAL PCRYPT_DATA_BLOB pEncodedAttributes ) { const BYTE *pbEncoded = *ppbEncoded; DWORD cbEncoded = *pcbEncoded; DWORD cValue; LONG lAllValues; CRYPT_DER_BLOB rgValueBlob[TRUSTED_SUBJECT_VALUE_COUNT]; cValue = TRUSTED_SUBJECT_VALUE_COUNT; if (0 >= (lAllValues = Asn1UtilExtractValues( pbEncoded, cbEncoded, ASN1UTIL_DEFINITE_LENGTH_FLAG, &cValue, rgExtractTrustedSubjectPara, rgValueBlob ))) { *pfTrusted = FALSE; return FALSE; } if (pSubjectIdentifier->cbData == rgValueBlob[TRUSTED_SUBJECT_IDENTIFIER_VALUE_INDEX].cbData && 0 == memcmp(pSubjectIdentifier->pbData, rgValueBlob[ TRUSTED_SUBJECT_IDENTIFIER_VALUE_INDEX].pbData, pSubjectIdentifier->cbData)) { *pfTrusted = TRUE; if (pEncodedAttributes) *pEncodedAttributes = rgValueBlob[TRUSTED_SUBJECT_ATTRIBUTES_VALUE_INDEX]; } else { cbEncoded -= lAllValues; *pcbEncoded = cbEncoded; pbEncoded += lAllValues; *ppbEncoded = pbEncoded; *pfTrusted = FALSE; } return TRUE; } //+------------------------------------------------------------------------- // Returns TRUE if the SubjectIdentifier exists in the CTL. Optionally // returns a pointer to and byte count of the Subject's encoded attributes. //-------------------------------------------------------------------------- BOOL WINAPI CertFindSubjectInSortedCTL( IN PCRYPT_DATA_BLOB pSubjectIdentifier, IN PCCTL_CONTEXT pCtlContext, IN DWORD dwFlags, IN void *pvReserved, OUT OPTIONAL PCRYPT_DER_BLOB pEncodedAttributes ) { PCONTEXT_ELEMENT pCacheEle; // not allocated PSORTED_CTL_FIND_INFO pSortedCtlFindInfo; // not allocated DWORD HashBucketIndex; BOOL fTrusted; if (NULL == (pCacheEle = GetCacheElement(ToContextElement(pCtlContext)))) goto NoCacheElementError; if (NULL == (pSortedCtlFindInfo = ToCtlContextSuffix(pCacheEle)->pSortedCtlFindInfo)) goto NotSortedCtlContext; HashBucketIndex = GetHashBucketIndex( pSortedCtlFindInfo->cHashBucket, pSortedCtlFindInfo->fHashedIdentifier, pSubjectIdentifier ); if (pSortedCtlFindInfo->pbEncodedHashBucket) { DWORD dwEntryOffset[2]; DWORD cbEncoded; const BYTE *pbEncoded; memcpy(dwEntryOffset, pSortedCtlFindInfo->pbEncodedHashBucket + sizeof(DWORD) * HashBucketIndex, sizeof(DWORD) * 2); if (dwEntryOffset[1] < dwEntryOffset[0] || dwEntryOffset[1] > pCtlContext->cbCtlContent) goto InvalidSortedCtlExtension; // Iterate through the encoded TrustedSubjects until a match // or reached a TrustedSubject in the next HashBucket. cbEncoded = dwEntryOffset[1] - dwEntryOffset[0]; pbEncoded = pCtlContext->pbCtlContent + dwEntryOffset[0]; while (cbEncoded) { if (!IsTrustedSubject( pSubjectIdentifier, &pbEncoded, &cbEncoded, &fTrusted, pEncodedAttributes)) goto IsTrustedSubjectError; if (fTrusted) goto CommonReturn; } } else if (pSortedCtlFindInfo->pdwHashBucketHead) { DWORD dwEntryIndex; dwEntryIndex = pSortedCtlFindInfo->pdwHashBucketHead[HashBucketIndex]; while (dwEntryIndex) { PHASH_BUCKET_ENTRY pHashBucketEntry; DWORD cbEncoded; const BYTE *pbEncoded; pHashBucketEntry = &pSortedCtlFindInfo->pHashBucketEntry[dwEntryIndex]; pbEncoded = pHashBucketEntry->pbEntry; assert(pbEncoded >= pSortedCtlFindInfo->pbEncodedSubjects && pbEncoded < pSortedCtlFindInfo->pbEncodedSubjects + pSortedCtlFindInfo->cbEncodedSubjects); cbEncoded = (DWORD)(pSortedCtlFindInfo->cbEncodedSubjects - (pbEncoded - pSortedCtlFindInfo->pbEncodedSubjects)); assert(cbEncoded); if (!IsTrustedSubject( pSubjectIdentifier, &pbEncoded, &cbEncoded, &fTrusted, pEncodedAttributes)) goto IsTrustedSubjectError; if (fTrusted) goto CommonReturn; dwEntryIndex = pHashBucketEntry->iNext; } } goto NotFoundError; CommonReturn: return fTrusted; NotFoundError: SetLastError((DWORD) CRYPT_E_NOT_FOUND); ErrorReturn: fTrusted = FALSE; goto CommonReturn; TRACE_ERROR(NoCacheElementError) SET_ERROR(NotSortedCtlContext, E_INVALIDARG) SET_ERROR(InvalidSortedCtlExtension, ERROR_INVALID_DATA) TRACE_ERROR(IsTrustedSubjectError) } //+------------------------------------------------------------------------- // Enumerates through the sequence of TrustedSubjects in a CTL context // created with CERT_CREATE_CONTEXT_SORTED_FLAG set. // // To start the enumeration, *ppvNextSubject must be NULL. Upon return, // *ppvNextSubject is updated to point to the next TrustedSubject in // the encoded sequence. // // Returns FALSE for no more subjects or invalid arguments. // // Note, the returned DER_BLOBs point directly into the encoded // bytes (not allocated, and must not be freed). //-------------------------------------------------------------------------- BOOL WINAPI CertEnumSubjectInSortedCTL( IN PCCTL_CONTEXT pCtlContext, IN OUT void **ppvNextSubject, OUT OPTIONAL PCRYPT_DER_BLOB pSubjectIdentifier, OUT OPTIONAL PCRYPT_DER_BLOB pEncodedAttributes ) { BOOL fResult; PCONTEXT_ELEMENT pCacheEle; // not allocated PSORTED_CTL_FIND_INFO pSortedCtlFindInfo; // not allocated const BYTE *pbEncodedSubjects; const BYTE *pbEncoded; DWORD cbEncoded; DWORD cValue; LONG lAllValues; CRYPT_DER_BLOB rgValueBlob[TRUSTED_SUBJECT_VALUE_COUNT]; if (NULL == (pCacheEle = GetCacheElement(ToContextElement(pCtlContext)))) goto NoCacheElementError; if (NULL == (pSortedCtlFindInfo = ToCtlContextSuffix(pCacheEle)->pSortedCtlFindInfo)) goto NotSortedCtlContext; cbEncoded = pSortedCtlFindInfo->cbEncodedSubjects; if (0 == cbEncoded) goto NotFoundError; pbEncodedSubjects = pSortedCtlFindInfo->pbEncodedSubjects; pbEncoded = *((const BYTE **) ppvNextSubject); if (NULL == pbEncoded) pbEncoded = pbEncodedSubjects; else if (pbEncoded < pbEncodedSubjects || pbEncoded >= pbEncodedSubjects + cbEncoded) goto NotFoundError; else cbEncoded -= (DWORD)(pbEncoded - pbEncodedSubjects); cValue = TRUSTED_SUBJECT_VALUE_COUNT; if (0 >= (lAllValues = Asn1UtilExtractValues( pbEncoded, cbEncoded, ASN1UTIL_DEFINITE_LENGTH_FLAG, &cValue, rgExtractTrustedSubjectPara, rgValueBlob ))) goto ExtractValuesError; if (pSubjectIdentifier) *pSubjectIdentifier = rgValueBlob[TRUSTED_SUBJECT_IDENTIFIER_VALUE_INDEX]; if (pEncodedAttributes) *pEncodedAttributes = rgValueBlob[TRUSTED_SUBJECT_ATTRIBUTES_VALUE_INDEX]; pbEncoded += lAllValues; *((const BYTE **) ppvNextSubject) = pbEncoded; fResult = TRUE; CommonReturn: return fResult; NotFoundError: SetLastError((DWORD) CRYPT_E_NOT_FOUND); ErrorReturn: *ppvNextSubject = NULL; fResult = FALSE; goto CommonReturn; TRACE_ERROR(NoCacheElementError) SET_ERROR(NotSortedCtlContext, E_INVALIDARG) TRACE_ERROR(ExtractValuesError) } //+========================================================================= // Key Identifier Property Functions //========================================================================== //+------------------------------------------------------------------------- // Decode the Key Identifier and its properties. //-------------------------------------------------------------------------- STATIC PKEYID_ELEMENT DecodeKeyIdElement( IN const BYTE *pbElement, IN DWORD cbElement ) { PKEYID_ELEMENT pEle = NULL; DWORD csStatus; MEMINFO MemInfo; MemInfo.pByte = (BYTE *) pbElement; MemInfo.cb = cbElement; MemInfo.cbSeek = 0; csStatus = LoadStoreElement( (HANDLE) &MemInfo, ReadFromMemory, SkipInMemory, cbElement, NULL, // pStore 0, // dwAddDisposition 0, // dwContextTypeFlags NULL, // pdwContextType (const void **) &pEle, TRUE // fKeyIdAllowed ); if (NULL == pEle && CSError != csStatus) SetLastError((DWORD) CRYPT_E_FILE_ERROR); return pEle; } STATIC BOOL SerializeKeyIdElement( IN HANDLE h, IN PFNWRITE pfn, IN PKEYID_ELEMENT pEle ) { BOOL fResult; PPROP_ELEMENT pPropEle; for (pPropEle = pEle->pPropHead; pPropEle; pPropEle = pPropEle->pNext) { if (pPropEle->dwPropId != CERT_KEY_CONTEXT_PROP_ID) { if (!WriteStoreElement( h, pfn, 0, // dwEncodingType pPropEle->dwPropId, pPropEle->pbData, pPropEle->cbData )) goto WriteElementError; } } if (!WriteStoreElement( h, pfn, 0, // dwEncodingType FILE_ELEMENT_KEYID_TYPE, pEle->KeyIdentifier.pbData, pEle->KeyIdentifier.cbData )) goto WriteElementError; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(WriteElementError); } //+------------------------------------------------------------------------- // Encode the Key Identifier and its properties. //-------------------------------------------------------------------------- STATIC BOOL EncodeKeyIdElement( IN PKEYID_ELEMENT pEle, OUT BYTE **ppbElement, OUT DWORD *pcbElement ) { BOOL fResult; MEMINFO MemInfo; BYTE *pbElement = NULL; DWORD cbElement = 0; memset(&MemInfo, 0, sizeof(MemInfo)); if (!SerializeKeyIdElement( (HANDLE) &MemInfo, WriteToMemory, pEle )) goto SerializeKeyIdElementError; cbElement = MemInfo.cbSeek; if (NULL == (pbElement = (BYTE *) PkiNonzeroAlloc(cbElement))) goto OutOfMemory; MemInfo.pByte = pbElement; MemInfo.cb = cbElement; MemInfo.cbSeek = 0; if (!SerializeKeyIdElement( (HANDLE) &MemInfo, WriteToMemory, pEle )) goto SerializeKeyIdElementError; fResult = TRUE; CommonReturn: *ppbElement = pbElement; *pcbElement = cbElement; return fResult; ErrorReturn: PkiFree(pbElement); pbElement = NULL; cbElement = 0; fResult = FALSE; goto CommonReturn; TRACE_ERROR(SerializeKeyIdElementError) TRACE_ERROR(OutOfMemory) } //+------------------------------------------------------------------------- // Get the property for the specified Key Identifier. // // The Key Identifier is the SHA1 hash of the encoded CERT_PUBLIC_KEY_INFO. // The Key Identifier for a certificate can be obtained by getting the // certificate's CERT_KEY_IDENTIFIER_PROP_ID. The // CryptCreateKeyIdentifierFromCSP API can be called to create the Key // Identifier from a CSP Public Key Blob. // // A Key Identifier can have the same properties as a certificate context. // CERT_KEY_PROV_INFO_PROP_ID is the property of most interest. // For CERT_KEY_PROV_INFO_PROP_ID, pvData points to a CRYPT_KEY_PROV_INFO // structure. Elements pointed to by fields in the pvData structure follow the // structure. Therefore, *pcbData will exceed the size of the structure. // // If CRYPT_KEYID_ALLOC_FLAG is set, then, *pvData is updated with a // pointer to allocated memory. LocalFree() must be called to free the // allocated memory. // // By default, searches the CurrentUser's list of Key Identifiers. // CRYPT_KEYID_MACHINE_FLAG can be set to search the LocalMachine's list // of Key Identifiers. When CRYPT_KEYID_MACHINE_FLAG is set, pwszComputerName // can also be set to specify the name of a remote computer to be searched // instead of the local machine. //-------------------------------------------------------------------------- BOOL WINAPI CryptGetKeyIdentifierProperty( IN const CRYPT_HASH_BLOB *pKeyIdentifier, IN DWORD dwPropId, IN DWORD dwFlags, IN OPTIONAL LPCWSTR pwszComputerName, IN OPTIONAL void *pvReserved, OUT void *pvData, IN OUT DWORD *pcbData ) { BOOL fResult; BYTE *pbElement = NULL; DWORD cbElement = 0; PKEYID_ELEMENT pKeyIdEle = NULL; if (!ILS_ReadKeyIdElement( pKeyIdentifier, dwFlags & CRYPT_KEYID_MACHINE_FLAG ? TRUE : FALSE, pwszComputerName, &pbElement, &cbElement )) goto ReadKeyIdElementError; if (NULL == (pKeyIdEle = DecodeKeyIdElement( pbElement, cbElement ))) goto DecodeKeyIdElementError; fResult = GetCallerProperty( pKeyIdEle->pPropHead, dwPropId, dwFlags & CRYPT_KEYID_ALLOC_FLAG ? TRUE : FALSE, pvData, pcbData ); CommonReturn: FreeKeyIdElement(pKeyIdEle); PkiFree(pbElement); return fResult; ErrorReturn: if (dwFlags & CRYPT_KEYID_ALLOC_FLAG) *((void **) pvData) = NULL; *pcbData = 0; fResult = FALSE; goto CommonReturn; TRACE_ERROR(ReadKeyIdElementError) TRACE_ERROR(DecodeKeyIdElementError) } //+------------------------------------------------------------------------- // Set the property for the specified Key Identifier. // // For CERT_KEY_PROV_INFO_PROP_ID pvData points to the // CRYPT_KEY_PROV_INFO data structure. For all other properties, pvData // points to a CRYPT_DATA_BLOB. // // Setting pvData == NULL, deletes the property. // // Set CRYPT_KEYID_MACHINE_FLAG to set the property for a LocalMachine // Key Identifier. Set pwszComputerName, to select a remote computer. // // If CRYPT_KEYID_DELETE_FLAG is set, the Key Identifier and all its // properties is deleted. // // If CRYPT_KEYID_SET_NEW_FLAG is set, the set fails if the property already // exists. For an existing property, FALSE is returned with LastError set to // CRYPT_E_EXISTS. //-------------------------------------------------------------------------- BOOL WINAPI CryptSetKeyIdentifierProperty( IN const CRYPT_HASH_BLOB *pKeyIdentifier, IN DWORD dwPropId, IN DWORD dwFlags, IN OPTIONAL LPCWSTR pwszComputerName, IN OPTIONAL void *pvReserved, IN const void *pvData ) { BOOL fResult; BYTE *pbElement = NULL; DWORD cbElement = 0; PKEYID_ELEMENT pKeyIdEle = NULL; if (dwFlags & CRYPT_KEYID_DELETE_FLAG) { return ILS_DeleteKeyIdElement( pKeyIdentifier, dwFlags & CRYPT_KEYID_MACHINE_FLAG ? TRUE : FALSE, pwszComputerName ); } if (!ILS_ReadKeyIdElement( pKeyIdentifier, dwFlags & CRYPT_KEYID_MACHINE_FLAG ? TRUE : FALSE, pwszComputerName, &pbElement, &cbElement )) { if (ERROR_FILE_NOT_FOUND != GetLastError()) goto ReadKeyIdElementError; } if (NULL == pbElement) { BYTE *pbKeyIdEncoded; if (NULL == (pbKeyIdEncoded = (BYTE *) PkiNonzeroAlloc( pKeyIdentifier->cbData))) goto OutOfMemory; if (NULL == (pKeyIdEle = CreateKeyIdElement( pbKeyIdEncoded, pKeyIdentifier->cbData ))) goto OutOfMemory; } else { if (NULL == (pKeyIdEle = DecodeKeyIdElement( pbElement, cbElement ))) goto DecodeKeyIdElementError; } if (dwFlags & CRYPT_KEYID_SET_NEW_FLAG) { if (FindPropElement(pKeyIdEle->pPropHead, dwPropId)) goto KeyIdExists; } if (!SetCallerProperty( &pKeyIdEle->pPropHead, dwPropId, dwFlags, pvData )) goto SetCallerPropertyError; PkiFree(pbElement); pbElement = NULL; if (!EncodeKeyIdElement( pKeyIdEle, &pbElement, &cbElement )) goto EncodeKeyIdElementError; if (!ILS_WriteKeyIdElement( pKeyIdentifier, dwFlags & CRYPT_KEYID_MACHINE_FLAG ? TRUE : FALSE, pwszComputerName, pbElement, cbElement )) goto WriteKeyIdElementError; fResult = TRUE; CommonReturn: FreeKeyIdElement(pKeyIdEle); PkiFree(pbElement); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(ReadKeyIdElementError) TRACE_ERROR(OutOfMemory) SET_ERROR(KeyIdExists, CRYPT_E_EXISTS) TRACE_ERROR(DecodeKeyIdElementError) TRACE_ERROR(SetCallerPropertyError) TRACE_ERROR(EncodeKeyIdElementError) TRACE_ERROR(WriteKeyIdElementError) } typedef struct _KEYID_ELEMENT_CALLBACK_ARG { DWORD dwPropId; DWORD dwFlags; void *pvArg; PFN_CRYPT_ENUM_KEYID_PROP pfnEnum; } KEYID_ELEMENT_CALLBACK_ARG, *PKEYID_ELEMENT_CALLBACK_ARG; STATIC BOOL KeyIdElementCallback( IN const CRYPT_HASH_BLOB *pKeyIdentifier, IN const BYTE *pbElement, IN DWORD cbElement, IN void *pvArg ) { BOOL fResult = TRUE; PKEYID_ELEMENT_CALLBACK_ARG pKeyIdArg = (PKEYID_ELEMENT_CALLBACK_ARG) pvArg; PKEYID_ELEMENT pKeyIdEle = NULL; PPROP_ELEMENT pPropEle; DWORD iProp; DWORD cProp = 0; DWORD *pdwPropId = NULL; void **ppvData = NULL; DWORD *pcbData = NULL; if (NULL == (pKeyIdEle = DecodeKeyIdElement( pbElement, cbElement ))) goto DecodeKeyIdElementError; // Get number of properties cProp = 0; pPropEle = pKeyIdEle->pPropHead; for ( ; pPropEle; pPropEle = pPropEle->pNext) { if (pKeyIdArg->dwPropId) { if (pKeyIdArg->dwPropId == pPropEle->dwPropId) { cProp = 1; break; } } else cProp++; } if (0 == cProp) { if (0 == pKeyIdArg->dwPropId) fResult = pKeyIdArg->pfnEnum( pKeyIdentifier, 0, // dwFlags NULL, // pvReserved pKeyIdArg->pvArg, 0, // cProp NULL, // rgdwPropId NULL, // rgpvData NULL // rgcbData ); } else { pdwPropId = (DWORD *) PkiZeroAlloc(cProp * sizeof(DWORD)); ppvData = (void **) PkiZeroAlloc(cProp * sizeof(void *)); pcbData = (DWORD *) PkiZeroAlloc(cProp * sizeof(DWORD)); if (NULL == pdwPropId || NULL == ppvData || NULL == pcbData) goto OutOfMemory; iProp = 0; pPropEle = pKeyIdEle->pPropHead; for ( ; pPropEle; pPropEle = pPropEle->pNext) { if (pKeyIdArg->dwPropId && pKeyIdArg->dwPropId != pPropEle->dwPropId) continue; if (GetCallerProperty( pPropEle, pPropEle->dwPropId, TRUE, // fAlloc (void *) &ppvData[iProp], &pcbData[iProp] )) { pdwPropId[iProp] = pPropEle->dwPropId; iProp++; if (iProp == cProp) break; } } if (0 == iProp) goto CommonReturn; fResult = pKeyIdArg->pfnEnum( pKeyIdentifier, 0, // dwFlags NULL, // pvReserved pKeyIdArg->pvArg, iProp, pdwPropId, ppvData, pcbData ); } CommonReturn: FreeKeyIdElement(pKeyIdEle); if (ppvData) { while (cProp--) PkiDefaultCryptFree(ppvData[cProp]); PkiFree(ppvData); } PkiFree(pdwPropId); PkiFree(pcbData); return fResult; ErrorReturn: goto CommonReturn; TRACE_ERROR(DecodeKeyIdElementError) TRACE_ERROR(OutOfMemory) } //+------------------------------------------------------------------------- // Enumerate the Key Identifiers. // // If pKeyIdentifier is NULL, enumerates all Key Identifers. Otherwise, // calls the callback for the specified KeyIdentifier. If dwPropId is // 0, calls the callback with all the properties. Otherwise, only calls // the callback with the specified property (cProp = 1). // Furthermore, when dwPropId is specified, skips KeyIdentifiers not // having the property. // // Set CRYPT_KEYID_MACHINE_FLAG to enumerate the LocalMachine // Key Identifiers. Set pwszComputerName, to enumerate Key Identifiers on // a remote computer. //-------------------------------------------------------------------------- BOOL WINAPI CryptEnumKeyIdentifierProperties( IN OPTIONAL const CRYPT_HASH_BLOB *pKeyIdentifier, IN DWORD dwPropId, IN DWORD dwFlags, IN OPTIONAL LPCWSTR pwszComputerName, IN OPTIONAL void *pvReserved, IN OPTIONAL void *pvArg, IN PFN_CRYPT_ENUM_KEYID_PROP pfnEnum ) { BOOL fResult; KEYID_ELEMENT_CALLBACK_ARG KeyIdArg = { dwPropId, dwFlags, pvArg, pfnEnum }; if (pKeyIdentifier) { BYTE *pbElement = NULL; DWORD cbElement; fResult = ILS_ReadKeyIdElement( pKeyIdentifier, dwFlags & CRYPT_KEYID_MACHINE_FLAG ? TRUE : FALSE, pwszComputerName, &pbElement, &cbElement ); if (fResult) fResult = KeyIdElementCallback( pKeyIdentifier, pbElement, cbElement, (void *) &KeyIdArg ); PkiFree(pbElement); } else fResult = ILS_OpenAllKeyIdElements( dwFlags & CRYPT_KEYID_MACHINE_FLAG ? TRUE : FALSE, pwszComputerName, (void *) &KeyIdArg, KeyIdElementCallback ); return fResult; } //+------------------------------------------------------------------------- // For either the CERT_KEY_IDENTIFIER_PROP_ID or CERT_KEY_PROV_INFO_PROP_ID // set the Crypt KeyIdentifier CERT_KEY_PROV_INFO_PROP_ID property if the // other property already exists. // // If dwPropId == 0, does an implicit GetProperty(KEY_PROV_INFO) //-------------------------------------------------------------------------- STATIC void SetCryptKeyIdentifierKeyProvInfoProperty( IN PCONTEXT_ELEMENT pEle, IN DWORD dwPropId, // may be 0 IN const void *pvData ) { PCRYPT_HASH_BLOB pKeyIdentifier = NULL; PCRYPT_KEY_PROV_INFO pKeyProvInfo = NULL; DWORD cbKeyProvInfo; void *pvOtherData = NULL; DWORD cbOtherData; CRYPT_HASH_BLOB OtherKeyIdentifier; if ((CERT_STORE_CERTIFICATE_CONTEXT - 1) != pEle->dwContextType) return; if (0 == dwPropId) { if (AllocAndGetProperty( pEle, CERT_KEY_PROV_INFO_PROP_ID, (void **) &pKeyProvInfo, &cbKeyProvInfo ) && cbKeyProvInfo) { SetCryptKeyIdentifierKeyProvInfoProperty( pEle, CERT_KEY_PROV_INFO_PROP_ID, pKeyProvInfo ); PkiFree(pKeyProvInfo); } return; } else if (NULL == pvData) return; switch (dwPropId) { case CERT_KEY_IDENTIFIER_PROP_ID: AllocAndGetProperty( pEle, CERT_KEY_PROV_INFO_PROP_ID, &pvOtherData, &cbOtherData ); if (pvOtherData) { pKeyIdentifier = (PCRYPT_HASH_BLOB) pvData; pKeyProvInfo = (PCRYPT_KEY_PROV_INFO) pvOtherData; } break; case CERT_KEY_PROV_INFO_PROP_ID: AllocAndGetProperty( pEle, CERT_KEY_IDENTIFIER_PROP_ID, &pvOtherData, &cbOtherData ); if (pvOtherData) { pKeyProvInfo = (PCRYPT_KEY_PROV_INFO) pvData; OtherKeyIdentifier.cbData = cbOtherData; OtherKeyIdentifier.pbData = (BYTE *)pvOtherData; pKeyIdentifier = &OtherKeyIdentifier; } break; default: return; } if (pvOtherData) { DWORD dwFlags = CRYPT_KEYID_SET_NEW_FLAG; if (pKeyProvInfo->dwFlags & CRYPT_MACHINE_KEYSET) dwFlags |= CRYPT_KEYID_MACHINE_FLAG; CryptSetKeyIdentifierProperty( pKeyIdentifier, CERT_KEY_PROV_INFO_PROP_ID, dwFlags, NULL, // pwszComputerName NULL, // pvReserved (const void *) pKeyProvInfo ); PkiFree(pvOtherData); } } //+------------------------------------------------------------------------- // Get the Key Identifier property for the specified element. // // Only supported for certificates. //-------------------------------------------------------------------------- STATIC BOOL GetKeyIdProperty( IN PCONTEXT_ELEMENT pEle, IN DWORD dwPropId, OUT void *pvData, IN OUT DWORD *pcbData ) { BOOL fResult; PCCERT_CONTEXT pCert; PCERT_INFO pCertInfo; PCERT_EXTENSION pExt; CRYPT_HASH_BLOB KeyIdentifier = { 0, NULL }; BYTE rgbHash[MAX_HASH_LEN]; if ((CERT_STORE_CERTIFICATE_CONTEXT - 1) != pEle->dwContextType) goto InvalidPropId; pCert = ToCertContext(pEle); pCertInfo = pCert->pCertInfo; if (pExt = CertFindExtension( szOID_SUBJECT_KEY_IDENTIFIER, pCertInfo->cExtension, pCertInfo->rgExtension )) { // Skip by the octet tag, length bytes Asn1UtilExtractContent( pExt->Value.pbData, pExt->Value.cbData, &KeyIdentifier.cbData, (const BYTE **) &KeyIdentifier.pbData ); } if (0 == KeyIdentifier.cbData) { const BYTE *pbPublicKeyInfo; DWORD cbPublicKeyInfo; if (!Asn1UtilExtractCertificatePublicKeyInfo( pCert->pbCertEncoded, pCert->cbCertEncoded, &cbPublicKeyInfo, &pbPublicKeyInfo )) goto ExtractPublicKeyInfoError; KeyIdentifier.cbData = sizeof(rgbHash); KeyIdentifier.pbData = rgbHash; if (!CryptHashCertificate( 0, // hCryptProv CALG_SHA1, 0, // dwFlags pbPublicKeyInfo, cbPublicKeyInfo, rgbHash, &KeyIdentifier.cbData )) goto HashPublicKeyInfoError; } if (!SetProperty( pEle, dwPropId, CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, &KeyIdentifier )) goto SetKeyIdPropertyError; fResult = GetProperty( pEle, dwPropId, pvData, pcbData ); CommonReturn: return fResult; ErrorReturn: fResult = FALSE; *pcbData = 0; goto CommonReturn; SET_ERROR(InvalidPropId, E_INVALIDARG) TRACE_ERROR(ExtractPublicKeyInfoError) TRACE_ERROR(HashPublicKeyInfoError) TRACE_ERROR(SetKeyIdPropertyError) } #ifdef CMS_PKCS7 //+------------------------------------------------------------------------- // If the verify signature fails with CRYPT_E_MISSING_PUBKEY_PARA, // build a certificate chain. Retry. Hopefully, the issuer's // CERT_PUBKEY_ALG_PARA_PROP_ID property gets set while building the chain. //-------------------------------------------------------------------------- STATIC BOOL VerifyCertificateSignatureWithChainPubKeyParaInheritance( IN HCRYPTPROV hCryptProv, IN DWORD dwCertEncodingType, IN DWORD dwSubjectType, IN void *pvSubject, IN PCCERT_CONTEXT pIssuer ) { if (CryptVerifyCertificateSignatureEx( hCryptProv, dwCertEncodingType, dwSubjectType, pvSubject, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *) pIssuer, 0, // dwFlags NULL // pvReserved )) return TRUE; else if (CRYPT_E_MISSING_PUBKEY_PARA != GetLastError()) return FALSE; else { PCCERT_CHAIN_CONTEXT pChainContext; CERT_CHAIN_PARA ChainPara; // Build a chain. Hopefully, the issuer inherit's its public key // parameters from up the chain memset(&ChainPara, 0, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); if (CertGetCertificateChain( NULL, // hChainEngine pIssuer, NULL, // pTime pIssuer->hCertStore, &ChainPara, CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL, NULL, // pvReserved &pChainContext )) CertFreeCertificateChain(pChainContext); // Try again. Hopefully the above chain building updated the issuer's // context property with the missing public key parameters return CryptVerifyCertificateSignatureEx( hCryptProv, dwCertEncodingType, dwSubjectType, pvSubject, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *) pIssuer, 0, // dwFlags NULL // pvReserved ); } } #endif // CMS_PKCS7