From: Dmitry Belyavskiy Date: Wed, 21 Aug 2019 15:33:14 +0000 (+0300) Subject: RFC 8398: Name constraints validation X-Git-Tag: openssl-3.0.0-alpha7~481 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=69d9245996088c485072aa4d4e86baec49ccaa80;p=thirdparty%2Fopenssl.git RFC 8398: Name constraints validation Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/9654) --- diff --git a/crypto/x509/v3_ncons.c b/crypto/x509/v3_ncons.c index 4543ec2e110..8da9cca24db 100644 --- a/crypto/x509/v3_ncons.c +++ b/crypto/x509/v3_ncons.c @@ -17,6 +17,7 @@ #include #include "crypto/x509.h" +#include "crypto/punycode.h" #include "ext_dat.h" DEFINE_STACK_OF(CONF_VALUE) @@ -38,6 +39,7 @@ static int nc_match_single(GENERAL_NAME *sub, GENERAL_NAME *gen); static int nc_dn(const X509_NAME *sub, const X509_NAME *nm); static int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns); static int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml); +static int nc_email_eai(ASN1_UTF8STRING *sub, ASN1_IA5STRING *eml); static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base); static int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base); @@ -459,6 +461,14 @@ static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) { GENERAL_SUBTREE *sub; int i, r, match = 0; + /* + * We need to compare not gen->type field but an "effective" type because + * the otherName field may contain EAI email address treated specially + * according to RFC 8398, section 6 + */ + int effective_type = ((gen->type == GEN_OTHERNAME) && + (OBJ_obj2nid(gen->d.otherName->type_id) == + NID_id_on_SmtpUTF8Mailbox)) ? GEN_EMAIL : gen->type; /* * Permitted subtrees: if any subtrees exist of matching the type at @@ -467,7 +477,7 @@ static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) { sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i); - if (gen->type != sub->base->type) + if (effective_type != sub->base->type) continue; if (!nc_minmax_valid(sub)) return X509_V_ERR_SUBTREE_MINMAX; @@ -490,7 +500,7 @@ static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) { sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i); - if (gen->type != sub->base->type) + if (effective_type != sub->base->type) continue; if (!nc_minmax_valid(sub)) return X509_V_ERR_SUBTREE_MINMAX; @@ -509,7 +519,14 @@ static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base) { - switch (base->type) { + switch (gen->type) { + case GEN_OTHERNAME: + /* + * We are here only when we have SmtpUTF8 name, + * so we match the value of othername with base->d.rfc822Name + */ + return nc_email_eai(gen->d.otherName->value->value.utf8string, + base->d.rfc822Name); case GEN_DIRNAME: return nc_dn(gen->d.directoryName, base->d.directoryName); @@ -577,13 +594,59 @@ static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base) } +/* + * This function implements comparison between ASCII/U-label in eml + * and A-label in base according to RFC 8398, section 6. + * Convert base to U-label and ASCII-parts of domain names, for base + * Octet-to-octet comparison of `eml` and `base` hostname parts + * (ASCII-parts should be compared in case-insensitive manner) + */ +static int nc_email_eai(ASN1_UTF8STRING *eml, ASN1_IA5STRING *base) +{ + const char *baseptr = (char *)base->data; + const char *emlptr = (char *)eml->data; + const char *emlat = strrchr(emlptr, '@'); + + char ulabel[256]; + size_t size = sizeof(ulabel) - 1; + + if (emlat == NULL) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + + memset(ulabel, 0, sizeof(ulabel)); + /* Special case: initial '.' is RHS match */ + if (*baseptr == '.') { + ulabel[0] = '.'; + size -= 1; + if (ossl_a2ulabel(baseptr, ulabel + 1, &size) <= 0) + return X509_V_ERR_UNSPECIFIED; + + if ((size_t)eml->length > size + 1) { + emlptr += eml->length - (size + 1); + if (ia5casecmp(ulabel, emlptr) == 0) + return X509_V_OK; + } + return X509_V_ERR_PERMITTED_VIOLATION; + } + + emlptr = emlat + 1; + if (ossl_a2ulabel(baseptr, ulabel, &size) <= 0) + return X509_V_ERR_UNSPECIFIED; + /* Just have hostname left to match: case insensitive */ + if (ia5casecmp(ulabel, emlptr)) + return X509_V_ERR_PERMITTED_VIOLATION; + + return X509_V_OK; + +} + static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base) { const char *baseptr = (char *)base->data; const char *emlptr = (char *)eml->data; - const char *baseat = strchr(baseptr, '@'); - const char *emlat = strchr(emlptr, '@'); + const char *baseat = strrchr(baseptr, '@'); + const char *emlat = strrchr(emlptr, '@'); if (!emlat) return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; /* Special case: initial '.' is RHS match */