From: Gé van Geldorp Date: Thu, 17 Nov 2005 21:02:48 +0000 (+0000) Subject: Wine-0_9_1 vendor import. Changes for ReactOS by cwittich X-Git-Tag: backups/ros-branch-0_2_9@19949~608 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=5f23f01a75359849522b1cb4f518f64a0d6bff71 Wine-0_9_1 vendor import. Changes for ReactOS by cwittich svn path=/trunk/; revision=19310 --- diff --git a/reactos/lib/crypt32/cert.c b/reactos/lib/crypt32/cert.c new file mode 100644 index 00000000000..744f9c3cb53 --- /dev/null +++ b/reactos/lib/crypt32/cert.c @@ -0,0 +1,3315 @@ +/* + * Copyright 2002 + Mike McCormack for CodeWeavers + * Copyright 2004,2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + * - Opening a cert store provider should be morphed to support loading + * external DLLs. + * - 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.) + * - Many flags, options and whatnot are unimplemented. + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(crypt); + +#define WINE_CRYPTCERTSTORE_MAGIC 0x74726563 +/* The following aren't defined in wincrypt.h, as they're "reserved" */ +#define CERT_CERT_PROP_ID 32 +#define CERT_CRL_PROP_ID 33 +#define CERT_CTL_PROP_ID 34 + +/* Some typedefs that make it easier to abstract which type of context we're + * working with. + */ +typedef const void *(WINAPI *CreateContextFunc)(DWORD dwCertEncodingType, + const BYTE *pbCertEncoded, DWORD cbCertEncoded); +typedef BOOL (WINAPI *AddContextToStoreFunc)(HCERTSTORE hCertStore, + const void *context, DWORD dwAddDisposition, const void **ppStoreContext); +typedef BOOL (WINAPI *AddEncodedContextToStoreFunc)(HCERTSTORE hCertStore, + DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded, + DWORD dwAddDisposition, const void **ppContext); +typedef const void *(WINAPI *EnumContextsInStoreFunc)(HCERTSTORE hCertStore, + const void *pPrevContext); +typedef BOOL (WINAPI *GetContextPropertyFunc)(const void *context, + DWORD dwPropID, void *pvData, DWORD *pcbData); +typedef BOOL (WINAPI *SetContextPropertyFunc)(const void *context, + DWORD dwPropID, DWORD dwFlags, const void *pvData); +typedef BOOL (WINAPI *SerializeElementFunc)(const void *context, DWORD dwFlags, + BYTE *pbElement, DWORD *pcbElement); +typedef BOOL (WINAPI *FreeContextFunc)(const void *context); +typedef BOOL (WINAPI *DeleteContextFunc)(const void *context); + +/* An abstract context (certificate, CRL, or CTL) interface */ +typedef struct _WINE_CONTEXT_INTERFACE +{ + CreateContextFunc create; + AddContextToStoreFunc addContextToStore; + AddEncodedContextToStoreFunc addEncodedToStore; + EnumContextsInStoreFunc enumContextsInStore; + GetContextPropertyFunc getProp; + SetContextPropertyFunc setProp; + SerializeElementFunc serialize; + FreeContextFunc free; + DeleteContextFunc deleteFromStore; +} WINE_CONTEXT_INTERFACE, *PWINE_CONTEXT_INTERFACE; + +static const WINE_CONTEXT_INTERFACE gCertInterface = { + (CreateContextFunc)CertCreateCertificateContext, + (AddContextToStoreFunc)CertAddCertificateContextToStore, + (AddEncodedContextToStoreFunc)CertAddEncodedCertificateToStore, + (EnumContextsInStoreFunc)CertEnumCertificatesInStore, + (GetContextPropertyFunc)CertGetCertificateContextProperty, + (SetContextPropertyFunc)CertSetCertificateContextProperty, + (SerializeElementFunc)CertSerializeCertificateStoreElement, + (FreeContextFunc)CertFreeCertificateContext, + (DeleteContextFunc)CertDeleteCertificateFromStore, +}; + +static const WINE_CONTEXT_INTERFACE gCRLInterface = { + (CreateContextFunc)CertCreateCRLContext, + (AddContextToStoreFunc)CertAddCRLContextToStore, + (AddEncodedContextToStoreFunc)CertAddEncodedCRLToStore, + (EnumContextsInStoreFunc)CertEnumCRLsInStore, + (GetContextPropertyFunc)CertGetCRLContextProperty, + (SetContextPropertyFunc)CertSetCRLContextProperty, + (SerializeElementFunc)CertSerializeCRLStoreElement, + (FreeContextFunc)CertFreeCRLContext, + (DeleteContextFunc)CertDeleteCRLFromStore, +}; + +static const WINE_CONTEXT_INTERFACE gCTLInterface = { + (CreateContextFunc)CertCreateCTLContext, + (AddContextToStoreFunc)CertAddCTLContextToStore, + (AddEncodedContextToStoreFunc)CertAddEncodedCTLToStore, + (EnumContextsInStoreFunc)CertEnumCTLsInStore, + (GetContextPropertyFunc)CertGetCTLContextProperty, + (SetContextPropertyFunc)CertSetCTLContextProperty, + (SerializeElementFunc)CertSerializeCTLStoreElement, + (FreeContextFunc)CertFreeCTLContext, + (DeleteContextFunc)CertDeleteCTLFromStore, +}; + +struct WINE_CRYPTCERTSTORE; + +typedef struct WINE_CRYPTCERTSTORE * (*StoreOpenFunc)(HCRYPTPROV hCryptProv, + DWORD dwFlags, const void *pvPara); + +struct _WINE_CERT_CONTEXT_REF; + +/* Called to enumerate the next certificate in a store. The returned pointer + * must be newly allocated (via CryptMemAlloc): CertFreeCertificateContext + * frees it. + */ +typedef struct _WINE_CERT_CONTEXT_REF * (*EnumCertFunc) + (struct WINE_CRYPTCERTSTORE *store, struct _WINE_CERT_CONTEXT_REF *pPrev); + +struct _WINE_CERT_CONTEXT; + +/* Called to create a new reference to an existing cert context. Should call + * CRYPT_InitCertRef to make sure the reference count is properly updated. + * If the store does not provide any additional allocated data (that is, does + * not need to implement a FreeCertFunc), it may use CRYPT_CreateCertRef for + * this. + */ +typedef struct _WINE_CERT_CONTEXT_REF * (*CreateRefFunc) + (struct _WINE_CERT_CONTEXT *context, HCERTSTORE store); + +/* Optional, called when a cert context reference is being freed. Don't free + * the ref pointer itself, CertFreeCertificateContext does that. + */ +typedef void (*FreeCertFunc)(struct _WINE_CERT_CONTEXT_REF *ref); + +typedef enum _CertStoreType { + StoreTypeMem, + StoreTypeCollection, + StoreTypeReg, + StoreTypeDummy, +} CertStoreType; + +/* A cert store is polymorphic through the use of function pointers. A type + * is still needed to distinguish collection stores from other types. + * On the function pointers: + * - closeStore is called when the store's ref count becomes 0 + * - addCert is called with a PWINE_CERT_CONTEXT as the second parameter + * - control is optional, but should be implemented by any store that supports + * persistence + */ +typedef struct WINE_CRYPTCERTSTORE +{ + DWORD dwMagic; + LONG ref; + DWORD dwOpenFlags; + HCRYPTPROV cryptProv; + CertStoreType type; + PFN_CERT_STORE_PROV_CLOSE closeStore; + PFN_CERT_STORE_PROV_WRITE_CERT addCert; + CreateRefFunc createCertRef; + EnumCertFunc enumCert; + PFN_CERT_STORE_PROV_DELETE_CERT deleteCert; + FreeCertFunc freeCert; /* optional */ + PFN_CERT_STORE_PROV_CONTROL control; /* optional */ +} WINECRYPT_CERTSTORE, *PWINECRYPT_CERTSTORE; + +/* A certificate context has pointers to data that are owned by this module, + * so rather than duplicate the data every time a certificate context is + * copied, I keep a reference count to the data. Thus I have two data + * structures, the "true" certificate context (that has the reference count) + * and a reference certificate context, that has a pointer to the true context. + * Each one can be cast to a PCERT_CONTEXT, though you'll usually be dealing + * with the reference version. + */ +typedef struct _WINE_CERT_CONTEXT +{ + CERT_CONTEXT cert; + LONG ref; + CRITICAL_SECTION cs; + struct list extendedProperties; +} WINE_CERT_CONTEXT, *PWINE_CERT_CONTEXT; + +typedef struct _WINE_CERT_CONTEXT_REF +{ + CERT_CONTEXT cert; + WINE_CERT_CONTEXT *context; +} WINE_CERT_CONTEXT_REF, *PWINE_CERT_CONTEXT_REF; + +/* An extended certificate property in serialized form is prefixed by this + * header. + */ +typedef struct _WINE_CERT_PROP_HEADER +{ + DWORD propID; + DWORD unknown; /* always 1 */ + DWORD cb; +} WINE_CERT_PROP_HEADER, *PWINE_CERT_PROP_HEADER; + +/* Stores an extended property in a cert. */ +typedef struct _WINE_CERT_PROPERTY +{ + WINE_CERT_PROP_HEADER hdr; + LPBYTE pbData; + struct list entry; +} WINE_CERT_PROPERTY, *PWINE_CERT_PROPERTY; + +/* A mem store has a list of these. They're also returned by the mem store + * during enumeration. + */ +typedef struct _WINE_CERT_LIST_ENTRY +{ + WINE_CERT_CONTEXT_REF cert; + struct list entry; +} WINE_CERT_LIST_ENTRY, *PWINE_CERT_LIST_ENTRY; + +typedef struct _WINE_MEMSTORE +{ + WINECRYPT_CERTSTORE hdr; + CRITICAL_SECTION cs; + struct list certs; +} WINE_MEMSTORE, *PWINE_MEMSTORE; + +typedef struct _WINE_HASH_TO_DELETE +{ + BYTE hash[20]; + struct list entry; +} WINE_HASH_TO_DELETE, *PWINE_HASH_TO_DELETE; + +/* Returned by a reg store during enumeration. */ +typedef struct _WINE_REG_CERT_CONTEXT +{ + WINE_CERT_CONTEXT_REF cert; + PWINE_CERT_CONTEXT_REF childContext; +} WINE_REG_CERT_CONTEXT, *PWINE_REG_CERT_CONTEXT; + +typedef struct _WINE_REGSTORE +{ + WINECRYPT_CERTSTORE hdr; + PWINECRYPT_CERTSTORE memStore; + HKEY key; + BOOL dirty; + CRITICAL_SECTION cs; + struct list certsToDelete; +} WINE_REGSTORE, *PWINE_REGSTORE; + +typedef struct _WINE_STORE_LIST_ENTRY +{ + PWINECRYPT_CERTSTORE store; + DWORD dwUpdateFlags; + DWORD dwPriority; + struct list entry; +} WINE_STORE_LIST_ENTRY, *PWINE_STORE_LIST_ENTRY; + +/* Returned by a collection store during enumeration. + * Note: relies on the list entry being valid after use, which a number of + * conditions might make untrue (reentrancy, closing a collection store before + * continuing an enumeration on it, ...). The tests seem to indicate this + * sort of unsafety is okay, since Windows isn't well-behaved in these + * scenarios either. + */ +typedef struct _WINE_COLLECTION_CERT_CONTEXT +{ + WINE_CERT_CONTEXT_REF cert; + PWINE_STORE_LIST_ENTRY entry; + PWINE_CERT_CONTEXT_REF childContext; +} WINE_COLLECTION_CERT_CONTEXT, *PWINE_COLLECTION_CERT_CONTEXT; + +typedef struct _WINE_COLLECTIONSTORE +{ + WINECRYPT_CERTSTORE hdr; + CRITICAL_SECTION cs; + struct list stores; +} WINE_COLLECTIONSTORE, *PWINE_COLLECTIONSTORE; + +/* Like CertGetCertificateContextProperty, but operates directly on the + * WINE_CERT_CONTEXT. Doesn't support special-case properties, since they + * are handled by CertGetCertificateContextProperty, and are particular to the + * store in which the property exists (which is separate from the context.) + */ +static BOOL WINAPI CRYPT_GetCertificateContextProperty( + PWINE_CERT_CONTEXT context, DWORD dwPropId, void *pvData, DWORD *pcbData); + +/* Like CertSetCertificateContextProperty, but operates directly on the + * WINE_CERT_CONTEXT. Doesn't handle special cases, since they're handled by + * CertSetCertificateContextProperty anyway. + */ +static BOOL WINAPI CRYPT_SetCertificateContextProperty( + PWINE_CERT_CONTEXT context, DWORD dwPropId, DWORD dwFlags, const void *pvData); + +/* Helper function for store reading functions and + * CertAddSerializedElementToStore. Returns a context of the appropriate type + * if it can, or NULL otherwise. Doesn't validate any of the properties in + * the serialized context (for example, bad hashes are retained.) + * *pdwContentType is set to the type of the returned context. + */ +static const void * WINAPI CRYPT_ReadSerializedElement(const BYTE *pbElement, + DWORD cbElement, DWORD dwContextTypeFlags, DWORD *pdwContentType); + +/* filter for page-fault exceptions */ +static WINE_EXCEPTION_FILTER(page_fault) +{ + if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) + return EXCEPTION_EXECUTE_HANDLER; + return EXCEPTION_CONTINUE_SEARCH; +} + +static void CRYPT_InitStore(WINECRYPT_CERTSTORE *store, HCRYPTPROV hCryptProv, + DWORD dwFlags, CertStoreType type) +{ + store->ref = 1; + store->dwMagic = WINE_CRYPTCERTSTORE_MAGIC; + store->type = type; + if (!hCryptProv) + { + hCryptProv = CRYPT_GetDefaultProvider(); + dwFlags |= CERT_STORE_NO_CRYPT_RELEASE_FLAG; + } + store->cryptProv = hCryptProv; + store->dwOpenFlags = dwFlags; +} + +/* Initializes the reference ref to point to pCertContext, which is assumed to + * be a PWINE_CERT_CONTEXT, and increments pCertContext's reference count. + * Also sets the hCertStore member of the reference to store. + */ +static void CRYPT_InitCertRef(PWINE_CERT_CONTEXT_REF ref, + PWINE_CERT_CONTEXT context, HCERTSTORE store) +{ + TRACE("(%p, %p)\n", ref, context); + memcpy(&ref->cert, context, sizeof(ref->cert)); + ref->context = context; + InterlockedIncrement(&context->ref); + ref->cert.hCertStore = store; +} + +static PWINE_CERT_CONTEXT_REF CRYPT_CreateCertRef(PWINE_CERT_CONTEXT context, + HCERTSTORE store) +{ + PWINE_CERT_CONTEXT_REF pCertRef = CryptMemAlloc( + sizeof(WINE_CERT_CONTEXT_REF)); + + if (pCertRef) + CRYPT_InitCertRef(pCertRef, context, store); + return pCertRef; +} + +static BOOL WINAPI CRYPT_MemAddCert(HCERTSTORE store, PCCERT_CONTEXT pCert, + DWORD dwAddDisposition) +{ + WINE_MEMSTORE *ms = (WINE_MEMSTORE *)store; + BOOL add = FALSE, ret; + + TRACE("(%p, %p, %ld)\n", store, pCert, dwAddDisposition); + + switch (dwAddDisposition) + { + case CERT_STORE_ADD_ALWAYS: + add = TRUE; + break; + case CERT_STORE_ADD_NEW: + { + BYTE hashToAdd[20], hash[20]; + DWORD size = sizeof(hashToAdd); + + ret = CRYPT_GetCertificateContextProperty((PWINE_CERT_CONTEXT)pCert, + CERT_HASH_PROP_ID, hashToAdd, &size); + if (ret) + { + PWINE_CERT_LIST_ENTRY cursor; + + /* Add if no cert with the same hash is found. */ + add = TRUE; + EnterCriticalSection(&ms->cs); + LIST_FOR_EACH_ENTRY(cursor, &ms->certs, WINE_CERT_LIST_ENTRY, entry) + { + size = sizeof(hash); + ret = CertGetCertificateContextProperty(&cursor->cert.cert, + CERT_HASH_PROP_ID, hash, &size); + if (ret && !memcmp(hashToAdd, hash, size)) + { + TRACE("found matching certificate, not adding\n"); + SetLastError(CRYPT_E_EXISTS); + add = FALSE; + break; + } + } + LeaveCriticalSection(&ms->cs); + } + break; + } + case CERT_STORE_ADD_REPLACE_EXISTING: + { + BYTE hashToAdd[20], hash[20]; + DWORD size = sizeof(hashToAdd); + + add = TRUE; + ret = CRYPT_GetCertificateContextProperty((PWINE_CERT_CONTEXT)pCert, + CERT_HASH_PROP_ID, hashToAdd, &size); + if (ret) + { + PWINE_CERT_LIST_ENTRY cursor, next; + + /* Look for existing cert to delete */ + EnterCriticalSection(&ms->cs); + LIST_FOR_EACH_ENTRY_SAFE(cursor, next, &ms->certs, + WINE_CERT_LIST_ENTRY, entry) + { + size = sizeof(hash); + ret = CertGetCertificateContextProperty(&cursor->cert.cert, + CERT_HASH_PROP_ID, hash, &size); + if (ret && !memcmp(hashToAdd, hash, size)) + { + TRACE("found matching certificate, replacing\n"); + list_remove(&cursor->entry); + CertFreeCertificateContext((PCCERT_CONTEXT)cursor); + break; + } + } + LeaveCriticalSection(&ms->cs); + } + break; + } + default: + FIXME("Unimplemented add disposition %ld\n", dwAddDisposition); + add = FALSE; + } + 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)pCert, store); + list_init(&entry->entry); + EnterCriticalSection(&ms->cs); + list_add_tail(&ms->certs, &entry->entry); + LeaveCriticalSection(&ms->cs); + ret = TRUE; + } + else + ret = FALSE; + } + else + ret = FALSE; + return ret; +} + +static PWINE_CERT_CONTEXT_REF CRYPT_MemEnumCert(PWINECRYPT_CERTSTORE store, + PWINE_CERT_CONTEXT_REF pPrev) +{ + WINE_MEMSTORE *ms = (WINE_MEMSTORE *)store; + PWINE_CERT_LIST_ENTRY prevEntry = (PWINE_CERT_LIST_ENTRY)pPrev, ret; + struct list *listNext; + + TRACE("(%p, %p)\n", store, pPrev); + EnterCriticalSection(&ms->cs); + if (prevEntry) + { + listNext = list_next(&ms->certs, &prevEntry->entry); + CertFreeCertificateContext((PCCERT_CONTEXT)pPrev); + } + else + listNext = list_next(&ms->certs, &ms->certs); + if (listNext) + { + ret = CryptMemAlloc(sizeof(WINE_CERT_LIST_ENTRY)); + memcpy(ret, LIST_ENTRY(listNext, WINE_CERT_LIST_ENTRY, entry), + sizeof(WINE_CERT_LIST_ENTRY)); + InterlockedIncrement(&ret->cert.context->ref); + } + else + { + SetLastError(CRYPT_E_NOT_FOUND); + ret = NULL; + } + LeaveCriticalSection(&ms->cs); + + TRACE("returning %p\n", ret); + return (PWINE_CERT_CONTEXT_REF)ret; +} + +static BOOL WINAPI CRYPT_MemDeleteCert(HCERTSTORE hCertStore, + PCCERT_CONTEXT pCertContext, DWORD dwFlags) +{ + WINE_MEMSTORE *store = (WINE_MEMSTORE *)hCertStore; + WINE_CERT_CONTEXT_REF *ref = (WINE_CERT_CONTEXT_REF *)pCertContext; + PWINE_CERT_LIST_ENTRY cert, next; + BOOL ret; + + /* Find the entry associated with the passed-in context, since the + * passed-in context may not be a list entry itself (e.g. if it came from + * CertDuplicateCertificateContext.) Pointing to the same context is + * a sufficient test of equality. + */ + EnterCriticalSection(&store->cs); + LIST_FOR_EACH_ENTRY_SAFE(cert, next, &store->certs, WINE_CERT_LIST_ENTRY, + entry) + { + if (cert->cert.context == ref->context) + { + TRACE("removing %p\n", cert); + /* FIXME: this isn't entirely thread-safe, the entry itself isn't + * protected. + */ + list_remove(&cert->entry); + cert->entry.prev = cert->entry.next = &store->certs; + break; + } + } + ret = TRUE; + LeaveCriticalSection(&store->cs); + return ret; +} + +static void WINAPI CRYPT_MemCloseStore(HCERTSTORE hCertStore, DWORD dwFlags) +{ + WINE_MEMSTORE *store = (WINE_MEMSTORE *)hCertStore; + PWINE_CERT_LIST_ENTRY cert, next; + + TRACE("(%p, %08lx)\n", store, dwFlags); + if (dwFlags) + FIXME("Unimplemented flags: %08lx\n", dwFlags); + + /* Note that CertFreeCertificateContext calls HeapFree on the passed-in + * pointer if its ref-count reaches zero. That's okay here because there + * aren't any allocated data outside of the WINE_CERT_CONTEXT_REF portion + * of the CertListEntry. + */ + LIST_FOR_EACH_ENTRY_SAFE(cert, next, &store->certs, WINE_CERT_LIST_ENTRY, + entry) + { + TRACE("removing %p\n", cert); + list_remove(&cert->entry); + CertFreeCertificateContext((PCCERT_CONTEXT)cert); + } + DeleteCriticalSection(&store->cs); + CryptMemFree(store); +} + +static WINECRYPT_CERTSTORE *CRYPT_MemOpenStore(HCRYPTPROV hCryptProv, + DWORD dwFlags, const void *pvPara) +{ + PWINE_MEMSTORE store; + + TRACE("(%ld, %08lx, %p)\n", hCryptProv, dwFlags, pvPara); + + if (dwFlags & CERT_STORE_DELETE_FLAG) + { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + store = NULL; + } + else + { + store = CryptMemAlloc(sizeof(WINE_MEMSTORE)); + if (store) + { + memset(store, 0, sizeof(WINE_MEMSTORE)); + CRYPT_InitStore(&store->hdr, hCryptProv, dwFlags, StoreTypeMem); + store->hdr.closeStore = CRYPT_MemCloseStore; + store->hdr.addCert = CRYPT_MemAddCert; + store->hdr.createCertRef = CRYPT_CreateCertRef; + store->hdr.enumCert = CRYPT_MemEnumCert; + store->hdr.deleteCert = CRYPT_MemDeleteCert; + store->hdr.freeCert = NULL; + InitializeCriticalSection(&store->cs); + list_init(&store->certs); + } + } + return (PWINECRYPT_CERTSTORE)store; +} + +static BOOL WINAPI CRYPT_CollectionAddCert(HCERTSTORE store, + PCCERT_CONTEXT pCert, DWORD dwAddDisposition) +{ + PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store; + PWINE_STORE_LIST_ENTRY entry, next; + BOOL ret; + + TRACE("(%p, %p, %ld)\n", store, pCert, dwAddDisposition); + + ret = FALSE; + 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) + { + ret = entry->store->addCert(entry->store, pCert, dwAddDisposition); + break; + } + } + LeaveCriticalSection(&cs->cs); + SetLastError(ret ? ERROR_SUCCESS : HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)); + return ret; +} + +static PWINE_CERT_CONTEXT_REF CRYPT_CollectionCreateCertRef( + PWINE_CERT_CONTEXT context, HCERTSTORE store) +{ + PWINE_COLLECTION_CERT_CONTEXT ret = CryptMemAlloc( + sizeof(WINE_COLLECTION_CERT_CONTEXT)); + + if (ret) + { + /* Initialize to empty for now, just make sure the size is right */ + CRYPT_InitCertRef((PWINE_CERT_CONTEXT_REF)ret, context, store); + ret->entry = NULL; + ret->childContext = NULL; + } + return (PWINE_CERT_CONTEXT_REF)ret; +} + +static void WINAPI CRYPT_CollectionCloseStore(HCERTSTORE store, DWORD dwFlags) +{ + PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store; + PWINE_STORE_LIST_ENTRY entry, next; + + TRACE("(%p, %08lx)\n", store, dwFlags); + + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &cs->stores, WINE_STORE_LIST_ENTRY, + entry) + { + TRACE("closing %p\n", entry); + CertCloseStore((HCERTSTORE)entry->store, dwFlags); + CryptMemFree(entry); + } + DeleteCriticalSection(&cs->cs); + CryptMemFree(cs); +} + +/* Advances a collection enumeration by one cert, if possible, where advancing + * means: + * - calling the current store's enumeration function once, and returning + * the enumerated cert 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 PWINE_COLLECTION_CERT_CONTEXT CRYPT_CollectionAdvanceEnum( + PWINE_COLLECTIONSTORE store, PWINE_STORE_LIST_ENTRY storeEntry, + PWINE_COLLECTION_CERT_CONTEXT pPrev) +{ + PWINE_COLLECTION_CERT_CONTEXT ret; + PWINE_CERT_CONTEXT_REF child; + + TRACE("(%p, %p, %p)\n", store, storeEntry, pPrev); + + if (pPrev) + { + child = storeEntry->store->enumCert((HCERTSTORE)storeEntry->store, + pPrev->childContext); + if (child) + { + ret = pPrev; + memcpy(&ret->cert, child, sizeof(WINE_CERT_CONTEXT_REF)); + ret->cert.cert.hCertStore = (HCERTSTORE)store; + InterlockedIncrement(&ret->cert.context->ref); + ret->childContext = child; + } + else + { + struct list *storeNext = list_next(&store->stores, + &storeEntry->entry); + + pPrev->childContext = NULL; + CertFreeCertificateContext((PCCERT_CONTEXT)pPrev); + if (storeNext) + { + storeEntry = LIST_ENTRY(storeNext, WINE_STORE_LIST_ENTRY, + entry); + ret = CRYPT_CollectionAdvanceEnum(store, storeEntry, NULL); + } + else + { + SetLastError(CRYPT_E_NOT_FOUND); + ret = NULL; + } + } + } + else + { + child = storeEntry->store->enumCert((HCERTSTORE)storeEntry->store, + NULL); + if (child) + { + ret = (PWINE_COLLECTION_CERT_CONTEXT)CRYPT_CollectionCreateCertRef( + child->context, store); + if (ret) + { + ret->entry = storeEntry; + ret->childContext = child; + } + else + CertFreeCertificateContext((PCCERT_CONTEXT)child); + } + else + { + struct list *storeNext = list_next(&store->stores, + &storeEntry->entry); + + if (storeNext) + { + storeEntry = LIST_ENTRY(storeNext, WINE_STORE_LIST_ENTRY, + entry); + ret = CRYPT_CollectionAdvanceEnum(store, storeEntry, NULL); + } + else + { + SetLastError(CRYPT_E_NOT_FOUND); + ret = NULL; + } + } + } + TRACE("returning %p\n", ret); + return ret; +} + +static PWINE_CERT_CONTEXT_REF CRYPT_CollectionEnumCert( + PWINECRYPT_CERTSTORE store, PWINE_CERT_CONTEXT_REF pPrev) +{ + PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store; + PWINE_COLLECTION_CERT_CONTEXT prevEntry = + (PWINE_COLLECTION_CERT_CONTEXT)pPrev, ret; + + TRACE("(%p, %p)\n", store, pPrev); + + if (prevEntry) + { + EnterCriticalSection(&cs->cs); + ret = CRYPT_CollectionAdvanceEnum(cs, prevEntry->entry, prevEntry); + LeaveCriticalSection(&cs->cs); + } + else + { + EnterCriticalSection(&cs->cs); + if (!list_empty(&cs->stores)) + { + PWINE_STORE_LIST_ENTRY storeEntry; + + storeEntry = LIST_ENTRY(cs->stores.next, WINE_STORE_LIST_ENTRY, + entry); + ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, prevEntry); + } + else + { + SetLastError(CRYPT_E_NOT_FOUND); + ret = NULL; + } + LeaveCriticalSection(&cs->cs); + } + TRACE("returning %p\n", ret); + return (PWINE_CERT_CONTEXT_REF)ret; +} + +static BOOL WINAPI CRYPT_CollectionDeleteCert(HCERTSTORE hCertStore, + PCCERT_CONTEXT pCertContext, DWORD dwFlags) +{ + PWINE_COLLECTION_CERT_CONTEXT context = + (PWINE_COLLECTION_CERT_CONTEXT)pCertContext; + BOOL ret; + + TRACE("(%p, %p, %08lx)\n", hCertStore, pCertContext, dwFlags); + + ret = CertDeleteCertificateFromStore((PCCERT_CONTEXT)context->childContext); + if (ret) + context->childContext = NULL; + return ret; +} + +static void CRYPT_CollectionFreeCert(PWINE_CERT_CONTEXT_REF ref) +{ + PWINE_COLLECTION_CERT_CONTEXT context = (PWINE_COLLECTION_CERT_CONTEXT)ref; + + TRACE("(%p)\n", ref); + + if (context->childContext) + CertFreeCertificateContext((PCCERT_CONTEXT)context->childContext); +} + +static WINECRYPT_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, hCryptProv, dwFlags, + StoreTypeCollection); + store->hdr.closeStore = CRYPT_CollectionCloseStore; + store->hdr.addCert = CRYPT_CollectionAddCert; + store->hdr.createCertRef = CRYPT_CollectionCreateCertRef; + store->hdr.enumCert = CRYPT_CollectionEnumCert; + store->hdr.deleteCert = CRYPT_CollectionDeleteCert; + store->hdr.freeCert = CRYPT_CollectionFreeCert; + InitializeCriticalSection(&store->cs); + list_init(&store->stores); + } + } + return (PWINECRYPT_CERTSTORE)store; +} + +static void CRYPT_HashToStr(LPBYTE hash, LPWSTR asciiHash) +{ + static const WCHAR fmt[] = { '%','0','2','X',0 }; + DWORD i; + + assert(hash); + assert(asciiHash); + + for (i = 0; i < 20; i++) + wsprintfW(asciiHash + i * 2, fmt, hash[i]); +} + +static const WCHAR CertsW[] = { 'C','e','r','t','i','f','i','c','a','t','e','s', + 0 }; +static const WCHAR CRLsW[] = { 'C','R','L','s',0 }; +static const WCHAR CTLsW[] = { 'C','T','L','s',0 }; +static const WCHAR BlobW[] = { 'B','l','o','b',0 }; + +static void CRYPT_RegReadSerializedFromReg(PWINE_REGSTORE store, HKEY key, + DWORD contextType) +{ + LONG rc; + DWORD index = 0; + WCHAR subKeyName[MAX_PATH]; + + do { + DWORD size = sizeof(subKeyName) / sizeof(WCHAR); + + rc = RegEnumKeyExW(key, index++, subKeyName, &size, NULL, NULL, NULL, + NULL); + if (!rc) + { + HKEY subKey; + + rc = RegOpenKeyExW(key, subKeyName, 0, KEY_READ, &subKey); + if (!rc) + { + LPBYTE buf = NULL; + + size = 0; + rc = RegQueryValueExW(subKey, BlobW, NULL, NULL, NULL, &size); + if (!rc) + buf = CryptMemAlloc(size); + if (buf) + { + rc = RegQueryValueExW(subKey, BlobW, NULL, NULL, buf, + &size); + if (!rc) + { + const void *context; + DWORD addedType; + + TRACE("Adding cert with hash %s\n", + debugstr_w(subKeyName)); + context = CRYPT_ReadSerializedElement(buf, size, + contextType, &addedType); + if (context) + { + const WINE_CONTEXT_INTERFACE *contextInterface; + BYTE hash[20]; + + switch (addedType) + { + case CERT_STORE_CERTIFICATE_CONTEXT: + contextInterface = &gCertInterface; + break; + case CERT_STORE_CRL_CONTEXT: + contextInterface = &gCRLInterface; + break; + case CERT_STORE_CTL_CONTEXT: + contextInterface = &gCTLInterface; + break; + default: + contextInterface = NULL; + } + if (contextInterface) + { + size = sizeof(hash); + if (contextInterface->getProp(context, + CERT_HASH_PROP_ID, hash, &size)) + { + WCHAR asciiHash[20 * 2 + 1]; + + CRYPT_HashToStr(hash, asciiHash); + TRACE("comparing %s\n", + debugstr_w(asciiHash)); + TRACE("with %s\n", debugstr_w(subKeyName)); + if (!lstrcmpW(asciiHash, subKeyName)) + { + TRACE("hash matches, adding\n"); + contextInterface->addContextToStore( + store, context, + CERT_STORE_ADD_REPLACE_EXISTING, NULL); + } + else + { + TRACE("hash doesn't match, ignoring\n"); + contextInterface->free(context); + } + } + } + } + } + CryptMemFree(buf); + } + RegCloseKey(subKey); + } + /* Ignore intermediate errors, continue enumerating */ + rc = ERROR_SUCCESS; + } + } while (!rc); +} + +static void CRYPT_RegReadFromReg(PWINE_REGSTORE store) +{ + static const WCHAR *subKeys[] = { CertsW, CRLsW, CTLsW }; + static const DWORD contextFlags[] = { CERT_STORE_CERTIFICATE_CONTEXT_FLAG, + CERT_STORE_CRL_CONTEXT_FLAG, CERT_STORE_CTL_CONTEXT_FLAG }; + DWORD i; + + for (i = 0; i < sizeof(subKeys) / sizeof(subKeys[0]); i++) + { + HKEY key; + LONG rc; + + rc = RegCreateKeyExW(store->key, subKeys[i], 0, NULL, 0, KEY_READ, NULL, + &key, NULL); + if (!rc) + { + CRYPT_RegReadSerializedFromReg(store, key, contextFlags[i]); + RegCloseKey(key); + } + } +} + +/* Hash is assumed to be 20 bytes in length (a SHA-1 hash) */ +static BOOL CRYPT_WriteSerializedToReg(HKEY key, LPBYTE hash, LPBYTE buf, + DWORD len) +{ + WCHAR asciiHash[20 * 2 + 1]; + LONG rc; + HKEY subKey; + BOOL ret; + + CRYPT_HashToStr(hash, asciiHash); + rc = RegCreateKeyExW(key, asciiHash, 0, NULL, 0, KEY_ALL_ACCESS, NULL, + &subKey, NULL); + if (!rc) + { + rc = RegSetValueExW(subKey, BlobW, 0, REG_BINARY, buf, len); + RegCloseKey(subKey); + } + if (!rc) + ret = TRUE; + else + { + SetLastError(rc); + ret = FALSE; + } + return ret; +} + +static BOOL CRYPT_SerializeContextsToReg(HKEY key, + const WINE_CONTEXT_INTERFACE *contextInterface, HCERTSTORE memStore) +{ + const void *context = NULL; + BOOL ret; + + do { + context = contextInterface->enumContextsInStore(memStore, context); + if (context) + { + BYTE hash[20]; + DWORD hashSize = sizeof(hash); + + ret = contextInterface->getProp(context, CERT_HASH_PROP_ID, hash, + &hashSize); + if (ret) + { + DWORD size = 0; + LPBYTE buf = NULL; + + ret = contextInterface->serialize(context, 0, NULL, &size); + if (size) + buf = CryptMemAlloc(size); + if (buf) + { + ret = contextInterface->serialize(context, 0, buf, &size); + if (ret) + ret = CRYPT_WriteSerializedToReg(key, hash, buf, size); + } + CryptMemFree(buf); + } + } + else + ret = TRUE; + } while (ret && context != NULL); + if (context) + contextInterface->free(context); + return ret; +} + +static BOOL CRYPT_RegWriteToReg(PWINE_REGSTORE store) +{ + static const WCHAR *subKeys[] = { CertsW, CRLsW, CTLsW }; + static const WINE_CONTEXT_INTERFACE *interfaces[] = { &gCertInterface, + &gCRLInterface, &gCTLInterface }; + struct list *listToDelete[] = { &store->certsToDelete, NULL, NULL }; + BOOL ret = TRUE; + DWORD i; + + for (i = 0; ret && i < sizeof(subKeys) / sizeof(subKeys[0]); i++) + { + HKEY key; + LONG rc = RegCreateKeyExW(store->key, subKeys[i], 0, NULL, 0, + KEY_ALL_ACCESS, NULL, &key, NULL); + + if (!rc) + { + if (listToDelete[i]) + { + PWINE_HASH_TO_DELETE toDelete, next; + WCHAR asciiHash[20 * 2 + 1]; + + EnterCriticalSection(&store->cs); + LIST_FOR_EACH_ENTRY_SAFE(toDelete, next, listToDelete[i], + WINE_HASH_TO_DELETE, entry) + { + LONG rc; + + CRYPT_HashToStr(toDelete->hash, asciiHash); + TRACE("Removing %s\n", debugstr_w(asciiHash)); + rc = RegDeleteKeyW(key, asciiHash); + if (rc != ERROR_SUCCESS && rc != ERROR_FILE_NOT_FOUND) + { + SetLastError(rc); + ret = FALSE; + } + list_remove(&toDelete->entry); + CryptMemFree(toDelete); + } + LeaveCriticalSection(&store->cs); + } + ret = CRYPT_SerializeContextsToReg(key, interfaces[i], + store->memStore); + RegCloseKey(key); + } + else + { + SetLastError(rc); + ret = FALSE; + } + } + return ret; +} + +/* If force is true or the registry store is dirty, writes the contents of the + * store to the registry. + */ +static BOOL CRYPT_RegFlushStore(PWINE_REGSTORE store, BOOL force) +{ + BOOL ret; + + if (store->dirty || force) + ret = CRYPT_RegWriteToReg(store); + else + ret = TRUE; + return ret; +} + +static void WINAPI CRYPT_RegCloseStore(HCERTSTORE hCertStore, DWORD dwFlags) +{ + PWINE_REGSTORE store = (PWINE_REGSTORE)hCertStore; + + TRACE("(%p, %08lx)\n", store, dwFlags); + if (dwFlags) + FIXME("Unimplemented flags: %08lx\n", dwFlags); + + CRYPT_RegFlushStore(store, FALSE); + /* certsToDelete should already be cleared by this point */ + store->memStore->closeStore(store->memStore, 0); + RegCloseKey(store->key); + DeleteCriticalSection(&store->cs); + CryptMemFree(store); +} + +static BOOL WINAPI CRYPT_RegAddCert(HCERTSTORE hCertStore, PCCERT_CONTEXT cert, + DWORD dwAddDisposition) +{ + PWINE_REGSTORE store = (PWINE_REGSTORE)hCertStore; + BOOL ret; + + TRACE("(%p, %p, %ld)\n", hCertStore, cert, dwAddDisposition); + + if (store->hdr.dwOpenFlags & CERT_STORE_READONLY_FLAG) + { + SetLastError(ERROR_ACCESS_DENIED); + ret = FALSE; + } + else + { + ret = store->memStore->addCert(store->memStore, cert, dwAddDisposition); + if (ret) + store->dirty = TRUE; + } + return ret; +} + +static PWINE_CERT_CONTEXT_REF CRYPT_RegCreateCertRef( + PWINE_CERT_CONTEXT context, HCERTSTORE store) +{ + PWINE_REG_CERT_CONTEXT ret = CryptMemAlloc( + sizeof(WINE_REG_CERT_CONTEXT)); + + if (ret) + { + CRYPT_InitCertRef((PWINE_CERT_CONTEXT_REF)ret, context, store); + ret->childContext = NULL; + } + return (PWINE_CERT_CONTEXT_REF)ret; +} + +static PWINE_CERT_CONTEXT_REF CRYPT_RegEnumCert(PWINECRYPT_CERTSTORE store, + PWINE_CERT_CONTEXT_REF pPrev) +{ + PWINE_REGSTORE rs = (PWINE_REGSTORE)store; + PWINE_CERT_CONTEXT_REF child; + PWINE_REG_CERT_CONTEXT prev = (PWINE_REG_CERT_CONTEXT)pPrev, ret = NULL; + + TRACE("(%p, %p)\n", store, pPrev); + + if (pPrev) + { + child = rs->memStore->enumCert(rs->memStore, prev->childContext); + if (child) + { + ret = (PWINE_REG_CERT_CONTEXT)pPrev; + memcpy(&ret->cert, child, sizeof(WINE_CERT_CONTEXT_REF)); + ret->cert.cert.hCertStore = (HCERTSTORE)store; + ret->childContext = child; + } + } + else + { + child = rs->memStore->enumCert(rs->memStore, NULL); + if (child) + { + ret = CryptMemAlloc(sizeof(WINE_REG_CERT_CONTEXT)); + + if (ret) + { + memcpy(&ret->cert, child, sizeof(WINE_CERT_CONTEXT_REF)); + ret->cert.cert.hCertStore = (HCERTSTORE)store; + ret->childContext = child; + } + else + CertFreeCertificateContext((PCCERT_CONTEXT)child); + } + } + return (PWINE_CERT_CONTEXT_REF)ret; +} + +static BOOL WINAPI CRYPT_RegDeleteCert(HCERTSTORE hCertStore, + PCCERT_CONTEXT pCertContext, DWORD dwFlags) +{ + PWINE_REGSTORE store = (PWINE_REGSTORE)hCertStore; + BOOL ret; + + TRACE("(%p, %p, %08lx)\n", store, pCertContext, dwFlags); + + if (store->hdr.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 = CertGetCertificateContextProperty(pCertContext, + CERT_HASH_PROP_ID, toDelete->hash, &size); + if (ret) + { + list_init(&toDelete->entry); + EnterCriticalSection(&store->cs); + list_add_tail(&store->certsToDelete, &toDelete->entry); + LeaveCriticalSection(&store->cs); + ret = store->memStore->deleteCert(store->memStore, pCertContext, + dwFlags); + } + else + CryptMemFree(toDelete); + } + else + ret = FALSE; + if (ret) + store->dirty = TRUE; + } + return ret; +} + +static void CRYPT_RegFreeCert(PWINE_CERT_CONTEXT_REF ref) +{ + PWINE_REG_CERT_CONTEXT context = (PWINE_REG_CERT_CONTEXT)ref; + + TRACE("(%p)\n", ref); + + if (context->childContext) + CertFreeCertificateContext((PCCERT_CONTEXT)context->childContext); +} + +static BOOL WINAPI CRYPT_RegControl(HCERTSTORE hCertStore, DWORD dwFlags, + DWORD dwCtrlType, void const *pvCtrlPara) +{ + PWINE_REGSTORE store = (PWINE_REGSTORE)hCertStore; + BOOL ret; + + switch (dwCtrlType) + { + case CERT_STORE_CTRL_RESYNC: + CRYPT_RegFlushStore(store, FALSE); + store->memStore->closeStore(store->memStore, 0); + store->memStore = CRYPT_MemOpenStore(store->hdr.cryptProv, + store->hdr.dwOpenFlags, NULL); + if (store->memStore) + { + CRYPT_RegReadFromReg(store); + ret = TRUE; + } + else + ret = FALSE; + break; + case CERT_STORE_CTRL_COMMIT: + ret = CRYPT_RegFlushStore(store, + dwFlags & CERT_STORE_CTRL_COMMIT_FORCE_FLAG); + break; + default: + FIXME("%ld: stub\n", dwCtrlType); + ret = FALSE; + } + return ret; +} + +/* Copied from shlwapi's SHDeleteKeyW, and reformatted to match this file. */ +static DWORD CRYPT_RecurseDeleteKey(HKEY hKey, LPCWSTR lpszSubKey) +{ + DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i; + WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf; + HKEY hSubKey = 0; + + TRACE("(hkey=%p,%s)\n", hKey, debugstr_w(lpszSubKey)); + + dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey); + if (!dwRet) + { + /* Find how many subkeys there are */ + dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount, + &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL); + if (!dwRet) + { + dwMaxSubkeyLen++; + if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR)) + { + /* Name too big: alloc a buffer for it */ + lpszName = CryptMemAlloc(dwMaxSubkeyLen*sizeof(WCHAR)); + } + + if (!lpszName) + dwRet = ERROR_NOT_ENOUGH_MEMORY; + else + { + /* Recursively delete all the subkeys */ + for (i = 0; i < dwKeyCount && !dwRet; i++) + { + dwSize = dwMaxSubkeyLen; + dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL, + NULL, NULL, NULL); + if (!dwRet) + dwRet = CRYPT_RecurseDeleteKey(hSubKey, lpszName); + } + + if (lpszName != szNameBuf) + { + /* Free buffer if allocated */ + CryptMemFree(lpszName); + } + } + } + + RegCloseKey(hSubKey); + if (!dwRet) + dwRet = RegDeleteKeyW(hKey, lpszSubKey); + } + return dwRet; +} + +static WINECRYPT_CERTSTORE *CRYPT_RegOpenStore(HCRYPTPROV hCryptProv, + DWORD dwFlags, const void *pvPara) +{ + PWINE_REGSTORE store = NULL; + + TRACE("(%ld, %08lx, %p)\n", hCryptProv, dwFlags, pvPara); + + if (dwFlags & CERT_STORE_DELETE_FLAG) + { + DWORD rc = CRYPT_RecurseDeleteKey((HKEY)pvPara, CertsW); + + if (rc == ERROR_SUCCESS || rc == ERROR_NO_MORE_ITEMS) + rc = CRYPT_RecurseDeleteKey((HKEY)pvPara, CRLsW); + if (rc == ERROR_SUCCESS || rc == ERROR_NO_MORE_ITEMS) + rc = CRYPT_RecurseDeleteKey((HKEY)pvPara, CTLsW); + if (rc == ERROR_NO_MORE_ITEMS) + rc = ERROR_SUCCESS; + SetLastError(rc); + } + else + { + HKEY key; + + if (DuplicateHandle(GetCurrentProcess(), (HANDLE)pvPara, + GetCurrentProcess(), (LPHANDLE)&key, + dwFlags & CERT_STORE_READONLY_FLAG ? KEY_READ : KEY_ALL_ACCESS, + TRUE, 0)) + { + PWINECRYPT_CERTSTORE memStore; + + memStore = CRYPT_MemOpenStore(hCryptProv, dwFlags, NULL); + if (memStore) + { + store = CryptMemAlloc(sizeof(WINE_REGSTORE)); + if (store) + { + memset(store, 0, sizeof(WINE_REGSTORE)); + CRYPT_InitStore(&store->hdr, hCryptProv, dwFlags, + StoreTypeReg); + store->hdr.closeStore = CRYPT_RegCloseStore; + store->hdr.addCert = CRYPT_RegAddCert; + store->hdr.createCertRef = CRYPT_RegCreateCertRef; + store->hdr.enumCert = CRYPT_RegEnumCert; + store->hdr.deleteCert = CRYPT_RegDeleteCert; + store->hdr.freeCert = CRYPT_RegFreeCert; + store->hdr.control = CRYPT_RegControl; + store->memStore = memStore; + store->key = key; + InitializeCriticalSection(&store->cs); + list_init(&store->certsToDelete); + CRYPT_RegReadFromReg(store); + store->dirty = FALSE; + } + } + } + } + TRACE("returning %p\n", store); + return (WINECRYPT_CERTSTORE *)store; +} + +/* FIXME: this isn't complete for the Root store, in which the top-level + * self-signed CA certs reside. Adding a cert to the Root store should present + * the user with a dialog indicating the consequences of doing so, and asking + * the user to confirm whether the cert should be added. + */ +static PWINECRYPT_CERTSTORE CRYPT_SysRegOpenStoreW(HCRYPTPROV hCryptProv, + DWORD dwFlags, const void *pvPara) +{ + static const WCHAR fmt[] = { '%','s','\\','%','s',0 }; + LPCWSTR storeName = (LPCWSTR)pvPara; + LPWSTR storePath; + PWINECRYPT_CERTSTORE store = NULL; + HKEY root; + LPCWSTR base; + BOOL ret; + + TRACE("(%ld, %08lx, %s)\n", hCryptProv, dwFlags, + debugstr_w((LPCWSTR)pvPara)); + + if (!pvPara) + { + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return NULL; + } + + ret = TRUE; + switch (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK) + { + case CERT_SYSTEM_STORE_LOCAL_MACHINE: + root = HKEY_LOCAL_MACHINE; + base = CERT_LOCAL_MACHINE_SYSTEM_STORE_REGPATH; + break; + case CERT_SYSTEM_STORE_CURRENT_USER: + root = HKEY_CURRENT_USER; + base = CERT_LOCAL_MACHINE_SYSTEM_STORE_REGPATH; + break; + case CERT_SYSTEM_STORE_CURRENT_SERVICE: + /* hklm\Software\Microsoft\Cryptography\Services\servicename\ + * SystemCertificates + */ + FIXME("CERT_SYSTEM_STORE_CURRENT_SERVICE, %s: stub\n", + debugstr_w(storeName)); + return NULL; + case CERT_SYSTEM_STORE_SERVICES: + /* hklm\Software\Microsoft\Cryptography\Services\servicename\ + * SystemCertificates + */ + FIXME("CERT_SYSTEM_STORE_SERVICES, %s: stub\n", + debugstr_w(storeName)); + return NULL; + case CERT_SYSTEM_STORE_USERS: + /* hku\user sid\Software\Microsoft\SystemCertificates */ + FIXME("CERT_SYSTEM_STORE_USERS, %s: stub\n", + debugstr_w(storeName)); + return NULL; + case CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY: + root = HKEY_CURRENT_USER; + base = CERT_GROUP_POLICY_SYSTEM_STORE_REGPATH; + break; + case CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY: + root = HKEY_LOCAL_MACHINE; + base = CERT_GROUP_POLICY_SYSTEM_STORE_REGPATH; + break; + case CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE: + /* hklm\Software\Microsoft\EnterpriseCertificates */ + FIXME("CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE, %s: stub\n", + debugstr_w(storeName)); + return NULL; + default: + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return NULL; + } + + storePath = CryptMemAlloc((lstrlenW(base) + lstrlenW(storeName) + 2) * + sizeof(WCHAR)); + if (storePath) + { + LONG rc; + HKEY key; + REGSAM sam = dwFlags & CERT_STORE_READONLY_FLAG ? KEY_READ : + KEY_ALL_ACCESS; + + wsprintfW(storePath, fmt, base, storeName); + if (dwFlags & CERT_STORE_OPEN_EXISTING_FLAG) + rc = RegOpenKeyExW(root, storePath, 0, sam, &key); + else + { + DWORD disp; + + rc = RegCreateKeyExW(root, storePath, 0, NULL, 0, sam, NULL, + &key, &disp); + if (!rc && dwFlags & CERT_STORE_CREATE_NEW_FLAG && + disp == REG_OPENED_EXISTING_KEY) + { + RegCloseKey(key); + rc = ERROR_FILE_EXISTS; + } + } + if (!rc) + { + store = CRYPT_RegOpenStore(hCryptProv, dwFlags, key); + RegCloseKey(key); + } + else + SetLastError(rc); + CryptMemFree(storePath); + } + return store; +} + +static PWINECRYPT_CERTSTORE CRYPT_SysRegOpenStoreA(HCRYPTPROV hCryptProv, + DWORD dwFlags, const void *pvPara) +{ + int len; + PWINECRYPT_CERTSTORE ret = NULL; + + TRACE("(%ld, %08lx, %s)\n", hCryptProv, dwFlags, + debugstr_a((LPCSTR)pvPara)); + + if (!pvPara) + { + SetLastError(ERROR_FILE_NOT_FOUND); + return NULL; + } + len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, NULL, 0); + if (len) + { + LPWSTR storeName = CryptMemAlloc(len * sizeof(WCHAR)); + + if (storeName) + { + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, storeName, len); + ret = CRYPT_SysRegOpenStoreW(hCryptProv, dwFlags, storeName); + CryptMemFree(storeName); + } + } + return ret; +} + +static PWINECRYPT_CERTSTORE CRYPT_SysOpenStoreW(HCRYPTPROV hCryptProv, + DWORD dwFlags, const void *pvPara) +{ + HCERTSTORE store = 0; + BOOL ret; + + TRACE("(%ld, %08lx, %s)\n", hCryptProv, dwFlags, + debugstr_w((LPCWSTR)pvPara)); + + if (!pvPara) + { + SetLastError(ERROR_FILE_NOT_FOUND); + return NULL; + } + /* This returns a different error than system registry stores if the + * location is invalid. + */ + switch (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK) + { + case CERT_SYSTEM_STORE_LOCAL_MACHINE: + case CERT_SYSTEM_STORE_CURRENT_USER: + case CERT_SYSTEM_STORE_CURRENT_SERVICE: + case CERT_SYSTEM_STORE_SERVICES: + case CERT_SYSTEM_STORE_USERS: + case CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY: + case CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY: + case CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE: + ret = TRUE; + break; + default: + SetLastError(ERROR_FILE_NOT_FOUND); + ret = FALSE; + } + if (ret) + { + HCERTSTORE regStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY_W, + 0, hCryptProv, dwFlags, pvPara); + + if (regStore) + { + store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0, + CERT_STORE_CREATE_NEW_FLAG, NULL); + if (store) + { + CertAddStoreToCollection(store, regStore, + dwFlags & CERT_STORE_READONLY_FLAG ? 0 : + CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0); + CertCloseStore(regStore, 0); + } + } + } + return (PWINECRYPT_CERTSTORE)store; +} + +static PWINECRYPT_CERTSTORE CRYPT_SysOpenStoreA(HCRYPTPROV hCryptProv, + DWORD dwFlags, const void *pvPara) +{ + int len; + PWINECRYPT_CERTSTORE ret = NULL; + + TRACE("(%ld, %08lx, %s)\n", hCryptProv, dwFlags, + debugstr_a((LPCSTR)pvPara)); + + if (!pvPara) + { + SetLastError(ERROR_FILE_NOT_FOUND); + return NULL; + } + len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, NULL, 0); + if (len) + { + LPWSTR storeName = CryptMemAlloc(len * sizeof(WCHAR)); + + if (storeName) + { + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, storeName, len); + ret = CRYPT_SysOpenStoreW(hCryptProv, dwFlags, storeName); + CryptMemFree(storeName); + } + } + return ret; +} + +HCERTSTORE WINAPI CertOpenStore(LPCSTR lpszStoreProvider, + DWORD dwMsgAndCertEncodingType, HCRYPTPROV hCryptProv, DWORD dwFlags, + const void* pvPara) +{ + WINECRYPT_CERTSTORE *hcs; + StoreOpenFunc openFunc = NULL; + + TRACE("(%s, %08lx, %08lx, %08lx, %p)\n", debugstr_a(lpszStoreProvider), + dwMsgAndCertEncodingType, hCryptProv, dwFlags, pvPara); + + if (!HIWORD(lpszStoreProvider)) + { + switch (LOWORD(lpszStoreProvider)) + { + case (int)CERT_STORE_PROV_MEMORY: + openFunc = CRYPT_MemOpenStore; + break; + case (int)CERT_STORE_PROV_REG: + openFunc = CRYPT_RegOpenStore; + break; + case (int)CERT_STORE_PROV_COLLECTION: + openFunc = CRYPT_CollectionOpenStore; + break; + case (int)CERT_STORE_PROV_SYSTEM_A: + openFunc = CRYPT_SysOpenStoreA; + break; + case (int)CERT_STORE_PROV_SYSTEM_W: + openFunc = CRYPT_SysOpenStoreW; + break; + case (int)CERT_STORE_PROV_SYSTEM_REGISTRY_A: + openFunc = CRYPT_SysRegOpenStoreA; + break; + case (int)CERT_STORE_PROV_SYSTEM_REGISTRY_W: + openFunc = CRYPT_SysRegOpenStoreW; + break; + default: + if (LOWORD(lpszStoreProvider)) + FIXME("unimplemented type %d\n", LOWORD(lpszStoreProvider)); + } + } + else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_MEMORY)) + openFunc = CRYPT_MemOpenStore; + else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_SYSTEM)) + openFunc = CRYPT_SysOpenStoreW; + else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_COLLECTION)) + openFunc = CRYPT_CollectionOpenStore; + else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_SYSTEM_REGISTRY)) + openFunc = CRYPT_SysRegOpenStoreW; + else + { + FIXME("unimplemented type %s\n", lpszStoreProvider); + openFunc = NULL; + } + + if (!openFunc) + { + /* FIXME: need to look for an installed provider for this type */ + SetLastError(ERROR_FILE_NOT_FOUND); + hcs = NULL; + } + else + hcs = openFunc(hCryptProv, dwFlags, pvPara); + return (HCERTSTORE)hcs; +} + +HCERTSTORE WINAPI CertOpenSystemStoreA(HCRYPTPROV hProv, + LPCSTR szSubSystemProtocol) +{ + HCERTSTORE ret = 0; + + if (szSubSystemProtocol) + { + int len = MultiByteToWideChar(CP_ACP, 0, szSubSystemProtocol, -1, NULL, + 0); + LPWSTR param = CryptMemAlloc(len * sizeof(WCHAR)); + + if (param) + { + MultiByteToWideChar(CP_ACP, 0, szSubSystemProtocol, -1, param, len); + ret = CertOpenSystemStoreW(hProv, param); + CryptMemFree(param); + } + } + else + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return ret; +} + +HCERTSTORE WINAPI CertOpenSystemStoreW(HCRYPTPROV hProv, + LPCWSTR szSubSystemProtocol) +{ + HCERTSTORE ret; + + if (!szSubSystemProtocol) + { + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return 0; + } + + /* FIXME: needs some tests. It seems to open both HKEY_LOCAL_MACHINE and + * HKEY_CURRENT_USER stores, but I'm not sure under what conditions, if any, + * it fails. + */ + ret = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, hProv, + CERT_STORE_CREATE_NEW_FLAG, NULL); + if (ret) + { + HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY_W, + 0, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE, szSubSystemProtocol); + + if (store) + { + CertAddStoreToCollection(ret, store, + CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0); + CertCloseStore(store, 0); + } + store = CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY_W, + 0, hProv, CERT_SYSTEM_STORE_CURRENT_USER, szSubSystemProtocol); + if (store) + { + CertAddStoreToCollection(ret, store, + CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0); + CertCloseStore(store, 0); + } + } + return ret; +} + +BOOL WINAPI CertSaveStore(HCERTSTORE hCertStore, DWORD dwMsgAndCertEncodingType, + DWORD dwSaveAs, DWORD dwSaveTo, void* pvSaveToPara, DWORD dwFlags) +{ + FIXME("(%p,%ld,%ld,%ld,%p,%08lx) stub!\n", hCertStore, + dwMsgAndCertEncodingType, dwSaveAs, dwSaveTo, pvSaveToPara, dwFlags); + 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; +} + +/* Decodes the encoded certificate and creates the certificate context for it. + * The reference count is initially zero, so you must create a reference to it + * to avoid leaking memory. + */ +static PWINE_CERT_CONTEXT CRYPT_CreateCertificateContext( + DWORD dwCertEncodingType, const BYTE *pbCertEncoded, DWORD cbCertEncoded) +{ + PWINE_CERT_CONTEXT cert = NULL; + BOOL ret; + PCERT_SIGNED_CONTENT_INFO signedCert = NULL; + PCERT_INFO certInfo = NULL; + DWORD size = 0; + + TRACE("(%08lx, %p, %ld)\n", dwCertEncodingType, pbCertEncoded, + cbCertEncoded); + + /* First try to decode it as a signed cert. */ + ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT, pbCertEncoded, + cbCertEncoded, CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, + (BYTE *)&signedCert, &size); + if (ret) + { + size = 0; + ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, + signedCert->ToBeSigned.pbData, signedCert->ToBeSigned.cbData, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, + (BYTE *)&certInfo, &size); + LocalFree(signedCert); + } + /* Failing that, try it as an unsigned cert */ + if (!ret) + { + size = 0; + ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, + pbCertEncoded, cbCertEncoded, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, + (BYTE *)&certInfo, &size); + } + if (ret) + { + BYTE *data = NULL; + + cert = CryptMemAlloc(sizeof(WINE_CERT_CONTEXT)); + if (!cert) + goto end; + data = CryptMemAlloc(cbCertEncoded); + if (!data) + { + CryptMemFree(cert); + cert = NULL; + goto end; + } + memcpy(data, pbCertEncoded, cbCertEncoded); + cert->cert.dwCertEncodingType = dwCertEncodingType; + cert->cert.pbCertEncoded = data; + cert->cert.cbCertEncoded = cbCertEncoded; + cert->cert.pCertInfo = certInfo; + cert->cert.hCertStore = 0; + cert->ref = 0; + InitializeCriticalSection(&cert->cs); + list_init(&cert->extendedProperties); + } + +end: + return cert; +} + +static void CRYPT_FreeCert(PWINE_CERT_CONTEXT context) +{ + PWINE_CERT_PROPERTY prop, next; + + CryptMemFree(context->cert.pbCertEncoded); + LocalFree(context->cert.pCertInfo); + DeleteCriticalSection(&context->cs); + LIST_FOR_EACH_ENTRY_SAFE(prop, next, &context->extendedProperties, + WINE_CERT_PROPERTY, entry) + { + list_remove(&prop->entry); + CryptMemFree(prop->pbData); + CryptMemFree(prop); + } + CryptMemFree(context); +} + +PCCERT_CONTEXT WINAPI CertCreateCertificateContext(DWORD dwCertEncodingType, + const BYTE *pbCertEncoded, DWORD cbCertEncoded) +{ + PWINE_CERT_CONTEXT cert; + PWINE_CERT_CONTEXT_REF ret = NULL; + + TRACE("(%08lx, %p, %ld)\n", dwCertEncodingType, pbCertEncoded, + cbCertEncoded); + + cert = CRYPT_CreateCertificateContext(dwCertEncodingType, pbCertEncoded, + cbCertEncoded); + if (cert) + ret = CRYPT_CreateCertRef(cert, 0); + return (PCCERT_CONTEXT)ret; +} + +/* Since the properties are stored in a list, this is a tad inefficient + * (O(n^2)) since I have to find the previous position every time. + */ +DWORD WINAPI CertEnumCertificateContextProperties(PCCERT_CONTEXT pCertContext, + DWORD dwPropId) +{ + PWINE_CERT_CONTEXT_REF ref = (PWINE_CERT_CONTEXT_REF)pCertContext; + DWORD ret; + + TRACE("(%p, %ld)\n", pCertContext, dwPropId); + + EnterCriticalSection(&ref->context->cs); + if (dwPropId) + { + PWINE_CERT_PROPERTY cursor = NULL; + + LIST_FOR_EACH_ENTRY(cursor, &ref->context->extendedProperties, + WINE_CERT_PROPERTY, entry) + { + if (cursor->hdr.propID == dwPropId) + break; + } + if (cursor) + { + if (cursor->entry.next != &ref->context->extendedProperties) + ret = LIST_ENTRY(cursor->entry.next, WINE_CERT_PROPERTY, + entry)->hdr.propID; + else + ret = 0; + } + else + ret = 0; + } + else if (!list_empty(&ref->context->extendedProperties)) + ret = LIST_ENTRY(ref->context->extendedProperties.next, + WINE_CERT_PROPERTY, entry)->hdr.propID; + else + ret = 0; + LeaveCriticalSection(&ref->context->cs); + return ret; +} + +static BOOL WINAPI CRYPT_GetCertificateContextProperty( + PWINE_CERT_CONTEXT context, DWORD dwPropId, void *pvData, DWORD *pcbData) +{ + PWINE_CERT_PROPERTY prop; + BOOL ret, found; + + TRACE("(%p, %ld, %p, %p)\n", context, dwPropId, pvData, pcbData); + + EnterCriticalSection(&context->cs); + ret = FALSE; + found = FALSE; + LIST_FOR_EACH_ENTRY(prop, &context->extendedProperties, + WINE_CERT_PROPERTY, entry) + { + if (prop->hdr.propID == dwPropId) + { + if (!pvData) + { + *pcbData = prop->hdr.cb; + ret = TRUE; + } + else if (*pcbData < prop->hdr.cb) + { + SetLastError(ERROR_MORE_DATA); + *pcbData = prop->hdr.cb; + } + else + { + memcpy(pvData, prop->pbData, prop->hdr.cb); + *pcbData = prop->hdr.cb; + ret = TRUE; + } + found = TRUE; + } + break; + } + if (!found) + { + /* Implicit properties */ + switch (dwPropId) + { + case CERT_SHA1_HASH_PROP_ID: + ret = CryptHashCertificate(0, CALG_SHA1, 0, + context->cert.pbCertEncoded, context->cert.cbCertEncoded, pvData, + pcbData); + if (ret) + { + CRYPT_DATA_BLOB blob = { *pcbData, pvData }; + + ret = CRYPT_SetCertificateContextProperty(context, dwPropId, + 0, &blob); + } + break; + case CERT_KEY_PROV_INFO_PROP_ID: + case CERT_MD5_HASH_PROP_ID: + case CERT_SIGNATURE_HASH_PROP_ID: + case CERT_KEY_IDENTIFIER_PROP_ID: + case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID: + case CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID: + case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID: + FIXME("implicit property %ld\n", dwPropId); + break; + } + } + LeaveCriticalSection(&context->cs); + TRACE("returning %d\n", ret); + return ret; +} + +BOOL WINAPI CertGetCertificateContextProperty(PCCERT_CONTEXT pCertContext, + DWORD dwPropId, void *pvData, DWORD *pcbData) +{ + PWINE_CERT_CONTEXT_REF ref = (PWINE_CERT_CONTEXT_REF)pCertContext; + BOOL ret; + + TRACE("(%p, %ld, %p, %p)\n", pCertContext, dwPropId, pvData, pcbData); + + /* Special cases for invalid/special prop IDs. + */ + switch (dwPropId) + { + case 0: + case CERT_CERT_PROP_ID: + case CERT_CRL_PROP_ID: + case CERT_CTL_PROP_ID: + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return FALSE; + case CERT_ACCESS_STATE_PROP_ID: + if (!pvData) + { + *pcbData = sizeof(DWORD); + return TRUE; + } + else if (*pcbData < sizeof(DWORD)) + { + SetLastError(ERROR_MORE_DATA); + *pcbData = sizeof(DWORD); + return FALSE; + } + else + { + DWORD state = 0; + + if (pCertContext->hCertStore) + { + PWINECRYPT_CERTSTORE store = + (PWINECRYPT_CERTSTORE)pCertContext->hCertStore; + + /* Take advantage of knowledge of the stores to answer the + * access state question + */ + if (store->type != StoreTypeReg || + !(store->dwOpenFlags & CERT_STORE_READONLY_FLAG)) + state |= CERT_ACCESS_STATE_WRITE_PERSIST_FLAG; + } + *(DWORD *)pvData = state; + return TRUE; + } + } + + ret = CRYPT_GetCertificateContextProperty(ref->context, dwPropId, + pvData, pcbData); + TRACE("returning %d\n", ret); + return ret; +} + +/* Copies cbData bytes from pbData to the context's property with ID + * dwPropId. + */ +static BOOL CRYPT_SaveCertificateContextProperty(PWINE_CERT_CONTEXT context, + DWORD dwPropId, const BYTE *pbData, size_t cbData) +{ + BOOL ret = FALSE; + LPBYTE data; + + if (cbData) + { + data = CryptMemAlloc(cbData); + if (data) + memcpy(data, pbData, cbData); + } + else + data = NULL; + if (!cbData || data) + { + PWINE_CERT_PROPERTY prop; + + EnterCriticalSection(&context->cs); + LIST_FOR_EACH_ENTRY(prop, &context->extendedProperties, + WINE_CERT_PROPERTY, entry) + { + if (prop->hdr.propID == dwPropId) + break; + } + if (prop && prop->entry.next != &context->extendedProperties) + { + CryptMemFree(prop->pbData); + prop->hdr.cb = cbData; + prop->pbData = cbData ? data : NULL; + ret = TRUE; + } + else + { + prop = CryptMemAlloc(sizeof(WINE_CERT_PROPERTY)); + if (prop) + { + prop->hdr.propID = dwPropId; + prop->hdr.unknown = 1; + prop->hdr.cb = cbData; + list_init(&prop->entry); + prop->pbData = cbData ? data : NULL; + list_add_tail(&context->extendedProperties, &prop->entry); + ret = TRUE; + } + else + CryptMemFree(data); + } + LeaveCriticalSection(&context->cs); + } + return ret; +} + +static BOOL WINAPI CRYPT_SetCertificateContextProperty( + PWINE_CERT_CONTEXT context, DWORD dwPropId, DWORD dwFlags, const void *pvData) +{ + BOOL ret = FALSE; + + TRACE("(%p, %ld, %08lx, %p)\n", context, dwPropId, dwFlags, pvData); + + if (!pvData) + { + PWINE_CERT_PROPERTY prop, next; + + EnterCriticalSection(&context->cs); + LIST_FOR_EACH_ENTRY_SAFE(prop, next, &context->extendedProperties, + WINE_CERT_PROPERTY, entry) + { + if (prop->hdr.propID == dwPropId) + { + list_remove(&prop->entry); + CryptMemFree(prop->pbData); + CryptMemFree(prop); + } + } + LeaveCriticalSection(&context->cs); + ret = TRUE; + } + else + { + switch (dwPropId) + { + case CERT_AUTO_ENROLL_PROP_ID: + case CERT_CTL_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_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 = CRYPT_SaveCertificateContextProperty(context, dwPropId, + blob->pbData, blob->cbData); + break; + } + case CERT_DATE_STAMP_PROP_ID: + ret = CRYPT_SaveCertificateContextProperty(context, dwPropId, + pvData, sizeof(FILETIME)); + break; + default: + FIXME("%ld: stub\n", dwPropId); + } + } + TRACE("returning %d\n", ret); + return ret; +} + +BOOL WINAPI CertSetCertificateContextProperty(PCCERT_CONTEXT pCertContext, + DWORD dwPropId, DWORD dwFlags, const void *pvData) +{ + PWINE_CERT_CONTEXT_REF ref = (PWINE_CERT_CONTEXT_REF)pCertContext; + BOOL ret; + + TRACE("(%p, %ld, %08lx, %p)\n", pCertContext, 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(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return FALSE; + } + ret = CRYPT_SetCertificateContextProperty(ref->context, dwPropId, + dwFlags, pvData); + TRACE("returning %d\n", ret); + return ret; +} + +/* Only the reference portion of the context is duplicated. The returned + * context has the cert store set to 0, to prevent the store's certificate free + * function from getting called on partial data. + * FIXME: is this okay? Needs a test. + */ +PCCERT_CONTEXT WINAPI CertDuplicateCertificateContext( + PCCERT_CONTEXT pCertContext) +{ + PWINE_CERT_CONTEXT_REF ref = (PWINE_CERT_CONTEXT_REF)pCertContext, ret; + + TRACE("(%p)\n", pCertContext); + if (ref) + { + ret = CryptMemAlloc(sizeof(WINE_CERT_CONTEXT_REF)); + if (ret) + { + memcpy(ret, ref, sizeof(*ret)); + ret->cert.hCertStore = 0; + InterlockedIncrement(&ret->context->ref); + } + } + else + ret = NULL; + return (PCCERT_CONTEXT)ret; +} + +BOOL WINAPI CertAddCertificateContextToStore(HCERTSTORE hCertStore, + PCCERT_CONTEXT pCertContext, DWORD dwAddDisposition, + PCCERT_CONTEXT *ppStoreContext) +{ + PWINECRYPT_CERTSTORE store = (PWINECRYPT_CERTSTORE)hCertStore; + PWINE_CERT_CONTEXT_REF ref = (PWINE_CERT_CONTEXT_REF)pCertContext; + PWINE_CERT_CONTEXT cert; + BOOL ret; + + TRACE("(%p, %p, %08lx, %p)\n", hCertStore, pCertContext, + dwAddDisposition, ppStoreContext); + + /* FIXME: some tests needed to verify return codes */ + if (!store) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + if (store->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + cert = CRYPT_CreateCertificateContext(ref->context->cert.dwCertEncodingType, + ref->context->cert.pbCertEncoded, ref->context->cert.cbCertEncoded); + if (cert) + { + PWINE_CERT_PROPERTY prop; + + ret = TRUE; + EnterCriticalSection(&ref->context->cs); + LIST_FOR_EACH_ENTRY(prop, &ref->context->extendedProperties, + WINE_CERT_PROPERTY, entry) + { + ret = CRYPT_SaveCertificateContextProperty(cert, prop->hdr.propID, + prop->pbData, prop->hdr.cb); + if (!ret) + break; + } + LeaveCriticalSection(&ref->context->cs); + if (ret) + { + ret = store->addCert(store, (PCCERT_CONTEXT)cert, dwAddDisposition); + if (ret && ppStoreContext) + *ppStoreContext = (PCCERT_CONTEXT)store->createCertRef(cert, + hCertStore); + } + if (!ret) + CRYPT_FreeCert(cert); + } + else + ret = FALSE; + return ret; +} + +BOOL WINAPI CertAddEncodedCertificateToStore(HCERTSTORE hCertStore, + DWORD dwCertEncodingType, const BYTE *pbCertEncoded, DWORD cbCertEncoded, + DWORD dwAddDisposition, PCCERT_CONTEXT *ppCertContext) +{ + WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *)hCertStore; + BOOL ret; + + TRACE("(%p, %08lx, %p, %ld, %08lx, %p)\n", hCertStore, dwCertEncodingType, + pbCertEncoded, cbCertEncoded, dwAddDisposition, ppCertContext); + + if (!hcs) + ret = FALSE; + else if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) + ret = FALSE; + else + { + PWINE_CERT_CONTEXT cert = CRYPT_CreateCertificateContext( + dwCertEncodingType, pbCertEncoded, cbCertEncoded); + + if (cert) + { + ret = hcs->addCert(hcs, (PCCERT_CONTEXT)cert, dwAddDisposition); + if (ret && ppCertContext) + *ppCertContext = (PCCERT_CONTEXT)hcs->createCertRef(cert, + hCertStore); + if (!ret) + CRYPT_FreeCert(cert); + } + else + ret = FALSE; + } + return ret; +} + +PCCERT_CONTEXT WINAPI CertEnumCertificatesInStore(HCERTSTORE hCertStore, + PCCERT_CONTEXT pPrev) +{ + WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *)hCertStore; + PWINE_CERT_CONTEXT_REF prev = (PWINE_CERT_CONTEXT_REF)pPrev; + PCCERT_CONTEXT ret; + + TRACE("(%p, %p)\n", hCertStore, pPrev); + if (!hCertStore) + ret = NULL; + else if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) + ret = NULL; + else + ret = (PCCERT_CONTEXT)hcs->enumCert(hcs, prev); + return ret; +} + +BOOL WINAPI CertDeleteCertificateFromStore(PCCERT_CONTEXT pCertContext) +{ + BOOL ret; + + TRACE("(%p)\n", pCertContext); + + if (!pCertContext) + ret = TRUE; + else if (!pCertContext->hCertStore) + { + ret = TRUE; + CertFreeCertificateContext(pCertContext); + } + else + { + PWINECRYPT_CERTSTORE hcs = + (PWINECRYPT_CERTSTORE)pCertContext->hCertStore; + + if (!hcs) + ret = TRUE; + else if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) + ret = FALSE; + else + { + ret = hcs->deleteCert(hcs, pCertContext, 0); + CertFreeCertificateContext(pCertContext); + } + } + return ret; +} + +BOOL WINAPI CertAddEncodedCRLToStore(HCERTSTORE hCertStore, + DWORD dwCertEncodingType, const BYTE *pbCrlEncoded, DWORD cbCrlEncoded, + DWORD dwAddDisposition, PCCRL_CONTEXT *ppCrlContext) +{ + FIXME("(%p, %08lx, %p, %ld, %08lx, %p): stub\n", hCertStore, + dwCertEncodingType, pbCrlEncoded, cbCrlEncoded, dwAddDisposition, + ppCrlContext); + return FALSE; +} + +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; +} + +BOOL WINAPI CertFreeCRLContext( PCCRL_CONTEXT pCrlContext) +{ + FIXME("%p\n", pCrlContext ); + + return TRUE; +} + +BOOL WINAPI CertDeleteCRLFromStore(PCCRL_CONTEXT pCrlContext) +{ + FIXME("(%p): stub\n", pCrlContext); + return TRUE; +} + +PCCRL_CONTEXT WINAPI CertEnumCRLsInStore(HCERTSTORE hCertStore, + PCCRL_CONTEXT pPrev) +{ + FIXME("(%p, %p): stub\n", hCertStore, pPrev); + return NULL; +} + +PCCTL_CONTEXT WINAPI CertCreateCTLContext(DWORD dwCertEncodingType, + const BYTE* pbCtlEncoded, DWORD cbCtlEncoded) +{ + FIXME("(%08lx, %p, %08lx): stub\n", dwCertEncodingType, pbCtlEncoded, + cbCtlEncoded); + return NULL; +} + +BOOL WINAPI CertAddEncodedCTLToStore(HCERTSTORE hCertStore, + DWORD dwMsgAndCertEncodingType, const BYTE *pbCtlEncoded, DWORD cbCtlEncoded, + DWORD dwAddDisposition, PCCTL_CONTEXT *ppCtlContext) +{ + FIXME("(%p, %08lx, %p, %ld, %08lx, %p): stub\n", hCertStore, + dwMsgAndCertEncodingType, pbCtlEncoded, cbCtlEncoded, dwAddDisposition, + ppCtlContext); + return FALSE; +} + +BOOL WINAPI CertAddCTLContextToStore(HCERTSTORE hCertStore, + PCCTL_CONTEXT pCtlContext, DWORD dwAddDisposition, + PCCTL_CONTEXT* ppStoreContext) +{ + FIXME("(%p, %p, %08lx, %p): stub\n", hCertStore, pCtlContext, + dwAddDisposition, ppStoreContext); + return TRUE; +} + +BOOL WINAPI CertFreeCTLContext(PCCTL_CONTEXT pCtlContext) +{ + FIXME("(%p): stub\n", pCtlContext ); + return TRUE; +} + +BOOL WINAPI CertDeleteCTLFromStore(PCCTL_CONTEXT pCtlContext) +{ + FIXME("(%p): stub\n", pCtlContext); + return TRUE; +} + +PCCTL_CONTEXT WINAPI CertEnumCTLsInStore(HCERTSTORE hCertStore, + PCCTL_CONTEXT pPrev) +{ + FIXME("(%p, %p): stub\n", hCertStore, pPrev); + return NULL; +} + + +BOOL WINAPI CertCloseStore(HCERTSTORE hCertStore, DWORD dwFlags) +{ + WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *) hCertStore; + + TRACE("(%p, %08lx)\n", hCertStore, dwFlags); + + if( ! hCertStore ) + return TRUE; + + if ( hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC ) + return FALSE; + + if (InterlockedDecrement(&hcs->ref) == 0) + { + TRACE("%p's ref count is 0, freeing\n", hcs); + hcs->dwMagic = 0; + if (!(hcs->dwOpenFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG)) + CryptReleaseContext(hcs->cryptProv, 0); + hcs->closeStore(hcs, dwFlags); + } + else + TRACE("%p's ref count is %ld\n", hcs, hcs->ref); + return TRUE; +} + +BOOL WINAPI CertControlStore(HCERTSTORE hCertStore, DWORD dwFlags, + DWORD dwCtrlType, void const *pvCtrlPara) +{ + WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *)hCertStore; + BOOL ret; + + TRACE("(%p, %08lx, %ld, %p)\n", hCertStore, dwFlags, dwCtrlType, + pvCtrlPara); + + if (!hcs) + ret = FALSE; + else if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) + ret = FALSE; + else + { + if (hcs->control) + ret = hcs->control(hCertStore, dwFlags, dwCtrlType, pvCtrlPara); + else + ret = TRUE; + } + 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 CertSerializeCRLStoreElement(PCCRL_CONTEXT pCrlContext, + DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement) +{ + FIXME("(%p, %08lx, %p, %p): stub\n", pCrlContext, dwFlags, pbElement, + pcbElement); + return FALSE; +} + +BOOL WINAPI CertGetCTLContextProperty(PCCTL_CONTEXT pCTLContext, + DWORD dwPropId, void *pvData, DWORD *pcbData) +{ + FIXME("(%p, %ld, %p, %p): stub\n", pCTLContext, dwPropId, pvData, pcbData); + return FALSE; +} + +BOOL WINAPI CertSetCTLContextProperty(PCCTL_CONTEXT pCTLContext, + DWORD dwPropId, DWORD dwFlags, const void *pvData) +{ + FIXME("(%p, %ld, %08lx, %p): stub\n", pCTLContext, dwPropId, dwFlags, + pvData); + return FALSE; +} + +BOOL WINAPI CertSerializeCTLStoreElement(PCCTL_CONTEXT pCtlContext, + DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement) +{ + FIXME("(%p, %08lx, %p, %p): stub\n", pCtlContext, dwFlags, pbElement, + pcbElement); + return FALSE; +} + +BOOL WINAPI CertSerializeCertificateStoreElement(PCCERT_CONTEXT pCertContext, + DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement) +{ + BOOL ret; + + TRACE("(%p, %08lx, %p, %p)\n", pCertContext, dwFlags, pbElement, + pcbElement); + + if (pCertContext) + { + PWINE_CERT_CONTEXT_REF ref = (PWINE_CERT_CONTEXT_REF)pCertContext; + DWORD bytesNeeded = sizeof(WINE_CERT_PROP_HEADER) + + pCertContext->cbCertEncoded; + PWINE_CERT_PROPERTY prop; + + EnterCriticalSection(&ref->context->cs); + LIST_FOR_EACH_ENTRY(prop, &ref->context->extendedProperties, + WINE_CERT_PROPERTY, entry) + bytesNeeded += sizeof(WINE_CERT_PROP_HEADER) + prop->hdr.cb; + if (!pbElement) + { + *pcbElement = bytesNeeded; + ret = TRUE; + } + else if (*pcbElement < bytesNeeded) + { + *pcbElement = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + PWINE_CERT_PROP_HEADER hdr; + + LIST_FOR_EACH_ENTRY(prop, &ref->context->extendedProperties, + WINE_CERT_PROPERTY, entry) + { + memcpy(pbElement, &prop->hdr, sizeof(WINE_CERT_PROP_HEADER)); + pbElement += sizeof(WINE_CERT_PROP_HEADER); + if (prop->hdr.cb) + { + memcpy(pbElement, prop->pbData, prop->hdr.cb); + pbElement += prop->hdr.cb; + } + } + hdr = (PWINE_CERT_PROP_HEADER)pbElement; + hdr->propID = CERT_CERT_PROP_ID; + hdr->unknown = 1; + hdr->cb = pCertContext->cbCertEncoded; + memcpy(pbElement + sizeof(WINE_CERT_PROP_HEADER), + pCertContext->pbCertEncoded, pCertContext->cbCertEncoded); + ret = TRUE; + } + LeaveCriticalSection(&ref->context->cs); + } + else + ret = FALSE; + return ret; +} + +/* Looks for the property with ID propID in the buffer buf. Returns a pointer + * to its header if a valid header is found, NULL if not. Valid means the + * length of thte property won't overrun buf, and the unknown field is 1. + */ +static const WINE_CERT_PROP_HEADER *CRYPT_findPropID(const BYTE *buf, + DWORD size, DWORD propID) +{ + const WINE_CERT_PROP_HEADER *ret = NULL; + BOOL done = FALSE; + + while (size && !ret && !done) + { + if (size < sizeof(WINE_CERT_PROP_HEADER)) + { + SetLastError(CRYPT_E_FILE_ERROR); + done = TRUE; + } + else + { + const WINE_CERT_PROP_HEADER *hdr = + (const WINE_CERT_PROP_HEADER *)buf; + + size -= sizeof(WINE_CERT_PROP_HEADER); + buf += sizeof(WINE_CERT_PROP_HEADER); + if (size < hdr->cb) + { + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + done = TRUE; + } + else if (!hdr->propID) + { + /* assume a zero prop ID means the data are uninitialized, so + * stop looking. + */ + done = TRUE; + } + else if (hdr->unknown != 1) + { + SetLastError(ERROR_FILE_NOT_FOUND); + done = TRUE; + } + else if (hdr->propID == propID) + ret = hdr; + else + { + buf += hdr->cb; + size -= hdr->cb; + } + } + } + return ret; +} + +static const void * WINAPI CRYPT_ReadSerializedElement(const BYTE *pbElement, + DWORD cbElement, DWORD dwContextTypeFlags, DWORD *pdwContentType) +{ + const void *context; + + TRACE("(%p, %ld, %08lx, %p)\n", pbElement, cbElement, dwContextTypeFlags, + pdwContentType); + + if (!cbElement) + { + SetLastError(ERROR_END_OF_MEDIA); + return NULL; + } + + __TRY + { + const WINE_CONTEXT_INTERFACE *contextInterface = NULL; + const WINE_CERT_PROP_HEADER *hdr = NULL; + DWORD type = 0; + BOOL ret; + + ret = TRUE; + context = NULL; + if (dwContextTypeFlags == CERT_STORE_ALL_CONTEXT_FLAG) + { + hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CERT_PROP_ID); + if (hdr) + type = CERT_STORE_CERTIFICATE_CONTEXT; + else + { + hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CRL_PROP_ID); + if (hdr) + type = CERT_STORE_CRL_CONTEXT; + else + { + hdr = CRYPT_findPropID(pbElement, cbElement, + CERT_CTL_PROP_ID); + if (hdr) + type = CERT_STORE_CTL_CONTEXT; + } + } + } + else if (dwContextTypeFlags & CERT_STORE_CERTIFICATE_CONTEXT_FLAG) + { + hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CERT_PROP_ID); + type = CERT_STORE_CERTIFICATE_CONTEXT; + } + else if (dwContextTypeFlags & CERT_STORE_CRL_CONTEXT_FLAG) + { + hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CRL_PROP_ID); + type = CERT_STORE_CRL_CONTEXT; + } + else if (dwContextTypeFlags & CERT_STORE_CTL_CONTEXT_FLAG) + { + hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CTL_PROP_ID); + type = CERT_STORE_CTL_CONTEXT; + } + + switch (type) + { + case CERT_STORE_CERTIFICATE_CONTEXT: + contextInterface = &gCertInterface; + break; + case CERT_STORE_CRL_CONTEXT: + contextInterface = &gCRLInterface; + break; + case CERT_STORE_CTL_CONTEXT: + contextInterface = &gCTLInterface; + break; + default: + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + ret = FALSE; + } + if (!hdr) + ret = FALSE; + + if (ret) + context = contextInterface->create(X509_ASN_ENCODING, + (BYTE *)hdr + sizeof(WINE_CERT_PROP_HEADER), hdr->cb); + if (ret && context) + { + BOOL noMoreProps = FALSE; + + while (!noMoreProps && ret) + { + if (cbElement < sizeof(WINE_CERT_PROP_HEADER)) + ret = FALSE; + else + { + const WINE_CERT_PROP_HEADER *hdr = + (const WINE_CERT_PROP_HEADER *)pbElement; + + TRACE("prop is %ld\n", hdr->propID); + cbElement -= sizeof(WINE_CERT_PROP_HEADER); + pbElement += sizeof(WINE_CERT_PROP_HEADER); + if (cbElement < hdr->cb) + { + SetLastError(HRESULT_FROM_WIN32( + ERROR_INVALID_PARAMETER)); + ret = FALSE; + } + else if (!hdr->propID) + { + /* Like in CRYPT_findPropID, stop if the propID is zero + */ + noMoreProps = TRUE; + } + else if (hdr->unknown != 1) + { + SetLastError(ERROR_FILE_NOT_FOUND); + ret = FALSE; + } + else if (hdr->propID != CERT_CERT_PROP_ID && + hdr->propID != CERT_CRL_PROP_ID && hdr->propID != + CERT_CTL_PROP_ID) + { + /* Have to create a blob for most types, but not + * for all.. arghh. + */ + switch (hdr->propID) + { + case CERT_AUTO_ENROLL_PROP_ID: + case CERT_CTL_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_PUBLIC_KEY_MD5_HASH_PROP_ID: + case CERT_ENROLLMENT_PROP_ID: + case CERT_CROSS_CERT_DIST_POINTS_PROP_ID: + case CERT_RENEWAL_PROP_ID: + { + CRYPT_DATA_BLOB blob = { hdr->cb, + (LPBYTE)pbElement }; + + ret = contextInterface->setProp(context, + hdr->propID, 0, &blob); + break; + } + case CERT_DATE_STAMP_PROP_ID: + ret = contextInterface->setProp(context, + hdr->propID, 0, pbElement); + break; + default: + FIXME("prop ID %ld: stub\n", hdr->propID); + } + } + pbElement += hdr->cb; + cbElement -= hdr->cb; + if (!cbElement) + noMoreProps = TRUE; + } + } + if (ret) + { + if (pdwContentType) + *pdwContentType = type; + } + else + { + contextInterface->free(context); + context = NULL; + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + context = NULL; + } + __ENDTRY + return context; +} + +BOOL WINAPI CertAddSerializedElementToStore(HCERTSTORE hCertStore, + const BYTE *pbElement, DWORD cbElement, DWORD dwAddDisposition, DWORD dwFlags, + DWORD dwContextTypeFlags, DWORD *pdwContentType, const void **ppvContext) +{ + const void *context; + DWORD type; + BOOL ret; + + TRACE("(%p, %p, %ld, %08lx, %08lx, %08lx, %p, %p)\n", hCertStore, + pbElement, cbElement, dwAddDisposition, dwFlags, dwContextTypeFlags, + pdwContentType, ppvContext); + + /* Call the internal function, then delete the hashes. Tests show this + * function uses real hash values, not whatever's stored in the hash + * property. + */ + context = CRYPT_ReadSerializedElement(pbElement, cbElement, + dwContextTypeFlags, &type); + if (context) + { + const WINE_CONTEXT_INTERFACE *contextInterface = NULL; + + switch (type) + { + case CERT_STORE_CERTIFICATE_CONTEXT: + contextInterface = &gCertInterface; + break; + case CERT_STORE_CRL_CONTEXT: + contextInterface = &gCRLInterface; + break; + case CERT_STORE_CTL_CONTEXT: + contextInterface = &gCTLInterface; + break; + default: + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + } + if (contextInterface) + { + contextInterface->setProp(context, CERT_HASH_PROP_ID, 0, NULL); + contextInterface->setProp(context, CERT_MD5_HASH_PROP_ID, 0, NULL); + contextInterface->setProp(context, CERT_SIGNATURE_HASH_PROP_ID, 0, + NULL); + if (pdwContentType) + *pdwContentType = type; + ret = contextInterface->addContextToStore(hCertStore, context, + dwAddDisposition, ppvContext); + contextInterface->free(context); + } + else + ret = FALSE; + } + else + ret = FALSE; + return ret; +} + +BOOL WINAPI CertFreeCertificateContext(PCCERT_CONTEXT pCertContext) +{ + TRACE("(%p)\n", pCertContext); + + if (pCertContext) + { + PWINE_CERT_CONTEXT_REF ref = (PWINE_CERT_CONTEXT_REF)pCertContext; + PWINECRYPT_CERTSTORE store = (PWINECRYPT_CERTSTORE)ref->cert.hCertStore; + + if (InterlockedDecrement(&ref->context->ref) == 0) + { + TRACE("%p's ref count is 0, freeing\n", ref->context); + CRYPT_FreeCert(ref->context); + } + else + TRACE("%p's ref count is %ld\n", ref->context, ref->context->ref); + if (store && store->dwMagic == WINE_CRYPTCERTSTORE_MAGIC && + store->freeCert) + store->freeCert(ref); + CryptMemFree(ref); + } + return TRUE; +} + +PCCERT_CONTEXT WINAPI CertFindCertificateInStore(HCERTSTORE hCertStore, + DWORD dwCertEncodingType, DWORD dwFlags, DWORD dwType, + const void *pvPara, PCCERT_CONTEXT pPrevCertContext) +{ + FIXME("stub: %p %ld %ld %ld %p %p\n", hCertStore, dwCertEncodingType, + dwFlags, dwType, pvPara, pPrevCertContext); + SetLastError(CRYPT_E_NOT_FOUND); + return NULL; +} + +BOOL WINAPI CertAddStoreToCollection(HCERTSTORE hCollectionStore, + HCERTSTORE hSiblingStore, DWORD dwUpdateFlags, DWORD dwPriority) +{ + PWINE_COLLECTIONSTORE collection = (PWINE_COLLECTIONSTORE)hCollectionStore; + WINECRYPT_CERTSTORE *sibling = (WINECRYPT_CERTSTORE *)hSiblingStore; + PWINE_STORE_LIST_ENTRY entry; + BOOL ret; + + TRACE("(%p, %p, %08lx, %ld)\n", hCollectionStore, hSiblingStore, + dwUpdateFlags, dwPriority); + + if (!collection || !sibling) + return TRUE; + if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC) + { + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return FALSE; + } + if (collection->hdr.type != StoreTypeCollection) + { + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return FALSE; + } + if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) + { + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return FALSE; + } + + entry = CryptMemAlloc(sizeof(WINE_STORE_LIST_ENTRY)); + if (entry) + { + InterlockedIncrement(&sibling->ref); + TRACE("sibling %p's ref count is %ld\n", sibling, sibling->ref); + entry->store = sibling; + entry->dwUpdateFlags = dwUpdateFlags; + entry->dwPriority = dwPriority; + list_init(&entry->entry); + TRACE("%p: adding %p, priority %ld\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 = (PWINE_COLLECTIONSTORE)hCollectionStore; + WINECRYPT_CERTSTORE *sibling = (WINECRYPT_CERTSTORE *)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(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return; + } + if (collection->hdr.type != StoreTypeCollection) + return; + if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) + { + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + 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); +} + +PCRYPT_ATTRIBUTE WINAPI CertFindAttribute(LPCSTR pszObjId, DWORD cAttr, + CRYPT_ATTRIBUTE rgAttr[]) +{ + PCRYPT_ATTRIBUTE ret = NULL; + DWORD i; + + TRACE("%s %ld %p\n", debugstr_a(pszObjId), cAttr, rgAttr); + + if (!cAttr) + return NULL; + if (!pszObjId) + { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + for (i = 0; !ret && i < cAttr; i++) + if (rgAttr[i].pszObjId && !strcmp(pszObjId, rgAttr[i].pszObjId)) + ret = &rgAttr[i]; + return ret; +} + +PCERT_EXTENSION WINAPI CertFindExtension(LPCSTR pszObjId, DWORD cExtensions, + CERT_EXTENSION rgExtensions[]) +{ + PCERT_EXTENSION ret = NULL; + DWORD i; + + TRACE("%s %ld %p\n", debugstr_a(pszObjId), cExtensions, rgExtensions); + + if (!cExtensions) + return NULL; + if (!pszObjId) + { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + for (i = 0; !ret && i < cExtensions; i++) + if (rgExtensions[i].pszObjId && !strcmp(pszObjId, + rgExtensions[i].pszObjId)) + ret = &rgExtensions[i]; + return ret; +} + +PCERT_RDN_ATTR WINAPI CertFindRDNAttr(LPCSTR pszObjId, PCERT_NAME_INFO pName) +{ + PCERT_RDN_ATTR ret = NULL; + DWORD i, j; + + TRACE("%s %p\n", debugstr_a(pszObjId), pName); + + if (!pszObjId) + { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + for (i = 0; !ret && i < pName->cRDN; i++) + for (j = 0; !ret && j < pName->rgRDN[i].cRDNAttr; j++) + if (pName->rgRDN[i].rgRDNAttr[j].pszObjId && !strcmp(pszObjId, + pName->rgRDN[i].rgRDNAttr[j].pszObjId)) + ret = &pName->rgRDN[i].rgRDNAttr[j]; + return ret; +} + +LONG WINAPI CertVerifyTimeValidity(LPFILETIME pTimeToVerify, + PCERT_INFO pCertInfo) +{ + FILETIME fileTime; + LONG ret; + + if (!pTimeToVerify) + { + SYSTEMTIME sysTime; + + GetSystemTime(&sysTime); + SystemTimeToFileTime(&sysTime, &fileTime); + pTimeToVerify = &fileTime; + } + if ((ret = CompareFileTime(pTimeToVerify, &pCertInfo->NotBefore)) >= 0) + { + ret = CompareFileTime(pTimeToVerify, &pCertInfo->NotAfter); + if (ret < 0) + ret = 0; + } + return ret; +} + +BOOL WINAPI CryptHashCertificate(HCRYPTPROV hCryptProv, ALG_ID Algid, + DWORD dwFlags, const BYTE *pbEncoded, DWORD cbEncoded, BYTE *pbComputedHash, + DWORD *pcbComputedHash) +{ + BOOL ret = TRUE; + HCRYPTHASH hHash = 0; + + TRACE("(%ld, %d, %08lx, %p, %ld, %p, %p)\n", hCryptProv, Algid, dwFlags, + pbEncoded, cbEncoded, pbComputedHash, pcbComputedHash); + + if (!hCryptProv) + hCryptProv = CRYPT_GetDefaultProvider(); + if (!Algid) + Algid = CALG_SHA1; + if (ret) + { + ret = CryptCreateHash(hCryptProv, Algid, 0, 0, &hHash); + if (ret) + { + ret = CryptHashData(hHash, pbEncoded, cbEncoded, 0); + if (ret) + ret = CryptGetHashParam(hHash, HP_HASHVAL, pbComputedHash, + pcbComputedHash, 0); + CryptDestroyHash(hHash); + } + } + return ret; +} + +BOOL WINAPI CryptSignCertificate(HCRYPTPROV hCryptProv, DWORD dwKeySpec, + DWORD dwCertEncodingType, const BYTE *pbEncodedToBeSigned, + DWORD cbEncodedToBeSigned, PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, + const void *pvHashAuxInfo, BYTE *pbSignature, DWORD *pcbSignature) +{ + BOOL ret; + ALG_ID algID; + HCRYPTHASH hHash; + + TRACE("(%08lx, %ld, %ld, %p, %ld, %p, %p, %p, %p)\n", hCryptProv, + dwKeySpec, dwCertEncodingType, pbEncodedToBeSigned, cbEncodedToBeSigned, + pSignatureAlgorithm, pvHashAuxInfo, pbSignature, pcbSignature); + + algID = CertOIDToAlgId(pSignatureAlgorithm->pszObjId); + if (!algID) + { + SetLastError(NTE_BAD_ALGID); + return FALSE; + } + if (!hCryptProv) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + ret = CryptCreateHash(hCryptProv, algID, 0, 0, &hHash); + if (ret) + { + ret = CryptHashData(hHash, pbEncodedToBeSigned, cbEncodedToBeSigned, 0); + if (ret) + ret = CryptSignHashW(hHash, dwKeySpec, NULL, 0, pbSignature, + pcbSignature); + CryptDestroyHash(hHash); + } + return ret; +} + +BOOL WINAPI CryptVerifyCertificateSignature(HCRYPTPROV hCryptProv, + DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded, + PCERT_PUBLIC_KEY_INFO pPublicKey) +{ + return CryptVerifyCertificateSignatureEx(hCryptProv, dwCertEncodingType, + CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, (void *)pbEncoded, + CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY, pPublicKey, 0, NULL); +} + +BOOL WINAPI CryptVerifyCertificateSignatureEx(HCRYPTPROV hCryptProv, + DWORD dwCertEncodingType, DWORD dwSubjectType, void *pvSubject, + DWORD dwIssuerType, void *pvIssuer, DWORD dwFlags, void *pvReserved) +{ + BOOL ret = TRUE; + CRYPT_DATA_BLOB subjectBlob; + + TRACE("(%08lx, %ld, %ld, %p, %ld, %p, %08lx, %p)\n", hCryptProv, + dwCertEncodingType, dwSubjectType, pvSubject, dwIssuerType, pvIssuer, + dwFlags, pvReserved); + + switch (dwSubjectType) + { + case CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB: + { + PCRYPT_DATA_BLOB blob = (PCRYPT_DATA_BLOB)pvSubject; + + subjectBlob.pbData = blob->pbData; + subjectBlob.cbData = blob->cbData; + break; + } + case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT: + { + PCERT_CONTEXT context = (PCERT_CONTEXT)pvSubject; + + subjectBlob.pbData = context->pbCertEncoded; + subjectBlob.cbData = context->cbCertEncoded; + break; + } + case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL: + { + PCRL_CONTEXT context = (PCRL_CONTEXT)pvSubject; + + subjectBlob.pbData = context->pbCrlEncoded; + subjectBlob.cbData = context->cbCrlEncoded; + break; + } + default: + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + ret = FALSE; + } + + if (ret) + { + PCERT_SIGNED_CONTENT_INFO signedCert = NULL; + DWORD size = 0; + + ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CERT, + subjectBlob.pbData, subjectBlob.cbData, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, + (BYTE *)&signedCert, &size); + if (ret) + { + switch (dwIssuerType) + { + case CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY: + { + PCERT_PUBLIC_KEY_INFO pubKeyInfo = + (PCERT_PUBLIC_KEY_INFO)pvIssuer; + ALG_ID algID = CertOIDToAlgId(pubKeyInfo->Algorithm.pszObjId); + + if (algID) + { + HCRYPTKEY key; + + ret = CryptImportPublicKeyInfoEx(hCryptProv, + dwCertEncodingType, pubKeyInfo, algID, 0, NULL, &key); + if (ret) + { + HCRYPTHASH hash; + + ret = CryptCreateHash(hCryptProv, algID, 0, 0, &hash); + if (ret) + { + ret = CryptHashData(hash, + signedCert->ToBeSigned.pbData, + signedCert->ToBeSigned.cbData, 0); + if (ret) + { + ret = CryptVerifySignatureW(hash, + signedCert->Signature.pbData, + signedCert->Signature.cbData, key, NULL, 0); + } + CryptDestroyHash(hash); + } + CryptDestroyKey(key); + } + } + else + { + SetLastError(NTE_BAD_ALGID); + ret = FALSE; + } + break; + } + case CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT: + case CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN: + FIXME("issuer type %ld: stub\n", dwIssuerType); + ret = FALSE; + break; + case CRYPT_VERIFY_CERT_SIGN_ISSUER_NULL: + if (pvIssuer) + { + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + ret = FALSE; + } + else + { + FIXME("unimplemented for NULL signer\n"); + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + ret = FALSE; + } + break; + default: + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + ret = FALSE; + } + LocalFree(signedCert); + } + } + return ret; +} + +HCRYPTOIDFUNCSET WINAPI CryptInitOIDFunctionSet(LPCSTR pszFuncName, DWORD dwFlags) +{ + FIXME("stub: %s %lx\n", debugstr_a(pszFuncName), dwFlags); + return NULL; +} + +BOOL WINAPI CryptUnregisterDefaultOIDFunction(DWORD dwEncodingType, + LPCSTR pszFuncName, LPCWSTR pwszDll) +{ + FIXME("stub: %lx %s %s\n", dwEncodingType, debugstr_a(pszFuncName), debugstr_w(pwszDll)); + return FALSE; +} diff --git a/reactos/lib/crypt32/crypt32.def b/reactos/lib/crypt32/crypt32.def new file mode 100644 index 00000000000..079203b0f74 --- /dev/null +++ b/reactos/lib/crypt32/crypt32.def @@ -0,0 +1,36 @@ +LIBRARY CRYPT32.DLL +EXPORTS + +CertAlgIdToOID@4 @1030 + +CertOIDToAlgId@4 @1095 + +CryptMemAlloc@4 @1175 +CryptMemFree@4 @1176 +CryptMemRealloc@8 @1177 + +CryptProtectData@28 @1193 + +CryptRegisterDefaultOIDFunction@16 @1195 +CryptSIPAddProvider@4 @1198 + +CryptSIPLoad@12 @1201 +CryptSIPRemoveProvider@4 @1203 + +CryptSIPRetrieveSubjectGuid@12 @1205 + +CryptUnprotectData@28 @1221 + +I_CryptCreateLruCache@8 @1240 + +I_CryptDetachTls@4 @1242 + +I_CryptFindLruEntryData@4 @1247 + +I_CryptFlushLruCache@4 @1249 +I_CryptFreeLruCache@4 @1250 +I_CryptFreeTls@8 @1251 + +I_CryptGetTls@4 @1260 + +I_CryptSetTls@8 @1268 \ No newline at end of file diff --git a/reactos/lib/crypt32/crypt32.rc b/reactos/lib/crypt32/crypt32.rc new file mode 100644 index 00000000000..af79f2ff1bf --- /dev/null +++ b/reactos/lib/crypt32/crypt32.rc @@ -0,0 +1,7 @@ +/* $Id$ */ + +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "Crypt\0" +#define REACTOS_STR_INTERNAL_NAME "crypt32\0" +#define REACTOS_STR_ORIGINAL_FILENAME "crypt32.dll\0" +#include diff --git a/reactos/lib/crypt32/crypt32.spec b/reactos/lib/crypt32/crypt32.spec new file mode 100644 index 00000000000..64db92d0a7e --- /dev/null +++ b/reactos/lib/crypt32/crypt32.spec @@ -0,0 +1,195 @@ +@ stdcall CertAddCRLContextToStore(long ptr long ptr) +@ stdcall CertAddCTLContextToStore(long ptr long ptr) +@ stdcall CertAddCertificateContextToStore(long ptr long ptr) +@ stdcall CertAddEncodedCRLToStore(long long ptr long long ptr) +@ stdcall CertAddEncodedCTLToStore(long long ptr long long ptr) +@ stdcall CertAddEncodedCertificateToStore(long long ptr long long ptr) +@ stub CertAddEncodedCertificateToSystemStoreA +@ stub CertAddEncodedCertificateToSystemStoreW +@ stub CertAddEnhancedKeyUsageIdentifier +@ stdcall CertAddSerializedElementToStore(ptr ptr long long long long ptr ptr) +@ stdcall CertAddStoreToCollection(ptr ptr long long) +@ stdcall CertAlgIdToOID(long) +@ stdcall CertCloseStore(ptr long) +@ stub CertCompareCertificate +@ stub CertCompareCertificateName +@ stub CertCompareIntegerBlob +@ stub CertComparePublicKeyInfo +@ stdcall CertControlStore(long long long ptr) +@ stdcall CertCreateCRLContext(long ptr long) +@ stdcall CertCreateCTLContext(long ptr long) +@ stub CertCreateCertificateChainEngine +@ stdcall CertCreateCertificateContext(long ptr long) +@ stdcall CertDeleteCRLFromStore(ptr) +@ stdcall CertDeleteCTLFromStore(ptr) +@ stdcall CertDeleteCertificateFromStore(ptr) +@ stub CertDuplicateCRLContext +@ stub CertDuplicateCTLContext +@ stdcall CertDuplicateCertificateContext(ptr) +@ stub CertDuplicateStore +@ stub CertEnumCRLContextProperties +@ stdcall CertEnumCRLsInStore(ptr ptr) +@ stub CertEnumCTLContextProperties +@ stdcall CertEnumCTLsInStore(ptr ptr) +@ stdcall CertEnumCertificateContextProperties(ptr long) +@ stdcall CertEnumCertificatesInStore(long ptr) +@ stdcall CertFindAttribute(str long ptr) +@ stub CertFindCTLInStore +@ stdcall CertFindCertificateInStore(long long long long ptr ptr) +@ stdcall CertFindExtension(str long ptr) +@ stdcall CertFindRDNAttr(str ptr) +@ stub CertFindSubjectInCTL +@ stdcall CertFreeCRLContext(ptr) +@ stdcall CertFreeCTLContext(ptr) +@ stub CertFreeCertificateChain +@ stub CertFreeCertificateChainEngine +@ stdcall CertFreeCertificateContext(ptr) +@ stdcall CertGetCRLContextProperty(ptr long ptr ptr) +@ stub CertGetCRLFromStore +@ stdcall CertGetCTLContextProperty(ptr long ptr ptr) +@ stub CertGetCertificateChain +@ stdcall CertGetCertificateContextProperty(ptr long ptr ptr) +@ stub CertGetEnhancedKeyUsage +@ stub CertGetIntendedKeyUsage +@ stub CertGetIssuerCertificateFromStore +@ stub CertGetPublicKeyLength +@ stub CertGetSubjectCertificateFromStore +@ stub CertIsRDNAttrsInCertificateName +@ stub CertNameToStrA +@ stub CertNameToStrW +@ stdcall CertOIDToAlgId(str) +@ stdcall CertOpenStore(str long long long ptr) +@ stdcall CertOpenSystemStoreA(long str) +@ stdcall CertOpenSystemStoreW(long wstr) +@ stub CertRDNValueToStrA +@ stub CertRDNValueToStrW +@ stub CertRemoveEnhancedKeyUsageIdentifier +@ stdcall CertRemoveStoreFromCollection(long long) +@ stdcall CertSaveStore(long long long long ptr long) +@ stdcall CertSerializeCRLStoreElement(ptr long ptr ptr) +@ stdcall CertSerializeCTLStoreElement(ptr long ptr ptr) +@ stdcall CertSerializeCertificateStoreElement(ptr long ptr ptr) +@ stdcall CertSetCRLContextProperty(ptr long long ptr) +@ stdcall CertSetCTLContextProperty(ptr long long ptr) +@ stdcall CertSetCertificateContextProperty(ptr long long ptr) +@ stub CertSetEnhancedKeyUsage +@ stub CertStrToNameA +@ stub CertStrToNameW +@ stub CertVerifyCRLRevocation +@ stub CertVerifyCRLTimeValidity +@ stub CertVerifyCTLUsage +@ stub CertVerifyRevocation +@ stub CertVerifySubjectCertificateContext +@ stdcall CertVerifyTimeValidity(ptr ptr) +@ stub CertVerifyValidityNesting +@ stub CreateFileU +@ stub CryptAcquireContextU +@ stub CryptCloseAsyncHandle +@ stub CryptCreateAsyncHandle +@ stub CryptDecodeMessage +@ stdcall CryptDecodeObject(long str ptr long long ptr ptr) +@ stdcall CryptDecodeObjectEx(long str ptr long long ptr ptr ptr) +@ stub CryptDecryptAndVerifyMessageSignature +@ stub CryptDecryptMessage +@ stdcall CryptEncodeObject(long str ptr ptr ptr) +@ stdcall CryptEncodeObjectEx(long str ptr long ptr ptr ptr) +@ stub CryptEncryptMessage +@ stub CryptEnumOIDFunction +@ stub CryptEnumOIDInfo +@ stub CryptEnumProvidersU +@ stub CryptExportPKCS8 +@ stdcall CryptExportPublicKeyInfo(long long long ptr ptr) +@ stdcall CryptExportPublicKeyInfoEx(long long long str long ptr ptr ptr) +@ stub CryptFindOIDInfo +@ stub CryptFormatObject +@ stub CryptFreeOIDFunctionAddress +@ stub CryptGetAsyncParam +@ stub CryptGetDefaultOIDDllList +@ stub CryptGetDefaultOIDFunctionAddress +@ stub CryptGetMessageCertificates +@ stub CryptGetMessageSignerCount +@ stub CryptGetOIDFunctionAddress +@ stdcall CryptGetOIDFunctionValue(long str str wstr ptr ptr ptr) +@ stdcall CryptHashCertificate(long long long ptr long ptr ptr) +@ stub CryptHashMessage +@ stub CryptHashPublicKeyInfo +@ stub CryptHashToBeSigned +@ stub CryptImportPKCS8 +@ stdcall CryptImportPublicKeyInfo(long long ptr ptr) +@ stdcall CryptImportPublicKeyInfoEx(long long ptr long long ptr ptr) +@ stdcall CryptInitOIDFunctionSet(str long) +@ stub CryptInstallOIDFunctionAddress +@ stub CryptLoadSip +@ stdcall CryptMemAlloc(long) +@ stdcall CryptMemFree(ptr) +@ stdcall CryptMemRealloc(ptr long) +@ stub CryptMsgCalculateEncodedLength +@ stub CryptMsgClose +@ stub CryptMsgControl +@ stub CryptMsgCountersign +@ stub CryptMsgCountersignEncoded +@ stub CryptMsgEncodeAndSignCTL +@ stub CryptMsgGetAndVerifySigner +@ stub CryptMsgGetParam +@ stub CryptMsgOpenToDecode +@ stub CryptMsgOpenToEncode +@ stub CryptMsgSignCTL +@ stub CryptMsgUpdate +@ stub CryptMsgVerifyCountersignatureEncoded +@ stdcall CryptProtectData(ptr wstr ptr ptr ptr long ptr) +@ stdcall CryptRegisterDefaultOIDFunction(long str long wstr) +@ stdcall CryptRegisterOIDFunction(long str str wstr str) +@ stub CryptRegisterOIDInfo +@ stdcall CryptSIPAddProvider(ptr) +@ stdcall CryptSIPLoad(ptr long ptr) +@ stdcall CryptSIPRemoveProvider(ptr) +@ stdcall CryptSIPRetrieveSubjectGuid(wstr long ptr) +@ stub CryptSetAsyncParam +@ stdcall CryptSetOIDFunctionValue(long str str wstr long ptr long) +@ stub CryptSetProviderU +@ stub CryptSignAndEncodeCertificate +@ stub CryptSignAndEncryptMessage +@ stdcall CryptSignCertificate(long long long ptr long ptr ptr ptr ptr) +@ stub CryptSignHashU +@ stub CryptSignMessage +@ stub CryptSignMessageWithKey +@ stdcall CryptUnprotectData(ptr ptr ptr ptr ptr long ptr) +@ stdcall CryptUnregisterDefaultOIDFunction(long str wstr) +@ stdcall CryptUnregisterOIDFunction(long str str) +@ stub CryptUnregisterOIDInfo +@ stdcall CryptVerifyCertificateSignature(long long ptr long ptr) +@ stdcall CryptVerifyCertificateSignatureEx(long long long ptr long ptr long ptr) +@ stub CryptVerifyDetachedMessageHash +@ stub CryptVerifyDetachedMessageSignature +@ stub CryptVerifyMessageHash +@ stub CryptVerifyMessageSignature +@ stub CryptVerifyMessageSignatureWithKey +@ stub CryptVerifySignatureU +@ stdcall I_CryptAllocTls() +@ stdcall I_CryptCreateLruCache(long long) +@ stub I_CryptCreateLruEntry +@ stdcall I_CryptDetachTls(long) +@ stdcall I_CryptFindLruEntryData(long) +@ stdcall I_CryptFlushLruCache(long) +@ stdcall I_CryptFreeLruCache(long) +@ stdcall I_CryptFreeTls(long long) +@ stub I_CryptGetDefaultCryptProv +@ stub I_CryptGetDefaultCryptProvForEncrypt +@ stub I_CryptGetOssGlobal +@ stdcall I_CryptGetTls(long) +@ stub I_CryptInsertLruEntry +@ stub I_CryptInstallOssGlobal +@ stub I_CryptReleaseLruEntry +@ stdcall I_CryptSetTls(long ptr) +@ stub I_CryptUninstallOssGlobal +@ stub PFXExportCertStore +@ stub PFXImportCertStore +@ stub RegCreateHKCUKeyExU +@ stub RegCreateKeyExU +@ stub RegDeleteValueU +@ stub RegEnumValueU +@ stub RegOpenHKCUKeyExU +@ stub RegOpenKeyExU +@ stub RegQueryInfoKeyU +@ stub RegQueryValueExU +@ stub RegSetValueExU diff --git a/reactos/lib/crypt32/crypt32.xml b/reactos/lib/crypt32/crypt32.xml new file mode 100644 index 00000000000..50a99d80908 --- /dev/null +++ b/reactos/lib/crypt32/crypt32.xml @@ -0,0 +1,14 @@ + + + . + + 0x501 + ntdll + kernel32 + wine + advapi32 + main.c + protectdata.c + crypt32.rc + precomp.h + diff --git a/reactos/lib/crypt32/crypt32_private.h b/reactos/lib/crypt32/crypt32_private.h new file mode 100644 index 00000000000..9b593533723 --- /dev/null +++ b/reactos/lib/crypt32/crypt32_private.h @@ -0,0 +1,27 @@ +/* + * Copyright 2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __CRYPT32_PRIVATE_H__ +#define __CRYPT32_PRIVATE_H__ + +/* Returns a handle to the default crypto provider; loads it if necessary. + * Returns NULL on failure. + */ +HCRYPTPROV CRYPT_GetDefaultProvider(void); + +#endif diff --git a/reactos/lib/crypt32/encode.c b/reactos/lib/crypt32/encode.c new file mode 100644 index 00000000000..cd9eaddd807 --- /dev/null +++ b/reactos/lib/crypt32/encode.c @@ -0,0 +1,5688 @@ +/* + * Copyright 2002 Mike McCormack for CodeWeavers + * Copyright 2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This file implements ASN.1 DER encoding and decoding of a limited set of + * types. It isn't a full ASN.1 implementation. Microsoft implements BER + * encoding of many of the basic types in msasn1.dll, but that interface is + * undocumented, so I implement them here. + * + * References: + * "A Layman's Guide to a Subset of ASN.1, BER, and DER", by Burton Kaliski + * (available online, look for a PDF copy as the HTML versions tend to have + * translation errors.) + * + * RFC3280, http://www.faqs.org/rfcs/rfc3280.html + * + * MSDN, especially: + * http://msdn.microsoft.com/library/en-us/seccrypto/security/constants_for_cryptencodeobject_and_cryptdecodeobject.asp + */ + +#include "precomp.h" + +/* This is a bit arbitrary, but to set some limit: */ +#define MAX_ENCODED_LEN 0x02000000 + +/* a few asn.1 tags we need */ +#define ASN_BOOL (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x01) +#define ASN_BITSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x03) +#define ASN_OCTETSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x04) +#define ASN_ENUMERATED (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x0a) +#define ASN_SETOF (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x11) +#define ASN_NUMERICSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x12) +#define ASN_PRINTABLESTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x13) +#define ASN_IA5STRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x16) +#define ASN_UTCTIME (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x17) +#define ASN_GENERALTIME (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x18) + +#define ASN_FLAGS_MASK 0xf0 +#define ASN_TYPE_MASK 0x0f + +WINE_DEFAULT_DEBUG_CHANNEL(crypt); + +static const WCHAR szDllName[] = { 'D','l','l',0 }; + +typedef BOOL (WINAPI *CryptEncodeObjectFunc)(DWORD, LPCSTR, const void *, + BYTE *, DWORD *); +typedef BOOL (WINAPI *CryptEncodeObjectExFunc)(DWORD, LPCSTR, const void *, + DWORD, PCRYPT_ENCODE_PARA, BYTE *, DWORD *); +typedef BOOL (WINAPI *CryptDecodeObjectFunc)(DWORD, LPCSTR, const BYTE *, + DWORD, DWORD, void *, DWORD *); +typedef BOOL (WINAPI *CryptDecodeObjectExFunc)(DWORD, LPCSTR, const BYTE *, + DWORD, DWORD, PCRYPT_DECODE_PARA, void *, DWORD *); + +/* Prototypes for built-in encoders/decoders. They follow the Ex style + * prototypes. The dwCertEncodingType and lpszStructType are ignored by the + * built-in functions, but the parameters are retained to simplify + * CryptEncodeObjectEx/CryptDecodeObjectEx, since they must call functions in + * external DLLs that follow these signatures. + * FIXME: some built-in functions are suitable to be called directly by + * CryptEncodeObjectEx/CryptDecodeObjectEx (they implement exception handling + * and memory allocation if requested), others are only suitable to be called + * internally. Comment which are which. + */ +static BOOL WINAPI CRYPT_AsnEncodeOid(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded); +static BOOL WINAPI CRYPT_AsnEncodeExtensions(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded); +static BOOL WINAPI CRYPT_AsnEncodeBool(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded); +static BOOL WINAPI CRYPT_AsnEncodePubKeyInfo(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded); +static BOOL WINAPI CRYPT_AsnEncodeOctets(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded); +static BOOL WINAPI CRYPT_AsnEncodeBits(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded); +static BOOL WINAPI CRYPT_AsnEncodeBitsSwapBytes(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded); +static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded); +static BOOL WINAPI CRYPT_AsnEncodeInteger(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded); +static BOOL WINAPI CRYPT_AsnEncodeUnsignedInteger(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded); +static BOOL WINAPI CRYPT_AsnEncodeChoiceOfTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded); + +static BOOL WINAPI CRYPT_AsnDecodeChoiceOfTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo); +static BOOL WINAPI CRYPT_AsnDecodePubKeyInfo(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo); +/* Like CRYPT_AsnDecodeExtensions, except assumes rgExtension is set ahead of + * time, doesn't do memory allocation, and doesn't do exception handling. + * (This isn't intended to be the externally-called one.) + */ +static BOOL WINAPI CRYPT_AsnDecodeExtensionsInternal(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo); +static BOOL WINAPI CRYPT_AsnDecodeOid(const BYTE *pbEncoded, DWORD cbEncoded, + DWORD dwFlags, LPSTR pszObjId, DWORD *pcbObjId); +/* Assumes algo->Parameters.pbData is set ahead of time */ +static BOOL WINAPI CRYPT_AsnDecodeAlgorithmId(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo); +static BOOL WINAPI CRYPT_AsnDecodeBool(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo); +/* Assumes the CRYPT_DATA_BLOB's pbData member has been initialized */ +static BOOL WINAPI CRYPT_AsnDecodeOctetsInternal(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo); +/* Like CRYPT_AsnDecodeBits, but assumes the CRYPT_INTEGER_BLOB's pbData + * member has been initialized, doesn't do exception handling, and doesn't do + * memory allocation. + */ +static BOOL WINAPI CRYPT_AsnDecodeBitsInternal(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo); +static BOOL WINAPI CRYPT_AsnDecodeBits(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo); +static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo); +/* Like CRYPT_AsnDecodeInteger, but assumes the CRYPT_INTEGER_BLOB's pbData + * member has been initialized, doesn't do exception handling, and doesn't do + * memory allocation. + */ +static BOOL WINAPI CRYPT_AsnDecodeIntegerInternal(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo); +/* Like CRYPT_AsnDecodeInteger, but unsigned. */ +static BOOL WINAPI CRYPT_AsnDecodeUnsignedIntegerInternal( + DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara, + void *pvStructInfo, DWORD *pcbStructInfo); + +/* filter for page-fault exceptions */ +static WINE_EXCEPTION_FILTER(page_fault) +{ + if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) + return EXCEPTION_EXECUTE_HANDLER; + return EXCEPTION_CONTINUE_SEARCH; +} + +static char *CRYPT_GetKeyName(DWORD dwEncodingType, LPCSTR pszFuncName, + LPCSTR pszOID) +{ + static const char szEncodingTypeFmt[] = + "Software\\Microsoft\\Cryptography\\OID\\EncodingType %ld\\%s\\%s"; + UINT len; + char numericOID[7]; /* enough for "#65535" */ + const char *oid; + LPSTR szKey; + + /* MSDN says the encoding type is a mask, but it isn't treated that way. + * (E.g., if dwEncodingType were 3, the key names "EncodingType 1" and + * "EncodingType 2" would be expected if it were a mask. Instead native + * stores values in "EncodingType 3". + */ + if (!HIWORD(pszOID)) + { + snprintf(numericOID, sizeof(numericOID), "#%d", LOWORD(pszOID)); + oid = numericOID; + } + else + oid = pszOID; + + /* This is enough: the lengths of the two string parameters are explicitly + * counted, and we need up to five additional characters for the encoding + * type. These are covered by the "%d", "%s", and "%s" characters in the + * format specifier that are removed by sprintf. + */ + len = sizeof(szEncodingTypeFmt) + lstrlenA(pszFuncName) + lstrlenA(oid); + szKey = CryptMemAlloc(len); + if (szKey) + sprintf(szKey, szEncodingTypeFmt, dwEncodingType, pszFuncName, oid); + return szKey; +} + +BOOL WINAPI CryptRegisterOIDFunction(DWORD dwEncodingType, LPCSTR pszFuncName, + LPCSTR pszOID, LPCWSTR pwszDll, LPCSTR pszOverrideFuncName) +{ + LONG r; + HKEY hKey; + LPSTR szKey; + + TRACE("%lx %s %s %s %s\n", dwEncodingType, pszFuncName, pszOID, + debugstr_w(pwszDll), pszOverrideFuncName); + + /* This only registers functions for encoding certs, not messages */ + if (!GET_CERT_ENCODING_TYPE(dwEncodingType)) + return TRUE; + + /* Native does nothing pwszDll is NULL */ + if (!pwszDll) + return TRUE; + + /* I'm not matching MS bug for bug here, because I doubt any app depends on + * it: + * - native "succeeds" if pszFuncName is NULL, but the nonsensical entry + * it creates would never be used + * - native returns an HRESULT rather than a Win32 error if pszOID is NULL. + * Instead I disallow both of these with ERROR_INVALID_PARAMETER. + */ + if (!pszFuncName || !pszOID) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID); + TRACE("Key name is %s\n", debugstr_a(szKey)); + + if (!szKey) + return FALSE; + + r = RegCreateKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey); + CryptMemFree(szKey); + if(r != ERROR_SUCCESS) + return FALSE; + + /* write the values */ + if (pszOverrideFuncName) + RegSetValueExA(hKey, "FuncName", 0, REG_SZ, (const BYTE*)pszOverrideFuncName, + lstrlenA(pszOverrideFuncName) + 1); + RegSetValueExW(hKey, szDllName, 0, REG_SZ, (const BYTE*) pwszDll, + (lstrlenW(pwszDll) + 1) * sizeof (WCHAR)); + + RegCloseKey(hKey); + return TRUE; +} + +BOOL WINAPI CryptUnregisterOIDFunction(DWORD dwEncodingType, LPCSTR pszFuncName, + LPCSTR pszOID) +{ + LPSTR szKey; + LONG rc; + + TRACE("%lx %s %s\n", dwEncodingType, pszFuncName, pszOID); + + if (!GET_CERT_ENCODING_TYPE(dwEncodingType)) + return TRUE; + + if (!pszFuncName || !pszOID) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID); + rc = RegDeleteKeyA(HKEY_LOCAL_MACHINE, szKey); + CryptMemFree(szKey); + if (rc) + SetLastError(rc); + return rc ? FALSE : TRUE; +} + +BOOL WINAPI CryptGetOIDFunctionValue(DWORD dwEncodingType, LPCSTR pszFuncName, + LPCSTR pszOID, LPCWSTR pwszValueName, DWORD *pdwValueType, BYTE *pbValueData, + DWORD *pcbValueData) +{ + LPSTR szKey; + LONG rc; + HKEY hKey; + + TRACE("%lx %s %s %s %p %p %p\n", dwEncodingType, debugstr_a(pszFuncName), + debugstr_a(pszOID), debugstr_w(pwszValueName), pdwValueType, pbValueData, + pcbValueData); + + if (!GET_CERT_ENCODING_TYPE(dwEncodingType)) + return TRUE; + + if (!pszFuncName || !pszOID || !pwszValueName) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID); + rc = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey); + CryptMemFree(szKey); + if (rc) + SetLastError(rc); + else + { + rc = RegQueryValueExW(hKey, pwszValueName, NULL, pdwValueType, + pbValueData, pcbValueData); + if (rc) + SetLastError(rc); + RegCloseKey(hKey); + } + return rc ? FALSE : TRUE; +} + +BOOL WINAPI CryptSetOIDFunctionValue(DWORD dwEncodingType, LPCSTR pszFuncName, + LPCSTR pszOID, LPCWSTR pwszValueName, DWORD dwValueType, + const BYTE *pbValueData, DWORD cbValueData) +{ + LPSTR szKey; + LONG rc; + HKEY hKey; + + TRACE("%lx %s %s %s %ld %p %ld\n", dwEncodingType, debugstr_a(pszFuncName), + debugstr_a(pszOID), debugstr_w(pwszValueName), dwValueType, pbValueData, + cbValueData); + + if (!GET_CERT_ENCODING_TYPE(dwEncodingType)) + return TRUE; + + if (!pszFuncName || !pszOID || !pwszValueName) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID); + rc = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey); + CryptMemFree(szKey); + if (rc) + SetLastError(rc); + else + { + rc = RegSetValueExW(hKey, pwszValueName, 0, dwValueType, pbValueData, + cbValueData); + if (rc) + SetLastError(rc); + RegCloseKey(hKey); + } + return rc ? FALSE : TRUE; +} + +/* Gets the registered function named szFuncName for dwCertEncodingType and + * lpszStructType, or NULL if one could not be found. *lib will be set to the + * handle of the module it's in, or NULL if no module was loaded. If the + * return value is NULL, *lib will also be NULL, to simplify error handling. + */ +static void *CRYPT_GetFunc(DWORD dwCertEncodingType, LPCSTR lpszStructType, + LPCSTR szFuncName, HMODULE *lib) +{ + void *ret = NULL; + char *szKey = CRYPT_GetKeyName(dwCertEncodingType, szFuncName, + lpszStructType); + const char *funcName; + long r; + HKEY hKey; + DWORD type, size = 0; + + TRACE("(%08lx %s %s %p)\n", dwCertEncodingType, debugstr_a(lpszStructType), + debugstr_a(szFuncName), lib); + + *lib = NULL; + r = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey); + CryptMemFree(szKey); + if(r != ERROR_SUCCESS) + return NULL; + + RegQueryValueExA(hKey, "FuncName", NULL, &type, NULL, &size); + if (GetLastError() == ERROR_MORE_DATA && type == REG_SZ) + { + funcName = CryptMemAlloc(size); + RegQueryValueExA(hKey, "FuncName", NULL, &type, (LPBYTE)funcName, + &size); + } + else + funcName = szFuncName; + RegQueryValueExW(hKey, szDllName, NULL, &type, NULL, &size); + if (GetLastError() == ERROR_MORE_DATA && type == REG_SZ) + { + LPWSTR dllName = CryptMemAlloc(size); + + RegQueryValueExW(hKey, szDllName, NULL, &type, (LPBYTE)dllName, + &size); + *lib = LoadLibraryW(dllName); + if (*lib) + { + ret = GetProcAddress(*lib, funcName); + if (!ret) + { + /* Unload the library, the caller doesn't want to unload it + * when the return value is NULL. + */ + FreeLibrary(*lib); + *lib = NULL; + } + } + CryptMemFree(dllName); + } + if (funcName != szFuncName) + CryptMemFree((char *)funcName); + TRACE("returning %p\n", ret); + return ret; +} + +BOOL WINAPI CryptEncodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType, + const void *pvStructInfo, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret = FALSE; + HMODULE lib; + CryptEncodeObjectFunc pCryptEncodeObject; + + TRACE("(0x%08lx, %s, %p, %p, %p)\n", dwCertEncodingType, + debugstr_a(lpszStructType), pvStructInfo, pbEncoded, + pcbEncoded); + + if (!pbEncoded && !pcbEncoded) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + /* Try registered DLL first.. */ + pCryptEncodeObject = + (CryptEncodeObjectFunc)CRYPT_GetFunc(dwCertEncodingType, + lpszStructType, CRYPT_OID_ENCODE_OBJECT_FUNC, &lib); + if (pCryptEncodeObject) + { + ret = pCryptEncodeObject(dwCertEncodingType, lpszStructType, + pvStructInfo, pbEncoded, pcbEncoded); + FreeLibrary(lib); + } + else + { + /* If not, use CryptEncodeObjectEx */ + ret = CryptEncodeObjectEx(dwCertEncodingType, lpszStructType, + pvStructInfo, 0, NULL, pbEncoded, pcbEncoded); + } + return ret; +} + +/* Helper function to check *pcbEncoded, set it to the required size, and + * optionally to allocate memory. Assumes pbEncoded is not NULL. + * If CRYPT_ENCODE_ALLOC_FLAG is set in dwFlags, *pbEncoded will be set to a + * pointer to the newly allocated memory. + */ +static BOOL CRYPT_EncodeEnsureSpace(DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded, + DWORD bytesNeeded) +{ + BOOL ret = TRUE; + + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + { + if (pEncodePara && pEncodePara->pfnAlloc) + *(BYTE **)pbEncoded = pEncodePara->pfnAlloc(bytesNeeded); + else + *(BYTE **)pbEncoded = LocalAlloc(0, bytesNeeded); + if (!*(BYTE **)pbEncoded) + ret = FALSE; + else + *pcbEncoded = bytesNeeded; + } + else if (bytesNeeded > *pcbEncoded) + { + *pcbEncoded = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + return ret; +} + +static BOOL CRYPT_EncodeLen(DWORD len, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + DWORD bytesNeeded, significantBytes = 0; + + if (len <= 0x7f) + bytesNeeded = 1; + else + { + DWORD temp; + + for (temp = len, significantBytes = sizeof(temp); !(temp & 0xff000000); + temp <<= 8, significantBytes--) + ; + bytesNeeded = significantBytes + 1; + } + if (!pbEncoded) + { + *pcbEncoded = bytesNeeded; + return TRUE; + } + if (*pcbEncoded < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + if (len <= 0x7f) + *pbEncoded = (BYTE)len; + else + { + DWORD i; + + *pbEncoded++ = significantBytes | 0x80; + for (i = 0; i < significantBytes; i++) + { + *(pbEncoded + significantBytes - i - 1) = (BYTE)(len & 0xff); + len >>= 8; + } + } + *pcbEncoded = bytesNeeded; + return TRUE; +} + +struct AsnEncodeSequenceItem +{ + const void *pvStructInfo; + CryptEncodeObjectExFunc encodeFunc; + DWORD size; /* used during encoding, not for your use */ +}; + +static BOOL WINAPI CRYPT_AsnEncodeSequence(DWORD dwCertEncodingType, + struct AsnEncodeSequenceItem items[], DWORD cItem, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + DWORD i, dataLen = 0; + + TRACE("%p, %ld, %08lx, %p, %p, %ld\n", items, cItem, dwFlags, pEncodePara, + pbEncoded, *pcbEncoded); + for (i = 0, ret = TRUE; ret && i < cItem; i++) + { + ret = items[i].encodeFunc(dwCertEncodingType, NULL, + items[i].pvStructInfo, dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, + NULL, &items[i].size); + dataLen += items[i].size; + } + if (ret) + { + DWORD lenBytes, bytesNeeded; + + CRYPT_EncodeLen(dataLen, NULL, &lenBytes); + bytesNeeded = 1 + lenBytes + dataLen; + if (!pbEncoded) + *pcbEncoded = bytesNeeded; + else + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, + pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_SEQUENCE; + CRYPT_EncodeLen(dataLen, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + for (i = 0; ret && i < cItem; i++) + { + ret = items[i].encodeFunc(dwCertEncodingType, NULL, + items[i].pvStructInfo, dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, + NULL, pbEncoded, &items[i].size); + pbEncoded += items[i].size; + } + } + } + } + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +struct AsnConstructedItem +{ + BYTE tag; + const void *pvStructInfo; + CryptEncodeObjectExFunc encodeFunc; +}; + +static BOOL WINAPI CRYPT_AsnEncodeConstructed(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + const struct AsnConstructedItem *item = + (const struct AsnConstructedItem *)pvStructInfo; + DWORD len; + + if ((ret = item->encodeFunc(dwCertEncodingType, lpszStructType, + item->pvStructInfo, dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, NULL, &len))) + { + DWORD dataLen, bytesNeeded; + + CRYPT_EncodeLen(len, NULL, &dataLen); + bytesNeeded = 1 + dataLen + len; + if (!pbEncoded) + *pcbEncoded = bytesNeeded; + else if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, + pbEncoded, pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_CONTEXT | ASN_CONSTRUCTOR | item->tag; + CRYPT_EncodeLen(len, pbEncoded, &dataLen); + pbEncoded += dataLen; + ret = item->encodeFunc(dwCertEncodingType, lpszStructType, + item->pvStructInfo, dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, + pbEncoded, &len); + } + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeCertVersion(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + const DWORD *ver = (const DWORD *)pvStructInfo; + BOOL ret; + + /* CERT_V1 is not encoded */ + if (*ver == CERT_V1) + { + *pcbEncoded = 0; + ret = TRUE; + } + else + { + struct AsnConstructedItem item = { 0, ver, CRYPT_AsnEncodeInt }; + + ret = CRYPT_AsnEncodeConstructed(dwCertEncodingType, X509_INTEGER, + &item, dwFlags, pEncodePara, pbEncoded, pcbEncoded); + } + return ret; +} + +static BOOL WINAPI CRYPT_CopyEncodedBlob(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + const CRYPT_DER_BLOB *blob = (const CRYPT_DER_BLOB *)pvStructInfo; + BOOL ret; + + if (!pbEncoded) + { + *pcbEncoded = blob->cbData; + ret = TRUE; + } + else if (*pcbEncoded < blob->cbData) + { + *pcbEncoded = blob->cbData; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + if (blob->cbData) + memcpy(pbEncoded, blob->pbData, blob->cbData); + *pcbEncoded = blob->cbData; + ret = TRUE; + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeValidity(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + /* This has two filetimes in a row, a NotBefore and a NotAfter */ + const FILETIME *timePtr = (const FILETIME *)pvStructInfo; + struct AsnEncodeSequenceItem items[] = { + { timePtr++, CRYPT_AsnEncodeChoiceOfTime, 0 }, + { timePtr, CRYPT_AsnEncodeChoiceOfTime, 0 }, + }; + + ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), dwFlags, pEncodePara, pbEncoded, + pcbEncoded); + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeAlgorithmId( + DWORD dwCertEncodingType, LPCSTR lpszStructType, const void *pvStructInfo, + DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, + DWORD *pcbEncoded) +{ + const CRYPT_ALGORITHM_IDENTIFIER *algo = + (const CRYPT_ALGORITHM_IDENTIFIER *)pvStructInfo; + BOOL ret; + struct AsnEncodeSequenceItem items[] = { + { algo->pszObjId, CRYPT_AsnEncodeOid, 0 }, + { &algo->Parameters, CRYPT_CopyEncodedBlob, 0 }, + }; + + ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), dwFlags, pEncodePara, pbEncoded, + pcbEncoded); + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodePubKeyInfo(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + const CERT_PUBLIC_KEY_INFO *info = + (const CERT_PUBLIC_KEY_INFO *)pvStructInfo; + struct AsnEncodeSequenceItem items[] = { + { &info->Algorithm, CRYPT_AsnEncodeAlgorithmId, 0 }, + { &info->PublicKey, CRYPT_AsnEncodeBits, 0 }, + }; + + TRACE("Encoding public key with OID %s\n", + debugstr_a(info->Algorithm.pszObjId)); + ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), dwFlags, pEncodePara, pbEncoded, + pcbEncoded); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeCert(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + const CERT_SIGNED_CONTENT_INFO *info = + (const CERT_SIGNED_CONTENT_INFO *)pvStructInfo; + struct AsnEncodeSequenceItem items[] = { + { &info->ToBeSigned, CRYPT_CopyEncodedBlob, 0 }, + { &info->SignatureAlgorithm, CRYPT_AsnEncodeAlgorithmId, 0 }, + { &info->Signature, CRYPT_AsnEncodeBitsSwapBytes, 0 }, + }; + + if (dwFlags & CRYPT_ENCODE_NO_SIGNATURE_BYTE_REVERSAL_FLAG) + items[2].encodeFunc = CRYPT_AsnEncodeBits; + ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), dwFlags, pEncodePara, pbEncoded, + pcbEncoded); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +/* Like in Windows, this blithely ignores the validity of the passed-in + * CERT_INFO, and just encodes it as-is. The resulting encoded data may not + * decode properly, see CRYPT_AsnDecodeCertInfo. + */ +static BOOL WINAPI CRYPT_AsnEncodeCertInfo(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + const CERT_INFO *info = (const CERT_INFO *)pvStructInfo; + struct AsnEncodeSequenceItem items[10] = { + { &info->dwVersion, CRYPT_AsnEncodeCertVersion, 0 }, + { &info->SerialNumber, CRYPT_AsnEncodeInteger, 0 }, + { &info->SignatureAlgorithm, CRYPT_AsnEncodeAlgorithmId, 0 }, + { &info->Issuer, CRYPT_CopyEncodedBlob, 0 }, + { &info->NotBefore, CRYPT_AsnEncodeValidity, 0 }, + { &info->Subject, CRYPT_CopyEncodedBlob, 0 }, + { &info->SubjectPublicKeyInfo, CRYPT_AsnEncodePubKeyInfo, 0 }, + { 0 } + }; + struct AsnConstructedItem constructed[3] = { { 0 } }; + DWORD cItem = 7, cConstructed = 0; + + if (info->IssuerUniqueId.cbData) + { + constructed[cConstructed].tag = 1; + constructed[cConstructed].pvStructInfo = &info->IssuerUniqueId; + constructed[cConstructed].encodeFunc = CRYPT_AsnEncodeBits; + items[cItem].pvStructInfo = &constructed[cConstructed]; + items[cItem].encodeFunc = CRYPT_AsnEncodeConstructed; + cConstructed++; + cItem++; + } + if (info->SubjectUniqueId.cbData) + { + constructed[cConstructed].tag = 2; + constructed[cConstructed].pvStructInfo = &info->SubjectUniqueId; + constructed[cConstructed].encodeFunc = CRYPT_AsnEncodeBits; + items[cItem].pvStructInfo = &constructed[cConstructed]; + items[cItem].encodeFunc = CRYPT_AsnEncodeConstructed; + cConstructed++; + cItem++; + } + if (info->cExtension) + { + constructed[cConstructed].tag = 3; + constructed[cConstructed].pvStructInfo = &info->cExtension; + constructed[cConstructed].encodeFunc = CRYPT_AsnEncodeExtensions; + items[cItem].pvStructInfo = &constructed[cConstructed]; + items[cItem].encodeFunc = CRYPT_AsnEncodeConstructed; + cConstructed++; + cItem++; + } + + ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items, cItem, + dwFlags, pEncodePara, pbEncoded, pcbEncoded); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeCRLEntry(const CRL_ENTRY *entry, + BYTE *pbEncoded, DWORD *pcbEncoded) +{ + struct AsnEncodeSequenceItem items[3] = { + { &entry->SerialNumber, CRYPT_AsnEncodeInteger, 0 }, + { &entry->RevocationDate, CRYPT_AsnEncodeChoiceOfTime, 0 }, + { 0 } + }; + DWORD cItem = 2; + BOOL ret; + + TRACE("%p, %p, %p\n", entry, pbEncoded, pcbEncoded); + + if (entry->cExtension) + { + items[cItem].pvStructInfo = &entry->cExtension; + items[cItem].encodeFunc = CRYPT_AsnEncodeExtensions; + cItem++; + } + + ret = CRYPT_AsnEncodeSequence(X509_ASN_ENCODING, items, cItem, 0, NULL, + pbEncoded, pcbEncoded); + + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeCRLEntries(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + DWORD cCRLEntry = *(const DWORD *)pvStructInfo; + DWORD bytesNeeded, dataLen, lenBytes, i; + const CRL_ENTRY *rgCRLEntry = *(const CRL_ENTRY **) + ((const BYTE *)pvStructInfo + sizeof(DWORD)); + BOOL ret = TRUE; + + for (i = 0, dataLen = 0; ret && i < cCRLEntry; i++) + { + DWORD size; + + ret = CRYPT_AsnEncodeCRLEntry(&rgCRLEntry[i], NULL, &size); + if (ret) + dataLen += size; + } + CRYPT_EncodeLen(dataLen, NULL, &lenBytes); + bytesNeeded = 1 + lenBytes + dataLen; + if (!pbEncoded) + *pcbEncoded = bytesNeeded; + else + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, + pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_SEQUENCEOF; + CRYPT_EncodeLen(dataLen, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + for (i = 0; i < cCRLEntry; i++) + { + DWORD size = dataLen; + + ret = CRYPT_AsnEncodeCRLEntry(&rgCRLEntry[i], pbEncoded, &size); + pbEncoded += size; + dataLen -= size; + } + } + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeCRLVersion(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + const DWORD *ver = (const DWORD *)pvStructInfo; + BOOL ret; + + /* CRL_V1 is not encoded */ + if (*ver == CRL_V1) + { + *pcbEncoded = 0; + ret = TRUE; + } + else + ret = CRYPT_AsnEncodeInt(dwCertEncodingType, X509_INTEGER, ver, + dwFlags, pEncodePara, pbEncoded, pcbEncoded); + return ret; +} + +/* Like in Windows, this blithely ignores the validity of the passed-in + * CRL_INFO, and just encodes it as-is. The resulting encoded data may not + * decode properly, see CRYPT_AsnDecodeCRLInfo. + */ +static BOOL WINAPI CRYPT_AsnEncodeCRLInfo(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + const CRL_INFO *info = (const CRL_INFO *)pvStructInfo; + struct AsnEncodeSequenceItem items[7] = { + { &info->dwVersion, CRYPT_AsnEncodeCRLVersion, 0 }, + { &info->SignatureAlgorithm, CRYPT_AsnEncodeAlgorithmId, 0 }, + { &info->Issuer, CRYPT_CopyEncodedBlob, 0 }, + { &info->ThisUpdate, CRYPT_AsnEncodeChoiceOfTime, 0 }, + { 0 } + }; + DWORD cItem = 4; + + if (info->NextUpdate.dwLowDateTime || info->NextUpdate.dwHighDateTime) + { + items[cItem].pvStructInfo = &info->NextUpdate; + items[cItem].encodeFunc = CRYPT_AsnEncodeChoiceOfTime; + cItem++; + } + if (info->cCRLEntry) + { + items[cItem].pvStructInfo = &info->cCRLEntry; + items[cItem].encodeFunc = CRYPT_AsnEncodeCRLEntries; + cItem++; + } + if (info->cExtension) + { + items[cItem].pvStructInfo = &info->cExtension; + items[cItem].encodeFunc = CRYPT_AsnEncodeExtensions; + cItem++; + } + + ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items, cItem, + dwFlags, pEncodePara, pbEncoded, pcbEncoded); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL CRYPT_AsnEncodeExtension(CERT_EXTENSION *ext, BYTE *pbEncoded, + DWORD *pcbEncoded) +{ + BOOL ret; + struct AsnEncodeSequenceItem items[3] = { + { ext->pszObjId, CRYPT_AsnEncodeOid, 0 }, + { NULL, NULL, 0 }, + { NULL, NULL, 0 }, + }; + DWORD cItem = 1; + + TRACE("%p, %p, %ld\n", ext, pbEncoded, *pcbEncoded); + + if (ext->fCritical) + { + items[cItem].pvStructInfo = &ext->fCritical; + items[cItem].encodeFunc = CRYPT_AsnEncodeBool; + cItem++; + } + items[cItem].pvStructInfo = &ext->Value; + items[cItem].encodeFunc = CRYPT_AsnEncodeOctets; + cItem++; + + ret = CRYPT_AsnEncodeSequence(X509_ASN_ENCODING, items, cItem, 0, NULL, + pbEncoded, pcbEncoded); + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeExtensions(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + DWORD bytesNeeded, dataLen, lenBytes, i; + const CERT_EXTENSIONS *exts = (const CERT_EXTENSIONS *)pvStructInfo; + + ret = TRUE; + for (i = 0, dataLen = 0; ret && i < exts->cExtension; i++) + { + DWORD size; + + ret = CRYPT_AsnEncodeExtension(&exts->rgExtension[i], NULL, &size); + if (ret) + dataLen += size; + } + CRYPT_EncodeLen(dataLen, NULL, &lenBytes); + bytesNeeded = 1 + lenBytes + dataLen; + if (!pbEncoded) + *pcbEncoded = bytesNeeded; + else + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, + pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_SEQUENCEOF; + CRYPT_EncodeLen(dataLen, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + for (i = 0; i < exts->cExtension; i++) + { + DWORD size = dataLen; + + ret = CRYPT_AsnEncodeExtension(&exts->rgExtension[i], + pbEncoded, &size); + pbEncoded += size; + dataLen -= size; + } + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeOid(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + LPCSTR pszObjId = (LPCSTR)pvStructInfo; + DWORD bytesNeeded = 0, lenBytes; + BOOL ret = TRUE; + int firstPos = 0; + BYTE firstByte = 0; + + TRACE("%s\n", debugstr_a(pszObjId)); + + if (pszObjId) + { + const char *ptr; + int val1, val2; + + if (sscanf(pszObjId, "%d.%d.%n", &val1, &val2, &firstPos) != 2) + { + SetLastError(CRYPT_E_ASN1_ERROR); + return FALSE; + } + bytesNeeded++; + firstByte = val1 * 40 + val2; + ptr = pszObjId + firstPos; + while (ret && *ptr) + { + int pos; + + /* note I assume each component is at most 32-bits long in base 2 */ + if (sscanf(ptr, "%d%n", &val1, &pos) == 1) + { + if (val1 >= 0x10000000) + bytesNeeded += 5; + else if (val1 >= 0x200000) + bytesNeeded += 4; + else if (val1 >= 0x4000) + bytesNeeded += 3; + else if (val1 >= 0x80) + bytesNeeded += 2; + else + bytesNeeded += 1; + ptr += pos; + if (*ptr == '.') + ptr++; + } + else + { + SetLastError(CRYPT_E_ASN1_ERROR); + return FALSE; + } + } + CRYPT_EncodeLen(bytesNeeded, NULL, &lenBytes); + } + else + lenBytes = 1; + bytesNeeded += 1 + lenBytes; + if (pbEncoded) + { + if (*pcbEncoded < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + *pbEncoded++ = ASN_OBJECTIDENTIFIER; + CRYPT_EncodeLen(bytesNeeded - 1 - lenBytes, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + if (pszObjId) + { + const char *ptr; + int val, pos; + + *pbEncoded++ = firstByte; + ptr = pszObjId + firstPos; + while (ret && *ptr) + { + sscanf(ptr, "%d%n", &val, &pos); + { + unsigned char outBytes[5]; + int numBytes, i; + + if (val >= 0x10000000) + numBytes = 5; + else if (val >= 0x200000) + numBytes = 4; + else if (val >= 0x4000) + numBytes = 3; + else if (val >= 0x80) + numBytes = 2; + else + numBytes = 1; + for (i = numBytes; i > 0; i--) + { + outBytes[i - 1] = val & 0x7f; + val >>= 7; + } + for (i = 0; i < numBytes - 1; i++) + *pbEncoded++ = outBytes[i] | 0x80; + *pbEncoded++ = outBytes[i]; + ptr += pos; + if (*ptr == '.') + ptr++; + } + } + } + } + } + *pcbEncoded = bytesNeeded; + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeNameValue(DWORD dwCertEncodingType, + CERT_NAME_VALUE *value, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BYTE tag; + DWORD bytesNeeded, lenBytes, encodedLen; + BOOL ret = TRUE; + + switch (value->dwValueType) + { + case CERT_RDN_NUMERIC_STRING: + tag = ASN_NUMERICSTRING; + encodedLen = value->Value.cbData; + break; + case CERT_RDN_PRINTABLE_STRING: + tag = ASN_PRINTABLESTRING; + encodedLen = value->Value.cbData; + break; + case CERT_RDN_IA5_STRING: + tag = ASN_IA5STRING; + encodedLen = value->Value.cbData; + break; + case CERT_RDN_ANY_TYPE: + /* explicitly disallowed */ + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return FALSE; + default: + FIXME("String type %ld unimplemented\n", value->dwValueType); + return FALSE; + } + CRYPT_EncodeLen(encodedLen, NULL, &lenBytes); + bytesNeeded = 1 + lenBytes + encodedLen; + if (pbEncoded) + { + if (*pcbEncoded < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + *pbEncoded++ = tag; + CRYPT_EncodeLen(encodedLen, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + switch (value->dwValueType) + { + case CERT_RDN_NUMERIC_STRING: + case CERT_RDN_PRINTABLE_STRING: + case CERT_RDN_IA5_STRING: + memcpy(pbEncoded, value->Value.pbData, value->Value.cbData); + } + } + } + *pcbEncoded = bytesNeeded; + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeRdnAttr(DWORD dwCertEncodingType, + CERT_RDN_ATTR *attr, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + DWORD bytesNeeded = 0, lenBytes, size; + BOOL ret; + + ret = CRYPT_AsnEncodeOid(dwCertEncodingType, NULL, attr->pszObjId, + 0, NULL, NULL, &size); + if (ret) + { + bytesNeeded += size; + /* hack: a CERT_RDN_ATTR is identical to a CERT_NAME_VALUE beginning + * with dwValueType, so "cast" it to get its encoded size + */ + ret = CRYPT_AsnEncodeNameValue(dwCertEncodingType, + (CERT_NAME_VALUE *)&attr->dwValueType, NULL, &size); + if (ret) + { + bytesNeeded += size; + CRYPT_EncodeLen(bytesNeeded, NULL, &lenBytes); + bytesNeeded += 1 + lenBytes; + if (pbEncoded) + { + if (*pcbEncoded < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + *pbEncoded++ = ASN_CONSTRUCTOR | ASN_SEQUENCE; + CRYPT_EncodeLen(bytesNeeded - lenBytes - 1, pbEncoded, + &lenBytes); + pbEncoded += lenBytes; + size = bytesNeeded - 1 - lenBytes; + ret = CRYPT_AsnEncodeOid(dwCertEncodingType, NULL, + attr->pszObjId, 0, NULL, pbEncoded, &size); + if (ret) + { + pbEncoded += size; + size = bytesNeeded - 1 - lenBytes - size; + ret = CRYPT_AsnEncodeNameValue(dwCertEncodingType, + (CERT_NAME_VALUE *)&attr->dwValueType, pbEncoded, + &size); + } + } + } + *pcbEncoded = bytesNeeded; + } + } + return ret; +} + +static int BLOBComp(const void *l, const void *r) +{ + CRYPT_DER_BLOB *a = (CRYPT_DER_BLOB *)l, *b = (CRYPT_DER_BLOB *)r; + int ret; + + if (!(ret = memcmp(a->pbData, b->pbData, min(a->cbData, b->cbData)))) + ret = a->cbData - b->cbData; + return ret; +} + +/* This encodes as a SET OF, which in DER must be lexicographically sorted. + */ +static BOOL WINAPI CRYPT_AsnEncodeRdn(DWORD dwCertEncodingType, CERT_RDN *rdn, + BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + CRYPT_DER_BLOB *blobs = NULL; + + _SEH_TRY + { + DWORD bytesNeeded = 0, lenBytes, i; + + blobs = NULL; + ret = TRUE; + if (rdn->cRDNAttr) + { + blobs = CryptMemAlloc(rdn->cRDNAttr * sizeof(CRYPT_DER_BLOB)); + if (!blobs) + ret = FALSE; + else + memset(blobs, 0, rdn->cRDNAttr * sizeof(CRYPT_DER_BLOB)); + } + for (i = 0; ret && i < rdn->cRDNAttr; i++) + { + ret = CRYPT_AsnEncodeRdnAttr(dwCertEncodingType, &rdn->rgRDNAttr[i], + NULL, &blobs[i].cbData); + if (ret) + bytesNeeded += blobs[i].cbData; + } + if (ret) + { + CRYPT_EncodeLen(bytesNeeded, NULL, &lenBytes); + bytesNeeded += 1 + lenBytes; + if (pbEncoded) + { + if (*pcbEncoded < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + for (i = 0; ret && i < rdn->cRDNAttr; i++) + { + blobs[i].pbData = CryptMemAlloc(blobs[i].cbData); + if (!blobs[i].pbData) + ret = FALSE; + else + ret = CRYPT_AsnEncodeRdnAttr(dwCertEncodingType, + &rdn->rgRDNAttr[i], blobs[i].pbData, + &blobs[i].cbData); + } + if (ret) + { + qsort(blobs, rdn->cRDNAttr, sizeof(CRYPT_DER_BLOB), + BLOBComp); + *pbEncoded++ = ASN_CONSTRUCTOR | ASN_SETOF; + CRYPT_EncodeLen(bytesNeeded - lenBytes - 1, pbEncoded, + &lenBytes); + pbEncoded += lenBytes; + for (i = 0; ret && i < rdn->cRDNAttr; i++) + { + memcpy(pbEncoded, blobs[i].pbData, blobs[i].cbData); + pbEncoded += blobs[i].cbData; + } + } + } + } + *pcbEncoded = bytesNeeded; + } + if (blobs) + { + for (i = 0; i < rdn->cRDNAttr; i++) + CryptMemFree(blobs[i].pbData); + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + return FALSE; + } + _SEH_END + CryptMemFree(blobs); + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeName(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + const CERT_NAME_INFO *info = (const CERT_NAME_INFO *)pvStructInfo; + DWORD bytesNeeded = 0, lenBytes, size, i; + + TRACE("encoding name with %ld RDNs\n", info->cRDN); + ret = TRUE; + for (i = 0; ret && i < info->cRDN; i++) + { + ret = CRYPT_AsnEncodeRdn(dwCertEncodingType, &info->rgRDN[i], NULL, + &size); + if (ret) + bytesNeeded += size; + } + CRYPT_EncodeLen(bytesNeeded, NULL, &lenBytes); + bytesNeeded += 1 + lenBytes; + if (ret) + { + if (!pbEncoded) + *pcbEncoded = bytesNeeded; + else + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, + pbEncoded, pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_SEQUENCEOF; + CRYPT_EncodeLen(bytesNeeded - lenBytes - 1, pbEncoded, + &lenBytes); + pbEncoded += lenBytes; + for (i = 0; ret && i < info->cRDN; i++) + { + size = bytesNeeded; + ret = CRYPT_AsnEncodeRdn(dwCertEncodingType, + &info->rgRDN[i], pbEncoded, &size); + if (ret) + { + pbEncoded += size; + bytesNeeded -= size; + } + } + } + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeBool(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL val = *(const BOOL *)pvStructInfo, ret; + + TRACE("%d\n", val); + + if (!pbEncoded) + { + *pcbEncoded = 3; + ret = TRUE; + } + else if (*pcbEncoded < 3) + { + *pcbEncoded = 3; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + *pcbEncoded = 3; + *pbEncoded++ = ASN_BOOL; + *pbEncoded++ = 1; + *pbEncoded++ = val ? 0xff : 0; + ret = TRUE; + } + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL CRYPT_AsnEncodeAltNameEntry(const CERT_ALT_NAME_ENTRY *entry, + BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + DWORD dataLen; + + ret = TRUE; + switch (entry->dwAltNameChoice) + { + case CERT_ALT_NAME_RFC822_NAME: + case CERT_ALT_NAME_DNS_NAME: + case CERT_ALT_NAME_URL: + if (entry->u.pwszURL) + { + DWORD i; + + /* Not + 1: don't encode the NULL-terminator */ + dataLen = lstrlenW(entry->u.pwszURL); + for (i = 0; ret && i < dataLen; i++) + { + if (entry->u.pwszURL[i] > 0x7f) + { + SetLastError(CRYPT_E_INVALID_IA5_STRING); + ret = FALSE; + *pcbEncoded = i; + } + } + } + else + dataLen = 0; + break; + case CERT_ALT_NAME_IP_ADDRESS: + dataLen = entry->u.IPAddress.cbData; + break; + case CERT_ALT_NAME_REGISTERED_ID: + /* FIXME: encode OID */ + case CERT_ALT_NAME_OTHER_NAME: + case CERT_ALT_NAME_DIRECTORY_NAME: + FIXME("name type %ld unimplemented\n", entry->dwAltNameChoice); + return FALSE; + default: + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return FALSE; + } + if (ret) + { + DWORD bytesNeeded, lenBytes; + + CRYPT_EncodeLen(dataLen, NULL, &lenBytes); + bytesNeeded = 1 + dataLen + lenBytes; + if (!pbEncoded) + *pcbEncoded = bytesNeeded; + else if (*pcbEncoded < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + *pcbEncoded = bytesNeeded; + ret = FALSE; + } + else + { + *pbEncoded++ = ASN_CONTEXT | (entry->dwAltNameChoice - 1); + CRYPT_EncodeLen(dataLen, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + switch (entry->dwAltNameChoice) + { + case CERT_ALT_NAME_RFC822_NAME: + case CERT_ALT_NAME_DNS_NAME: + case CERT_ALT_NAME_URL: + { + DWORD i; + + for (i = 0; i < dataLen; i++) + *pbEncoded++ = (BYTE)entry->u.pwszURL[i]; + break; + } + case CERT_ALT_NAME_IP_ADDRESS: + memcpy(pbEncoded, entry->u.IPAddress.pbData, dataLen); + break; + } + if (ret) + *pcbEncoded = bytesNeeded; + } + } + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeAltName(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + const CERT_ALT_NAME_INFO *info = + (const CERT_ALT_NAME_INFO *)pvStructInfo; + + DWORD bytesNeeded, dataLen, lenBytes, i; + + ret = TRUE; + /* FIXME: should check that cAltEntry is not bigger than 0xff, since we + * can't encode an erroneous entry index if it's bigger than this. + */ + for (i = 0, dataLen = 0; ret && i < info->cAltEntry; i++) + { + DWORD len; + + ret = CRYPT_AsnEncodeAltNameEntry(&info->rgAltEntry[i], NULL, + &len); + if (ret) + dataLen += len; + else if (GetLastError() == CRYPT_E_INVALID_IA5_STRING) + { + /* CRYPT_AsnEncodeAltNameEntry encoded the index of + * the bad character, now set the index of the bad + * entry + */ + *pcbEncoded = (BYTE)i << + CERT_ALT_NAME_ENTRY_ERR_INDEX_SHIFT | len; + } + } + if (ret) + { + CRYPT_EncodeLen(dataLen, NULL, &lenBytes); + bytesNeeded = 1 + lenBytes + dataLen; + if (!pbEncoded) + { + *pcbEncoded = bytesNeeded; + ret = TRUE; + } + else + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, + pbEncoded, pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_SEQUENCEOF; + CRYPT_EncodeLen(dataLen, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + for (i = 0; ret && i < info->cAltEntry; i++) + { + DWORD len = dataLen; + + ret = CRYPT_AsnEncodeAltNameEntry(&info->rgAltEntry[i], + pbEncoded, &len); + if (ret) + { + pbEncoded += len; + dataLen -= len; + } + } + } + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeBasicConstraints2(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + const CERT_BASIC_CONSTRAINTS2_INFO *info = + (const CERT_BASIC_CONSTRAINTS2_INFO *)pvStructInfo; + struct AsnEncodeSequenceItem items[2] = { { 0 } }; + DWORD cItem = 0; + + if (info->fCA) + { + items[cItem].pvStructInfo = &info->fCA; + items[cItem].encodeFunc = CRYPT_AsnEncodeBool; + cItem++; + } + if (info->fPathLenConstraint) + { + items[cItem].pvStructInfo = &info->dwPathLenConstraint; + items[cItem].encodeFunc = CRYPT_AsnEncodeInt; + cItem++; + } + ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items, cItem, + dwFlags, pEncodePara, pbEncoded, pcbEncoded); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeRsaPubKey(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + const BLOBHEADER *hdr = + (const BLOBHEADER *)pvStructInfo; + + if (hdr->bType != PUBLICKEYBLOB) + { + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + ret = FALSE; + } + else + { + const RSAPUBKEY *rsaPubKey = (const RSAPUBKEY *) + ((const BYTE *)pvStructInfo + sizeof(BLOBHEADER)); + CRYPT_INTEGER_BLOB blob = { rsaPubKey->bitlen / 8, + (BYTE *)pvStructInfo + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) }; + struct AsnEncodeSequenceItem items[] = { + { &blob, CRYPT_AsnEncodeUnsignedInteger, 0 }, + { &rsaPubKey->pubexp, CRYPT_AsnEncodeInt, 0 }, + }; + + ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), dwFlags, pEncodePara, pbEncoded, + pcbEncoded); + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeOctets(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + const CRYPT_DATA_BLOB *blob = (const CRYPT_DATA_BLOB *)pvStructInfo; + DWORD bytesNeeded, lenBytes; + + TRACE("(%ld, %p), %08lx, %p, %p, %ld\n", blob->cbData, blob->pbData, + dwFlags, pEncodePara, pbEncoded, *pcbEncoded); + + CRYPT_EncodeLen(blob->cbData, NULL, &lenBytes); + bytesNeeded = 1 + lenBytes + blob->cbData; + if (!pbEncoded) + { + *pcbEncoded = bytesNeeded; + ret = TRUE; + } + else + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, + pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_OCTETSTRING; + CRYPT_EncodeLen(blob->cbData, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + if (blob->cbData) + memcpy(pbEncoded, blob->pbData, blob->cbData); + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeBits(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + const CRYPT_BIT_BLOB *blob = (const CRYPT_BIT_BLOB *)pvStructInfo; + DWORD bytesNeeded, lenBytes, dataBytes; + BYTE unusedBits; + + /* yep, MS allows cUnusedBits to be >= 8 */ + if (!blob->cUnusedBits) + { + dataBytes = blob->cbData; + unusedBits = 0; + } + else if (blob->cbData * 8 > blob->cUnusedBits) + { + dataBytes = (blob->cbData * 8 - blob->cUnusedBits) / 8 + 1; + unusedBits = blob->cUnusedBits >= 8 ? blob->cUnusedBits / 8 : + blob->cUnusedBits; + } + else + { + dataBytes = 0; + unusedBits = 0; + } + CRYPT_EncodeLen(dataBytes + 1, NULL, &lenBytes); + bytesNeeded = 1 + lenBytes + dataBytes + 1; + if (!pbEncoded) + { + *pcbEncoded = bytesNeeded; + ret = TRUE; + } + else + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, + pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_BITSTRING; + CRYPT_EncodeLen(dataBytes + 1, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + *pbEncoded++ = unusedBits; + if (dataBytes) + { + BYTE mask = 0xff << unusedBits; + + if (dataBytes > 1) + { + memcpy(pbEncoded, blob->pbData, dataBytes - 1); + pbEncoded += dataBytes - 1; + } + *pbEncoded = *(blob->pbData + dataBytes - 1) & mask; + } + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeBitsSwapBytes(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + const CRYPT_BIT_BLOB *blob = (const CRYPT_BIT_BLOB *)pvStructInfo; + CRYPT_BIT_BLOB newBlob = { blob->cbData, NULL, blob->cUnusedBits }; + + ret = TRUE; + if (newBlob.cbData) + { + newBlob.pbData = CryptMemAlloc(newBlob.cbData); + if (newBlob.pbData) + { + DWORD i; + + for (i = 0; i < newBlob.cbData; i++) + newBlob.pbData[newBlob.cbData - i - 1] = blob->pbData[i]; + } + else + ret = FALSE; + } + if (ret) + ret = CRYPT_AsnEncodeBits(dwCertEncodingType, lpszStructType, + &newBlob, dwFlags, pEncodePara, pbEncoded, pcbEncoded); + CryptMemFree(newBlob.pbData); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + CRYPT_INTEGER_BLOB blob = { sizeof(INT), (BYTE *)pvStructInfo }; + + return CRYPT_AsnEncodeInteger(dwCertEncodingType, X509_MULTI_BYTE_INTEGER, + &blob, dwFlags, pEncodePara, pbEncoded, pcbEncoded); +} + +static BOOL WINAPI CRYPT_AsnEncodeInteger(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + DWORD significantBytes, lenBytes; + BYTE padByte = 0, bytesNeeded; + BOOL pad = FALSE; + const CRYPT_INTEGER_BLOB *blob = + (const CRYPT_INTEGER_BLOB *)pvStructInfo; + + significantBytes = blob->cbData; + if (significantBytes) + { + if (blob->pbData[significantBytes - 1] & 0x80) + { + /* negative, lop off leading (little-endian) 0xffs */ + for (; significantBytes > 0 && + blob->pbData[significantBytes - 1] == 0xff; significantBytes--) + ; + if (blob->pbData[significantBytes - 1] < 0x80) + { + padByte = 0xff; + pad = TRUE; + } + } + else + { + /* positive, lop off leading (little-endian) zeroes */ + for (; significantBytes > 0 && + !blob->pbData[significantBytes - 1]; significantBytes--) + ; + if (significantBytes == 0) + significantBytes = 1; + if (blob->pbData[significantBytes - 1] > 0x7f) + { + padByte = 0; + pad = TRUE; + } + } + } + if (pad) + CRYPT_EncodeLen(significantBytes + 1, NULL, &lenBytes); + else + CRYPT_EncodeLen(significantBytes, NULL, &lenBytes); + bytesNeeded = 1 + lenBytes + significantBytes; + if (pad) + bytesNeeded++; + if (!pbEncoded) + { + *pcbEncoded = bytesNeeded; + ret = TRUE; + } + else + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, + pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_INTEGER; + if (pad) + { + CRYPT_EncodeLen(significantBytes + 1, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + *pbEncoded++ = padByte; + } + else + { + CRYPT_EncodeLen(significantBytes, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + } + for (; significantBytes > 0; significantBytes--) + *(pbEncoded++) = blob->pbData[significantBytes - 1]; + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeUnsignedInteger(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + DWORD significantBytes, lenBytes; + BYTE bytesNeeded; + BOOL pad = FALSE; + const CRYPT_INTEGER_BLOB *blob = + (const CRYPT_INTEGER_BLOB *)pvStructInfo; + + significantBytes = blob->cbData; + if (significantBytes) + { + /* positive, lop off leading (little-endian) zeroes */ + for (; significantBytes > 0 && !blob->pbData[significantBytes - 1]; + significantBytes--) + ; + if (significantBytes == 0) + significantBytes = 1; + if (blob->pbData[significantBytes - 1] > 0x7f) + pad = TRUE; + } + if (pad) + CRYPT_EncodeLen(significantBytes + 1, NULL, &lenBytes); + else + CRYPT_EncodeLen(significantBytes, NULL, &lenBytes); + bytesNeeded = 1 + lenBytes + significantBytes; + if (pad) + bytesNeeded++; + if (!pbEncoded) + { + *pcbEncoded = bytesNeeded; + ret = TRUE; + } + else + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, + pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_INTEGER; + if (pad) + { + CRYPT_EncodeLen(significantBytes + 1, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + *pbEncoded++ = 0; + } + else + { + CRYPT_EncodeLen(significantBytes, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + } + for (; significantBytes > 0; significantBytes--) + *(pbEncoded++) = blob->pbData[significantBytes - 1]; + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeEnumerated(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + CRYPT_INTEGER_BLOB blob; + BOOL ret; + + /* Encode as an unsigned integer, then change the tag to enumerated */ + blob.cbData = sizeof(DWORD); + blob.pbData = (BYTE *)pvStructInfo; + ret = CRYPT_AsnEncodeUnsignedInteger(dwCertEncodingType, + X509_MULTI_BYTE_UINT, &blob, dwFlags, pEncodePara, pbEncoded, pcbEncoded); + if (ret && pbEncoded) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + pbEncoded[0] = ASN_ENUMERATED; + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeUtcTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + SYSTEMTIME sysTime; + /* sorry, magic number: enough for tag, len, YYMMDDHHMMSSZ\0. I use a + * temporary buffer because the output buffer is not NULL-terminated. + */ + char buf[16]; + static const DWORD bytesNeeded = sizeof(buf) - 1; + + if (!pbEncoded) + { + *pcbEncoded = bytesNeeded; + ret = TRUE; + } + else + { + /* Sanity check the year, this is a two-digit year format */ + ret = FileTimeToSystemTime((const FILETIME *)pvStructInfo, + &sysTime); + if (ret && (sysTime.wYear < 1950 || sysTime.wYear > 2050)) + { + SetLastError(CRYPT_E_BAD_ENCODE); + ret = FALSE; + } + if (ret) + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, + pbEncoded, pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + buf[0] = ASN_UTCTIME; + buf[1] = bytesNeeded - 2; + snprintf(buf + 2, sizeof(buf) - 2, + "%02d%02d%02d%02d%02d%02dZ", sysTime.wYear >= 2000 ? + sysTime.wYear - 2000 : sysTime.wYear - 1900, + sysTime.wDay, sysTime.wMonth, sysTime.wHour, + sysTime.wMinute, sysTime.wSecond); + memcpy(pbEncoded, buf, bytesNeeded); + } + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeGeneralizedTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + SYSTEMTIME sysTime; + /* sorry, magic number: enough for tag, len, YYYYMMDDHHMMSSZ\0. I use a + * temporary buffer because the output buffer is not NULL-terminated. + */ + char buf[18]; + static const DWORD bytesNeeded = sizeof(buf) - 1; + + if (!pbEncoded) + { + *pcbEncoded = bytesNeeded; + ret = TRUE; + } + else + { + ret = FileTimeToSystemTime((const FILETIME *)pvStructInfo, + &sysTime); + if (ret) + ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, + pcbEncoded, bytesNeeded); + if (ret) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + buf[0] = ASN_GENERALTIME; + buf[1] = bytesNeeded - 2; + snprintf(buf + 2, sizeof(buf) - 2, "%04d%02d%02d%02d%02d%02dZ", + sysTime.wYear, sysTime.wDay, sysTime.wMonth, sysTime.wHour, + sysTime.wMinute, sysTime.wSecond); + memcpy(pbEncoded, buf, bytesNeeded); + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeChoiceOfTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + SYSTEMTIME sysTime; + + /* Check the year, if it's in the UTCTime range call that encode func */ + if (!FileTimeToSystemTime((const FILETIME *)pvStructInfo, &sysTime)) + return FALSE; + if (sysTime.wYear >= 1950 && sysTime.wYear <= 2050) + ret = CRYPT_AsnEncodeUtcTime(dwCertEncodingType, lpszStructType, + pvStructInfo, dwFlags, pEncodePara, pbEncoded, pcbEncoded); + else + ret = CRYPT_AsnEncodeGeneralizedTime(dwCertEncodingType, + lpszStructType, pvStructInfo, dwFlags, pEncodePara, pbEncoded, + pcbEncoded); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnEncodeSequenceOfAny(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + BOOL ret; + + _SEH_TRY + { + DWORD bytesNeeded, dataLen, lenBytes, i; + const CRYPT_SEQUENCE_OF_ANY *seq = + (const CRYPT_SEQUENCE_OF_ANY *)pvStructInfo; + + for (i = 0, dataLen = 0; i < seq->cValue; i++) + dataLen += seq->rgValue[i].cbData; + CRYPT_EncodeLen(dataLen, NULL, &lenBytes); + bytesNeeded = 1 + lenBytes + dataLen; + if (!pbEncoded) + { + *pcbEncoded = bytesNeeded; + ret = TRUE; + } + else + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, + pcbEncoded, bytesNeeded))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_SEQUENCEOF; + CRYPT_EncodeLen(dataLen, pbEncoded, &lenBytes); + pbEncoded += lenBytes; + for (i = 0; i < seq->cValue; i++) + { + memcpy(pbEncoded, seq->rgValue[i].pbData, + seq->rgValue[i].cbData); + pbEncoded += seq->rgValue[i].cbData; + } + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +BOOL WINAPI CryptEncodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType, + const void *pvStructInfo, DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, + void *pvEncoded, DWORD *pcbEncoded) +{ + BOOL ret = FALSE; + HMODULE lib = NULL; + CryptEncodeObjectExFunc encodeFunc = NULL; + + TRACE("(0x%08lx, %s, %p, 0x%08lx, %p, %p, %p)\n", dwCertEncodingType, + debugstr_a(lpszStructType), pvStructInfo, dwFlags, pEncodePara, + pvEncoded, pcbEncoded); + + if (!pvEncoded && !pcbEncoded) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING + && (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) != PKCS_7_ASN_ENCODING) + { + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + SetLastError(NOERROR); + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG && pvEncoded) + *(BYTE **)pvEncoded = NULL; + if (!HIWORD(lpszStructType)) + { + switch (LOWORD(lpszStructType)) + { + case (WORD)X509_CERT: + encodeFunc = CRYPT_AsnEncodeCert; + break; + case (WORD)X509_CERT_TO_BE_SIGNED: + encodeFunc = CRYPT_AsnEncodeCertInfo; + break; + case (WORD)X509_CERT_CRL_TO_BE_SIGNED: + encodeFunc = CRYPT_AsnEncodeCRLInfo; + break; + case (WORD)X509_EXTENSIONS: + encodeFunc = CRYPT_AsnEncodeExtensions; + break; + case (WORD)X509_NAME: + encodeFunc = CRYPT_AsnEncodeName; + break; + case (WORD)X509_PUBLIC_KEY_INFO: + encodeFunc = CRYPT_AsnEncodePubKeyInfo; + break; + case (WORD)X509_ALTERNATE_NAME: + encodeFunc = CRYPT_AsnEncodeAltName; + break; + case (WORD)X509_BASIC_CONSTRAINTS2: + encodeFunc = CRYPT_AsnEncodeBasicConstraints2; + break; + case (WORD)RSA_CSP_PUBLICKEYBLOB: + encodeFunc = CRYPT_AsnEncodeRsaPubKey; + break; + case (WORD)X509_OCTET_STRING: + encodeFunc = CRYPT_AsnEncodeOctets; + break; + case (WORD)X509_BITS: + case (WORD)X509_KEY_USAGE: + encodeFunc = CRYPT_AsnEncodeBits; + break; + case (WORD)X509_INTEGER: + encodeFunc = CRYPT_AsnEncodeInt; + break; + case (WORD)X509_MULTI_BYTE_INTEGER: + encodeFunc = CRYPT_AsnEncodeInteger; + break; + case (WORD)X509_MULTI_BYTE_UINT: + encodeFunc = CRYPT_AsnEncodeUnsignedInteger; + break; + case (WORD)X509_ENUMERATED: + encodeFunc = CRYPT_AsnEncodeEnumerated; + break; + case (WORD)X509_CHOICE_OF_TIME: + encodeFunc = CRYPT_AsnEncodeChoiceOfTime; + break; + case (WORD)X509_SEQUENCE_OF_ANY: + encodeFunc = CRYPT_AsnEncodeSequenceOfAny; + break; + case (WORD)PKCS_UTC_TIME: + encodeFunc = CRYPT_AsnEncodeUtcTime; + break; + default: + FIXME("%d: unimplemented\n", LOWORD(lpszStructType)); + } + } + else if (!strcmp(lpszStructType, szOID_CERT_EXTENSIONS)) + encodeFunc = CRYPT_AsnEncodeExtensions; + else if (!strcmp(lpszStructType, szOID_RSA_signingTime)) + encodeFunc = CRYPT_AsnEncodeUtcTime; + else if (!strcmp(lpszStructType, szOID_CRL_REASON_CODE)) + encodeFunc = CRYPT_AsnEncodeEnumerated; + else if (!strcmp(lpszStructType, szOID_KEY_USAGE)) + encodeFunc = CRYPT_AsnEncodeBits; + else if (!strcmp(lpszStructType, szOID_SUBJECT_KEY_IDENTIFIER)) + encodeFunc = CRYPT_AsnEncodeOctets; + else if (!strcmp(lpszStructType, szOID_BASIC_CONSTRAINTS2)) + encodeFunc = CRYPT_AsnEncodeBasicConstraints2; + else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME)) + encodeFunc = CRYPT_AsnEncodeAltName; + else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME2)) + encodeFunc = CRYPT_AsnEncodeAltName; + else if (!strcmp(lpszStructType, szOID_NEXT_UPDATE_LOCATION)) + encodeFunc = CRYPT_AsnEncodeAltName; + else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME)) + encodeFunc = CRYPT_AsnEncodeAltName; + else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME2)) + encodeFunc = CRYPT_AsnEncodeAltName; + else + TRACE("OID %s not found or unimplemented, looking for DLL\n", + debugstr_a(lpszStructType)); + if (!encodeFunc) + encodeFunc = (CryptEncodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType, + lpszStructType, CRYPT_OID_ENCODE_OBJECT_EX_FUNC, &lib); + if (encodeFunc) + ret = encodeFunc(dwCertEncodingType, lpszStructType, pvStructInfo, + dwFlags, pEncodePara, pvEncoded, pcbEncoded); + else + SetLastError(ERROR_FILE_NOT_FOUND); + if (lib) + FreeLibrary(lib); + return ret; +} + +BOOL WINAPI CryptDecodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType, + const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, + DWORD *pcbStructInfo) +{ + BOOL ret = FALSE; + HMODULE lib; + CryptDecodeObjectFunc pCryptDecodeObject; + + TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p)\n", dwCertEncodingType, + debugstr_a(lpszStructType), pbEncoded, cbEncoded, dwFlags, + pvStructInfo, pcbStructInfo); + + if (!pvStructInfo && !pcbStructInfo) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + /* Try registered DLL first.. */ + pCryptDecodeObject = + (CryptDecodeObjectFunc)CRYPT_GetFunc(dwCertEncodingType, + lpszStructType, CRYPT_OID_DECODE_OBJECT_FUNC, &lib); + if (pCryptDecodeObject) + { + ret = pCryptDecodeObject(dwCertEncodingType, lpszStructType, + pbEncoded, cbEncoded, dwFlags, pvStructInfo, pcbStructInfo); + FreeLibrary(lib); + } + else + { + /* If not, use CryptDecodeObjectEx */ + ret = CryptDecodeObjectEx(dwCertEncodingType, lpszStructType, pbEncoded, + cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo); + } + return ret; +} + +/* Gets the number of length bytes from the given (leading) length byte */ +#define GET_LEN_BYTES(b) ((b) <= 0x7f ? 1 : 1 + ((b) & 0x7f)) + +/* Helper function to get the encoded length of the data starting at pbEncoded, + * where pbEncoded[0] is the tag. If the data are too short to contain a + * length or if the length is too large for cbEncoded, sets an appropriate + * error code and returns FALSE. + */ +static BOOL WINAPI CRYPT_GetLen(const BYTE *pbEncoded, DWORD cbEncoded, + DWORD *len) +{ + BOOL ret; + + if (cbEncoded <= 1) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + else if (pbEncoded[1] <= 0x7f) + { + if (pbEncoded[1] + 1 > cbEncoded) + { + SetLastError(CRYPT_E_ASN1_EOD); + ret = FALSE; + } + else + { + *len = pbEncoded[1]; + ret = TRUE; + } + } + else + { + BYTE lenLen = GET_LEN_BYTES(pbEncoded[1]); + + if (lenLen > sizeof(DWORD) + 1) + { + SetLastError(CRYPT_E_ASN1_LARGE); + ret = FALSE; + } + else if (lenLen + 2 > cbEncoded) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + else + { + DWORD out = 0; + + pbEncoded += 2; + while (--lenLen) + { + out <<= 8; + out |= *pbEncoded++; + } + if (out + lenLen + 1 > cbEncoded) + { + SetLastError(CRYPT_E_ASN1_EOD); + ret = FALSE; + } + else + { + *len = out; + ret = TRUE; + } + } + } + return ret; +} + +/* Helper function to check *pcbStructInfo, set it to the required size, and + * optionally to allocate memory. Assumes pvStructInfo is not NULL. + * If CRYPT_DECODE_ALLOC_FLAG is set in dwFlags, *pvStructInfo will be set to a + * pointer to the newly allocated memory. + */ +static BOOL CRYPT_DecodeEnsureSpace(DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo, + DWORD bytesNeeded) +{ + BOOL ret = TRUE; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + { + if (pDecodePara && pDecodePara->pfnAlloc) + *(BYTE **)pvStructInfo = pDecodePara->pfnAlloc(bytesNeeded); + else + *(BYTE **)pvStructInfo = LocalAlloc(0, bytesNeeded); + if (!*(BYTE **)pvStructInfo) + ret = FALSE; + else + *pcbStructInfo = bytesNeeded; + } + else if (*pcbStructInfo < bytesNeeded) + { + *pcbStructInfo = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + return ret; +} + +/* A few of the members need explanation: + * offset: + * A sequence is decoded into a struct. The offset member is the + * offset of this item within that struct. + * decodeFunc: + * The decoder function to use. If this is NULL, then the member isn't + * decoded, but minSize space is reserved for it. + * minSize: + * The minimum amount of space occupied after decoding. You must set this. + * optional: + * If true, and a decoding function fails with CRYPT_E_ASN1_BADTAG, then + * minSize space is filled with 0 for this member. (Any other failure + * results in CRYPT_AsnDecodeSequence failing.) + * hasPointer, pointerOffset, minSize: + * If the item has dynamic data, set hasPointer to TRUE, pointerOffset to + * the offset within the (outer) struct of the data pointer (or to the + * first data pointer, if more than one exist). + * size: + * Used by CRYPT_AsnDecodeSequence, not for your use. + */ +struct AsnDecodeSequenceItem +{ + DWORD offset; + CryptDecodeObjectExFunc decodeFunc; + DWORD minSize; + BOOL optional; + BOOL hasPointer; + DWORD pointerOffset; + DWORD size; +}; + +/* This decodes an arbitrary sequence into a contiguous block of memory + * (basically, a struct.) Each element being decoded is described by a struct + * AsnDecodeSequenceItem, see above. + * startingPointer is an optional pointer to the first place where dynamic + * data will be stored. If you know the starting offset, you may pass it + * here. Otherwise, pass NULL, and one will be inferred from the items. + * Each item decoder is never called with CRYPT_DECODE_ALLOC_FLAG set. + * If any undecoded data are left over, fails with CRYPT_E_ASN1_CORRUPT. + */ +static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType, + struct AsnDecodeSequenceItem items[], DWORD cItem, const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara, + void *pvStructInfo, DWORD *pcbStructInfo, void *startingPointer) +{ + BOOL ret; + + TRACE("%p, %ld, %p, %ld, %08lx, %p, %p, %ld, %p\n", items, cItem, pbEncoded, + cbEncoded, dwFlags, pDecodePara, pvStructInfo, *pcbStructInfo, + startingPointer); + + if (pbEncoded[0] == ASN_SEQUENCE) + { + DWORD dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + DWORD i, bytesNeeded = 0, minSize = 0; + const BYTE *ptr; + + ptr = pbEncoded + 1 + lenBytes; + for (i = 0; ret && i < cItem; i++) + { + DWORD nextItemLen; + + minSize += items[i].minSize; + if (cbEncoded - (ptr - pbEncoded) != 0) + { + if ((ret = CRYPT_GetLen(ptr, cbEncoded - (ptr - pbEncoded), + &nextItemLen))) + { + BYTE nextItemLenBytes = GET_LEN_BYTES(ptr[1]); + + if (items[i].decodeFunc) + { + TRACE("sizing item %ld\n", i); + ret = items[i].decodeFunc(dwCertEncodingType, NULL, + ptr, 1 + nextItemLenBytes + nextItemLen, + dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, + &items[i].size); + if (ret) + { + /* Account for alignment padding */ + bytesNeeded += items[i].size; + if (items[i].size % sizeof(DWORD)) + bytesNeeded += sizeof(DWORD) - + items[i].size % sizeof(DWORD); + ptr += 1 + nextItemLenBytes + nextItemLen; + } + else if (items[i].optional && + GetLastError() == CRYPT_E_ASN1_BADTAG) + { + TRACE("skipping optional item %ld\n", i); + bytesNeeded += items[i].minSize; + SetLastError(NOERROR); + ret = TRUE; + } + else + TRACE("item %ld failed: %08lx\n", i, + GetLastError()); + } + else + bytesNeeded += items[i].minSize; + } + } + else if (items[i].optional) + bytesNeeded += items[i].minSize; + else + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + } + if (cbEncoded - (ptr - pbEncoded) != 0) + { + TRACE("%ld remaining bytes, failing\n", cbEncoded - + (ptr - pbEncoded)); + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + if (ret) + { + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, bytesNeeded))) + { + BYTE *nextData; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + if (startingPointer) + nextData = (BYTE *)startingPointer; + else + nextData = (BYTE *)pvStructInfo + minSize; + memset(pvStructInfo, 0, minSize); + ptr = pbEncoded + 1 + lenBytes; + for (i = 0; ret && i < cItem; i++) + { + if (cbEncoded - (ptr - pbEncoded) != 0) + { + DWORD nextItemLen; + BYTE nextItemLenBytes = GET_LEN_BYTES(ptr[1]); + + CRYPT_GetLen(ptr, cbEncoded - (ptr - pbEncoded), + &nextItemLen); + if (items[i].hasPointer) + { + *(BYTE **)((BYTE *)pvStructInfo + + items[i].pointerOffset) = nextData; + } + if (items[i].decodeFunc) + { + TRACE("decoding item %ld\n", i); + ret = items[i].decodeFunc(dwCertEncodingType, + NULL, ptr, 1 + nextItemLenBytes + nextItemLen, + dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, + (BYTE *)pvStructInfo + items[i].offset, + &items[i].size); + if (!ret) + TRACE("item %ld failed: %08lx\n", i, + GetLastError()); + } + else + items[i].size = items[i].minSize; + if (ret) + { + if (items[i].hasPointer && + items[i].size > items[i].minSize) + { + nextData += items[i].size - + items[i].minSize; + /* align nextData to DWORD boundaries */ + if (items[i].size % sizeof(DWORD)) + { + nextData += sizeof(DWORD) - + items[i].size % sizeof(DWORD); + } + } + ptr += 1 + nextItemLenBytes + nextItemLen; + } + else if (items[i].optional && + GetLastError() == CRYPT_E_ASN1_BADTAG) + { + SetLastError(NOERROR); + ret = TRUE; + } + } + else if (!items[i].optional) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + } + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +/* Decodes a DER-encoded BLOB into a CRYPT_DER_BLOB struct pointed to by + * pvStructInfo. The BLOB must be non-empty, otherwise the last error is set + * to CRYPT_E_ASN1_CORRUPT. + * Warning: assumes the CRYPT_DER_BLOB pointed to by pvStructInfo has pbData + * set! + */ +static BOOL WINAPI CRYPT_AsnDecodeDerBlob(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + DWORD dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + DWORD bytesNeeded = sizeof(CRYPT_DER_BLOB); + + if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG)) + bytesNeeded += 1 + lenBytes + dataLen; + + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, + pvStructInfo, pcbStructInfo, bytesNeeded))) + { + CRYPT_DER_BLOB *blob = (CRYPT_DER_BLOB *)pvStructInfo; + + blob->cbData = 1 + lenBytes + dataLen; + if (blob->cbData) + { + if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) + blob->pbData = (BYTE *)pbEncoded; + else + { + assert(blob->pbData); + memcpy(blob->pbData, pbEncoded, blob->cbData); + } + } + else + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + } + } + return ret; +} + +/* Like CRYPT_AsnDecodeBitsInternal, but swaps the bytes */ +static BOOL WINAPI CRYPT_AsnDecodeBitsSwapBytes(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + TRACE("(%p, %ld, 0x%08lx, %p, %p, %ld)\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + /* Can't use the CRYPT_DECODE_NOCOPY_FLAG, because we modify the bytes in- + * place. + */ + ret = CRYPT_AsnDecodeBitsInternal(dwCertEncodingType, lpszStructType, + pbEncoded, cbEncoded, dwFlags & ~CRYPT_DECODE_NOCOPY_FLAG, pDecodePara, + pvStructInfo, pcbStructInfo); + if (ret && pvStructInfo) + { + CRYPT_BIT_BLOB *blob = (CRYPT_BIT_BLOB *)pvStructInfo; + + if (blob->cbData) + { + DWORD i; + BYTE temp; + + for (i = 0; i < blob->cbData / 2; i++) + { + temp = blob->pbData[i]; + blob->pbData[i] = blob->pbData[blob->cbData - i - 1]; + blob->pbData[blob->cbData - i - 1] = temp; + } + } + } + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeCert(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = TRUE; + + TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + _SEH_TRY + { + struct AsnDecodeSequenceItem items[] = { + { offsetof(CERT_SIGNED_CONTENT_INFO, ToBeSigned), + CRYPT_AsnDecodeDerBlob, sizeof(CRYPT_DER_BLOB), FALSE, TRUE, + offsetof(CERT_SIGNED_CONTENT_INFO, ToBeSigned.pbData), 0 }, + { offsetof(CERT_SIGNED_CONTENT_INFO, SignatureAlgorithm), + CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER), + FALSE, TRUE, offsetof(CERT_SIGNED_CONTENT_INFO, + SignatureAlgorithm.pszObjId), 0 }, + { offsetof(CERT_SIGNED_CONTENT_INFO, Signature), + CRYPT_AsnDecodeBitsSwapBytes, sizeof(CRYPT_BIT_BLOB), FALSE, TRUE, + offsetof(CERT_SIGNED_CONTENT_INFO, Signature.pbData), 0 }, + }; + + if (dwFlags & CRYPT_DECODE_NO_SIGNATURE_BYTE_REVERSAL_FLAG) + items[2].decodeFunc = CRYPT_AsnDecodeBitsInternal; + ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, NULL); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeCertVersion(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + if (pbEncoded[0] == (ASN_CONTEXT | ASN_CONSTRUCTOR)) + { + DWORD dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + + ret = CRYPT_AsnDecodeInt(dwCertEncodingType, X509_INTEGER, + pbEncoded + 1 + lenBytes, dataLen, dwFlags, pDecodePara, + pvStructInfo, pcbStructInfo); + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeValidity(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + struct AsnDecodeSequenceItem items[] = { + { offsetof(CERT_PRIVATE_KEY_VALIDITY, NotBefore), + CRYPT_AsnDecodeChoiceOfTime, sizeof(FILETIME), FALSE, FALSE, 0 }, + { offsetof(CERT_PRIVATE_KEY_VALIDITY, NotAfter), + CRYPT_AsnDecodeChoiceOfTime, sizeof(FILETIME), FALSE, FALSE, 0 }, + }; + + ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, NULL); + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeCertExtensions(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + if (pbEncoded[0] == (ASN_CONTEXT | ASN_CONSTRUCTOR | 3)) + { + DWORD dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + + ret = CRYPT_AsnDecodeExtensionsInternal(dwCertEncodingType, + X509_EXTENSIONS, pbEncoded + 1 + lenBytes, dataLen, dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo); + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeCertInfo(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = TRUE; + + TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + _SEH_TRY + { + struct AsnDecodeSequenceItem items[] = { + { offsetof(CERT_INFO, dwVersion), CRYPT_AsnDecodeCertVersion, + sizeof(DWORD), TRUE, FALSE, 0, 0 }, + { offsetof(CERT_INFO, SerialNumber), CRYPT_AsnDecodeIntegerInternal, + sizeof(CRYPT_INTEGER_BLOB), FALSE, TRUE, offsetof(CERT_INFO, + SerialNumber.pbData), 0 }, + { offsetof(CERT_INFO, SignatureAlgorithm), CRYPT_AsnDecodeAlgorithmId, + sizeof(CRYPT_ALGORITHM_IDENTIFIER), FALSE, TRUE, offsetof(CERT_INFO, + SignatureAlgorithm.pszObjId), 0 }, + { offsetof(CERT_INFO, Issuer), CRYPT_AsnDecodeDerBlob, + sizeof(CRYPT_DER_BLOB), FALSE, TRUE, offsetof(CERT_INFO, + Issuer.pbData) }, + { offsetof(CERT_INFO, NotBefore), CRYPT_AsnDecodeValidity, + sizeof(CERT_PRIVATE_KEY_VALIDITY), FALSE, FALSE, 0 }, + { offsetof(CERT_INFO, Subject), CRYPT_AsnDecodeDerBlob, + sizeof(CRYPT_DER_BLOB), FALSE, TRUE, offsetof(CERT_INFO, + Subject.pbData) }, + { offsetof(CERT_INFO, SubjectPublicKeyInfo), CRYPT_AsnDecodePubKeyInfo, + sizeof(CERT_PUBLIC_KEY_INFO), FALSE, TRUE, offsetof(CERT_INFO, + SubjectPublicKeyInfo.Algorithm.Parameters.pbData), 0 }, + { offsetof(CERT_INFO, IssuerUniqueId), CRYPT_AsnDecodeBitsInternal, + sizeof(CRYPT_BIT_BLOB), TRUE, TRUE, offsetof(CERT_INFO, + IssuerUniqueId.pbData), 0 }, + { offsetof(CERT_INFO, SubjectUniqueId), CRYPT_AsnDecodeBitsInternal, + sizeof(CRYPT_BIT_BLOB), TRUE, TRUE, offsetof(CERT_INFO, + SubjectUniqueId.pbData), 0 }, + { offsetof(CERT_INFO, cExtension), CRYPT_AsnDecodeCertExtensions, + sizeof(CERT_EXTENSIONS), TRUE, TRUE, offsetof(CERT_INFO, + rgExtension), 0 }, + }; + + ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, NULL); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL CRYPT_AsnDecodeCRLEntry(const BYTE *pbEncoded, DWORD cbEncoded, + DWORD dwFlags, PCRL_ENTRY entry, DWORD *pcbEntry) +{ + BOOL ret; + struct AsnDecodeSequenceItem items[] = { + { offsetof(CRL_ENTRY, SerialNumber), CRYPT_AsnDecodeIntegerInternal, + sizeof(CRYPT_INTEGER_BLOB), FALSE, TRUE, offsetof(CRL_ENTRY, + SerialNumber.pbData), 0 }, + { offsetof(CRL_ENTRY, RevocationDate), CRYPT_AsnDecodeChoiceOfTime, + sizeof(FILETIME), FALSE, FALSE, 0 }, + { offsetof(CRL_ENTRY, cExtension), CRYPT_AsnDecodeExtensionsInternal, + sizeof(CERT_EXTENSIONS), TRUE, TRUE, offsetof(CRL_ENTRY, + rgExtension), 0 }, + }; + + TRACE("%p, %ld, %08lx, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, entry, + *pcbEntry); + + ret = CRYPT_AsnDecodeSequence(X509_ASN_ENCODING, items, + sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, + NULL, entry, pcbEntry, entry ? entry->SerialNumber.pbData : NULL); + TRACE("Returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +typedef struct _WINE_CRL_ENTRIES { + DWORD cCRLEntry; + PCRL_ENTRY rgCRLEntry; +} WINE_CRL_ENTRIES, *PWINE_CRL_ENTRIES; + +/* Warning: assumes pvStructInfo is a WINE_CRL_ENTRIES whose rgCRLEntry has + * been set prior to calling. + */ +static BOOL WINAPI CRYPT_AsnDecodeCRLEntries(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + if (pbEncoded[0] == ASN_SEQUENCEOF) + { + DWORD dataLen, bytesNeeded; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + DWORD cCRLEntry = 0; + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + + bytesNeeded = sizeof(WINE_CRL_ENTRIES); + if (dataLen) + { + const BYTE *ptr; + DWORD size; + + for (ptr = pbEncoded + 1 + lenBytes; ret && + ptr - pbEncoded - 1 - lenBytes < dataLen; ) + { + size = 0; + ret = CRYPT_AsnDecodeCRLEntry(ptr, + cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size); + if (ret) + { + DWORD nextLen; + + cCRLEntry++; + bytesNeeded += size; + ret = CRYPT_GetLen(ptr, + cbEncoded - (ptr - pbEncoded), &nextLen); + if (ret) + ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]); + } + } + } + if (ret) + { + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, bytesNeeded))) + { + DWORD size, i; + BYTE *nextData; + const BYTE *ptr; + PWINE_CRL_ENTRIES entries; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + *pcbStructInfo = bytesNeeded; + entries = (PWINE_CRL_ENTRIES)pvStructInfo; + entries->cCRLEntry = cCRLEntry; + assert(entries->rgCRLEntry); + nextData = (BYTE *)entries->rgCRLEntry + + entries->cCRLEntry * sizeof(CRL_ENTRY); + for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret && + i < cCRLEntry && ptr - pbEncoded - 1 - lenBytes < + dataLen; i++) + { + entries->rgCRLEntry[i].SerialNumber.pbData = nextData; + size = bytesNeeded; + ret = CRYPT_AsnDecodeCRLEntry(ptr, + cbEncoded - (ptr - pbEncoded), dwFlags, + &entries->rgCRLEntry[i], &size); + if (ret) + { + DWORD nextLen; + + bytesNeeded -= size; + /* Increment nextData by the difference of the + * minimum size and the actual size. + */ + if (size > sizeof(CRL_ENTRY)) + nextData += size - sizeof(CRL_ENTRY); + ret = CRYPT_GetLen(ptr, + cbEncoded - (ptr - pbEncoded), &nextLen); + if (ret) + ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]); + } + } + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + TRACE("Returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeCRLInfo(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = TRUE; + + TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + _SEH_TRY + { + struct AsnDecodeSequenceItem items[] = { + { offsetof(CRL_INFO, dwVersion), CRYPT_AsnDecodeCertVersion, + sizeof(DWORD), TRUE, FALSE, 0, 0 }, + { offsetof(CRL_INFO, SignatureAlgorithm), CRYPT_AsnDecodeAlgorithmId, + sizeof(CRYPT_ALGORITHM_IDENTIFIER), FALSE, TRUE, offsetof(CRL_INFO, + SignatureAlgorithm.pszObjId), 0 }, + { offsetof(CRL_INFO, Issuer), CRYPT_AsnDecodeDerBlob, + sizeof(CRYPT_DER_BLOB), FALSE, TRUE, offsetof(CRL_INFO, + Issuer.pbData) }, + { offsetof(CRL_INFO, ThisUpdate), CRYPT_AsnDecodeChoiceOfTime, + sizeof(FILETIME), FALSE, FALSE, 0 }, + { offsetof(CRL_INFO, NextUpdate), CRYPT_AsnDecodeChoiceOfTime, + sizeof(FILETIME), TRUE, FALSE, 0 }, + { offsetof(CRL_INFO, cCRLEntry), CRYPT_AsnDecodeCRLEntries, + sizeof(WINE_CRL_ENTRIES), TRUE, TRUE, offsetof(CRL_INFO, + rgCRLEntry), 0 }, + /* Note that the extensions are ignored by MS, so I'll ignore them too + */ + { offsetof(CRL_INFO, cExtension), NULL, + sizeof(CERT_EXTENSIONS), TRUE, FALSE, 0 }, + }; + + ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, NULL); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + + TRACE("Returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +/* Differences between this and CRYPT_AsnDecodeOid: + * - pvStructInfo is a LPSTR *, not an LPSTR + * - CRYPT_AsnDecodeOid doesn't account for the size of an LPSTR in its byte + * count, whereas our callers (typically CRYPT_AsnDecodeSequence) expect this + * to + */ +static BOOL WINAPI CRYPT_AsnDecodeOidWrapper(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + ret = CRYPT_AsnDecodeOid(pbEncoded, cbEncoded, dwFlags, + pvStructInfo ? *(LPSTR *)pvStructInfo : NULL, pcbStructInfo); + if (ret || GetLastError() == ERROR_MORE_DATA) + *pcbStructInfo += sizeof(LPSTR); + if (ret && pvStructInfo) + TRACE("returning %s\n", debugstr_a(*(LPSTR *)pvStructInfo)); + return ret; +} + +/* Warning: assumes ext->pszObjId is set ahead of time! */ +static BOOL CRYPT_AsnDecodeExtension(const BYTE *pbEncoded, DWORD cbEncoded, + DWORD dwFlags, CERT_EXTENSION *ext, DWORD *pcbExt) +{ + struct AsnDecodeSequenceItem items[] = { + { offsetof(CERT_EXTENSION, pszObjId), CRYPT_AsnDecodeOidWrapper, + sizeof(LPSTR), FALSE, TRUE, offsetof(CERT_EXTENSION, pszObjId), 0 }, + { offsetof(CERT_EXTENSION, fCritical), CRYPT_AsnDecodeBool, + sizeof(BOOL), TRUE, FALSE, 0, 0 }, + { offsetof(CERT_EXTENSION, Value), CRYPT_AsnDecodeOctetsInternal, + sizeof(CRYPT_OBJID_BLOB), FALSE, TRUE, offsetof(CERT_EXTENSION, + Value.pbData) }, + }; + BOOL ret = TRUE; + + TRACE("%p, %ld, %08lx, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, ext, + *pcbExt); + + if (ext) + TRACE("ext->pszObjId is %p\n", ext->pszObjId); + ret = CRYPT_AsnDecodeSequence(X509_ASN_ENCODING, items, + sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, NULL, + ext, pcbExt, ext ? ext->pszObjId : NULL); + if (ext) + TRACE("ext->pszObjId is %p (%s)\n", ext->pszObjId, + debugstr_a(ext->pszObjId)); + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeExtensionsInternal(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = TRUE; + + TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + if (pbEncoded[0] == ASN_SEQUENCEOF) + { + DWORD dataLen, bytesNeeded; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + DWORD cExtension = 0; + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + + bytesNeeded = sizeof(CERT_EXTENSIONS); + if (dataLen) + { + const BYTE *ptr; + DWORD size; + + for (ptr = pbEncoded + 1 + lenBytes; ret && + ptr - pbEncoded - 1 - lenBytes < dataLen; ) + { + size = 0; + ret = CRYPT_AsnDecodeExtension(ptr, + cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size); + if (ret) + { + DWORD nextLen; + + cExtension++; + bytesNeeded += size; + ret = CRYPT_GetLen(ptr, + cbEncoded - (ptr - pbEncoded), &nextLen); + if (ret) + ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]); + } + } + } + if (ret) + { + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if (*pcbStructInfo < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + *pcbStructInfo = bytesNeeded; + ret = FALSE; + } + else + { + DWORD size, i; + BYTE *nextData; + const BYTE *ptr; + CERT_EXTENSIONS *exts; + + *pcbStructInfo = bytesNeeded; + exts = (CERT_EXTENSIONS *)pvStructInfo; + exts->cExtension = cExtension; + assert(exts->rgExtension); + nextData = (BYTE *)exts->rgExtension + + exts->cExtension * sizeof(CERT_EXTENSION); + for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret && + i < cExtension && ptr - pbEncoded - 1 - lenBytes < + dataLen; i++) + { + exts->rgExtension[i].pszObjId = (LPSTR)nextData; + size = bytesNeeded; + ret = CRYPT_AsnDecodeExtension(ptr, + cbEncoded - (ptr - pbEncoded), dwFlags, + &exts->rgExtension[i], &size); + if (ret) + { + DWORD nextLen; + + bytesNeeded -= size; + if (size > sizeof(CERT_EXTENSION)) + nextData += size - sizeof(CERT_EXTENSION); + ret = CRYPT_GetLen(ptr, + cbEncoded - (ptr - pbEncoded), &nextLen); + if (ret) + ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]); + } + } + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeExtensions(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = TRUE; + + _SEH_TRY + { + ret = CRYPT_AsnDecodeExtensionsInternal(dwCertEncodingType, + lpszStructType, pbEncoded, cbEncoded, + dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, pcbStructInfo); + if (ret && pvStructInfo) + { + ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo, + pcbStructInfo, *pcbStructInfo); + if (ret) + { + CERT_EXTENSIONS *exts; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + exts = (CERT_EXTENSIONS *)pvStructInfo; + exts->rgExtension = (CERT_EXTENSION *)((BYTE *)exts + + sizeof(CERT_EXTENSIONS)); + ret = CRYPT_AsnDecodeExtensionsInternal(dwCertEncodingType, + lpszStructType, pbEncoded, cbEncoded, + dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo, + pcbStructInfo); + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +/* FIXME: honor the CRYPT_DECODE_SHARE_OID_STRING_FLAG. */ +static BOOL WINAPI CRYPT_AsnDecodeOid(const BYTE *pbEncoded, DWORD cbEncoded, + DWORD dwFlags, LPSTR pszObjId, DWORD *pcbObjId) +{ + BOOL ret = TRUE; + + TRACE("%p, %ld, %08lx, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, pszObjId, + *pcbObjId); + + _SEH_TRY + { + if (pbEncoded[0] == ASN_OBJECTIDENTIFIER) + { + DWORD dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + DWORD bytesNeeded; + + if (dataLen) + { + /* The largest possible string for the first two components + * is 2.175 (= 2 * 40 + 175 = 255), so this is big enough. + */ + char firstTwo[6]; + const BYTE *ptr; + + snprintf(firstTwo, sizeof(firstTwo), "%d.%d", + pbEncoded[1 + lenBytes] / 40, + pbEncoded[1 + lenBytes] - (pbEncoded[1 + lenBytes] / 40) + * 40); + bytesNeeded = strlen(firstTwo) + 1; + for (ptr = pbEncoded + 2 + lenBytes; ret && + ptr - pbEncoded - 1 - lenBytes < dataLen; ) + { + /* large enough for ".4000000" */ + char str[9]; + int val = 0; + + while (ptr - pbEncoded - 1 - lenBytes < dataLen && + (*ptr & 0x80)) + { + val <<= 7; + val |= *ptr & 0x7f; + ptr++; + } + if (ptr - pbEncoded - 1 - lenBytes >= dataLen || + (*ptr & 0x80)) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + else + { + val <<= 7; + val |= *ptr++; + snprintf(str, sizeof(str), ".%d", val); + bytesNeeded += strlen(str); + } + } + if (!pszObjId) + *pcbObjId = bytesNeeded; + else if (*pcbObjId < bytesNeeded) + { + *pcbObjId = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + *pszObjId = 0; + sprintf(pszObjId, "%d.%d", pbEncoded[1 + lenBytes] / 40, + pbEncoded[1 + lenBytes] - (pbEncoded[1 + lenBytes] / + 40) * 40); + pszObjId += strlen(pszObjId); + for (ptr = pbEncoded + 2 + lenBytes; ret && + ptr - pbEncoded - 1 - lenBytes < dataLen; ) + { + int val = 0; + + while (ptr - pbEncoded - 1 - lenBytes < dataLen && + (*ptr & 0x80)) + { + val <<= 7; + val |= *ptr & 0x7f; + ptr++; + } + val <<= 7; + val |= *ptr++; + sprintf(pszObjId, ".%d", val); + pszObjId += strlen(pszObjId); + } + } + } + else + bytesNeeded = 0; + *pcbObjId = bytesNeeded; + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +/* Warning: this assumes the address of value->Value.pbData is already set, in + * order to avoid overwriting memory. (In some cases, it may change it, if it + * doesn't copy anything to memory.) Be sure to set it correctly! + */ +static BOOL WINAPI CRYPT_AsnDecodeNameValue(const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, CERT_NAME_VALUE *value, DWORD *pcbValue) +{ + BOOL ret = TRUE; + + _SEH_TRY + { + DWORD dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + + switch (pbEncoded[0]) + { + case ASN_NUMERICSTRING: + case ASN_PRINTABLESTRING: + case ASN_IA5STRING: + break; + default: + FIXME("Unimplemented string type %02x\n", pbEncoded[0]); + SetLastError(OSS_UNIMPLEMENTED); + ret = FALSE; + } + if (ret) + { + DWORD bytesNeeded = sizeof(CERT_NAME_VALUE); + + switch (pbEncoded[0]) + { + case ASN_NUMERICSTRING: + case ASN_PRINTABLESTRING: + case ASN_IA5STRING: + if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG)) + bytesNeeded += dataLen; + break; + } + if (!value) + *pcbValue = bytesNeeded; + else if (*pcbValue < bytesNeeded) + { + *pcbValue = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + *pcbValue = bytesNeeded; + switch (pbEncoded[0]) + { + case ASN_NUMERICSTRING: + value->dwValueType = CERT_RDN_NUMERIC_STRING; + break; + case ASN_PRINTABLESTRING: + value->dwValueType = CERT_RDN_PRINTABLE_STRING; + break; + case ASN_IA5STRING: + value->dwValueType = CERT_RDN_IA5_STRING; + break; + } + if (dataLen) + { + switch (pbEncoded[0]) + { + case ASN_NUMERICSTRING: + case ASN_PRINTABLESTRING: + case ASN_IA5STRING: + value->Value.cbData = dataLen; + if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) + value->Value.pbData = (BYTE *)pbEncoded + 1 + + lenBytes; + else + { + assert(value->Value.pbData); + memcpy(value->Value.pbData, + pbEncoded + 1 + lenBytes, dataLen); + } + break; + } + } + else + { + value->Value.cbData = 0; + value->Value.pbData = NULL; + } + } + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +/* FIXME: this should use CRYPT_AsnDecodeSequence (though that won't accept it + * at the moment because of the ASN_CONSTRUCTOR tag.) + */ +static BOOL WINAPI CRYPT_AsnDecodeRdnAttr(const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, CERT_RDN_ATTR *attr, DWORD *pcbAttr) +{ + BOOL ret; + + _SEH_TRY + { + if (pbEncoded[0] == (ASN_CONSTRUCTOR | ASN_SEQUENCE)) + { + DWORD bytesNeeded, dataLen, size; + BYTE lenBytes; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + /* The data length must be at least 4, two for the tag and + * length for the OID, and two for the string (assuming both + * have short-form lengths.) + */ + if (dataLen < 4) + { + SetLastError(CRYPT_E_ASN1_EOD); + ret = FALSE; + } + else + { + bytesNeeded = sizeof(CERT_RDN_ATTR); + lenBytes = GET_LEN_BYTES(pbEncoded[1]); + ret = CRYPT_AsnDecodeOid(pbEncoded + 1 + lenBytes, + cbEncoded - 1 - lenBytes, dwFlags, NULL, &size); + if (ret) + { + /* ugly: need to know the size of the next element of + * the sequence, so get it directly + */ + DWORD objIdOfset = 1 + lenBytes, objIdLen, + nameValueOffset = 0; + + ret = CRYPT_GetLen(pbEncoded + objIdOfset, + cbEncoded - objIdOfset, &objIdLen); + bytesNeeded += size; + /* hack: like encoding, this takes advantage of the + * fact that the rest of the structure is identical to + * a CERT_NAME_VALUE. + */ + if (ret) + { + nameValueOffset = objIdOfset + objIdLen + 1 + + GET_LEN_BYTES(pbEncoded[objIdOfset]); + ret = CRYPT_AsnDecodeNameValue( + pbEncoded + nameValueOffset, + cbEncoded - nameValueOffset, dwFlags, NULL, &size); + } + if (ret) + { + bytesNeeded += size; + if (!attr) + *pcbAttr = bytesNeeded; + else if (*pcbAttr < bytesNeeded) + { + *pcbAttr = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + BYTE *originalData = attr->Value.pbData; + + *pcbAttr = bytesNeeded; + /* strange: decode the value first, because it + * has a counted size, and we can store the OID + * after it. Keep track of the original data + * pointer, we'll need to know whether it was + * changed. + */ + size = bytesNeeded; + ret = CRYPT_AsnDecodeNameValue( + pbEncoded + nameValueOffset, + cbEncoded - nameValueOffset, dwFlags, + (CERT_NAME_VALUE *)&attr->dwValueType, &size); + if (ret) + { + if (objIdLen) + { + /* if the data were copied to the + * original location, the OID goes + * after. Otherwise it goes in the + * spot originally reserved for the + * data. + */ + if (attr->Value.pbData == originalData) + attr->pszObjId = + (LPSTR)(attr->Value.pbData + + attr->Value.cbData); + else + attr->pszObjId = + (LPSTR)originalData; + size = bytesNeeded - size; + ret = CRYPT_AsnDecodeOid( + pbEncoded + objIdOfset, + cbEncoded - objIdOfset, + dwFlags, attr->pszObjId, &size); + } + else + attr->pszObjId = NULL; + } + } + } + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeRdn(const BYTE *pbEncoded, DWORD cbEncoded, + DWORD dwFlags, CERT_RDN *rdn, DWORD *pcbRdn) +{ + BOOL ret = TRUE; + + _SEH_TRY + { + if (pbEncoded[0] == (ASN_CONSTRUCTOR | ASN_SETOF)) + { + DWORD dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + DWORD bytesNeeded, cRDNAttr = 0; + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + + bytesNeeded = sizeof(CERT_RDN); + if (dataLen) + { + const BYTE *ptr; + DWORD size; + + for (ptr = pbEncoded + 1 + lenBytes; ret && + ptr - pbEncoded - 1 - lenBytes < dataLen; ) + { + ret = CRYPT_AsnDecodeRdnAttr(ptr, + cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size); + if (ret) + { + DWORD nextLen; + + cRDNAttr++; + bytesNeeded += size; + ret = CRYPT_GetLen(ptr, + cbEncoded - (ptr - pbEncoded), &nextLen); + if (ret) + ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]); + } + } + } + if (ret) + { + if (!rdn) + *pcbRdn = bytesNeeded; + else if (*pcbRdn < bytesNeeded) + { + *pcbRdn = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + DWORD size, i; + BYTE *nextData; + const BYTE *ptr; + + *pcbRdn = bytesNeeded; + rdn->cRDNAttr = cRDNAttr; + rdn->rgRDNAttr = (CERT_RDN_ATTR *)((BYTE *)rdn + + sizeof(CERT_RDN)); + nextData = (BYTE *)rdn->rgRDNAttr + + rdn->cRDNAttr * sizeof(CERT_RDN_ATTR); + for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret && + i < cRDNAttr && ptr - pbEncoded - 1 - lenBytes < + dataLen; i++) + { + rdn->rgRDNAttr[i].Value.pbData = nextData; + size = bytesNeeded; + ret = CRYPT_AsnDecodeRdnAttr(ptr, + cbEncoded - (ptr - pbEncoded), dwFlags, + &rdn->rgRDNAttr[i], &size); + if (ret) + { + DWORD nextLen; + + bytesNeeded -= size; + /* If dwFlags & CRYPT_DECODE_NOCOPY_FLAG, the + * data may not have been copied. + */ + if (rdn->rgRDNAttr[i].Value.pbData == nextData) + nextData += + rdn->rgRDNAttr[i].Value.cbData; + /* Ugly: the OID, if copied, is stored in + * memory after the value, so increment by its + * string length if it's set and points here. + */ + if ((const BYTE *)rdn->rgRDNAttr[i].pszObjId + == nextData) + nextData += strlen( + rdn->rgRDNAttr[i].pszObjId) + 1; + ret = CRYPT_GetLen(ptr, + cbEncoded - (ptr - pbEncoded), &nextLen); + if (ret) + ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]); + } + } + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeName(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = TRUE; + + _SEH_TRY + { + if (pbEncoded[0] == (ASN_CONSTRUCTOR | ASN_SEQUENCEOF)) + { + DWORD dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + DWORD bytesNeeded, cRDN = 0; + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + + bytesNeeded = sizeof(CERT_NAME_INFO); + if (dataLen) + { + const BYTE *ptr; + + for (ptr = pbEncoded + 1 + lenBytes; ret && + ptr - pbEncoded - 1 - lenBytes < dataLen; ) + { + DWORD size; + + ret = CRYPT_AsnDecodeRdn(ptr, + cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size); + if (ret) + { + DWORD nextLen; + + cRDN++; + bytesNeeded += size; + ret = CRYPT_GetLen(ptr, + cbEncoded - (ptr - pbEncoded), &nextLen); + if (ret) + ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]); + } + } + } + if (ret) + { + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, bytesNeeded))) + { + CERT_NAME_INFO *info; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + info = (CERT_NAME_INFO *)pvStructInfo; + info->cRDN = cRDN; + if (info->cRDN == 0) + info->rgRDN = NULL; + else + { + DWORD size, i; + BYTE *nextData; + const BYTE *ptr; + + info->rgRDN = (CERT_RDN *)((BYTE *)pvStructInfo + + sizeof(CERT_NAME_INFO)); + nextData = (BYTE *)info->rgRDN + + info->cRDN * sizeof(CERT_RDN); + for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret && + i < cRDN && ptr - pbEncoded - 1 - lenBytes < + dataLen; i++) + { + info->rgRDN[i].rgRDNAttr = + (CERT_RDN_ATTR *)nextData; + size = bytesNeeded; + ret = CRYPT_AsnDecodeRdn(ptr, + cbEncoded - (ptr - pbEncoded), dwFlags, + &info->rgRDN[i], &size); + if (ret) + { + DWORD nextLen; + + nextData += size; + bytesNeeded -= size; + ret = CRYPT_GetLen(ptr, + cbEncoded - (ptr - pbEncoded), &nextLen); + if (ret) + ptr += nextLen + 1 + + GET_LEN_BYTES(ptr[1]); + } + } + } + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeCopyBytes(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = TRUE; + DWORD bytesNeeded = sizeof(CRYPT_OBJID_BLOB); + + TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG)) + bytesNeeded += cbEncoded; + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if (*pcbStructInfo < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + *pcbStructInfo = bytesNeeded; + ret = FALSE; + } + else + { + PCRYPT_OBJID_BLOB blob = (PCRYPT_OBJID_BLOB)pvStructInfo; + + *pcbStructInfo = bytesNeeded; + blob->cbData = cbEncoded; + if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) + blob->pbData = (LPBYTE)pbEncoded; + else + { + assert(blob->pbData); + memcpy(blob->pbData, pbEncoded, blob->cbData); + } + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeAlgorithmId(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + CRYPT_ALGORITHM_IDENTIFIER *algo = + (CRYPT_ALGORITHM_IDENTIFIER *)pvStructInfo; + BOOL ret = TRUE; + struct AsnDecodeSequenceItem items[] = { + { offsetof(CRYPT_ALGORITHM_IDENTIFIER, pszObjId), + CRYPT_AsnDecodeOidWrapper, sizeof(LPSTR), FALSE, TRUE, + offsetof(CRYPT_ALGORITHM_IDENTIFIER, pszObjId), 0 }, + { offsetof(CRYPT_ALGORITHM_IDENTIFIER, Parameters), + CRYPT_AsnDecodeCopyBytes, sizeof(CRYPT_OBJID_BLOB), TRUE, TRUE, + offsetof(CRYPT_ALGORITHM_IDENTIFIER, Parameters.pbData), 0 }, + }; + + TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, algo ? algo->pszObjId : NULL); + if (ret && pvStructInfo) + { + TRACE("pszObjId is %p (%s)\n", algo->pszObjId, + debugstr_a(algo->pszObjId)); + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodePubKeyInfo(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = TRUE; + + _SEH_TRY + { + struct AsnDecodeSequenceItem items[] = { + { offsetof(CERT_PUBLIC_KEY_INFO, Algorithm), + CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER), + FALSE, TRUE, offsetof(CERT_PUBLIC_KEY_INFO, + Algorithm.pszObjId) }, + { offsetof(CERT_PUBLIC_KEY_INFO, PublicKey), + CRYPT_AsnDecodeBitsInternal, sizeof(CRYPT_BIT_BLOB), FALSE, TRUE, + offsetof(CERT_PUBLIC_KEY_INFO, PublicKey.pbData) }, + }; + + ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, NULL); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeBool(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + if (cbEncoded < 3) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + return FALSE; + } + if (pbEncoded[0] != ASN_BOOL) + { + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + if (GET_LEN_BYTES(pbEncoded[1]) > 1) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + return FALSE; + } + if (pbEncoded[1] > 1) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + return FALSE; + } + if (!pvStructInfo) + { + *pcbStructInfo = sizeof(BOOL); + ret = TRUE; + } + else if (*pcbStructInfo < sizeof(BOOL)) + { + *pcbStructInfo = sizeof(BOOL); + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + *(BOOL *)pvStructInfo = pbEncoded[2] ? TRUE : FALSE; + ret = TRUE; + } + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL CRYPT_AsnDecodeAltNameEntry(const BYTE *pbEncoded, DWORD cbEncoded, + DWORD dwFlags, CERT_ALT_NAME_ENTRY *entry, DWORD *pcbEntry) +{ + DWORD dataLen, lenBytes, bytesNeeded = sizeof(CERT_ALT_NAME_ENTRY); + BOOL ret; + + if (cbEncoded < 2) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + return FALSE; + } + if ((pbEncoded[0] & ASN_FLAGS_MASK) != ASN_CONTEXT) + { + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + lenBytes = GET_LEN_BYTES(pbEncoded[1]); + if (1 + lenBytes > cbEncoded) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + return FALSE; + } + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + switch (pbEncoded[0] & ASN_TYPE_MASK) + { + case 1: /* rfc822Name */ + case 2: /* dNSName */ + case 6: /* uniformResourceIdentifier */ + bytesNeeded += (dataLen + 1) * sizeof(WCHAR); + break; + case 7: /* iPAddress */ + bytesNeeded += dataLen; + break; + case 8: /* registeredID */ + /* FIXME: decode as OID */ + case 0: /* otherName */ + case 4: /* directoryName */ + FIXME("stub\n"); + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + break; + case 3: /* x400Address, unimplemented */ + case 5: /* ediPartyName, unimplemented */ + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + break; + default: + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + if (ret) + { + if (!entry) + *pcbEntry = bytesNeeded; + else if (*pcbEntry < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + /* MS used values one greater than the asn1 ones.. sigh */ + entry->dwAltNameChoice = (pbEncoded[0] & 0x7f) + 1; + switch (pbEncoded[0] & ASN_TYPE_MASK) + { + case 1: /* rfc822Name */ + case 2: /* dNSName */ + case 6: /* uniformResourceIdentifier */ + { + DWORD i; + + for (i = 0; i < dataLen; i++) + entry->u.pwszURL[i] = + (WCHAR)pbEncoded[1 + lenBytes + i]; + entry->u.pwszURL[i] = 0; + break; + } + case 7: /* iPAddress */ + /* The next data pointer is in the pwszURL spot, that is, + * the first 4 bytes. Need to move it to the next spot. + */ + entry->u.IPAddress.pbData = (LPBYTE)entry->u.pwszURL; + entry->u.IPAddress.cbData = dataLen; + memcpy(entry->u.IPAddress.pbData, pbEncoded + 1 + lenBytes, + dataLen); + break; + } + } + } + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeAltName(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = TRUE; + + _SEH_TRY + { + if (pbEncoded[0] == ASN_SEQUENCEOF) + { + DWORD dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + DWORD bytesNeeded, cEntry = 0; + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + + bytesNeeded = sizeof(CERT_ALT_NAME_INFO); + if (dataLen) + { + const BYTE *ptr; + + for (ptr = pbEncoded + 1 + lenBytes; ret && + ptr - pbEncoded - 1 - lenBytes < dataLen; ) + { + DWORD size; + + ret = CRYPT_AsnDecodeAltNameEntry(ptr, + cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size); + if (ret) + { + DWORD nextLen; + + cEntry++; + bytesNeeded += size; + ret = CRYPT_GetLen(ptr, + cbEncoded - (ptr - pbEncoded), &nextLen); + if (ret) + ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]); + } + } + } + if (ret) + { + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, bytesNeeded))) + { + CERT_ALT_NAME_INFO *info; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + info = (CERT_ALT_NAME_INFO *)pvStructInfo; + info->cAltEntry = 0; + if (cEntry == 0) + info->rgAltEntry = NULL; + else + { + DWORD size, i; + BYTE *nextData; + const BYTE *ptr; + + info->rgAltEntry = + (CERT_ALT_NAME_ENTRY *)((BYTE *)pvStructInfo + + sizeof(CERT_ALT_NAME_INFO)); + nextData = (BYTE *)info->rgAltEntry + + cEntry * sizeof(CERT_ALT_NAME_ENTRY); + for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret && + i < cEntry && ptr - pbEncoded - 1 - lenBytes < + dataLen; i++) + { + info->rgAltEntry[i].u.pwszURL = + (LPWSTR)nextData; + size = bytesNeeded; + ret = CRYPT_AsnDecodeAltNameEntry(ptr, + cbEncoded - (ptr - pbEncoded), dwFlags, + &info->rgAltEntry[i], &size); + if (ret) + { + DWORD nextLen; + + info->cAltEntry++; + nextData += size - + sizeof(CERT_ALT_NAME_ENTRY); + bytesNeeded -= size; + ret = CRYPT_GetLen(ptr, + cbEncoded - (ptr - pbEncoded), &nextLen); + if (ret) + ptr += nextLen + 1 + + GET_LEN_BYTES(ptr[1]); + } + } + } + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +struct PATH_LEN_CONSTRAINT +{ + BOOL fPathLenConstraint; + DWORD dwPathLenConstraint; +}; + +static BOOL WINAPI CRYPT_AsnDecodePathLenConstraint(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = TRUE; + + if (cbEncoded) + { + if (pbEncoded[0] == ASN_INTEGER) + { + DWORD bytesNeeded = sizeof(struct PATH_LEN_CONSTRAINT); + + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if (*pcbStructInfo < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + *pcbStructInfo = bytesNeeded; + ret = FALSE; + } + else + { + struct PATH_LEN_CONSTRAINT *constraint = + (struct PATH_LEN_CONSTRAINT *)pvStructInfo; + DWORD size = sizeof(constraint->dwPathLenConstraint); + + ret = CRYPT_AsnDecodeInt(dwCertEncodingType, X509_INTEGER, + pbEncoded, cbEncoded, 0, NULL, + &constraint->dwPathLenConstraint, &size); + if (ret) + constraint->fPathLenConstraint = TRUE; + TRACE("got an int, dwPathLenConstraint is %ld\n", + constraint->dwPathLenConstraint); + } + } + else + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + } + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeBasicConstraints2(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + _SEH_TRY + { + struct AsnDecodeSequenceItem items[] = { + { offsetof(CERT_BASIC_CONSTRAINTS2_INFO, fCA), CRYPT_AsnDecodeBool, + sizeof(BOOL), TRUE, FALSE, 0, 0 }, + { offsetof(CERT_BASIC_CONSTRAINTS2_INFO, fPathLenConstraint), + CRYPT_AsnDecodePathLenConstraint, sizeof(struct PATH_LEN_CONSTRAINT), + TRUE, FALSE, 0, 0 }, + }; + + ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, NULL); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +#define RSA1_MAGIC 0x31415352 + +struct DECODED_RSA_PUB_KEY +{ + DWORD pubexp; + CRYPT_INTEGER_BLOB modulus; +}; + +static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + _SEH_TRY + { + struct AsnDecodeSequenceItem items[] = { + { offsetof(struct DECODED_RSA_PUB_KEY, modulus), + CRYPT_AsnDecodeUnsignedIntegerInternal, sizeof(CRYPT_INTEGER_BLOB), + FALSE, TRUE, offsetof(struct DECODED_RSA_PUB_KEY, modulus.pbData), + 0 }, + { offsetof(struct DECODED_RSA_PUB_KEY, pubexp), + CRYPT_AsnDecodeInt, sizeof(DWORD), FALSE, FALSE, 0, 0 }, + }; + struct DECODED_RSA_PUB_KEY *decodedKey = NULL; + DWORD size = 0; + + ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items, + sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, + CRYPT_DECODE_ALLOC_FLAG, NULL, &decodedKey, &size, NULL); + if (ret) + { + DWORD bytesNeeded = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + + decodedKey->modulus.cbData; + + if (!pvStructInfo) + { + *pcbStructInfo = bytesNeeded; + ret = TRUE; + } + else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, + pvStructInfo, pcbStructInfo, bytesNeeded))) + { + BLOBHEADER *hdr; + RSAPUBKEY *rsaPubKey; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + hdr = (BLOBHEADER *)pvStructInfo; + hdr->bType = PUBLICKEYBLOB; + hdr->bVersion = CUR_BLOB_VERSION; + hdr->reserved = 0; + hdr->aiKeyAlg = CALG_RSA_KEYX; + rsaPubKey = (RSAPUBKEY *)((BYTE *)pvStructInfo + + sizeof(BLOBHEADER)); + rsaPubKey->magic = RSA1_MAGIC; + rsaPubKey->pubexp = decodedKey->pubexp; + rsaPubKey->bitlen = decodedKey->modulus.cbData * 8; + memcpy((BYTE *)pvStructInfo + sizeof(BLOBHEADER) + + sizeof(RSAPUBKEY), decodedKey->modulus.pbData, + decodedKey->modulus.cbData); + } + LocalFree(decodedKey); + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeOctetsInternal(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + if (pbEncoded[0] == ASN_OCTETSTRING) + { + DWORD bytesNeeded, dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) + bytesNeeded = sizeof(CRYPT_DATA_BLOB); + else + bytesNeeded = dataLen + sizeof(CRYPT_DATA_BLOB); + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if (*pcbStructInfo < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + *pcbStructInfo = bytesNeeded; + ret = FALSE; + } + else + { + CRYPT_DATA_BLOB *blob; + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + + blob = (CRYPT_DATA_BLOB *)pvStructInfo; + blob->cbData = dataLen; + if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) + blob->pbData = (BYTE *)pbEncoded + 1 + lenBytes; + else + { + assert(blob->pbData); + if (blob->cbData) + memcpy(blob->pbData, pbEncoded + 1 + lenBytes, + blob->cbData); + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeOctets(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + _SEH_TRY + { + DWORD bytesNeeded; + + if ((ret = CRYPT_AsnDecodeOctetsInternal(dwCertEncodingType, + lpszStructType, pbEncoded, cbEncoded, + dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, &bytesNeeded))) + { + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, + pvStructInfo, pcbStructInfo, bytesNeeded))) + { + CRYPT_DATA_BLOB *blob; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + blob = (CRYPT_DATA_BLOB *)pvStructInfo; + blob->pbData = (BYTE *)pvStructInfo + sizeof(CRYPT_DATA_BLOB); + ret = CRYPT_AsnDecodeOctetsInternal(dwCertEncodingType, + lpszStructType, pbEncoded, cbEncoded, + dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo, + &bytesNeeded); + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeBitsInternal(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + TRACE("(%p, %ld, 0x%08lx, %p, %p, %ld)\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, *pcbStructInfo); + + if (pbEncoded[0] == ASN_BITSTRING) + { + DWORD bytesNeeded, dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) + bytesNeeded = sizeof(CRYPT_BIT_BLOB); + else + bytesNeeded = dataLen - 1 + sizeof(CRYPT_BIT_BLOB); + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if (*pcbStructInfo < bytesNeeded) + { + *pcbStructInfo = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + CRYPT_BIT_BLOB *blob; + + blob = (CRYPT_BIT_BLOB *)pvStructInfo; + blob->cbData = dataLen - 1; + blob->cUnusedBits = *(pbEncoded + 1 + + GET_LEN_BYTES(pbEncoded[1])); + if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) + { + blob->pbData = (BYTE *)pbEncoded + 2 + + GET_LEN_BYTES(pbEncoded[1]); + } + else + { + assert(blob->pbData); + if (blob->cbData) + { + BYTE mask = 0xff << blob->cUnusedBits; + + memcpy(blob->pbData, pbEncoded + 2 + + GET_LEN_BYTES(pbEncoded[1]), blob->cbData); + blob->pbData[blob->cbData - 1] &= mask; + } + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeBits(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + TRACE("(%p, %ld, 0x%08lx, %p, %p, %p)\n", pbEncoded, cbEncoded, dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo); + + _SEH_TRY + { + DWORD bytesNeeded; + + if ((ret = CRYPT_AsnDecodeBitsInternal(dwCertEncodingType, + lpszStructType, pbEncoded, cbEncoded, + dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, &bytesNeeded))) + { + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, + pvStructInfo, pcbStructInfo, bytesNeeded))) + { + CRYPT_BIT_BLOB *blob; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + blob = (CRYPT_BIT_BLOB *)pvStructInfo; + blob->pbData = (BYTE *)pvStructInfo + sizeof(CRYPT_BIT_BLOB); + ret = CRYPT_AsnDecodeBitsInternal(dwCertEncodingType, + lpszStructType, pbEncoded, cbEncoded, + dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo, + &bytesNeeded); + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + TRACE("returning %d (%08lx)\n", ret, GetLastError()); + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + if (!pvStructInfo) + { + *pcbStructInfo = sizeof(int); + return TRUE; + } + _SEH_TRY + { + BYTE buf[sizeof(CRYPT_INTEGER_BLOB) + sizeof(int)]; + CRYPT_INTEGER_BLOB *blob = (CRYPT_INTEGER_BLOB *)buf; + DWORD size = sizeof(buf); + + blob->pbData = buf + sizeof(CRYPT_INTEGER_BLOB); + ret = CRYPT_AsnDecodeIntegerInternal(dwCertEncodingType, + X509_MULTI_BYTE_INTEGER, pbEncoded, cbEncoded, 0, NULL, &buf, &size); + if (ret) + { + if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, + pvStructInfo, pcbStructInfo, sizeof(int)))) + { + int val, i; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + if (blob->pbData[blob->cbData - 1] & 0x80) + { + /* initialize to a negative value to sign-extend */ + val = -1; + } + else + val = 0; + for (i = 0; i < blob->cbData; i++) + { + val <<= 8; + val |= blob->pbData[blob->cbData - i - 1]; + } + memcpy(pvStructInfo, &val, sizeof(int)); + } + } + else if (GetLastError() == ERROR_MORE_DATA) + SetLastError(CRYPT_E_ASN1_LARGE); + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeIntegerInternal(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + if (pbEncoded[0] == ASN_INTEGER) + { + DWORD bytesNeeded, dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + + bytesNeeded = dataLen + sizeof(CRYPT_INTEGER_BLOB); + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if (*pcbStructInfo < bytesNeeded) + { + *pcbStructInfo = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + CRYPT_INTEGER_BLOB *blob = (CRYPT_INTEGER_BLOB *)pvStructInfo; + + blob->cbData = dataLen; + assert(blob->pbData); + if (blob->cbData) + { + DWORD i; + + for (i = 0; i < blob->cbData; i++) + { + blob->pbData[i] = *(pbEncoded + 1 + lenBytes + + dataLen - i - 1); + } + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeInteger(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + _SEH_TRY + { + DWORD bytesNeeded; + + if ((ret = CRYPT_AsnDecodeIntegerInternal(dwCertEncodingType, + lpszStructType, pbEncoded, cbEncoded, + dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, NULL, &bytesNeeded))) + { + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, + pvStructInfo, pcbStructInfo, bytesNeeded))) + { + CRYPT_INTEGER_BLOB *blob; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + blob = (CRYPT_INTEGER_BLOB *)pvStructInfo; + blob->pbData = (BYTE *)pvStructInfo + + sizeof(CRYPT_INTEGER_BLOB); + ret = CRYPT_AsnDecodeIntegerInternal(dwCertEncodingType, + lpszStructType, pbEncoded, cbEncoded, + dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, pvStructInfo, + &bytesNeeded); + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeUnsignedIntegerInternal( + DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara, + void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + if (pbEncoded[0] == ASN_INTEGER) + { + DWORD bytesNeeded, dataLen; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + + bytesNeeded = dataLen + sizeof(CRYPT_INTEGER_BLOB); + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if (*pcbStructInfo < bytesNeeded) + { + *pcbStructInfo = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + CRYPT_INTEGER_BLOB *blob = (CRYPT_INTEGER_BLOB *)pvStructInfo; + + blob->cbData = dataLen; + assert(blob->pbData); + /* remove leading zero byte if it exists */ + if (blob->cbData && *(pbEncoded + 1 + lenBytes) == 0) + { + blob->cbData--; + blob->pbData++; + } + if (blob->cbData) + { + DWORD i; + + for (i = 0; i < blob->cbData; i++) + { + blob->pbData[i] = *(pbEncoded + 1 + lenBytes + + dataLen - i - 1); + } + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeUnsignedInteger(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + _SEH_TRY + { + DWORD bytesNeeded; + + if ((ret = CRYPT_AsnDecodeUnsignedIntegerInternal(dwCertEncodingType, + lpszStructType, pbEncoded, cbEncoded, + dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, NULL, &bytesNeeded))) + { + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, + pvStructInfo, pcbStructInfo, bytesNeeded))) + { + CRYPT_INTEGER_BLOB *blob; + + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + blob = (CRYPT_INTEGER_BLOB *)pvStructInfo; + blob->pbData = (BYTE *)pvStructInfo + + sizeof(CRYPT_INTEGER_BLOB); + ret = CRYPT_AsnDecodeUnsignedIntegerInternal(dwCertEncodingType, + lpszStructType, pbEncoded, cbEncoded, + dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, pvStructInfo, + &bytesNeeded); + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeEnumerated(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + if (!pvStructInfo) + { + *pcbStructInfo = sizeof(int); + return TRUE; + } + _SEH_TRY + { + if (pbEncoded[0] == ASN_ENUMERATED) + { + unsigned int val = 0, i; + + if (cbEncoded <= 1) + { + SetLastError(CRYPT_E_ASN1_EOD); + ret = FALSE; + } + else if (pbEncoded[1] == 0) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + else + { + /* A little strange looking, but we have to accept a sign byte: + * 0xffffffff gets encoded as 0a 05 00 ff ff ff ff. Also, + * assuming a small length is okay here, it has to be in short + * form. + */ + if (pbEncoded[1] > sizeof(unsigned int) + 1) + { + SetLastError(CRYPT_E_ASN1_LARGE); + return FALSE; + } + for (i = 0; i < pbEncoded[1]; i++) + { + val <<= 8; + val |= pbEncoded[2 + i]; + } + if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, + pvStructInfo, pcbStructInfo, sizeof(unsigned int)))) + { + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + memcpy(pvStructInfo, &val, sizeof(unsigned int)); + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +/* Modifies word, pbEncoded, and len, and magically sets a value ret to FALSE + * if it fails. + */ +#define CRYPT_TIME_GET_DIGITS(pbEncoded, len, numDigits, word) \ + do { \ + BYTE i; \ + \ + (word) = 0; \ + for (i = 0; (len) > 0 && i < (numDigits); i++, (len)--) \ + { \ + if (!isdigit(*(pbEncoded))) \ + { \ + SetLastError(CRYPT_E_ASN1_CORRUPT); \ + ret = FALSE; \ + } \ + else \ + { \ + (word) *= 10; \ + (word) += *(pbEncoded)++ - '0'; \ + } \ + } \ + } while (0) + +static BOOL CRYPT_AsnDecodeTimeZone(const BYTE *pbEncoded, DWORD len, + SYSTEMTIME *sysTime) +{ + BOOL ret; + + _SEH_TRY + { + ret = TRUE; + if (len >= 3 && (*pbEncoded == '+' || *pbEncoded == '-')) + { + WORD hours, minutes = 0; + BYTE sign = *pbEncoded++; + + len--; + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, hours); + if (ret && hours >= 24) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + else if (len >= 2) + { + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, minutes); + if (ret && minutes >= 60) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + } + if (ret) + { + if (sign == '+') + { + sysTime->wHour += hours; + sysTime->wMinute += minutes; + } + else + { + if (hours > sysTime->wHour) + { + sysTime->wDay--; + sysTime->wHour = 24 - (hours - sysTime->wHour); + } + else + sysTime->wHour -= hours; + if (minutes > sysTime->wMinute) + { + sysTime->wHour--; + sysTime->wMinute = 60 - (minutes - sysTime->wMinute); + } + else + sysTime->wMinute -= minutes; + } + } + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +#define MIN_ENCODED_TIME_LENGTH 10 + +static BOOL WINAPI CRYPT_AsnDecodeUtcTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + if (!pvStructInfo) + { + *pcbStructInfo = sizeof(FILETIME); + return TRUE; + } + _SEH_TRY + { + ret = TRUE; + if (pbEncoded[0] == ASN_UTCTIME) + { + if (cbEncoded <= 1) + { + SetLastError(CRYPT_E_ASN1_EOD); + ret = FALSE; + } + else if (pbEncoded[1] > 0x7f) + { + /* long-form date strings really can't be valid */ + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + else + { + SYSTEMTIME sysTime = { 0 }; + BYTE len = pbEncoded[1]; + + if (len < MIN_ENCODED_TIME_LENGTH) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + else + { + pbEncoded += 2; + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wYear); + if (sysTime.wYear >= 50) + sysTime.wYear += 1900; + else + sysTime.wYear += 2000; + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMinute); + if (ret && len > 0) + { + if (len >= 2 && isdigit(*pbEncoded) && + isdigit(*(pbEncoded + 1))) + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, + sysTime.wSecond); + else if (isdigit(*pbEncoded)) + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 1, + sysTime.wSecond); + if (ret) + ret = CRYPT_AsnDecodeTimeZone(pbEncoded, len, + &sysTime); + } + if (ret && (ret = CRYPT_DecodeEnsureSpace(dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, + sizeof(FILETIME)))) + { + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + ret = SystemTimeToFileTime(&sysTime, + (FILETIME *)pvStructInfo); + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeGeneralizedTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + if (!pvStructInfo) + { + *pcbStructInfo = sizeof(FILETIME); + return TRUE; + } + _SEH_TRY + { + ret = TRUE; + if (pbEncoded[0] == ASN_GENERALTIME) + { + if (cbEncoded <= 1) + { + SetLastError(CRYPT_E_ASN1_EOD); + ret = FALSE; + } + else if (pbEncoded[1] > 0x7f) + { + /* long-form date strings really can't be valid */ + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + else + { + BYTE len = pbEncoded[1]; + + if (len < MIN_ENCODED_TIME_LENGTH) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + ret = FALSE; + } + else + { + SYSTEMTIME sysTime = { 0 }; + + pbEncoded += 2; + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 4, sysTime.wYear); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour); + if (ret && len > 0) + { + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, + sysTime.wMinute); + if (ret && len > 0) + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, + sysTime.wSecond); + if (ret && len > 0 && (*pbEncoded == '.' || + *pbEncoded == ',')) + { + BYTE digits; + + pbEncoded++; + len--; + /* workaround macro weirdness */ + digits = min(len, 3); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, digits, + sysTime.wMilliseconds); + } + if (ret) + ret = CRYPT_AsnDecodeTimeZone(pbEncoded, len, + &sysTime); + } + if (ret && (ret = CRYPT_DecodeEnsureSpace(dwFlags, + pDecodePara, pvStructInfo, pcbStructInfo, + sizeof(FILETIME)))) + { + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + ret = SystemTimeToFileTime(&sysTime, + (FILETIME *)pvStructInfo); + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeChoiceOfTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret; + + _SEH_TRY + { + if (pbEncoded[0] == ASN_UTCTIME) + ret = CRYPT_AsnDecodeUtcTime(dwCertEncodingType, lpszStructType, + pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo, + pcbStructInfo); + else if (pbEncoded[0] == ASN_GENERALTIME) + ret = CRYPT_AsnDecodeGeneralizedTime(dwCertEncodingType, + lpszStructType, pbEncoded, cbEncoded, dwFlags, pDecodePara, + pvStructInfo, pcbStructInfo); + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + ret = FALSE; + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +static BOOL WINAPI CRYPT_AsnDecodeSequenceOfAny(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = TRUE; + + _SEH_TRY + { + if (pbEncoded[0] == ASN_SEQUENCEOF) + { + DWORD bytesNeeded, dataLen, remainingLen, cValue; + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes; + const BYTE *ptr; + + lenBytes = GET_LEN_BYTES(pbEncoded[1]); + bytesNeeded = sizeof(CRYPT_SEQUENCE_OF_ANY); + cValue = 0; + ptr = pbEncoded + 1 + lenBytes; + remainingLen = dataLen; + while (ret && remainingLen) + { + DWORD nextLen; + + ret = CRYPT_GetLen(ptr, remainingLen, &nextLen); + if (ret) + { + DWORD nextLenBytes = GET_LEN_BYTES(ptr[1]); + + remainingLen -= 1 + nextLenBytes + nextLen; + ptr += 1 + nextLenBytes + nextLen; + bytesNeeded += sizeof(CRYPT_DER_BLOB); + if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG)) + bytesNeeded += 1 + nextLenBytes + nextLen; + cValue++; + } + } + if (ret) + { + CRYPT_SEQUENCE_OF_ANY *seq; + BYTE *nextPtr; + DWORD i; + + if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, + pvStructInfo, pcbStructInfo, bytesNeeded))) + { + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + seq = (CRYPT_SEQUENCE_OF_ANY *)pvStructInfo; + seq->cValue = cValue; + seq->rgValue = (CRYPT_DER_BLOB *)((BYTE *)seq + + sizeof(*seq)); + nextPtr = (BYTE *)seq->rgValue + + cValue * sizeof(CRYPT_DER_BLOB); + ptr = pbEncoded + 1 + lenBytes; + remainingLen = dataLen; + i = 0; + while (ret && remainingLen) + { + DWORD nextLen; + + ret = CRYPT_GetLen(ptr, remainingLen, &nextLen); + if (ret) + { + DWORD nextLenBytes = GET_LEN_BYTES(ptr[1]); + + seq->rgValue[i].cbData = 1 + nextLenBytes + + nextLen; + if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) + seq->rgValue[i].pbData = (BYTE *)ptr; + else + { + seq->rgValue[i].pbData = nextPtr; + memcpy(nextPtr, ptr, 1 + nextLenBytes + + nextLen); + nextPtr += 1 + nextLenBytes + nextLen; + } + remainingLen -= 1 + nextLenBytes + nextLen; + ptr += 1 + nextLenBytes + nextLen; + i++; + } + } + } + } + } + } + else + { + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + } + _SEH_EXCEPT(page_fault) + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + _SEH_END + return ret; +} + +BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType, + const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + BOOL ret = FALSE; + HMODULE lib = NULL; + CryptDecodeObjectExFunc decodeFunc = NULL; + + TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p, %p)\n", + dwCertEncodingType, debugstr_a(lpszStructType), pbEncoded, + cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo); + + if (!pvStructInfo && !pcbStructInfo) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING + && (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) != PKCS_7_ASN_ENCODING) + { + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + if (!cbEncoded) + { + SetLastError(CRYPT_E_ASN1_EOD); + return FALSE; + } + if (cbEncoded > MAX_ENCODED_LEN) + { + SetLastError(CRYPT_E_ASN1_LARGE); + return FALSE; + } + + SetLastError(NOERROR); + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG && pvStructInfo) + *(BYTE **)pvStructInfo = NULL; + if (!HIWORD(lpszStructType)) + { + switch (LOWORD(lpszStructType)) + { + case (WORD)X509_CERT: + decodeFunc = CRYPT_AsnDecodeCert; + break; + case (WORD)X509_CERT_TO_BE_SIGNED: + decodeFunc = CRYPT_AsnDecodeCertInfo; + break; + case (WORD)X509_CERT_CRL_TO_BE_SIGNED: + decodeFunc = CRYPT_AsnDecodeCRLInfo; + break; + case (WORD)X509_EXTENSIONS: + decodeFunc = CRYPT_AsnDecodeExtensions; + break; + case (WORD)X509_NAME: + decodeFunc = CRYPT_AsnDecodeName; + break; + case (WORD)X509_PUBLIC_KEY_INFO: + decodeFunc = CRYPT_AsnDecodePubKeyInfo; + break; + case (WORD)X509_ALTERNATE_NAME: + decodeFunc = CRYPT_AsnDecodeAltName; + break; + case (WORD)X509_BASIC_CONSTRAINTS2: + decodeFunc = CRYPT_AsnDecodeBasicConstraints2; + break; + case (WORD)RSA_CSP_PUBLICKEYBLOB: + decodeFunc = CRYPT_AsnDecodeRsaPubKey; + break; + case (WORD)X509_OCTET_STRING: + decodeFunc = CRYPT_AsnDecodeOctets; + break; + case (WORD)X509_BITS: + case (WORD)X509_KEY_USAGE: + decodeFunc = CRYPT_AsnDecodeBits; + break; + case (WORD)X509_INTEGER: + decodeFunc = CRYPT_AsnDecodeInt; + break; + case (WORD)X509_MULTI_BYTE_INTEGER: + decodeFunc = CRYPT_AsnDecodeInteger; + break; + case (WORD)X509_MULTI_BYTE_UINT: + decodeFunc = CRYPT_AsnDecodeUnsignedInteger; + break; + case (WORD)X509_ENUMERATED: + decodeFunc = CRYPT_AsnDecodeEnumerated; + break; + case (WORD)X509_CHOICE_OF_TIME: + decodeFunc = CRYPT_AsnDecodeChoiceOfTime; + break; + case (WORD)X509_SEQUENCE_OF_ANY: + decodeFunc = CRYPT_AsnDecodeSequenceOfAny; + break; + case (WORD)PKCS_UTC_TIME: + decodeFunc = CRYPT_AsnDecodeUtcTime; + break; + default: + FIXME("%d: unimplemented\n", LOWORD(lpszStructType)); + } + } + else if (!strcmp(lpszStructType, szOID_CERT_EXTENSIONS)) + decodeFunc = CRYPT_AsnDecodeExtensions; + else if (!strcmp(lpszStructType, szOID_RSA_signingTime)) + decodeFunc = CRYPT_AsnDecodeUtcTime; + else if (!strcmp(lpszStructType, szOID_CRL_REASON_CODE)) + decodeFunc = CRYPT_AsnDecodeEnumerated; + else if (!strcmp(lpszStructType, szOID_KEY_USAGE)) + decodeFunc = CRYPT_AsnDecodeBits; + else if (!strcmp(lpszStructType, szOID_SUBJECT_KEY_IDENTIFIER)) + decodeFunc = CRYPT_AsnDecodeOctets; + else if (!strcmp(lpszStructType, szOID_BASIC_CONSTRAINTS2)) + decodeFunc = CRYPT_AsnDecodeBasicConstraints2; + else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME)) + decodeFunc = CRYPT_AsnDecodeAltName; + else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME2)) + decodeFunc = CRYPT_AsnDecodeAltName; + else if (!strcmp(lpszStructType, szOID_NEXT_UPDATE_LOCATION)) + decodeFunc = CRYPT_AsnDecodeAltName; + else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME)) + decodeFunc = CRYPT_AsnDecodeAltName; + else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME2)) + decodeFunc = CRYPT_AsnDecodeAltName; + else + TRACE("OID %s not found or unimplemented, looking for DLL\n", + debugstr_a(lpszStructType)); + if (!decodeFunc) + decodeFunc = (CryptDecodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType, + lpszStructType, CRYPT_OID_DECODE_OBJECT_EX_FUNC, &lib); + if (decodeFunc) + ret = decodeFunc(dwCertEncodingType, lpszStructType, pbEncoded, + cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo); + else + SetLastError(ERROR_FILE_NOT_FOUND); + if (lib) + FreeLibrary(lib); + return ret; +} + +BOOL WINAPI CryptExportPublicKeyInfo(HCRYPTPROV hCryptProv, DWORD dwKeySpec, + DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, DWORD *pcbInfo) +{ + return CryptExportPublicKeyInfoEx(hCryptProv, dwKeySpec, dwCertEncodingType, + NULL, 0, NULL, pInfo, pcbInfo); +} + +static BOOL WINAPI CRYPT_ExportRsaPublicKeyInfoEx(HCRYPTPROV hCryptProv, + DWORD dwKeySpec, DWORD dwCertEncodingType, LPSTR pszPublicKeyObjId, + DWORD dwFlags, void *pvAuxInfo, PCERT_PUBLIC_KEY_INFO pInfo, DWORD *pcbInfo) +{ + BOOL ret; + HCRYPTKEY key; + + TRACE("(%ld, %ld, %08lx, %s, %08lx, %p, %p, %p)\n", hCryptProv, dwKeySpec, + dwCertEncodingType, debugstr_a(pszPublicKeyObjId), dwFlags, pvAuxInfo, + pInfo, pcbInfo); + + if (!pszPublicKeyObjId) + pszPublicKeyObjId = szOID_RSA_RSA; + if ((ret = CryptGetUserKey(hCryptProv, dwKeySpec, &key))) + { + DWORD keySize = 0; + + ret = CryptExportKey(key, 0, PUBLICKEYBLOB, 0, NULL, &keySize); + if (ret) + { + LPBYTE pubKey = CryptMemAlloc(keySize); + + if (pubKey) + { + ret = CryptExportKey(key, 0, PUBLICKEYBLOB, 0, pubKey, + &keySize); + if (ret) + { + DWORD encodedLen = 0; + + ret = CryptEncodeObject(dwCertEncodingType, + RSA_CSP_PUBLICKEYBLOB, pubKey, NULL, &encodedLen); + if (ret) + { + DWORD sizeNeeded = sizeof(CERT_PUBLIC_KEY_INFO) + + strlen(pszPublicKeyObjId) + 1 + encodedLen; + + if (!pInfo) + *pcbInfo = sizeNeeded; + else if (*pcbInfo < sizeNeeded) + { + SetLastError(ERROR_MORE_DATA); + *pcbInfo = sizeNeeded; + ret = FALSE; + } + else + { + pInfo->Algorithm.pszObjId = (char *)pInfo + + sizeof(CERT_PUBLIC_KEY_INFO); + lstrcpyA(pInfo->Algorithm.pszObjId, + pszPublicKeyObjId); + pInfo->Algorithm.Parameters.cbData = 0; + pInfo->Algorithm.Parameters.pbData = NULL; + pInfo->PublicKey.pbData = + (BYTE *)pInfo->Algorithm.pszObjId + + lstrlenA(pInfo->Algorithm.pszObjId) + 1; + pInfo->PublicKey.cbData = encodedLen; + pInfo->PublicKey.cUnusedBits = 0; + ret = CryptEncodeObject(dwCertEncodingType, + RSA_CSP_PUBLICKEYBLOB, pubKey, + pInfo->PublicKey.pbData, &pInfo->PublicKey.cbData); + } + } + } + CryptMemFree(pubKey); + } + else + ret = FALSE; + } + CryptDestroyKey(key); + } + return ret; +} + +typedef BOOL (WINAPI *ExportPublicKeyInfoExFunc)(HCRYPTPROV hCryptProv, + DWORD dwKeySpec, DWORD dwCertEncodingType, LPSTR pszPublicKeyObjId, + DWORD dwFlags, void *pvAuxInfo, PCERT_PUBLIC_KEY_INFO pInfo, DWORD *pcbInfo); + +BOOL WINAPI CryptExportPublicKeyInfoEx(HCRYPTPROV hCryptProv, DWORD dwKeySpec, + DWORD dwCertEncodingType, LPSTR pszPublicKeyObjId, DWORD dwFlags, + void *pvAuxInfo, PCERT_PUBLIC_KEY_INFO pInfo, DWORD *pcbInfo) +{ + BOOL ret; + ExportPublicKeyInfoExFunc exportFunc = NULL; + HMODULE lib = NULL; + + TRACE("(%ld, %ld, %08lx, %s, %08lx, %p, %p, %p)\n", hCryptProv, dwKeySpec, + dwCertEncodingType, debugstr_a(pszPublicKeyObjId), dwFlags, pvAuxInfo, + pInfo, pcbInfo); + + if (!hCryptProv) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (pszPublicKeyObjId) + exportFunc = CRYPT_GetFunc(dwCertEncodingType, pszPublicKeyObjId, + CRYPT_OID_EXPORT_PUBLIC_KEY_INFO_FUNC, &lib); + if (!exportFunc) + exportFunc = CRYPT_ExportRsaPublicKeyInfoEx; + ret = exportFunc(hCryptProv, dwKeySpec, dwCertEncodingType, + pszPublicKeyObjId, dwFlags, pvAuxInfo, pInfo, pcbInfo); + if (lib) + FreeLibrary(lib); + return ret; +} + +BOOL WINAPI CryptImportPublicKeyInfo(HCRYPTPROV hCryptProv, + DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey) +{ + return CryptImportPublicKeyInfoEx(hCryptProv, dwCertEncodingType, pInfo, + 0, 0, NULL, phKey); +} + +static BOOL WINAPI CRYPT_ImportRsaPublicKeyInfoEx(HCRYPTPROV hCryptProv, + DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, ALG_ID aiKeyAlg, + DWORD dwFlags, void *pvAuxInfo, HCRYPTKEY *phKey) +{ + BOOL ret; + DWORD pubKeySize = 0; + + TRACE("(%ld, %ld, %p, %d, %08lx, %p, %p)\n", hCryptProv, + dwCertEncodingType, pInfo, aiKeyAlg, dwFlags, pvAuxInfo, phKey); + + ret = CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, + pInfo->PublicKey.pbData, pInfo->PublicKey.cbData, 0, NULL, &pubKeySize); + if (ret) + { + LPBYTE pubKey = CryptMemAlloc(pubKeySize); + + if (pubKey) + { + ret = CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, + pInfo->PublicKey.pbData, pInfo->PublicKey.cbData, 0, pubKey, + &pubKeySize); + if (ret) + ret = CryptImportKey(hCryptProv, pubKey, pubKeySize, 0, 0, + phKey); + CryptMemFree(pubKey); + } + else + ret = FALSE; + } + return ret; +} + +typedef BOOL (WINAPI *ImportPublicKeyInfoExFunc)(HCRYPTPROV hCryptProv, + DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, ALG_ID aiKeyAlg, + DWORD dwFlags, void *pvAuxInfo, HCRYPTKEY *phKey); + +BOOL WINAPI CryptImportPublicKeyInfoEx(HCRYPTPROV hCryptProv, + DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, ALG_ID aiKeyAlg, + DWORD dwFlags, void *pvAuxInfo, HCRYPTKEY *phKey) +{ + BOOL ret; + ImportPublicKeyInfoExFunc importFunc = NULL; + HMODULE lib = NULL; + + TRACE("(%ld, %ld, %p, %d, %08lx, %p, %p)\n", hCryptProv, + dwCertEncodingType, pInfo, aiKeyAlg, dwFlags, pvAuxInfo, phKey); + + importFunc = CRYPT_GetFunc(dwCertEncodingType, pInfo->Algorithm.pszObjId, + CRYPT_OID_IMPORT_PUBLIC_KEY_INFO_FUNC, &lib); + if (!importFunc) + importFunc = CRYPT_ImportRsaPublicKeyInfoEx; + ret = importFunc(hCryptProv, dwCertEncodingType, pInfo, aiKeyAlg, dwFlags, + pvAuxInfo, phKey); + if (lib) + FreeLibrary(lib); + return ret; +} diff --git a/reactos/lib/crypt32/main.c b/reactos/lib/crypt32/main.c new file mode 100644 index 00000000000..0dda361f9aa --- /dev/null +++ b/reactos/lib/crypt32/main.c @@ -0,0 +1,341 @@ +/* + * Copyright 2002 Mike McCormack for CodeWeavers + * Copyright 2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(crypt); + +static HCRYPTPROV hDefProv; + +struct OIDToAlgID +{ + LPCSTR oid; + DWORD algID; +}; + +static const struct OIDToAlgID oidToAlgID[] = { + { szOID_RSA_RSA, CALG_RSA_KEYX }, + { szOID_RSA_MD2RSA, CALG_MD2 }, + { szOID_RSA_MD4RSA, CALG_MD4 }, + { szOID_RSA_MD5RSA, CALG_MD5 }, + { szOID_RSA_SHA1RSA, CALG_SHA }, + { szOID_RSA_DH, CALG_DH_SF }, + { szOID_RSA_SMIMEalgESDH, CALG_DH_EPHEM }, + { szOID_RSA_SMIMEalgCMS3DESwrap, CALG_3DES }, + { szOID_RSA_SMIMEalgCMSRC2wrap, CALG_RC2 }, + { szOID_RSA_MD2, CALG_MD2 }, + { szOID_RSA_MD4, CALG_MD4 }, + { szOID_RSA_MD5, CALG_MD5 }, + { szOID_RSA_RC2CBC, CALG_RC2 }, + { szOID_RSA_RC4, CALG_RC4 }, + { szOID_RSA_DES_EDE3_CBC, CALG_3DES }, + { szOID_ANSI_X942_DH, CALG_DH_SF }, + { szOID_X957_DSA, CALG_DSS_SIGN }, + { szOID_X957_SHA1DSA, CALG_SHA }, + { szOID_OIWSEC_md4RSA, CALG_MD4 }, + { szOID_OIWSEC_md5RSA, CALG_MD5 }, + { szOID_OIWSEC_md4RSA2, CALG_MD4 }, + { szOID_OIWSEC_desCBC, CALG_DES }, + { szOID_OIWSEC_dsa, CALG_DSS_SIGN }, + { szOID_OIWSEC_shaDSA, CALG_SHA }, + { szOID_OIWSEC_shaRSA, CALG_SHA }, + { szOID_OIWSEC_sha, CALG_SHA }, + { szOID_OIWSEC_rsaXchg, CALG_RSA_KEYX }, + { szOID_OIWSEC_sha1, CALG_SHA }, + { szOID_OIWSEC_dsaSHA1, CALG_SHA }, + { szOID_OIWSEC_sha1RSASign, CALG_SHA }, + { szOID_OIWDIR_md2RSA, CALG_MD2 }, + { szOID_INFOSEC_mosaicUpdatedSig, CALG_SHA }, + { szOID_INFOSEC_mosaicKMandUpdSig, CALG_DSS_SIGN }, +}; + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_DETACH: + if (hDefProv) CryptReleaseContext(hDefProv, 0); + break; + } + return TRUE; +} + +HCRYPTPROV CRYPT_GetDefaultProvider(void) +{ + if (!hDefProv) + CryptAcquireContextW(&hDefProv, NULL, MS_ENHANCED_PROV_W, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + return hDefProv; +} + +/* this function is called by Internet Explorer when it is about to verify a downloaded component */ +BOOL WINAPI I_CryptCreateLruCache(DWORD x, DWORD y) +{ + FIXME("stub!\n"); + return FALSE; +} + +/* these functions all have an unknown number of args */ +BOOL WINAPI I_CryptFindLruEntryData(DWORD x) +{ + FIXME("stub!\n"); + return FALSE; +} + +BOOL WINAPI I_CryptFlushLruCache(DWORD x) +{ + FIXME("stub!\n"); + return FALSE; +} + +BOOL WINAPI I_CryptFreeLruCache(DWORD x) +{ + FIXME("stub!\n"); + return FALSE; +} + +BOOL WINAPI CryptSIPRemoveProvider(GUID *pgProv) +{ + FIXME("stub!\n"); + return FALSE; +} + +/* convert a guid to a wide character string */ +static void CRYPT_guid2wstr( LPGUID guid, LPWSTR wstr ) +{ + char str[40]; + + sprintf(str, "{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7] ); + MultiByteToWideChar( CP_ACP, 0, str, -1, wstr, 40 ); +} + +/* + * Helper for CryptSIPAddProvider + * + * Add a registry key containing a dll name and function under + * "Software\\Microsoft\\Cryptography\\OID\\EncodingType 0\\\\" + */ +static LONG CRYPT_SIPWriteFunction( LPGUID guid, LPCWSTR szKey, + LPCWSTR szDll, LPCWSTR szFunction ) +{ + static const WCHAR szOID[] = { + 'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'C','r','y','p','t','o','g','r','a','p','h','y','\\', + 'O','I','D','\\', + 'E','n','c','o','d','i','n','g','T','y','p','e',' ','0','\\', + 'C','r','y','p','t','S','I','P','D','l','l', 0 }; + static const WCHAR szBackSlash[] = { '\\', 0 }; + static const WCHAR szDllName[] = { 'D','l','l',0 }; + static const WCHAR szFuncName[] = { 'F','u','n','c','N','a','m','e',0 }; + WCHAR szFullKey[ 0x100 ]; + LONG r; + HKEY hKey; + + if( !szFunction ) + return ERROR_SUCCESS; + + /* max length of szFullKey depends on our code only, so we won't overrun */ + lstrcpyW( szFullKey, szOID ); + lstrcatW( szFullKey, szKey ); + lstrcatW( szFullKey, szBackSlash ); + CRYPT_guid2wstr( guid, &szFullKey[ lstrlenW( szFullKey ) ] ); + lstrcatW( szFullKey, szBackSlash ); + + TRACE("key is %s\n", debugstr_w( szFullKey ) ); + + r = RegCreateKeyW( HKEY_LOCAL_MACHINE, szFullKey, &hKey ); + if( r != ERROR_SUCCESS ) + return r; + + /* write the values */ + RegSetValueExW( hKey, szFuncName, 0, REG_SZ, (const BYTE*) szFunction, + ( lstrlenW( szFunction ) + 1 ) * sizeof (WCHAR) ); + RegSetValueExW( hKey, szDllName, 0, REG_SZ, (const BYTE*) szDll, + ( lstrlenW( szDll ) + 1) * sizeof (WCHAR) ); + + RegCloseKey( hKey ); + + return ERROR_SUCCESS; +} + +BOOL WINAPI CryptSIPAddProvider(SIP_ADD_NEWPROVIDER *psNewProv) +{ + static const WCHAR szCreate[] = { + 'C','r','e','a','t','e', + 'I','n','d','i','r','e','c','t','D','a','t','a',0}; + static const WCHAR szGetSigned[] = { + 'G','e','t','S','i','g','n','e','d','D','a','t','a','M','s','g',0}; + static const WCHAR szIsMyFile[] = { + 'I','s','M','y','F','i','l','e','T','y','p','e', 0 }; + static const WCHAR szPutSigned[] = { + 'P','u','t','S','i','g','n','e','d','D','a','t','a','M','s','g',0}; + static const WCHAR szRemoveSigned[] = { + 'R','e','m','o','v','e', + 'S','i','g','n','e','d','D','a','t','a','M','s','g',0}; + static const WCHAR szVerify[] = { + 'V','e','r','i','f','y', + 'I','n','d','i','r','e','c','t','D','a','t','a',0}; + + TRACE("%p\n", psNewProv); + + if( !psNewProv ) + return FALSE; + + TRACE("%s %s %s %s\n", + debugstr_guid( psNewProv->pgSubject ), + debugstr_w( psNewProv->pwszDLLFileName ), + debugstr_w( psNewProv->pwszMagicNumber ), + debugstr_w( psNewProv->pwszIsFunctionName ) ); + +#define CRYPT_SIPADDPROV( key, field ) \ + CRYPT_SIPWriteFunction( psNewProv->pgSubject, key, \ + psNewProv->pwszDLLFileName, psNewProv->field) + + CRYPT_SIPADDPROV( szGetSigned, pwszGetFuncName ); + CRYPT_SIPADDPROV( szPutSigned, pwszPutFuncName ); + CRYPT_SIPADDPROV( szCreate, pwszCreateFuncName ); + CRYPT_SIPADDPROV( szVerify, pwszVerifyFuncName ); + CRYPT_SIPADDPROV( szRemoveSigned, pwszRemoveFuncName ); + CRYPT_SIPADDPROV( szIsMyFile, pwszIsFunctionNameFmt2 ); + +#undef CRYPT_SIPADDPROV + + return TRUE; +} + +BOOL WINAPI CryptSIPRetrieveSubjectGuid + (LPCWSTR FileName, HANDLE hFileIn, GUID *pgSubject) +{ + FIXME("stub!\n"); + return FALSE; +} + +BOOL WINAPI CryptSIPLoad + (const GUID *pgSubject, DWORD dwFlags, SIP_DISPATCH_INFO *pSipDispatch) +{ + FIXME("stub!\n"); + return FALSE; +} + +BOOL WINAPI CryptRegisterDefaultOIDFunction(DWORD dwEncodingType, + LPCSTR pszFuncName, DWORD dwIndex, + LPCWSTR pwszDll) +{ + FIXME("(%lx,%s,%lx,%s) stub!\n", dwEncodingType, pszFuncName, dwIndex, + debugstr_w(pwszDll)); + return FALSE; +} + +LPCSTR WINAPI CertAlgIdToOID(DWORD dwAlgId) +{ + switch (dwAlgId) + { + case CALG_RSA_KEYX: + return szOID_RSA_RSA; + case CALG_DH_EPHEM: + return szOID_RSA_SMIMEalgESDH; + case CALG_MD2: + return szOID_RSA_MD2; + case CALG_MD4: + return szOID_RSA_MD4; + case CALG_MD5: + return szOID_RSA_MD5; + case CALG_RC2: + return szOID_RSA_RC2CBC; + case CALG_RC4: + return szOID_RSA_RC4; + case CALG_3DES: + return szOID_RSA_DES_EDE3_CBC; + case CALG_DH_SF: + return szOID_ANSI_X942_DH; + case CALG_DSS_SIGN: + return szOID_X957_DSA; + case CALG_DES: + return szOID_OIWSEC_desCBC; + case CALG_SHA: + return szOID_OIWSEC_sha1; + default: + return NULL; + } +} + +DWORD WINAPI CertOIDToAlgId(LPCSTR pszObjId) +{ + int i; + + if (pszObjId) + { + for (i = 0; i < sizeof(oidToAlgID) / sizeof(oidToAlgID[0]); i++) + { + if (!strcmp(pszObjId, oidToAlgID[i].oid)) + return oidToAlgID[i].algID; + } + } + return 0; +} + +LPVOID WINAPI CryptMemAlloc(ULONG cbSize) +{ + return HeapAlloc(GetProcessHeap(), 0, cbSize); +} + +LPVOID WINAPI CryptMemRealloc(LPVOID pv, ULONG cbSize) +{ + return HeapReAlloc(GetProcessHeap(), 0, pv, cbSize); +} + +VOID WINAPI CryptMemFree(LPVOID pv) +{ + HeapFree(GetProcessHeap(), 0, pv); +} + +DWORD WINAPI I_CryptAllocTls(void) +{ + return TlsAlloc(); +} + +LPVOID WINAPI I_CryptDetachTls(DWORD dwTlsIndex) +{ + LPVOID ret; + + ret = TlsGetValue(dwTlsIndex); + + TlsSetValue(dwTlsIndex, NULL); + return ret; +} + +LPVOID WINAPI I_CryptGetTls(DWORD dwTlsIndex) +{ + return TlsGetValue(dwTlsIndex); +} + +BOOL WINAPI I_CryptSetTls(DWORD dwTlsIndex, LPVOID lpTlsValue) +{ + return TlsSetValue(dwTlsIndex, lpTlsValue); +} + +BOOL WINAPI I_CryptFreeTls(DWORD dwTlsIndex, DWORD unknown) +{ + TRACE("(%ld, %ld)\n", dwTlsIndex, unknown); + return TlsFree(dwTlsIndex); +} diff --git a/reactos/lib/crypt32/precomp.h b/reactos/lib/crypt32/precomp.h new file mode 100644 index 00000000000..4e76ba75c0d --- /dev/null +++ b/reactos/lib/crypt32/precomp.h @@ -0,0 +1,20 @@ + +#include "config.h" + +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wincrypt.h" +#include "winreg.h" +#include "snmp.h" +#include "winnls.h" +#include "mssip.h" +#include "crypt32_private.h" +#include "wine/debug.h" + +#define NTOS_MODE_USER +#include diff --git a/reactos/lib/crypt32/protectdata.c b/reactos/lib/crypt32/protectdata.c new file mode 100644 index 00000000000..d62f8372e79 --- /dev/null +++ b/reactos/lib/crypt32/protectdata.c @@ -0,0 +1,1128 @@ +/* + * Copyright 2005 Kees Cook + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * The Win32 CryptProtectData and CryptUnprotectData functions are meant + * to provide a mechanism for encrypting data on a machine where other users + * of the system can't be trusted. It is used in many examples as a way + * to store username and password information to the registry, but store + * it not in the clear. + * + * The encryption is symmetric, but the method is unknown. However, since + * it is keyed to the machine and the user, it is unlikely that the values + * would be portable. Since programs must first call CryptProtectData to + * get a cipher text, the underlying system doesn't have to exactly + * match the real Windows version. However, attempts have been made to + * at least try to look like the Windows version, including guesses at the + * purpose of various portions of the "opaque data blob" that is used. + * + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(crypt); + +#define CRYPT32_PROTECTDATA_PROV PROV_RSA_FULL +#define CRYPT32_PROTECTDATA_HASH_CALG CALG_MD5 +#define CRYPT32_PROTECTDATA_KEY_CALG CALG_RC2 +#define CRYPT32_PROTECTDATA_SALT_LEN 16 + +static const BYTE crypt32_protectdata_secret[] = { + 'I','\'','m',' ','h','u','n','t','i','n','g',' ', + 'w','a','b','b','i','t','s',0 +}; + +/* + * The data format returned by the real Windows CryptProtectData seems + * to be something like this: + + DWORD count0; - how many "info0_*[16]" blocks follow (was always 1) + BYTE info0_0[16]; - unknown information + ... + DWORD count1; - how many "info1_*[16]" blocks follow (was always 1) + BYTE info1_0[16]; - unknown information + ... + DWORD null0; - NULL "end of records"? + DWORD str_len; - length of WCHAR string including term + WCHAR str[str_len]; - The "dataDescription" value + DWORD unknown0; - unknown value (seems large, but only WORD large) + DWORD unknown1; - unknown value (seems small, less than a BYTE) + DWORD data_len; - length of data (was 16 in samples) + BYTE data[data_len]; - unknown data (fingerprint?) + DWORD null1; - NULL ? + DWORD unknown2; - unknown value (seems large, but only WORD large) + DWORD unknown3; - unknown value (seems small, less than a BYTE) + DWORD salt_len; - length of salt(?) data + BYTE salt[salt_len]; - salt(?) for symmetric encryption + DWORD cipher_len; - length of cipher(?) data - was close to plain len + BYTE cipher[cipher_len]; - cipher text? + DWORD crc_len; - length of fingerprint(?) data - was 20 byte==160b SHA1 + BYTE crc[crc_len]; - fingerprint of record? + + * The data structures used in Wine are modelled after this guess. + */ + +struct protect_data_t +{ + DWORD count0; + DATA_BLOB info0; /* using this to hold crypt_magic_str */ + DWORD count1; + DATA_BLOB info1; + DWORD null0; + WCHAR * szDataDescr; /* serialized differently than the DATA_BLOBs */ + DWORD unknown0; /* perhaps the HASH alg const should go here? */ + DWORD unknown1; + DATA_BLOB data0; + DWORD null1; + DWORD unknown2; /* perhaps the KEY alg const should go here? */ + DWORD unknown3; + DATA_BLOB salt; + DATA_BLOB cipher; + DATA_BLOB fingerprint; +}; + +/* this is used to check if an incoming structure was built by Wine */ +static const char * crypt_magic_str = "Wine Crypt32 ok"; + +/* debugging tool to print strings of hex chars */ +static const char * +hex_str(unsigned char *p, int n) +{ + const char * ptr; + char report[80]; + int r=-1; + report[0]='\0'; + ptr = wine_dbg_sprintf("%s",""); + while (--n >= 0) + { + if (r++ % 20 == 19) + { + ptr = wine_dbg_sprintf("%s%s",ptr,report); + report[0]='\0'; + } + sprintf(report+strlen(report),"%s%02x", r ? "," : "", *p++); + } + return wine_dbg_sprintf("%s%s",ptr,report); +} + +#define TRACE_DATA_BLOB(blob) do { \ + TRACE("%s cbData: %u\n", #blob ,(unsigned int)((blob)->cbData)); \ + TRACE("%s pbData @ %p:%s\n", #blob ,(blob)->pbData, \ + hex_str((blob)->pbData, (blob)->cbData)); \ +} while (0) + +static +void serialize_dword(DWORD value,BYTE ** ptr) +{ + /*TRACE("called\n");*/ + + memcpy(*ptr,&value,sizeof(DWORD)); + *ptr+=sizeof(DWORD); +} + +static +void serialize_string(BYTE * str,BYTE ** ptr,DWORD len, DWORD width, + BOOL prepend_len) +{ + /*TRACE("called %ux%u\n",(unsigned int)len,(unsigned int)width);*/ + + if (prepend_len) + { + serialize_dword(len,ptr); + } + memcpy(*ptr,str,len*width); + *ptr+=len*width; +} + +static +BOOL unserialize_dword(BYTE * ptr, DWORD *index, DWORD size, DWORD * value) +{ + /*TRACE("called\n");*/ + + if (!ptr || !index || !value) return FALSE; + + if (*index+sizeof(DWORD)>size) + { + return FALSE; + } + + memcpy(value,&(ptr[*index]),sizeof(DWORD)); + *index+=sizeof(DWORD); + + return TRUE; +} + +static +BOOL unserialize_string(BYTE * ptr, DWORD *index, DWORD size, + DWORD len, DWORD width, BOOL inline_len, + BYTE ** data, DWORD * stored) +{ + /*TRACE("called\n");*/ + + if (!ptr || !data) return FALSE; + + if (inline_len) { + if (!unserialize_dword(ptr,index,size,&len)) + return FALSE; + } + + if (*index+len*width>size) + { + return FALSE; + } + + if (!(*data = CryptMemAlloc( len*width))) + { + return FALSE; + } + + memcpy(*data,&(ptr[*index]),len*width); + if (stored) + { + *stored = len; + } + *index+=len*width; + + return TRUE; +} + +static +BOOL serialize(struct protect_data_t * pInfo, DATA_BLOB * pSerial) +{ + BYTE * ptr; + DWORD dwStrLen; + DWORD dwStruct; + + TRACE("called\n"); + + if (!pInfo || !pInfo->szDataDescr || !pSerial || + !pInfo->info0.pbData || !pInfo->info1.pbData || + !pInfo->data0.pbData || !pInfo->salt.pbData || + !pInfo->cipher.pbData || !pInfo->fingerprint.pbData) + { + return FALSE; + } + + if (pInfo->info0.cbData!=16) + { + ERR("protect_data_t info0 not 16 bytes long\n"); + } + + if (pInfo->info1.cbData!=16) + { + ERR("protect_data_t info1 not 16 bytes long\n"); + } + + dwStrLen=lstrlenW(pInfo->szDataDescr); + + pSerial->cbData=0; + pSerial->cbData+=sizeof(DWORD)*8; /* 8 raw DWORDs */ + pSerial->cbData+=sizeof(DWORD)*4; /* 4 BLOBs with size */ + pSerial->cbData+=pInfo->info0.cbData; + pSerial->cbData+=pInfo->info1.cbData; + pSerial->cbData+=(dwStrLen+1)*sizeof(WCHAR) + 4; /* str, null, size */ + pSerial->cbData+=pInfo->data0.cbData; + pSerial->cbData+=pInfo->salt.cbData; + pSerial->cbData+=pInfo->cipher.cbData; + pSerial->cbData+=pInfo->fingerprint.cbData; + + /* save the actual structure size */ + dwStruct = pSerial->cbData; + /* There may be a 256 byte minimum, but I can't prove it. */ + /*if (pSerial->cbData<256) pSerial->cbData=256;*/ + + pSerial->pbData=LocalAlloc(LPTR,pSerial->cbData); + if (!pSerial->pbData) return FALSE; + + ptr=pSerial->pbData; + + /* count0 */ + serialize_dword(pInfo->count0,&ptr); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* info0 */ + serialize_string(pInfo->info0.pbData,&ptr, + pInfo->info0.cbData,sizeof(BYTE),FALSE); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* count1 */ + serialize_dword(pInfo->count1,&ptr); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* info1 */ + serialize_string(pInfo->info1.pbData,&ptr, + pInfo->info1.cbData,sizeof(BYTE),FALSE); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* null0 */ + serialize_dword(pInfo->null0,&ptr); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* szDataDescr */ + serialize_string((BYTE*)pInfo->szDataDescr,&ptr, + (dwStrLen+1)*sizeof(WCHAR),sizeof(BYTE),TRUE); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* unknown0 */ + serialize_dword(pInfo->unknown0,&ptr); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + /* unknown1 */ + serialize_dword(pInfo->unknown1,&ptr); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* data0 */ + serialize_string(pInfo->data0.pbData,&ptr, + pInfo->data0.cbData,sizeof(BYTE),TRUE); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* null1 */ + serialize_dword(pInfo->null1,&ptr); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* unknown2 */ + serialize_dword(pInfo->unknown2,&ptr); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + /* unknown3 */ + serialize_dword(pInfo->unknown3,&ptr); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* salt */ + serialize_string(pInfo->salt.pbData,&ptr, + pInfo->salt.cbData,sizeof(BYTE),TRUE); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* cipher */ + serialize_string(pInfo->cipher.pbData,&ptr, + pInfo->cipher.cbData,sizeof(BYTE),TRUE); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + /* fingerprint */ + serialize_string(pInfo->fingerprint.pbData,&ptr, + pInfo->fingerprint.cbData,sizeof(BYTE),TRUE); + /*TRACE("used %u\n",ptr-pSerial->pbData);*/ + + if (ptr - pSerial->pbData != dwStruct) + { + ERR("struct size changed!? %u != expected %u\n", + ptr - pSerial->pbData, (unsigned int)dwStruct); + LocalFree(pSerial->pbData); + pSerial->pbData=NULL; + pSerial->cbData=0; + return FALSE; + } + + return TRUE; +} + +static +BOOL unserialize(DATA_BLOB * pSerial, struct protect_data_t * pInfo) +{ + BYTE * ptr; + DWORD index; + DWORD size; + BOOL status=TRUE; + + TRACE("called\n"); + + if (!pInfo || !pSerial || !pSerial->pbData) + return FALSE; + + index=0; + ptr=pSerial->pbData; + size=pSerial->cbData; + + /* count0 */ + if (!unserialize_dword(ptr,&index,size,&pInfo->count0)) + { + ERR("reading count0 failed!\n"); + return FALSE; + } + + /* info0 */ + if (!unserialize_string(ptr,&index,size,16,sizeof(BYTE),FALSE, + &pInfo->info0.pbData, &pInfo->info0.cbData)) + { + ERR("reading info0 failed!\n"); + return FALSE; + } + + /* count1 */ + if (!unserialize_dword(ptr,&index,size,&pInfo->count1)) + { + ERR("reading count1 failed!\n"); + return FALSE; + } + + /* info1 */ + if (!unserialize_string(ptr,&index,size,16,sizeof(BYTE),FALSE, + &pInfo->info1.pbData, &pInfo->info1.cbData)) + { + ERR("reading info1 failed!\n"); + return FALSE; + } + + /* null0 */ + if (!unserialize_dword(ptr,&index,size,&pInfo->null0)) + { + ERR("reading null0 failed!\n"); + return FALSE; + } + + /* szDataDescr */ + if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE, + (BYTE**)&pInfo->szDataDescr, NULL)) + { + ERR("reading szDataDescr failed!\n"); + return FALSE; + } + + /* unknown0 */ + if (!unserialize_dword(ptr,&index,size,&pInfo->unknown0)) + { + ERR("reading unknown0 failed!\n"); + return FALSE; + } + + /* unknown1 */ + if (!unserialize_dword(ptr,&index,size,&pInfo->unknown1)) + { + ERR("reading unknown1 failed!\n"); + return FALSE; + } + + /* data0 */ + if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE, + &pInfo->data0.pbData, &pInfo->data0.cbData)) + { + ERR("reading data0 failed!\n"); + return FALSE; + } + + /* null1 */ + if (!unserialize_dword(ptr,&index,size,&pInfo->null1)) + { + ERR("reading null1 failed!\n"); + return FALSE; + } + + /* unknown2 */ + if (!unserialize_dword(ptr,&index,size,&pInfo->unknown2)) + { + ERR("reading unknown2 failed!\n"); + return FALSE; + } + + /* unknown3 */ + if (!unserialize_dword(ptr,&index,size,&pInfo->unknown3)) + { + ERR("reading unknown3 failed!\n"); + return FALSE; + } + + /* salt */ + if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE, + &pInfo->salt.pbData, &pInfo->salt.cbData)) + { + ERR("reading salt failed!\n"); + return FALSE; + } + + /* cipher */ + if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE, + &pInfo->cipher.pbData, &pInfo->cipher.cbData)) + { + ERR("reading cipher failed!\n"); + return FALSE; + } + + /* fingerprint */ + if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE, + &pInfo->fingerprint.pbData, &pInfo->fingerprint.cbData)) + { + ERR("reading fingerprint failed!\n"); + return FALSE; + } + + /* allow structure size to be too big (since some applications + * will pad this up to 256 bytes, it seems) */ + if (index>size) + { + /* this is an impossible-to-reach test, but if the padding + * issue is ever understood, this may become more useful */ + ERR("loaded corrupt structure! (used %u expected %u)\n", + (unsigned int)index, (unsigned int)size); + status=FALSE; + } + + return status; +} + +/* perform sanity checks */ +static +BOOL valid_protect_data(struct protect_data_t * pInfo) +{ + BOOL status=TRUE; + + TRACE("called\n"); + + if (pInfo->count0 != 0x0001) + { + ERR("count0 != 0x0001 !\n"); + status=FALSE; + } + if (pInfo->count1 != 0x0001) + { + ERR("count0 != 0x0001 !\n"); + status=FALSE; + } + if (pInfo->null0 != 0x0000) + { + ERR("null0 != 0x0000 !\n"); + status=FALSE; + } + if (pInfo->null1 != 0x0000) + { + ERR("null1 != 0x0000 !\n"); + status=FALSE; + } + /* since we have no idea what info0 is used for, and it seems + * rather constant, we can test for a Wine-specific magic string + * there to be reasonably sure we're using data created by the Wine + * implementation of CryptProtectData. + */ + if (pInfo->info0.cbData!=strlen(crypt_magic_str)+1 || + strcmp( (LPCSTR)pInfo->info0.pbData,crypt_magic_str) != 0) + { + ERR("info0 magic value not matched !\n"); + status=FALSE; + } + + if (!status) + { + ERR("unrecognized CryptProtectData block\n"); + } + + return status; +} + +static +void free_protect_data(struct protect_data_t * pInfo) +{ + TRACE("called\n"); + + if (!pInfo) return; + + if (pInfo->info0.pbData) + CryptMemFree(pInfo->info0.pbData); + if (pInfo->info1.pbData) + CryptMemFree(pInfo->info1.pbData); + if (pInfo->szDataDescr) + CryptMemFree(pInfo->szDataDescr); + if (pInfo->data0.pbData) + CryptMemFree(pInfo->data0.pbData); + if (pInfo->salt.pbData) + CryptMemFree(pInfo->salt.pbData); + if (pInfo->cipher.pbData) + CryptMemFree(pInfo->cipher.pbData); + if (pInfo->fingerprint.pbData) + CryptMemFree(pInfo->fingerprint.pbData); +} + +/* copies a string into a data blob */ +static +BYTE * convert_str_to_blob(char* str, DATA_BLOB* blob) +{ + if (!str || !blob) return NULL; + + blob->cbData=strlen(str)+1; + if (!(blob->pbData=CryptMemAlloc(blob->cbData))) + { + blob->cbData=0; + } + else { + strcpy((LPSTR)blob->pbData, str); + } + + return blob->pbData; +} + +/* + * Populates everything except "cipher" and "fingerprint". + */ +static +BOOL fill_protect_data(struct protect_data_t * pInfo, LPCWSTR szDataDescr, + HCRYPTPROV hProv) +{ + DWORD dwStrLen; + + TRACE("called\n"); + + if (!pInfo) return FALSE; + + dwStrLen=lstrlenW(szDataDescr); + + memset(pInfo,0,sizeof(*pInfo)); + + pInfo->count0=0x0001; + + convert_str_to_blob((char*)crypt_magic_str,&pInfo->info0); + + pInfo->count1=0x0001; + + convert_str_to_blob((char*)crypt_magic_str,&pInfo->info1); + + pInfo->null0=0x0000; + + if ((pInfo->szDataDescr=CryptMemAlloc((dwStrLen+1)*sizeof(WCHAR)))) + { + memcpy(pInfo->szDataDescr,szDataDescr,(dwStrLen+1)*sizeof(WCHAR)); + } + + pInfo->unknown0=0x0000; + pInfo->unknown1=0x0000; + + convert_str_to_blob((char*)crypt_magic_str,&pInfo->data0); + + pInfo->null1=0x0000; + pInfo->unknown2=0x0000; + pInfo->unknown3=0x0000; + + /* allocate memory to hold a salt */ + pInfo->salt.cbData=CRYPT32_PROTECTDATA_SALT_LEN; + if ((pInfo->salt.pbData=CryptMemAlloc(pInfo->salt.cbData))) + { + /* generate random salt */ + if (!CryptGenRandom(hProv, pInfo->salt.cbData, pInfo->salt.pbData)) + { + ERR("CryptGenRandom\n"); + free_protect_data(pInfo); + return FALSE; + } + } + + /* debug: show our salt */ + TRACE_DATA_BLOB(&pInfo->salt); + + pInfo->cipher.cbData=0; + pInfo->cipher.pbData=NULL; + + pInfo->fingerprint.cbData=0; + pInfo->fingerprint.pbData=NULL; + + /* check all the allocations at once */ + if (!pInfo->info0.pbData || + !pInfo->info1.pbData || + !pInfo->szDataDescr || + !pInfo->data0.pbData || + !pInfo->salt.pbData + ) + { + ERR("could not allocate protect_data structures\n"); + free_protect_data(pInfo); + return FALSE; + } + + return TRUE; +} + +static +BOOL convert_hash_to_blob(HCRYPTHASH hHash, DATA_BLOB * blob) +{ + DWORD dwSize; + + TRACE("called\n"); + + if (!blob) return FALSE; + + dwSize=sizeof(DWORD); + if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&blob->cbData, + &dwSize, 0)) + { + ERR("failed to get hash size\n"); + return FALSE; + } + + if (!(blob->pbData=CryptMemAlloc(blob->cbData))) + { + ERR("failed to allocate blob memory\n"); + return FALSE; + } + + dwSize=blob->cbData; + if (!CryptGetHashParam(hHash, HP_HASHVAL, blob->pbData, &dwSize, 0)) + { + ERR("failed to get hash value\n"); + CryptMemFree(blob->pbData); + blob->pbData=NULL; + blob->cbData=0; + return FALSE; + } + + return TRUE; +} + +/* test that a given hash matches an exported-to-blob hash value */ +static +BOOL hash_matches_blob(HCRYPTHASH hHash, DATA_BLOB * two) +{ + BOOL rc = FALSE; + DATA_BLOB one; + + if (!two || !two->pbData) return FALSE; + + if (!convert_hash_to_blob(hHash,&one)) { + return FALSE; + } + + if ( one.cbData == two->cbData && + memcmp( one.pbData, two->pbData, one.cbData ) == 0 ) + { + rc = TRUE; + } + + CryptMemFree(one.pbData); + return rc; +} + +/* create an encryption key from a given salt and optional entropy */ +static +BOOL load_encryption_key(HCRYPTPROV hProv, DATA_BLOB * salt, + DATA_BLOB * pOptionalEntropy, HCRYPTKEY * phKey) +{ + BOOL rc = TRUE; + HCRYPTHASH hSaltHash; + char * szUsername = NULL; + DWORD dwUsernameLen; + DWORD dwError; + + /* create hash for salt */ + if (!salt || !phKey || + !CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hSaltHash)) + { + ERR("CryptCreateHash\n"); + return FALSE; + } + + /* This should be the "logon credentials" instead of username */ + dwError=GetLastError(); + dwUsernameLen = 0; + if (!GetUserNameA(NULL,&dwUsernameLen) && + GetLastError()==ERROR_MORE_DATA && dwUsernameLen && + (szUsername = CryptMemAlloc(dwUsernameLen))) + { + szUsername[0]='\0'; + GetUserNameA( szUsername, &dwUsernameLen ); + } + SetLastError(dwError); + + /* salt the hash with: + * - the user id + * - an "internal secret" + * - randomness (from the salt) + * - user-supplied entropy + */ + if ((szUsername && !CryptHashData(hSaltHash,(LPBYTE)szUsername,dwUsernameLen,0)) || + !CryptHashData(hSaltHash,crypt32_protectdata_secret, + sizeof(crypt32_protectdata_secret)-1,0) || + !CryptHashData(hSaltHash,salt->pbData,salt->cbData,0) || + (pOptionalEntropy && !CryptHashData(hSaltHash, + pOptionalEntropy->pbData, + pOptionalEntropy->cbData,0))) + { + ERR("CryptHashData\n"); + rc = FALSE; + } + + /* produce a symmetric key */ + if (rc && !CryptDeriveKey(hProv,CRYPT32_PROTECTDATA_KEY_CALG, + hSaltHash,CRYPT_EXPORTABLE,phKey)) + { + ERR("CryptDeriveKey\n"); + rc = FALSE; + } + + /* clean up */ + CryptDestroyHash(hSaltHash); + if (szUsername) CryptMemFree(szUsername); + + return rc; +} + +/* debugging tool to print the structures of a ProtectData call */ +static void +report(DATA_BLOB* pDataIn, DATA_BLOB* pOptionalEntropy, + CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags) +{ + TRACE("pPromptStruct: %p\n", pPromptStruct); + if (pPromptStruct) + { + TRACE(" cbSize: 0x%x\n",(unsigned int)pPromptStruct->cbSize); + TRACE(" dwPromptFlags: 0x%x\n",(unsigned int)pPromptStruct->dwPromptFlags); + TRACE(" hwndApp: %p\n", pPromptStruct->hwndApp); + TRACE(" szPrompt: %p %s\n", + pPromptStruct->szPrompt, + pPromptStruct->szPrompt ? debugstr_w(pPromptStruct->szPrompt) + : ""); + } + TRACE("dwFlags: 0x%04x\n",(unsigned int)dwFlags); + TRACE_DATA_BLOB(pDataIn); + if (pOptionalEntropy) + { + TRACE_DATA_BLOB(pOptionalEntropy); + TRACE(" %s\n",debugstr_an((LPCSTR)pOptionalEntropy->pbData,pOptionalEntropy->cbData)); + } + +} + + +/*************************************************************************** + * CryptProtectData [CRYPT32.@] + * + * Generate Cipher data from given Plain and Entropy data. + * + * PARAMS + * pDataIn [I] Plain data to be enciphered + * szDataDescr [I] Optional Unicode string describing the Plain data + * pOptionalEntropy [I] Optional entropy data to adjust cipher, can be NULL + * pvReserved [I] Reserved, must be NULL + * pPromptStruct [I] Structure describing if/how to prompt during ciphering + * dwFlags [I] Flags describing options to the ciphering + * pDataOut [O] Resulting Cipher data, for calls to CryptUnprotectData + * + * RETURNS + * TRUE If a Cipher was generated. + * FALSE If something failed and no Cipher is available. + * + * FIXME + * The true Windows encryption and keying mechanisms are unknown. + * + * dwFlags and pPromptStruct are currently ignored. + * + * NOTES + * Memory allocated in pDataOut must be freed with LocalFree. + * + */ +BOOL WINAPI CryptProtectData(DATA_BLOB* pDataIn, + LPCWSTR szDataDescr, + DATA_BLOB* pOptionalEntropy, + PVOID pvReserved, + CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, + DWORD dwFlags, + DATA_BLOB* pDataOut) +{ + BOOL rc = FALSE; + + HCRYPTPROV hProv; + struct protect_data_t protect_data; + HCRYPTHASH hHash; + HCRYPTKEY hKey; + DWORD dwLength; + + TRACE("called\n"); + + SetLastError(ERROR_SUCCESS); + + if (!pDataIn || !pDataOut) + { + SetLastError(ERROR_INVALID_PARAMETER); + goto finished; + } + + /* debug: show our arguments */ + report(pDataIn,pOptionalEntropy,pPromptStruct,dwFlags); + TRACE("\tszDataDescr: %p %s\n", szDataDescr, + szDataDescr ? debugstr_w(szDataDescr) : ""); + + /* Windows appears to create an empty szDataDescr instead of maintaining + * a NULL */ + if (!szDataDescr) + szDataDescr=(WCHAR[]){'\0'}; + + /* get crypt context */ + if (!CryptAcquireContextW(&hProv,NULL,NULL,CRYPT32_PROTECTDATA_PROV,CRYPT_VERIFYCONTEXT)) + { + ERR("CryptAcquireContextW failed\n"); + goto finished; + } + + /* populate our structure */ + if (!fill_protect_data(&protect_data,szDataDescr,hProv)) + { + ERR("fill_protect_data\n"); + goto free_context; + } + + /* load key */ + if (!load_encryption_key(hProv,&protect_data.salt,pOptionalEntropy,&hKey)) + { + goto free_protect_data; + } + + /* create a hash for the encryption validation */ + if (!CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hHash)) + { + ERR("CryptCreateHash\n"); + goto free_key; + } + + /* calculate storage required */ + dwLength=pDataIn->cbData; + if (CryptEncrypt(hKey, 0, TRUE, 0, pDataIn->pbData, &dwLength, 0) || + GetLastError()!=ERROR_MORE_DATA) + { + ERR("CryptEncrypt\n"); + goto free_hash; + } + TRACE("required encrypted storage: %u\n",(unsigned int)dwLength); + + /* copy plain text into cipher area for CryptEncrypt call */ + protect_data.cipher.cbData=dwLength; + if (!(protect_data.cipher.pbData=CryptMemAlloc( + protect_data.cipher.cbData))) + { + ERR("CryptMemAlloc\n"); + goto free_hash; + } + memcpy(protect_data.cipher.pbData,pDataIn->pbData,pDataIn->cbData); + + /* encrypt! */ + dwLength=pDataIn->cbData; + if (!CryptEncrypt(hKey, hHash, TRUE, 0, protect_data.cipher.pbData, + &dwLength, protect_data.cipher.cbData)) + { + ERR("CryptEncrypt %u\n",(unsigned int)GetLastError()); + goto free_hash; + } + protect_data.cipher.cbData=dwLength; + + /* debug: show the cipher */ + TRACE_DATA_BLOB(&protect_data.cipher); + + /* attach our fingerprint */ + if (!convert_hash_to_blob(hHash, &protect_data.fingerprint)) + { + ERR("convert_hash_to_blob\n"); + goto free_hash; + } + + /* serialize into an opaque blob */ + if (!serialize(&protect_data, pDataOut)) + { + ERR("serialize\n"); + goto free_hash; + } + + /* success! */ + rc=TRUE; + +free_hash: + CryptDestroyHash(hHash); +free_key: + CryptDestroyKey(hKey); +free_protect_data: + free_protect_data(&protect_data); +free_context: + CryptReleaseContext(hProv,0); +finished: + /* If some error occurred, and no error code was set, force one. */ + if (!rc && GetLastError()==ERROR_SUCCESS) + { + SetLastError(ERROR_INVALID_DATA); + } + + if (rc) + { + SetLastError(ERROR_SUCCESS); + + TRACE_DATA_BLOB(pDataOut); + } + + TRACE("returning %s\n", rc ? "ok" : "FAIL"); + + return rc; +} + + +/*************************************************************************** + * CryptUnprotectData [CRYPT32.@] + * + * Generate Plain data and Description from given Cipher and Entropy data. + * + * PARAMS + * pDataIn [I] Cipher data to be decoded + * ppszDataDescr [O] Optional Unicode string describing the Plain data + * pOptionalEntropy [I] Optional entropy data to adjust cipher, can be NULL + * pvReserved [I] Reserved, must be NULL + * pPromptStruct [I] Structure describing if/how to prompt during decoding + * dwFlags [I] Flags describing options to the decoding + * pDataOut [O] Resulting Plain data, from calls to CryptProtectData + * + * RETURNS + * TRUE If a Plain was generated. + * FALSE If something failed and no Plain is available. + * + * FIXME + * The true Windows encryption and keying mechanisms are unknown. + * + * dwFlags and pPromptStruct are currently ignored. + * + * NOTES + * Memory allocated in pDataOut and non-NULL ppszDataDescr must be freed + * with LocalFree. + * + */ +BOOL WINAPI CryptUnprotectData(DATA_BLOB* pDataIn, + LPWSTR * ppszDataDescr, + DATA_BLOB* pOptionalEntropy, + PVOID pvReserved, + CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, + DWORD dwFlags, + DATA_BLOB* pDataOut) +{ + BOOL rc = FALSE; + + HCRYPTPROV hProv; + struct protect_data_t protect_data; + HCRYPTHASH hHash; + HCRYPTKEY hKey; + DWORD dwLength; + + const char * announce_bad_opaque_data = "CryptUnprotectData received a DATA_BLOB that seems to have NOT been generated by Wine. Please enable tracing ('export WINEDEBUG=crypt') to see details."; + + TRACE("called\n"); + + SetLastError(ERROR_SUCCESS); + + if (!pDataIn || !pDataOut) + { + SetLastError(ERROR_INVALID_PARAMETER); + goto finished; + } + + /* debug: show our arguments */ + report(pDataIn,pOptionalEntropy,pPromptStruct,dwFlags); + TRACE("\tppszDataDescr: %p\n", ppszDataDescr); + + /* take apart the opaque blob */ + if (!unserialize(pDataIn, &protect_data)) + { + SetLastError(ERROR_INVALID_DATA); + FIXME("%s\n",announce_bad_opaque_data); + goto finished; + } + + /* perform basic validation on the resulting structure */ + if (!valid_protect_data(&protect_data)) + { + SetLastError(ERROR_INVALID_DATA); + FIXME("%s\n",announce_bad_opaque_data); + goto free_protect_data; + } + + /* get a crypt context */ + if (!CryptAcquireContextW(&hProv,NULL,NULL,CRYPT32_PROTECTDATA_PROV,CRYPT_VERIFYCONTEXT)) + { + ERR("CryptAcquireContextW failed\n"); + goto free_protect_data; + } + + /* load key */ + if (!load_encryption_key(hProv,&protect_data.salt,pOptionalEntropy,&hKey)) + { + goto free_context; + } + + /* create a hash for the decryption validation */ + if (!CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hHash)) + { + ERR("CryptCreateHash\n"); + goto free_key; + } + + /* prepare for plaintext */ + pDataOut->cbData=protect_data.cipher.cbData; + if (!(pDataOut->pbData=LocalAlloc( LPTR, pDataOut->cbData))) + { + ERR("CryptMemAlloc\n"); + goto free_hash; + } + memcpy(pDataOut->pbData,protect_data.cipher.pbData,protect_data.cipher.cbData); + + /* decrypt! */ + if (!CryptDecrypt(hKey, hHash, TRUE, 0, pDataOut->pbData, + &pDataOut->cbData) || + /* check the hash fingerprint */ + pDataOut->cbData > protect_data.cipher.cbData || + !hash_matches_blob(hHash, &protect_data.fingerprint)) + { + SetLastError(ERROR_INVALID_DATA); + + LocalFree( pDataOut->pbData ); + pDataOut->pbData = NULL; + pDataOut->cbData = 0; + + goto free_hash; + } + + /* Copy out the description */ + dwLength = (lstrlenW(protect_data.szDataDescr)+1) * sizeof(WCHAR); + if (ppszDataDescr) + { + if (!(*ppszDataDescr = LocalAlloc(LPTR,dwLength))) + { + ERR("LocalAlloc (ppszDataDescr)\n"); + goto free_hash; + } + else { + memcpy(*ppszDataDescr,protect_data.szDataDescr,dwLength); + } + } + + /* success! */ + rc = TRUE; + +free_hash: + CryptDestroyHash(hHash); +free_key: + CryptDestroyKey(hKey); +free_context: + CryptReleaseContext(hProv,0); +free_protect_data: + free_protect_data(&protect_data); +finished: + /* If some error occurred, and no error code was set, force one. */ + if (!rc && GetLastError()==ERROR_SUCCESS) + { + SetLastError(ERROR_INVALID_DATA); + } + + if (rc) { + SetLastError(ERROR_SUCCESS); + + if (ppszDataDescr) + { + TRACE("szDataDescr: %s\n",debugstr_w(*ppszDataDescr)); + } + TRACE_DATA_BLOB(pDataOut); + } + + TRACE("returning %s\n", rc ? "ok" : "FAIL"); + + return rc; +}