| /* | |
| * 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; | |
| static BOOL CRYPT_SerializeStoreElement(const void *context, | |
| const BYTE *encodedContext, DWORD cbEncodedContext, DWORD contextPropID, | |
| const WINE_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 | |
| { | |
| WINE_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 = (WINE_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 = (WINE_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 | |
| { | |
| Context_Release(context_from_ptr(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 */ | |
| Context_Release(context_from_ptr(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 */ | |
| Context_Release(context_from_ptr(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) | |
| Context_Release(context_from_ptr(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) | |
| { | |
| interface = *pCertInterface; | |
| interface.serialize = (SerializeElementFunc)CRYPT_SerializeCertNoHash; | |
| ret = CRYPT_SerializeContextsToStream(output, handle, &interface, | |
| store); | |
| } | |
| if (ret) | |
| { | |
| interface = *pCRLInterface; | |
| interface.serialize = (SerializeElementFunc)CRYPT_SerializeCRLNoHash; | |
| ret = CRYPT_SerializeContextsToStream(output, handle, &interface, | |
| store); | |
| } | |
| if (ret) | |
| { | |
| interface = *pCTLInterface; | |
| 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); | |
| Context_Release(context_from_ptr(context)); | |
| } | |
| else | |
| ret = FALSE; | |
| } | |
| else | |
| ret = FALSE; | |
| return ret; | |
| } |