| /* | |
| * 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, PCONTEXT_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 = (LPBYTE)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); | |
| } |