diff --git a/dlls/crypt32/Makefile.in b/dlls/crypt32/Makefile.in index 09f5d7fc6d3..5e1862e240c 100644 --- a/dlls/crypt32/Makefile.in +++ b/dlls/crypt32/Makefile.in @@ -9,6 +9,7 @@ IMPORTS = user32 advapi32 kernel32 ntdll C_SRCS = \ cert.c \ + crl.c \ context.c \ decode.c \ encode.c \ diff --git a/dlls/crypt32/crl.c b/dlls/crypt32/crl.c new file mode 100644 index 00000000000..3c60578ad7a --- /dev/null +++ b/dlls/crypt32/crl.c @@ -0,0 +1,445 @@ +/* + * Copyright 2006 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 +#include +#include "windef.h" +#include "winbase.h" +#include "wincrypt.h" +#include "wine/debug.h" +#include "crypt32_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(crypt); + +PCCRL_CONTEXT WINAPI CertCreateCRLContext(DWORD dwCertEncodingType, + const BYTE* pbCrlEncoded, DWORD cbCrlEncoded) +{ + PCRL_CONTEXT crl = NULL; + BOOL ret; + PCERT_SIGNED_CONTENT_INFO signedCrl = NULL; + PCRL_INFO crlInfo = NULL; + DWORD size = 0; + + TRACE("(%08lx, %p, %ld)\n", dwCertEncodingType, pbCrlEncoded, + cbCrlEncoded); + + if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING) + { + SetLastError(E_INVALIDARG); + return NULL; + } + /* First try to decode it as a signed crl. */ + ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CERT, pbCrlEncoded, + cbCrlEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&signedCrl, &size); + if (ret) + { + size = 0; + ret = CryptDecodeObjectEx(dwCertEncodingType, + X509_CERT_CRL_TO_BE_SIGNED, signedCrl->ToBeSigned.pbData, + signedCrl->ToBeSigned.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, + (BYTE *)&crlInfo, &size); + LocalFree(signedCrl); + } + /* Failing that, try it as an unsigned crl */ + if (!ret) + { + size = 0; + ret = CryptDecodeObjectEx(dwCertEncodingType, + X509_CERT_CRL_TO_BE_SIGNED, pbCrlEncoded, cbCrlEncoded, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, + (BYTE *)&crlInfo, &size); + } + if (ret) + { + BYTE *data = NULL; + + crl = (PCRL_CONTEXT)Context_CreateDataContext(sizeof(CRL_CONTEXT)); + if (!crl) + goto end; + data = CryptMemAlloc(cbCrlEncoded); + if (!data) + { + CryptMemFree(crl); + crl = NULL; + goto end; + } + memcpy(data, pbCrlEncoded, cbCrlEncoded); + crl->dwCertEncodingType = dwCertEncodingType; + crl->pbCrlEncoded = data; + crl->cbCrlEncoded = cbCrlEncoded; + crl->pCrlInfo = crlInfo; + crl->hCertStore = 0; + } + +end: + return (PCCRL_CONTEXT)crl; +} + +BOOL WINAPI CertAddEncodedCRLToStore(HCERTSTORE hCertStore, + DWORD dwCertEncodingType, const BYTE *pbCrlEncoded, DWORD cbCrlEncoded, + DWORD dwAddDisposition, PCCRL_CONTEXT *ppCrlContext) +{ + PCCRL_CONTEXT crl = CertCreateCRLContext(dwCertEncodingType, + pbCrlEncoded, cbCrlEncoded); + BOOL ret; + + TRACE("(%p, %08lx, %p, %ld, %08lx, %p)\n", hCertStore, dwCertEncodingType, + pbCrlEncoded, cbCrlEncoded, dwAddDisposition, ppCrlContext); + + if (crl) + { + ret = CertAddCRLContextToStore(hCertStore, crl, dwAddDisposition, + ppCrlContext); + CertFreeCRLContext(crl); + } + else + ret = FALSE; + return ret; +} + +typedef BOOL (*CrlCompareFunc)(PCCRL_CONTEXT pCrlContext, DWORD dwType, + DWORD dwFlags, const void *pvPara); + +static BOOL compare_crl_any(PCCRL_CONTEXT pCrlContext, DWORD dwType, + DWORD dwFlags, const void *pvPara) +{ + return TRUE; +} + +static BOOL compare_crl_issued_by(PCCRL_CONTEXT pCrlContext, DWORD dwType, + DWORD dwFlags, const void *pvPara) +{ + BOOL ret; + + if (pvPara) + { + PCCERT_CONTEXT issuer = (PCCERT_CONTEXT)pvPara; + + ret = CertCompareCertificateName(issuer->dwCertEncodingType, + &issuer->pCertInfo->Issuer, &pCrlContext->pCrlInfo->Issuer); + } + else + ret = TRUE; + return ret; +} + +static BOOL compare_crl_existing(PCCRL_CONTEXT pCrlContext, DWORD dwType, + DWORD dwFlags, const void *pvPara) +{ + BOOL ret; + + if (pvPara) + { + PCCRL_CONTEXT crl = (PCCRL_CONTEXT)pvPara; + + ret = CertCompareCertificateName(pCrlContext->dwCertEncodingType, + &pCrlContext->pCrlInfo->Issuer, &crl->pCrlInfo->Issuer); + } + else + ret = TRUE; + return ret; +} + +PCCRL_CONTEXT WINAPI CertFindCRLInStore(HCERTSTORE hCertStore, + DWORD dwCertEncodingType, DWORD dwFindFlags, DWORD dwFindType, + const void *pvFindPara, PCCRL_CONTEXT pPrevCrlContext) +{ + PCCRL_CONTEXT ret; + CrlCompareFunc compare; + + TRACE("(%p, %ld, %ld, %ld, %p, %p)\n", hCertStore, dwCertEncodingType, + dwFindFlags, dwFindType, pvFindPara, pPrevCrlContext); + + switch (dwFindType) + { + case CRL_FIND_ANY: + compare = compare_crl_any; + break; + case CRL_FIND_ISSUED_BY: + compare = compare_crl_issued_by; + break; + case CRL_FIND_EXISTING: + compare = compare_crl_existing; + break; + default: + FIXME("find type %08lx unimplemented\n", dwFindType); + compare = NULL; + } + + if (compare) + { + BOOL matches = FALSE; + + ret = pPrevCrlContext; + do { + ret = CertEnumCRLsInStore(hCertStore, ret); + if (ret) + matches = compare(ret, dwFindType, dwFindFlags, pvFindPara); + } while (ret != NULL && !matches); + if (!ret) + SetLastError(CRYPT_E_NOT_FOUND); + } + else + { + SetLastError(CRYPT_E_NOT_FOUND); + ret = NULL; + } + return ret; +} + +PCCRL_CONTEXT WINAPI CertDuplicateCRLContext(PCCRL_CONTEXT pCrlContext) +{ + TRACE("(%p)\n", pCrlContext); + Context_AddRef((void *)pCrlContext, sizeof(CRL_CONTEXT)); + return pCrlContext; +} + +static void CrlDataContext_Free(void *context) +{ + PCRL_CONTEXT crlContext = (PCRL_CONTEXT)context; + + CryptMemFree(crlContext->pbCrlEncoded); + LocalFree(crlContext->pCrlInfo); +} + +BOOL WINAPI CertFreeCRLContext( PCCRL_CONTEXT pCrlContext) +{ + TRACE("(%p)\n", pCrlContext); + + if (pCrlContext) + Context_Release((void *)pCrlContext, sizeof(CRL_CONTEXT), + CrlDataContext_Free); + return TRUE; +} + +DWORD WINAPI CertEnumCRLContextProperties(PCCRL_CONTEXT pCRLContext, + DWORD dwPropId) +{ + PCONTEXT_PROPERTY_LIST properties = Context_GetProperties( + (void *)pCRLContext, sizeof(CRL_CONTEXT)); + DWORD ret; + + TRACE("(%p, %ld)\n", pCRLContext, dwPropId); + + if (properties) + ret = ContextPropertyList_EnumPropIDs(properties, dwPropId); + else + ret = 0; + return ret; +} + +static BOOL WINAPI CRLContext_SetProperty(void *context, DWORD dwPropId, + DWORD dwFlags, const void *pvData); + +static BOOL CRLContext_GetHashProp(void *context, DWORD dwPropId, + ALG_ID algID, const BYTE *toHash, DWORD toHashLen, void *pvData, + DWORD *pcbData) +{ + BOOL ret = CryptHashCertificate(0, algID, 0, toHash, toHashLen, pvData, + pcbData); + if (ret) + { + CRYPT_DATA_BLOB blob = { *pcbData, pvData }; + + ret = CRLContext_SetProperty(context, dwPropId, 0, &blob); + } + return ret; +} + +static BOOL WINAPI CRLContext_GetProperty(void *context, DWORD dwPropId, + void *pvData, DWORD *pcbData) +{ + PCCRL_CONTEXT pCRLContext = (PCCRL_CONTEXT)context; + PCONTEXT_PROPERTY_LIST properties = + Context_GetProperties(context, sizeof(CRL_CONTEXT)); + BOOL ret; + CRYPT_DATA_BLOB blob; + + TRACE("(%p, %ld, %p, %p)\n", context, dwPropId, pvData, pcbData); + + if (properties) + ret = ContextPropertyList_FindProperty(properties, dwPropId, &blob); + else + ret = FALSE; + if (ret) + { + if (!pvData) + { + *pcbData = blob.cbData; + ret = TRUE; + } + else if (*pcbData < blob.cbData) + { + SetLastError(ERROR_MORE_DATA); + *pcbData = blob.cbData; + } + else + { + memcpy(pvData, blob.pbData, blob.cbData); + *pcbData = blob.cbData; + ret = TRUE; + } + } + else + { + /* Implicit properties */ + switch (dwPropId) + { + case CERT_SHA1_HASH_PROP_ID: + ret = CRLContext_GetHashProp(context, dwPropId, CALG_SHA1, + pCRLContext->pbCrlEncoded, pCRLContext->cbCrlEncoded, pvData, + pcbData); + break; + case CERT_MD5_HASH_PROP_ID: + ret = CRLContext_GetHashProp(context, dwPropId, CALG_MD5, + pCRLContext->pbCrlEncoded, pCRLContext->cbCrlEncoded, pvData, + pcbData); + break; + default: + SetLastError(CRYPT_E_NOT_FOUND); + } + } + TRACE("returning %d\n", ret); + return ret; +} + +BOOL WINAPI CertGetCRLContextProperty(PCCRL_CONTEXT pCRLContext, + DWORD dwPropId, void *pvData, DWORD *pcbData) +{ + BOOL ret; + + TRACE("(%p, %ld, %p, %p)\n", pCRLContext, dwPropId, pvData, pcbData); + + switch (dwPropId) + { + case 0: + case CERT_CERT_PROP_ID: + case CERT_CRL_PROP_ID: + case CERT_CTL_PROP_ID: + SetLastError(E_INVALIDARG); + ret = FALSE; + break; + case CERT_ACCESS_STATE_PROP_ID: + if (!pvData) + { + *pcbData = sizeof(DWORD); + ret = TRUE; + } + else if (*pcbData < sizeof(DWORD)) + { + SetLastError(ERROR_MORE_DATA); + *pcbData = sizeof(DWORD); + ret = FALSE; + } + else + { + *(DWORD *)pvData = + CertStore_GetAccessState(pCRLContext->hCertStore); + ret = TRUE; + } + break; + default: + ret = CRLContext_GetProperty((void *)pCRLContext, dwPropId, pvData, + pcbData); + } + return ret; +} + +static BOOL WINAPI CRLContext_SetProperty(void *context, DWORD dwPropId, + DWORD dwFlags, const void *pvData) +{ + PCONTEXT_PROPERTY_LIST properties = + Context_GetProperties(context, sizeof(CERT_CONTEXT)); + BOOL ret; + + TRACE("(%p, %ld, %08lx, %p)\n", context, dwPropId, dwFlags, pvData); + + if (!properties) + ret = FALSE; + else if (!pvData) + { + ContextPropertyList_RemoveProperty(properties, dwPropId); + ret = TRUE; + } + else + { + switch (dwPropId) + { + case CERT_AUTO_ENROLL_PROP_ID: + case CERT_CTL_USAGE_PROP_ID: /* same as CERT_ENHKEY_USAGE_PROP_ID */ + case CERT_DESCRIPTION_PROP_ID: + case CERT_FRIENDLY_NAME_PROP_ID: + case CERT_HASH_PROP_ID: + case CERT_KEY_IDENTIFIER_PROP_ID: + case CERT_MD5_HASH_PROP_ID: + case CERT_NEXT_UPDATE_LOCATION_PROP_ID: + case CERT_PUBKEY_ALG_PARA_PROP_ID: + case CERT_PVK_FILE_PROP_ID: + case CERT_SIGNATURE_HASH_PROP_ID: + case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID: + case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID: + case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID: + case CERT_ENROLLMENT_PROP_ID: + case CERT_CROSS_CERT_DIST_POINTS_PROP_ID: + case CERT_RENEWAL_PROP_ID: + { + PCRYPT_DATA_BLOB blob = (PCRYPT_DATA_BLOB)pvData; + + ret = ContextPropertyList_SetProperty(properties, dwPropId, + blob->pbData, blob->cbData); + break; + } + case CERT_DATE_STAMP_PROP_ID: + ret = ContextPropertyList_SetProperty(properties, dwPropId, + (LPBYTE)pvData, sizeof(FILETIME)); + break; + default: + FIXME("%ld: stub\n", dwPropId); + ret = FALSE; + } + } + TRACE("returning %d\n", ret); + return ret; +} + +BOOL WINAPI CertSetCRLContextProperty(PCCRL_CONTEXT pCRLContext, + DWORD dwPropId, DWORD dwFlags, const void *pvData) +{ + BOOL ret; + + TRACE("(%p, %ld, %08lx, %p)\n", pCRLContext, dwPropId, dwFlags, pvData); + + /* Handle special cases for "read-only"/invalid prop IDs. Windows just + * crashes on most of these, I'll be safer. + */ + switch (dwPropId) + { + case 0: + case CERT_ACCESS_STATE_PROP_ID: + case CERT_CERT_PROP_ID: + case CERT_CRL_PROP_ID: + case CERT_CTL_PROP_ID: + SetLastError(E_INVALIDARG); + return FALSE; + } + ret = CRLContext_SetProperty((void *)pCRLContext, dwPropId, dwFlags, + pvData); + TRACE("returning %d\n", ret); + return ret; +} diff --git a/dlls/crypt32/crypt32.spec b/dlls/crypt32/crypt32.spec index 4692e54d50b..88dba5b91a1 100644 --- a/dlls/crypt32/crypt32.spec +++ b/dlls/crypt32/crypt32.spec @@ -28,13 +28,14 @@ @ stdcall CertDuplicateCTLContext(ptr) @ stdcall CertDuplicateCertificateContext(ptr) @ stdcall CertDuplicateStore(ptr) -@ stub CertEnumCRLContextProperties +@ stdcall CertEnumCRLContextProperties(ptr long) @ stdcall CertEnumCRLsInStore(ptr ptr) @ stub CertEnumCTLContextProperties @ stdcall CertEnumCTLsInStore(ptr ptr) @ stdcall CertEnumCertificateContextProperties(ptr long) @ stdcall CertEnumCertificatesInStore(long ptr) @ stdcall CertFindAttribute(str long ptr) +@ stdcall CertFindCRLInStore(long long long long ptr ptr) @ stub CertFindCTLInStore @ stdcall CertFindCertificateInStore(long long long long ptr ptr) @ stdcall CertFindExtension(str long ptr) diff --git a/dlls/crypt32/store.c b/dlls/crypt32/store.c index e5c5de8c61f..774d2b386d2 100644 --- a/dlls/crypt32/store.c +++ b/dlls/crypt32/store.c @@ -17,9 +17,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * FIXME: - * - As you can see in the stubs below, support for CRLs and CTLs is missing. - * Mostly this should be copy-paste work, and some code (e.g. extended - * properties) could be shared between them. * - The concept of physical stores and locations isn't implemented. (This * doesn't mean registry stores et al aren't implemented. See the PSDK for * registering and enumerating physical stores and locations.) @@ -134,6 +131,7 @@ typedef struct WINE_CRYPTCERTSTORE CertStoreType type; PFN_CERT_STORE_PROV_CLOSE closeStore; CONTEXT_STORE certs; + CONTEXT_STORE crls; PFN_CERT_STORE_PROV_CONTROL control; /* optional */ } WINECRYPT_CERTSTORE, *PWINECRYPT_CERTSTORE; @@ -141,6 +139,7 @@ typedef struct _WINE_MEMSTORE { WINECRYPT_CERTSTORE hdr; struct ContextList *certs; + struct ContextList *crls; } WINE_MEMSTORE, *PWINE_MEMSTORE; typedef struct _WINE_HASH_TO_DELETE @@ -158,6 +157,7 @@ typedef struct _WINE_REGSTOREINFO BOOL dirty; CRITICAL_SECTION cs; struct list certsToDelete; + struct list crlsToDelete; } WINE_REGSTOREINFO, *PWINE_REGSTOREINFO; typedef struct _WINE_STORE_LIST_ENTRY @@ -184,6 +184,8 @@ typedef struct _WINE_PROVIDERSTORE PFN_CERT_STORE_PROV_CLOSE provCloseStore; PFN_CERT_STORE_PROV_WRITE_CERT provWriteCert; PFN_CERT_STORE_PROV_DELETE_CERT provDeleteCert; + PFN_CERT_STORE_PROV_WRITE_CRL provWriteCrl; + PFN_CERT_STORE_PROV_DELETE_CRL provDeleteCrl; PFN_CERT_STORE_PROV_CONTROL provControl; } WINE_PROVIDERSTORE, *PWINE_PROVIDERSTORE; @@ -244,9 +246,52 @@ static BOOL CRYPT_MemDeleteCert(PWINECRYPT_CERTSTORE store, void *pCertContext) return TRUE; } +static BOOL CRYPT_MemAddCrl(PWINECRYPT_CERTSTORE store, void *crl, + void *toReplace, const void **ppStoreContext) +{ + WINE_MEMSTORE *ms = (WINE_MEMSTORE *)store; + PCRL_CONTEXT context; + + TRACE("(%p, %p, %p, %p)\n", store, crl, toReplace, ppStoreContext); + + context = (PCRL_CONTEXT)ContextList_Add(ms->crls, crl, toReplace); + if (context) + { + context->hCertStore = store; + if (ppStoreContext) + *ppStoreContext = + CertDuplicateCRLContext(context); + } + return context ? TRUE : FALSE; +} + +static void *CRYPT_MemEnumCrl(PWINECRYPT_CERTSTORE store, void *pPrev) +{ + WINE_MEMSTORE *ms = (WINE_MEMSTORE *)store; + void *ret; + + TRACE("(%p, %p)\n", store, pPrev); + + ret = ContextList_Enum(ms->crls, pPrev); + if (!ret) + SetLastError(CRYPT_E_NOT_FOUND); + + TRACE("returning %p\n", ret); + return ret; +} + +static BOOL CRYPT_MemDeleteCrl(PWINECRYPT_CERTSTORE store, void *pCrlContext) +{ + WINE_MEMSTORE *ms = (WINE_MEMSTORE *)store; + + ContextList_Delete(ms->crls, pCrlContext); + return TRUE; +} + static void CRYPT_MemEmptyStore(PWINE_MEMSTORE store) { ContextList_Empty(store->certs); + ContextList_Empty(store->crls); } static void WINAPI CRYPT_MemCloseStore(HCERTSTORE hCertStore, DWORD dwFlags) @@ -258,6 +303,7 @@ static void WINAPI CRYPT_MemCloseStore(HCERTSTORE hCertStore, DWORD dwFlags) FIXME("Unimplemented flags: %08lx\n", dwFlags); ContextList_Free(store->certs); + ContextList_Free(store->crls); CryptMemFree(store); } @@ -284,9 +330,14 @@ static WINECRYPT_CERTSTORE *CRYPT_MemOpenStore(HCRYPTPROV hCryptProv, store->hdr.certs.addContext = CRYPT_MemAddCert; store->hdr.certs.enumContext = CRYPT_MemEnumCert; store->hdr.certs.deleteContext = CRYPT_MemDeleteCert; + store->hdr.crls.addContext = CRYPT_MemAddCrl; + store->hdr.crls.enumContext = CRYPT_MemEnumCrl; + store->hdr.crls.deleteContext = CRYPT_MemDeleteCrl; store->hdr.control = NULL; store->certs = ContextList_Create(pCertInterface, sizeof(CERT_CONTEXT)); + store->crls = ContextList_Create(pCRLInterface, + sizeof(CRL_CONTEXT)); } } return (PWINECRYPT_CERTSTORE)store; @@ -375,31 +426,6 @@ static BOOL CRYPT_CollectionAddContext(PWINE_COLLECTIONSTORE store, 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((PCCERT_CONTEXT)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 @@ -452,6 +478,31 @@ static void *CRYPT_CollectionAdvanceEnum(PWINE_COLLECTIONSTORE store, 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((PCCERT_CONTEXT)childContext); + return ret; +} + static void *CRYPT_CollectionEnumCert(PWINECRYPT_CERTSTORE store, void *pPrev) { PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store; @@ -506,6 +557,85 @@ static BOOL CRYPT_CollectionDeleteCert(PWINECRYPT_CERTSTORE store, 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((PCCRL_CONTEXT)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(CERT_CONTEXT)); + + ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, + offsetof(WINECRYPT_CERTSTORE, 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, + offsetof(WINECRYPT_CERTSTORE, 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; + + TRACE("(%p, %p)\n", store, pCrlContext); + + ret = CertDeleteCRLFromStore((PCCRL_CONTEXT) + Context_GetLinkedContext(pCrlContext, sizeof(CRL_CONTEXT))); + return ret; +} + static WINECRYPT_CERTSTORE *CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags, const void *pvPara) { @@ -528,6 +658,9 @@ static WINECRYPT_CERTSTORE *CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv, 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; InitializeCriticalSection(&store->cs); list_init(&store->stores); } @@ -601,8 +734,7 @@ static void *CRYPT_ProvEnumCert(PWINECRYPT_CERTSTORE store, void *pPrev) return ret; } -static BOOL CRYPT_ProvDeleteCert(PWINECRYPT_CERTSTORE store, - void *cert) +static BOOL CRYPT_ProvDeleteCert(PWINECRYPT_CERTSTORE store, void *cert) { PWINE_PROVIDERSTORE ps = (PWINE_PROVIDERSTORE)store; BOOL ret = TRUE; @@ -616,6 +748,73 @@ static BOOL CRYPT_ProvDeleteCert(PWINECRYPT_CERTSTORE store, return ret; } +static BOOL CRYPT_ProvAddCRL(PWINECRYPT_CERTSTORE store, void *crl, + void *toReplace, const void **ppStoreContext) +{ + PWINE_PROVIDERSTORE ps = (PWINE_PROVIDERSTORE)store; + BOOL ret; + + TRACE("(%p, %p, %p, %p)\n", store, crl, toReplace, ppStoreContext); + + if (toReplace) + ret = ps->memStore->crls.addContext(ps->memStore, crl, toReplace, + (const void **)ppStoreContext); + else + { + if (ps->hdr.dwOpenFlags & CERT_STORE_READONLY_FLAG) + { + SetLastError(ERROR_ACCESS_DENIED); + ret = FALSE; + } + else + { + ret = TRUE; + if (ps->provWriteCrl) + ret = ps->provWriteCrl(ps->hStoreProv, (PCCRL_CONTEXT)crl, + CERT_STORE_PROV_WRITE_ADD_FLAG); + if (ret) + ret = ps->memStore->crls.addContext(ps->memStore, crl, NULL, + (const void **)ppStoreContext); + } + } + /* dirty trick: replace the returned context's hCertStore with + * store. + */ + if (ppStoreContext) + (*(PCRL_CONTEXT *)ppStoreContext)->hCertStore = store; + return ret; +} + +static void *CRYPT_ProvEnumCRL(PWINECRYPT_CERTSTORE store, void *pPrev) +{ + PWINE_PROVIDERSTORE ps = (PWINE_PROVIDERSTORE)store; + void *ret; + + ret = ps->memStore->crls.enumContext(ps->memStore, pPrev); + if (ret) + { + /* same dirty trick: replace the returned context's hCertStore with + * store. + */ + ((PCERT_CONTEXT)ret)->hCertStore = store; + } + return ret; +} + +static BOOL CRYPT_ProvDeleteCRL(PWINECRYPT_CERTSTORE store, void *crl) +{ + PWINE_PROVIDERSTORE ps = (PWINE_PROVIDERSTORE)store; + BOOL ret = TRUE; + + TRACE("(%p, %p)\n", store, crl); + + if (ps->provDeleteCrl) + ret = ps->provDeleteCrl(ps->hStoreProv, crl, 0); + if (ret) + ret = ps->memStore->crls.deleteContext(ps->memStore, crl); + return ret; +} + static BOOL WINAPI CRYPT_ProvControl(HCERTSTORE hCertStore, DWORD dwFlags, DWORD dwCtrlType, void const *pvCtrlPara) { @@ -654,6 +853,9 @@ static PWINECRYPT_CERTSTORE CRYPT_ProvCreateStore(HCRYPTPROV hCryptProv, ret->hdr.certs.addContext = CRYPT_ProvAddCert; ret->hdr.certs.enumContext = CRYPT_ProvEnumCert; ret->hdr.certs.deleteContext = CRYPT_ProvDeleteCert; + ret->hdr.crls.addContext = CRYPT_ProvAddCRL; + ret->hdr.crls.enumContext = CRYPT_ProvEnumCRL; + ret->hdr.crls.deleteContext = CRYPT_ProvDeleteCRL; ret->hdr.control = CRYPT_ProvControl; if (pProvInfo->cStoreProvFunc > CERT_STORE_PROV_CLOSE_FUNC) ret->provCloseStore = @@ -672,6 +874,18 @@ static PWINECRYPT_CERTSTORE CRYPT_ProvCreateStore(HCRYPTPROV hCryptProv, CERT_STORE_PROV_DELETE_CERT_FUNC]; else ret->provDeleteCert = NULL; + if (pProvInfo->cStoreProvFunc > + CERT_STORE_PROV_WRITE_CRL_FUNC) + ret->provWriteCrl = pProvInfo->rgpvStoreProvFunc[ + CERT_STORE_PROV_WRITE_CRL_FUNC]; + else + ret->provWriteCert = NULL; + if (pProvInfo->cStoreProvFunc > + CERT_STORE_PROV_DELETE_CRL_FUNC) + ret->provDeleteCrl = pProvInfo->rgpvStoreProvFunc[ + CERT_STORE_PROV_DELETE_CRL_FUNC]; + else + ret->provDeleteCert = NULL; if (pProvInfo->cStoreProvFunc > CERT_STORE_PROV_CONTROL_FUNC) ret->provControl = pProvInfo->rgpvStoreProvFunc[ @@ -930,7 +1144,8 @@ static BOOL CRYPT_RegWriteToReg(PWINE_REGSTOREINFO store) static const WCHAR *subKeys[] = { CertsW, CRLsW, CTLsW }; static const WINE_CONTEXT_INTERFACE *interfaces[] = { &gCertInterface, &gCRLInterface, &gCTLInterface }; - struct list *listToDelete[] = { &store->certsToDelete, NULL, NULL }; + struct list *listToDelete[] = { &store->certsToDelete, &store->crlsToDelete, + NULL }; BOOL ret = TRUE; DWORD i; @@ -1071,6 +1286,68 @@ static BOOL WINAPI CRYPT_RegDeleteCert(HCERTSTORE hCertStore, return ret; } +static BOOL WINAPI CRYPT_RegWriteCRL(HCERTSTORE hCertStore, + PCCRL_CONTEXT crl, DWORD dwFlags) +{ + PWINE_REGSTOREINFO store = (PWINE_REGSTOREINFO)hCertStore; + BOOL ret; + + TRACE("(%p, %p, %ld)\n", hCertStore, crl, dwFlags); + + if (dwFlags & CERT_STORE_PROV_WRITE_ADD_FLAG) + { + store->dirty = TRUE; + ret = TRUE; + } + else + ret = FALSE; + return ret; +} + +static BOOL WINAPI CRYPT_RegDeleteCRL(HCERTSTORE hCertStore, + PCCRL_CONTEXT pCrlContext, DWORD dwFlags) +{ + PWINE_REGSTOREINFO store = (PWINE_REGSTOREINFO)hCertStore; + BOOL ret; + + TRACE("(%p, %p, %08lx)\n", store, pCrlContext, dwFlags); + + if (store->dwOpenFlags & CERT_STORE_READONLY_FLAG) + { + SetLastError(ERROR_ACCESS_DENIED); + ret = FALSE; + } + else + { + PWINE_HASH_TO_DELETE toDelete = + CryptMemAlloc(sizeof(WINE_HASH_TO_DELETE)); + + if (toDelete) + { + DWORD size = sizeof(toDelete->hash); + + ret = CertGetCRLContextProperty(pCrlContext, CERT_HASH_PROP_ID, + toDelete->hash, &size); + if (ret) + { + EnterCriticalSection(&store->cs); + list_add_tail(&store->crlsToDelete, &toDelete->entry); + LeaveCriticalSection(&store->cs); + } + else + { + CryptMemFree(toDelete); + ret = FALSE; + } + } + else + ret = FALSE; + if (ret) + store->dirty = TRUE; + } + return ret; +} + static BOOL WINAPI CRYPT_RegControl(HCERTSTORE hCertStore, DWORD dwFlags, DWORD dwCtrlType, void const *pvCtrlPara) { @@ -1159,8 +1436,8 @@ static void *regProvFuncs[] = { CRYPT_RegDeleteCert, NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */ NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */ - NULL, /* CERT_STORE_PROV_WRITE_CRL_FUNC */ - NULL, /* CERT_STORE_PROV_DELETE_CRL_FUNC */ + CRYPT_RegWriteCRL, + CRYPT_RegDeleteCRL, NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */ NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */ NULL, /* CERT_STORE_PROV_WRITE_CTL_FUNC */ @@ -1215,6 +1492,7 @@ static WINECRYPT_CERTSTORE *CRYPT_RegOpenStore(HCRYPTPROV hCryptProv, regInfo->key = key; InitializeCriticalSection(®Info->cs); list_init(®Info->certsToDelete); + list_init(®Info->crlsToDelete); CRYPT_RegReadFromReg(regInfo); regInfo->dirty = FALSE; provInfo.cbSize = sizeof(provInfo); @@ -1563,35 +1841,6 @@ BOOL WINAPI CertSaveStore(HCERTSTORE hCertStore, DWORD dwMsgAndCertEncodingType, return TRUE; } -PCCRL_CONTEXT WINAPI CertCreateCRLContext( DWORD dwCertEncodingType, - const BYTE* pbCrlEncoded, DWORD cbCrlEncoded) -{ - PCRL_CONTEXT pcrl; - BYTE* data; - - TRACE("%08lx %p %08lx\n", dwCertEncodingType, pbCrlEncoded, cbCrlEncoded); - - /* FIXME: semi-stub, need to use CryptDecodeObjectEx to decode the CRL. */ - pcrl = CryptMemAlloc( sizeof (CRL_CONTEXT) ); - if( !pcrl ) - return NULL; - - data = CryptMemAlloc( cbCrlEncoded ); - if( !data ) - { - CryptMemFree( pcrl ); - return NULL; - } - - pcrl->dwCertEncodingType = dwCertEncodingType; - pcrl->pbCrlEncoded = data; - pcrl->cbCrlEncoded = cbCrlEncoded; - pcrl->pCrlInfo = NULL; - pcrl->hCertStore = 0; - - return pcrl; -} - DWORD CertStore_GetAccessState(HCERTSTORE hCertStore) { DWORD state = 0; @@ -1740,49 +1989,144 @@ BOOL WINAPI CertDeleteCertificateFromStore(PCCERT_CONTEXT pCertContext) return ret; } -BOOL WINAPI CertAddEncodedCRLToStore(HCERTSTORE hCertStore, - DWORD dwCertEncodingType, const BYTE *pbCrlEncoded, DWORD cbCrlEncoded, - DWORD dwAddDisposition, PCCRL_CONTEXT *ppCrlContext) +static void CrlContext_CopyProperties(PCCRL_CONTEXT to, PCCRL_CONTEXT from) { - FIXME("(%p, %08lx, %p, %ld, %08lx, %p): stub\n", hCertStore, - dwCertEncodingType, pbCrlEncoded, cbCrlEncoded, dwAddDisposition, - ppCrlContext); - return FALSE; + PCONTEXT_PROPERTY_LIST toProperties, fromProperties; + + toProperties = Context_GetProperties((void *)to, sizeof(CRL_CONTEXT)); + fromProperties = Context_GetProperties((void *)from, sizeof(CRL_CONTEXT)); + ContextPropertyList_Copy(toProperties, fromProperties); } -BOOL WINAPI CertAddCRLContextToStore( HCERTSTORE hCertStore, - PCCRL_CONTEXT pCrlContext, DWORD dwAddDisposition, - PCCRL_CONTEXT* ppStoreContext ) +BOOL WINAPI CertAddCRLContextToStore(HCERTSTORE hCertStore, + PCCRL_CONTEXT pCrlContext, DWORD dwAddDisposition, + PCCRL_CONTEXT* ppStoreContext) { - FIXME("%p %p %08lx %p\n", hCertStore, pCrlContext, - dwAddDisposition, ppStoreContext); - return TRUE; -} + PWINECRYPT_CERTSTORE store = (PWINECRYPT_CERTSTORE)hCertStore; + BOOL ret = TRUE; + PCCRL_CONTEXT toAdd = NULL, existing = NULL; -PCCRL_CONTEXT WINAPI CertDuplicateCRLContext(PCCRL_CONTEXT pCrlContext) -{ - FIXME("(%p): stub\n", pCrlContext); - return pCrlContext; -} + TRACE("(%p, %p, %08lx, %p)\n", hCertStore, pCrlContext, + dwAddDisposition, ppStoreContext); -BOOL WINAPI CertFreeCRLContext( PCCRL_CONTEXT pCrlContext) -{ - FIXME("%p\n", pCrlContext ); + /* Weird case to pass a test */ + if (dwAddDisposition == 0) + { + SetLastError(STATUS_ACCESS_VIOLATION); + return FALSE; + } + if (dwAddDisposition != CERT_STORE_ADD_ALWAYS) + { + existing = CertFindCRLInStore(hCertStore, 0, 0, CRL_FIND_EXISTING, + pCrlContext, NULL); + } - return TRUE; + switch (dwAddDisposition) + { + case CERT_STORE_ADD_ALWAYS: + toAdd = CertDuplicateCRLContext(pCrlContext); + break; + case CERT_STORE_ADD_NEW: + if (existing) + { + TRACE("found matching CRL, not adding\n"); + SetLastError(CRYPT_E_EXISTS); + ret = FALSE; + } + else + toAdd = CertDuplicateCRLContext(pCrlContext); + break; + case CERT_STORE_ADD_NEWER: + if (existing) + { + LONG newer = CompareFileTime(&existing->pCrlInfo->ThisUpdate, + &pCrlContext->pCrlInfo->ThisUpdate); + + if (newer < 0) + toAdd = CertDuplicateCRLContext(pCrlContext); + else + { + TRACE("existing CRL is newer, not adding\n"); + SetLastError(CRYPT_E_EXISTS); + ret = FALSE; + } + } + else + toAdd = CertDuplicateCRLContext(pCrlContext); + break; + case CERT_STORE_ADD_REPLACE_EXISTING: + toAdd = CertDuplicateCRLContext(pCrlContext); + break; + case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES: + toAdd = CertDuplicateCRLContext(pCrlContext); + if (existing) + CrlContext_CopyProperties(toAdd, existing); + break; + case CERT_STORE_ADD_USE_EXISTING: + if (existing) + CrlContext_CopyProperties(existing, pCrlContext); + break; + default: + FIXME("Unimplemented add disposition %ld\n", dwAddDisposition); + ret = FALSE; + } + + if (toAdd) + { + if (store) + ret = store->crls.addContext(store, (void *)toAdd, + (void *)existing, (const void **)ppStoreContext); + else if (ppStoreContext) + *ppStoreContext = CertDuplicateCRLContext(toAdd); + CertFreeCRLContext(toAdd); + } + CertFreeCRLContext(existing); + + TRACE("returning %d\n", ret); + return ret; } BOOL WINAPI CertDeleteCRLFromStore(PCCRL_CONTEXT pCrlContext) { - FIXME("(%p): stub\n", pCrlContext); - return TRUE; + BOOL ret; + + TRACE("(%p)\n", pCrlContext); + + if (!pCrlContext) + ret = TRUE; + else if (!pCrlContext->hCertStore) + { + ret = TRUE; + CertFreeCRLContext(pCrlContext); + } + else + { + PWINECRYPT_CERTSTORE hcs = + (PWINECRYPT_CERTSTORE)pCrlContext->hCertStore; + + if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) + ret = FALSE; + else + ret = hcs->crls.deleteContext(hcs, (void *)pCrlContext); + CertFreeCRLContext(pCrlContext); + } + return ret; } PCCRL_CONTEXT WINAPI CertEnumCRLsInStore(HCERTSTORE hCertStore, PCCRL_CONTEXT pPrev) { - FIXME("(%p, %p): stub\n", hCertStore, pPrev); - return NULL; + WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *)hCertStore; + PCCRL_CONTEXT ret; + + TRACE("(%p, %p)\n", hCertStore, pPrev); + if (!hCertStore) + ret = NULL; + else if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) + ret = NULL; + else + ret = (PCCRL_CONTEXT)hcs->crls.enumContext(hcs, (void *)pPrev); + return ret; } PCCTL_CONTEXT WINAPI CertCreateCTLContext(DWORD dwCertEncodingType, @@ -1896,21 +2240,6 @@ BOOL WINAPI CertControlStore(HCERTSTORE hCertStore, DWORD dwFlags, return ret; } -BOOL WINAPI CertGetCRLContextProperty(PCCRL_CONTEXT pCRLContext, - DWORD dwPropId, void *pvData, DWORD *pcbData) -{ - FIXME("(%p, %ld, %p, %p): stub\n", pCRLContext, dwPropId, pvData, pcbData); - return FALSE; -} - -BOOL WINAPI CertSetCRLContextProperty(PCCRL_CONTEXT pCRLContext, - DWORD dwPropId, DWORD dwFlags, const void *pvData) -{ - FIXME("(%p, %ld, %08lx, %p): stub\n", pCRLContext, dwPropId, dwFlags, - pvData); - return FALSE; -} - BOOL WINAPI CertGetCTLContextProperty(PCCTL_CONTEXT pCTLContext, DWORD dwPropId, void *pvData, DWORD *pcbData) { diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c index 78b05302269..b63ac8c5561 100644 --- a/dlls/crypt32/tests/store.c +++ b/dlls/crypt32/tests/store.c @@ -1884,6 +1884,312 @@ static void testAddSerialized(void) CertCloseStore(store, 0); } +static const BYTE CRL[] = { 0x30, 0x2c, 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, 0x18, 0x0f, 0x31, + 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x5a }; +static const BYTE newerCRL[] = { 0x30, 0x2a, 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, 0x17, 0x0d, 0x30, 0x36, + 0x30, 0x35, 0x31, 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a }; + +static void testCreateCRL(void) +{ + PCCRL_CONTEXT context; + + context = CertCreateCRLContext(0, NULL, 0); + ok(!context && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08lx\n", GetLastError()); + context = CertCreateCRLContext(X509_ASN_ENCODING, NULL, 0); + ok(!context && GetLastError() == CRYPT_E_ASN1_EOD, + "Expected CRYPT_E_ASN1_EOD, got %08lx\n", GetLastError()); + context = CertCreateCRLContext(X509_ASN_ENCODING, bigCert, sizeof(bigCert)); + ok(!context && GetLastError() == CRYPT_E_ASN1_CORRUPT, + "Expected CRYPT_E_ASN1_CORRUPT, got %08lx\n", GetLastError()); + context = CertCreateCRLContext(X509_ASN_ENCODING, signedCRL, + sizeof(signedCRL) - 1); + ok(!context && (GetLastError() == CRYPT_E_ASN1_EOD || + GetLastError() == CRYPT_E_ASN1_CORRUPT), + "Expected CRYPT_E_ASN1_EOD or CRYPT_E_ASN1_CORRUPT, got %08lx\n", + GetLastError()); + context = CertCreateCRLContext(X509_ASN_ENCODING, signedCRL, + sizeof(signedCRL)); + ok(context != NULL, "CertCreateCRLContext failed: %08lx\n", GetLastError()); + if (context) + CertFreeCRLContext(context); + context = CertCreateCRLContext(X509_ASN_ENCODING, CRL, sizeof(CRL)); + ok(context != NULL, "CertCreateCRLContext failed: %08lx\n", GetLastError()); + if (context) + CertFreeCRLContext(context); +} + +static void testAddCRL(void) +{ + HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, + CERT_STORE_CREATE_NEW_FLAG, NULL); + PCCRL_CONTEXT context; + BOOL ret; + + if (!store) return; + + /* Bad CRL encoding type */ + ret = CertAddEncodedCRLToStore(0, 0, NULL, 0, 0, NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08lx\n", GetLastError()); + ret = CertAddEncodedCRLToStore(store, 0, NULL, 0, 0, NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08lx\n", GetLastError()); + ret = CertAddEncodedCRLToStore(0, 0, signedCRL, sizeof(signedCRL), 0, NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08lx\n", GetLastError()); + ret = CertAddEncodedCRLToStore(store, 0, signedCRL, sizeof(signedCRL), 0, + NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08lx\n", GetLastError()); + ret = CertAddEncodedCRLToStore(0, 0, signedCRL, sizeof(signedCRL), + CERT_STORE_ADD_ALWAYS, NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08lx\n", GetLastError()); + ret = CertAddEncodedCRLToStore(store, 0, signedCRL, sizeof(signedCRL), + CERT_STORE_ADD_ALWAYS, NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08lx\n", GetLastError()); + + /* No CRL */ + ret = CertAddEncodedCRLToStore(0, X509_ASN_ENCODING, NULL, 0, 0, NULL); + ok(!ret && GetLastError() == CRYPT_E_ASN1_EOD, + "Expected CRYPT_E_ASN1_EOD, got %08lx\n", GetLastError()); + ret = CertAddEncodedCRLToStore(store, X509_ASN_ENCODING, NULL, 0, 0, NULL); + ok(!ret && GetLastError() == CRYPT_E_ASN1_EOD, + "Expected CRYPT_E_ASN1_EOD, got %08lx\n", GetLastError()); + + /* Weird--bad add disposition leads to an access violation in Windows. */ + ret = CertAddEncodedCRLToStore(0, X509_ASN_ENCODING, signedCRL, + sizeof(signedCRL), 0, NULL); + ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION, + "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError()); + ret = CertAddEncodedCRLToStore(store, X509_ASN_ENCODING, signedCRL, + sizeof(signedCRL), 0, NULL); + ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION, + "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError()); + + /* Weird--can add a CRL to the NULL store (does this have special meaning?) + */ + context = NULL; + ret = CertAddEncodedCRLToStore(0, X509_ASN_ENCODING, signedCRL, + sizeof(signedCRL), CERT_STORE_ADD_ALWAYS, &context); + ok(ret, "CertAddEncodedCRLToStore failed: %08lx\n", GetLastError()); + if (context) + CertFreeCRLContext(context); + + /* Normal cases: a "signed" CRL is okay.. */ + ret = CertAddEncodedCRLToStore(store, X509_ASN_ENCODING, signedCRL, + sizeof(signedCRL), CERT_STORE_ADD_ALWAYS, NULL); + /* and an unsigned one is too. */ + ret = CertAddEncodedCRLToStore(store, X509_ASN_ENCODING, CRL, sizeof(CRL), + CERT_STORE_ADD_ALWAYS, NULL); + ok(ret, "CertAddEncodedCRLToStore failed: %08lx\n", GetLastError()); + + ret = CertAddEncodedCRLToStore(store, X509_ASN_ENCODING, newerCRL, + sizeof(newerCRL), CERT_STORE_ADD_NEW, NULL); + ok(!ret && GetLastError() == CRYPT_E_EXISTS, + "Expected CRYPT_E_EXISTS, got %08lx\n", GetLastError()); + + /* This should replace (one of) the existing CRL(s). */ + ret = CertAddEncodedCRLToStore(store, X509_ASN_ENCODING, newerCRL, + sizeof(newerCRL), CERT_STORE_ADD_NEWER, NULL); + ok(ret, "CertAddEncodedCRLToStore failed: %08lx\n", GetLastError()); + + CertCloseStore(store, 0); +} + +static void testFindCRL(void) +{ + HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, + CERT_STORE_CREATE_NEW_FLAG, NULL); + PCCRL_CONTEXT context; + PCCERT_CONTEXT cert; + BOOL ret; + + if (!store) return; + + ret = CertAddEncodedCRLToStore(store, X509_ASN_ENCODING, signedCRL, + sizeof(signedCRL), CERT_STORE_ADD_ALWAYS, NULL); + ok(ret, "CertAddEncodedCRLToStore failed: %08lx\n", GetLastError()); + + /* Crashes + context = CertFindCRLInStore(NULL, 0, 0, 0, NULL, NULL); + */ + + /* Find any context */ + context = CertFindCRLInStore(store, 0, 0, CRL_FIND_ANY, NULL, NULL); + ok(context != NULL, "Expected a context\n"); + if (context) + CertFreeCRLContext(context); + /* Bogus flags are ignored */ + context = CertFindCRLInStore(store, 0, 1234, CRL_FIND_ANY, NULL, NULL); + ok(context != NULL, "Expected a context\n"); + if (context) + CertFreeCRLContext(context); + /* CRL encoding type is ignored too */ + context = CertFindCRLInStore(store, 1234, 0, CRL_FIND_ANY, NULL, NULL); + ok(context != NULL, "Expected a context\n"); + if (context) + CertFreeCRLContext(context); + + /* This appears to match any cert */ + context = CertFindCRLInStore(store, 0, 0, CRL_FIND_ISSUED_BY, NULL, NULL); + ok(context != NULL, "Expected a context\n"); + if (context) + CertFreeCRLContext(context); + + /* Try to match an issuer that isn't in the store */ + cert = CertCreateCertificateContext(X509_ASN_ENCODING, bigCert2, + sizeof(bigCert2)); + ok(cert != NULL, "CertCreateCertificateContext failed: %08lx\n", + GetLastError()); + context = CertFindCRLInStore(store, 0, 0, CRL_FIND_ISSUED_BY, cert, NULL); + ok(context == NULL, "Expected no matching context\n"); + CertFreeCertificateContext(cert); + + /* Match an issuer that is in the store */ + cert = CertCreateCertificateContext(X509_ASN_ENCODING, bigCert, + sizeof(bigCert)); + ok(cert != NULL, "CertCreateCertificateContext failed: %08lx\n", + GetLastError()); + context = CertFindCRLInStore(store, 0, 0, CRL_FIND_ISSUED_BY, cert, NULL); + ok(context != NULL, "Expected a context\n"); + if (context) + CertFreeCRLContext(context); + CertFreeCertificateContext(cert); + + CertCloseStore(store, 0); +} + +static void checkCRLHash(const BYTE *data, DWORD dataLen, ALG_ID algID, + PCCRL_CONTEXT context, DWORD propID) +{ + BYTE hash[20] = { 0 }, hashProperty[20]; + BOOL ret; + DWORD size; + + memset(hash, 0, sizeof(hash)); + memset(hashProperty, 0, sizeof(hashProperty)); + size = sizeof(hash); + ret = CryptHashCertificate(0, algID, 0, data, dataLen, hash, &size); + ok(ret, "CryptHashCertificate failed: %08lx\n", GetLastError()); + ret = CertGetCRLContextProperty(context, propID, hashProperty, &size); + ok(ret, "CertGetCRLContextProperty failed: %08lx\n", GetLastError()); + ok(!memcmp(hash, hashProperty, size), "Unexpected hash for property %ld\n", + propID); +} + +static void testCRLProperties(void) +{ + PCCRL_CONTEXT context = CertCreateCRLContext(X509_ASN_ENCODING, + CRL, sizeof(CRL)); + + ok(context != NULL, "CertCreateCRLContext failed: %08lx\n", GetLastError()); + if (context) + { + DWORD propID, numProps, access, size; + BOOL ret; + BYTE hash[20] = { 0 }, hashProperty[20]; + CRYPT_DATA_BLOB blob; + + /* This crashes + propID = CertEnumCRLContextProperties(NULL, 0); + */ + + propID = 0; + numProps = 0; + do { + propID = CertEnumCRLContextProperties(context, propID); + if (propID) + numProps++; + } while (propID != 0); + ok(numProps == 0, "Expected 0 properties, got %ld\n", numProps); + + /* Tests with a NULL cert context. Prop ID 0 fails.. */ + ret = CertSetCRLContextProperty(NULL, 0, 0, NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08lx\n", GetLastError()); + /* while this just crashes. + ret = CertSetCRLContextProperty(NULL, CERT_KEY_PROV_HANDLE_PROP_ID, 0, + NULL); + */ + + ret = CertSetCRLContextProperty(context, 0, 0, NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08lx\n", GetLastError()); + /* Can't set the cert property directly, this crashes. + ret = CertSetCRLContextProperty(context, CERT_CRL_PROP_ID, 0, CRL); + */ + + /* These all crash. + ret = CertGetCRLContextProperty(context, CERT_ACCESS_STATE_PROP_ID, 0, + NULL); + ret = CertGetCRLContextProperty(context, CERT_HASH_PROP_ID, NULL, NULL); + ret = CertGetCRLContextProperty(context, CERT_HASH_PROP_ID, + hashProperty, NULL); + */ + /* A missing prop */ + size = 0; + ret = CertGetCRLContextProperty(context, CERT_KEY_PROV_INFO_PROP_ID, + NULL, &size); + ok(!ret && GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND, got %08lx\n", GetLastError()); + /* And, an implicit property */ + ret = CertGetCRLContextProperty(context, CERT_ACCESS_STATE_PROP_ID, + NULL, &size); + ok(ret, "CertGetCRLContextProperty failed: %08lx\n", GetLastError()); + ret = CertGetCRLContextProperty(context, CERT_ACCESS_STATE_PROP_ID, + &access, &size); + ok(ret, "CertGetCRLContextProperty failed: %08lx\n", GetLastError()); + ok(!(access & CERT_ACCESS_STATE_WRITE_PERSIST_FLAG), + "Didn't expect a persisted crl\n"); + /* Trying to set this "read only" property crashes. + access |= CERT_ACCESS_STATE_WRITE_PERSIST_FLAG; + ret = CertSetCRLContextProperty(context, CERT_ACCESS_STATE_PROP_ID, 0, + &access); + */ + + /* Can I set the hash to an invalid hash? */ + blob.pbData = hash; + blob.cbData = sizeof(hash); + ret = CertSetCRLContextProperty(context, CERT_HASH_PROP_ID, 0, &blob); + ok(ret, "CertSetCRLContextProperty failed: %08lx\n", + GetLastError()); + size = sizeof(hashProperty); + ret = CertGetCRLContextProperty(context, CERT_HASH_PROP_ID, + hashProperty, &size); + ok(!memcmp(hashProperty, hash, sizeof(hash)), "Unexpected hash\n"); + /* Delete the (bogus) hash, and get the real one */ + ret = CertSetCRLContextProperty(context, CERT_HASH_PROP_ID, 0, NULL); + ok(ret, "CertSetCRLContextProperty failed: %08lx\n", GetLastError()); + checkCRLHash(CRL, sizeof(CRL), CALG_SHA1, context, CERT_HASH_PROP_ID); + + /* Now that the hash property is set, we should get one property when + * enumerating. + */ + propID = 0; + numProps = 0; + do { + propID = CertEnumCRLContextProperties(context, propID); + if (propID) + numProps++; + } while (propID != 0); + ok(numProps == 1, "Expected 1 properties, got %ld\n", numProps); + + /* Check a few other implicit properties */ + checkCRLHash(CRL, sizeof(CRL), CALG_MD5, context, + CERT_MD5_HASH_PROP_ID); + + CertFreeCRLContext(context); + } +} + START_TEST(store) { testAddCert(); @@ -1892,6 +2198,10 @@ START_TEST(store) testGetSubjectCert(); testGetIssuerCert(); + testCreateCRL(); + testAddCRL(); + testFindCRL(); + /* various combinations of CertOpenStore */ testMemStore(); testCollectionStore(); @@ -1902,5 +2212,6 @@ START_TEST(store) testCertOpenSystemStore(); testCertProperties(); + testCRLProperties(); testAddSerialized(); } diff --git a/include/wincrypt.h b/include/wincrypt.h index 16110b22cd5..a5f426924da 100644 --- a/include/wincrypt.h +++ b/include/wincrypt.h @@ -1939,6 +1939,22 @@ static const WCHAR CERT_PHYSICAL_STORE_AUTH_ROOT_NAME[] = #define CERT_FIND_VALID_ENHKEY_USAGE_FLAG 0x20 #define CERT_FIND_VALID_CTL_USAGE_FLAG 0x20 +#define CRL_FIND_ANY 0 +#define CRL_FIND_ISSUED_BY 1 +#define CRL_FIND_EXISTING 2 +#define CRL_FIND_ISSUED_FOR 3 + +#define CRL_FIND_ISSUED_BY_AKI_FLAG 0x1 +#define CRL_FIND_ISSUED_BY_SIGNATURE_FLAG 0x2 +#define CRL_FIND_ISSUED_BY_DELTA_FLAG 0x4 +#define CRL_FIND_ISSUED_BY_BASE_FLAG 0x8 + +typedef struct _CRL_FIND_ISSUED_FOR_PARA +{ + PCCERT_CONTEXT pSubjectCert; + PCCERT_CONTEXT pIssuerCert; +} CRL_FIND_ISSUED_FOR_PARA, *PCRL_FIND_ISSUED_FOR_PARA; + /* PFN_CERT_STORE_PROV_WRITE_CERT dwFlags values */ #define CERT_STORE_PROV_WRITE_ADD_FLAG 0x1