--- trunk/reactos/lib/crypt32/cert.c 2005-11-17 20:16:02 UTC (rev 19303)
+++ trunk/reactos/lib/crypt32/cert.c 2005-11-17 20:17:53 UTC (rev 19304)
@@ -0,0 +1,3315 @@
+/*
+ * Copyright 2002
+ Mike McCormack for CodeWeavers
+ * Copyright 2004,2005 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * FIXME:
+ * - As you can see in the stubs below, support for CRLs and CTLs is missing.
+ * Mostly this should be copy-paste work, and some code (e.g. extended
+ * properties) could be shared between them.
+ * - Opening a cert store provider should be morphed to support loading
+ * external DLLs.
+ * - The concept of physical stores and locations isn't implemented. (This
+ * doesn't mean registry stores et al aren't implemented. See the PSDK for
+ * registering and enumerating physical stores and locations.)
+ * - Many flags, options and whatnot are unimplemented.
+ */
+
+#include "precomp.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(crypt);
+
+#define WINE_CRYPTCERTSTORE_MAGIC 0x74726563
+/* The following aren't defined in wincrypt.h, as they're "reserved" */
+#define CERT_CERT_PROP_ID 32
+#define CERT_CRL_PROP_ID 33
+#define CERT_CTL_PROP_ID 34
+
+/* Some typedefs that make it easier to abstract which type of context we're
+ * working with.
+ */
+typedef const void *(WINAPI *CreateContextFunc)(DWORD dwCertEncodingType,
+ const BYTE *pbCertEncoded, DWORD cbCertEncoded);
+typedef BOOL (WINAPI *AddContextToStoreFunc)(HCERTSTORE hCertStore,
+ const void *context, DWORD dwAddDisposition, const void **ppStoreContext);
+typedef BOOL (WINAPI *AddEncodedContextToStoreFunc)(HCERTSTORE hCertStore,
+ DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwAddDisposition, const void **ppContext);
+typedef const void *(WINAPI *EnumContextsInStoreFunc)(HCERTSTORE hCertStore,
+ const void *pPrevContext);
+typedef BOOL (WINAPI *GetContextPropertyFunc)(const void *context,
+ DWORD dwPropID, void *pvData, DWORD *pcbData);
+typedef BOOL (WINAPI *SetContextPropertyFunc)(const void *context,
+ DWORD dwPropID, DWORD dwFlags, const void *pvData);
+typedef BOOL (WINAPI *SerializeElementFunc)(const void *context, DWORD dwFlags,
+ BYTE *pbElement, DWORD *pcbElement);
+typedef BOOL (WINAPI *FreeContextFunc)(const void *context);
+typedef BOOL (WINAPI *DeleteContextFunc)(const void *context);
+
+/* An abstract context (certificate, CRL, or CTL) interface */
+typedef struct _WINE_CONTEXT_INTERFACE
+{
+ CreateContextFunc create;
+ AddContextToStoreFunc addContextToStore;
+ AddEncodedContextToStoreFunc addEncodedToStore;
+ EnumContextsInStoreFunc enumContextsInStore;
+ GetContextPropertyFunc getProp;
+ SetContextPropertyFunc setProp;
+ SerializeElementFunc serialize;
+ FreeContextFunc free;
+ DeleteContextFunc deleteFromStore;
+} WINE_CONTEXT_INTERFACE, *PWINE_CONTEXT_INTERFACE;
+
+static const WINE_CONTEXT_INTERFACE gCertInterface = {
+ (CreateContextFunc)CertCreateCertificateContext,
+ (AddContextToStoreFunc)CertAddCertificateContextToStore,
+ (AddEncodedContextToStoreFunc)CertAddEncodedCertificateToStore,
+ (EnumContextsInStoreFunc)CertEnumCertificatesInStore,
+ (GetContextPropertyFunc)CertGetCertificateContextProperty,
+ (SetContextPropertyFunc)CertSetCertificateContextProperty,
+ (SerializeElementFunc)CertSerializeCertificateStoreElement,
+ (FreeContextFunc)CertFreeCertificateContext,
+ (DeleteContextFunc)CertDeleteCertificateFromStore,
+};
+
+static const WINE_CONTEXT_INTERFACE gCRLInterface = {
+ (CreateContextFunc)CertCreateCRLContext,
+ (AddContextToStoreFunc)CertAddCRLContextToStore,
+ (AddEncodedContextToStoreFunc)CertAddEncodedCRLToStore,
+ (EnumContextsInStoreFunc)CertEnumCRLsInStore,
+ (GetContextPropertyFunc)CertGetCRLContextProperty,
+ (SetContextPropertyFunc)CertSetCRLContextProperty,
+ (SerializeElementFunc)CertSerializeCRLStoreElement,
+ (FreeContextFunc)CertFreeCRLContext,
+ (DeleteContextFunc)CertDeleteCRLFromStore,
+};
+
+static const WINE_CONTEXT_INTERFACE gCTLInterface = {
+ (CreateContextFunc)CertCreateCTLContext,
+ (AddContextToStoreFunc)CertAddCTLContextToStore,
+ (AddEncodedContextToStoreFunc)CertAddEncodedCTLToStore,
+ (EnumContextsInStoreFunc)CertEnumCTLsInStore,
+ (GetContextPropertyFunc)CertGetCTLContextProperty,
+ (SetContextPropertyFunc)CertSetCTLContextProperty,
+ (SerializeElementFunc)CertSerializeCTLStoreElement,
+ (FreeContextFunc)CertFreeCTLContext,
+ (DeleteContextFunc)CertDeleteCTLFromStore,
+};
+
+struct WINE_CRYPTCERTSTORE;
+
+typedef struct WINE_CRYPTCERTSTORE * (*StoreOpenFunc)(HCRYPTPROV hCryptProv,
+ DWORD dwFlags, const void *pvPara);
+
+struct _WINE_CERT_CONTEXT_REF;
+
+/* Called to enumerate the next certificate in a store. The returned pointer
+ * must be newly allocated (via CryptMemAlloc): CertFreeCertificateContext
+ * frees it.
+ */
+typedef struct _WINE_CERT_CONTEXT_REF * (*EnumCertFunc)
+ (struct WINE_CRYPTCERTSTORE *store, struct _WINE_CERT_CONTEXT_REF *pPrev);
+
+struct _WINE_CERT_CONTEXT;
+
+/* Called to create a new reference to an existing cert context. Should call
+ * CRYPT_InitCertRef to make sure the reference count is properly updated.
+ * If the store does not provide any additional allocated data (that is, does
+ * not need to implement a FreeCertFunc), it may use CRYPT_CreateCertRef for
+ * this.
+ */
+typedef struct _WINE_CERT_CONTEXT_REF * (*CreateRefFunc)
+ (struct _WINE_CERT_CONTEXT *context, HCERTSTORE store);
+
+/* Optional, called when a cert context reference is being freed. Don't free
+ * the ref pointer itself, CertFreeCertificateContext does that.
+ */
+typedef void (*FreeCertFunc)(struct _WINE_CERT_CONTEXT_REF *ref);
+
+typedef enum _CertStoreType {
+ StoreTypeMem,
+ StoreTypeCollection,
+ StoreTypeReg,
+ StoreTypeDummy,
+} CertStoreType;
+
+/* A cert store is polymorphic through the use of function pointers. A type
+ * is still needed to distinguish collection stores from other types.
+ * On the function pointers:
+ * - closeStore is called when the store's ref count becomes 0
+ * - addCert is called with a PWINE_CERT_CONTEXT as the second parameter
+ * - control is optional, but should be implemented by any store that supports
+ * persistence
+ */
+typedef struct WINE_CRYPTCERTSTORE
+{
+ DWORD dwMagic;
+ LONG ref;
+ DWORD dwOpenFlags;
+ HCRYPTPROV cryptProv;
+ CertStoreType type;
+ PFN_CERT_STORE_PROV_CLOSE closeStore;
+ PFN_CERT_STORE_PROV_WRITE_CERT addCert;
+ CreateRefFunc createCertRef;
+ EnumCertFunc enumCert;
+ PFN_CERT_STORE_PROV_DELETE_CERT deleteCert;
+ FreeCertFunc freeCert; /* optional */
+ PFN_CERT_STORE_PROV_CONTROL control; /* optional */
+} WINECRYPT_CERTSTORE, *PWINECRYPT_CERTSTORE;
+
+/* A certificate context has pointers to data that are owned by this module,
+ * so rather than duplicate the data every time a certificate context is
+ * copied, I keep a reference count to the data. Thus I have two data
+ * structures, the "true" certificate context (that has the reference count)
+ * and a reference certificate context, that has a pointer to the true context.
+ * Each one can be cast to a PCERT_CONTEXT, though you'll usually be dealing
+ * with the reference version.
+ */
+typedef struct _WINE_CERT_CONTEXT
+{
+ CERT_CONTEXT cert;
+ LONG ref;
+ CRITICAL_SECTION cs;
+ struct list extendedProperties;
+} WINE_CERT_CONTEXT, *PWINE_CERT_CONTEXT;
+
+typedef struct _WINE_CERT_CONTEXT_REF
+{
+ CERT_CONTEXT cert;
+ WINE_CERT_CONTEXT *context;
+} WINE_CERT_CONTEXT_REF, *PWINE_CERT_CONTEXT_REF;
+
+/* An extended certificate property in serialized form is prefixed by this
+ * header.
+ */
+typedef struct _WINE_CERT_PROP_HEADER
+{
+ DWORD propID;
+ DWORD unknown; /* always 1 */
+ DWORD cb;
+} WINE_CERT_PROP_HEADER, *PWINE_CERT_PROP_HEADER;
+
+/* Stores an extended property in a cert. */
+typedef struct _WINE_CERT_PROPERTY
+{
+ WINE_CERT_PROP_HEADER hdr;
+ LPBYTE pbData;
+ struct list entry;
+} WINE_CERT_PROPERTY, *PWINE_CERT_PROPERTY;
+
+/* A mem store has a list of these. They're also returned by the mem store
+ * during enumeration.
+ */
+typedef struct _WINE_CERT_LIST_ENTRY
+{
+ WINE_CERT_CONTEXT_REF cert;
+ struct list entry;
+} WINE_CERT_LIST_ENTRY, *PWINE_CERT_LIST_ENTRY;
+
+typedef struct _WINE_MEMSTORE
+{
+ WINECRYPT_CERTSTORE hdr;
+ CRITICAL_SECTION cs;
+ struct list certs;
+} WINE_MEMSTORE, *PWINE_MEMSTORE;
+
+typedef struct _WINE_HASH_TO_DELETE
+{
+ BYTE hash[20];
+ struct list entry;
+} WINE_HASH_TO_DELETE, *PWINE_HASH_TO_DELETE;
+
+/* Returned by a reg store during enumeration. */
+typedef struct _WINE_REG_CERT_CONTEXT
+{
+ WINE_CERT_CONTEXT_REF cert;
+ PWINE_CERT_CONTEXT_REF childContext;
+} WINE_REG_CERT_CONTEXT, *PWINE_REG_CERT_CONTEXT;
+
+typedef struct _WINE_REGSTORE
+{
+ WINECRYPT_CERTSTORE hdr;
+ PWINECRYPT_CERTSTORE memStore;
+ HKEY key;
+ BOOL dirty;
+ CRITICAL_SECTION cs;
+ struct list certsToDelete;
+} WINE_REGSTORE, *PWINE_REGSTORE;
+
+typedef struct _WINE_STORE_LIST_ENTRY
+{
+ PWINECRYPT_CERTSTORE store;
+ DWORD dwUpdateFlags;
+ DWORD dwPriority;
+ struct list entry;
+} WINE_STORE_LIST_ENTRY, *PWINE_STORE_LIST_ENTRY;
+
+/* Returned by a collection store during enumeration.
+ * Note: relies on the list entry being valid after use, which a number of
+ * conditions might make untrue (reentrancy, closing a collection store before
+ * continuing an enumeration on it, ...). The tests seem to indicate this
+ * sort of unsafety is okay, since Windows isn't well-behaved in these
+ * scenarios either.
+ */
+typedef struct _WINE_COLLECTION_CERT_CONTEXT
+{
+ WINE_CERT_CONTEXT_REF cert;
+ PWINE_STORE_LIST_ENTRY entry;
+ PWINE_CERT_CONTEXT_REF childContext;
+} WINE_COLLECTION_CERT_CONTEXT, *PWINE_COLLECTION_CERT_CONTEXT;
+
+typedef struct _WINE_COLLECTIONSTORE
+{
+ WINECRYPT_CERTSTORE hdr;
+ CRITICAL_SECTION cs;
+ struct list stores;
+} WINE_COLLECTIONSTORE, *PWINE_COLLECTIONSTORE;
+
+/* Like CertGetCertificateContextProperty, but operates directly on the
+ * WINE_CERT_CONTEXT. Doesn't support special-case properties, since they
+ * are handled by CertGetCertificateContextProperty, and are particular to the
+ * store in which the property exists (which is separate from the context.)
+ */
+static BOOL WINAPI CRYPT_GetCertificateContextProperty(
+ PWINE_CERT_CONTEXT context, DWORD dwPropId, void *pvData, DWORD *pcbData);
+
+/* Like CertSetCertificateContextProperty, but operates directly on the
+ * WINE_CERT_CONTEXT. Doesn't handle special cases, since they're handled by
+ * CertSetCertificateContextProperty anyway.
+ */
+static BOOL WINAPI CRYPT_SetCertificateContextProperty(
+ PWINE_CERT_CONTEXT context, DWORD dwPropId, DWORD dwFlags, const void *pvData);
+
+/* Helper function for store reading functions and
+ * CertAddSerializedElementToStore. Returns a context of the appropriate type
+ * if it can, or NULL otherwise. Doesn't validate any of the properties in
+ * the serialized context (for example, bad hashes are retained.)
+ * *pdwContentType is set to the type of the returned context.
+ */
+static const void * WINAPI CRYPT_ReadSerializedElement(const BYTE *pbElement,
+ DWORD cbElement, DWORD dwContextTypeFlags, DWORD *pdwContentType);
+
+/* filter for page-fault exceptions */
+static WINE_EXCEPTION_FILTER(page_fault)
+{
+ if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
+ return EXCEPTION_EXECUTE_HANDLER;
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+static void CRYPT_InitStore(WINECRYPT_CERTSTORE *store, HCRYPTPROV hCryptProv,
+ DWORD dwFlags, CertStoreType type)
+{
+ store->ref = 1;
+ store->dwMagic = WINE_CRYPTCERTSTORE_MAGIC;
+ store->type = type;
+ if (!hCryptProv)
+ {
+ hCryptProv = CRYPT_GetDefaultProvider();
+ dwFlags |= CERT_STORE_NO_CRYPT_RELEASE_FLAG;
+ }
+ store->cryptProv = hCryptProv;
+ store->dwOpenFlags = dwFlags;
+}
+
+/* Initializes the reference ref to point to pCertContext, which is assumed to
+ * be a PWINE_CERT_CONTEXT, and increments pCertContext's reference count.
+ * Also sets the hCertStore member of the reference to store.
+ */
+static void CRYPT_InitCertRef(PWINE_CERT_CONTEXT_REF ref,
+ PWINE_CERT_CONTEXT context, HCERTSTORE store)
+{
+ TRACE("(%p, %p)\n", ref, context);
+ memcpy(&ref->cert, context, sizeof(ref->cert));
+ ref->context = context;
+ InterlockedIncrement(&context->ref);
+ ref->cert.hCertStore = store;
+}
+
+static PWINE_CERT_CONTEXT_REF CRYPT_CreateCertRef(PWINE_CERT_CONTEXT context,
+ HCERTSTORE store)
+{
+ PWINE_CERT_CONTEXT_REF pCertRef = CryptMemAlloc(
+ sizeof(WINE_CERT_CONTEXT_REF));
+
+ if (pCertRef)
+ CRYPT_InitCertRef(pCertRef, context, store);
+ return pCertRef;
+}
+
+static BOOL WINAPI CRYPT_MemAddCert(HCERTSTORE store, PCCERT_CONTEXT pCert,
+ DWORD dwAddDisposition)
+{
+ WINE_MEMSTORE *ms = (WINE_MEMSTORE *)store;
+ BOOL add = FALSE, ret;
+
+ TRACE("(%p, %p, %ld)\n", store, pCert, dwAddDisposition);
+
+ switch (dwAddDisposition)
+ {
+ case CERT_STORE_ADD_ALWAYS:
+ add = TRUE;
+ break;
+ case CERT_STORE_ADD_NEW:
+ {
+ BYTE hashToAdd[20], hash[20];
+ DWORD size = sizeof(hashToAdd);
+
+ ret = CRYPT_GetCertificateContextProperty((PWINE_CERT_CONTEXT)pCert,
+ CERT_HASH_PROP_ID, hashToAdd, &size);
+ if (ret)
+ {
+ PWINE_CERT_LIST_ENTRY cursor;
+
+ /* Add if no cert with the same hash is found. */
+ add = TRUE;
+ EnterCriticalSection(&ms->cs);
+ LIST_FOR_EACH_ENTRY(cursor, &ms->certs, WINE_CERT_LIST_ENTRY, entry)
+ {
+ size = sizeof(hash);
+ ret = CertGetCertificateContextProperty(&cursor->cert.cert,
+ CERT_HASH_PROP_ID, hash, &size);
+ if (ret && !memcmp(hashToAdd, hash, size))
+ {
+ TRACE("found matching certificate, not adding\n");
+ SetLastError(CRYPT_E_EXISTS);
+ add = FALSE;
+ break;
+ }
+ }
+ LeaveCriticalSection(&ms->cs);
+ }
+ break;
+ }
+ case CERT_STORE_ADD_REPLACE_EXISTING:
+ {
+ BYTE hashToAdd[20], hash[20];
+ DWORD size = sizeof(hashToAdd);
+
+ add = TRUE;
+ ret = CRYPT_GetCertificateContextProperty((PWINE_CERT_CONTEXT)pCert,
+ CERT_HASH_PROP_ID, hashToAdd, &size);
+ if (ret)
+ {
+ PWINE_CERT_LIST_ENTRY cursor, next;
+
+ /* Look for existing cert to delete */
+ EnterCriticalSection(&ms->cs);
+ LIST_FOR_EACH_ENTRY_SAFE(cursor, next, &ms->certs,
+ WINE_CERT_LIST_ENTRY, entry)
+ {
+ size = sizeof(hash);
+ ret = CertGetCertificateContextProperty(&cursor->cert.cert,
+ CERT_HASH_PROP_ID, hash, &size);
+ if (ret && !memcmp(hashToAdd, hash, size))
+ {
+ TRACE("found matching certificate, replacing\n");
+ list_remove(&cursor->entry);
+ CertFreeCertificateContext((PCCERT_CONTEXT)cursor);
+ break;
+ }
+ }
+ LeaveCriticalSection(&ms->cs);
+ }
+ break;
+ }
+ default:
+ FIXME("Unimplemented add disposition %ld\n", dwAddDisposition);
+ add = FALSE;
+ }
+ if (add)
+ {
+ PWINE_CERT_LIST_ENTRY entry = CryptMemAlloc(
+ sizeof(WINE_CERT_LIST_ENTRY));
+
+ if (entry)
+ {
+ TRACE("adding %p\n", entry);
+ CRYPT_InitCertRef(&entry->cert, (PWINE_CERT_CONTEXT)pCert, store);
+ list_init(&entry->entry);
+ EnterCriticalSection(&ms->cs);
+ list_add_tail(&ms->certs, &entry->entry);
+ LeaveCriticalSection(&ms->cs);
+ ret = TRUE;
+ }
+ else
+ ret = FALSE;
+ }
+ else
+ ret = FALSE;
+ return ret;
+}
+
+static PWINE_CERT_CONTEXT_REF CRYPT_MemEnumCert(PWINECRYPT_CERTSTORE store,
+ PWINE_CERT_CONTEXT_REF pPrev)
+{
+ WINE_MEMSTORE *ms = (WINE_MEMSTORE *)store;
+ PWINE_CERT_LIST_ENTRY prevEntry = (PWINE_CERT_LIST_ENTRY)pPrev, ret;
+ struct list *listNext;
+
+ TRACE("(%p, %p)\n", store, pPrev);
+ EnterCriticalSection(&ms->cs);
+ if (prevEntry)
+ {
+ listNext = list_next(&ms->certs, &prevEntry->entry);
+ CertFreeCertificateContext((PCCERT_CONTEXT)pPrev);
+ }
+ else
+ listNext = list_next(&ms->certs, &ms->certs);
+ if (listNext)
+ {
+ ret = CryptMemAlloc(sizeof(WINE_CERT_LIST_ENTRY));
+ memcpy(ret, LIST_ENTRY(listNext, WINE_CERT_LIST_ENTRY, entry),
+ sizeof(WINE_CERT_LIST_ENTRY));
+ InterlockedIncrement(&ret->cert.context->ref);
+ }
+ else
+ {
+ SetLastError(CRYPT_E_NOT_FOUND);
+ ret = NULL;
+ }
+ LeaveCriticalSection(&ms->cs);
+
+ TRACE("returning %p\n", ret);
+ return (PWINE_CERT_CONTEXT_REF)ret;
+}
+
+static BOOL WINAPI CRYPT_MemDeleteCert(HCERTSTORE hCertStore,
+ PCCERT_CONTEXT pCertContext, DWORD dwFlags)
+{
+ WINE_MEMSTORE *store = (WINE_MEMSTORE *)hCertStore;
+ WINE_CERT_CONTEXT_REF *ref = (WINE_CERT_CONTEXT_REF *)pCertContext;
+ PWINE_CERT_LIST_ENTRY cert, next;
+ BOOL ret;
+
+ /* Find the entry associated with the passed-in context, since the
+ * passed-in context may not be a list entry itself (e.g. if it came from
+ * CertDuplicateCertificateContext.) Pointing to the same context is
+ * a sufficient test of equality.
+ */
+ EnterCriticalSection(&store->cs);
+ LIST_FOR_EACH_ENTRY_SAFE(cert, next, &store->certs, WINE_CERT_LIST_ENTRY,
+ entry)
+ {
+ if (cert->cert.context == ref->context)
+ {
+ TRACE("removing %p\n", cert);
+ /* FIXME: this isn't entirely thread-safe, the entry itself isn't
+ * protected.
+ */
+ list_remove(&cert->entry);
+ cert->entry.prev = cert->entry.next = &store->certs;
+ break;
+ }
+ }
+ ret = TRUE;
+ LeaveCriticalSection(&store->cs);
+ return ret;
+}
+
+static void WINAPI CRYPT_MemCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
+{
+ WINE_MEMSTORE *store = (WINE_MEMSTORE *)hCertStore;
+ PWINE_CERT_LIST_ENTRY cert, next;
+
+ TRACE("(%p, %08lx)\n", store, dwFlags);
+ if (dwFlags)
+ FIXME("Unimplemented flags: %08lx\n", dwFlags);
+
+ /* Note that CertFreeCertificateContext calls HeapFree on the passed-in
+ * pointer if its ref-count reaches zero. That's okay here because there
+ * aren't any allocated data outside of the WINE_CERT_CONTEXT_REF portion
+ * of the CertListEntry.
+ */
+ LIST_FOR_EACH_ENTRY_SAFE(cert, next, &store->certs, WINE_CERT_LIST_ENTRY,
+ entry)
+ {
+ TRACE("removing %p\n", cert);
+ list_remove(&cert->entry);
+ CertFreeCertificateContext((PCCERT_CONTEXT)cert);
+ }
+ DeleteCriticalSection(&store->cs);
+ CryptMemFree(store);
+}
+
+static WINECRYPT_CERTSTORE *CRYPT_MemOpenStore(HCRYPTPROV hCryptProv,
+ DWORD dwFlags, const void *pvPara)
+{
+ PWINE_MEMSTORE store;
+
+ TRACE("(%ld, %08lx, %p)\n", hCryptProv, dwFlags, pvPara);
+
+ if (dwFlags & CERT_STORE_DELETE_FLAG)
+ {
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ store = NULL;
+ }
+ else
+ {
+ store = CryptMemAlloc(sizeof(WINE_MEMSTORE));
+ if (store)
+ {
+ memset(store, 0, sizeof(WINE_MEMSTORE));
+ CRYPT_InitStore(&store->hdr, hCryptProv, dwFlags, StoreTypeMem);
+ store->hdr.closeStore = CRYPT_MemCloseStore;
+ store->hdr.addCert = CRYPT_MemAddCert;
+ store->hdr.createCertRef = CRYPT_CreateCertRef;
+ store->hdr.enumCert = CRYPT_MemEnumCert;
+ store->hdr.deleteCert = CRYPT_MemDeleteCert;
+ store->hdr.freeCert = NULL;
+ InitializeCriticalSection(&store->cs);
+ list_init(&store->certs);
+ }
+ }
+ return (PWINECRYPT_CERTSTORE)store;
+}
+
+static BOOL WINAPI CRYPT_CollectionAddCert(HCERTSTORE store,
+ PCCERT_CONTEXT pCert, DWORD dwAddDisposition)
+{
+ PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
+ PWINE_STORE_LIST_ENTRY entry, next;
+ BOOL ret;
+
+ TRACE("(%p, %p, %ld)\n", store, pCert, dwAddDisposition);
+
+ ret = FALSE;
+ EnterCriticalSection(&cs->cs);
+ LIST_FOR_EACH_ENTRY_SAFE(entry, next, &cs->stores, WINE_STORE_LIST_ENTRY,
+ entry)
+ {
+ if (entry->dwUpdateFlags & CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG)
+ {
+ ret = entry->store->addCert(entry->store, pCert, dwAddDisposition);
+ break;
+ }
+ }
+ LeaveCriticalSection(&cs->cs);
+ SetLastError(ret ? ERROR_SUCCESS : HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
+ return ret;
+}
+
+static PWINE_CERT_CONTEXT_REF CRYPT_CollectionCreateCertRef(
+ PWINE_CERT_CONTEXT context, HCERTSTORE store)
+{
+ PWINE_COLLECTION_CERT_CONTEXT ret = CryptMemAlloc(
+ sizeof(WINE_COLLECTION_CERT_CONTEXT));
+
+ if (ret)
+ {
+ /* Initialize to empty for now, just make sure the size is right */
+ CRYPT_InitCertRef((PWINE_CERT_CONTEXT_REF)ret, context, store);
+ ret->entry = NULL;
+ ret->childContext = NULL;
+ }
+ return (PWINE_CERT_CONTEXT_REF)ret;
+}
+
+static void WINAPI CRYPT_CollectionCloseStore(HCERTSTORE store, DWORD dwFlags)
+{
+ PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
+ PWINE_STORE_LIST_ENTRY entry, next;
+
+ TRACE("(%p, %08lx)\n", store, dwFlags);
+
+ LIST_FOR_EACH_ENTRY_SAFE(entry, next, &cs->stores, WINE_STORE_LIST_ENTRY,
+ entry)
+ {
+ TRACE("closing %p\n", entry);
+ CertCloseStore((HCERTSTORE)entry->store, dwFlags);
+ CryptMemFree(entry);
+ }
+ DeleteCriticalSection(&cs->cs);
+ CryptMemFree(cs);
+}
+
+/* Advances a collection enumeration by one cert, if possible, where advancing
+ * means:
+ * - calling the current store's enumeration function once, and returning
+ * the enumerated cert if one is returned
+ * - moving to the next store if the current store has no more items, and
+ * recursively calling itself to get the next item.
+ * Returns NULL if the collection contains no more items or on error.
+ * Assumes the collection store's lock is held.
+ */
+static PWINE_COLLECTION_CERT_CONTEXT CRYPT_CollectionAdvanceEnum(
+ PWINE_COLLECTIONSTORE store, PWINE_STORE_LIST_ENTRY storeEntry,
+ PWINE_COLLECTION_CERT_CONTEXT pPrev)
+{
+ PWINE_COLLECTION_CERT_CONTEXT ret;
+ PWINE_CERT_CONTEXT_REF child;
+
+ TRACE("(%p, %p, %p)\n", store, storeEntry, pPrev);
+
+ if (pPrev)
+ {
+ child = storeEntry->store->enumCert((HCERTSTORE)storeEntry->store,
+ pPrev->childContext);
+ if (child)
+ {
+ ret = pPrev;
+ memcpy(&ret->cert, child, sizeof(WINE_CERT_CONTEXT_REF));
+ ret->cert.cert.hCertStore = (HCERTSTORE)store;
+ InterlockedIncrement(&ret->cert.context->ref);
+ ret->childContext = child;
+ }
+ else
+ {
+ struct list *storeNext = list_next(&store->stores,
+ &storeEntry->entry);
+
+ pPrev->childContext = NULL;
+ CertFreeCertificateContext((PCCERT_CONTEXT)pPrev);
+ if (storeNext)
+ {
+ storeEntry = LIST_ENTRY(storeNext, WINE_STORE_LIST_ENTRY,
+ entry);
+ ret = CRYPT_CollectionAdvanceEnum(store, storeEntry, NULL);
+ }
+ else
+ {
+ SetLastError(CRYPT_E_NOT_FOUND);
+ ret = NULL;
+ }
+ }
+ }
+ else
+ {
+ child = storeEntry->store->enumCert((HCERTSTORE)storeEntry->store,
+ NULL);
+ if (child)
+ {
+ ret = (PWINE_COLLECTION_CERT_CONTEXT)CRYPT_CollectionCreateCertRef(
+ child->context, store);
+ if (ret)
+ {
+ ret->entry = storeEntry;
+ ret->childContext = child;
+ }
+ else
+ CertFreeCertificateContext((PCCERT_CONTEXT)child);
+ }
+ else
+ {
+ struct list *storeNext = list_next(&store->stores,
+ &storeEntry->entry);
+
+ if (storeNext)
+ {
+ storeEntry = LIST_ENTRY(storeNext, WINE_STORE_LIST_ENTRY,
+ entry);
+ ret = CRYPT_CollectionAdvanceEnum(store, storeEntry, NULL);
+ }
+ else
+ {
+ SetLastError(CRYPT_E_NOT_FOUND);
+ ret = NULL;
+ }
+ }
+ }
+ TRACE("returning %p\n", ret);
+ return ret;
+}
+
+static PWINE_CERT_CONTEXT_REF CRYPT_CollectionEnumCert(
+ PWINECRYPT_CERTSTORE store, PWINE_CERT_CONTEXT_REF pPrev)
+{
+ PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
+ PWINE_COLLECTION_CERT_CONTEXT prevEntry =
+ (PWINE_COLLECTION_CERT_CONTEXT)pPrev, ret;
+
+ TRACE("(%p, %p)\n", store, pPrev);
+
+ if (prevEntry)
+ {
+ EnterCriticalSection(&cs->cs);
+ ret = CRYPT_CollectionAdvanceEnum(cs, prevEntry->entry, prevEntry);
+ LeaveCriticalSection(&cs->cs);
+ }
+ else
+ {
+ EnterCriticalSection(&cs->cs);
+ if (!list_empty(&cs->stores))
+ {
+ PWINE_STORE_LIST_ENTRY storeEntry;
+
+ storeEntry = LIST_ENTRY(cs->stores.next, WINE_STORE_LIST_ENTRY,
+ entry);
+ ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, prevEntry);
+ }
+ else
+ {
+ SetLastError(CRYPT_E_NOT_FOUND);
+ ret = NULL;
+ }
+ LeaveCriticalSection(&cs->cs);
+ }
+ TRACE("returning %p\n", ret);
+ return (PWINE_CERT_CONTEXT_REF)ret;
+}
+
+static BOOL WINAPI CRYPT_CollectionDeleteCert(HCERTSTORE hCertStore,
+ PCCERT_CONTEXT pCertContext, DWORD dwFlags)
+{
+ PWINE_COLLECTION_CERT_CONTEXT context =
+ (PWINE_COLLECTION_CERT_CONTEXT)pCertContext;
+ BOOL ret;
+
+ TRACE("(%p, %p, %08lx)\n", hCertStore, pCertContext, dwFlags);
+
+ ret = CertDeleteCertificateFromStore((PCCERT_CONTEXT)context->childContext);
+ if (ret)
+ context->childContext = NULL;
+ return ret;
+}
+
+static void CRYPT_CollectionFreeCert(PWINE_CERT_CONTEXT_REF ref)
+{
+ PWINE_COLLECTION_CERT_CONTEXT context = (PWINE_COLLECTION_CERT_CONTEXT)ref;
+
+ TRACE("(%p)\n", ref);
+
+ if (context->childContext)
+ CertFreeCertificateContext((PCCERT_CONTEXT)context->childContext);
+}
+
+static WINECRYPT_CERTSTORE *CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv,
+ DWORD dwFlags, const void *pvPara)
+{
+ PWINE_COLLECTIONSTORE store;
+
+ if (dwFlags & CERT_STORE_DELETE_FLAG)
+ {
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ store = NULL;
+ }
+ else
+ {
+ store = CryptMemAlloc(sizeof(WINE_COLLECTIONSTORE));
+ if (store)
+ {
+ memset(store, 0, sizeof(WINE_COLLECTIONSTORE));
+ CRYPT_InitStore(&store->hdr, hCryptProv, dwFlags,
+ StoreTypeCollection);
+ store->hdr.closeStore = CRYPT_CollectionCloseStore;
+ store->hdr.addCert = CRYPT_CollectionAddCert;
+ store->hdr.createCertRef = CRYPT_CollectionCreateCertRef;
+ store->hdr.enumCert = CRYPT_CollectionEnumCert;
+ store->hdr.deleteCert = CRYPT_CollectionDeleteCert;
+ store->hdr.freeCert = CRYPT_CollectionFreeCert;
+ InitializeCriticalSection(&store->cs);
+ list_init(&store->stores);
+ }
+ }
+ return (PWINECRYPT_CERTSTORE)store;
+}
+
+static void CRYPT_HashToStr(LPBYTE hash, LPWSTR asciiHash)
+{
+ static const WCHAR fmt[] = { '%','0','2','X',0 };
+ DWORD i;
+
+ assert(hash);
+ assert(asciiHash);
+
+ for (i = 0; i < 20; i++)
+ wsprintfW(asciiHash + i * 2, fmt, hash[i]);
+}
+
+static const WCHAR CertsW[] = { 'C','e','r','t','i','f','i','c','a','t','e','s',
+ 0 };
+static const WCHAR CRLsW[] = { 'C','R','L','s',0 };
+static const WCHAR CTLsW[] = { 'C','T','L','s',0 };
+static const WCHAR BlobW[] = { 'B','l','o','b',0 };
+
+static void CRYPT_RegReadSerializedFromReg(PWINE_REGSTORE store, HKEY key,
+ DWORD contextType)
+{
+ LONG rc;
+ DWORD index = 0;
+ WCHAR subKeyName[MAX_PATH];
+
+ do {
+ DWORD size = sizeof(subKeyName) / sizeof(WCHAR);
+
+ rc = RegEnumKeyExW(key, index++, subKeyName, &size, NULL, NULL, NULL,
+ NULL);
+ if (!rc)
+ {
+ HKEY subKey;
+
+ rc = RegOpenKeyExW(key, subKeyName, 0, KEY_READ, &subKey);
+ if (!rc)
+ {
+ LPBYTE buf = NULL;
+
+ size = 0;
+ rc = RegQueryValueExW(subKey, BlobW, NULL, NULL, NULL, &size);
+ if (!rc)
+ buf = CryptMemAlloc(size);
+ if (buf)
+ {
+ rc = RegQueryValueExW(subKey, BlobW, NULL, NULL, buf,
+ &size);
+ if (!rc)
+ {
+ const void *context;
+ DWORD addedType;
+
+ TRACE("Adding cert with hash %s\n",
+ debugstr_w(subKeyName));
+ context = CRYPT_ReadSerializedElement(buf, size,
+ contextType, &addedType);
+ if (context)
+ {
+ const WINE_CONTEXT_INTERFACE *contextInterface;
+ BYTE hash[20];
+
+ switch (addedType)
+ {
+ case CERT_STORE_CERTIFICATE_CONTEXT:
+ contextInterface = &gCertInterface;
+ break;
+ case CERT_STORE_CRL_CONTEXT:
+ contextInterface = &gCRLInterface;
+ break;
+ case CERT_STORE_CTL_CONTEXT:
+ contextInterface = &gCTLInterface;
+ break;
+ default:
+ contextInterface = NULL;
+ }
+ if (contextInterface)
+ {
+ size = sizeof(hash);
+ if (contextInterface->getProp(context,
+ CERT_HASH_PROP_ID, hash, &size))
+ {
+ WCHAR asciiHash[20 * 2 + 1];
+
+ CRYPT_HashToStr(hash, asciiHash);
+ TRACE("comparing %s\n",
+ debugstr_w(asciiHash));
+ TRACE("with %s\n", debugstr_w(subKeyName));
+ if (!lstrcmpW(asciiHash, subKeyName))
+ {
+ TRACE("hash matches, adding\n");
+ contextInterface->addContextToStore(
+ store, context,
+ CERT_STORE_ADD_REPLACE_EXISTING, NULL);
+ }
+ else
+ {
+ TRACE("hash doesn't match, ignoring\n");
+ contextInterface->free(context);
+ }
+ }
+ }
+ }
+ }
+ CryptMemFree(buf);
+ }
+ RegCloseKey(subKey);
+ }
+ /* Ignore intermediate errors, continue enumerating */
+ rc = ERROR_SUCCESS;
+ }
+ } while (!rc);
+}
+
+static void CRYPT_RegReadFromReg(PWINE_REGSTORE store)
+{
+ static const WCHAR *subKeys[] = { CertsW, CRLsW, CTLsW };
+ static const DWORD contextFlags[] = { CERT_STORE_CERTIFICATE_CONTEXT_FLAG,
+ CERT_STORE_CRL_CONTEXT_FLAG, CERT_STORE_CTL_CONTEXT_FLAG };
+ DWORD i;
+
+ for (i = 0; i < sizeof(subKeys) / sizeof(subKeys[0]); i++)
+ {
+ HKEY key;
+ LONG rc;
+
+ rc = RegCreateKeyExW(store->key, subKeys[i], 0, NULL, 0, KEY_READ, NULL,
+ &key, NULL);
+ if (!rc)
+ {
+ CRYPT_RegReadSerializedFromReg(store, key, contextFlags[i]);
+ RegCloseKey(key);
+ }
+ }
+}
+
+/* Hash is assumed to be 20 bytes in length (a SHA-1 hash) */
+static BOOL CRYPT_WriteSerializedToReg(HKEY key, LPBYTE hash, LPBYTE buf,
+ DWORD len)
+{
+ WCHAR asciiHash[20 * 2 + 1];
+ LONG rc;
+ HKEY subKey;
+ BOOL ret;
+
+ CRYPT_HashToStr(hash, asciiHash);
+ rc = RegCreateKeyExW(key, asciiHash, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
+ &subKey, NULL);
+ if (!rc)
+ {
+ rc = RegSetValueExW(subKey, BlobW, 0, REG_BINARY, buf, len);
[truncated at 1000 lines; 10343 more skipped]