TLS/SSL and crypto library
リビジョン | 69d9245996088c485072aa4d4e86baec49ccaa80 (tree) |
---|---|
日時 | 2020-08-26 20:04:17 |
作者 | Dmitry Belyavskiy <beldmit@gmai...> |
コミッター | Dmitry Belyavskiy |
RFC 8398: Name constraints validation
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9654)
@@ -17,6 +17,7 @@ | ||
17 | 17 | #include <openssl/bn.h> |
18 | 18 | |
19 | 19 | #include "crypto/x509.h" |
20 | +#include "crypto/punycode.h" | |
20 | 21 | #include "ext_dat.h" |
21 | 22 | |
22 | 23 | DEFINE_STACK_OF(CONF_VALUE) |
@@ -38,6 +39,7 @@ static int nc_match_single(GENERAL_NAME *sub, GENERAL_NAME *gen); | ||
38 | 39 | static int nc_dn(const X509_NAME *sub, const X509_NAME *nm); |
39 | 40 | static int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns); |
40 | 41 | static int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml); |
42 | +static int nc_email_eai(ASN1_UTF8STRING *sub, ASN1_IA5STRING *eml); | |
41 | 43 | static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base); |
42 | 44 | static int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base); |
43 | 45 |
@@ -459,6 +461,14 @@ static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) | ||
459 | 461 | { |
460 | 462 | GENERAL_SUBTREE *sub; |
461 | 463 | int i, r, match = 0; |
464 | + /* | |
465 | + * We need to compare not gen->type field but an "effective" type because | |
466 | + * the otherName field may contain EAI email address treated specially | |
467 | + * according to RFC 8398, section 6 | |
468 | + */ | |
469 | + int effective_type = ((gen->type == GEN_OTHERNAME) && | |
470 | + (OBJ_obj2nid(gen->d.otherName->type_id) == | |
471 | + NID_id_on_SmtpUTF8Mailbox)) ? GEN_EMAIL : gen->type; | |
462 | 472 | |
463 | 473 | /* |
464 | 474 | * 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) | ||
467 | 477 | |
468 | 478 | for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) { |
469 | 479 | sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i); |
470 | - if (gen->type != sub->base->type) | |
480 | + if (effective_type != sub->base->type) | |
471 | 481 | continue; |
472 | 482 | if (!nc_minmax_valid(sub)) |
473 | 483 | return X509_V_ERR_SUBTREE_MINMAX; |
@@ -490,7 +500,7 @@ static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) | ||
490 | 500 | |
491 | 501 | for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) { |
492 | 502 | sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i); |
493 | - if (gen->type != sub->base->type) | |
503 | + if (effective_type != sub->base->type) | |
494 | 504 | continue; |
495 | 505 | if (!nc_minmax_valid(sub)) |
496 | 506 | return X509_V_ERR_SUBTREE_MINMAX; |
@@ -509,7 +519,14 @@ static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) | ||
509 | 519 | |
510 | 520 | static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base) |
511 | 521 | { |
512 | - switch (base->type) { | |
522 | + switch (gen->type) { | |
523 | + case GEN_OTHERNAME: | |
524 | + /* | |
525 | + * We are here only when we have SmtpUTF8 name, | |
526 | + * so we match the value of othername with base->d.rfc822Name | |
527 | + */ | |
528 | + return nc_email_eai(gen->d.otherName->value->value.utf8string, | |
529 | + base->d.rfc822Name); | |
513 | 530 | case GEN_DIRNAME: |
514 | 531 | return nc_dn(gen->d.directoryName, base->d.directoryName); |
515 | 532 |
@@ -577,13 +594,59 @@ static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base) | ||
577 | 594 | |
578 | 595 | } |
579 | 596 | |
597 | +/* | |
598 | + * This function implements comparison between ASCII/U-label in eml | |
599 | + * and A-label in base according to RFC 8398, section 6. | |
600 | + * Convert base to U-label and ASCII-parts of domain names, for base | |
601 | + * Octet-to-octet comparison of `eml` and `base` hostname parts | |
602 | + * (ASCII-parts should be compared in case-insensitive manner) | |
603 | + */ | |
604 | +static int nc_email_eai(ASN1_UTF8STRING *eml, ASN1_IA5STRING *base) | |
605 | +{ | |
606 | + const char *baseptr = (char *)base->data; | |
607 | + const char *emlptr = (char *)eml->data; | |
608 | + const char *emlat = strrchr(emlptr, '@'); | |
609 | + | |
610 | + char ulabel[256]; | |
611 | + size_t size = sizeof(ulabel) - 1; | |
612 | + | |
613 | + if (emlat == NULL) | |
614 | + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | |
615 | + | |
616 | + memset(ulabel, 0, sizeof(ulabel)); | |
617 | + /* Special case: initial '.' is RHS match */ | |
618 | + if (*baseptr == '.') { | |
619 | + ulabel[0] = '.'; | |
620 | + size -= 1; | |
621 | + if (ossl_a2ulabel(baseptr, ulabel + 1, &size) <= 0) | |
622 | + return X509_V_ERR_UNSPECIFIED; | |
623 | + | |
624 | + if ((size_t)eml->length > size + 1) { | |
625 | + emlptr += eml->length - (size + 1); | |
626 | + if (ia5casecmp(ulabel, emlptr) == 0) | |
627 | + return X509_V_OK; | |
628 | + } | |
629 | + return X509_V_ERR_PERMITTED_VIOLATION; | |
630 | + } | |
631 | + | |
632 | + emlptr = emlat + 1; | |
633 | + if (ossl_a2ulabel(baseptr, ulabel, &size) <= 0) | |
634 | + return X509_V_ERR_UNSPECIFIED; | |
635 | + /* Just have hostname left to match: case insensitive */ | |
636 | + if (ia5casecmp(ulabel, emlptr)) | |
637 | + return X509_V_ERR_PERMITTED_VIOLATION; | |
638 | + | |
639 | + return X509_V_OK; | |
640 | + | |
641 | +} | |
642 | + | |
580 | 643 | static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base) |
581 | 644 | { |
582 | 645 | const char *baseptr = (char *)base->data; |
583 | 646 | const char *emlptr = (char *)eml->data; |
584 | 647 | |
585 | - const char *baseat = strchr(baseptr, '@'); | |
586 | - const char *emlat = strchr(emlptr, '@'); | |
648 | + const char *baseat = strrchr(baseptr, '@'); | |
649 | + const char *emlat = strrchr(emlptr, '@'); | |
587 | 650 | if (!emlat) |
588 | 651 | return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; |
589 | 652 | /* Special case: initial '.' is RHS match */ |