/* * crypt32 Crypt*Object functions * * Copyright 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 #include "windef.h" #include "winbase.h" #include "wincrypt.h" #include "imagehlp.h" #include "crypt32_private.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(crypt); static BOOL CRYPT_ReadBlobFromFile(LPCWSTR fileName, PCERT_BLOB blob) { BOOL ret = FALSE; HANDLE file; TRACE("%s\n", debugstr_w(fileName)); file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (file != INVALID_HANDLE_VALUE) { ret = TRUE; blob->cbData = GetFileSize(file, NULL); if (blob->cbData) { blob->pbData = CryptMemAlloc(blob->cbData); if (blob->pbData) { DWORD read; ret = ReadFile(file, blob->pbData, blob->cbData, &read, NULL); } } CloseHandle(file); } TRACE("returning %d\n", ret); return ret; } static BOOL CRYPT_QueryContextObject(DWORD dwObjectType, const void *pvObject, DWORD dwExpectedContentTypeFlags, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCERTSTORE *phCertStore, const void **ppvContext) { CERT_BLOB fileBlob; const CERT_BLOB *blob; HCERTSTORE store; DWORD contentType; BOOL ret; switch (dwObjectType) { case CERT_QUERY_OBJECT_FILE: /* Cert, CRL, and CTL contexts can't be "embedded" in a file, so * just read the file directly */ ret = CRYPT_ReadBlobFromFile((LPCWSTR)pvObject, &fileBlob); blob = &fileBlob; break; case CERT_QUERY_OBJECT_BLOB: blob = (const CERT_BLOB *)pvObject; ret = TRUE; break; default: SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */ ret = FALSE; } if (!ret) return FALSE; store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); ret = FALSE; if (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CERT) { ret = pCertInterface->addEncodedToStore(store, X509_ASN_ENCODING, blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext); if (ret) contentType = CERT_QUERY_CONTENT_CERT; } if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CRL)) { ret = pCRLInterface->addEncodedToStore(store, X509_ASN_ENCODING, blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext); if (ret) contentType = CERT_QUERY_CONTENT_CRL; } if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CTL)) { ret = pCTLInterface->addEncodedToStore(store, X509_ASN_ENCODING, blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext); if (ret) contentType = CERT_QUERY_CONTENT_CTL; } if (ret) { if (pdwMsgAndCertEncodingType) *pdwMsgAndCertEncodingType = X509_ASN_ENCODING; if (pdwContentType) *pdwContentType = contentType; if (phCertStore) *phCertStore = CertDuplicateStore(store); } CertCloseStore(store, 0); if (blob == &fileBlob) CryptMemFree(blob->pbData); TRACE("returning %d\n", ret); return ret; } static BOOL CRYPT_QuerySerializedContextObject(DWORD dwObjectType, const void *pvObject, DWORD dwExpectedContentTypeFlags, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCERTSTORE *phCertStore, const void **ppvContext) { CERT_BLOB fileBlob; const CERT_BLOB *blob; const WINE_CONTEXT_INTERFACE *contextInterface = NULL; const void *context; DWORD contextType; BOOL ret; switch (dwObjectType) { case CERT_QUERY_OBJECT_FILE: /* Cert, CRL, and CTL contexts can't be "embedded" in a file, so * just read the file directly */ ret = CRYPT_ReadBlobFromFile((LPCWSTR)pvObject, &fileBlob); blob = &fileBlob; break; case CERT_QUERY_OBJECT_BLOB: blob = (const CERT_BLOB *)pvObject; ret = TRUE; break; default: SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */ ret = FALSE; } if (!ret) return FALSE; context = CRYPT_ReadSerializedElement(blob->pbData, blob->cbData, CERT_STORE_ALL_CONTEXT_FLAG, &contextType); if (context) { DWORD contentType, certStoreOffset; ret = TRUE; switch (contextType) { case CERT_STORE_CERTIFICATE_CONTEXT: contextInterface = pCertInterface; contentType = CERT_QUERY_CONTENT_SERIALIZED_CERT; certStoreOffset = offsetof(CERT_CONTEXT, hCertStore); if (!(dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT)) { SetLastError(ERROR_INVALID_DATA); ret = FALSE; goto end; } break; case CERT_STORE_CRL_CONTEXT: contextInterface = pCRLInterface; contentType = CERT_QUERY_CONTENT_SERIALIZED_CRL; certStoreOffset = offsetof(CRL_CONTEXT, hCertStore); if (!(dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL)) { SetLastError(ERROR_INVALID_DATA); ret = FALSE; goto end; } break; case CERT_STORE_CTL_CONTEXT: contextInterface = pCTLInterface; contentType = CERT_QUERY_CONTENT_SERIALIZED_CTL; certStoreOffset = offsetof(CTL_CONTEXT, hCertStore); if (!(dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL)) { SetLastError(ERROR_INVALID_DATA); ret = FALSE; goto end; } break; default: SetLastError(ERROR_INVALID_DATA); ret = FALSE; goto end; } if (pdwMsgAndCertEncodingType) *pdwMsgAndCertEncodingType = X509_ASN_ENCODING; if (pdwContentType) *pdwContentType = contentType; if (phCertStore) *phCertStore = CertDuplicateStore( *(HCERTSTORE *)((const BYTE *)context + certStoreOffset)); if (ppvContext) *ppvContext = contextInterface->duplicate(context); } end: if (contextInterface && context) contextInterface->free(context); if (blob == &fileBlob) CryptMemFree(blob->pbData); TRACE("returning %d\n", ret); return ret; } static BOOL CRYPT_QuerySerializedStoreObject(DWORD dwObjectType, const void *pvObject, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg) { LPCWSTR fileName = (LPCWSTR)pvObject; HANDLE file; BOOL ret = FALSE; if (dwObjectType != CERT_QUERY_OBJECT_FILE) { FIXME("unimplemented for non-file type %d\n", dwObjectType); SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */ return FALSE; } TRACE("%s\n", debugstr_w(fileName)); file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (file != INVALID_HANDLE_VALUE) { HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); ret = CRYPT_ReadSerializedStoreFromFile(file, store); if (ret) { if (pdwMsgAndCertEncodingType) *pdwMsgAndCertEncodingType = X509_ASN_ENCODING; if (pdwContentType) *pdwContentType = CERT_QUERY_CONTENT_SERIALIZED_STORE; if (phCertStore) *phCertStore = CertDuplicateStore(store); } CertCloseStore(store, 0); CloseHandle(file); } TRACE("returning %d\n", ret); return ret; } /* Used to decode non-embedded messages */ static BOOL CRYPT_QueryMessageObject(DWORD dwObjectType, const void *pvObject, DWORD dwExpectedContentTypeFlags, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg) { CERT_BLOB fileBlob; const CERT_BLOB *blob; BOOL ret; HCRYPTMSG msg = NULL; DWORD encodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; switch (dwObjectType) { case CERT_QUERY_OBJECT_FILE: /* This isn't an embedded PKCS7 message, so just read the file * directly */ ret = CRYPT_ReadBlobFromFile((LPCWSTR)pvObject, &fileBlob); blob = &fileBlob; break; case CERT_QUERY_OBJECT_BLOB: blob = (const CERT_BLOB *)pvObject; ret = TRUE; break; default: SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */ ret = FALSE; } if (!ret) return FALSE; ret = FALSE; /* Try it first as a PKCS content info */ if ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) || (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED)) { msg = CryptMsgOpenToDecode(encodingType, 0, 0, 0, NULL, NULL); if (msg) { ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE); if (ret) { DWORD type, len = sizeof(type); ret = CryptMsgGetParam(msg, CMSG_TYPE_PARAM, 0, &type, &len); if (ret) { if ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED)) { if (type != CMSG_SIGNED) { SetLastError(ERROR_INVALID_DATA); ret = FALSE; } else if (pdwContentType) *pdwContentType = CERT_QUERY_CONTENT_PKCS7_SIGNED; } else if ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED)) { if (type != CMSG_DATA) { SetLastError(ERROR_INVALID_DATA); ret = FALSE; } else if (pdwContentType) *pdwContentType = CERT_QUERY_CONTENT_PKCS7_UNSIGNED; } } } if (!ret) { CryptMsgClose(msg); msg = NULL; } } } /* Failing that, try explicitly typed messages */ if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED)) { msg = CryptMsgOpenToDecode(encodingType, 0, CMSG_SIGNED, 0, NULL, NULL); if (msg) { ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE); if (!ret) { CryptMsgClose(msg); msg = NULL; } } if (msg && pdwContentType) *pdwContentType = CERT_QUERY_CONTENT_PKCS7_SIGNED; } if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED)) { msg = CryptMsgOpenToDecode(encodingType, 0, CMSG_DATA, 0, NULL, NULL); if (msg) { ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE); if (!ret) { CryptMsgClose(msg); msg = NULL; } } if (msg && pdwContentType) *pdwContentType = CERT_QUERY_CONTENT_PKCS7_UNSIGNED; } if (pdwMsgAndCertEncodingType) *pdwMsgAndCertEncodingType = encodingType; if (msg) { if (phMsg) *phMsg = msg; if (phCertStore) *phCertStore = CertOpenStore(CERT_STORE_PROV_MSG, encodingType, 0, 0, msg); } if (blob == &fileBlob) CryptMemFree(blob->pbData); TRACE("returning %d\n", ret); return ret; } static BOOL CRYPT_QueryEmbeddedMessageObject(DWORD dwObjectType, const void *pvObject, DWORD dwExpectedContentTypeFlags, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg) { HANDLE file; BOOL ret = FALSE; if (dwObjectType != CERT_QUERY_OBJECT_FILE) { FIXME("don't know what to do for type %d embedded signed messages\n", dwObjectType); SetLastError(E_INVALIDARG); return FALSE; } file = CreateFileW((LPCWSTR)pvObject, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (file != INVALID_HANDLE_VALUE) { DWORD len = 0; ret = ImageGetCertificateData(file, 0, NULL, &len); if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { WIN_CERTIFICATE *winCert = HeapAlloc(GetProcessHeap(), 0, len); if (winCert) { ret = ImageGetCertificateData(file, 0, winCert, &len); if (ret) { CERT_BLOB blob = { winCert->dwLength, winCert->bCertificate }; ret = CRYPT_QueryMessageObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED, pdwMsgAndCertEncodingType, NULL, phCertStore, phMsg); if (ret && pdwContentType) *pdwContentType = CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED; } HeapFree(GetProcessHeap(), 0, winCert); } } CloseHandle(file); } TRACE("returning %d\n", ret); return ret; } BOOL WINAPI CryptQueryObject(DWORD dwObjectType, const void *pvObject, DWORD dwExpectedContentTypeFlags, DWORD dwExpectedFormatTypeFlags, DWORD dwFlags, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, DWORD *pdwFormatType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg, const void **ppvContext) { static const DWORD unimplementedTypes = CERT_QUERY_CONTENT_FLAG_PKCS10 | CERT_QUERY_CONTENT_FLAG_PFX | CERT_QUERY_CONTENT_FLAG_CERT_PAIR; BOOL ret = TRUE; TRACE("(%08x, %p, %08x, %08x, %08x, %p, %p, %p, %p, %p, %p)\n", dwObjectType, pvObject, dwExpectedContentTypeFlags, dwExpectedFormatTypeFlags, dwFlags, pdwMsgAndCertEncodingType, pdwContentType, pdwFormatType, phCertStore, phMsg, ppvContext); if (dwExpectedContentTypeFlags & unimplementedTypes) WARN("unimplemented for types %08x\n", dwExpectedContentTypeFlags & unimplementedTypes); if (!(dwExpectedFormatTypeFlags & CERT_QUERY_FORMAT_FLAG_BINARY)) { FIXME("unimplemented for anything but binary\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } if (pdwFormatType) *pdwFormatType = CERT_QUERY_FORMAT_BINARY; if (phCertStore) *phCertStore = NULL; if (phMsg) *phMsg = NULL; if (ppvContext) *ppvContext = NULL; ret = FALSE; if ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CERT) || (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CRL) || (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CTL)) { ret = CRYPT_QueryContextObject(dwObjectType, pvObject, dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType, phCertStore, ppvContext); } if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE)) { ret = CRYPT_QuerySerializedStoreObject(dwObjectType, pvObject, pdwMsgAndCertEncodingType, pdwContentType, phCertStore, phMsg); } if (!ret && ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT) || (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL) || (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL))) { ret = CRYPT_QuerySerializedContextObject(dwObjectType, pvObject, dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType, phCertStore, ppvContext); } if (!ret && ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) || (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED))) { ret = CRYPT_QueryMessageObject(dwObjectType, pvObject, dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType, phCertStore, phMsg); } if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED)) { ret = CRYPT_QueryEmbeddedMessageObject(dwObjectType, pvObject, dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType, phCertStore, phMsg); } TRACE("returning %d\n", ret); return ret; }