From: Bob Beck Date: Thu, 16 Jul 2026 13:30:27 +0000 (+0200) Subject: Rework v3nametest for SAN-first matching, add flag and long-name coverage X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;ds=inline;p=thirdparty%2Fopenssl.git Rework v3nametest for SAN-first matching, add flag and long-name coverage Drive the match matrix primarily through SAN dNSName/rfc822Name rather than subject CN/emailAddress (subject setters cut to one each, decoy multi-SAN variants added). Add explicit coverage of ALWAYS/NEVER_CHECK_ SUBJECT and the non-matching-SAN-plus-matching-subject case, and a long-name test for the 64/68-byte dNSName boundary and truncation guards. Test only; no behavioural change. Reviewed-by: Neil Horman Reviewed-by: Andrew Dinh MergeDate: Thu Jul 30 09:11:28 2026 (Merged from https://github.com/openssl/openssl/pull/31981) --- diff --git a/test/v3nametest.c b/test/v3nametest.c index 757db8a18e..cfa8376bbb 100644 --- a/test/v3nametest.c +++ b/test/v3nametest.c @@ -180,55 +180,29 @@ static int set_cn1(X509 *crt, const char *name) return set_cn(crt, NID_commonName, name, 0); } -static int set_cn_and_email(X509 *crt, const char *name) -{ - return set_cn(crt, NID_commonName, name, - NID_pkcs9_emailAddress, "dummy@example.com", 0); -} - -static int set_cn2(X509 *crt, const char *name) -{ - return set_cn(crt, NID_commonName, "dummy value", - NID_commonName, name, 0); -} - -static int set_cn3(X509 *crt, const char *name) -{ - return set_cn(crt, NID_commonName, name, - NID_commonName, "dummy value", 0); -} - static int set_email1(X509 *crt, const char *name) { return set_cn(crt, NID_pkcs9_emailAddress, name, 0); } -static int set_email2(X509 *crt, const char *name) -{ - return set_cn(crt, NID_pkcs9_emailAddress, "dummy@example.com", - NID_pkcs9_emailAddress, name, 0); -} - -static int set_email3(X509 *crt, const char *name) +static int set_altname_dns(X509 *crt, const char *name) { - return set_cn(crt, NID_pkcs9_emailAddress, name, - NID_pkcs9_emailAddress, "dummy@example.com", 0); + return set_altname(crt, GEN_DNS, name, 0); } -static int set_email_and_cn(X509 *crt, const char *name) +static int set_altname_dns2(X509 *crt, const char *name) { - return set_cn(crt, NID_pkcs9_emailAddress, name, - NID_commonName, "www.example.org", 0); + return set_altname(crt, GEN_DNS, "dummy.example.org", GEN_DNS, name, 0); } -static int set_altname_dns(X509 *crt, const char *name) +static int set_altname_email(X509 *crt, const char *name) { - return set_altname(crt, GEN_DNS, name, 0); + return set_altname(crt, GEN_EMAIL, name, 0); } -static int set_altname_email(X509 *crt, const char *name) +static int set_altname_email2(X509 *crt, const char *name) { - return set_altname(crt, GEN_EMAIL, name, 0); + return set_altname(crt, GEN_EMAIL, "dummy@example.com", GEN_EMAIL, name, 0); } OSSL_END_ALLOW_DEPRECATED #endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */ @@ -244,15 +218,11 @@ struct set_name_fn { OSSL_BEGIN_ALLOW_DEPRECATED static const struct set_name_fn name_fns[] = { { set_cn1, "set CN", 1, 0 }, - { set_cn2, "set CN", 1, 0 }, - { set_cn3, "set CN", 1, 0 }, - { set_cn_and_email, "set CN", 1, 0 }, { set_email1, "set emailAddress", 0, 1 }, - { set_email2, "set emailAddress", 0, 1 }, - { set_email3, "set emailAddress", 0, 1 }, - { set_email_and_cn, "set emailAddress", 0, 1 }, { set_altname_dns, "set dnsName", 1, 0 }, + { set_altname_dns2, "set dnsName", 1, 0 }, { set_altname_email, "set rfc822Name", 0, 1 }, + { set_altname_email2, "set rfc822Name", 0, 1 }, }; static X509 *make_cert(void) @@ -365,6 +335,139 @@ static int call_run_cert(int i) } return failed == 0; } + +/* + * name64 is a well-formed dNSName exactly 64 bytes long; name68 is the same + * 64 bytes followed by a further label, so it too is a well-formed dNSName + * but 68 bytes long. The 64-byte prefix of name68 is thus a valid, distinct + * name -- a check that only compared the first 64 bytes would wrongly treat + * the two as equal. + */ +static const char name64[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com"; +static const char name68[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com.net"; + +static int test_long_names(void) +{ + X509 *crt = NULL; + int failed = 0; + + /* + * A dNSName may exceed 64 bytes. Each name matches itself but not the + * other: the 64-byte name must not match the 68-byte certificate, nor + * vice versa (no truncation to 64 bytes). + */ + if (!TEST_ptr(crt = make_cert()) + || !TEST_true(set_altname_dns(crt, name68)) + || !TEST_int_eq(X509_check_host(crt, name68, 0, 0, NULL), 1) + || !TEST_int_eq(X509_check_host(crt, name64, 0, 0, NULL), 0)) + failed = 1; + X509_free(crt); + crt = NULL; + + if (!TEST_ptr(crt = make_cert()) + || !TEST_true(set_altname_dns(crt, name64)) + || !TEST_int_eq(X509_check_host(crt, name64, 0, 0, NULL), 1) + || !TEST_int_eq(X509_check_host(crt, name68, 0, 0, NULL), 0)) + failed = 1; + X509_free(crt); + crt = NULL; + + /* + * The 64-byte name can be a commonName and is matched when subject + * checking is requested; the 68-byte name, which shares its first 64 + * bytes, must not match it. + */ + if (!TEST_ptr(crt = make_cert()) + || !TEST_true(set_cn1(crt, name64)) + || !TEST_int_eq(X509_check_host(crt, name64, 0, + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, NULL), + 1) + || !TEST_int_eq(X509_check_host(crt, name68, 0, + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, NULL), + 0)) + failed = 1; + X509_free(crt); + + return failed == 0; +} + +/* + * The subject commonName / emailAddress is consulted during verification + * only when X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT is set, and never when + * X509_CHECK_FLAG_NEVER_CHECK_SUBJECT is set -- whether or not a subject + * alternative name of the corresponding type is present. (The default, + * flags == 0, is exercised by the "set CN" / "set emailAddress" entries in + * the main matrix.) + */ +static int test_check_subject_flags(void) +{ + X509 *crt = NULL; + int failed = 0; + + /* Subject commonName, no SAN. */ + if (!TEST_ptr(crt = make_cert()) + || !TEST_true(set_cn1(crt, "example.com")) + || !TEST_int_eq(X509_check_host(crt, "example.com", 0, + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, NULL), + 1) + || !TEST_int_eq(X509_check_host(crt, "example.com", 0, + X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, NULL), + 0)) + failed = 1; + X509_free(crt); + crt = NULL; + + /* + * A non-matching dNSName SAN alongside a matching commonName: the SAN + * matches its own name, the requested name does not match the SAN, and + * the commonName is consulted only under ALWAYS_CHECK_SUBJECT -- never + * by default, despite a SAN being present. + */ + if (!TEST_ptr(crt = make_cert()) + || !TEST_true(set_cn1(crt, "example.com")) + || !TEST_true(set_altname_dns(crt, "san.example.net")) + || !TEST_int_eq(X509_check_host(crt, "san.example.net", 0, 0, NULL), 1) + || !TEST_int_eq(X509_check_host(crt, "example.com", 0, 0, NULL), 0) + || !TEST_int_eq(X509_check_host(crt, "example.com", 0, + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, NULL), + 1) + || !TEST_int_eq(X509_check_host(crt, "example.com", 0, + X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, NULL), + 0)) + failed = 1; + X509_free(crt); + crt = NULL; + + /* Subject emailAddress, no SAN. */ + if (!TEST_ptr(crt = make_cert()) + || !TEST_true(set_email1(crt, "user@example.com")) + || !TEST_int_eq(X509_check_email(crt, "user@example.com", 0, + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT), + 1) + || !TEST_int_eq(X509_check_email(crt, "user@example.com", 0, + X509_CHECK_FLAG_NEVER_CHECK_SUBJECT), + 0)) + failed = 1; + X509_free(crt); + crt = NULL; + + /* A non-matching rfc822Name SAN alongside a matching subject emailAddress. */ + if (!TEST_ptr(crt = make_cert()) + || !TEST_true(set_email1(crt, "user@example.com")) + || !TEST_true(set_altname_email(crt, "san@example.net")) + || !TEST_int_eq(X509_check_email(crt, "san@example.net", 0, 0), 1) + || !TEST_int_eq(X509_check_email(crt, "user@example.com", 0, 0), 0) + || !TEST_int_eq(X509_check_email(crt, "user@example.com", 0, + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT), + 1) + || !TEST_int_eq(X509_check_email(crt, "user@example.com", 0, + X509_CHECK_FLAG_NEVER_CHECK_SUBJECT), + 0)) + failed = 1; + X509_free(crt); + + return failed == 0; +} OSSL_END_ALLOW_DEPRECATED #endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */ @@ -668,6 +771,8 @@ int setup_tests(void) { #if !defined(OPENSSL_NO_DEPRECATED_4_1) ADD_ALL_TESTS(call_run_cert, OSSL_NELEM(name_fns)); + ADD_TEST(test_long_names); + ADD_TEST(test_check_subject_flags); #endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */ ADD_TEST(test_GENERAL_NAME_cmp); return 1;