/* * Copyright 2004-2006 Juan Lang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include "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' }; BOOL CRYPT_ReadSerializedStoreFromFile(HANDLE file, HCERTSTORE store) { BYTE fileHeaderBuf[sizeof(fileHeader)]; DWORD read; BOOL ret; /* Failure reading is non-critical, we'll leave the store empty */ ret = ReadFile(file, fileHeaderBuf, sizeof(fileHeaderBuf), &read, NULL); if (ret) { 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 = ReadFile(file, &propHdr, sizeof(propHdr), &read, NULL); 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 (buf) { ret = ReadFile(file, buf, propHdr.cb, &read, NULL); 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 ret = CRYPT_ReadContextProp(contextInterface, context, &propHdr, buf, read); } } else ret = FALSE; } } while (ret && read > 0); if (contextInterface && context) { /* Free the last context added */ contextInterface->free(context); } CryptMemFree(buf); ret = TRUE; } } else ret = TRUE; return ret; } 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); } 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; } 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); } BOOL CRYPT_WriteSerializedStoreToFile(HANDLE file, HCERTSTORE store) { SetFilePointer(file, 0, NULL, FILE_BEGIN); return CRYPT_WriteSerializedStoreToStream(store, CRYPT_FileOutputFunc, file); } 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; }