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
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
|