crypt32: Support CERT_NAME_SEARCH_ALL_NAMES_FLAG in CertGetNameStringW().

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Paul Gofman 2022-04-15 14:44:32 +03:00 committed by Alexandre Julliard
parent b1b9a75411
commit 0c35a851cd
3 changed files with 130 additions and 44 deletions

View File

@ -905,6 +905,39 @@ DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT cert, DWORD type,
return ret;
}
static BOOL cert_get_alt_name_info(PCCERT_CONTEXT cert, BOOL alt_name_issuer, PCERT_ALT_NAME_INFO *info)
{
static const char *oids[][2] =
{
{ szOID_SUBJECT_ALT_NAME2, szOID_SUBJECT_ALT_NAME },
{ szOID_ISSUER_ALT_NAME2, szOID_ISSUER_ALT_NAME },
};
PCERT_EXTENSION ext;
DWORD bytes = 0;
ext = CertFindExtension(oids[!!alt_name_issuer][0], cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
if (!ext)
ext = CertFindExtension(oids[!!alt_name_issuer][1], cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
if (!ext) return FALSE;
return CryptDecodeObjectEx(cert->dwCertEncodingType, X509_ALTERNATE_NAME, ext->Value.pbData, ext->Value.cbData,
CRYPT_DECODE_ALLOC_FLAG, NULL, info, &bytes);
}
static PCERT_ALT_NAME_ENTRY cert_find_next_alt_name_entry(PCERT_ALT_NAME_INFO info, DWORD entry_type,
unsigned int *index)
{
unsigned int i;
for (i = *index; i < info->cAltEntry; ++i)
if (info->rgAltEntry[i].dwAltNameChoice == entry_type)
{
*index = i + 1;
return &info->rgAltEntry[i];
}
return NULL;
}
/* Searches cert's extensions for the alternate name extension with OID
* altNameOID, and if found, searches it for the alternate name type entryType.
* If found, returns a pointer to the entry, otherwise returns NULL.
@ -915,31 +948,12 @@ DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT cert, DWORD type,
* you're done with the return value.
*/
static PCERT_ALT_NAME_ENTRY cert_find_alt_name_entry(PCCERT_CONTEXT cert, BOOL alt_name_issuer,
DWORD entryType, PCERT_ALT_NAME_INFO *info)
DWORD entry_type, PCERT_ALT_NAME_INFO *info)
{
static const char *oids[][2] =
{
{ szOID_SUBJECT_ALT_NAME2, szOID_SUBJECT_ALT_NAME },
{ szOID_ISSUER_ALT_NAME2, szOID_ISSUER_ALT_NAME },
};
PCERT_EXTENSION ext;
DWORD bytes = 0;
unsigned int i;
unsigned int index = 0;
ext = CertFindExtension(oids[!!alt_name_issuer][0], cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
if (!ext)
ext = CertFindExtension(oids[!!alt_name_issuer][1], cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
if (!ext) return NULL;
if (!CryptDecodeObjectEx(cert->dwCertEncodingType, X509_ALTERNATE_NAME, ext->Value.pbData, ext->Value.cbData,
CRYPT_DECODE_ALLOC_FLAG, NULL, info, &bytes))
return NULL;
for (i = 0; i < (*info)->cAltEntry; ++i)
if ((*info)->rgAltEntry[i].dwAltNameChoice == entryType)
return &(*info)->rgAltEntry[i];
return NULL;
if (!cert_get_alt_name_info(cert, alt_name_issuer, info)) return NULL;
return cert_find_next_alt_name_entry(*info, entry_type, &index);
}
static DWORD cert_get_name_from_rdn_attr(DWORD encodingType,
@ -978,9 +992,10 @@ static DWORD copy_output_str(WCHAR *dst, const WCHAR *src, DWORD dst_size)
DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT cert, DWORD type, DWORD flags, void *type_para,
LPWSTR name_string, DWORD name_len)
{
static const DWORD supported_flags = CERT_NAME_ISSUER_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG;
BOOL alt_name_issuer, search_all_names;
CERT_ALT_NAME_INFO *info = NULL;
PCERT_ALT_NAME_ENTRY entry;
BOOL alt_name_issuer;
PCERT_NAME_BLOB name;
DWORD ret = 0;
@ -989,6 +1004,16 @@ DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT cert, DWORD type, DWORD flags, vo
if (!cert)
goto done;
if (flags & ~supported_flags)
FIXME("Unsupported flags %#lx.\n", flags);
search_all_names = flags & CERT_NAME_SEARCH_ALL_NAMES_FLAG;
if (search_all_names && type != CERT_NAME_DNS_TYPE)
{
WARN("CERT_NAME_SEARCH_ALL_NAMES_FLAG used with type %lu.\n", type);
goto done;
}
alt_name_issuer = flags & CERT_NAME_ISSUER_FLAG;
name = alt_name_issuer ? &cert->pCertInfo->Issuer : &cert->pCertInfo->Subject;
@ -1077,15 +1102,43 @@ DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT cert, DWORD type, DWORD flags, vo
}
case CERT_NAME_DNS_TYPE:
{
entry = cert_find_alt_name_entry(cert, alt_name_issuer, CERT_ALT_NAME_DNS_NAME, &info);
unsigned int index = 0, len;
if (entry)
if (cert_get_alt_name_info(cert, alt_name_issuer, &info)
&& (entry = cert_find_next_alt_name_entry(info, CERT_ALT_NAME_DNS_NAME, &index)))
{
ret = copy_output_str(name_string, entry->u.pwszDNSName, name_len);
break;
if (search_all_names)
{
do
{
if (name_string && name_len == 1) break;
ret += len = copy_output_str(name_string, entry->u.pwszDNSName, name_len ? name_len - 1 : 0);
if (name_string && name_len)
{
name_string += len;
name_len -= len;
}
}
while ((entry = cert_find_next_alt_name_entry(info, CERT_ALT_NAME_DNS_NAME, &index)));
}
else ret = copy_output_str(name_string, entry->u.pwszDNSName, name_len);
}
else
{
if (!search_all_names || name_len != 1)
{
len = search_all_names && name_len ? name_len - 1 : name_len;
ret = cert_get_name_from_rdn_attr(cert->dwCertEncodingType, name, szOID_COMMON_NAME,
name_string, len);
if (name_string) name_string += ret;
}
}
if (search_all_names)
{
if (name_string && name_len) *name_string = 0;
++ret;
}
ret = cert_get_name_from_rdn_attr(cert->dwCertEncodingType, name, szOID_COMMON_NAME,
name_string, name_len);
break;
}
case CERT_NAME_URL_TYPE:

View File

@ -847,39 +847,63 @@ static void test_CertStrToNameW(void)
static void test_CertGetNameString_value_(unsigned int line, PCCERT_CONTEXT context, DWORD type, DWORD flags,
void *type_para, const char *expected)
{
DWORD len, retlen, expected_len;
WCHAR expectedW[512];
DWORD len, retlen;
WCHAR strW[512];
unsigned int i;
char str[512];
for (i = 0; expected[i]; ++i)
expectedW[i] = expected[i];
expectedW[i] = 0;
expected_len = 0;
while(expected[expected_len])
{
while((expectedW[expected_len] = expected[expected_len]))
++expected_len;
if (!(flags & CERT_NAME_SEARCH_ALL_NAMES_FLAG))
break;
expectedW[expected_len++] = 0;
}
expectedW[expected_len++] = 0;
len = CertGetNameStringA(context, type, flags, type_para, NULL, 0);
ok(len == strlen(expected) + 1, "line %u: unexpected length %ld.\n", line, len);
ok(len == expected_len, "line %u: unexpected length %ld, expected %ld.\n", line, len, expected_len);
memset(str, 0xcc, len);
retlen = CertGetNameStringA(context, type, flags, type_para, str, len);
ok(retlen == len, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len);
ok(!strcmp(str, expected), "line %u: unexpected value %s.\n", line, str);
ok(!memcmp(str, expected, expected_len), "line %u: unexpected value %s.\n", line, debugstr_an(str, expected_len));
str[0] = str[1] = 0xcc;
retlen = CertGetNameStringA(context, type, flags, type_para, str, len - 1);
ok(retlen == 1, "line %u: Unexpected len %lu, expected 1.\n", line, retlen);
if (len == 1) return;
ok(!str[0], "line %u: unexpected str[0] %#x.\n", line, str[0]);
ok(str[1] == expected[1], "line %u: unexpected str[1] %#x.\n", line, str[1]);
ok(!memcmp(str + 1, expected + 1, len - 2),
"line %u: str %s, string data mismatch.\n", line, debugstr_a(str + 1));
retlen = CertGetNameStringA(context, type, flags, type_para, str, 0);
ok(retlen == len, "line %u: Unexpected len %lu, expected 1.\n", line, retlen);
memset(strW, 0xcc, len * sizeof(*strW));
retlen = CertGetNameStringW(context, type, flags, type_para, strW, len);
ok(retlen == len, "line %u: unexpected len %lu, expected 1.\n", line, retlen);
ok(!wcscmp(strW, expectedW), "line %u: unexpected value %s.\n", line, debugstr_w(strW));
ok(retlen == expected_len, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, expected_len);
ok(!memcmp(strW, expectedW, len * sizeof(*strW)), "line %u: unexpected value %s.\n", line, debugstr_wn(strW, len));
strW[0] = strW[1] = 0xcccc;
retlen = CertGetNameStringW(context, type, flags, type_para, strW, len - 1);
ok(retlen == len - 1, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len - 1);
ok(!wcsncmp(strW, expectedW, retlen - 1), "line %u: string data mismatch.\n", line);
ok(!strW[retlen - 1], "line %u: string is not zero terminated.\n", line);
if (flags & CERT_NAME_SEARCH_ALL_NAMES_FLAG)
{
ok(!memcmp(strW, expectedW, (retlen - 2) * sizeof(*strW)),
"line %u: str %s, string data mismatch.\n", line, debugstr_wn(strW, retlen - 2));
ok(!strW[retlen - 2], "line %u: string is not zero terminated.\n", line);
ok(!strW[retlen - 1], "line %u: string sequence is not zero terminated.\n", line);
retlen = CertGetNameStringW(context, type, flags, type_para, strW, 1);
ok(retlen == 1, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len - 1);
ok(!strW[retlen - 1], "line %u: string sequence is not zero terminated.\n", line);
}
else
{
ok(!memcmp(strW, expectedW, (retlen - 1) * sizeof(*strW)),
"line %u: str %s, string data mismatch.\n", line, debugstr_wn(strW, retlen - 1));
ok(!strW[retlen - 1], "line %u: string is not zero terminated.\n", line);
}
retlen = CertGetNameStringA(context, type, flags, type_para, NULL, len - 1);
ok(retlen == len, "line %u: unexpected len %lu, expected %lu\n", line, retlen, len);
retlen = CertGetNameStringW(context, type, flags, type_para, NULL, len - 1);
@ -924,6 +948,9 @@ static void test_CertGetNameString(void)
test_CertGetNameString_value(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, localhost);
test_CertGetNameString_value(context, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, localhost);
test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, 0, NULL, localhost);
test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, CERT_NAME_SEARCH_ALL_NAMES_FLAG, NULL, "localhost\0");
test_CertGetNameString_value(context, CERT_NAME_EMAIL_TYPE, CERT_NAME_SEARCH_ALL_NAMES_FLAG, NULL, "");
test_CertGetNameString_value(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_SEARCH_ALL_NAMES_FLAG, NULL, "");
CertFreeCertificateContext(context);
@ -945,6 +972,10 @@ static void test_CertGetNameString(void)
test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, CERT_NAME_ISSUER_FLAG, NULL, "ex3.org");
test_CertGetNameString_value(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, "server_cn.org");
test_CertGetNameString_value(context, CERT_NAME_ATTR_TYPE, 0, (void *)szOID_SUR_NAME, "");
test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, CERT_NAME_SEARCH_ALL_NAMES_FLAG,
NULL, "ex1.org\0*.ex2.org\0");
test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, CERT_NAME_SEARCH_ALL_NAMES_FLAG | CERT_NAME_ISSUER_FLAG,
NULL, "ex3.org\0*.ex4.org\0");
CertFreeCertificateContext(context);
}

View File

@ -3351,8 +3351,10 @@ typedef struct _CTL_FIND_SUBJECT_PARA
#define CERT_NAME_URL_TYPE 7
#define CERT_NAME_UPN_TYPE 8
#define CERT_NAME_ISSUER_FLAG 0x00000001
#define CERT_NAME_DISABLE_IE4_UTF8_FLAG 0x00010000
#define CERT_NAME_ISSUER_FLAG 0x00000001
#define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x00000002
#define CERT_NAME_DISABLE_IE4_UTF8_FLAG 0x00010000
#define CERT_NAME_STR_ENABLE_PUNYCODE_FLAG 0x00200000
/* CryptFormatObject flags */
#define CRYPT_FORMAT_STR_MULTI_LINE 0x0001