/* * Copyright 2004-2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include <stdarg.h> #include "windef.h" #include "winbase.h" #include "wincrypt.h" #include "wine/debug.h" #include "wine/list.h" #include "crypt32_private.h" WINE_DEFAULT_DEBUG_CHANNEL(crypt); typedef struct _WINE_STORE_LIST_ENTRY { PWINECRYPT_CERTSTORE store; DWORD dwUpdateFlags; DWORD dwPriority; struct list entry; } WINE_STORE_LIST_ENTRY, *PWINE_STORE_LIST_ENTRY; typedef struct _WINE_COLLECTIONSTORE { WINECRYPT_CERTSTORE hdr; CRITICAL_SECTION cs; struct list stores; } WINE_COLLECTIONSTORE, *PWINE_COLLECTIONSTORE; static void WINAPI CRYPT_CollectionCloseStore(HCERTSTORE store, DWORD dwFlags) { PWINE_COLLECTIONSTORE cs = store; PWINE_STORE_LIST_ENTRY entry, next; TRACE("(%p, %08x)\n", store, dwFlags); LIST_FOR_EACH_ENTRY_SAFE(entry, next, &cs->stores, WINE_STORE_LIST_ENTRY, entry) { TRACE("closing %p\n", entry); CertCloseStore(entry->store, dwFlags); CryptMemFree(entry); } cs->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&cs->cs); CRYPT_FreeStore(store); } static void *CRYPT_CollectionCreateContextFromChild(PWINE_COLLECTIONSTORE store, PWINE_STORE_LIST_ENTRY storeEntry, void *child, size_t contextSize, BOOL addRef) { void *ret = Context_CreateLinkContext(contextSize, child, sizeof(PWINE_STORE_LIST_ENTRY), addRef); if (ret) *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(ret, contextSize) = storeEntry; return ret; } static BOOL CRYPT_CollectionAddContext(PWINE_COLLECTIONSTORE store, unsigned int contextFuncsOffset, void *context, void *toReplace, unsigned int contextSize, void **pChildContext) { BOOL ret; void *childContext = NULL; PWINE_STORE_LIST_ENTRY storeEntry = NULL; TRACE("(%p, %d, %p, %p, %d)\n", store, contextFuncsOffset, context, toReplace, contextSize); ret = FALSE; if (toReplace) { void *existingLinked = Context_GetLinkedContext(toReplace, contextSize); PCONTEXT_FUNCS contextFuncs; storeEntry = *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(toReplace, contextSize); contextFuncs = (PCONTEXT_FUNCS)((LPBYTE)storeEntry->store + contextFuncsOffset); ret = contextFuncs->addContext(storeEntry->store, context, existingLinked, (const void **)&childContext); } else { PWINE_STORE_LIST_ENTRY entry, next; EnterCriticalSection(&store->cs); LIST_FOR_EACH_ENTRY_SAFE(entry, next, &store->stores, WINE_STORE_LIST_ENTRY, entry) { if (entry->dwUpdateFlags & CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG) { PCONTEXT_FUNCS contextFuncs = (PCONTEXT_FUNCS)( (LPBYTE)entry->store + contextFuncsOffset); storeEntry = entry; ret = contextFuncs->addContext(entry->store, context, NULL, (const void **)&childContext); break; } } LeaveCriticalSection(&store->cs); if (!storeEntry) SetLastError(E_ACCESSDENIED); } *pChildContext = childContext; return ret; } /* Advances a collection enumeration by one context, if possible, where * advancing means: * - calling the current store's enumeration function once, and returning * the enumerated context 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 void *CRYPT_CollectionAdvanceEnum(PWINE_COLLECTIONSTORE store, PWINE_STORE_LIST_ENTRY storeEntry, const CONTEXT_FUNCS *contextFuncs, PCWINE_CONTEXT_INTERFACE contextInterface, void *pPrev, size_t contextSize) { void *ret, *child; struct list *storeNext = list_next(&store->stores, &storeEntry->entry); TRACE("(%p, %p, %p)\n", store, storeEntry, pPrev); if (pPrev) { /* Ref-counting funny business: "duplicate" (addref) the child, because * the free(pPrev) below can cause the ref count to become negative. */ child = Context_GetLinkedContext(pPrev, contextSize); contextInterface->duplicate(child); child = contextFuncs->enumContext(storeEntry->store, child); contextInterface->free(pPrev); pPrev = NULL; } else child = contextFuncs->enumContext(storeEntry->store, NULL); if (child) ret = CRYPT_CollectionCreateContextFromChild(store, storeEntry, child, contextSize, FALSE); else { if (storeNext) { /* We always want the same function pointers (from certs, crls) * in the next store, so use the same offset into the next store. */ size_t offset = (const BYTE *)contextFuncs - (LPBYTE)storeEntry->store; PWINE_STORE_LIST_ENTRY storeNextEntry = LIST_ENTRY(storeNext, WINE_STORE_LIST_ENTRY, entry); PCONTEXT_FUNCS storeNextContexts = (PCONTEXT_FUNCS)((LPBYTE)storeNextEntry->store + offset); ret = CRYPT_CollectionAdvanceEnum(store, storeNextEntry, storeNextContexts, contextInterface, NULL, contextSize); } else { SetLastError(CRYPT_E_NOT_FOUND); ret = NULL; } } TRACE("returning %p\n", ret); return ret; } static BOOL CRYPT_CollectionAddCert(PWINECRYPT_CERTSTORE store, void *cert, void *toReplace, const void **ppStoreContext) { BOOL ret; void *childContext = NULL; PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store; ret = CRYPT_CollectionAddContext(cs, offsetof(WINECRYPT_CERTSTORE, certs), cert, toReplace, sizeof(CERT_CONTEXT), &childContext); if (ppStoreContext && childContext) { PWINE_STORE_LIST_ENTRY storeEntry = *(PWINE_STORE_LIST_ENTRY *) Context_GetExtra(childContext, sizeof(CERT_CONTEXT)); PCERT_CONTEXT context = CRYPT_CollectionCreateContextFromChild(cs, storeEntry, childContext, sizeof(CERT_CONTEXT), TRUE); if (context) context->hCertStore = store; *ppStoreContext = context; } CertFreeCertificateContext(childContext); return ret; } static void *CRYPT_CollectionEnumCert(PWINECRYPT_CERTSTORE store, void *pPrev) { PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store; void *ret; TRACE("(%p, %p)\n", store, pPrev); EnterCriticalSection(&cs->cs); if (pPrev) { PWINE_STORE_LIST_ENTRY storeEntry = *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(pPrev, sizeof(CERT_CONTEXT)); ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, &storeEntry->store->certs, pCertInterface, pPrev, sizeof(CERT_CONTEXT)); } else { if (!list_empty(&cs->stores)) { PWINE_STORE_LIST_ENTRY storeEntry = LIST_ENTRY(cs->stores.next, WINE_STORE_LIST_ENTRY, entry); ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, &storeEntry->store->certs, pCertInterface, NULL, sizeof(CERT_CONTEXT)); } else { SetLastError(CRYPT_E_NOT_FOUND); ret = NULL; } } LeaveCriticalSection(&cs->cs); if (ret) ((PCERT_CONTEXT)ret)->hCertStore = store; TRACE("returning %p\n", ret); return ret; } static BOOL CRYPT_CollectionDeleteCert(PWINECRYPT_CERTSTORE store, void *pCertContext) { BOOL ret; PCCERT_CONTEXT linked; TRACE("(%p, %p)\n", store, pCertContext); /* Deleting the linked context results in its ref count getting * decreased, but the caller of this (CertDeleteCertificateFromStore) also * decreases pCertContext's ref count, by calling * CertFreeCertificateContext. Increase ref count of linked context to * compensate. */ linked = Context_GetLinkedContext(pCertContext, sizeof(CERT_CONTEXT)); CertDuplicateCertificateContext(linked); ret = CertDeleteCertificateFromStore(linked); return ret; } static BOOL CRYPT_CollectionAddCRL(PWINECRYPT_CERTSTORE store, void *crl, void *toReplace, const void **ppStoreContext) { BOOL ret; void *childContext = NULL; PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store; ret = CRYPT_CollectionAddContext(cs, offsetof(WINECRYPT_CERTSTORE, crls), crl, toReplace, sizeof(CRL_CONTEXT), &childContext); if (ppStoreContext && childContext) { PWINE_STORE_LIST_ENTRY storeEntry = *(PWINE_STORE_LIST_ENTRY *) Context_GetExtra(childContext, sizeof(CRL_CONTEXT)); PCRL_CONTEXT context = CRYPT_CollectionCreateContextFromChild(cs, storeEntry, childContext, sizeof(CRL_CONTEXT), TRUE); if (context) context->hCertStore = store; *ppStoreContext = context; } CertFreeCRLContext(childContext); return ret; } static void *CRYPT_CollectionEnumCRL(PWINECRYPT_CERTSTORE store, void *pPrev) { PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store; void *ret; TRACE("(%p, %p)\n", store, pPrev); EnterCriticalSection(&cs->cs); if (pPrev) { PWINE_STORE_LIST_ENTRY storeEntry = *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(pPrev, sizeof(CRL_CONTEXT)); ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, &storeEntry->store->crls, pCRLInterface, pPrev, sizeof(CRL_CONTEXT)); } else { if (!list_empty(&cs->stores)) { PWINE_STORE_LIST_ENTRY storeEntry = LIST_ENTRY(cs->stores.next, WINE_STORE_LIST_ENTRY, entry); ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, &storeEntry->store->crls, pCRLInterface, NULL, sizeof(CRL_CONTEXT)); } else { SetLastError(CRYPT_E_NOT_FOUND); ret = NULL; } } LeaveCriticalSection(&cs->cs); if (ret) ((PCRL_CONTEXT)ret)->hCertStore = store; TRACE("returning %p\n", ret); return ret; } static BOOL CRYPT_CollectionDeleteCRL(PWINECRYPT_CERTSTORE store, void *pCrlContext) { BOOL ret; PCCRL_CONTEXT linked; TRACE("(%p, %p)\n", store, pCrlContext); /* Deleting the linked context results in its ref count getting * decreased, but the caller of this (CertDeleteCRLFromStore) also * decreases pCrlContext's ref count, by calling CertFreeCRLContext. * Increase ref count of linked context to compensate. */ linked = Context_GetLinkedContext(pCrlContext, sizeof(CRL_CONTEXT)); CertDuplicateCRLContext(linked); ret = CertDeleteCRLFromStore(linked); return ret; } static BOOL CRYPT_CollectionAddCTL(PWINECRYPT_CERTSTORE store, void *ctl, void *toReplace, const void **ppStoreContext) { BOOL ret; void *childContext = NULL; PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store; ret = CRYPT_CollectionAddContext(cs, offsetof(WINECRYPT_CERTSTORE, ctls), ctl, toReplace, sizeof(CTL_CONTEXT), &childContext); if (ppStoreContext && childContext) { PWINE_STORE_LIST_ENTRY storeEntry = *(PWINE_STORE_LIST_ENTRY *) Context_GetExtra(childContext, sizeof(CTL_CONTEXT)); PCTL_CONTEXT context = CRYPT_CollectionCreateContextFromChild(cs, storeEntry, childContext, sizeof(CTL_CONTEXT), TRUE); if (context) context->hCertStore = store; *ppStoreContext = context; } CertFreeCTLContext(childContext); return ret; } static void *CRYPT_CollectionEnumCTL(PWINECRYPT_CERTSTORE store, void *pPrev) { PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store; void *ret; TRACE("(%p, %p)\n", store, pPrev); EnterCriticalSection(&cs->cs); if (pPrev) { PWINE_STORE_LIST_ENTRY storeEntry = *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(pPrev, sizeof(CTL_CONTEXT)); ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, &storeEntry->store->ctls, pCTLInterface, pPrev, sizeof(CTL_CONTEXT)); } else { if (!list_empty(&cs->stores)) { PWINE_STORE_LIST_ENTRY storeEntry = LIST_ENTRY(cs->stores.next, WINE_STORE_LIST_ENTRY, entry); ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, &storeEntry->store->ctls, pCTLInterface, NULL, sizeof(CTL_CONTEXT)); } else { SetLastError(CRYPT_E_NOT_FOUND); ret = NULL; } } LeaveCriticalSection(&cs->cs); if (ret) ((PCTL_CONTEXT)ret)->hCertStore = store; TRACE("returning %p\n", ret); return ret; } static BOOL CRYPT_CollectionDeleteCTL(PWINECRYPT_CERTSTORE store, void *pCtlContext) { BOOL ret; PCCTL_CONTEXT linked; TRACE("(%p, %p)\n", store, pCtlContext); /* Deleting the linked context results in its ref count getting * decreased, but the caller of this (CertDeleteCTLFromStore) also * decreases pCtlContext's ref count, by calling CertFreeCTLContext. * Increase ref count of linked context to compensate. */ linked = Context_GetLinkedContext(pCtlContext, sizeof(CTL_CONTEXT)); CertDuplicateCTLContext(linked); ret = CertDeleteCTLFromStore(linked); return ret; } static BOOL WINAPI CRYPT_CollectionControl(HCERTSTORE hCertStore, DWORD dwFlags, DWORD dwCtrlType, void const *pvCtrlPara) { BOOL ret; PWINE_COLLECTIONSTORE store = hCertStore; PWINE_STORE_LIST_ENTRY entry; TRACE("(%p, %08x, %d, %p)\n", hCertStore, dwFlags, dwCtrlType, pvCtrlPara); if (!store) return TRUE; if (store->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC) { SetLastError(E_INVALIDARG); return FALSE; } if (store->hdr.type != StoreTypeCollection) { SetLastError(E_INVALIDARG); return FALSE; } ret = TRUE; EnterCriticalSection(&store->cs); LIST_FOR_EACH_ENTRY(entry, &store->stores, WINE_STORE_LIST_ENTRY, entry) { if (entry->store->control) { ret = entry->store->control(entry->store, dwFlags, dwCtrlType, pvCtrlPara); if (!ret) break; } } LeaveCriticalSection(&store->cs); return ret; } PWINECRYPT_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, dwFlags, StoreTypeCollection); store->hdr.closeStore = CRYPT_CollectionCloseStore; store->hdr.certs.addContext = CRYPT_CollectionAddCert; store->hdr.certs.enumContext = CRYPT_CollectionEnumCert; store->hdr.certs.deleteContext = CRYPT_CollectionDeleteCert; store->hdr.crls.addContext = CRYPT_CollectionAddCRL; store->hdr.crls.enumContext = CRYPT_CollectionEnumCRL; store->hdr.crls.deleteContext = CRYPT_CollectionDeleteCRL; store->hdr.ctls.addContext = CRYPT_CollectionAddCTL; store->hdr.ctls.enumContext = CRYPT_CollectionEnumCTL; store->hdr.ctls.deleteContext = CRYPT_CollectionDeleteCTL; store->hdr.control = CRYPT_CollectionControl; InitializeCriticalSection(&store->cs); store->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PWINE_COLLECTIONSTORE->cs"); list_init(&store->stores); } } return (PWINECRYPT_CERTSTORE)store; } BOOL WINAPI CertAddStoreToCollection(HCERTSTORE hCollectionStore, HCERTSTORE hSiblingStore, DWORD dwUpdateFlags, DWORD dwPriority) { PWINE_COLLECTIONSTORE collection = hCollectionStore; WINECRYPT_CERTSTORE *sibling = hSiblingStore; PWINE_STORE_LIST_ENTRY entry; BOOL ret; TRACE("(%p, %p, %08x, %d)\n", hCollectionStore, hSiblingStore, dwUpdateFlags, dwPriority); if (!collection || !sibling) return TRUE; if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC) { SetLastError(E_INVALIDARG); return FALSE; } if (collection->hdr.type != StoreTypeCollection) { SetLastError(E_INVALIDARG); return FALSE; } if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) { SetLastError(E_INVALIDARG); return FALSE; } entry = CryptMemAlloc(sizeof(WINE_STORE_LIST_ENTRY)); if (entry) { InterlockedIncrement(&sibling->ref); TRACE("sibling %p's ref count is %d\n", sibling, sibling->ref); entry->store = sibling; entry->dwUpdateFlags = dwUpdateFlags; entry->dwPriority = dwPriority; list_init(&entry->entry); TRACE("%p: adding %p, priority %d\n", collection, entry, dwPriority); EnterCriticalSection(&collection->cs); if (dwPriority) { PWINE_STORE_LIST_ENTRY cursor; BOOL added = FALSE; LIST_FOR_EACH_ENTRY(cursor, &collection->stores, WINE_STORE_LIST_ENTRY, entry) { if (cursor->dwPriority < dwPriority) { list_add_before(&cursor->entry, &entry->entry); added = TRUE; break; } } if (!added) list_add_tail(&collection->stores, &entry->entry); } else list_add_tail(&collection->stores, &entry->entry); LeaveCriticalSection(&collection->cs); ret = TRUE; } else ret = FALSE; return ret; } void WINAPI CertRemoveStoreFromCollection(HCERTSTORE hCollectionStore, HCERTSTORE hSiblingStore) { PWINE_COLLECTIONSTORE collection = hCollectionStore; WINECRYPT_CERTSTORE *sibling = hSiblingStore; PWINE_STORE_LIST_ENTRY store, next; TRACE("(%p, %p)\n", hCollectionStore, hSiblingStore); if (!collection || !sibling) return; if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC) { SetLastError(E_INVALIDARG); return; } if (collection->hdr.type != StoreTypeCollection) return; if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) { SetLastError(E_INVALIDARG); return; } EnterCriticalSection(&collection->cs); LIST_FOR_EACH_ENTRY_SAFE(store, next, &collection->stores, WINE_STORE_LIST_ENTRY, entry) { if (store->store == sibling) { list_remove(&store->entry); CertCloseStore(store->store, 0); CryptMemFree(store); break; } } LeaveCriticalSection(&collection->cs); }