|  | /* | 
|  | * Copyright 2004-2007 Juan Lang | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "wine/port.h" | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "wincrypt.h" | 
|  | #include "wine/debug.h" | 
|  | #include "wine/exception.h" | 
|  | #include "crypt32_private.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(crypt); | 
|  |  | 
|  | /* 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; | 
|  |  | 
|  | static BOOL CRYPT_SerializeStoreElement(const void *context, | 
|  | const BYTE *encodedContext, DWORD cbEncodedContext, DWORD contextPropID, | 
|  | PCWINE_CONTEXT_INTERFACE contextInterface, DWORD dwFlags, BOOL omitHashes, | 
|  | BYTE *pbElement, DWORD *pcbElement) | 
|  | { | 
|  | BOOL ret; | 
|  |  | 
|  | TRACE("(%p, %p, %08x, %d, %p, %p)\n", context, contextInterface, dwFlags, | 
|  | omitHashes, pbElement, pcbElement); | 
|  |  | 
|  | if (context) | 
|  | { | 
|  | DWORD bytesNeeded = sizeof(WINE_CERT_PROP_HEADER) + cbEncodedContext; | 
|  | DWORD prop = 0; | 
|  |  | 
|  | ret = TRUE; | 
|  | do { | 
|  | prop = contextInterface->enumProps(context, prop); | 
|  | if (prop && (!omitHashes || !IS_CERT_HASH_PROP_ID(prop))) | 
|  | { | 
|  | DWORD propSize = 0; | 
|  |  | 
|  | ret = contextInterface->getProp(context, prop, NULL, &propSize); | 
|  | if (ret) | 
|  | bytesNeeded += sizeof(WINE_CERT_PROP_HEADER) + propSize; | 
|  | } | 
|  | } while (ret && prop != 0); | 
|  |  | 
|  | if (!pbElement) | 
|  | { | 
|  | *pcbElement = bytesNeeded; | 
|  | ret = TRUE; | 
|  | } | 
|  | else if (*pcbElement < bytesNeeded) | 
|  | { | 
|  | *pcbElement = bytesNeeded; | 
|  | SetLastError(ERROR_MORE_DATA); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | PWINE_CERT_PROP_HEADER hdr; | 
|  | DWORD bufSize = 0; | 
|  | LPBYTE buf = NULL; | 
|  |  | 
|  | prop = 0; | 
|  | do { | 
|  | prop = contextInterface->enumProps(context, prop); | 
|  | if (prop && (!omitHashes || !IS_CERT_HASH_PROP_ID(prop))) | 
|  | { | 
|  | DWORD propSize = 0; | 
|  |  | 
|  | ret = contextInterface->getProp(context, prop, NULL, | 
|  | &propSize); | 
|  | if (ret) | 
|  | { | 
|  | if (bufSize < propSize) | 
|  | { | 
|  | if (buf) | 
|  | buf = CryptMemRealloc(buf, propSize); | 
|  | else | 
|  | buf = CryptMemAlloc(propSize); | 
|  | bufSize = propSize; | 
|  | } | 
|  | if (buf) | 
|  | { | 
|  | ret = contextInterface->getProp(context, prop, buf, | 
|  | &propSize); | 
|  | if (ret) | 
|  | { | 
|  | hdr = (PWINE_CERT_PROP_HEADER)pbElement; | 
|  | hdr->propID = prop; | 
|  | hdr->unknown = 1; | 
|  | hdr->cb = propSize; | 
|  | pbElement += sizeof(WINE_CERT_PROP_HEADER); | 
|  | if (propSize) | 
|  | { | 
|  | memcpy(pbElement, buf, propSize); | 
|  | pbElement += propSize; | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | } while (ret && prop != 0); | 
|  | CryptMemFree(buf); | 
|  |  | 
|  | hdr = (PWINE_CERT_PROP_HEADER)pbElement; | 
|  | hdr->propID = contextPropID; | 
|  | hdr->unknown = 1; | 
|  | hdr->cb = cbEncodedContext; | 
|  | memcpy(pbElement + sizeof(WINE_CERT_PROP_HEADER), | 
|  | encodedContext, cbEncodedContext); | 
|  | } | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | BOOL WINAPI CertSerializeCertificateStoreElement(PCCERT_CONTEXT pCertContext, | 
|  | DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement) | 
|  | { | 
|  | return CRYPT_SerializeStoreElement(pCertContext, | 
|  | pCertContext->pbCertEncoded, pCertContext->cbCertEncoded, | 
|  | CERT_CERT_PROP_ID, pCertInterface, dwFlags, FALSE, pbElement, pcbElement); | 
|  | } | 
|  |  | 
|  | BOOL WINAPI CertSerializeCRLStoreElement(PCCRL_CONTEXT pCrlContext, | 
|  | DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement) | 
|  | { | 
|  | return CRYPT_SerializeStoreElement(pCrlContext, | 
|  | pCrlContext->pbCrlEncoded, pCrlContext->cbCrlEncoded, | 
|  | CERT_CRL_PROP_ID, pCRLInterface, dwFlags, FALSE, pbElement, pcbElement); | 
|  | } | 
|  |  | 
|  | BOOL WINAPI CertSerializeCTLStoreElement(PCCTL_CONTEXT pCtlContext, | 
|  | DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement) | 
|  | { | 
|  | return CRYPT_SerializeStoreElement(pCtlContext, | 
|  | pCtlContext->pbCtlEncoded, pCtlContext->cbCtlEncoded, | 
|  | CERT_CTL_PROP_ID, pCTLInterface, dwFlags, FALSE, pbElement, pcbElement); | 
|  | } | 
|  |  | 
|  | /* 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 the 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(E_INVALIDARG); | 
|  | 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 BOOL CRYPT_ReadContextProp( | 
|  | const WINE_CONTEXT_INTERFACE *contextInterface, const void *context, | 
|  | const WINE_CERT_PROP_HEADER *hdr, const BYTE *pbElement, DWORD cbElement) | 
|  | { | 
|  | BOOL ret; | 
|  |  | 
|  | if (cbElement < hdr->cb) | 
|  | { | 
|  | SetLastError(E_INVALIDARG); | 
|  | ret = FALSE; | 
|  | } | 
|  | 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; | 
|  | case CERT_KEY_PROV_INFO_PROP_ID: | 
|  | { | 
|  | PCRYPT_KEY_PROV_INFO info = | 
|  | (PCRYPT_KEY_PROV_INFO)pbElement; | 
|  |  | 
|  | CRYPT_FixKeyProvInfoPointers(info); | 
|  | ret = contextInterface->setProp(context, | 
|  | hdr->propID, 0, pbElement); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | /* ignore the context itself */ | 
|  | ret = TRUE; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | const void *CRYPT_ReadSerializedElement(const BYTE *pbElement, DWORD cbElement, | 
|  | DWORD dwContextTypeFlags, DWORD *pdwContentType) | 
|  | { | 
|  | const void *context; | 
|  |  | 
|  | TRACE("(%p, %d, %08x, %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 = pCertInterface; | 
|  | break; | 
|  | case CERT_STORE_CRL_CONTEXT: | 
|  | contextInterface = pCRLInterface; | 
|  | break; | 
|  | case CERT_STORE_CTL_CONTEXT: | 
|  | contextInterface = pCTLInterface; | 
|  | break; | 
|  | default: | 
|  | SetLastError(E_INVALIDARG); | 
|  | 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 %d\n", hdr->propID); | 
|  | cbElement -= sizeof(WINE_CERT_PROP_HEADER); | 
|  | pbElement += sizeof(WINE_CERT_PROP_HEADER); | 
|  | if (!hdr->propID) | 
|  | { | 
|  | /* Like in CRYPT_findPropID, stop if the propID is zero | 
|  | */ | 
|  | noMoreProps = TRUE; | 
|  | } | 
|  | else | 
|  | ret = CRYPT_ReadContextProp(contextInterface, context, | 
|  | hdr, pbElement, cbElement); | 
|  | pbElement += hdr->cb; | 
|  | cbElement -= hdr->cb; | 
|  | if (!cbElement) | 
|  | noMoreProps = TRUE; | 
|  | } | 
|  | } | 
|  | if (ret) | 
|  | { | 
|  | if (pdwContentType) | 
|  | *pdwContentType = type; | 
|  | } | 
|  | else | 
|  | { | 
|  | contextInterface->free(context); | 
|  | context = NULL; | 
|  | } | 
|  | } | 
|  | } | 
|  | __EXCEPT_PAGE_FAULT | 
|  | { | 
|  | SetLastError(STATUS_ACCESS_VIOLATION); | 
|  | context = NULL; | 
|  | } | 
|  | __ENDTRY | 
|  | return context; | 
|  | } | 
|  |  | 
|  | static const BYTE fileHeader[] = { 0, 0, 0, 0, 'C','E','R','T' }; | 
|  |  | 
|  | typedef BOOL (*read_serialized_func)(void *handle, void *buffer, | 
|  | DWORD bytesToRead, DWORD *bytesRead); | 
|  |  | 
|  | static BOOL CRYPT_ReadSerializedStore(void *handle, | 
|  | read_serialized_func read_func, HCERTSTORE store) | 
|  | { | 
|  | BYTE fileHeaderBuf[sizeof(fileHeader)]; | 
|  | DWORD read; | 
|  | BOOL ret; | 
|  |  | 
|  | /* Failure reading is non-critical, we'll leave the store empty */ | 
|  | ret = read_func(handle, fileHeaderBuf, sizeof(fileHeaderBuf), &read); | 
|  | if (ret) | 
|  | { | 
|  | if (!read) | 
|  | ; /* an empty file is okay */ | 
|  | else if (read != sizeof(fileHeaderBuf)) | 
|  | ret = FALSE; | 
|  | else if (!memcmp(fileHeaderBuf, fileHeader, read)) | 
|  | { | 
|  | WINE_CERT_PROP_HEADER propHdr; | 
|  | const void *context = NULL; | 
|  | const WINE_CONTEXT_INTERFACE *contextInterface = NULL; | 
|  | LPBYTE buf = NULL; | 
|  | DWORD bufSize = 0; | 
|  |  | 
|  | do { | 
|  | ret = read_func(handle, &propHdr, sizeof(propHdr), &read); | 
|  | if (ret && read == sizeof(propHdr)) | 
|  | { | 
|  | if (contextInterface && context && | 
|  | (propHdr.propID == CERT_CERT_PROP_ID || | 
|  | propHdr.propID == CERT_CRL_PROP_ID || | 
|  | propHdr.propID == CERT_CTL_PROP_ID)) | 
|  | { | 
|  | /* We have a new context, so free the existing one */ | 
|  | contextInterface->free(context); | 
|  | } | 
|  | if (propHdr.cb > bufSize) | 
|  | { | 
|  | /* Not reusing realloc, because the old data aren't | 
|  | * needed any longer. | 
|  | */ | 
|  | CryptMemFree(buf); | 
|  | buf = CryptMemAlloc(propHdr.cb); | 
|  | bufSize = propHdr.cb; | 
|  | } | 
|  | if (!propHdr.cb) | 
|  | ; /* Property is empty, nothing to do */ | 
|  | else if (buf) | 
|  | { | 
|  | ret = read_func(handle, buf, propHdr.cb, &read); | 
|  | if (ret && read == propHdr.cb) | 
|  | { | 
|  | if (propHdr.propID == CERT_CERT_PROP_ID) | 
|  | { | 
|  | contextInterface = pCertInterface; | 
|  | ret = contextInterface->addEncodedToStore(store, | 
|  | X509_ASN_ENCODING, buf, read, | 
|  | CERT_STORE_ADD_NEW, &context); | 
|  | } | 
|  | else if (propHdr.propID == CERT_CRL_PROP_ID) | 
|  | { | 
|  | contextInterface = pCRLInterface; | 
|  | ret = contextInterface->addEncodedToStore(store, | 
|  | X509_ASN_ENCODING, buf, read, | 
|  | CERT_STORE_ADD_NEW, &context); | 
|  | } | 
|  | else if (propHdr.propID == CERT_CTL_PROP_ID) | 
|  | { | 
|  | contextInterface = pCTLInterface; | 
|  | ret = contextInterface->addEncodedToStore(store, | 
|  | X509_ASN_ENCODING, buf, read, | 
|  | CERT_STORE_ADD_NEW, &context); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (!contextInterface) | 
|  | { | 
|  | WARN("prop id %d before a context id\n", | 
|  | propHdr.propID); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | ret = CRYPT_ReadContextProp( | 
|  | contextInterface, context, &propHdr, buf, | 
|  | read); | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | } | 
|  | } while (ret && read > 0 && propHdr.cb); | 
|  | if (contextInterface && context) | 
|  | { | 
|  | /* Free the last context added */ | 
|  | contextInterface->free(context); | 
|  | } | 
|  | CryptMemFree(buf); | 
|  | ret = TRUE; | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | ret = TRUE; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL read_file_wrapper(void *handle, void *buffer, DWORD bytesToRead, | 
|  | DWORD *bytesRead) | 
|  | { | 
|  | return ReadFile(handle, buffer, bytesToRead, bytesRead, NULL); | 
|  | } | 
|  |  | 
|  | BOOL CRYPT_ReadSerializedStoreFromFile(HANDLE file, HCERTSTORE store) | 
|  | { | 
|  | return CRYPT_ReadSerializedStore(file, read_file_wrapper, store); | 
|  | } | 
|  |  | 
|  | struct BlobReader | 
|  | { | 
|  | const CRYPT_DATA_BLOB *blob; | 
|  | DWORD current; | 
|  | }; | 
|  |  | 
|  | static BOOL read_blob_wrapper(void *handle, void *buffer, DWORD bytesToRead, | 
|  | DWORD *bytesRead) | 
|  | { | 
|  | struct BlobReader *reader = handle; | 
|  | BOOL ret; | 
|  |  | 
|  | if (reader->current < reader->blob->cbData) | 
|  | { | 
|  | *bytesRead = min(bytesToRead, reader->blob->cbData - reader->current); | 
|  | memcpy(buffer, reader->blob->pbData + reader->current, *bytesRead); | 
|  | reader->current += *bytesRead; | 
|  | ret = TRUE; | 
|  | } | 
|  | else if (reader->current == reader->blob->cbData) | 
|  | { | 
|  | *bytesRead = 0; | 
|  | ret = TRUE; | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | BOOL CRYPT_ReadSerializedStoreFromBlob(const CRYPT_DATA_BLOB *blob, | 
|  | HCERTSTORE store) | 
|  | { | 
|  | struct BlobReader reader = { blob, 0 }; | 
|  |  | 
|  | return CRYPT_ReadSerializedStore(&reader, read_blob_wrapper, store); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_SerializeCertNoHash(PCCERT_CONTEXT pCertContext, | 
|  | DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement) | 
|  | { | 
|  | return CRYPT_SerializeStoreElement(pCertContext, | 
|  | pCertContext->pbCertEncoded, pCertContext->cbCertEncoded, | 
|  | CERT_CERT_PROP_ID, pCertInterface, dwFlags, TRUE, pbElement, pcbElement); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_SerializeCRLNoHash(PCCRL_CONTEXT pCrlContext, | 
|  | DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement) | 
|  | { | 
|  | return CRYPT_SerializeStoreElement(pCrlContext, | 
|  | pCrlContext->pbCrlEncoded, pCrlContext->cbCrlEncoded, | 
|  | CERT_CRL_PROP_ID, pCRLInterface, dwFlags, TRUE, pbElement, pcbElement); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_SerializeCTLNoHash(PCCTL_CONTEXT pCtlContext, | 
|  | DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement) | 
|  | { | 
|  | return CRYPT_SerializeStoreElement(pCtlContext, | 
|  | pCtlContext->pbCtlEncoded, pCtlContext->cbCtlEncoded, | 
|  | CERT_CTL_PROP_ID, pCTLInterface, dwFlags, TRUE, pbElement, pcbElement); | 
|  | } | 
|  |  | 
|  | typedef BOOL (*SerializedOutputFunc)(void *handle, const void *buffer, | 
|  | DWORD size); | 
|  |  | 
|  | static BOOL CRYPT_SerializeContextsToStream(SerializedOutputFunc output, | 
|  | void *handle, const WINE_CONTEXT_INTERFACE *contextInterface, HCERTSTORE store) | 
|  | { | 
|  | const void *context = NULL; | 
|  | BOOL ret; | 
|  |  | 
|  | do { | 
|  | context = contextInterface->enumContextsInStore(store, context); | 
|  | if (context) | 
|  | { | 
|  | 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 = output(handle, buf, size); | 
|  | } | 
|  | CryptMemFree(buf); | 
|  | } | 
|  | else | 
|  | ret = TRUE; | 
|  | } while (ret && context != NULL); | 
|  | if (context) | 
|  | contextInterface->free(context); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_WriteSerializedStoreToStream(HCERTSTORE store, | 
|  | SerializedOutputFunc output, void *handle) | 
|  | { | 
|  | static const BYTE fileTrailer[12] = { 0 }; | 
|  | WINE_CONTEXT_INTERFACE interface; | 
|  | BOOL ret; | 
|  |  | 
|  | ret = output(handle, fileHeader, sizeof(fileHeader)); | 
|  | if (ret) | 
|  | { | 
|  | memcpy(&interface, pCertInterface, sizeof(interface)); | 
|  | interface.serialize = (SerializeElementFunc)CRYPT_SerializeCertNoHash; | 
|  | ret = CRYPT_SerializeContextsToStream(output, handle, &interface, | 
|  | store); | 
|  | } | 
|  | if (ret) | 
|  | { | 
|  | memcpy(&interface, pCRLInterface, sizeof(interface)); | 
|  | interface.serialize = (SerializeElementFunc)CRYPT_SerializeCRLNoHash; | 
|  | ret = CRYPT_SerializeContextsToStream(output, handle, &interface, | 
|  | store); | 
|  | } | 
|  | if (ret) | 
|  | { | 
|  | memcpy(&interface, pCTLInterface, sizeof(interface)); | 
|  | interface.serialize = (SerializeElementFunc)CRYPT_SerializeCTLNoHash; | 
|  | ret = CRYPT_SerializeContextsToStream(output, handle, &interface, | 
|  | store); | 
|  | } | 
|  | if (ret) | 
|  | ret = output(handle, fileTrailer, sizeof(fileTrailer)); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_FileOutputFunc(void *handle, const void *buffer, DWORD size) | 
|  | { | 
|  | return WriteFile(handle, buffer, size, &size, NULL); | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_WriteSerializedStoreToFile(HANDLE file, HCERTSTORE store) | 
|  | { | 
|  | SetFilePointer(file, 0, NULL, FILE_BEGIN); | 
|  | return CRYPT_WriteSerializedStoreToStream(store, CRYPT_FileOutputFunc, | 
|  | file); | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_SavePKCSToMem(HCERTSTORE store, | 
|  | DWORD dwMsgAndCertEncodingType, void *handle) | 
|  | { | 
|  | CERT_BLOB *blob = handle; | 
|  | CRYPT_SIGNED_INFO signedInfo = { 0 }; | 
|  | PCCERT_CONTEXT cert = NULL; | 
|  | PCCRL_CONTEXT crl = NULL; | 
|  | DWORD size; | 
|  | BOOL ret = TRUE; | 
|  |  | 
|  | TRACE("(%d, %p)\n", blob->pbData ? blob->cbData : 0, blob->pbData); | 
|  |  | 
|  | do { | 
|  | cert = CertEnumCertificatesInStore(store, cert); | 
|  | if (cert) | 
|  | signedInfo.cCertEncoded++; | 
|  | } while (cert); | 
|  | if (signedInfo.cCertEncoded) | 
|  | { | 
|  | signedInfo.rgCertEncoded = CryptMemAlloc( | 
|  | signedInfo.cCertEncoded * sizeof(CERT_BLOB)); | 
|  | if (!signedInfo.rgCertEncoded) | 
|  | { | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | DWORD i = 0; | 
|  |  | 
|  | do { | 
|  | cert = CertEnumCertificatesInStore(store, cert); | 
|  | if (cert) | 
|  | { | 
|  | signedInfo.rgCertEncoded[i].cbData = cert->cbCertEncoded; | 
|  | signedInfo.rgCertEncoded[i].pbData = cert->pbCertEncoded; | 
|  | i++; | 
|  | } | 
|  | } while (cert); | 
|  | } | 
|  | } | 
|  |  | 
|  | do { | 
|  | crl = CertEnumCRLsInStore(store, crl); | 
|  | if (crl) | 
|  | signedInfo.cCrlEncoded++; | 
|  | } while (crl); | 
|  | if (signedInfo.cCrlEncoded) | 
|  | { | 
|  | signedInfo.rgCrlEncoded = CryptMemAlloc( | 
|  | signedInfo.cCrlEncoded * sizeof(CERT_BLOB)); | 
|  | if (!signedInfo.rgCrlEncoded) | 
|  | { | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | DWORD i = 0; | 
|  |  | 
|  | do { | 
|  | crl = CertEnumCRLsInStore(store, crl); | 
|  | if (crl) | 
|  | { | 
|  | signedInfo.rgCrlEncoded[i].cbData = crl->cbCrlEncoded; | 
|  | signedInfo.rgCrlEncoded[i].pbData = crl->pbCrlEncoded; | 
|  | i++; | 
|  | } | 
|  | } while (crl); | 
|  | } | 
|  | } | 
|  | if (ret) | 
|  | { | 
|  | ret = CRYPT_AsnEncodeCMSSignedInfo(&signedInfo, NULL, &size); | 
|  | if (ret) | 
|  | { | 
|  | if (!blob->pbData) | 
|  | blob->cbData = size; | 
|  | else if (blob->cbData < size) | 
|  | { | 
|  | blob->cbData = size; | 
|  | SetLastError(ERROR_MORE_DATA); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | blob->cbData = size; | 
|  | ret = CRYPT_AsnEncodeCMSSignedInfo(&signedInfo, blob->pbData, | 
|  | &blob->cbData); | 
|  | } | 
|  | } | 
|  | } | 
|  | CryptMemFree(signedInfo.rgCertEncoded); | 
|  | CryptMemFree(signedInfo.rgCrlEncoded); | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_SavePKCSToFile(HCERTSTORE store, | 
|  | DWORD dwMsgAndCertEncodingType, void *handle) | 
|  | { | 
|  | CERT_BLOB blob = { 0, NULL }; | 
|  | BOOL ret; | 
|  |  | 
|  | TRACE("(%p)\n", handle); | 
|  |  | 
|  | ret = CRYPT_SavePKCSToMem(store, dwMsgAndCertEncodingType, &blob); | 
|  | if (ret) | 
|  | { | 
|  | blob.pbData = CryptMemAlloc(blob.cbData); | 
|  | if (blob.pbData) | 
|  | { | 
|  | ret = CRYPT_SavePKCSToMem(store, dwMsgAndCertEncodingType, &blob); | 
|  | if (ret) | 
|  | ret = WriteFile(handle, blob.pbData, blob.cbData, | 
|  | &blob.cbData, NULL); | 
|  | } | 
|  | else | 
|  | { | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_SaveSerializedToFile(HCERTSTORE store, | 
|  | DWORD dwMsgAndCertEncodingType, void *handle) | 
|  | { | 
|  | return CRYPT_WriteSerializedStoreToFile(handle, store); | 
|  | } | 
|  |  | 
|  | struct MemWrittenTracker | 
|  | { | 
|  | DWORD cbData; | 
|  | BYTE *pbData; | 
|  | DWORD written; | 
|  | }; | 
|  |  | 
|  | /* handle is a pointer to a MemWrittenTracker.  Assumes its pointer is valid. */ | 
|  | static BOOL CRYPT_MemOutputFunc(void *handle, const void *buffer, DWORD size) | 
|  | { | 
|  | struct MemWrittenTracker *tracker = handle; | 
|  | BOOL ret; | 
|  |  | 
|  | if (tracker->written + size > tracker->cbData) | 
|  | { | 
|  | SetLastError(ERROR_MORE_DATA); | 
|  | /* Update written so caller can notify its caller of the required size | 
|  | */ | 
|  | tracker->written += size; | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | memcpy(tracker->pbData + tracker->written, buffer, size); | 
|  | tracker->written += size; | 
|  | ret = TRUE; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_CountSerializedBytes(void *handle, const void *buffer, | 
|  | DWORD size) | 
|  | { | 
|  | *(DWORD *)handle += size; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_SaveSerializedToMem(HCERTSTORE store, | 
|  | DWORD dwMsgAndCertEncodingType, void *handle) | 
|  | { | 
|  | CERT_BLOB *blob = handle; | 
|  | DWORD size = 0; | 
|  | BOOL ret; | 
|  |  | 
|  | ret = CRYPT_WriteSerializedStoreToStream(store, CRYPT_CountSerializedBytes, | 
|  | &size); | 
|  | if (ret) | 
|  | { | 
|  | if (!blob->pbData) | 
|  | blob->cbData = size; | 
|  | else if (blob->cbData < size) | 
|  | { | 
|  | SetLastError(ERROR_MORE_DATA); | 
|  | blob->cbData = size; | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | struct MemWrittenTracker tracker = { blob->cbData, blob->pbData, | 
|  | 0 }; | 
|  |  | 
|  | ret = CRYPT_WriteSerializedStoreToStream(store, CRYPT_MemOutputFunc, | 
|  | &tracker); | 
|  | if (!ret && GetLastError() == ERROR_MORE_DATA) | 
|  | blob->cbData = tracker.written; | 
|  | } | 
|  | } | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | BOOL WINAPI CertSaveStore(HCERTSTORE hCertStore, DWORD dwMsgAndCertEncodingType, | 
|  | DWORD dwSaveAs, DWORD dwSaveTo, void *pvSaveToPara, DWORD dwFlags) | 
|  | { | 
|  | BOOL (*saveFunc)(HCERTSTORE, DWORD, void *); | 
|  | void *handle; | 
|  | BOOL ret, closeFile = TRUE; | 
|  |  | 
|  | TRACE("(%p, %08x, %d, %d, %p, %08x)\n", hCertStore, | 
|  | dwMsgAndCertEncodingType, dwSaveAs, dwSaveTo, pvSaveToPara, dwFlags); | 
|  |  | 
|  | switch (dwSaveAs) | 
|  | { | 
|  | case CERT_STORE_SAVE_AS_STORE: | 
|  | if (dwSaveTo == CERT_STORE_SAVE_TO_MEMORY) | 
|  | saveFunc = CRYPT_SaveSerializedToMem; | 
|  | else | 
|  | saveFunc = CRYPT_SaveSerializedToFile; | 
|  | break; | 
|  | case CERT_STORE_SAVE_AS_PKCS7: | 
|  | if (dwSaveTo == CERT_STORE_SAVE_TO_MEMORY) | 
|  | saveFunc = CRYPT_SavePKCSToMem; | 
|  | else | 
|  | saveFunc = CRYPT_SavePKCSToFile; | 
|  | break; | 
|  | default: | 
|  | WARN("unimplemented for %d\n", dwSaveAs); | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | switch (dwSaveTo) | 
|  | { | 
|  | case CERT_STORE_SAVE_TO_FILE: | 
|  | handle = pvSaveToPara; | 
|  | closeFile = FALSE; | 
|  | break; | 
|  | case CERT_STORE_SAVE_TO_FILENAME_A: | 
|  | handle = CreateFileA(pvSaveToPara, GENERIC_WRITE, 0, NULL, | 
|  | CREATE_ALWAYS, 0, NULL); | 
|  | break; | 
|  | case CERT_STORE_SAVE_TO_FILENAME_W: | 
|  | handle = CreateFileW(pvSaveToPara, GENERIC_WRITE, 0, NULL, | 
|  | CREATE_ALWAYS, 0, NULL); | 
|  | break; | 
|  | case CERT_STORE_SAVE_TO_MEMORY: | 
|  | handle = pvSaveToPara; | 
|  | break; | 
|  | default: | 
|  | WARN("unimplemented for %d\n", dwSaveTo); | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | ret = saveFunc(hCertStore, dwMsgAndCertEncodingType, handle); | 
|  | if (closeFile) | 
|  | CloseHandle(handle); | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | 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, %d, %08x, %08x, %08x, %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 = pCertInterface; | 
|  | break; | 
|  | case CERT_STORE_CRL_CONTEXT: | 
|  | contextInterface = pCRLInterface; | 
|  | break; | 
|  | case CERT_STORE_CTL_CONTEXT: | 
|  | contextInterface = pCTLInterface; | 
|  | break; | 
|  | default: | 
|  | SetLastError(E_INVALIDARG); | 
|  | } | 
|  | 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; | 
|  | } |