blob: 6fe380c8e381b87f34c4781246db206914097a9a [file] [log] [blame]
/*
* 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);
}