crypt32: Fix certificate adding
- Implement add disposition in CertAddCertificateContextToStore,
rather than in each store.
- Add a few more tests.
diff --git a/dlls/crypt32/store.c b/dlls/crypt32/store.c
index cf896f7..f6d190c 100644
--- a/dlls/crypt32/store.c
+++ b/dlls/crypt32/store.c
@@ -126,11 +126,14 @@
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 @@
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 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 @@
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 @@
{
/* 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 @@
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 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 @@
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 @@
if (cert)
{
- ret = hcs->addCert(hcs, cert, dwAddDisposition, ppCertContext);
+ ret = CertAddCertificateContextToStore(hCertStore,
+ (PCCERT_CONTEXT)cert, dwAddDisposition, ppCertContext);
CertFreeCertificateContext((PCCERT_CONTEXT)cert);
}
else
diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c
index bc52ebf..b5dd1d6 100644
--- a/dlls/crypt32/tests/store.c
+++ b/dlls/crypt32/tests/store.c
@@ -101,6 +101,8 @@
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 @@
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 @@
START_TEST(store)
{
+ testAddCert();
testDupCert();
testFindCert();
testGetSubjectCert();