Fixes to host checking.
authorViktor Dukhovni <viktor@dukhovni.org>
Wed, 21 May 2014 09:57:44 +0000 (10:57 +0100)
committerDr. Stephen Henson <steve@openssl.org>
Wed, 21 May 2014 10:31:28 +0000 (11:31 +0100)
Fixes to host checking wild card support and add support for
setting host checking flags when verifying a certificate
chain.

crypto/x509/vpm_int.h
crypto/x509/x509_vfy.c
crypto/x509/x509_vfy.h
crypto/x509/x509_vpm.c
crypto/x509v3/v3_utl.c
crypto/x509v3/v3nametest.c
crypto/x509v3/x509v3.h
doc/crypto/X509_VERIFY_PARAM_set_flags.pod
doc/crypto/X509_check_host.pod

index af998212a11482ba270af19cd042367708fa2211..d18a4d48e9207363420e75aea4dd6da9558d7e72 100644 (file)
@@ -62,6 +62,7 @@ struct X509_VERIFY_PARAM_ID_st
        {
        unsigned char *host;    /* If not NULL hostname to match */
        size_t hostlen;
        {
        unsigned char *host;    /* If not NULL hostname to match */
        size_t hostlen;
+       unsigned int hostflags; /* Flags to control matching features */
        unsigned char *email;   /* If not NULL email address to match */
        size_t emaillen;
        unsigned char *ip;      /* If not NULL IP address to match */
        unsigned char *email;   /* If not NULL email address to match */
        size_t emaillen;
        unsigned char *ip;      /* If not NULL IP address to match */
index 32b07a0306de76cb556baa92ac50d739d6839c60..350c3475fe4ba7a6db65228ad6132a3db09fa149 100644 (file)
@@ -744,7 +744,8 @@ static int check_id(X509_STORE_CTX *ctx)
        X509_VERIFY_PARAM *vpm = ctx->param;
        X509_VERIFY_PARAM_ID *id = vpm->id;
        X509 *x = ctx->cert;
        X509_VERIFY_PARAM *vpm = ctx->param;
        X509_VERIFY_PARAM_ID *id = vpm->id;
        X509 *x = ctx->cert;
-       if (id->host && !X509_check_host(x, id->host, id->hostlen, 0))
+       if (id->host && !X509_check_host(x, id->host, id->hostlen,
+                                        id->hostflags))
                {
                if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH))
                        return 0;
                {
                if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH))
                        return 0;
index 24115ccfa28c8edbd66761bddfc896545e3f1d7b..4b61d28d7473193e8c6cad20c462b7c0fe5678d2 100644 (file)
@@ -560,6 +560,8 @@ int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param,
 
 int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
                                const unsigned char *name, size_t namelen);
 
 int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
                                const unsigned char *name, size_t namelen);
+void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
+                                       unsigned int flags);
 int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
                                const unsigned char *email, size_t emaillen);
 int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
 int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
                                const unsigned char *email, size_t emaillen);
 int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
index 14336a7ef7ada6198efb262235e0b74db8cee809..9b22093277e420a8bafb4b092c268fae736bc986 100644 (file)
@@ -239,6 +239,7 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest,
                {
                if (!X509_VERIFY_PARAM_set1_host(dest, id->host, id->hostlen))
                        return 0;
                {
                if (!X509_VERIFY_PARAM_set1_host(dest, id->host, id->hostlen))
                        return 0;
+               dest->id->hostflags = id->hostflags;
                }
 
        if (test_x509_verify_param_copy_id(email, NULL))
                }
 
        if (test_x509_verify_param_copy_id(email, NULL))
@@ -402,6 +403,12 @@ int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
                                        name, namelen);
        }
 
                                        name, namelen);
        }
 
+void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
+                                       unsigned int flags)
+       {
+       param->id->hostflags = flags;
+       }
+
 int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
                                const unsigned char *email, size_t emaillen)
        {
 int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
                                const unsigned char *email, size_t emaillen)
        {
@@ -437,7 +444,7 @@ const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param)
        return param->name;
        }
 
        return param->name;
        }
 
-static X509_VERIFY_PARAM_ID _empty_id = {NULL, 0, NULL, 0, NULL, 0};
+static X509_VERIFY_PARAM_ID _empty_id = {NULL, 0, 0U, NULL, 0, NULL, 0};
 
 #define vpm_empty_id (X509_VERIFY_PARAM_ID *)&_empty_id
 
 
 #define vpm_empty_id (X509_VERIFY_PARAM_ID *)&_empty_id
 
index de43c2fb3b2eb74430a7e7dd28e6539b4a6f5f77..7a4bd459604cbdcfa688da4f45a79439bb3680b8 100644 (file)
@@ -569,11 +569,13 @@ void X509_email_free(STACK_OF(OPENSSL_STRING) *sk)
 }
 
 typedef int (*equal_fn)(const unsigned char *pattern, size_t pattern_len,
 }
 
 typedef int (*equal_fn)(const unsigned char *pattern, size_t pattern_len,
-                       const unsigned char *subject, size_t subject_len);
+                       const unsigned char *subject, size_t subject_len,
+                       unsigned int flags);
 
 /* Compare while ASCII ignoring case. */
 static int equal_nocase(const unsigned char *pattern, size_t 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)
+                       const unsigned char *subject, size_t subject_len,
+                       unsigned int unused_flags)
        {
        if (pattern_len != subject_len)
                return 0;
        {
        if (pattern_len != subject_len)
                return 0;
@@ -602,7 +604,8 @@ 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,
 
 /* Compare using memcmp. */
 static int equal_case(const unsigned char *pattern, size_t pattern_len,
-                     const unsigned char *subject, size_t subject_len)
+                     const unsigned char *subject, size_t subject_len,
+                     unsigned int unused_flags)
 {
        /* The pattern must not contain NUL characters. */
        if (memchr(pattern, '\0', pattern_len) != NULL)
 {
        /* The pattern must not contain NUL characters. */
        if (memchr(pattern, '\0', pattern_len) != NULL)
@@ -615,7 +618,8 @@ static int equal_case(const unsigned char *pattern, size_t pattern_len,
 /* RFC 5280, section 7.5, requires that only the domain is compared in
    a case-insensitive manner. */
 static int equal_email(const unsigned char *a, size_t a_len,
 /* RFC 5280, section 7.5, requires that only the domain is compared in
    a case-insensitive manner. */
 static int equal_email(const unsigned char *a, size_t a_len,
-                      const unsigned char *b, size_t b_len)
+                      const unsigned char *b, size_t b_len,
+                      unsigned int unused_flags)
        {
        size_t i = a_len;
        if (a_len != b_len)
        {
        size_t i = a_len;
        if (a_len != b_len)
@@ -629,103 +633,177 @@ static int equal_email(const unsigned char *a, size_t a_len,
                if (a[i] == '@' || b[i] == '@')
                        {
                        if (!equal_nocase(a + i, a_len - i,
                if (a[i] == '@' || b[i] == '@')
                        {
                        if (!equal_nocase(a + i, a_len - i,
-                                         b + i, a_len - i))
+                                         b + i, a_len - i, 0))
                                return 0;
                        break;
                        }
                }
        if (i == 0)
                i = a_len;
                                return 0;
                        break;
                        }
                }
        if (i == 0)
                i = a_len;
-       return equal_case(a, i, b, i);
+       return equal_case(a, i, b, i, 0);
        }
 
 /* Compare the prefix and suffix with the subject, and check that the
    characters in-between are valid. */
 static int wildcard_match(const unsigned char *prefix, size_t prefix_len,
                          const unsigned char *suffix, size_t suffix_len,
        }
 
 /* Compare the prefix and suffix with the subject, and check that the
    characters in-between are valid. */
 static int wildcard_match(const unsigned char *prefix, size_t prefix_len,
                          const unsigned char *suffix, size_t suffix_len,
-                         const unsigned char *subject, size_t subject_len)
+                         const unsigned char *subject, size_t subject_len,
+                         unsigned int flags)
        {
        const unsigned char *wildcard_start;
        const unsigned char *wildcard_end;
        const unsigned char *p;
        {
        const unsigned char *wildcard_start;
        const unsigned char *wildcard_end;
        const unsigned char *p;
+       int allow_multi = 0;
+       int allow_idna = 0;
+
        if (subject_len < prefix_len + suffix_len)
                return 0;
        if (subject_len < prefix_len + suffix_len)
                return 0;
-       if (!equal_nocase(prefix, prefix_len, subject, prefix_len))
+       if (!equal_nocase(prefix, prefix_len, subject, prefix_len, flags))
                return 0;
        wildcard_start = subject + prefix_len;
        wildcard_end = subject + (subject_len - suffix_len);
                return 0;
        wildcard_start = subject + prefix_len;
        wildcard_end = subject + (subject_len - suffix_len);
-       if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len))
+       if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len, flags))
                return 0;
                return 0;
-       /* The wildcard must match at least one character. */
-       if (wildcard_start == wildcard_end)
+       /*
+        * If the wildcard makes up the entire first label, it must match at
+        * least one character.
+        */
+       if (prefix_len == 0 && *suffix == '.')
+               {
+               if (wildcard_start == wildcard_end)
+                       return 0;
+               allow_idna = 1;
+               if (flags & X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS)
+                       allow_multi = 1;
+               }
+       /* IDNA labels cannot match partial wildcards */
+       if (!allow_idna &&
+           subject_len >= 4 && strncasecmp((char *)subject, "xn--", 4) == 0)
                return 0;
                return 0;
-       /* Check that the part matched by the wildcard contains only
-          permitted characters and only matches a single label. */
+       /* The wildcard may match a literal '*' */
+       if (wildcard_end == wildcard_start + 1 && *wildcard_start == '*')
+               return 1;
+       /*
+        * Check that the part matched by the wildcard contains only
+        * permitted characters and only matches a single label unless
+        * allow_multi is set.
+        */
        for (p = wildcard_start; p != wildcard_end; ++p)
                if (!(('0' <= *p && *p <= '9') ||
                      ('A' <= *p && *p <= 'Z') ||
                      ('a' <= *p && *p <= 'z') ||
        for (p = wildcard_start; p != wildcard_end; ++p)
                if (!(('0' <= *p && *p <= '9') ||
                      ('A' <= *p && *p <= 'Z') ||
                      ('a' <= *p && *p <= 'z') ||
-                     *p == '-'))
+                     *p == '-' || (allow_multi && *p == '.')))
                        return 0;
        return 1;
        }
 
                        return 0;
        return 1;
        }
 
-/* Checks if the memory region consistens of [0-9A-Za-z.-]. */
-static int valid_domain_characters(const unsigned char *p, size_t len)
-       {
-       while (len)
-               {
-               if (!(('0' <= *p && *p <= '9') ||
-                     ('A' <= *p && *p <= 'Z') ||
-                     ('a' <= *p && *p <= 'z') ||
-                     *p == '-' || *p == '.'))
-                       return 0;
-               ++p;
-               --len;
-               }
-       return 1;
-       }
+#define LABEL_START    (1 << 0)
+#define LABEL_END      (1 << 1)
+#define LABEL_HYPHEN   (1 << 2)
+#define LABEL_IDNA     (1 << 3)
 
 
-/* Find the '*' in a wildcard pattern.  If no such character is found
-   or the pattern is otherwise invalid, returns NULL. */
-static const unsigned char *wildcard_find_star(const unsigned char *pattern,
-                                              size_t pattern_len)
+static const unsigned char *valid_star(const unsigned char *p, size_t len,
+                                               unsigned int flags)
        {
        {
-       const unsigned char *star = memchr(pattern, '*', pattern_len);
-       size_t dot_count = 0;
-       const unsigned char *suffix_start;
-       size_t suffix_length;
-       if (star == NULL)
-               return NULL;
-       suffix_start = star + 1;
-       suffix_length = (pattern + pattern_len) - (star + 1);
-       if (!(valid_domain_characters(pattern, star - pattern) &&
-             valid_domain_characters(suffix_start, suffix_length)))
-               return NULL;
-       /* Check that the suffix matches at least two labels. */
-       while (suffix_length)
+       const unsigned char *star = 0;
+       size_t i;
+       int state = LABEL_START;
+       int dots = 0;
+       for (i = 0; i < len; ++i)
                {
                {
-               if (*suffix_start == '.')
-                       ++dot_count;
-               ++suffix_start;
-               --suffix_length;
+               /*
+                * Locate first and only legal wildcard, either at the start
+                * or end of a non-IDNA first and not final label.
+                */
+               if (p[i] == '*')
+                       {
+                       int atstart = (state & LABEL_START);
+                       int atend = (i == len - 1 || p[i+i] == '.');
+                       /*
+                        * At most one wildcard per pattern.
+                        * No wildcards in IDNA labels.
+                        * No wildcards after the first label.
+                        */
+                       if (star != NULL || (state & LABEL_IDNA) != 0 || dots)
+                               return NULL;
+                       /* Only full-label '*.example.com' wildcards? */
+                       if ((flags & X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS)
+                           && (!atstart || !atend))
+                               return NULL;
+                       /* No 'foo*bar' wildcards */
+                       if (!atstart && !atend)
+                               return NULL;
+                       star = &p[i];
+                       state &= ~LABEL_START;
+                       }
+               else if ((state & LABEL_START) != 0)
+                       {
+                       /*
+                        * At the start of a label, skip any "xn--" and
+                        * remain in the LABEL_START state, but set the
+                        * IDNA label state
+                        */
+                       if ((state & LABEL_IDNA) == 0 && len - i >= 4
+                           && strncasecmp((char *)&p[i], "xn--", 4) == 0)
+                               {
+                               i += 3;
+                               state |= LABEL_IDNA;
+                               continue;
+                               }
+                       /* Labels must start with a letter or digit */
+                       state &= ~LABEL_START;
+                       if (('a' <= p[i] && p[i] <= 'z')
+                           || ('A' <= p[i] && p[i] <= 'Z')
+                           || ('0' <= p[i] && p[i] <= '9'))
+                               continue;
+                       return NULL;
+                       }
+               else if (('a' <= p[i] && p[i] <= 'z')
+                        || ('A' <= p[i] && p[i] <= 'Z')
+                        || ('0' <= p[i] && p[i] <= '9'))
+                       {
+                       state &= LABEL_IDNA;
+                       continue;
+                       }
+               else if (p[i] == '.')
+                       {
+                       if (state & (LABEL_HYPHEN | LABEL_START))
+                               return NULL;
+                       state = LABEL_START;
+                       ++dots;
+                       }
+               else if (p[i] == '-')
+                       {
+                       if (state & LABEL_HYPHEN)
+                               return NULL;
+                       state |= LABEL_HYPHEN;
+                       }
+               else
+                       return NULL;
                }
                }
-       if (dot_count < 2)
+
+       /*
+        * The final label must not end in a hyphen or ".", and
+        * there must be at least two dots after the star.
+        */
+       if ((state & (LABEL_START | LABEL_HYPHEN)) != 0
+           || dots < 2)
                return NULL;
        return star;
        }
 
 /* Compare using wildcards. */
 static int equal_wildcard(const unsigned char *pattern, size_t pattern_len,
                return NULL;
        return star;
        }
 
 /* Compare using wildcards. */
 static int equal_wildcard(const unsigned char *pattern, size_t pattern_len,
-                         const unsigned char *subject, size_t subject_len)
+                         const unsigned char *subject, size_t subject_len,
+                         unsigned int flags)
        {
        {
-       const unsigned char *star = wildcard_find_star(pattern, pattern_len);
+       const unsigned char *star = valid_star(pattern, pattern_len, flags);
        if (star == NULL)
                return equal_nocase(pattern, pattern_len,
        if (star == NULL)
                return equal_nocase(pattern, pattern_len,
-                                   subject, subject_len);
+                                   subject, subject_len, flags);
        return wildcard_match(pattern, star - pattern,
                              star + 1, (pattern + pattern_len) - star - 1,
        return wildcard_match(pattern, star - pattern,
                              star + 1, (pattern + pattern_len) - star - 1,
-                             subject, subject_len);
+                             subject, subject_len, flags);
        }
 
 /* Compare an ASN1_STRING to a supplied string. If they match
        }
 
 /* Compare an ASN1_STRING to a supplied string. If they match
@@ -734,6 +812,7 @@ static int equal_wildcard(const unsigned char *pattern, size_t pattern_len,
  */
 
 static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
  */
 
 static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
+                               unsigned int flags,
                                const unsigned char *b, size_t blen)
        {
        if (!a->data || !a->length)
                                const unsigned char *b, size_t blen)
        {
        if (!a->data || !a->length)
@@ -743,7 +822,7 @@ static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
                if (cmp_type != a->type)
                        return 0;
                if (cmp_type == V_ASN1_IA5STRING)
                if (cmp_type != a->type)
                        return 0;
                if (cmp_type == V_ASN1_IA5STRING)
-                       return equal(a->data, a->length, b, blen);
+                       return equal(a->data, a->length, b, blen, flags);
                if (a->length == (int)blen && !memcmp(a->data, b, blen))
                        return 1;
                else
                if (a->length == (int)blen && !memcmp(a->data, b, blen))
                        return 1;
                else
@@ -756,7 +835,7 @@ static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
                astrlen = ASN1_STRING_to_UTF8(&astr, a);
                if (astrlen < 0)
                        return -1;
                astrlen = ASN1_STRING_to_UTF8(&astr, a);
                if (astrlen < 0)
                        return -1;
-               rv = equal(astr, astrlen, b, blen);
+               rv = equal(astr, astrlen, b, blen, flags);
                OPENSSL_free(astr);
                return rv;
                }
                OPENSSL_free(astr);
                return rv;
                }
@@ -770,6 +849,7 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
        int i;
        int cnid;
        int alt_type;
        int i;
        int cnid;
        int alt_type;
+       int san_present = 0;
        equal_fn equal;
        if (check_type == GEN_EMAIL)
                {
        equal_fn equal;
        if (check_type == GEN_EMAIL)
                {
@@ -805,15 +885,17 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
                        GENERAL_NAME *gen;
                        ASN1_STRING *cstr;
                        gen = sk_GENERAL_NAME_value(gens, i);
                        GENERAL_NAME *gen;
                        ASN1_STRING *cstr;
                        gen = sk_GENERAL_NAME_value(gens, i);
-                       if(gen->type != check_type)
+                       if (gen->type != check_type)
                                continue;
                                continue;
+                       san_present = 1;
                        if (check_type == GEN_EMAIL)
                                cstr = gen->d.rfc822Name;
                        else if (check_type == GEN_DNS)
                                cstr = gen->d.dNSName;
                        else
                                cstr = gen->d.iPAddress;
                        if (check_type == GEN_EMAIL)
                                cstr = gen->d.rfc822Name;
                        else if (check_type == GEN_DNS)
                                cstr = gen->d.dNSName;
                        else
                                cstr = gen->d.iPAddress;
-                       if (do_check_string(cstr, alt_type, equal, chk, chklen))
+                       if (do_check_string(cstr, alt_type, equal, flags,
+                                           chk, chklen))
                                {
                                rv = 1;
                                break;
                                {
                                rv = 1;
                                break;
@@ -822,7 +904,9 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
                GENERAL_NAMES_free(gens);
                if (rv)
                        return 1;
                GENERAL_NAMES_free(gens);
                if (rv)
                        return 1;
-               if (!(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) || !cnid)
+               if (!cnid
+                   || (san_present
+                       && !(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT)))
                        return 0;
                }
        i = -1;
                        return 0;
                }
        i = -1;
@@ -833,7 +917,7 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
                ASN1_STRING *str;
                ne = X509_NAME_get_entry(name, i);
                str = X509_NAME_ENTRY_get_data(ne);
                ASN1_STRING *str;
                ne = X509_NAME_get_entry(name, i);
                str = X509_NAME_ENTRY_get_data(ne);
-               if (do_check_string(str, -1, equal, chk, chklen))
+               if (do_check_string(str, -1, equal, flags, chk, chklen))
                        return 1;
                }
        return 0;
                        return 1;
                }
        return 0;
index 77d86795c11e18e94441423bdff779fb698a278c..4cd6f3688809997b0fd4d33dc8ecc27f5b415196 100644 (file)
@@ -7,11 +7,10 @@ static const char *const names[] =
        {
        "a", "b", ".", "*", "@",
        ".a", "a.", ".b", "b.", ".*", "*.", "*@", "@*", "a@", "@a", "b@", "..",
        {
        "a", "b", ".", "*", "@",
        ".a", "a.", ".b", "b.", ".*", "*.", "*@", "@*", "a@", "@a", "b@", "..",
-       "@@", "**",
-       "*.com", "*com", "*.*.com", "*com", "com*", "*example.com",
-       "*@example.com", "test@*.example.com",
-       "example.com", "www.example.com", "test.www.example.com",
-       "*.example.com", "*.www.example.com", "test.*.example.com", "www.*.com",
+       "@@", "**", "*.com", "*com", "*.*.com", "*com", "com*", "*example.com",
+       "*@example.com", "test@*.example.com", "example.com", "www.example.com",
+       "test.www.example.com", "*.example.com", "*.www.example.com",
+       "test.*.example.com", "www.*.com",
        "example.net", "xn--rger-koa.example.com",
        "a.example.com", "b.example.com",
        "postmaster@example.com", "Postmaster@example.com",
        "example.net", "xn--rger-koa.example.com",
        "a.example.com", "b.example.com",
        "postmaster@example.com", "Postmaster@example.com",
@@ -21,28 +20,20 @@ static const char *const names[] =
 
 static const char *const exceptions[] =
        {
 
 static const char *const exceptions[] =
        {
-       "set CN: host: [*.example.com] does not match [*.example.com]",
        "set CN: host: [*.example.com] matches [a.example.com]",
        "set CN: host: [*.example.com] matches [b.example.com]",
        "set CN: host: [*.example.com] matches [www.example.com]",
        "set CN: host: [*.example.com] matches [xn--rger-koa.example.com]",
        "set CN: host: [*.example.com] matches [a.example.com]",
        "set CN: host: [*.example.com] matches [b.example.com]",
        "set CN: host: [*.example.com] matches [www.example.com]",
        "set CN: host: [*.example.com] matches [xn--rger-koa.example.com]",
-       "set CN: host: [test.*.example.com] does not match [test.*.example.com]",
-       "set CN: host: [test.*.example.com] matches [test.www.example.com]",
-       "set CN: host: [*.www.example.com] does not match [*.www.example.com]",
        "set CN: host: [*.www.example.com] matches [test.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]",
        "set emailAddress: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]",
        "set dnsName: host: [*.example.com] matches [www.example.com]",
        "set CN: host: [*.www.example.com] matches [test.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]",
        "set emailAddress: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]",
        "set dnsName: host: [*.example.com] matches [www.example.com]",
-       "set dnsName: host: [*.example.com] does not match [*.example.com]",
        "set dnsName: host: [*.example.com] matches [a.example.com]",
        "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: [*.example.com] matches [a.example.com]",
        "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: [*.www.example.com] does not match [*.www.example.com]",
-       "set dnsName: host: [test.*.example.com] matches [test.www.example.com]",
-       "set dnsName: host: [test.*.example.com] does not match [test.*.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]",
        "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]",
index 272374e5a2a8626e0e0877262d0a70a0df9cad23..406300f29656c7322b5381442700b3ad29d39c31 100644 (file)
@@ -704,8 +704,12 @@ STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x);
 
 /* Always check subject name for host match even if subject alt names present */
 #define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT   0x1
 
 /* Always check subject name for host match even if subject alt names present */
 #define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT   0x1
-/* Disable wild-card matching for dnsName fields and common name. */
+/* Disable wildcard matching for dnsName fields and common name. */
 #define X509_CHECK_FLAG_NO_WILDCARDS   0x2
 #define X509_CHECK_FLAG_NO_WILDCARDS   0x2
+/* Wildcards must not match a partial label. */
+#define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0x4
+/* Allow (non-partial) wildcards to match multiple labels. */
+#define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS 0x8
 
 int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen,
                                        unsigned int flags);
 
 int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen,
                                        unsigned int flags);
index 46cac2bea2beaa83542a5c4b0261dade5f28e6eb..1059d4ff84fee2aba4dffeb11202c5e08e8b995f 100644 (file)
@@ -26,6 +26,17 @@ X509_VERIFY_PARAM_set_flags, X509_VERIFY_PARAM_clear_flags, X509_VERIFY_PARAM_ge
  void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth);
  int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param);
 
  void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth);
  int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param);
 
+ int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
+                                const unsigned char *name, size_t namelen);
+ void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
+                                     unsigned int flags);
+ int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
+                                const unsigned char *email, size_t emaillen);
+ int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
+                              const unsigned char *ip, size_t iplen);
+ int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param,
+                                  const char *ipasc);
+
 =head1 DESCRIPTION
 
 These functions manipulate the B<X509_VERIFY_PARAM> structure associated with
 =head1 DESCRIPTION
 
 These functions manipulate the B<X509_VERIFY_PARAM> structure associated with
@@ -61,12 +72,43 @@ X509_VERIFY_PARAM_set_depth() sets the maximum verification depth to B<depth>.
 That is the maximum number of untrusted CA certificates that can appear in a
 chain.
 
 That is the maximum number of untrusted CA certificates that can appear in a
 chain.
 
+X509_VERIFY_PARAM_set1_host() sets the expected DNS hostname to B<name>.  If
+B<name> is NUL-terminated, B<namelen> may be zero, otherwise B<namelen> must
+be set to the length of B<name>.  When a hostname is specified, certificate
+verification automatically invokes L<X509_check_host(3)> with flags equal to
+the B<flags> argument given to B<X509_VERIFY_PARAM_set_hostflags()> (default
+zero).  Applications are strongly advised to use this interface in preference
+to explicitly calling L<X509_check_host(3)>, hostname checks are
+out of scope with the DANE-EE(3) certificate usage, and the internal
+check will be suppressed as appropriate when DANE support is added
+to OpenSSL.
+
+X509_VERIFY_PARAM_set1_email() sets the expected RFC822 email address to
+B<email>.  If B<email is NUL-terminated, B<emaillen> may be zero, otherwise
+B<emaillen> must be set to the length of B<email>.  When an email address
+is specified, certificate verification automatically invokes
+L<X509_check_email(3)>.
+
+X509_VERIFY_PARAM_set1_ip() sets the expected IP address to B<ip>.
+The B<ip> argument is in binary format, in network byte-order and
+B<iplen> must be set to 4 for IPv4 and 16 for IPv6.  When an IP
+address is specified, certificate verification automatically invokes
+L<X509_check_ip(3)>.
+
+X509_VERIFY_PARAM_set1_ip_asc() sets the expected IP address to
+B<ipasc>.  The B<ipasc> argument is a NUL-terminal ASCII string:
+dotted decimal quad for IPv4 and colon-separated hexadecimal for
+IPv6.  The condensed "::" notation is supported for IPv6 addresses.
+
 =head1 RETURN VALUES
 
 =head1 RETURN VALUES
 
-X509_VERIFY_PARAM_set_flags(), X509_VERIFY_PARAM_clear_flags(), 
+X509_VERIFY_PARAM_set_flags(), X509_VERIFY_PARAM_clear_flags(),
 X509_VERIFY_PARAM_set_purpose(), X509_VERIFY_PARAM_set_trust(),
 X509_VERIFY_PARAM_set_purpose(), X509_VERIFY_PARAM_set_trust(),
-X509_VERIFY_PARAM_add0_policy() and X509_VERIFY_PARAM_set1_policies() return 1
-for success and 0 for failure. 
+X509_VERIFY_PARAM_add0_policy() X509_VERIFY_PARAM_set1_policies(),
+X509_VERIFY_PARAM_set1_host(), X509_VERIFY_PARAM_set_hostflags(),
+X509_VERIFY_PARAM_set1_email(), X509_VERIFY_PARAM_set1_ip() and
+X509_VERIFY_PARAM_set1_ip_asc() return 1 for success and 0 for
+failure.
 
 X509_VERIFY_PARAM_get_flags() returns the current verification flags.
 
 
 X509_VERIFY_PARAM_get_flags() returns the current verification flags.
 
index 5ac2137a50ee9974e275d9d40590c61d02d1a86a..64a84d2ab5e490bbe1a402b2460d09349c1fe335 100644 (file)
@@ -47,17 +47,38 @@ X509_check_ip_asc() is similar, except that the NUL-terminated
 string B<address> is first converted to the internal representation.
 
 The B<flags> argument is usually 0.  It can be the bitwise OR of the
 string B<address> is first converted to the internal representation.
 
 The B<flags> argument is usually 0.  It can be the bitwise OR of the
-flags B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT>,
-B<X509_CHECK_FLAG_NO_WILDCARDS>.
+flags:
+
+=over 4
+
+=item B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT>,
+
+=item B<X509_CHECK_FLAG_NO_WILDCARDS>,
+
+=item B<X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS>,
+
+=item B<X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS>.
+
+=back
 
 The B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT> flag causes the function
 
 The B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT> flag causes the function
-to check the subject DN even if the certificate contains a subject
-alternative name extension is present; the default is to ignore the
-subject DN in preference of the extension.
+to consider the subject DN even if the certificate contains at least
+one subject alternative name of the right type (DNS name or email
+address as appropriate); the default is to ignore the subject DN
+when at least one corresponding subject alternative names is present.
 
 
-If present, B<X509_CHECK_FLAG_NO_WILDCARDS> disables wildcard
+If set, B<X509_CHECK_FLAG_NO_WILDCARDS> disables wildcard
 expansion; this only applies to B<X509_check_host>.
 
 expansion; this only applies to B<X509_check_host>.
 
+If set, B<X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS> suppresses support
+for "*" as wildcard pattern in labels that have a prefix or suffix,
+such as: "www*" or "*www"; this only aplies to B<X509_check_host>.
+
+If set, B<X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS>, allows a "*"
+that constitutes the complete label of a DNS name (e.g.
+"*.example.com") to match more than one label in B<name>;
+this only applies to B<X509_check_host>.
+
 =head1 RETURN VALUES
 
 The functions return 1 for a successful match, 0 for a failed match
 =head1 RETURN VALUES
 
 The functions return 1 for a successful match, 0 for a failed match