Commit 00565bb35c for openssl.org
commit 00565bb35cbaddad3fdd9c2a00d6ae360b7161f5
Author: Bob Beck <beck@openssl.org>
Date: Thu Jul 16 16:13:38 2026 +0200
Stop consulting the subject DN by default in X509 name checks
do_x509_check() now matches the subject commonName (host checks) and
emailAddress (email checks) only when X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT
is set, rather than whenever no matching subjectAltName is present.
Update v3nametest to request ALWAYS_CHECK_SUBJECT for its subject-DN cases.
Fixes: openssl/project#1897
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Andrew Dinh <andrewd@openssl.org>
MergeDate: Wed Aug 26 15:21:48 2026
(Merged from https://github.com/openssl/openssl/pull/31982)
diff --git a/CHANGES.md b/CHANGES.md
index 7e04ce4b4b..6a1523e593 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -279,6 +279,15 @@ OpenSSL Releases
*Marcel Cornu and Tomasz Kantecki*
+ * X509 certificate verification no longer consults the subject
+ distinguished name by default. Previously, when a certificate
+ contained no subject alternative name of the type being checked, the
+ subject commonName (for host name checks) or emailAddress (for email
+ checks) was matched instead. This fallback now happens only when the
+ `X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT` flag is set.
+
+ *Bob Beck*
+
* Changed the output of the -disabled option for the list command.
Displaying disabled features, protocols, and algorithms, in relevant sections.
Disabled features are now generated at configuration time.
diff --git a/crypto/x509/v3_utl.c b/crypto/x509/v3_utl.c
index 4ccfcacadb..70f4bd8e34 100644
--- a/crypto/x509/v3_utl.c
+++ b/crypto/x509/v3_utl.c
@@ -874,7 +874,6 @@ static int do_x509_check(const X509 *x, const char *chk, size_t chklen,
int i;
int cnid = NID_undef;
int alt_type;
- int san_present = 0;
int rv = 0;
equal_fn equal;
@@ -934,15 +933,8 @@ static int do_x509_check(const X509 *x, const char *chk, size_t chklen,
* SmtpUTF8Mailbox is encoded as UTF8String.
*
* If it is not a UTF8String then that is unexpected, and
- * we ignore the invalid SAN (neither set san_present nor
- * consider it a candidate for equality). This does mean
- * that the subject CN may be considered, as would be the
- * case when the malformed SmtpUtf8Mailbox SAN is instead
- * simply absent.
- *
- * When CN-ID matching is not desirable, applications can
- * choose to turn it off, doing so is at this time a best
- * practice.
+ * we ignore the invalid SAN, so it is not considered a
+ * candidate for equality.
*/
if (othername_nid != NID_id_on_SmtpUTF8Mailbox
|| gen->d.otherName->value->type != V_ASN1_UTF8STRING)
@@ -968,7 +960,6 @@ static int do_x509_check(const X509 *x, const char *chk, size_t chklen,
cstr = gen->d.iPAddress;
break;
}
- san_present = 1;
/* Positive on success, negative on error! */
if ((rv = do_check_string(cstr, alt_type, equal, flags,
chk, chklen, peername))
@@ -978,12 +969,17 @@ static int do_x509_check(const X509 *x, const char *chk, size_t chklen,
GENERAL_NAMES_free(gens);
if (rv != 0)
return rv;
- if (san_present && !(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT))
- return 0;
}
- /* We're done if CN-ID is not pertinent */
- if (cnid == NID_undef || (flags & X509_CHECK_FLAG_NEVER_CHECK_SUBJECT))
+ /*
+ * The subject DN is not consulted by default: the subject commonName or
+ * emailAddress is matched only when the caller explicitly opts in with
+ * X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, and never when
+ * X509_CHECK_FLAG_NEVER_CHECK_SUBJECT is set.
+ */
+ if (cnid == NID_undef
+ || (flags & X509_CHECK_FLAG_NEVER_CHECK_SUBJECT) != 0
+ || (flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) == 0)
return 0;
i = -1;
diff --git a/doc/man3/X509_VERIFY_PARAM_set1_host.pod b/doc/man3/X509_VERIFY_PARAM_set1_host.pod
index 76608a18d6..d21096ad44 100644
--- a/doc/man3/X509_VERIFY_PARAM_set1_host.pod
+++ b/doc/man3/X509_VERIFY_PARAM_set1_host.pod
@@ -277,14 +277,14 @@ checks are performed. Configuring a list for a given name type implicitly
enables the corresponding check; clearing it (by passing NULL or the
empty string to the C<set1_> form) disables it.
-Unless suppressed by the host flags, the configured hostnames are also
-matched against the B<commonName> attribute of the certificate's subject
-distinguished name, and the configured RFC 822 email addresses against the
-subject B<emailAddress> attribute. SMTPUTF8 email and IP addresses have no
-subject distinguished name counterpart and are matched only against the
-subject alternative name extension. See L<X509_VERIFY_PARAM_set_hostflags(3)>
-and L<X509_check_host(3)> for the flags that govern whether and when the
-subject distinguished name is consulted.
+By default only the subject alternative name extension is consulted. When
+B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT> is set, the configured hostnames
+are also matched against the B<commonName> attribute of the certificate's
+subject distinguished name, and the configured RFC 822 email addresses
+against the subject B<emailAddress> attribute. SMTPUTF8 email and IP
+addresses have no subject distinguished name counterpart and are always
+matched only against the subject alternative name extension. See
+L<X509_VERIFY_PARAM_set_hostflags(3)> and L<X509_check_host(3)>.
=head1 SEE ALSO
diff --git a/doc/man3/X509_VERIFY_PARAM_set_hostflags.pod b/doc/man3/X509_VERIFY_PARAM_set_hostflags.pod
index 714334d886..bfe6d155f4 100644
--- a/doc/man3/X509_VERIFY_PARAM_set_hostflags.pod
+++ b/doc/man3/X509_VERIFY_PARAM_set_hostflags.pod
@@ -43,21 +43,19 @@ The I<flags> default to 0. They may be set to a bitwise OR of the following:
=back
-The B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT> flag causes the function
-to consider the subject DN even if the certificate contains at least
-one subject alternative name of the right type (DNS name or email
-address as appropriate); the default is to ignore the subject DN
-when at least one corresponding subject alternative names is present.
-
-The B<X509_CHECK_FLAG_NEVER_CHECK_SUBJECT> flag causes the function to never
-consider the subject DN even if the certificate contains no subject alternative
-names of the right type (DNS name or email address as appropriate); the default
-is to use the subject DN when no corresponding subject alternative names are
-present.
-
-If both B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT> and
-B<X509_CHECK_FLAG_NEVER_CHECK_SUBJECT> are specified, the latter takes
-precedence and the subject DN is not checked for matching names.
+By default the subject distinguished name is not consulted; matching is
+performed only against the subject alternative name extension.
+
+The B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT> flag causes the function to
+also match against the subject DN (the B<commonName> attribute for DNS
+names, the B<emailAddress> attribute for email addresses), whether or not
+the certificate contains a subject alternative name of the corresponding
+type.
+
+The B<X509_CHECK_FLAG_NEVER_CHECK_SUBJECT> flag suppresses matching against
+the subject DN. As that is already the default, this flag has an effect
+only in combination with B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT>, over
+which it takes precedence.
If set, B<X509_CHECK_FLAG_NO_WILDCARDS> disables wildcard
expansion.
diff --git a/doc/man3/X509_check_host.pod b/doc/man3/X509_check_host.pod
index 6b095f9582..e43dddbbe5 100644
--- a/doc/man3/X509_check_host.pod
+++ b/doc/man3/X509_check_host.pod
@@ -24,13 +24,14 @@ The validity of the certificate and its trust level has to be checked by
other means.
X509_check_host() checks if the certificate Subject Alternative
-Name (SAN) or Subject CommonName (CN) matches the specified hostname,
-which must be encoded in the preferred name syntax described
-in section 3.5 of RFC 1034. By default, wildcards are supported
+Name (SAN) matches the specified hostname, and, when
+B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT> is set, the Subject CommonName
+(CN) as well. The hostname must be encoded in the preferred name syntax
+described in section 3.5 of RFC 1034. By default, wildcards are supported
and they match only in the left-most label; but they may match
part of that label with an explicit prefix or suffix. For example,
by default, the host B<name> "www.example.com" would match a
-certificate with a SAN or CN value of "*.example.com", "w*.example.com"
+certificate with a SAN value of "*.example.com", "w*.example.com"
or "*w.example.com".
Per section 6.4.2 of RFC 6125, B<name> values representing international
@@ -87,20 +88,19 @@ flags:
=back
-The B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT> flag causes the function
-to consider the subject DN even if the certificate contains at least
-one subject alternative name of the right type (DNS name or email
-address as appropriate); the default is to ignore the subject DN
-when at least one corresponding subject alternative names is present.
-
-The B<X509_CHECK_FLAG_NEVER_CHECK_SUBJECT> flag causes the function to never
-consider the subject DN even if the certificate contains no subject alternative
-names of the right type (DNS name or email address as appropriate); the default
-is to use the subject DN when no corresponding subject alternative names are
-present.
-If both B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT> and
-B<X509_CHECK_FLAG_NEVER_CHECK_SUBJECT> are specified, the latter takes
-precedence and the subject DN is not checked for matching names.
+By default the subject distinguished name is not consulted; matching is
+performed only against the subject alternative name extension.
+
+The B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT> flag causes the function to
+also match against the subject DN (the B<commonName> attribute for DNS
+names, the B<emailAddress> attribute for email addresses), whether or not
+the certificate contains a subject alternative name of the corresponding
+type.
+
+The B<X509_CHECK_FLAG_NEVER_CHECK_SUBJECT> flag suppresses matching against
+the subject DN. As that is already the default, this flag has an effect
+only in combination with B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT>, over
+which it takes precedence.
If set, B<X509_CHECK_FLAG_NO_WILDCARDS> disables wildcard
expansion; this only applies to B<X509_check_host>.
diff --git a/test/v3nametest.c b/test/v3nametest.c
index 82777e6086..bfdee4d309 100644
--- a/test/v3nametest.c
+++ b/test/v3nametest.c
@@ -212,17 +212,18 @@ struct set_name_fn {
const char *name;
int host;
int email;
+ int subject; /* name is in the subject DN, so needs ALWAYS_CHECK_SUBJECT */
};
#if !defined(OPENSSL_NO_DEPRECATED_4_1)
OSSL_BEGIN_ALLOW_DEPRECATED
static const struct set_name_fn name_fns[] = {
- { set_cn1, "set CN", 1, 0 },
- { set_email1, "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 },
+ { set_cn1, "set CN", 1, 0, 1 },
+ { set_email1, "set emailAddress", 0, 1, 1 },
+ { set_altname_dns, "set dnsName", 1, 0, 0 },
+ { set_altname_dns2, "set dnsName", 1, 0, 0 },
+ { set_altname_email, "set rfc822Name", 0, 1, 0 },
+ { set_altname_email2, "set rfc822Name", 0, 1, 0 },
};
static X509 *make_cert(void)
@@ -259,6 +260,7 @@ static int run_cert(X509 *crt, const char *nameincert,
{
const char *const *pname = names;
int failed = 0;
+ unsigned int subj = fn->subject ? X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT : 0;
for (; *pname != NULL; ++pname) {
int samename = OPENSSL_strcasecmp(nameincert, *pname) == 0;
@@ -271,7 +273,7 @@ static int run_cert(X509 *crt, const char *nameincert,
memcpy(name, *pname, namelen + 1);
match = -1;
- if (!TEST_int_ge(ret = X509_check_host(crt, name, namelen, 0, NULL),
+ if (!TEST_int_ge(ret = X509_check_host(crt, name, namelen, subj, NULL),
0)) {
failed = 1;
} else if (fn->host) {
@@ -286,7 +288,7 @@ static int run_cert(X509 *crt, const char *nameincert,
match = -1;
if (!TEST_int_ge(ret = X509_check_host(crt, name, namelen,
- X509_CHECK_FLAG_NO_WILDCARDS,
+ X509_CHECK_FLAG_NO_WILDCARDS | subj,
NULL),
0)) {
failed = 1;
@@ -302,7 +304,7 @@ static int run_cert(X509 *crt, const char *nameincert,
failed = 1;
match = -1;
- ret = X509_check_email(crt, name, namelen, 0);
+ ret = X509_check_email(crt, name, namelen, subj);
if (fn->email) {
if (ret && !samename)
match = 1;
@@ -395,9 +397,9 @@ static int test_long_names(void)
* 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.)
+ * alternative name of the corresponding type is present. In particular the
+ * default, flags == 0, matches neither, even when the certificate has no
+ * subject alternative name at all.
*/
static int test_check_subject_flags(void)
{
@@ -407,6 +409,7 @@ static int test_check_subject_flags(void)
/* 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, 0, NULL), 0)
|| !TEST_int_eq(X509_check_host(crt, "example.com", 0,
X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, NULL),
1)
@@ -441,6 +444,7 @@ static int test_check_subject_flags(void)
/* 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, 0), 0)
|| !TEST_int_eq(X509_check_email(crt, "user@example.com", 0,
X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT),
1)