| /* | |
| * 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 "crypt32_private.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(crypt); | |
| typedef struct _WINE_STORE_LIST_ENTRY | |
| { | |
| WINECRYPT_CERTSTORE *store; | |
| DWORD dwUpdateFlags; | |
| DWORD dwPriority; | |
| struct list entry; | |
| } WINE_STORE_LIST_ENTRY; | |
| typedef struct _WINE_COLLECTIONSTORE | |
| { | |
| WINECRYPT_CERTSTORE hdr; | |
| CRITICAL_SECTION cs; | |
| struct list stores; | |
| } WINE_COLLECTIONSTORE; | |
| static void Collection_addref(WINECRYPT_CERTSTORE *store) | |
| { | |
| LONG ref = InterlockedIncrement(&store->ref); | |
| TRACE("ref = %d\n", ref); | |
| } | |
| static DWORD Collection_release(WINECRYPT_CERTSTORE *store, DWORD flags) | |
| { | |
| WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; | |
| WINE_STORE_LIST_ENTRY *entry, *next; | |
| LONG ref; | |
| if(flags) | |
| FIXME("Unimplemented flags %x\n", flags); | |
| ref = InterlockedDecrement(&cs->hdr.ref); | |
| TRACE("(%p) ref=%d\n", store, ref); | |
| if(ref) | |
| return ERROR_SUCCESS; | |
| LIST_FOR_EACH_ENTRY_SAFE(entry, next, &cs->stores, WINE_STORE_LIST_ENTRY, entry) | |
| { | |
| TRACE("closing %p\n", entry); | |
| entry->store->vtbl->release(entry->store, flags); | |
| CryptMemFree(entry); | |
| } | |
| cs->cs.DebugInfo->Spare[0] = 0; | |
| DeleteCriticalSection(&cs->cs); | |
| CRYPT_FreeStore(store); | |
| return ERROR_SUCCESS; | |
| } | |
| static void Collection_releaseContext(WINECRYPT_CERTSTORE *store, context_t *context) | |
| { | |
| /* We don't cache context links, so just free them. */ | |
| Context_Free(context); | |
| } | |
| static context_t *CRYPT_CollectionCreateContextFromChild(WINE_COLLECTIONSTORE *store, | |
| WINE_STORE_LIST_ENTRY *storeEntry, context_t *child) | |
| { | |
| context_t *ret; | |
| ret = child->vtbl->clone(child, &store->hdr, TRUE); | |
| if (!ret) | |
| return NULL; | |
| ret->u.ptr = storeEntry; | |
| return ret; | |
| } | |
| static BOOL CRYPT_CollectionAddContext(WINE_COLLECTIONSTORE *store, | |
| unsigned int contextFuncsOffset, context_t *context, context_t *toReplace, | |
| context_t **pChildContext) | |
| { | |
| BOOL ret; | |
| context_t *childContext = NULL; | |
| WINE_STORE_LIST_ENTRY *storeEntry = NULL; | |
| TRACE("(%p, %d, %p, %p)\n", store, contextFuncsOffset, context, toReplace); | |
| ret = FALSE; | |
| if (toReplace) | |
| { | |
| context_t *existingLinked = toReplace->linked; | |
| CONTEXT_FUNCS *contextFuncs; | |
| storeEntry = toReplace->u.ptr; | |
| contextFuncs = (CONTEXT_FUNCS*)((LPBYTE)storeEntry->store->vtbl + | |
| contextFuncsOffset); | |
| ret = contextFuncs->addContext(storeEntry->store, context, | |
| existingLinked, &childContext, TRUE); | |
| } | |
| else | |
| { | |
| WINE_STORE_LIST_ENTRY *entry; | |
| EnterCriticalSection(&store->cs); | |
| LIST_FOR_EACH_ENTRY(entry, &store->stores, WINE_STORE_LIST_ENTRY, entry) | |
| { | |
| if (entry->dwUpdateFlags & CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG) | |
| { | |
| CONTEXT_FUNCS *contextFuncs = (CONTEXT_FUNCS*)( | |
| (LPBYTE)entry->store->vtbl + contextFuncsOffset); | |
| storeEntry = entry; | |
| ret = contextFuncs->addContext(entry->store, context, NULL, &childContext, TRUE); | |
| 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 context_t *CRYPT_CollectionAdvanceEnum(WINE_COLLECTIONSTORE *store, | |
| WINE_STORE_LIST_ENTRY *storeEntry, const CONTEXT_FUNCS *contextFuncs, | |
| context_t *prev) | |
| { | |
| context_t *child, *ret; | |
| struct list *storeNext = list_next(&store->stores, &storeEntry->entry); | |
| TRACE("(%p, %p, %p)\n", store, storeEntry, prev); | |
| if (prev) | |
| { | |
| /* Ref-counting funny business: "duplicate" (addref) the child, because | |
| * the free(pPrev) below can cause the ref count to become negative. | |
| */ | |
| child = prev->linked; | |
| Context_AddRef(child); | |
| child = contextFuncs->enumContext(storeEntry->store, child); | |
| Context_Release(prev); | |
| } | |
| else | |
| { | |
| child = contextFuncs->enumContext(storeEntry->store, NULL); | |
| } | |
| if (child) { | |
| ret = CRYPT_CollectionCreateContextFromChild(store, storeEntry, child); | |
| Context_Release(child); | |
| } | |
| 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->vtbl; | |
| WINE_STORE_LIST_ENTRY *storeNextEntry = | |
| LIST_ENTRY(storeNext, WINE_STORE_LIST_ENTRY, entry); | |
| CONTEXT_FUNCS *storeNextContexts = | |
| (CONTEXT_FUNCS*)((LPBYTE)storeNextEntry->store->vtbl + offset); | |
| ret = CRYPT_CollectionAdvanceEnum(store, storeNextEntry, | |
| storeNextContexts, NULL); | |
| } | |
| else | |
| { | |
| SetLastError(CRYPT_E_NOT_FOUND); | |
| ret = NULL; | |
| } | |
| } | |
| TRACE("returning %p\n", ret); | |
| return ret; | |
| } | |
| static BOOL Collection_addCert(WINECRYPT_CERTSTORE *store, context_t *cert, | |
| context_t *toReplace, context_t **ppStoreContext, BOOL use_link) | |
| { | |
| BOOL ret; | |
| context_t *childContext = NULL; | |
| WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; | |
| ret = CRYPT_CollectionAddContext(cs, offsetof(store_vtbl_t, certs), | |
| cert, toReplace, &childContext); | |
| if (ppStoreContext && childContext) | |
| { | |
| WINE_STORE_LIST_ENTRY *storeEntry = childContext->u.ptr; | |
| cert_t *context = (cert_t*)CRYPT_CollectionCreateContextFromChild(cs, storeEntry, | |
| childContext); | |
| *ppStoreContext = &context->base; | |
| } | |
| if (childContext) | |
| Context_Release(childContext); | |
| return ret; | |
| } | |
| static context_t *Collection_enumCert(WINECRYPT_CERTSTORE *store, context_t *prev) | |
| { | |
| WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; | |
| context_t *ret; | |
| TRACE("(%p, %p)\n", store, prev); | |
| EnterCriticalSection(&cs->cs); | |
| if (prev) | |
| { | |
| WINE_STORE_LIST_ENTRY *storeEntry = prev->u.ptr; | |
| ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, | |
| &storeEntry->store->vtbl->certs, prev); | |
| } | |
| else | |
| { | |
| if (!list_empty(&cs->stores)) | |
| { | |
| WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next, | |
| WINE_STORE_LIST_ENTRY, entry); | |
| ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, | |
| &storeEntry->store->vtbl->certs, NULL); | |
| } | |
| else | |
| { | |
| SetLastError(CRYPT_E_NOT_FOUND); | |
| ret = NULL; | |
| } | |
| } | |
| LeaveCriticalSection(&cs->cs); | |
| TRACE("returning %p\n", ret); | |
| return ret; | |
| } | |
| static BOOL Collection_deleteCert(WINECRYPT_CERTSTORE *store, context_t *context) | |
| { | |
| cert_t *cert = (cert_t*)context; | |
| cert_t *linked; | |
| TRACE("(%p, %p)\n", store, cert); | |
| linked = (cert_t*)context->linked; | |
| return CertDeleteCertificateFromStore(&linked->ctx); | |
| } | |
| static BOOL Collection_addCRL(WINECRYPT_CERTSTORE *store, context_t *crl, | |
| context_t *toReplace, context_t **ppStoreContext, BOOL use_link) | |
| { | |
| BOOL ret; | |
| context_t *childContext = NULL; | |
| WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; | |
| ret = CRYPT_CollectionAddContext(cs, offsetof(store_vtbl_t, crls), | |
| crl, toReplace, &childContext); | |
| if (ppStoreContext && childContext) | |
| { | |
| WINE_STORE_LIST_ENTRY *storeEntry = childContext->u.ptr; | |
| crl_t *context = (crl_t*)CRYPT_CollectionCreateContextFromChild(cs, storeEntry, | |
| childContext); | |
| *ppStoreContext = &context->base; | |
| } | |
| if (childContext) | |
| Context_Release(childContext); | |
| return ret; | |
| } | |
| static context_t *Collection_enumCRL(WINECRYPT_CERTSTORE *store, context_t *prev) | |
| { | |
| WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; | |
| context_t *ret; | |
| TRACE("(%p, %p)\n", store, prev); | |
| EnterCriticalSection(&cs->cs); | |
| if (prev) | |
| { | |
| WINE_STORE_LIST_ENTRY *storeEntry = prev->u.ptr; | |
| ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, | |
| &storeEntry->store->vtbl->crls, prev); | |
| } | |
| else | |
| { | |
| if (!list_empty(&cs->stores)) | |
| { | |
| WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next, | |
| WINE_STORE_LIST_ENTRY, entry); | |
| ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, | |
| &storeEntry->store->vtbl->crls, NULL); | |
| } | |
| else | |
| { | |
| SetLastError(CRYPT_E_NOT_FOUND); | |
| ret = NULL; | |
| } | |
| } | |
| LeaveCriticalSection(&cs->cs); | |
| TRACE("returning %p\n", ret); | |
| return ret; | |
| } | |
| static BOOL Collection_deleteCRL(WINECRYPT_CERTSTORE *store, context_t *context) | |
| { | |
| crl_t *crl = (crl_t*)context, *linked; | |
| TRACE("(%p, %p)\n", store, crl); | |
| linked = (crl_t*)context->linked; | |
| return CertDeleteCRLFromStore(&linked->ctx); | |
| } | |
| static BOOL Collection_addCTL(WINECRYPT_CERTSTORE *store, context_t *ctl, | |
| context_t *toReplace, context_t **ppStoreContext, BOOL use_link) | |
| { | |
| BOOL ret; | |
| context_t *childContext = NULL; | |
| WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; | |
| ret = CRYPT_CollectionAddContext(cs, offsetof(store_vtbl_t, ctls), | |
| ctl, toReplace, &childContext); | |
| if (ppStoreContext && childContext) | |
| { | |
| WINE_STORE_LIST_ENTRY *storeEntry = childContext->u.ptr; | |
| ctl_t *context = (ctl_t*)CRYPT_CollectionCreateContextFromChild(cs, storeEntry, | |
| childContext); | |
| *ppStoreContext = &context->base; | |
| } | |
| if (childContext) | |
| Context_Release(childContext); | |
| return ret; | |
| } | |
| static context_t *Collection_enumCTL(WINECRYPT_CERTSTORE *store, context_t *prev) | |
| { | |
| WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; | |
| void *ret; | |
| TRACE("(%p, %p)\n", store, prev); | |
| EnterCriticalSection(&cs->cs); | |
| if (prev) | |
| { | |
| WINE_STORE_LIST_ENTRY *storeEntry = prev->u.ptr; | |
| ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, | |
| &storeEntry->store->vtbl->ctls, prev); | |
| } | |
| else | |
| { | |
| if (!list_empty(&cs->stores)) | |
| { | |
| WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next, | |
| WINE_STORE_LIST_ENTRY, entry); | |
| ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, | |
| &storeEntry->store->vtbl->ctls, NULL); | |
| } | |
| else | |
| { | |
| SetLastError(CRYPT_E_NOT_FOUND); | |
| ret = NULL; | |
| } | |
| } | |
| LeaveCriticalSection(&cs->cs); | |
| TRACE("returning %p\n", ret); | |
| return ret; | |
| } | |
| static BOOL Collection_deleteCTL(WINECRYPT_CERTSTORE *store, context_t *context) | |
| { | |
| ctl_t *ctl = (ctl_t*)context, *linked; | |
| TRACE("(%p, %p)\n", store, ctl); | |
| linked = (ctl_t*)context->linked; | |
| return CertDeleteCTLFromStore(&linked->ctx); | |
| } | |
| static BOOL Collection_control(WINECRYPT_CERTSTORE *cert_store, DWORD dwFlags, | |
| DWORD dwCtrlType, void const *pvCtrlPara) | |
| { | |
| BOOL ret; | |
| WINE_COLLECTIONSTORE *store = (WINE_COLLECTIONSTORE*)cert_store; | |
| WINE_STORE_LIST_ENTRY *entry; | |
| TRACE("(%p, %08x, %d, %p)\n", cert_store, 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->vtbl->control) | |
| { | |
| ret = entry->store->vtbl->control(entry->store, dwFlags, dwCtrlType, pvCtrlPara); | |
| if (!ret) | |
| break; | |
| } | |
| } | |
| LeaveCriticalSection(&store->cs); | |
| return ret; | |
| } | |
| static const store_vtbl_t CollectionStoreVtbl = { | |
| Collection_addref, | |
| Collection_release, | |
| Collection_releaseContext, | |
| Collection_control, | |
| { | |
| Collection_addCert, | |
| Collection_enumCert, | |
| Collection_deleteCert | |
| }, { | |
| Collection_addCRL, | |
| Collection_enumCRL, | |
| Collection_deleteCRL | |
| }, { | |
| Collection_addCTL, | |
| Collection_enumCTL, | |
| Collection_deleteCTL | |
| } | |
| }; | |
| WINECRYPT_CERTSTORE *CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv, | |
| DWORD dwFlags, const void *pvPara) | |
| { | |
| WINE_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, &CollectionStoreVtbl); | |
| InitializeCriticalSection(&store->cs); | |
| store->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PWINE_COLLECTIONSTORE->cs"); | |
| list_init(&store->stores); | |
| } | |
| } | |
| return (WINECRYPT_CERTSTORE*)store; | |
| } | |
| BOOL WINAPI CertAddStoreToCollection(HCERTSTORE hCollectionStore, | |
| HCERTSTORE hSiblingStore, DWORD dwUpdateFlags, DWORD dwPriority) | |
| { | |
| WINE_COLLECTIONSTORE *collection = hCollectionStore; | |
| WINECRYPT_CERTSTORE *sibling = hSiblingStore; | |
| WINE_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; | |
| TRACE("%p: adding %p, priority %d\n", collection, entry, dwPriority); | |
| EnterCriticalSection(&collection->cs); | |
| if (dwPriority) | |
| { | |
| WINE_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) | |
| { | |
| WINE_COLLECTIONSTORE *collection = hCollectionStore; | |
| WINECRYPT_CERTSTORE *sibling = hSiblingStore; | |
| WINE_STORE_LIST_ENTRY *store; | |
| 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(store, &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); | |
| } |