diff options
author | Adam Langley <agl@chromium.org> | 2014-06-20 23:00:00 +0400 |
---|---|---|
committer | Adam Langley <agl@chromium.org> | 2014-06-21 00:17:42 +0400 |
commit | 8241005bfa9b49ebe3c60c85a6b30b97eeb3884e (patch) | |
tree | fbc1aa6da05fcc4f14c07e8013a5590f54339ad9 /crypto | |
parent | 4c65f3a2f12b9aa300a08242f03117f32e1f628a (diff) |
Client-side namecheck wildcards.
A client reference identity of ".example.com" matches a server certificate
presented identity that is any sub-domain of "example.com" (e.g.
"www.sub.example.com).
With the X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS flag, it matches only direct
child sub-domains (e.g. "www.sub.example.com"). (cherry picked from commit
e52c52f10bb8e34aaf8f28f3e5b56939e8f6b357)
(Imported from upstream's 3cc8a3f2343cda796de90c127b9e907ca3ec2da5)
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/x509v3/v3_utl.c | 59 | ||||
-rw-r--r-- | crypto/x509v3/v3nametest.c | 11 | ||||
-rw-r--r-- | crypto/x509v3/x509v3.h | 8 |
3 files changed, 72 insertions, 6 deletions
diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c index e1f8ef12..10b3d6b1 100644 --- a/crypto/x509v3/v3_utl.c +++ b/crypto/x509v3/v3_utl.c @@ -579,11 +579,50 @@ typedef int (*equal_fn)(const unsigned char *pattern, size_t pattern_len, const unsigned char *subject, size_t subject_len, unsigned int flags); +/* Skip pattern prefix to match "wildcard" subject */ +static void skip_prefix(const unsigned char **p, size_t *plen, + const unsigned char *subject, size_t subject_len, + unsigned int flags) + { + const unsigned char *pattern = *p; + size_t pattern_len = *plen; + + /* + * If subject starts with a leading '.' followed by more octets, and + * pattern is longer, compare just an equal-length suffix with the + * full subject (starting at the '.'), provided the prefix contains + * no NULs. (We check again that subject starts with '.' and + * contains at least one subsequent character, just in case the + * internal _X509_CHECK_FLAG_DOT_SUBDOMAINS flag was erroneously + * set by the user). + */ + if ((flags & _X509_CHECK_FLAG_DOT_SUBDOMAINS) == 0 || + subject_len <= 1 || subject[0] != '.') + return; + + while (pattern_len > subject_len && *pattern) + { + if ((flags & X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS) && + *pattern == '.') + break; + ++pattern; + --pattern_len; + } + + /* Skip if entire prefix acceptable */ + if (pattern_len == subject_len) + { + *p = pattern; + *plen = pattern_len; + } + } + /* Compare while ASCII ignoring case. */ static int equal_nocase(const unsigned char *pattern, size_t pattern_len, const unsigned char *subject, size_t subject_len, - unsigned int unused_flags) + unsigned int flags) { + skip_prefix(&pattern, &pattern_len, subject, subject_len, flags); if (pattern_len != subject_len) return 0; while (pattern_len) @@ -612,11 +651,9 @@ static int equal_nocase(const unsigned char *pattern, size_t pattern_len, /* Compare using memcmp. */ static int equal_case(const unsigned char *pattern, size_t pattern_len, const unsigned char *subject, size_t subject_len, - unsigned int unused_flags) + unsigned int flags) { - /* The pattern must not contain NUL characters. */ - if (memchr(pattern, '\0', pattern_len) != NULL) - return 0; + skip_prefix(&pattern, &pattern_len, subject, subject_len, flags); if (pattern_len != subject_len) return 0; return !memcmp(pattern, subject, pattern_len); @@ -804,7 +841,14 @@ static int equal_wildcard(const unsigned char *pattern, size_t pattern_len, const unsigned char *subject, size_t subject_len, unsigned int flags) { - const unsigned char *star = valid_star(pattern, pattern_len, flags); + const unsigned char *star = NULL; + + /* + * Subject names starting with '.' can only match a wildcard pattern + * via a subject sub-domain pattern suffix match. + */ + if (!(subject_len > 1 && subject[0] == '.')) + star = valid_star(pattern, pattern_len, flags); if (star == NULL) return equal_nocase(pattern, pattern_len, subject, subject_len, flags); @@ -868,6 +912,9 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen, else if (check_type == GEN_DNS) { cnid = NID_commonName; + /* Implicit client-side DNS sub-domain pattern */ + if (chklen > 1 && chk[0] == '.') + flags |= _X509_CHECK_FLAG_DOT_SUBDOMAINS; alt_type = V_ASN1_IA5STRING; if (flags & X509_CHECK_FLAG_NO_WILDCARDS) equal = equal_nocase; diff --git a/crypto/x509v3/v3nametest.c b/crypto/x509v3/v3nametest.c index bdba29bc..d0d97ff4 100644 --- a/crypto/x509v3/v3nametest.c +++ b/crypto/x509v3/v3nametest.c @@ -67,6 +67,7 @@ static const char *const names[] = "*@example.com", "test@*.example.com", "example.com", "www.example.com", "test.www.example.com", "*.example.com", "*.www.example.com", "test.*.example.com", "www.*.com", + ".www.example.com", "*www.example.com", "example.net", "xn--rger-koa.example.com", "a.example.com", "b.example.com", "postmaster@example.com", "Postmaster@example.com", @@ -81,6 +82,11 @@ static const char *const exceptions[] = "set CN: host: [*.example.com] matches [www.example.com]", "set CN: host: [*.example.com] matches [xn--rger-koa.example.com]", "set CN: host: [*.www.example.com] matches [test.www.example.com]", + "set CN: host: [*.www.example.com] matches [.www.example.com]", + "set CN: host: [*www.example.com] matches [www.example.com]", + "set CN: host: [test.www.example.com] matches [.www.example.com]", + "set CN: host-no-wildcards: [*.www.example.com] matches [.www.example.com]", + "set CN: host-no-wildcards: [test.www.example.com] matches [.www.example.com]", "set emailAddress: email: [postmaster@example.com] does not match [Postmaster@example.com]", "set emailAddress: email: [postmaster@EXAMPLE.COM] does not match [Postmaster@example.com]", "set emailAddress: email: [Postmaster@example.com] does not match [postmaster@example.com]", @@ -90,6 +96,11 @@ static const char *const exceptions[] = "set dnsName: host: [*.example.com] matches [b.example.com]", "set dnsName: host: [*.example.com] matches [xn--rger-koa.example.com]", "set dnsName: host: [*.www.example.com] matches [test.www.example.com]", + "set dnsName: host-no-wildcards: [*.www.example.com] matches [.www.example.com]", + "set dnsName: host-no-wildcards: [test.www.example.com] matches [.www.example.com]", + "set dnsName: host: [*.www.example.com] matches [.www.example.com]", + "set dnsName: host: [*www.example.com] matches [www.example.com]", + "set dnsName: host: [test.www.example.com] matches [.www.example.com]", "set rfc822Name: email: [postmaster@example.com] does not match [Postmaster@example.com]", "set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@example.com]", "set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]", diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h index e7890831..d74a4449 100644 --- a/crypto/x509v3/x509v3.h +++ b/crypto/x509v3/x509v3.h @@ -704,6 +704,14 @@ STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x); #define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0x4 /* Allow (non-partial) wildcards to match multiple labels. */ #define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS 0x8 +/* Constraint verifier subdomain patterns to match a single labels. */ +#define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0x10 +/* + * Match reference identifiers starting with "." to any sub-domain. + * This is a non-public flag, turned on implicitly when the subject + * reference identity is a DNS name. + */ +#define _X509_CHECK_FLAG_DOT_SUBDOMAINS 0x8000 int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen, unsigned int flags); |