crypt32: Fix certificate adding

- Implement add disposition in CertAddCertificateContextToStore,
  rather than in each store.
- Add a few more tests.
This commit is contained in:
Juan Lang 2006-03-30 07:27:05 -08:00 committed by Alexandre Julliard
parent ef9038c761
commit 5eadd8c791
2 changed files with 289 additions and 144 deletions

View File

@ -126,11 +126,14 @@ struct _WINE_CERT_CONTEXT;
typedef struct _WINE_CERT_CONTEXT * (*EnumCertFunc)
(struct WINE_CRYPTCERTSTORE *store, struct _WINE_CERT_CONTEXT *pPrev);
/* Called to add a new certificate context to a store. If ppStoreContext is
* not NULL, the added context should be returned in *ppStoreContext.
/* Called to add a certificate context to a store. If toReplace is not NULL,
* context replaces toReplace in the store, and access checks should not be
* performed. Otherwise context is a new context, and it should only be
* added if the store allows it. If ppStoreContext is not NULL, the added
* context should be returned in *ppStoreContext.
*/
typedef BOOL (*AddCertFunc)(struct WINE_CRYPTCERTSTORE *store,
struct _WINE_CERT_CONTEXT *context, DWORD dwAddDisposition,
struct _WINE_CERT_CONTEXT *context, struct _WINE_CERT_CONTEXT *toReplace,
PCCERT_CONTEXT *ppStoreContext);
typedef enum _CertStoreType {
@ -174,7 +177,7 @@ typedef struct _WINE_CERT_CONTEXT
LONG ref;
ContextType type;
} WINE_CERT_CONTEXT, *PWINE_CERT_CONTEXT;
typedef const struct _WINE_CERT_CONTEXT PCWINE_CERT_CONTEXT;
typedef const struct _WINE_CERT_CONTEXT *PCWINE_CERT_CONTEXT;
typedef struct _WINE_CERT_CONTEXT_DATA
{
@ -317,83 +320,38 @@ static void CRYPT_InitCertRef(PWINE_CERT_CONTEXT_LINK ref,
}
static BOOL CRYPT_MemAddCert(PWINECRYPT_CERTSTORE store,
PWINE_CERT_CONTEXT cert, DWORD dwAddDisposition,
PWINE_CERT_CONTEXT cert, PWINE_CERT_CONTEXT toReplace,
PCCERT_CONTEXT *ppStoreContext)
{
WINE_MEMSTORE *ms = (WINE_MEMSTORE *)store;
BOOL add = FALSE, ret;
PCCERT_CONTEXT existing = NULL;
PWINE_CERT_LIST_ENTRY entry = CryptMemAlloc(sizeof(WINE_CERT_LIST_ENTRY));
BOOL ret;
TRACE("(%p, %p, %ld, %p)\n", store, cert, dwAddDisposition, ppStoreContext);
TRACE("(%p, %p, %p, %p)\n", store, cert, toReplace, ppStoreContext);
if (dwAddDisposition != CERT_STORE_ADD_ALWAYS)
if (entry)
{
BYTE hashToAdd[20];
DWORD size = sizeof(hashToAdd);
PWINE_CERT_LIST_ENTRY existing = (PWINE_CERT_LIST_ENTRY)toReplace;
ret = CRYPT_GetCertificateContextProperty((PWINE_CERT_CONTEXT)cert,
CERT_HASH_PROP_ID, hashToAdd, &size);
if (ret)
{
CRYPT_HASH_BLOB blob = { sizeof(hashToAdd), hashToAdd };
existing = CertFindCertificateInStore(store,
cert->cert.dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob,
NULL);
}
}
switch (dwAddDisposition)
{
case CERT_STORE_ADD_ALWAYS:
add = TRUE;
break;
case CERT_STORE_ADD_NEW:
{
TRACE("adding %p\n", entry);
CRYPT_InitCertRef(&entry->cert, (PWINE_CERT_CONTEXT)cert, store);
EnterCriticalSection(&ms->cs);
if (existing)
{
TRACE("found matching certificate, not adding\n");
SetLastError(CRYPT_E_EXISTS);
add = FALSE;
entry->entry.prev = existing->entry.prev;
entry->entry.next = existing->entry.next;
entry->entry.prev->next = &entry->entry;
entry->entry.next->prev = &entry->entry;
existing->entry.prev = existing->entry.next = &existing->entry;
CertFreeCertificateContext((PCCERT_CONTEXT)existing);
}
else
add = TRUE;
break;
}
case CERT_STORE_ADD_REPLACE_EXISTING:
{
add = TRUE;
if (existing)
{
TRACE("found matching certificate, replacing\n");
CertDeleteCertificateFromStore(existing);
}
break;
}
default:
FIXME("Unimplemented add disposition %ld\n", dwAddDisposition);
add = FALSE;
}
if (existing)
CertFreeCertificateContext(existing);
if (add)
{
PWINE_CERT_LIST_ENTRY entry = CryptMemAlloc(
sizeof(WINE_CERT_LIST_ENTRY));
if (entry)
{
TRACE("adding %p\n", entry);
CRYPT_InitCertRef(&entry->cert, (PWINE_CERT_CONTEXT)cert, store);
EnterCriticalSection(&ms->cs);
list_add_tail(&ms->certs, &entry->entry);
LeaveCriticalSection(&ms->cs);
if (ppStoreContext)
*ppStoreContext =
CertDuplicateCertificateContext((PCCERT_CONTEXT)entry);
ret = TRUE;
}
else
ret = FALSE;
LeaveCriticalSection(&ms->cs);
if (ppStoreContext)
*ppStoreContext =
CertDuplicateCertificateContext((PCCERT_CONTEXT)entry);
ret = TRUE;
}
else
ret = FALSE;
@ -506,30 +464,78 @@ static WINECRYPT_CERTSTORE *CRYPT_MemOpenStore(HCRYPTPROV hCryptProv,
return (PWINECRYPT_CERTSTORE)store;
}
static PWINE_COLLECTION_CERT_CONTEXT CRYPT_CollectionCreateContextFromChild(
PWINE_COLLECTIONSTORE store, PWINE_STORE_LIST_ENTRY storeEntry,
PWINE_CERT_CONTEXT child)
{
PWINE_COLLECTION_CERT_CONTEXT ret =
CryptMemAlloc(sizeof(WINE_COLLECTION_CERT_CONTEXT));
if (ret)
{
CRYPT_InitCertRef((PWINE_CERT_CONTEXT_LINK)ret, child, store);
/* The child has already been addref'd, and CRYPT_InitCertRef does
* again, so free child once to get the ref count right. (Not doing so
* will leak memory if the caller calls CertFreeCertificateContext
* rather than CertEnumCertificatesInStore.)
*/
CertFreeCertificateContext((PCCERT_CONTEXT)child);
ret->storeEntry = storeEntry;
}
else
CertFreeCertificateContext((PCCERT_CONTEXT)child);
return ret;
}
static BOOL CRYPT_CollectionAddCert(PWINECRYPT_CERTSTORE store,
PWINE_CERT_CONTEXT cert, DWORD dwAddDisposition,
PWINE_CERT_CONTEXT cert, PWINE_CERT_CONTEXT toReplace,
PCCERT_CONTEXT *ppStoreContext)
{
PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
PWINE_STORE_LIST_ENTRY entry, next;
BOOL ret;
PCCERT_CONTEXT childContext = NULL;
PWINE_STORE_LIST_ENTRY storeEntry = NULL;
TRACE("(%p, %p, %ld, %p)\n", store, cert, dwAddDisposition, ppStoreContext);
TRACE("(%p, %p, %p, %p)\n", store, cert, toReplace, ppStoreContext);
ret = FALSE;
EnterCriticalSection(&cs->cs);
LIST_FOR_EACH_ENTRY_SAFE(entry, next, &cs->stores, WINE_STORE_LIST_ENTRY,
entry)
if (toReplace)
{
if (entry->dwUpdateFlags & CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG)
{
ret = entry->store->addCert(entry->store, cert, dwAddDisposition,
ppStoreContext);
break;
}
PWINE_COLLECTION_CERT_CONTEXT existing =
(PWINE_COLLECTION_CERT_CONTEXT)toReplace;
storeEntry = existing->storeEntry;
ret = storeEntry->store->addCert(storeEntry->store, cert,
existing->cert.linked, &childContext);
}
LeaveCriticalSection(&cs->cs);
SetLastError(ret ? ERROR_SUCCESS : HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
else
{
PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
PWINE_STORE_LIST_ENTRY entry, next;
EnterCriticalSection(&cs->cs);
LIST_FOR_EACH_ENTRY_SAFE(entry, next, &cs->stores,
WINE_STORE_LIST_ENTRY, entry)
{
if (entry->dwUpdateFlags & CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG)
{
storeEntry = entry;
ret = entry->store->addCert(entry->store, cert, NULL,
&childContext);
break;
}
}
LeaveCriticalSection(&cs->cs);
if (!storeEntry)
SetLastError(HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
}
if (ppStoreContext && childContext)
{
*ppStoreContext =
(PCCERT_CONTEXT)CRYPT_CollectionCreateContextFromChild(
(PWINE_COLLECTIONSTORE)store, storeEntry,
(PWINE_CERT_CONTEXT)childContext);
}
CertFreeCertificateContext(childContext);
return ret;
}
@ -574,7 +580,8 @@ static PWINE_COLLECTION_CERT_CONTEXT CRYPT_CollectionAdvanceEnum(
{
/* Ref-counting funny business: "duplicate" (addref) the child, because
* the CertFreeCertificateContext(pPrev) below can cause the ref count
* to become negative. See comment below as well.
* to become negative. See comment in
* CRYPT_CollectionCreateContextFromChild as well.
*/
child = ((PWINE_COLLECTION_CERT_CONTEXT)pPrev)->cert.linked;
CertDuplicateCertificateContext((PCCERT_CONTEXT)child);
@ -587,22 +594,7 @@ static PWINE_COLLECTION_CERT_CONTEXT CRYPT_CollectionAdvanceEnum(
child = storeEntry->store->enumCert((HCERTSTORE)storeEntry->store,
NULL);
if (child)
{
ret = CryptMemAlloc(sizeof(WINE_COLLECTION_CERT_CONTEXT));
if (ret)
{
CRYPT_InitCertRef((PWINE_CERT_CONTEXT_LINK)ret, child, store);
/* enumCert already addref'd once, and CRYPT_InitCertRef does again,
* so free child once to get the ref count right. (Not doing so
* will leak memory if the caller calls CertFreeCertificateContext
* rather than CertEnumCertificatesInStore.)
*/
CertFreeCertificateContext((PCCERT_CONTEXT)child);
ret->storeEntry = storeEntry;
}
else
CertFreeCertificateContext((PCCERT_CONTEXT)child);
}
ret = CRYPT_CollectionCreateContextFromChild(store, storeEntry, child);
else
{
if (storeNext)
@ -713,36 +705,40 @@ static void WINAPI CRYPT_ProvCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
}
static BOOL CRYPT_ProvAddCert(PWINECRYPT_CERTSTORE store,
PWINE_CERT_CONTEXT cert, DWORD dwAddDisposition,
PWINE_CERT_CONTEXT cert, PWINE_CERT_CONTEXT toReplace,
PCCERT_CONTEXT *ppStoreContext)
{
PWINE_PROVIDERSTORE ps = (PWINE_PROVIDERSTORE)store;
BOOL ret;
TRACE("(%p, %p, %ld, %p)\n", store, cert, dwAddDisposition, ppStoreContext);
TRACE("(%p, %p, %p, %p)\n", store, cert, toReplace, ppStoreContext);
if (ps->hdr.dwOpenFlags & CERT_STORE_READONLY_FLAG)
{
SetLastError(ERROR_ACCESS_DENIED);
ret = FALSE;
}
if (toReplace)
ret = ps->memStore->addCert(ps->memStore, cert, toReplace,
ppStoreContext);
else
{
ret = TRUE;
if (ps->provWriteCert)
ret = ps->provWriteCert(ps->hStoreProv, (PCCERT_CONTEXT)cert,
CERT_STORE_PROV_WRITE_ADD_FLAG);
if (ret)
if (ps->hdr.dwOpenFlags & CERT_STORE_READONLY_FLAG)
{
ret = ps->memStore->addCert(ps->memStore, cert,
dwAddDisposition, ppStoreContext);
/* dirty trick: replace the returned context's hCertStore with
* store.
*/
if (ppStoreContext)
(*(PCERT_CONTEXT *)ppStoreContext)->hCertStore = store;
SetLastError(ERROR_ACCESS_DENIED);
ret = FALSE;
}
else
{
ret = TRUE;
if (ps->provWriteCert)
ret = ps->provWriteCert(ps->hStoreProv, (PCCERT_CONTEXT)cert,
CERT_STORE_PROV_WRITE_ADD_FLAG);
if (ret)
ret = ps->memStore->addCert(ps->memStore, cert, NULL,
ppStoreContext);
}
}
/* dirty trick: replace the returned context's hCertStore with
* store.
*/
if (ppStoreContext)
(*(PCERT_CONTEXT *)ppStoreContext)->hCertStore = store;
return ret;
}
@ -2094,43 +2090,84 @@ PCCERT_CONTEXT WINAPI CertDuplicateCertificateContext(
return pCertContext;
}
static void CertContext_CopyProperties(PCCERT_CONTEXT to, PCCERT_CONTEXT from)
{
PWINE_CERT_CONTEXT_DATA toData, fromData;
toData = CertContext_GetDataContext((PWINE_CERT_CONTEXT)to);
fromData = CertContext_GetDataContext((PWINE_CERT_CONTEXT)from);
ContextPropertyList_Copy(toData->properties, fromData->properties);
}
BOOL WINAPI CertAddCertificateContextToStore(HCERTSTORE hCertStore,
PCCERT_CONTEXT pCertContext, DWORD dwAddDisposition,
PCCERT_CONTEXT *ppStoreContext)
{
PWINECRYPT_CERTSTORE store = (PWINECRYPT_CERTSTORE)hCertStore;
PWINE_CERT_CONTEXT_DATA linked = CertContext_GetDataContext(
(PWINE_CERT_CONTEXT)pCertContext);
PWINE_CERT_CONTEXT cert;
BOOL ret;
BOOL ret = TRUE;
PCCERT_CONTEXT toAdd = NULL, existing = NULL;
TRACE("(%p, %p, %08lx, %p)\n", hCertStore, pCertContext,
dwAddDisposition, ppStoreContext);
/* FIXME: some tests needed to verify return codes */
if (!store)
if (dwAddDisposition != CERT_STORE_ADD_ALWAYS)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (store->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
BYTE hashToAdd[20];
DWORD size = sizeof(hashToAdd);
ret = CRYPT_GetCertificateContextProperty(
(PWINE_CERT_CONTEXT)pCertContext, CERT_HASH_PROP_ID, hashToAdd, &size);
if (ret)
{
CRYPT_HASH_BLOB blob = { sizeof(hashToAdd), hashToAdd };
existing = CertFindCertificateInStore(hCertStore,
pCertContext->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob,
NULL);
}
}
cert = CRYPT_CreateCertificateContext(pCertContext->dwCertEncodingType,
pCertContext->pbCertEncoded, pCertContext->cbCertEncoded);
if (cert)
switch (dwAddDisposition)
{
PWINE_CERT_CONTEXT_DATA certData = CertContext_GetDataContext(cert);
ContextPropertyList_Copy(certData->properties, linked->properties);
ret = store->addCert(store, cert, dwAddDisposition, ppStoreContext);
CertFreeCertificateContext((PCCERT_CONTEXT)cert);
}
else
case CERT_STORE_ADD_ALWAYS:
toAdd = CertDuplicateCertificateContext(pCertContext);
break;
case CERT_STORE_ADD_NEW:
if (existing)
{
TRACE("found matching certificate, not adding\n");
SetLastError(CRYPT_E_EXISTS);
ret = FALSE;
}
else
toAdd = CertDuplicateCertificateContext(pCertContext);
break;
case CERT_STORE_ADD_REPLACE_EXISTING:
toAdd = CertDuplicateCertificateContext(pCertContext);
break;
case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES:
toAdd = CertDuplicateCertificateContext(pCertContext);
if (existing)
CertContext_CopyProperties(toAdd, existing);
break;
case CERT_STORE_ADD_USE_EXISTING:
if (existing)
CertContext_CopyProperties(existing, pCertContext);
break;
default:
FIXME("Unimplemented add disposition %ld\n", dwAddDisposition);
ret = FALSE;
}
if (toAdd)
{
ret = store->addCert(store, (PWINE_CERT_CONTEXT)toAdd,
(PWINE_CERT_CONTEXT)existing, ppStoreContext);
CertFreeCertificateContext(toAdd);
}
CertFreeCertificateContext(existing);
TRACE("returning %d\n", ret);
return ret;
}
@ -2155,7 +2192,8 @@ BOOL WINAPI CertAddEncodedCertificateToStore(HCERTSTORE hCertStore,
if (cert)
{
ret = hcs->addCert(hcs, cert, dwAddDisposition, ppCertContext);
ret = CertAddCertificateContextToStore(hCertStore,
(PCCERT_CONTEXT)cert, dwAddDisposition, ppCertContext);
CertFreeCertificateContext((PCCERT_CONTEXT)cert);
}
else

View File

@ -101,6 +101,8 @@ static const BYTE bigCert2[] = { 0x30, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x02, 0x06,
static const BYTE subjectName2[] = { 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06,
0x03, 0x55, 0x04, 0x03, 0x13, 0x0a, 0x41, 0x6c, 0x65, 0x78, 0x20, 0x4c, 0x61,
0x6e, 0x67, 0x00 };
static const BYTE bigCert2Hash[] = { 0x4a, 0x7f, 0x32, 0x1f, 0xcf, 0x3b, 0xc0,
0x87, 0x48, 0x2b, 0xa1, 0x86, 0x54, 0x18, 0xe4, 0x3a, 0x0e, 0x53, 0x7e, 0x2b };
static const BYTE certWithUsage[] = { 0x30, 0x81, 0x93, 0x02, 0x01, 0x01, 0x30,
0x02, 0x06, 0x00, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
0x03, 0x13, 0x0a, 0x4a, 0x75, 0x61, 0x6e, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00,
@ -115,6 +117,110 @@ static const BYTE certWithUsage[] = { 0x30, 0x81, 0x93, 0x02, 0x01, 0x01, 0x30,
0x03, 0x02, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 };
static const BYTE serialNum[] = { 1 };
static void testAddCert(void)
{
HCERTSTORE store;
store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
CERT_STORE_CREATE_NEW_FLAG, NULL);
ok(store != NULL, "CertOpenStore failed: %ld\n", GetLastError());
if (store != NULL)
{
HCERTSTORE collection;
PCCERT_CONTEXT context;
BOOL ret;
ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
bigCert, sizeof(bigCert), CERT_STORE_ADD_ALWAYS, NULL);
ok(ret, "CertAddEncodedCertificateToStore failed: %08lx\n",
GetLastError());
ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
bigCert2, sizeof(bigCert2), CERT_STORE_ADD_NEW, NULL);
ok(ret, "CertAddEncodedCertificateToStore failed: %08lx\n",
GetLastError());
/* This has the same name as bigCert, so finding isn't done by name */
ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
certWithUsage, sizeof(certWithUsage), CERT_STORE_ADD_NEW, &context);
ok(ret, "CertAddEncodedCertificateToStore failed: %08lx\n",
GetLastError());
ok(context != NULL, "Expected a context\n");
if (context)
{
CRYPT_DATA_BLOB hash = { sizeof(bigCert2Hash),
(LPBYTE)bigCert2Hash };
CertDeleteCertificateFromStore(context);
/* Set the same hash as bigCert2, and try to readd it */
ret = CertSetCertificateContextProperty(context, CERT_HASH_PROP_ID,
0, &hash);
ok(ret, "CertSetCertificateContextProperty failed: %08lx\n",
GetLastError());
ret = CertAddCertificateContextToStore(store, context,
CERT_STORE_ADD_NEW, NULL);
/* The failure is a bit odd (CRYPT_E_ASN1_BADTAG), so just check
* that it fails.
*/
ok(!ret, "Expected failure\n");
CertFreeCertificateContext(context);
}
context = CertCreateCertificateContext(X509_ASN_ENCODING, bigCert2,
sizeof(bigCert2));
ok(context != NULL, "Expected a context\n");
if (context)
{
/* Try to readd bigCert2 to the store */
ret = CertAddCertificateContextToStore(store, context,
CERT_STORE_ADD_NEW, NULL);
ok(!ret && GetLastError() == CRYPT_E_EXISTS,
"Expected CRYPT_E_EXISTS, got %08lx\n", GetLastError());
CertFreeCertificateContext(context);
}
/* FIXME: test whether adding a cert with the same subject name and
* serial number (but different otherwise) as an existing cert works.
*/
collection = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
CERT_STORE_CREATE_NEW_FLAG, NULL);
ok(collection != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
if (collection)
{
/* Add store to the collection, but disable updates */
CertAddStoreToCollection(collection, store, 0, 0);
context = CertCreateCertificateContext(X509_ASN_ENCODING, bigCert2,
sizeof(bigCert2));
ok(context != NULL, "Expected a context\n");
if (context)
{
/* Try to readd bigCert2 to the collection */
ret = CertAddCertificateContextToStore(collection, context,
CERT_STORE_ADD_NEW, NULL);
ok(!ret && GetLastError() == CRYPT_E_EXISTS,
"Expected CRYPT_E_EXISTS, got %08lx\n", GetLastError());
/* Replacing an existing certificate context is allowed, even
* though updates to the collection aren't..
*/
ret = CertAddCertificateContextToStore(collection, context,
CERT_STORE_ADD_REPLACE_EXISTING, NULL);
ok(ret, "CertAddCertificateContextToStore failed: %08lx\n",
GetLastError());
/* but adding a new certificate isn't allowed. */
ret = CertAddCertificateContextToStore(collection, context,
CERT_STORE_ADD_ALWAYS, NULL);
ok(!ret && GetLastError() ==
HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED),
"Expected HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), got %08lx\n",
GetLastError());
CertFreeCertificateContext(context);
}
CertCloseStore(collection, 0);
}
CertCloseStore(store, 0);
}
}
static void testDupCert(void)
{
HCERTSTORE store;
@ -1592,6 +1698,7 @@ static void testAddSerialized(void)
START_TEST(store)
{
testAddCert();
testDupCert();
testFindCert();
testGetSubjectCert();