summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/libcrypto/x509v3/v3_utl.c425
-rw-r--r--src/lib/libcrypto/x509v3/x509v3.h29
2 files changed, 452 insertions, 2 deletions
diff --git a/src/lib/libcrypto/x509v3/v3_utl.c b/src/lib/libcrypto/x509v3/v3_utl.c
index ee135a0b52..7516cd3c20 100644
--- a/src/lib/libcrypto/x509v3/v3_utl.c
+++ b/src/lib/libcrypto/x509v3/v3_utl.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: v3_utl.c,v 1.24 2015/02/07 13:19:15 doug Exp $ */ 1/* $OpenBSD: v3_utl.c,v 1.25 2016/09/03 11:56:33 beck Exp $ */
2/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 2/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
3 * project. 3 * project.
4 */ 4 */
@@ -638,6 +638,429 @@ X509_email_free(STACK_OF(OPENSSL_STRING) *sk)
638 sk_OPENSSL_STRING_pop_free(sk, str_free); 638 sk_OPENSSL_STRING_pop_free(sk, str_free);
639} 639}
640 640
641typedef int (*equal_fn) (const unsigned char *pattern, size_t pattern_len,
642 const unsigned char *subject, size_t subject_len, unsigned int flags);
643
644/* Skip pattern prefix to match "wildcard" subject */
645static void skip_prefix(const unsigned char **p, size_t *plen,
646 const unsigned char *subject, size_t subject_len, unsigned int flags)
647{
648 const unsigned char *pattern = *p;
649 size_t pattern_len = *plen;
650
651 /*
652 * If subject starts with a leading '.' followed by more octets, and
653 * pattern is longer, compare just an equal-length suffix with the
654 * full subject (starting at the '.'), provided the prefix contains
655 * no NULs.
656 */
657 if ((flags & _X509_CHECK_FLAG_DOT_SUBDOMAINS) == 0)
658 return;
659
660 while (pattern_len > subject_len && *pattern) {
661 if ((flags & X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS) &&
662 *pattern == '.')
663 break;
664 ++pattern;
665 --pattern_len;
666 }
667
668 /* Skip if entire prefix acceptable */
669 if (pattern_len == subject_len) {
670 *p = pattern;
671 *plen = pattern_len;
672 }
673}
674
675/*
676 * Open/BoringSSL uses memcmp for "equal_case" while their
677 * "equal_nocase" function is a hand-rolled strncasecmp that does not
678 * allow \0 in the pattern. Since an embedded \0 is likely a sign of
679 * problems, we simply don't allow it in either case, and then we use
680 * standard libc funcitons.
681 */
682
683/* Compare using strncasecmp */
684static int equal_nocase(const unsigned char *pattern, size_t pattern_len,
685 const unsigned char *subject, size_t subject_len,
686 unsigned int flags)
687{
688 if (memchr(pattern, '\0', pattern_len) != NULL)
689 return 0;
690 if (memchr(subject, '\0', subject_len) != NULL)
691 return 0;
692 skip_prefix(&pattern, &pattern_len, subject, subject_len, flags);
693 if (pattern_len != subject_len)
694 return 0;
695 return (strncasecmp(pattern, subject, pattern_len) == 0);
696}
697
698/* Compare using strncmp. */
699static int equal_case(const unsigned char *pattern, size_t pattern_len,
700 const unsigned char *subject, size_t subject_len,
701 unsigned int flags)
702{
703 if (memchr(pattern, 0, pattern_len) != NULL)
704 return 0;
705 if (memchr(subject, 0, subject_len) != NULL)
706 return 0;
707 skip_prefix(&pattern, &pattern_len, subject, subject_len, flags);
708 if (pattern_len != subject_len)
709 return 0;
710 return (strncmp(pattern, subject, pattern_len) == 0);
711}
712
713/*
714 * RFC 5280, section 7.5, requires that only the domain is compared in a
715 * case-insensitive manner.
716 */
717static int equal_email(const unsigned char *a, size_t a_len,
718 const unsigned char *b, size_t b_len,
719 unsigned int unused_flags)
720{
721 size_t pos = a_len;
722 if (a_len != b_len)
723 return 0;
724 /*
725 * We search backwards for the '@' character, so that we do not have to
726 * deal with quoted local-parts. The domain part is compared in a
727 * case-insensitive manner.
728 */
729 while (pos > 0) {
730 pos--;
731 if (a[pos] == '@' || b[pos] == '@') {
732 if (!equal_nocase(a + pos, a_len - pos, b + pos, a_len - pos, 0))
733 return 0;
734 break;
735 }
736 }
737 if (pos == 0)
738 pos = a_len;
739 return equal_case(a, pos, b, pos, 0);
740}
741
742/*
743 * Compare the prefix and suffix with the subject, and check that the
744 * characters in-between are valid.
745 */
746static int wildcard_match(const unsigned char *prefix, size_t prefix_len,
747 const unsigned char *suffix, size_t suffix_len,
748 const unsigned char *subject, size_t subject_len, unsigned int flags)
749{
750 const unsigned char *wildcard_start;
751 const unsigned char *wildcard_end;
752 const unsigned char *p;
753 int allow_multi = 0;
754 int allow_idna = 0;
755
756 if (subject_len < prefix_len + suffix_len)
757 return 0;
758 if (!equal_nocase(prefix, prefix_len, subject, prefix_len, flags))
759 return 0;
760 wildcard_start = subject + prefix_len;
761 wildcard_end = subject + (subject_len - suffix_len);
762 if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len, flags))
763 return 0;
764 /*
765 * If the wildcard makes up the entire first label, it must match at
766 * least one character.
767 */
768 if (prefix_len == 0 && *suffix == '.') {
769 if (wildcard_start == wildcard_end)
770 return 0;
771 allow_idna = 1;
772 if (flags & X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS)
773 allow_multi = 1;
774 }
775 /* IDNA labels cannot match partial wildcards */
776 if (!allow_idna &&
777 subject_len >= 4
778 && strncasecmp((char *)subject, "xn--", 4) == 0)
779 return 0;
780 /* The wildcard may match a literal '*' */
781 if (wildcard_end == wildcard_start + 1 && *wildcard_start == '*')
782 return 1;
783 /*
784 * Check that the part matched by the wildcard contains only
785 * permitted characters and only matches a single label unless
786 * allow_multi is set.
787 */
788 for (p = wildcard_start; p != wildcard_end; ++p)
789 if (!(('0' <= *p && *p <= '9') || ('A' <= *p && *p <= 'Z') ||
790 ('a' <= *p && *p <= 'z') || *p == '-' ||
791 (allow_multi && *p == '.')))
792 return 0;
793 return 1;
794}
795
796#define LABEL_START (1 << 0)
797#define LABEL_END (1 << 1)
798#define LABEL_HYPHEN (1 << 2)
799#define LABEL_IDNA (1 << 3)
800
801static const unsigned char *valid_star(const unsigned char *p, size_t len,
802 unsigned int flags)
803{
804 const unsigned char *star = 0;
805 size_t i;
806 int state = LABEL_START;
807 int dots = 0;
808 for (i = 0; i < len; ++i) {
809 /*
810 * Locate first and only legal wildcard, either at the start
811 * or end of a non-IDNA first and not final label.
812 */
813 if (p[i] == '*') {
814 int atstart = (state & LABEL_START);
815 int atend = (i == len - 1 || p[i + 1] == '.');
816 /*
817 * At most one wildcard per pattern.
818 * No wildcards in IDNA labels.
819 * No wildcards after the first label.
820 */
821 if (star != NULL || (state & LABEL_IDNA) != 0 || dots)
822 return NULL;
823 /* Only full-label '*.example.com' wildcards? */
824 if ((flags & X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS)
825 && (!atstart || !atend))
826 return NULL;
827 /* No 'foo*bar' wildcards */
828 if (!atstart && !atend)
829 return NULL;
830 star = &p[i];
831 state &= ~LABEL_START;
832 } else if ((state & LABEL_START) != 0) {
833 /*
834 * At the start of a label, skip any "xn--" and
835 * remain in the LABEL_START state, but set the
836 * IDNA label state
837 */
838 if ((state & LABEL_IDNA) == 0 && len - i >= 4
839 && strncasecmp((char *)&p[i], "xn--", 4) == 0) {
840 i += 3;
841 state |= LABEL_IDNA;
842 continue;
843 }
844 /* Labels must start with a letter or digit */
845 state &= ~LABEL_START;
846 if (('a' <= p[i] && p[i] <= 'z')
847 || ('A' <= p[i] && p[i] <= 'Z')
848 || ('0' <= p[i] && p[i] <= '9'))
849 continue;
850 return NULL;
851 } else if (('a' <= p[i] && p[i] <= 'z')
852 || ('A' <= p[i] && p[i] <= 'Z')
853 || ('0' <= p[i] && p[i] <= '9')) {
854 state &= LABEL_IDNA;
855 continue;
856 } else if (p[i] == '.') {
857 if (state & (LABEL_HYPHEN | LABEL_START))
858 return NULL;
859 state = LABEL_START;
860 ++dots;
861 } else if (p[i] == '-') {
862 /* no domain/subdomain starts with '-' */
863 if ((state & LABEL_START) != 0)
864 return NULL;
865 state |= LABEL_HYPHEN;
866 } else
867 return NULL;
868 }
869
870 /*
871 * The final label must not end in a hyphen or ".", and
872 * there must be at least two dots after the star.
873 */
874 if ((state & (LABEL_START | LABEL_HYPHEN)) != 0 || dots < 2)
875 return NULL;
876 return star;
877}
878
879/* Compare using wildcards. */
880static int equal_wildcard(const unsigned char *pattern, size_t pattern_len,
881 const unsigned char *subject, size_t subject_len, unsigned int flags)
882{
883 const unsigned char *star = NULL;
884
885 /*
886 * Subject names starting with '.' can only match a wildcard pattern
887 * via a subject sub-domain pattern suffix match.
888 */
889 if (!(subject_len > 1 && subject[0] == '.'))
890 star = valid_star(pattern, pattern_len, flags);
891 if (star == NULL)
892 return equal_nocase(pattern, pattern_len,
893 subject, subject_len, flags);
894 return wildcard_match(pattern, star - pattern,
895 star + 1, (pattern + pattern_len) - star - 1,
896 subject, subject_len, flags);
897}
898
899/*
900 * Compare an ASN1_STRING to a supplied string. If they match return 1. If
901 * cmp_type > 0 only compare if string matches the type, otherwise convert it
902 * to UTF8.
903 */
904
905static int
906do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
907 unsigned int flags, const char *b, size_t blen, char **peername)
908{
909 int rv = 0;
910
911 if (!a->data || !a->length)
912 return 0;
913 if (cmp_type > 0) {
914 if (cmp_type != a->type)
915 return 0;
916 if (cmp_type == V_ASN1_IA5STRING)
917 rv = equal(a->data, a->length, (unsigned char *)b,
918 blen, flags);
919 else if (a->length == (int)blen && !memcmp(a->data, b, blen))
920 rv = 1;
921 if (rv > 0 && peername &&
922 (*peername = strndup((char *)a->data, a->length)) == NULL)
923 rv = -1;
924 } else {
925 int astrlen;
926 unsigned char *astr;
927 astrlen = ASN1_STRING_to_UTF8(&astr, a);
928 if (astrlen < 0)
929 return -1;
930 rv = equal(astr, astrlen, (unsigned char *)b, blen, flags);
931 if (rv > 0 && peername &&
932 (*peername = strndup((char *)astr, astrlen)) == NULL)
933 rv = -1;
934 free(astr);
935 }
936 return rv;
937}
938
939static int do_x509_check(X509 *x, const char *chk, size_t chklen,
940 unsigned int flags, int check_type, char **peername)
941{
942 GENERAL_NAMES *gens = NULL;
943 X509_NAME *name = NULL;
944 size_t i;
945 int j;
946 int cnid = NID_undef;
947 int alt_type;
948 int san_present = 0;
949 int rv = 0;
950 equal_fn equal;
951
952 /* See below, this flag is internal-only */
953 flags &= ~_X509_CHECK_FLAG_DOT_SUBDOMAINS;
954 if (check_type == GEN_EMAIL) {
955 cnid = NID_pkcs9_emailAddress;
956 alt_type = V_ASN1_IA5STRING;
957 equal = equal_email;
958 } else if (check_type == GEN_DNS) {
959 cnid = NID_commonName;
960 /* Implicit client-side DNS sub-domain pattern */
961 if (chklen > 1 && chk[0] == '.')
962 flags |= _X509_CHECK_FLAG_DOT_SUBDOMAINS;
963 alt_type = V_ASN1_IA5STRING;
964 if (flags & X509_CHECK_FLAG_NO_WILDCARDS)
965 equal = equal_nocase;
966 else
967 equal = equal_wildcard;
968 } else {
969 alt_type = V_ASN1_OCTET_STRING;
970 equal = equal_case;
971 }
972
973 gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
974 if (gens != NULL) {
975 for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
976 GENERAL_NAME *gen;
977 ASN1_STRING *cstr;
978 gen = sk_GENERAL_NAME_value(gens, i);
979 if (gen->type != check_type)
980 continue;
981 san_present = 1;
982 if (check_type == GEN_EMAIL)
983 cstr = gen->d.rfc822Name;
984 else if (check_type == GEN_DNS)
985 cstr = gen->d.dNSName;
986 else
987 cstr = gen->d.iPAddress;
988 /* Positive on success, negative on error! */
989 if ((rv = do_check_string(cstr, alt_type, equal, flags,
990 chk, chklen, peername)) != 0)
991 break;
992 }
993 GENERAL_NAMES_free(gens);
994 if (rv != 0)
995 return rv;
996 if (cnid == NID_undef ||
997 (san_present &&
998 !(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT)))
999 return 0;
1000 }
1001
1002 /* We're done if CN-ID is not pertinent */
1003 if (cnid == NID_undef)
1004 return 0;
1005
1006 j = -1;
1007 name = X509_get_subject_name(x);
1008 while ((j = X509_NAME_get_index_by_NID(name, cnid, j)) >= 0) {
1009 X509_NAME_ENTRY *ne;
1010 ASN1_STRING *str;
1011 if ((ne = X509_NAME_get_entry(name, j)) == NULL)
1012 return -1;
1013 if ((str = X509_NAME_ENTRY_get_data(ne)) == NULL)
1014 return -1;
1015 /* Positive on success, negative on error! */
1016 if ((rv = do_check_string(str, -1, equal, flags,
1017 chk, chklen, peername)) != 0)
1018 return rv;
1019 }
1020 return 0;
1021}
1022
1023int X509_check_host(X509 *x, const char *chk, size_t chklen,
1024 unsigned int flags, char **peername)
1025{
1026 if (chk == NULL)
1027 return -2;
1028 if (memchr(chk, '\0', chklen))
1029 return -2;
1030 return do_x509_check(x, chk, chklen, flags, GEN_DNS, peername);
1031}
1032
1033int X509_check_email(X509 *x, const char *chk, size_t chklen,
1034 unsigned int flags)
1035{
1036 if (chk == NULL)
1037 return -2;
1038 if (memchr(chk, '\0', chklen))
1039 return -2;
1040 return do_x509_check(x, chk, chklen, flags, GEN_EMAIL, NULL);
1041}
1042
1043int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen,
1044 unsigned int flags)
1045{
1046 if (chk == NULL)
1047 return -2;
1048 return do_x509_check(x, (char *)chk, chklen, flags, GEN_IPADD, NULL);
1049}
1050
1051int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags)
1052{
1053 unsigned char ipout[16];
1054 size_t iplen;
1055
1056 if (ipasc == NULL)
1057 return -2;
1058 iplen = (size_t)a2i_ipadd(ipout, ipasc);
1059 if (iplen == 0)
1060 return -2;
1061 return do_x509_check(x, (char *)ipout, iplen, flags, GEN_IPADD, NULL);
1062}
1063
641/* Convert IP addresses both IPv4 and IPv6 into an 1064/* Convert IP addresses both IPv4 and IPv6 into an
642 * OCTET STRING compatible with RFC3280. 1065 * OCTET STRING compatible with RFC3280.
643 */ 1066 */
diff --git a/src/lib/libcrypto/x509v3/x509v3.h b/src/lib/libcrypto/x509v3/x509v3.h
index b45626a885..c13342f349 100644
--- a/src/lib/libcrypto/x509v3/x509v3.h
+++ b/src/lib/libcrypto/x509v3/x509v3.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: x509v3.h,v 1.16 2015/02/10 13:28:17 jsing Exp $ */ 1/* $OpenBSD: x509v3.h,v 1.17 2016/09/03 11:56:33 beck Exp $ */
2/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 2/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
3 * project 1999. 3 * project 1999.
4 */ 4 */
@@ -701,6 +701,33 @@ STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x);
701void X509_email_free(STACK_OF(OPENSSL_STRING) *sk); 701void X509_email_free(STACK_OF(OPENSSL_STRING) *sk);
702STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x); 702STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x);
703 703
704/* Flags for X509_check_* functions */
705/* Always check subject name for host match even if subject alt names present */
706#define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 0x1
707/* Disable wildcard matching for dnsName fields and common name. */
708#define X509_CHECK_FLAG_NO_WILDCARDS 0x2
709/* Wildcards must not match a partial label. */
710#define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0x4
711/* Allow (non-partial) wildcards to match multiple labels. */
712#define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS 0x8
713/* Constraint verifier subdomain patterns to match a single labels. */
714#define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0x10
715
716/*
717 * Match reference identifiers starting with "." to any sub-domain.
718 * This is a non-public flag, turned on implicitly when the subject
719 * reference identity is a DNS name.
720 */
721#define _X509_CHECK_FLAG_DOT_SUBDOMAINS 0x8000
722
723int X509_check_host(X509 *x, const char *chk, size_t chklen,
724 unsigned int flags, char **peername);
725int X509_check_email(X509 *x, const char *chk, size_t chklen,
726 unsigned int flags);
727int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen,
728 unsigned int flags);
729int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags);
730
704ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc); 731ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc);
705ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc); 732ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc);
706int a2i_ipadd(unsigned char *ipout, const char *ipasc); 733int a2i_ipadd(unsigned char *ipout, const char *ipasc);