wine/dlls/crypt32/serialize.c

1008 lines
31 KiB
C

/*
* 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 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(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;
}