diff --git a/dlls/crypt32/crypt32_En.rc b/dlls/crypt32/crypt32_En.rc index 38e3b2e91f9..f7912068e56 100644 --- a/dlls/crypt32/crypt32_En.rc +++ b/dlls/crypt32/crypt32_En.rc @@ -172,3 +172,18 @@ STRINGTABLE DISCARDABLE IDS_LOCALIZEDNAME_CA "Intermediate Certification Authorities" IDS_LOCALIZEDNAME_ADDRESSBOOK "Other People" } + +STRINGTABLE DISCARDABLE +{ + IDS_KEY_ID "KeyID=" + IDS_CERT_ISSUER "Certificate Issuer: " + IDS_CERT_SERIAL_NUMBER "Certificate Serial Number=" + IDS_ALT_NAME_OTHER_NAME "Other Name=" + IDS_ALT_NAME_RFC822_NAME "Email Address=" + IDS_ALT_NAME_DNS_NAME "DNS Name=" + IDS_ALT_NAME_DIRECTORY_NAME "Directory Name=" + IDS_ALT_NAME_URL "URL=" + IDS_ALT_NAME_IP_ADDRESS "IP Address=" + IDS_ALT_NAME_MASK "Mask=" + IDS_ALT_NAME_REGISTERED_ID "Registered ID=" +} diff --git a/dlls/crypt32/cryptres.h b/dlls/crypt32/cryptres.h index 32c81690e2c..be123bff3f5 100644 --- a/dlls/crypt32/cryptres.h +++ b/dlls/crypt32/cryptres.h @@ -165,4 +165,16 @@ #define IDS_LOCALIZEDNAME_CA 1143 #define IDS_LOCALIZEDNAME_ADDRESSBOOK 1144 +#define IDS_KEY_ID 1200 +#define IDS_CERT_ISSUER 1201 +#define IDS_CERT_SERIAL_NUMBER 1202 +#define IDS_ALT_NAME_OTHER_NAME 1203 +#define IDS_ALT_NAME_RFC822_NAME 1204 +#define IDS_ALT_NAME_DNS_NAME 1205 +#define IDS_ALT_NAME_DIRECTORY_NAME 1206 +#define IDS_ALT_NAME_URL 1207 +#define IDS_ALT_NAME_IP_ADDRESS 1208 +#define IDS_ALT_NAME_MASK 1209 +#define IDS_ALT_NAME_REGISTERED_ID 1210 + #endif /* ndef __WINE_CRYPTRES_H__ */ diff --git a/dlls/crypt32/object.c b/dlls/crypt32/object.c index e9590a899ba..76687a69b74 100644 --- a/dlls/crypt32/object.c +++ b/dlls/crypt32/object.c @@ -18,11 +18,14 @@ * 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 "crypt32_private.h" +#include "cryptres.h" #include "wine/unicode.h" #include "wine/debug.h" @@ -598,14 +601,440 @@ static BOOL WINAPI CRYPT_FormatHexString(DWORD dwCertEncodingType, return ret; } +#define MAX_STRING_RESOURCE_LEN 128 + +static BOOL CRYPT_FormatHexStringWithPrefix(CRYPT_DATA_BLOB *blob, int id, + LPWSTR str, DWORD *pcbStr) +{ + WCHAR buf[MAX_STRING_RESOURCE_LEN]; + DWORD bytesNeeded; + BOOL ret; + + LoadStringW(hInstance, id, buf, sizeof(buf) / sizeof(buf[0])); + CRYPT_FormatHexString(X509_ASN_ENCODING, 0, 0, NULL, NULL, + blob->pbData, blob->cbData, NULL, &bytesNeeded); + bytesNeeded += strlenW(buf) * sizeof(WCHAR); + if (!str) + { + *pcbStr = bytesNeeded; + ret = TRUE; + } + else if (*pcbStr < bytesNeeded) + { + *pcbStr = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + *pcbStr = bytesNeeded; + strcpyW(str, buf); + str += strlenW(str); + bytesNeeded -= strlenW(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(CRYPT_DATA_BLOB *keyId, LPWSTR str, + DWORD *pcbStr) +{ + return CRYPT_FormatHexStringWithPrefix(keyId, IDS_KEY_ID, str, pcbStr); +} + +static BOOL CRYPT_FormatCertSerialNumber(CRYPT_DATA_BLOB *serialNum, LPWSTR str, + DWORD *pcbStr) +{ + return CRYPT_FormatHexStringWithPrefix(serialNum, IDS_CERT_SERIAL_NUMBER, + str, pcbStr); +} + +static const WCHAR crlf[] = { '\r','\n',0 }; + +static BOOL CRYPT_FormatAltNameEntry(DWORD dwFormatStrType, + 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); + + switch (entry->dwAltNameChoice) + { + case CERT_ALT_NAME_RFC822_NAME: + LoadStringW(hInstance, IDS_ALT_NAME_RFC822_NAME, buf, + sizeof(buf) / sizeof(buf[0])); + bytesNeeded += strlenW(entry->u.pwszRfc822Name) * sizeof(WCHAR); + ret = TRUE; + break; + case CERT_ALT_NAME_DNS_NAME: + LoadStringW(hInstance, IDS_ALT_NAME_DNS_NAME, buf, + sizeof(buf) / sizeof(buf[0])); + bytesNeeded += strlenW(entry->u.pwszDNSName) * sizeof(WCHAR); + ret = TRUE; + break; + case CERT_ALT_NAME_URL: + LoadStringW(hInstance, IDS_ALT_NAME_URL, buf, + sizeof(buf) / sizeof(buf[0])); + bytesNeeded += strlenW(entry->u.pwszURL) * sizeof(WCHAR); + ret = TRUE; + break; + case CERT_ALT_NAME_IP_ADDRESS: + { + static const WCHAR ipAddrWithMaskFmt[] = { '%','d','.','%','d','.', + '%','d','.','%','d','/','%','d','.','%','d','.','%','d','.','%','d',0 + }; + static const WCHAR ipAddrFmt[] = { '%','d','.','%','d','.','%','d', + '.','%','d',0 }; + + LoadStringW(hInstance, IDS_ALT_NAME_IP_ADDRESS, buf, + sizeof(buf) / sizeof(buf[0])); + if (entry->u.IPAddress.cbData == 8) + { + if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) + { + LoadStringW(hInstance, IDS_ALT_NAME_MASK, mask, + sizeof(mask) / sizeof(mask[0])); + bytesNeeded += strlenW(mask) * sizeof(WCHAR); + sprintfW(ipAddrBuf, ipAddrFmt, + entry->u.IPAddress.pbData[0], + entry->u.IPAddress.pbData[1], + entry->u.IPAddress.pbData[2], + entry->u.IPAddress.pbData[3]); + bytesNeeded += strlenW(ipAddrBuf) * sizeof(WCHAR); + sprintfW(maskBuf, ipAddrFmt, + entry->u.IPAddress.pbData[4], + entry->u.IPAddress.pbData[5], + entry->u.IPAddress.pbData[6], + entry->u.IPAddress.pbData[7]); + bytesNeeded += strlenW(maskBuf) * sizeof(WCHAR); + bytesNeeded += strlenW(crlf) * sizeof(WCHAR); + } + else + { + sprintfW(ipAddrBuf, ipAddrWithMaskFmt, + 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 += (strlenW(ipAddrBuf) + 1) * sizeof(WCHAR); + } + ret = TRUE; + } + else + { + FIXME("unknown IP address format (%d bytes)\n", + entry->u.IPAddress.cbData); + ret = FALSE; + } + break; + } + default: + FIXME("unimplemented for %d\n", entry->dwAltNameChoice); + ret = FALSE; + } + if (ret) + { + bytesNeeded += strlenW(buf) * sizeof(WCHAR); + if (!str) + *pcbStr = bytesNeeded; + else if (*pcbStr < bytesNeeded) + { + *pcbStr = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + *pcbStr = bytesNeeded; + strcpyW(str, buf); + str += strlenW(str); + switch (entry->dwAltNameChoice) + { + case CERT_ALT_NAME_RFC822_NAME: + case CERT_ALT_NAME_DNS_NAME: + case CERT_ALT_NAME_URL: + strcpyW(str, entry->u.pwszURL); + break; + case CERT_ALT_NAME_IP_ADDRESS: + if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE) + { + strcpyW(str, ipAddrBuf); + str += strlenW(ipAddrBuf); + strcpyW(str, crlf); + str += strlenW(crlf); + strcpyW(str, mask); + str += strlenW(mask); + strcpyW(str, maskBuf); + } + else + strcpyW(str, ipAddrBuf); + break; + } + } + } + return ret; +} + +static const WCHAR commaSpace[] = { ',',' ',0 }; + +static BOOL CRYPT_FormatAltNameInfo(DWORD dwFormatStrType, + 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 = strlenW(crlf) * sizeof(WCHAR); + } + else + { + sep = commaSpace; + sepLen = strlenW(commaSpace) * sizeof(WCHAR); + } + + for (i = 0; ret && i < name->cAltEntry; i++) + { + ret = CRYPT_FormatAltNameEntry(dwFormatStrType, &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, + &name->rgAltEntry[i], str, &size); + if (ret) + { + str += size / sizeof(WCHAR) - 1; + if (i < name->cAltEntry - 1) + { + strcpyW(str, sep); + str += sepLen / sizeof(WCHAR); + } + } + } + } + } + return ret; +} + +static BOOL CRYPT_FormatCertIssuer(DWORD dwFormatStrType, + CERT_ALT_NAME_INFO *issuer, LPWSTR str, DWORD *pcbStr) +{ + WCHAR buf[MAX_STRING_RESOURCE_LEN]; + DWORD bytesNeeded; + BOOL ret; + + LoadStringW(hInstance, IDS_CERT_ISSUER, buf, sizeof(buf) / sizeof(buf[0])); + ret = CRYPT_FormatAltNameInfo(dwFormatStrType, issuer, NULL, &bytesNeeded); + bytesNeeded += strlenW(buf) * sizeof(WCHAR); + if (ret) + { + if (!str) + *pcbStr = bytesNeeded; + else if (*pcbStr < bytesNeeded) + { + *pcbStr = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + *pcbStr = bytesNeeded; + strcpyW(str, buf); + str += strlenW(str); + bytesNeeded -= strlenW(str) * sizeof(WCHAR); + ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 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 = strlenW(crlf) * sizeof(WCHAR); + } + else + { + sep = commaSpace; + sepLen = strlenW(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; + ret = CRYPT_FormatKeyId(&info->KeyId, str, &size); + if (ret) + str += size / sizeof(WCHAR); + } + if (info->AuthorityCertIssuer.cAltEntry) + { + if (needSeparator) + { + strcpyW(str, sep); + str += sepLen / sizeof(WCHAR); + } + needSeparator = TRUE; + ret = CRYPT_FormatCertIssuer(dwFormatStrType, + &info->AuthorityCertIssuer, str, &size); + if (ret) + str += size / sizeof(WCHAR); + } + if (info->AuthorityCertSerialNumber.cbData) + { + if (needSeparator) + { + strcpyW(str, sep); + str += sepLen / sizeof(WCHAR); + } + ret = CRYPT_FormatCertSerialNumber( + &info->AuthorityCertSerialNumber, str, &size); + } + } + } + LocalFree(info); + } + 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 (!HIWORD(lpszStructType)) + { + switch (LOWORD(lpszStructType)) + { + case LOWORD(X509_AUTHORITY_KEY_ID2): + format = CRYPT_FormatAuthorityKeyId2; + break; + } + } + else if (!strcmp(lpszStructType, szOID_AUTHORITY_KEY_IDENTIFIER2)) + format = CRYPT_FormatAuthorityKeyId2; + if (!format && !(formatStrType & CRYPT_FORMAT_STR_NO_HEX)) + format = CRYPT_FormatHexString; + return format; +} + BOOL WINAPI CryptFormatObject(DWORD dwCertEncodingType, DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat) { - static HCRYPTOIDFUNCSET set = NULL; CryptFormatObjectFunc format = NULL; HCRYPTOIDFUNCADDR hFunc = NULL; BOOL ret = FALSE; @@ -614,12 +1043,16 @@ BOOL WINAPI CryptFormatObject(DWORD dwCertEncodingType, DWORD dwFormatType, dwFormatType, dwFormatStrType, pFormatStruct, debugstr_a(lpszStructType), pbEncoded, cbEncoded, pbFormat, pcbFormat); - if (!set) - set = CryptInitOIDFunctionSet(CRYPT_OID_FORMAT_OBJECT_FUNC, 0); - CryptGetOIDFunctionAddress(set, dwCertEncodingType, lpszStructType, 0, - (void **)&format, &hFunc); - if (!format && !(dwFormatStrType & CRYPT_FORMAT_STR_NO_HEX)) - format = CRYPT_FormatHexString; + 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) ret = format(dwCertEncodingType, dwFormatType, dwFormatStrType, pFormatStruct, lpszStructType, pbEncoded, cbEncoded, pbFormat, diff --git a/dlls/crypt32/tests/main.c b/dlls/crypt32/tests/main.c index fa50327ff41..67d4725742b 100644 --- a/dlls/crypt32/tests/main.c +++ b/dlls/crypt32/tests/main.c @@ -404,7 +404,6 @@ static void test_format_object(void) */ SetLastError(0xdeadbeef); ret = pCryptFormatObject(0, 0, 0, NULL, NULL, NULL, 0, NULL, &size); - todo_wine ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError()); /* When called with the default encoding type for any undefined struct type @@ -471,7 +470,6 @@ static void test_format_object(void) SetLastError(0xdeadbeef); ret = pCryptFormatObject(X509_ASN_ENCODING, 0, 0, NULL, szOID_AUTHORITY_KEY_IDENTIFIER2, NULL, 0, NULL, &size); - todo_wine ok(!ret && GetLastError() == E_INVALIDARG, "expected E_INVALIDARG, got %d\n", GetLastError()); }