Commit 1a65972ad0 for openssl.org
commit 1a65972ad0b5e3a02e33de3cd7bda61764067710
Author: Bob Beck <beck@openssl.org>
Date: Fri May 29 05:39:40 2026 -0600
Add documentation for NAME_CONSTRAINTS_check
We document which names and name constraints will be evaluated
as well as the limits that will be placed on the evauluation on
a per certificate basis.
We call out in the BUGS section that the RFC 5280 requires a byte
per byte match of name constraints unless the higher level protocol
has defines a different matching method for wildcards. This
"deferall of specification" and corresponding lack of specification
by upper level protocols means that across implementations encountering
the default behaviour is to be expected, and that therefore relying
on excluded names to constrain signers in a PKI from signing wildcards
is ill advised.
This is then cross referenced in the documentation for X509_verify_cert
and the maximum possible comparisons which can be forces in a certificate
validtion noted in the BUGS section of X509_verify_cert.
Fixes: https://github.com/openssl/openssl/issues/30706
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
MergeDate: Wed Jun 24 13:03:54 2026
(Merged from https://github.com/openssl/openssl/pull/31334)
diff --git a/doc/build.info b/doc/build.info
index 6e8dfedb5c..44b06941e4 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -1499,6 +1499,10 @@ DEPEND[html/man3/MDC2_Init.html]=man3/MDC2_Init.pod
GENERATE[html/man3/MDC2_Init.html]=man3/MDC2_Init.pod
DEPEND[man/man3/MDC2_Init.3]=man3/MDC2_Init.pod
GENERATE[man/man3/MDC2_Init.3]=man3/MDC2_Init.pod
+DEPEND[html/man3/NAME_CONSTRAINTS_check.html]=man3/NAME_CONSTRAINTS_check.pod
+GENERATE[html/man3/NAME_CONSTRAINTS_check.html]=man3/NAME_CONSTRAINTS_check.pod
+DEPEND[man/man3/NAME_CONSTRAINTS_check.3]=man3/NAME_CONSTRAINTS_check.pod
+GENERATE[man/man3/NAME_CONSTRAINTS_check.3]=man3/NAME_CONSTRAINTS_check.pod
DEPEND[html/man3/NCONF_new_ex.html]=man3/NCONF_new_ex.pod
GENERATE[html/man3/NCONF_new_ex.html]=man3/NCONF_new_ex.pod
DEPEND[man/man3/NCONF_new_ex.3]=man3/NCONF_new_ex.pod
@@ -3426,6 +3430,7 @@ html/man3/GENERAL_NAME.html \
html/man3/HMAC.html \
html/man3/MD5.html \
html/man3/MDC2_Init.html \
+html/man3/NAME_CONSTRAINTS_check.html \
html/man3/NCONF_new_ex.html \
html/man3/OBJ_nid2obj.html \
html/man3/OCSP_REQUEST_new.html \
@@ -4101,6 +4106,7 @@ man/man3/GENERAL_NAME.3 \
man/man3/HMAC.3 \
man/man3/MD5.3 \
man/man3/MDC2_Init.3 \
+man/man3/NAME_CONSTRAINTS_check.3 \
man/man3/NCONF_new_ex.3 \
man/man3/OBJ_nid2obj.3 \
man/man3/OCSP_REQUEST_new.3 \
diff --git a/doc/man3/NAME_CONSTRAINTS_check.pod b/doc/man3/NAME_CONSTRAINTS_check.pod
new file mode 100644
index 0000000000..3dfe39ea29
--- /dev/null
+++ b/doc/man3/NAME_CONSTRAINTS_check.pod
@@ -0,0 +1,211 @@
+=pod
+
+=head1 NAME
+
+NAME_CONSTRAINTS_check,
+NAME_CONSTRAINTS_check_CN - check a certificate's names against a name
+constraints extension
+
+=head1 SYNOPSIS
+
+ #include <openssl/x509v3.h>
+
+ int NAME_CONSTRAINTS_check(const X509 *x, NAME_CONSTRAINTS *nc);
+ int NAME_CONSTRAINTS_check_CN(const X509 *x, NAME_CONSTRAINTS *nc);
+
+=head1 DESCRIPTION
+
+NAME_CONSTRAINTS_check() tests whether the names asserted by certificate
+I<x> satisfy the name constraints I<nc>. It implements the matching
+primitive of RFC 5280 section 4.2.1.10: given a constraint set (a
+B<NAME_CONSTRAINTS> structure containing zero or more B<permittedSubtrees>
+and B<excludedSubtrees>) and a candidate certificate, decide whether the
+certificate's names fall within the permitted subtrees and outside the
+excluded subtrees.
+
+The names considered by NAME_CONSTRAINTS_check() are:
+
+=over 4
+
+=item *
+
+The certificate's subject distinguished name, matched as a B<directoryName>
+general-name type. The subject is considered only when it is nonempty.
+
+=item *
+
+Each B<emailAddress> attribute appearing within the subject distinguished
+name, matched as an B<rfc822Name> general-name type. These attributes are
+the historical, pre-SAN way of expressing an email address in a
+certificate's subject, and RFC 5280 requires that they be subjected to
+name-constraint checking.
+
+=item *
+
+Each entry in the certificate's subject alternative name extension, matched
+according to its declared general-name type.
+
+=back
+
+NAME_CONSTRAINTS_check() implements matching for the following
+general-name types: B<directoryName>, B<dNSName>, B<rfc822Name>,
+B<uniformResourceIdentifier>, and B<iPAddress>. The B<otherName> form
+B<id-on-SmtpUTF8Mailbox> (RFC 8398) is additionally matched against
+B<rfc822Name> subtrees. Any other general-name type, including
+B<x400Address>, B<ediPartyName>, B<registeredID>, and other B<otherName>
+forms, yields B<X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE>.
+
+For each name considered, the function evaluates two conditions:
+
+=over 4
+
+=item *
+
+If I<nc> contains at least one B<permittedSubtree> of the same general-name
+type as the name, the name must match at least one of those permitted
+subtrees. If I<nc> contains no permitted subtrees of that type, no
+permitted-subtrees test is imposed on names of that type.
+
+=item *
+
+The name must not match any B<excludedSubtree> of the same general-name
+type in I<nc>.
+
+=back
+
+The function returns at the first violation encountered; it does not
+collect or report multiple failures.
+
+For B<dNSName> entries, matching follows the byte/label algorithm of
+RFC 5280 section 4.2.1.10, which RFC 5280 mandates when no
+protocol-specific matching rules apply. Because this match is performed
+without awareness of any specific higher-level protocol, additional
+matching rules defined by later or more specific protocols must be
+applied independently of this function to the certificate chain.
+
+NAME_CONSTRAINTS_check() performs only the constraint match for a single
+certificate against a single constraint set. It does B<not> perform the
+chain-wide enforcement of RFC 5280 section 6.1.4(g)-(j): callers wishing to
+enforce name constraints across an entire certification path must walk the
+chain themselves and apply each ancestor's constraint set to certificates
+lower in the chain, observing the usual exceptions (for example,
+self-issued intermediate certificates are exempt from constraints imposed
+by certificates above them, except when they are the leaf of the chain).
+For full RFC 5280 name-constraint enforcement integrated with chain
+validation, applications should use L<X509_verify_cert(3)>, which performs
+this internally.
+
+NAME_CONSTRAINTS_check() enforces an implementation limit on the product
+of the certificate's name count and the constraint set's subtree count, to
+prevent computationally expensive matching on pathological input. If that
+limit is exceeded the function returns B<X509_V_ERR_UNSPECIFIED> without
+performing any matching. The current limit is 2**20 (1,048,576) on the
+product of the name count (subject DN entries plus B<subjectAltName>
+entries) and the subtree count (B<permittedSubtrees> plus
+B<excludedSubtrees>).
+
+=head1 RETURN VALUES
+
+NAME_CONSTRAINTS_check() returns B<X509_V_OK> if every name considered
+satisfies the constraints. Otherwise it returns one of the following
+B<X509_V_ERR_*> codes:
+
+=over 4
+
+=item B<X509_V_ERR_PERMITTED_VIOLATION>
+
+A name of a type for which I<nc> contains at least one permitted subtree
+failed to match any of those subtrees.
+
+=item B<X509_V_ERR_EXCLUDED_VIOLATION>
+
+A name matched an excluded subtree.
+
+=item B<X509_V_ERR_SUBTREE_MINMAX>
+
+A subtree in I<nc> specified a B<minimum> other than 0 or a B<maximum> at
+all. RFC 5280 requires that these B<GeneralSubtree> fields not be used,
+and a constraint set that uses them cannot be processed.
+
+=item B<X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE>
+
+A general-name type for which matching is not implemented was encountered.
+The list of supported types is given in the DESCRIPTION above.
+
+=item B<X509_V_ERR_UNSUPPORTED_NAME_SYNTAX>
+
+A name in the certificate is encoded in a way that cannot be matched (for
+example, an B<emailAddress> attribute in the subject that is not encoded
+as an B<IA5String>).
+
+=item B<X509_V_ERR_UNSPECIFIED>
+
+The product of the certificate's name count and the constraint set's
+subtree count exceeded the implementation limit; no matching was
+performed.
+
+=back
+
+Other B<X509_V_ERR_*> codes may be returned by deeper name-matching
+helpers (for example, codes arising from individual general-name type
+comparisons). Callers should treat the return value as the authoritative
+success/failure signal and treat any value other than B<X509_V_OK> as a
+failure, rather than enumerating the specific codes above.
+
+=head1 NOTES
+
+NAME_CONSTRAINTS_check() does not match the certificate's commonName
+against B<dNSName> name constraints; that check is provided by a separate
+function, B<NAME_CONSTRAINTS_check_CN>(). The commonName-as-DNS-identity
+practice is a legacy concern: modern certificates assert DNS identities
+through B<dNSName> entries in the subject alternative name extension,
+which NAME_CONSTRAINTS_check() already covers. NAME_CONSTRAINTS_check_CN()
+is required only for older certificates that express a DNS identity
+through their commonName instead of, or in addition to, the SAN; for
+certificates conforming to modern profiles a call to NAME_CONSTRAINTS_check()
+alone is generally sufficient.
+
+=head1 BUGS
+
+RFC 9525's wildcard semantics apply only to presented-identifier
+matching for TLS service identity, and explicitly call out they are not
+valid for any other purpose; they do not define wildcard handling
+for name-constraint matching. NAME_CONSTRAINTS_check() therefore
+follows RFC 5280's requirements for when this is undefined, and treats
+the B<*> character in a B<dNSName> as a literal label component, per
+the RFC 5280 algorithm, which is often contrary to caller expectation.
+
+Even if specified in the future, due to the "fallback implementation"
+nature of matching wildcards in SAN B<dNSName> entries specified by
+RFC 5280, name constraint behaviour in the presence of wildcards
+should not be strictly relied upon across implementations and
+protocols. This matters most for the use of B<excluded names>
+constraints, which should not be relied upon to reliably constrain
+signing certificates for a PKI in a security dependent manner unless
+the consumers of these certificates are themselves known to be
+constrained by other means to only use implementations that provide
+different semantics, or the PKI can be constrained by other means to
+ensure that wildcards are never issued from such signing
+certificates.
+
+=head1 SEE ALSO
+
+L<X509_verify_cert(3)>,
+L<X509_VERIFY_PARAM_set_flags(3)>
+
+=head1 HISTORY
+
+NAME_CONSTRAINTS_check() was added in OpenSSL 1.0.0.
+
+NAME_CONSTRAINTS_check_CN() was added in OpenSSL 1.1.0.
+
+=head1 COPYRIGHT
+
+Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License"). You may not use
+this file except in compliance with the License. You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/X509_verify_cert.pod b/doc/man3/X509_verify_cert.pod
index 5beb41f560..aaaaace4d6 100644
--- a/doc/man3/X509_verify_cert.pod
+++ b/doc/man3/X509_verify_cert.pod
@@ -104,7 +104,9 @@ terminator.
=item 7.
-B<Name constraint validation> per RFC 5280 section 4.2.1.10.
+B<Name constraint validation> per RFC 5280 section 4.2.1.10; see
+L<NAME_CONSTRAINTS_check(3)> for details of the matching primitive and
+the general-name types it covers.
=item 8.
@@ -450,8 +452,17 @@ above it. Callers should nevertheless be aware that the chain returned in
this mode does not necessarily terminate at an RFC 5280-style trust
anchor.
+L<NAME_CONSTRAINTS_check(3)> caps each per-pair check at 2**20
+comparisons, but the chain orchestrator issues B<N>(B<N>-1)/2 such
+checks for an B<N>-certificate chain. An adversary submitting a
+maximally constructed chain can therefore force up to approximately
+6.3 million name-constraint comparisons in a four-certificate chain
+(one leaf and three name-constrained signers), or approximately 5.2
+billion at the default chain-depth limit of 100.
+
=head1 SEE ALSO
+L<NAME_CONSTRAINTS_check(3)>,
L<SSL_add_expected_rpk(3)>,
L<SSL_CTX_dane_enable(3)>,
L<SSL_dane_tlsa_add(3)>,
diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt
index 57a255a4a5..5e8126d12c 100644
--- a/util/missingcrypto.txt
+++ b/util/missingcrypto.txt
@@ -570,8 +570,6 @@ LONG_it(3)
MD2_options(3)
MD4_Transform(3)
MD5_Transform(3)
-NAME_CONSTRAINTS_check(3)
-NAME_CONSTRAINTS_check_CN(3)
NAME_CONSTRAINTS_it(3)
NAMING_AUTHORITY_it(3)
NCONF_WIN32(3)