/* * 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 #define NONAMELESSUNION #include "windef.h" #include "winbase.h" #include "wincrypt.h" #include "mssip.h" #include "winuser.h" #include "wintrust.h" #include "crypt32_private.h" #include "cryptres.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) && read == blob->cbData; if (!ret) CryptMemFree(blob->pbData); } else ret = FALSE; } CloseHandle(file); } TRACE("returning %d\n", ret); return ret; } static BOOL CRYPT_QueryContextBlob(const CERT_BLOB *blob, DWORD dwExpectedContentTypeFlags, HCERTSTORE store, DWORD *contentType, const void **ppvContext) { BOOL 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) *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) *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) *contentType = CERT_QUERY_CONTENT_CTL; } return ret; } static BOOL CRYPT_QueryContextObject(DWORD dwObjectType, const void *pvObject, DWORD dwExpectedContentTypeFlags, DWORD dwExpectedFormatTypeFlags, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, DWORD *pdwFormatType, HCERTSTORE *phCertStore, const void **ppvContext) { CERT_BLOB fileBlob; const CERT_BLOB *blob; HCERTSTORE store; BOOL ret; DWORD formatType = 0; 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(pvObject, &fileBlob); blob = &fileBlob; break; case CERT_QUERY_OBJECT_BLOB: blob = pvObject; ret = TRUE; break; default: SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */ ret = FALSE; } if (!ret) return FALSE; ret = FALSE; store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); if (dwExpectedFormatTypeFlags & CERT_QUERY_FORMAT_FLAG_BINARY) { ret = CRYPT_QueryContextBlob(blob, dwExpectedContentTypeFlags, store, pdwContentType, ppvContext); if (ret) formatType = CERT_QUERY_FORMAT_BINARY; } if (!ret && (dwExpectedFormatTypeFlags & CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED)) { CRYPT_DATA_BLOB trimmed = { blob->cbData, blob->pbData }; CRYPT_DATA_BLOB decoded; while (trimmed.cbData && !trimmed.pbData[trimmed.cbData - 1]) trimmed.cbData--; ret = CryptStringToBinaryA((LPSTR)trimmed.pbData, trimmed.cbData, CRYPT_STRING_BASE64_ANY, NULL, &decoded.cbData, NULL, NULL); if (ret) { decoded.pbData = CryptMemAlloc(decoded.cbData); if (decoded.pbData) { ret = CryptStringToBinaryA((LPSTR)trimmed.pbData, trimmed.cbData, CRYPT_STRING_BASE64_ANY, decoded.pbData, &decoded.cbData, NULL, NULL); if (ret) { ret = CRYPT_QueryContextBlob(&decoded, dwExpectedContentTypeFlags, store, pdwContentType, ppvContext); if (ret) formatType = CERT_QUERY_FORMAT_BASE64_ENCODED; } CryptMemFree(decoded.pbData); } else ret = FALSE; } } if (ret) { if (pdwMsgAndCertEncodingType) *pdwMsgAndCertEncodingType = X509_ASN_ENCODING; if (pdwFormatType) *pdwFormatType = formatType; 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(pvObject, &fileBlob); blob = &fileBlob; break; case CERT_QUERY_OBJECT_BLOB: blob = pvObject; ret = TRUE; break; default: SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */ ret = FALSE; } if (!ret) return FALSE; ret = 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 = context; Context_AddRef(context_from_ptr(context)); } } end: if (contextInterface && context) Context_Release(context_from_ptr(context)); if (blob == &fileBlob) CryptMemFree(blob->pbData); TRACE("returning %d\n", ret); return ret; } static BOOL CRYPT_QuerySerializedStoreFromFile(LPCWSTR fileName, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg) { HANDLE file; BOOL ret = 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; } static BOOL CRYPT_QuerySerializedStoreFromBlob(const CRYPT_DATA_BLOB *blob, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg) { HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); BOOL ret; TRACE("(%ld, %p)\n", blob->cbData, blob->pbData); ret = CRYPT_ReadSerializedStoreFromBlob(blob, 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); TRACE("returning %d\n", ret); return ret; } static BOOL CRYPT_QuerySerializedStoreObject(DWORD dwObjectType, const void *pvObject, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg) { switch (dwObjectType) { case CERT_QUERY_OBJECT_FILE: return CRYPT_QuerySerializedStoreFromFile(pvObject, pdwMsgAndCertEncodingType, pdwContentType, phCertStore, phMsg); case CERT_QUERY_OBJECT_BLOB: return CRYPT_QuerySerializedStoreFromBlob(pvObject, pdwMsgAndCertEncodingType, pdwContentType, phCertStore, phMsg); default: FIXME("unimplemented for type %ld\n", dwObjectType); SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */ return FALSE; } } static BOOL CRYPT_QuerySignedMessage(const CRYPT_DATA_BLOB *blob, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCRYPTMSG *phMsg) { DWORD encodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; BOOL ret = FALSE; HCRYPTMSG msg; if ((msg = CryptMsgOpenToDecode(encodingType, 0, 0, 0, NULL, NULL))) { 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 (type != CMSG_SIGNED) { SetLastError(ERROR_INVALID_DATA); ret = FALSE; } } } if (!ret) { CryptMsgClose(msg); 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 (ret) { if (pdwMsgAndCertEncodingType) *pdwMsgAndCertEncodingType = encodingType; if (pdwContentType) *pdwContentType = CERT_QUERY_CONTENT_PKCS7_SIGNED; if (phMsg) *phMsg = msg; } return ret; } static BOOL CRYPT_QueryUnsignedMessage(const CRYPT_DATA_BLOB *blob, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCRYPTMSG *phMsg) { DWORD encodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; BOOL ret = FALSE; HCRYPTMSG msg; if ((msg = CryptMsgOpenToDecode(encodingType, 0, 0, 0, NULL, NULL))) { 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 (type != CMSG_DATA) { SetLastError(ERROR_INVALID_DATA); ret = FALSE; } } } if (!ret) { CryptMsgClose(msg); 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 (ret) { if (pdwMsgAndCertEncodingType) *pdwMsgAndCertEncodingType = encodingType; if (pdwContentType) *pdwContentType = CERT_QUERY_CONTENT_PKCS7_SIGNED; if (phMsg) *phMsg = msg; } return ret; } /* Used to decode non-embedded messages */ static BOOL CRYPT_QueryMessageObject(DWORD dwObjectType, const void *pvObject, DWORD dwExpectedContentTypeFlags, DWORD dwExpectedFormatTypeFlags, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, DWORD *pdwFormatType, 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; DWORD formatType = 0; TRACE("(%ld, %p, %08lx, %08lx, %p, %p, %p, %p, %p)\n", dwObjectType, pvObject, dwExpectedContentTypeFlags, dwExpectedFormatTypeFlags, pdwMsgAndCertEncodingType, pdwContentType, pdwFormatType, phCertStore, phMsg); switch (dwObjectType) { case CERT_QUERY_OBJECT_FILE: /* This isn't an embedded PKCS7 message, so just read the file * directly */ ret = CRYPT_ReadBlobFromFile(pvObject, &fileBlob); blob = &fileBlob; break; case CERT_QUERY_OBJECT_BLOB: blob = pvObject; ret = TRUE; break; default: SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */ ret = FALSE; } if (!ret) return FALSE; ret = FALSE; if (dwExpectedFormatTypeFlags & CERT_QUERY_FORMAT_FLAG_BINARY) { /* Try it first as a signed message */ if (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) ret = CRYPT_QuerySignedMessage(blob, pdwMsgAndCertEncodingType, pdwContentType, &msg); /* Failing that, try as an unsigned message */ if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED)) ret = CRYPT_QueryUnsignedMessage(blob, pdwMsgAndCertEncodingType, pdwContentType, &msg); if (ret) formatType = CERT_QUERY_FORMAT_BINARY; } if (!ret && (dwExpectedFormatTypeFlags & CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED)) { CRYPT_DATA_BLOB trimmed = { blob->cbData, blob->pbData }; CRYPT_DATA_BLOB decoded; while (trimmed.cbData && !trimmed.pbData[trimmed.cbData - 1]) trimmed.cbData--; ret = CryptStringToBinaryA((LPSTR)trimmed.pbData, trimmed.cbData, CRYPT_STRING_BASE64_ANY, NULL, &decoded.cbData, NULL, NULL); if (ret) { decoded.pbData = CryptMemAlloc(decoded.cbData); if (decoded.pbData) { ret = CryptStringToBinaryA((LPSTR)trimmed.pbData, trimmed.cbData, CRYPT_STRING_BASE64_ANY, decoded.pbData, &decoded.cbData, NULL, NULL); if (ret) { /* Try it first as a signed message */ if (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) ret = CRYPT_QuerySignedMessage(&decoded, pdwMsgAndCertEncodingType, pdwContentType, &msg); /* Failing that, try as an unsigned message */ if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED)) ret = CRYPT_QueryUnsignedMessage(&decoded, pdwMsgAndCertEncodingType, pdwContentType, &msg); if (ret) formatType = CERT_QUERY_FORMAT_BASE64_ENCODED; } CryptMemFree(decoded.pbData); } else ret = FALSE; } if (!ret && !(blob->cbData % sizeof(WCHAR))) { CRYPT_DATA_BLOB decoded; LPWSTR str = (LPWSTR)blob->pbData; DWORD strLen = blob->cbData / sizeof(WCHAR); /* Try again, assuming the input string is UTF-16 base64 */ while (strLen && !str[strLen - 1]) strLen--; ret = CryptStringToBinaryW(str, strLen, CRYPT_STRING_BASE64_ANY, NULL, &decoded.cbData, NULL, NULL); if (ret) { decoded.pbData = CryptMemAlloc(decoded.cbData); if (decoded.pbData) { ret = CryptStringToBinaryW(str, strLen, CRYPT_STRING_BASE64_ANY, decoded.pbData, &decoded.cbData, NULL, NULL); if (ret) { /* Try it first as a signed message */ if (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) ret = CRYPT_QuerySignedMessage(&decoded, pdwMsgAndCertEncodingType, pdwContentType, &msg); /* Failing that, try as an unsigned message */ if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED)) ret = CRYPT_QueryUnsignedMessage(&decoded, pdwMsgAndCertEncodingType, pdwContentType, &msg); if (ret) formatType = CERT_QUERY_FORMAT_BASE64_ENCODED; } CryptMemFree(decoded.pbData); } else ret = FALSE; } } } if (ret) { if (pdwFormatType) *pdwFormatType = formatType; if (phCertStore) *phCertStore = CertOpenStore(CERT_STORE_PROV_MSG, encodingType, 0, 0, msg); if (phMsg) *phMsg = msg; else CryptMsgClose(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; GUID subject; BOOL ret = FALSE; TRACE("%s\n", debugstr_w(pvObject)); if (dwObjectType == CERT_QUERY_OBJECT_BLOB) { WCHAR temp_path[MAX_PATH], temp_name[MAX_PATH]; const CERT_BLOB *b = pvObject; TRACE("cbData %lu, pbData %p.\n", b->cbData, b->pbData); if (!GetTempPathW(MAX_PATH, temp_path) || !GetTempFileNameW(temp_path, L"blb", 0, temp_name)) { ERR("Failed getting temp file name.\n"); return FALSE; } file = CreateFileW(temp_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (file == INVALID_HANDLE_VALUE) { ERR("Could not create temp file.\n"); SetLastError(ERROR_OUTOFMEMORY); return FALSE; } if (!WriteFile(file, b->pbData, b->cbData, NULL, NULL)) { CloseHandle(file); ERR("Could not write temp file.\n"); SetLastError(ERROR_OUTOFMEMORY); return FALSE; } } else if (dwObjectType == CERT_QUERY_OBJECT_FILE) { file = CreateFileW(pvObject, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } else { WARN("Unknown dwObjectType %lu.\n", dwObjectType); SetLastError(E_INVALIDARG); return FALSE; } if (file != INVALID_HANDLE_VALUE) { ret = CryptSIPRetrieveSubjectGuid(pvObject, file, &subject); if (ret) { SIP_DISPATCH_INFO sip; memset(&sip, 0, sizeof(sip)); sip.cbSize = sizeof(sip); ret = CryptSIPLoad(&subject, 0, &sip); if (ret) { SIP_SUBJECTINFO subjectInfo; CERT_BLOB blob; DWORD encodingType; memset(&subjectInfo, 0, sizeof(subjectInfo)); subjectInfo.cbSize = sizeof(subjectInfo); subjectInfo.pgSubjectType = &subject; subjectInfo.hFile = file; subjectInfo.pwsFileName = pvObject; ret = sip.pfGet(&subjectInfo, &encodingType, 0, &blob.cbData, NULL); if (ret) { blob.pbData = CryptMemAlloc(blob.cbData); if (blob.pbData) { ret = sip.pfGet(&subjectInfo, &encodingType, 0, &blob.cbData, blob.pbData); if (ret) { ret = CRYPT_QueryMessageObject( CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED, CERT_QUERY_FORMAT_FLAG_BINARY, pdwMsgAndCertEncodingType, NULL, NULL, phCertStore, phMsg); if (ret && pdwContentType) *pdwContentType = CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED; } CryptMemFree(blob.pbData); } else { SetLastError(ERROR_OUTOFMEMORY); ret = FALSE; } } } } CloseHandle(file); } TRACE("returning %d\n", ret); return ret; } static BOOL CRYPT_QueryPFXObject(DWORD dwObjectType, const void *pvObject, DWORD dwExpectedContentTypeFlags, DWORD dwExpectedFormatTypeFlags, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, DWORD *pdwFormatType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg) { CRYPT_DATA_BLOB blob = {0}, *ptr; BOOL ret; TRACE("(%ld, %p, %08lx, %08lx, %p, %p, %p, %p, %p)\n", dwObjectType, pvObject, dwExpectedContentTypeFlags, dwExpectedFormatTypeFlags, pdwMsgAndCertEncodingType, pdwContentType, pdwFormatType, phCertStore, phMsg); switch (dwObjectType) { case CERT_QUERY_OBJECT_FILE: if (!CRYPT_ReadBlobFromFile(pvObject, &blob)) return FALSE; ptr = &blob; break; case CERT_QUERY_OBJECT_BLOB: ptr = (CRYPT_DATA_BLOB *)pvObject; break; default: return FALSE; } ret = PFXIsPFXBlob(ptr); if (ret) { if (pdwMsgAndCertEncodingType) *pdwMsgAndCertEncodingType = X509_ASN_ENCODING; if (pdwContentType) *pdwContentType = CERT_QUERY_CONTENT_PFX; if (pdwFormatType) *pdwFormatType = CERT_QUERY_FORMAT_BINARY; if (phCertStore) *phCertStore = NULL; if (phMsg) *phMsg = NULL; } CryptMemFree(blob.pbData); 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_CERT_PAIR; BOOL ret = TRUE; TRACE("(%08lx, %p, %08lx, %08lx, %08lx, %p, %p, %p, %p, %p, %p)\n", dwObjectType, pvObject, dwExpectedContentTypeFlags, dwExpectedFormatTypeFlags, dwFlags, pdwMsgAndCertEncodingType, pdwContentType, pdwFormatType, phCertStore, phMsg, ppvContext); if (dwObjectType != CERT_QUERY_OBJECT_BLOB && dwObjectType != CERT_QUERY_OBJECT_FILE) { WARN("unsupported type %ld\n", dwObjectType); SetLastError(E_INVALIDARG); return FALSE; } if (!pvObject) { WARN("missing required argument\n"); SetLastError(E_INVALIDARG); return FALSE; } if (dwExpectedContentTypeFlags & unimplementedTypes) WARN("unimplemented for types %08lx\n", dwExpectedContentTypeFlags & unimplementedTypes); 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, dwExpectedFormatTypeFlags, pdwMsgAndCertEncodingType, pdwContentType, pdwFormatType, 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, dwExpectedFormatTypeFlags, pdwMsgAndCertEncodingType, pdwContentType, pdwFormatType, phCertStore, phMsg); } if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED)) { ret = CRYPT_QueryEmbeddedMessageObject(dwObjectType, pvObject, dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType, phCertStore, phMsg); } if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PFX)) { ret = CRYPT_QueryPFXObject(dwObjectType, pvObject, dwExpectedContentTypeFlags, dwExpectedFormatTypeFlags, pdwMsgAndCertEncodingType, pdwContentType, pdwFormatType, phCertStore, phMsg); } if (!ret) SetLastError(CRYPT_E_NO_MATCH); TRACE("returning %d\n", ret); return ret; } static BOOL WINAPI CRYPT_FormatHexString(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { BOOL ret; DWORD bytesNeeded; if (cbEncoded) bytesNeeded = (cbEncoded * 3) * sizeof(WCHAR); else bytesNeeded = sizeof(WCHAR); if (!pbFormat) { *pcbFormat = bytesNeeded; ret = TRUE; } else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { DWORD i; LPWSTR ptr = pbFormat; *pcbFormat = bytesNeeded; if (cbEncoded) { for (i = 0; i < cbEncoded; i++) { if (i < cbEncoded - 1) ptr += swprintf(ptr, 4, L"%02x ", pbEncoded[i]); else ptr += swprintf(ptr, 3, L"%02x", pbEncoded[i]); } } else *ptr = 0; ret = TRUE; } return ret; } #define MAX_STRING_RESOURCE_LEN 128 static const WCHAR commaSpace[] = L", "; struct BitToString { BYTE bit; int id; WCHAR str[MAX_STRING_RESOURCE_LEN]; }; static BOOL CRYPT_FormatBits(BYTE bits, const struct BitToString *map, DWORD mapEntries, void *pbFormat, DWORD *pcbFormat, BOOL *first) { DWORD bytesNeeded = sizeof(WCHAR); unsigned int i; BOOL ret = TRUE, localFirst = *first; for (i = 0; i < mapEntries; i++) if (bits & map[i].bit) { if (!localFirst) bytesNeeded += lstrlenW(commaSpace) * sizeof(WCHAR); localFirst = FALSE; bytesNeeded += lstrlenW(map[i].str) * sizeof(WCHAR); } if (!pbFormat) { *first = localFirst; *pcbFormat = bytesNeeded; } else if (*pcbFormat < bytesNeeded) { *first = localFirst; *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; localFirst = *first; *pcbFormat = bytesNeeded; for (i = 0; i < mapEntries; i++) if (bits & map[i].bit) { if (!localFirst) { lstrcpyW(str, commaSpace); str += lstrlenW(commaSpace); } localFirst = FALSE; lstrcpyW(str, map[i].str); str += lstrlenW(map[i].str); } *first = localFirst; } return ret; } static struct BitToString keyUsageByte0Map[] = { { CERT_DIGITAL_SIGNATURE_KEY_USAGE, IDS_DIGITAL_SIGNATURE, { 0 } }, { CERT_NON_REPUDIATION_KEY_USAGE, IDS_NON_REPUDIATION, { 0 } }, { CERT_KEY_ENCIPHERMENT_KEY_USAGE, IDS_KEY_ENCIPHERMENT, { 0 } }, { CERT_DATA_ENCIPHERMENT_KEY_USAGE, IDS_DATA_ENCIPHERMENT, { 0 } }, { CERT_KEY_AGREEMENT_KEY_USAGE, IDS_KEY_AGREEMENT, { 0 } }, { CERT_KEY_CERT_SIGN_KEY_USAGE, IDS_CERT_SIGN, { 0 } }, { CERT_OFFLINE_CRL_SIGN_KEY_USAGE, IDS_OFFLINE_CRL_SIGN, { 0 } }, { CERT_CRL_SIGN_KEY_USAGE, IDS_CRL_SIGN, { 0 } }, { CERT_ENCIPHER_ONLY_KEY_USAGE, IDS_ENCIPHER_ONLY, { 0 } }, }; static struct BitToString keyUsageByte1Map[] = { { CERT_DECIPHER_ONLY_KEY_USAGE, IDS_DECIPHER_ONLY, { 0 } }, }; static BOOL WINAPI CRYPT_FormatKeyUsage(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { DWORD size; CRYPT_BIT_BLOB *bits; BOOL ret; if (!cbEncoded) { SetLastError(E_INVALIDARG); return FALSE; } if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_KEY_USAGE, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &bits, &size))) { WCHAR infoNotAvailable[MAX_STRING_RESOURCE_LEN]; DWORD bytesNeeded = sizeof(WCHAR); LoadStringW(hInstance, IDS_INFO_NOT_AVAILABLE, infoNotAvailable, ARRAY_SIZE(infoNotAvailable)); if (!bits->cbData || bits->cbData > 2) { bytesNeeded += lstrlenW(infoNotAvailable) * sizeof(WCHAR); if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; *pcbFormat = bytesNeeded; lstrcpyW(str, infoNotAvailable); } } else { static BOOL stringsLoaded = FALSE; unsigned int i; DWORD bitStringLen; BOOL first = TRUE; if (!stringsLoaded) { for (i = 0; i < ARRAY_SIZE(keyUsageByte0Map); i++) LoadStringW(hInstance, keyUsageByte0Map[i].id, keyUsageByte0Map[i].str, MAX_STRING_RESOURCE_LEN); for (i = 0; i < ARRAY_SIZE(keyUsageByte1Map); i++) LoadStringW(hInstance, keyUsageByte1Map[i].id, keyUsageByte1Map[i].str, MAX_STRING_RESOURCE_LEN); stringsLoaded = TRUE; } CRYPT_FormatBits(bits->pbData[0], keyUsageByte0Map, ARRAY_SIZE(keyUsageByte0Map), NULL, &bitStringLen, &first); bytesNeeded += bitStringLen; if (bits->cbData == 2) { CRYPT_FormatBits(bits->pbData[1], keyUsageByte1Map, ARRAY_SIZE(keyUsageByte1Map), NULL, &bitStringLen, &first); bytesNeeded += bitStringLen; } bytesNeeded += 3 * sizeof(WCHAR); /* " (" + ")" */ CRYPT_FormatHexString(0, 0, 0, NULL, NULL, bits->pbData, bits->cbData, NULL, &size); bytesNeeded += size; if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; bitStringLen = bytesNeeded; first = TRUE; CRYPT_FormatBits(bits->pbData[0], keyUsageByte0Map, ARRAY_SIZE(keyUsageByte0Map), str, &bitStringLen, &first); str += bitStringLen / sizeof(WCHAR) - 1; if (bits->cbData == 2) { bitStringLen = bytesNeeded; CRYPT_FormatBits(bits->pbData[1], keyUsageByte1Map, ARRAY_SIZE(keyUsageByte1Map), str, &bitStringLen, &first); str += bitStringLen / sizeof(WCHAR) - 1; } *str++ = ' '; *str++ = '('; CRYPT_FormatHexString(0, 0, 0, NULL, NULL, bits->pbData, bits->cbData, str, &size); str += size / sizeof(WCHAR) - 1; *str++ = ')'; *str = 0; } } LocalFree(bits); } return ret; } static const WCHAR crlf[] = L"\r\n"; static WCHAR subjectTypeHeader[MAX_STRING_RESOURCE_LEN]; static WCHAR subjectTypeCA[MAX_STRING_RESOURCE_LEN]; static WCHAR subjectTypeEndCert[MAX_STRING_RESOURCE_LEN]; static WCHAR pathLengthHeader[MAX_STRING_RESOURCE_LEN]; static BOOL WINAPI CRYPT_FormatBasicConstraints2(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { DWORD size; CERT_BASIC_CONSTRAINTS2_INFO *info; BOOL ret; if (!cbEncoded) { SetLastError(E_INVALIDARG); return FALSE; } if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_BASIC_CONSTRAINTS2, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size))) { static BOOL stringsLoaded = FALSE; DWORD bytesNeeded = sizeof(WCHAR); /* space for the NULL terminator */ WCHAR pathLength[MAX_STRING_RESOURCE_LEN]; LPCWSTR sep, subjectType; DWORD sepLen; if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { sep = crlf; sepLen = lstrlenW(crlf) * sizeof(WCHAR); } else { sep = commaSpace; sepLen = lstrlenW(commaSpace) * sizeof(WCHAR); } if (!stringsLoaded) { LoadStringW(hInstance, IDS_SUBJECT_TYPE, subjectTypeHeader, ARRAY_SIZE(subjectTypeHeader)); LoadStringW(hInstance, IDS_SUBJECT_TYPE_CA, subjectTypeCA, ARRAY_SIZE(subjectTypeCA)); LoadStringW(hInstance, IDS_SUBJECT_TYPE_END_CERT, subjectTypeEndCert, ARRAY_SIZE(subjectTypeEndCert)); LoadStringW(hInstance, IDS_PATH_LENGTH, pathLengthHeader, ARRAY_SIZE(pathLengthHeader)); stringsLoaded = TRUE; } bytesNeeded += lstrlenW(subjectTypeHeader) * sizeof(WCHAR); if (info->fCA) subjectType = subjectTypeCA; else subjectType = subjectTypeEndCert; bytesNeeded += lstrlenW(subjectType) * sizeof(WCHAR); bytesNeeded += sepLen; bytesNeeded += lstrlenW(pathLengthHeader) * sizeof(WCHAR); if (info->fPathLenConstraint) swprintf(pathLength, ARRAY_SIZE(pathLength), L"%d", info->dwPathLenConstraint); else LoadStringW(hInstance, IDS_PATH_LENGTH_NONE, pathLength, ARRAY_SIZE(pathLength)); bytesNeeded += lstrlenW(pathLength) * sizeof(WCHAR); if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; *pcbFormat = bytesNeeded; lstrcpyW(str, subjectTypeHeader); str += lstrlenW(subjectTypeHeader); lstrcpyW(str, subjectType); str += lstrlenW(subjectType); lstrcpyW(str, sep); str += sepLen / sizeof(WCHAR); lstrcpyW(str, pathLengthHeader); str += lstrlenW(pathLengthHeader); lstrcpyW(str, pathLength); } LocalFree(info); } return ret; } static BOOL CRYPT_FormatHexStringWithPrefix(const CRYPT_DATA_BLOB *blob, int id, LPWSTR str, DWORD *pcbStr) { WCHAR buf[MAX_STRING_RESOURCE_LEN]; DWORD bytesNeeded; BOOL ret; LoadStringW(hInstance, id, buf, ARRAY_SIZE(buf)); CRYPT_FormatHexString(X509_ASN_ENCODING, 0, 0, NULL, NULL, blob->pbData, blob->cbData, NULL, &bytesNeeded); bytesNeeded += lstrlenW(buf) * sizeof(WCHAR); if (!str) { *pcbStr = bytesNeeded; ret = TRUE; } else if (*pcbStr < bytesNeeded) { *pcbStr = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { *pcbStr = bytesNeeded; lstrcpyW(str, buf); str += lstrlenW(str); bytesNeeded -= lstrlenW(str) * sizeof(WCHAR); ret = CRYPT_FormatHexString(X509_ASN_ENCODING, 0, 0, NULL, NULL, blob->pbData, blob->cbData, str, &bytesNeeded); } return ret; } static BOOL CRYPT_FormatKeyId(const CRYPT_DATA_BLOB *keyId, LPWSTR str, DWORD *pcbStr) { return CRYPT_FormatHexStringWithPrefix(keyId, IDS_KEY_ID, str, pcbStr); } static BOOL CRYPT_FormatCertSerialNumber(const CRYPT_DATA_BLOB *serialNum, LPWSTR str, DWORD *pcbStr) { return CRYPT_FormatHexStringWithPrefix(serialNum, IDS_CERT_SERIAL_NUMBER, str, pcbStr); } static const WCHAR indent[] = L" "; static const WCHAR colonCrlf[] = L":\r\n"; static BOOL CRYPT_FormatAltNameEntry(DWORD dwFormatStrType, DWORD indentLevel, const CERT_ALT_NAME_ENTRY *entry, LPWSTR str, DWORD *pcbStr) { BOOL ret; WCHAR buf[MAX_STRING_RESOURCE_LEN]; WCHAR mask[MAX_STRING_RESOURCE_LEN]; WCHAR ipAddrBuf[32]; WCHAR maskBuf[16]; DWORD bytesNeeded = sizeof(WCHAR); DWORD strType = CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG; if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) bytesNeeded += indentLevel * lstrlenW(indent) * sizeof(WCHAR); switch (entry->dwAltNameChoice) { case CERT_ALT_NAME_RFC822_NAME: LoadStringW(hInstance, IDS_ALT_NAME_RFC822_NAME, buf, ARRAY_SIZE(buf)); bytesNeeded += lstrlenW(entry->u.pwszRfc822Name) * sizeof(WCHAR); ret = TRUE; break; case CERT_ALT_NAME_DNS_NAME: LoadStringW(hInstance, IDS_ALT_NAME_DNS_NAME, buf, ARRAY_SIZE(buf)); bytesNeeded += lstrlenW(entry->u.pwszDNSName) * sizeof(WCHAR); ret = TRUE; break; case CERT_ALT_NAME_DIRECTORY_NAME: { DWORD directoryNameLen; if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) strType |= CERT_NAME_STR_CRLF_FLAG; directoryNameLen = cert_name_to_str_with_indent(X509_ASN_ENCODING, indentLevel + 1, &entry->u.DirectoryName, strType, NULL, 0); LoadStringW(hInstance, IDS_ALT_NAME_DIRECTORY_NAME, buf, ARRAY_SIZE(buf)); bytesNeeded += (directoryNameLen - 1) * sizeof(WCHAR); if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) bytesNeeded += lstrlenW(colonCrlf) * sizeof(WCHAR); else bytesNeeded += sizeof(WCHAR); /* '=' */ ret = TRUE; break; } case CERT_ALT_NAME_URL: LoadStringW(hInstance, IDS_ALT_NAME_URL, buf, ARRAY_SIZE(buf)); bytesNeeded += lstrlenW(entry->u.pwszURL) * sizeof(WCHAR); ret = TRUE; break; case CERT_ALT_NAME_IP_ADDRESS: { LoadStringW(hInstance, IDS_ALT_NAME_IP_ADDRESS, buf, ARRAY_SIZE(buf)); if (entry->u.IPAddress.cbData == 8) { if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { LoadStringW(hInstance, IDS_ALT_NAME_MASK, mask, ARRAY_SIZE(mask)); bytesNeeded += lstrlenW(mask) * sizeof(WCHAR); swprintf(ipAddrBuf, ARRAY_SIZE(ipAddrBuf), L"%d.%d.%d.%d", entry->u.IPAddress.pbData[0], entry->u.IPAddress.pbData[1], entry->u.IPAddress.pbData[2], entry->u.IPAddress.pbData[3]); bytesNeeded += lstrlenW(ipAddrBuf) * sizeof(WCHAR); /* indent again, for the mask line */ bytesNeeded += indentLevel * lstrlenW(indent) * sizeof(WCHAR); swprintf(maskBuf, ARRAY_SIZE(maskBuf), L"%d.%d.%d.%d", entry->u.IPAddress.pbData[4], entry->u.IPAddress.pbData[5], entry->u.IPAddress.pbData[6], entry->u.IPAddress.pbData[7]); bytesNeeded += lstrlenW(maskBuf) * sizeof(WCHAR); bytesNeeded += lstrlenW(crlf) * sizeof(WCHAR); } else { swprintf(ipAddrBuf, ARRAY_SIZE(ipAddrBuf), L"%d.%d.%d.%d/%d.%d.%d.%d", entry->u.IPAddress.pbData[0], entry->u.IPAddress.pbData[1], entry->u.IPAddress.pbData[2], entry->u.IPAddress.pbData[3], entry->u.IPAddress.pbData[4], entry->u.IPAddress.pbData[5], entry->u.IPAddress.pbData[6], entry->u.IPAddress.pbData[7]); bytesNeeded += (lstrlenW(ipAddrBuf) + 1) * sizeof(WCHAR); } ret = TRUE; } else { FIXME("unknown IP address format (%ld bytes)\n", entry->u.IPAddress.cbData); ret = FALSE; } break; } default: FIXME("unimplemented for %ld\n", entry->dwAltNameChoice); ret = FALSE; } if (ret) { bytesNeeded += lstrlenW(buf) * sizeof(WCHAR); if (!str) *pcbStr = bytesNeeded; else if (*pcbStr < bytesNeeded) { *pcbStr = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { DWORD i; *pcbStr = bytesNeeded; if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { for (i = 0; i < indentLevel; i++) { lstrcpyW(str, indent); str += lstrlenW(indent); } } lstrcpyW(str, buf); str += lstrlenW(str); switch (entry->dwAltNameChoice) { case CERT_ALT_NAME_RFC822_NAME: case CERT_ALT_NAME_DNS_NAME: case CERT_ALT_NAME_URL: lstrcpyW(str, entry->u.pwszURL); break; case CERT_ALT_NAME_DIRECTORY_NAME: if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { lstrcpyW(str, colonCrlf); str += lstrlenW(colonCrlf); } else *str++ = '='; cert_name_to_str_with_indent(X509_ASN_ENCODING, indentLevel + 1, &entry->u.DirectoryName, strType, str, bytesNeeded / sizeof(WCHAR)); break; case CERT_ALT_NAME_IP_ADDRESS: if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { lstrcpyW(str, ipAddrBuf); str += lstrlenW(ipAddrBuf); lstrcpyW(str, crlf); str += lstrlenW(crlf); if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { for (i = 0; i < indentLevel; i++) { lstrcpyW(str, indent); str += lstrlenW(indent); } } lstrcpyW(str, mask); str += lstrlenW(mask); lstrcpyW(str, maskBuf); } else lstrcpyW(str, ipAddrBuf); break; } } } return ret; } static BOOL CRYPT_FormatAltNameInfo(DWORD dwFormatStrType, DWORD indentLevel, const CERT_ALT_NAME_INFO *name, LPWSTR str, DWORD *pcbStr) { DWORD i, size, bytesNeeded = 0; BOOL ret = TRUE; LPCWSTR sep; DWORD sepLen; if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { sep = crlf; sepLen = lstrlenW(crlf) * sizeof(WCHAR); } else { sep = commaSpace; sepLen = lstrlenW(commaSpace) * sizeof(WCHAR); } for (i = 0; ret && i < name->cAltEntry; i++) { ret = CRYPT_FormatAltNameEntry(dwFormatStrType, indentLevel, &name->rgAltEntry[i], NULL, &size); if (ret) { bytesNeeded += size - sizeof(WCHAR); if (i < name->cAltEntry - 1) bytesNeeded += sepLen; } } if (ret) { bytesNeeded += sizeof(WCHAR); if (!str) *pcbStr = bytesNeeded; else if (*pcbStr < bytesNeeded) { *pcbStr = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { *pcbStr = bytesNeeded; for (i = 0; ret && i < name->cAltEntry; i++) { ret = CRYPT_FormatAltNameEntry(dwFormatStrType, indentLevel, &name->rgAltEntry[i], str, &size); if (ret) { str += size / sizeof(WCHAR) - 1; if (i < name->cAltEntry - 1) { lstrcpyW(str, sep); str += sepLen / sizeof(WCHAR); } } } } } return ret; } static const WCHAR colonSep[] = L": "; static BOOL WINAPI CRYPT_FormatAltName(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { BOOL ret; CERT_ALT_NAME_INFO *info; DWORD size; if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_ALTERNATE_NAME, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size))) { ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 0, info, pbFormat, pcbFormat); LocalFree(info); } return ret; } static BOOL CRYPT_FormatCertIssuer(DWORD dwFormatStrType, const CERT_ALT_NAME_INFO *issuer, LPWSTR str, DWORD *pcbStr) { WCHAR buf[MAX_STRING_RESOURCE_LEN]; DWORD bytesNeeded, sepLen; LPCWSTR sep; BOOL ret; LoadStringW(hInstance, IDS_CERT_ISSUER, buf, ARRAY_SIZE(buf)); ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 1, issuer, NULL, &bytesNeeded); bytesNeeded += lstrlenW(buf) * sizeof(WCHAR); if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { sep = colonCrlf; sepLen = lstrlenW(colonCrlf) * sizeof(WCHAR); } else { sep = colonSep; sepLen = lstrlenW(colonSep) * sizeof(WCHAR); } bytesNeeded += sepLen; if (ret) { if (!str) *pcbStr = bytesNeeded; else if (*pcbStr < bytesNeeded) { *pcbStr = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { *pcbStr = bytesNeeded; lstrcpyW(str, buf); bytesNeeded -= lstrlenW(str) * sizeof(WCHAR); str += lstrlenW(str); lstrcpyW(str, sep); str += sepLen / sizeof(WCHAR); ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 1, issuer, str, &bytesNeeded); } } return ret; } static BOOL WINAPI CRYPT_FormatAuthorityKeyId2(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { CERT_AUTHORITY_KEY_ID2_INFO *info; DWORD size; BOOL ret = FALSE; if (!cbEncoded) { SetLastError(E_INVALIDARG); return FALSE; } if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_AUTHORITY_KEY_ID2, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size))) { DWORD bytesNeeded = sizeof(WCHAR); /* space for the NULL terminator */ LPCWSTR sep; DWORD sepLen; BOOL needSeparator = FALSE; if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { sep = crlf; sepLen = lstrlenW(crlf) * sizeof(WCHAR); } else { sep = commaSpace; sepLen = lstrlenW(commaSpace) * sizeof(WCHAR); } if (info->KeyId.cbData) { needSeparator = TRUE; ret = CRYPT_FormatKeyId(&info->KeyId, NULL, &size); if (ret) { /* don't include NULL-terminator more than once */ bytesNeeded += size - sizeof(WCHAR); } } if (info->AuthorityCertIssuer.cAltEntry) { if (needSeparator) bytesNeeded += sepLen; needSeparator = TRUE; ret = CRYPT_FormatCertIssuer(dwFormatStrType, &info->AuthorityCertIssuer, NULL, &size); if (ret) { /* don't include NULL-terminator more than once */ bytesNeeded += size - sizeof(WCHAR); } } if (info->AuthorityCertSerialNumber.cbData) { if (needSeparator) bytesNeeded += sepLen; ret = CRYPT_FormatCertSerialNumber( &info->AuthorityCertSerialNumber, NULL, &size); if (ret) { /* don't include NULL-terminator more than once */ bytesNeeded += size - sizeof(WCHAR); } } if (ret) { if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; *pcbFormat = bytesNeeded; needSeparator = FALSE; if (info->KeyId.cbData) { needSeparator = TRUE; /* Overestimate size available, it's already been checked * above. */ size = bytesNeeded; ret = CRYPT_FormatKeyId(&info->KeyId, str, &size); if (ret) str += size / sizeof(WCHAR) - 1; } if (info->AuthorityCertIssuer.cAltEntry) { if (needSeparator) { lstrcpyW(str, sep); str += sepLen / sizeof(WCHAR); } needSeparator = TRUE; /* Overestimate size available, it's already been checked * above. */ size = bytesNeeded; ret = CRYPT_FormatCertIssuer(dwFormatStrType, &info->AuthorityCertIssuer, str, &size); if (ret) str += size / sizeof(WCHAR) - 1; } if (info->AuthorityCertSerialNumber.cbData) { if (needSeparator) { lstrcpyW(str, sep); str += sepLen / sizeof(WCHAR); } /* Overestimate size available, it's already been checked * above. */ size = bytesNeeded; ret = CRYPT_FormatCertSerialNumber( &info->AuthorityCertSerialNumber, str, &size); } } } LocalFree(info); } return ret; } static WCHAR aia[MAX_STRING_RESOURCE_LEN]; static WCHAR accessMethod[MAX_STRING_RESOURCE_LEN]; static WCHAR ocsp[MAX_STRING_RESOURCE_LEN]; static WCHAR caIssuers[MAX_STRING_RESOURCE_LEN]; static WCHAR unknown[MAX_STRING_RESOURCE_LEN]; static WCHAR accessLocation[MAX_STRING_RESOURCE_LEN]; static BOOL WINAPI CRYPT_FormatAuthorityInfoAccess(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { CERT_AUTHORITY_INFO_ACCESS *info; DWORD size; BOOL ret = FALSE; if (!cbEncoded) { SetLastError(E_INVALIDARG); return FALSE; } if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_AUTHORITY_INFO_ACCESS, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size))) { DWORD bytesNeeded = sizeof(WCHAR); if (!info->cAccDescr) { WCHAR infoNotAvailable[MAX_STRING_RESOURCE_LEN]; LoadStringW(hInstance, IDS_INFO_NOT_AVAILABLE, infoNotAvailable, ARRAY_SIZE(infoNotAvailable)); bytesNeeded += lstrlenW(infoNotAvailable) * sizeof(WCHAR); if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { *pcbFormat = bytesNeeded; lstrcpyW(pbFormat, infoNotAvailable); } } else { static const WCHAR equal[] = L"="; static BOOL stringsLoaded = FALSE; DWORD i; LPCWSTR headingSep, accessMethodSep, locationSep; WCHAR accessDescrNum[11]; if (!stringsLoaded) { LoadStringW(hInstance, IDS_AIA, aia, ARRAY_SIZE(aia)); LoadStringW(hInstance, IDS_ACCESS_METHOD, accessMethod, ARRAY_SIZE(accessMethod)); LoadStringW(hInstance, IDS_ACCESS_METHOD_OCSP, ocsp, ARRAY_SIZE(ocsp)); LoadStringW(hInstance, IDS_ACCESS_METHOD_CA_ISSUERS, caIssuers, ARRAY_SIZE(caIssuers)); LoadStringW(hInstance, IDS_ACCESS_METHOD_UNKNOWN, unknown, ARRAY_SIZE(unknown)); LoadStringW(hInstance, IDS_ACCESS_LOCATION, accessLocation, ARRAY_SIZE(accessLocation)); stringsLoaded = TRUE; } if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { headingSep = crlf; accessMethodSep = crlf; locationSep = colonCrlf; } else { headingSep = colonSep; accessMethodSep = commaSpace; locationSep = equal; } for (i = 0; ret && i < info->cAccDescr; i++) { /* Heading */ bytesNeeded += sizeof(WCHAR); /* left bracket */ swprintf(accessDescrNum, ARRAY_SIZE(accessDescrNum), L"%d", i + 1); bytesNeeded += lstrlenW(accessDescrNum) * sizeof(WCHAR); bytesNeeded += sizeof(WCHAR); /* right bracket */ bytesNeeded += lstrlenW(aia) * sizeof(WCHAR); bytesNeeded += lstrlenW(headingSep) * sizeof(WCHAR); /* Access method */ bytesNeeded += lstrlenW(accessMethod) * sizeof(WCHAR); if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) bytesNeeded += lstrlenW(indent) * sizeof(WCHAR); if (!strcmp(info->rgAccDescr[i].pszAccessMethod, szOID_PKIX_OCSP)) bytesNeeded += lstrlenW(ocsp) * sizeof(WCHAR); else if (!strcmp(info->rgAccDescr[i].pszAccessMethod, szOID_PKIX_CA_ISSUERS)) bytesNeeded += lstrlenW(caIssuers) * sizeof(caIssuers); else bytesNeeded += lstrlenW(unknown) * sizeof(WCHAR); bytesNeeded += sizeof(WCHAR); /* space */ bytesNeeded += sizeof(WCHAR); /* left paren */ bytesNeeded += strlen(info->rgAccDescr[i].pszAccessMethod) * sizeof(WCHAR); bytesNeeded += sizeof(WCHAR); /* right paren */ /* Delimiter between access method and location */ bytesNeeded += lstrlenW(accessMethodSep) * sizeof(WCHAR); if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) bytesNeeded += lstrlenW(indent) * sizeof(WCHAR); bytesNeeded += lstrlenW(accessLocation) * sizeof(WCHAR); bytesNeeded += lstrlenW(locationSep) * sizeof(WCHAR); ret = CRYPT_FormatAltNameEntry(dwFormatStrType, 2, &info->rgAccDescr[i].AccessLocation, NULL, &size); if (ret) bytesNeeded += size - sizeof(WCHAR); /* Need extra delimiter between access method entries */ if (i < info->cAccDescr - 1) bytesNeeded += lstrlenW(accessMethodSep) * sizeof(WCHAR); } if (ret) { if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; DWORD altNameEntrySize; *pcbFormat = bytesNeeded; for (i = 0; ret && i < info->cAccDescr; i++) { LPCSTR oidPtr; *str++ = '['; swprintf(accessDescrNum, ARRAY_SIZE(accessDescrNum), L"%d", i + 1); lstrcpyW(str, accessDescrNum); str += lstrlenW(accessDescrNum); *str++ = ']'; lstrcpyW(str, aia); str += lstrlenW(aia); lstrcpyW(str, headingSep); str += lstrlenW(headingSep); if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { lstrcpyW(str, indent); str += lstrlenW(indent); } lstrcpyW(str, accessMethod); str += lstrlenW(accessMethod); if (!strcmp(info->rgAccDescr[i].pszAccessMethod, szOID_PKIX_OCSP)) { lstrcpyW(str, ocsp); str += lstrlenW(ocsp); } else if (!strcmp(info->rgAccDescr[i].pszAccessMethod, szOID_PKIX_CA_ISSUERS)) { lstrcpyW(str, caIssuers); str += lstrlenW(caIssuers); } else { lstrcpyW(str, unknown); str += lstrlenW(unknown); } *str++ = ' '; *str++ = '('; for (oidPtr = info->rgAccDescr[i].pszAccessMethod; *oidPtr; oidPtr++, str++) *str = *oidPtr; *str++ = ')'; lstrcpyW(str, accessMethodSep); str += lstrlenW(accessMethodSep); if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { lstrcpyW(str, indent); str += lstrlenW(indent); } lstrcpyW(str, accessLocation); str += lstrlenW(accessLocation); lstrcpyW(str, locationSep); str += lstrlenW(locationSep); /* This overestimates the size available, but that * won't matter since we checked earlier whether enough * space for the entire string was available. */ altNameEntrySize = bytesNeeded; ret = CRYPT_FormatAltNameEntry(dwFormatStrType, 2, &info->rgAccDescr[i].AccessLocation, str, &altNameEntrySize); if (ret) str += altNameEntrySize / sizeof(WCHAR) - 1; if (i < info->cAccDescr - 1) { lstrcpyW(str, accessMethodSep); str += lstrlenW(accessMethodSep); } } } } } LocalFree(info); } return ret; } static WCHAR keyCompromise[MAX_STRING_RESOURCE_LEN]; static WCHAR caCompromise[MAX_STRING_RESOURCE_LEN]; static WCHAR affiliationChanged[MAX_STRING_RESOURCE_LEN]; static WCHAR superseded[MAX_STRING_RESOURCE_LEN]; static WCHAR operationCeased[MAX_STRING_RESOURCE_LEN]; static WCHAR certificateHold[MAX_STRING_RESOURCE_LEN]; struct reason_map_entry { BYTE reasonBit; LPWSTR reason; int id; }; static struct reason_map_entry reason_map[] = { { CRL_REASON_KEY_COMPROMISE_FLAG, keyCompromise, IDS_REASON_KEY_COMPROMISE }, { CRL_REASON_CA_COMPROMISE_FLAG, caCompromise, IDS_REASON_CA_COMPROMISE }, { CRL_REASON_AFFILIATION_CHANGED_FLAG, affiliationChanged, IDS_REASON_AFFILIATION_CHANGED }, { CRL_REASON_SUPERSEDED_FLAG, superseded, IDS_REASON_SUPERSEDED }, { CRL_REASON_CESSATION_OF_OPERATION_FLAG, operationCeased, IDS_REASON_CESSATION_OF_OPERATION }, { CRL_REASON_CERTIFICATE_HOLD_FLAG, certificateHold, IDS_REASON_CERTIFICATE_HOLD }, }; static BOOL CRYPT_FormatReason(DWORD dwFormatStrType, const CRYPT_BIT_BLOB *reasonFlags, LPWSTR str, DWORD *pcbStr) { static const WCHAR sep[] = L", "; static BOOL stringsLoaded = FALSE; unsigned int i, numReasons = 0; BOOL ret = TRUE; DWORD bytesNeeded = sizeof(WCHAR); WCHAR bits[6]; if (!stringsLoaded) { for (i = 0; i < ARRAY_SIZE(reason_map); i++) LoadStringW(hInstance, reason_map[i].id, reason_map[i].reason, MAX_STRING_RESOURCE_LEN); stringsLoaded = TRUE; } /* No need to check reasonFlags->cbData, we already know it's positive. * Ignore any other bytes, as they're for undefined bits. */ for (i = 0; i < ARRAY_SIZE(reason_map); i++) { if (reasonFlags->pbData[0] & reason_map[i].reasonBit) { bytesNeeded += lstrlenW(reason_map[i].reason) * sizeof(WCHAR); if (numReasons++) bytesNeeded += lstrlenW(sep) * sizeof(WCHAR); } } swprintf(bits, ARRAY_SIZE(bits), L" (%02x)", reasonFlags->pbData[0]); bytesNeeded += lstrlenW(bits); if (!str) *pcbStr = bytesNeeded; else if (*pcbStr < bytesNeeded) { *pcbStr = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { *pcbStr = bytesNeeded; for (i = 0; i < ARRAY_SIZE(reason_map); i++) { if (reasonFlags->pbData[0] & reason_map[i].reasonBit) { lstrcpyW(str, reason_map[i].reason); str += lstrlenW(reason_map[i].reason); if (i < ARRAY_SIZE(reason_map) - 1 && numReasons) { lstrcpyW(str, sep); str += lstrlenW(sep); } } } lstrcpyW(str, bits); } return ret; } static WCHAR crlDistPoint[MAX_STRING_RESOURCE_LEN]; static WCHAR distPointName[MAX_STRING_RESOURCE_LEN]; static WCHAR fullName[MAX_STRING_RESOURCE_LEN]; static WCHAR rdnName[MAX_STRING_RESOURCE_LEN]; static WCHAR reason[MAX_STRING_RESOURCE_LEN]; static WCHAR issuer[MAX_STRING_RESOURCE_LEN]; static BOOL WINAPI CRYPT_FormatCRLDistPoints(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { CRL_DIST_POINTS_INFO *info; DWORD size; BOOL ret = FALSE; if (!cbEncoded) { SetLastError(E_INVALIDARG); return FALSE; } if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CRL_DIST_POINTS, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size))) { static const WCHAR colon[] = L":"; static BOOL stringsLoaded = FALSE; DWORD bytesNeeded = sizeof(WCHAR); /* space for NULL terminator */ BOOL haveAnEntry = FALSE; LPCWSTR headingSep, nameSep; WCHAR distPointNum[11]; DWORD i; if (!stringsLoaded) { LoadStringW(hInstance, IDS_CRL_DIST_POINT, crlDistPoint, ARRAY_SIZE(crlDistPoint)); LoadStringW(hInstance, IDS_CRL_DIST_POINT_NAME, distPointName, ARRAY_SIZE(distPointName)); LoadStringW(hInstance, IDS_CRL_DIST_POINT_FULL_NAME, fullName, ARRAY_SIZE(fullName)); LoadStringW(hInstance, IDS_CRL_DIST_POINT_RDN_NAME, rdnName, ARRAY_SIZE(rdnName)); LoadStringW(hInstance, IDS_CRL_DIST_POINT_REASON, reason, ARRAY_SIZE(reason)); LoadStringW(hInstance, IDS_CRL_DIST_POINT_ISSUER, issuer, ARRAY_SIZE(issuer)); stringsLoaded = TRUE; } if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { headingSep = crlf; nameSep = colonCrlf; } else { headingSep = colonSep; nameSep = colon; } for (i = 0; ret && i < info->cDistPoint; i++) { CRL_DIST_POINT *distPoint = &info->rgDistPoint[i]; if (distPoint->DistPointName.dwDistPointNameChoice != CRL_DIST_POINT_NO_NAME) { bytesNeeded += lstrlenW(distPointName) * sizeof(WCHAR); bytesNeeded += lstrlenW(nameSep) * sizeof(WCHAR); if (distPoint->DistPointName.dwDistPointNameChoice == CRL_DIST_POINT_FULL_NAME) bytesNeeded += lstrlenW(fullName) * sizeof(WCHAR); else bytesNeeded += lstrlenW(rdnName) * sizeof(WCHAR); bytesNeeded += lstrlenW(nameSep) * sizeof(WCHAR); if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) bytesNeeded += 2 * lstrlenW(indent) * sizeof(WCHAR); /* The indent level (3) is higher than when used as the issuer, * because the name is subordinate to the name type (full vs. * RDN.) */ ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 3, &distPoint->DistPointName.u.FullName, NULL, &size); if (ret) bytesNeeded += size - sizeof(WCHAR); haveAnEntry = TRUE; } else if (distPoint->ReasonFlags.cbData) { bytesNeeded += lstrlenW(reason) * sizeof(WCHAR); ret = CRYPT_FormatReason(dwFormatStrType, &distPoint->ReasonFlags, NULL, &size); if (ret) bytesNeeded += size - sizeof(WCHAR); haveAnEntry = TRUE; } else if (distPoint->CRLIssuer.cAltEntry) { bytesNeeded += lstrlenW(issuer) * sizeof(WCHAR); bytesNeeded += lstrlenW(nameSep) * sizeof(WCHAR); ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 2, &distPoint->CRLIssuer, NULL, &size); if (ret) bytesNeeded += size - sizeof(WCHAR); haveAnEntry = TRUE; } if (haveAnEntry) { bytesNeeded += sizeof(WCHAR); /* left bracket */ swprintf(distPointNum, ARRAY_SIZE(distPointNum), L"%d", i + 1); bytesNeeded += lstrlenW(distPointNum) * sizeof(WCHAR); bytesNeeded += sizeof(WCHAR); /* right bracket */ bytesNeeded += lstrlenW(crlDistPoint) * sizeof(WCHAR); bytesNeeded += lstrlenW(headingSep) * sizeof(WCHAR); if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) bytesNeeded += lstrlenW(indent) * sizeof(WCHAR); } } if (!haveAnEntry) { WCHAR infoNotAvailable[MAX_STRING_RESOURCE_LEN]; LoadStringW(hInstance, IDS_INFO_NOT_AVAILABLE, infoNotAvailable, ARRAY_SIZE(infoNotAvailable)); bytesNeeded += lstrlenW(infoNotAvailable) * sizeof(WCHAR); if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { *pcbFormat = bytesNeeded; lstrcpyW(pbFormat, infoNotAvailable); } } else { if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; *pcbFormat = bytesNeeded; for (i = 0; ret && i < info->cDistPoint; i++) { CRL_DIST_POINT *distPoint = &info->rgDistPoint[i]; *str++ = '['; swprintf(distPointNum, ARRAY_SIZE(distPointNum), L"%d", i + 1); lstrcpyW(str, distPointNum); str += lstrlenW(distPointNum); *str++ = ']'; lstrcpyW(str, crlDistPoint); str += lstrlenW(crlDistPoint); lstrcpyW(str, headingSep); str += lstrlenW(headingSep); if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { lstrcpyW(str, indent); str += lstrlenW(indent); } if (distPoint->DistPointName.dwDistPointNameChoice != CRL_DIST_POINT_NO_NAME) { DWORD altNameSize = bytesNeeded; lstrcpyW(str, distPointName); str += lstrlenW(distPointName); lstrcpyW(str, nameSep); str += lstrlenW(nameSep); if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { lstrcpyW(str, indent); str += lstrlenW(indent); lstrcpyW(str, indent); str += lstrlenW(indent); } if (distPoint->DistPointName.dwDistPointNameChoice == CRL_DIST_POINT_FULL_NAME) { lstrcpyW(str, fullName); str += lstrlenW(fullName); } else { lstrcpyW(str, rdnName); str += lstrlenW(rdnName); } lstrcpyW(str, nameSep); str += lstrlenW(nameSep); ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 3, &distPoint->DistPointName.u.FullName, str, &altNameSize); if (ret) str += altNameSize / sizeof(WCHAR) - 1; } else if (distPoint->ReasonFlags.cbData) { DWORD reasonSize = bytesNeeded; lstrcpyW(str, reason); str += lstrlenW(reason); ret = CRYPT_FormatReason(dwFormatStrType, &distPoint->ReasonFlags, str, &reasonSize); if (ret) str += reasonSize / sizeof(WCHAR) - 1; } else if (distPoint->CRLIssuer.cAltEntry) { DWORD crlIssuerSize = bytesNeeded; lstrcpyW(str, issuer); str += lstrlenW(issuer); lstrcpyW(str, nameSep); str += lstrlenW(nameSep); ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 2, &distPoint->CRLIssuer, str, &crlIssuerSize); if (ret) str += crlIssuerSize / sizeof(WCHAR) - 1; } } } } LocalFree(info); } return ret; } static BOOL WINAPI CRYPT_FormatEnhancedKeyUsage(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { CERT_ENHKEY_USAGE *usage; DWORD size; BOOL ret = FALSE; if (!cbEncoded) { SetLastError(E_INVALIDARG); return FALSE; } if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_ENHANCED_KEY_USAGE, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &usage, &size))) { WCHAR unknown[MAX_STRING_RESOURCE_LEN]; DWORD i; DWORD bytesNeeded = sizeof(WCHAR); /* space for the NULL terminator */ LPCWSTR sep; DWORD sepLen; if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { sep = crlf; sepLen = lstrlenW(crlf) * sizeof(WCHAR); } else { sep = commaSpace; sepLen = lstrlenW(commaSpace) * sizeof(WCHAR); } LoadStringW(hInstance, IDS_USAGE_UNKNOWN, unknown, ARRAY_SIZE(unknown)); for (i = 0; i < usage->cUsageIdentifier; i++) { PCCRYPT_OID_INFO info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, usage->rgpszUsageIdentifier[i], CRYPT_ENHKEY_USAGE_OID_GROUP_ID); if (info) bytesNeeded += lstrlenW(info->pwszName) * sizeof(WCHAR); else bytesNeeded += lstrlenW(unknown) * sizeof(WCHAR); bytesNeeded += sizeof(WCHAR); /* space */ bytesNeeded += sizeof(WCHAR); /* left paren */ bytesNeeded += strlen(usage->rgpszUsageIdentifier[i]) * sizeof(WCHAR); bytesNeeded += sizeof(WCHAR); /* right paren */ if (i < usage->cUsageIdentifier - 1) bytesNeeded += sepLen; } if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; *pcbFormat = bytesNeeded; for (i = 0; i < usage->cUsageIdentifier; i++) { PCCRYPT_OID_INFO info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, usage->rgpszUsageIdentifier[i], CRYPT_ENHKEY_USAGE_OID_GROUP_ID); LPCSTR oidPtr; if (info) { lstrcpyW(str, info->pwszName); str += lstrlenW(info->pwszName); } else { lstrcpyW(str, unknown); str += lstrlenW(unknown); } *str++ = ' '; *str++ = '('; for (oidPtr = usage->rgpszUsageIdentifier[i]; *oidPtr; oidPtr++) *str++ = *oidPtr; *str++ = ')'; *str = 0; if (i < usage->cUsageIdentifier - 1) { lstrcpyW(str, sep); str += sepLen / sizeof(WCHAR); } } } LocalFree(usage); } return ret; } static struct BitToString netscapeCertTypeMap[] = { { NETSCAPE_SSL_CLIENT_AUTH_CERT_TYPE, IDS_NETSCAPE_SSL_CLIENT, { 0 } }, { NETSCAPE_SSL_SERVER_AUTH_CERT_TYPE, IDS_NETSCAPE_SSL_SERVER, { 0 } }, { NETSCAPE_SMIME_CERT_TYPE, IDS_NETSCAPE_SMIME, { 0 } }, { NETSCAPE_SIGN_CERT_TYPE, IDS_NETSCAPE_SIGN, { 0 } }, { NETSCAPE_SSL_CA_CERT_TYPE, IDS_NETSCAPE_SSL_CA, { 0 } }, { NETSCAPE_SMIME_CA_CERT_TYPE, IDS_NETSCAPE_SMIME_CA, { 0 } }, { NETSCAPE_SIGN_CA_CERT_TYPE, IDS_NETSCAPE_SIGN_CA, { 0 } }, }; static BOOL WINAPI CRYPT_FormatNetscapeCertType(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { DWORD size; CRYPT_BIT_BLOB *bits; BOOL ret; if (!cbEncoded) { SetLastError(E_INVALIDARG); return FALSE; } if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_BITS, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &bits, &size))) { WCHAR infoNotAvailable[MAX_STRING_RESOURCE_LEN]; DWORD bytesNeeded = sizeof(WCHAR); LoadStringW(hInstance, IDS_INFO_NOT_AVAILABLE, infoNotAvailable, ARRAY_SIZE(infoNotAvailable)); if (!bits->cbData || bits->cbData > 1) { bytesNeeded += lstrlenW(infoNotAvailable) * sizeof(WCHAR); if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; *pcbFormat = bytesNeeded; lstrcpyW(str, infoNotAvailable); } } else { static BOOL stringsLoaded = FALSE; unsigned int i; DWORD bitStringLen; BOOL first = TRUE; if (!stringsLoaded) { for (i = 0; i < ARRAY_SIZE(netscapeCertTypeMap); i++) LoadStringW(hInstance, netscapeCertTypeMap[i].id, netscapeCertTypeMap[i].str, MAX_STRING_RESOURCE_LEN); stringsLoaded = TRUE; } CRYPT_FormatBits(bits->pbData[0], netscapeCertTypeMap, ARRAY_SIZE(netscapeCertTypeMap), NULL, &bitStringLen, &first); bytesNeeded += bitStringLen; bytesNeeded += 3 * sizeof(WCHAR); /* " (" + ")" */ CRYPT_FormatHexString(0, 0, 0, NULL, NULL, bits->pbData, bits->cbData, NULL, &size); bytesNeeded += size; if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; bitStringLen = bytesNeeded; first = TRUE; CRYPT_FormatBits(bits->pbData[0], netscapeCertTypeMap, ARRAY_SIZE(netscapeCertTypeMap), str, &bitStringLen, &first); str += bitStringLen / sizeof(WCHAR) - 1; *str++ = ' '; *str++ = '('; CRYPT_FormatHexString(0, 0, 0, NULL, NULL, bits->pbData, bits->cbData, str, &size); str += size / sizeof(WCHAR) - 1; *str++ = ')'; *str = 0; } } LocalFree(bits); } return ret; } static WCHAR financialCriteria[MAX_STRING_RESOURCE_LEN]; static WCHAR available[MAX_STRING_RESOURCE_LEN]; static WCHAR notAvailable[MAX_STRING_RESOURCE_LEN]; static WCHAR meetsCriteria[MAX_STRING_RESOURCE_LEN]; static WCHAR yes[MAX_STRING_RESOURCE_LEN]; static WCHAR no[MAX_STRING_RESOURCE_LEN]; static BOOL WINAPI CRYPT_FormatSpcFinancialCriteria(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { SPC_FINANCIAL_CRITERIA criteria; DWORD size = sizeof(criteria); BOOL ret = FALSE; if (!cbEncoded) { SetLastError(E_INVALIDARG); return FALSE; } if ((ret = CryptDecodeObjectEx(dwCertEncodingType, SPC_FINANCIAL_CRITERIA_STRUCT, pbEncoded, cbEncoded, 0, NULL, &criteria, &size))) { static BOOL stringsLoaded = FALSE; DWORD bytesNeeded = sizeof(WCHAR); LPCWSTR sep; DWORD sepLen; if (!stringsLoaded) { LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA, financialCriteria, ARRAY_SIZE(financialCriteria)); LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA_AVAILABLE, available, ARRAY_SIZE(available)); LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA_NOT_AVAILABLE, notAvailable, ARRAY_SIZE(notAvailable)); LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA_MEETS_CRITERIA, meetsCriteria, ARRAY_SIZE(meetsCriteria)); LoadStringW(hInstance, IDS_YES, yes, ARRAY_SIZE(yes)); LoadStringW(hInstance, IDS_NO, no, ARRAY_SIZE(no)); stringsLoaded = TRUE; } if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) { sep = crlf; sepLen = lstrlenW(crlf) * sizeof(WCHAR); } else { sep = commaSpace; sepLen = lstrlenW(commaSpace) * sizeof(WCHAR); } bytesNeeded += lstrlenW(financialCriteria) * sizeof(WCHAR); if (criteria.fFinancialInfoAvailable) { bytesNeeded += lstrlenW(available) * sizeof(WCHAR); bytesNeeded += sepLen; bytesNeeded += lstrlenW(meetsCriteria) * sizeof(WCHAR); if (criteria.fMeetsCriteria) bytesNeeded += lstrlenW(yes) * sizeof(WCHAR); else bytesNeeded += lstrlenW(no) * sizeof(WCHAR); } else bytesNeeded += lstrlenW(notAvailable) * sizeof(WCHAR); if (!pbFormat) *pcbFormat = bytesNeeded; else if (*pcbFormat < bytesNeeded) { *pcbFormat = bytesNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; *pcbFormat = bytesNeeded; lstrcpyW(str, financialCriteria); str += lstrlenW(financialCriteria); if (criteria.fFinancialInfoAvailable) { lstrcpyW(str, available); str += lstrlenW(available); lstrcpyW(str, sep); str += sepLen / sizeof(WCHAR); lstrcpyW(str, meetsCriteria); str += lstrlenW(meetsCriteria); if (criteria.fMeetsCriteria) lstrcpyW(str, yes); else lstrcpyW(str, no); } else { lstrcpyW(str, notAvailable); } } } return ret; } static BOOL WINAPI CRYPT_FormatUnicodeString(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { CERT_NAME_VALUE *value; DWORD size; BOOL ret; if (!cbEncoded) { SetLastError(E_INVALIDARG); return FALSE; } if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_UNICODE_ANY_STRING, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &value, &size))) { if (!pbFormat) *pcbFormat = value->Value.cbData; else if (*pcbFormat < value->Value.cbData) { *pcbFormat = value->Value.cbData; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPWSTR str = pbFormat; *pcbFormat = value->Value.cbData; lstrcpyW(str, (LPWSTR)value->Value.pbData); } } return ret; } typedef BOOL (WINAPI *CryptFormatObjectFunc)(DWORD, DWORD, DWORD, void *, LPCSTR, const BYTE *, DWORD, void *, DWORD *); static CryptFormatObjectFunc CRYPT_GetBuiltinFormatFunction(DWORD encodingType, DWORD formatStrType, LPCSTR lpszStructType) { CryptFormatObjectFunc format = NULL; if ((encodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING) { SetLastError(ERROR_FILE_NOT_FOUND); return NULL; } if (IS_INTOID(lpszStructType)) { switch (LOWORD(lpszStructType)) { case LOWORD(X509_KEY_USAGE): format = CRYPT_FormatKeyUsage; break; case LOWORD(X509_ALTERNATE_NAME): format = CRYPT_FormatAltName; break; case LOWORD(X509_BASIC_CONSTRAINTS2): format = CRYPT_FormatBasicConstraints2; break; case LOWORD(X509_AUTHORITY_KEY_ID2): format = CRYPT_FormatAuthorityKeyId2; break; case LOWORD(X509_AUTHORITY_INFO_ACCESS): format = CRYPT_FormatAuthorityInfoAccess; break; case LOWORD(X509_CRL_DIST_POINTS): format = CRYPT_FormatCRLDistPoints; break; case LOWORD(X509_ENHANCED_KEY_USAGE): format = CRYPT_FormatEnhancedKeyUsage; break; case LOWORD(SPC_FINANCIAL_CRITERIA_STRUCT): format = CRYPT_FormatSpcFinancialCriteria; break; } } else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME)) format = CRYPT_FormatAltName; else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME)) format = CRYPT_FormatAltName; else if (!strcmp(lpszStructType, szOID_KEY_USAGE)) format = CRYPT_FormatKeyUsage; else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME2)) format = CRYPT_FormatAltName; else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME2)) format = CRYPT_FormatAltName; else if (!strcmp(lpszStructType, szOID_BASIC_CONSTRAINTS2)) format = CRYPT_FormatBasicConstraints2; else if (!strcmp(lpszStructType, szOID_AUTHORITY_INFO_ACCESS)) format = CRYPT_FormatAuthorityInfoAccess; else if (!strcmp(lpszStructType, szOID_AUTHORITY_KEY_IDENTIFIER2)) format = CRYPT_FormatAuthorityKeyId2; else if (!strcmp(lpszStructType, szOID_CRL_DIST_POINTS)) format = CRYPT_FormatCRLDistPoints; else if (!strcmp(lpszStructType, szOID_ENHANCED_KEY_USAGE)) format = CRYPT_FormatEnhancedKeyUsage; else if (!strcmp(lpszStructType, szOID_NETSCAPE_CERT_TYPE)) format = CRYPT_FormatNetscapeCertType; else if (!strcmp(lpszStructType, szOID_NETSCAPE_BASE_URL) || !strcmp(lpszStructType, szOID_NETSCAPE_REVOCATION_URL) || !strcmp(lpszStructType, szOID_NETSCAPE_CA_REVOCATION_URL) || !strcmp(lpszStructType, szOID_NETSCAPE_CERT_RENEWAL_URL) || !strcmp(lpszStructType, szOID_NETSCAPE_CA_POLICY_URL) || !strcmp(lpszStructType, szOID_NETSCAPE_SSL_SERVER_NAME) || !strcmp(lpszStructType, szOID_NETSCAPE_COMMENT)) format = CRYPT_FormatUnicodeString; else if (!strcmp(lpszStructType, SPC_FINANCIAL_CRITERIA_OBJID)) format = CRYPT_FormatSpcFinancialCriteria; return format; } BOOL WINAPI CryptFormatObject(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { CryptFormatObjectFunc format = NULL; HCRYPTOIDFUNCADDR hFunc = NULL; BOOL ret = FALSE; TRACE("(%08lx, %ld, %08lx, %p, %s, %p, %ld, %p, %p)\n", dwCertEncodingType, dwFormatType, dwFormatStrType, pFormatStruct, debugstr_a(lpszStructType), pbEncoded, cbEncoded, pbFormat, pcbFormat); if (!(format = CRYPT_GetBuiltinFormatFunction(dwCertEncodingType, dwFormatStrType, lpszStructType))) { static HCRYPTOIDFUNCSET set = NULL; if (!set) set = CryptInitOIDFunctionSet(CRYPT_OID_FORMAT_OBJECT_FUNC, 0); CryptGetOIDFunctionAddress(set, dwCertEncodingType, lpszStructType, 0, (void **)&format, &hFunc); } if (!format && (dwCertEncodingType & CERT_ENCODING_TYPE_MASK) == X509_ASN_ENCODING && !(dwFormatStrType & CRYPT_FORMAT_STR_NO_HEX)) format = CRYPT_FormatHexString; if (format) ret = format(dwCertEncodingType, dwFormatType, dwFormatStrType, pFormatStruct, lpszStructType, pbEncoded, cbEncoded, pbFormat, pcbFormat); if (hFunc) CryptFreeOIDFunctionAddress(hFunc, 0); return ret; }