| /* |
| * 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, *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) |
| { |
| 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); |
| prev = NULL; |
| } |
| 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; |
| list_init(&entry->entry); |
| 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, *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); |
| } |