Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

16257 lines
518 KiB

//+-------------------------------------------------------------------------
// 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 <dbgdef.h>
#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