Multiple verifier reference identities.
authorViktor Dukhovni <openssl-users@dukhovni.org>
Sun, 22 Jun 2014 05:38:57 +0000 (01:38 -0400)
committerViktor Dukhovni <openssl-users@dukhovni.org>
Mon, 23 Jun 2014 00:32:35 +0000 (20:32 -0400)
Implemented as STACK_OF(OPENSSL_STRING).

crypto/x509/x509_lcl.h
crypto/x509/x509_vfy.c
crypto/x509/x509_vfy.h
crypto/x509/x509_vpm.c
doc/crypto/X509_VERIFY_PARAM_set_flags.pod
doc/crypto/X509_check_host.pod

index a71af34..a20bed2 100644 (file)
@@ -60,7 +60,7 @@
 
 struct X509_VERIFY_PARAM_ID_st
        {
-       unsigned char *host;    /* If not NULL hostname to match */
+       STACK_OF(OPENSSL_STRING) *hosts;        /* Set of acceptable names */
        unsigned int hostflags; /* Flags to control matching features */
        unsigned char *email;   /* If not NULL email address to match */
        size_t emaillen;
index b6f16bb..49425f4 100644 (file)
@@ -743,12 +743,27 @@ static int check_id_error(X509_STORE_CTX *ctx, int errcode)
        return ctx->verify_cb(0, ctx);
        }
 
+static int check_hosts(X509 *x, X509_VERIFY_PARAM_ID *id)
+       {
+       int i;
+       int n = sk_OPENSSL_STRING_num(id->hosts);
+       unsigned char *name;
+
+       for (i = 0; i < n; ++i)
+               {
+               name = (unsigned char *)sk_OPENSSL_STRING_value(id->hosts, i);
+               if (X509_check_host(x, name, 0, id->hostflags)) > 0)
+                       return 1;
+               }
+       return n == 0;
+       }
+
 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;
-       if (id->host && X509_check_host(x, id->host, 0, id->hostflags) <= 0)
+       if (id->hosts && !check_hosts(x, id) <= 0)
                {
                if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH))
                        return 0;
index 4b61d28..4aca857 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_add1_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,
index d7cc231..d95943c 100644 (file)
 
 /* X509_VERIFY_PARAM functions */
 
+#define SET_HOST 0
+#define ADD_HOST 1
+
+static char *str_copy(const char *s) { return OPENSSL_strdup(s); }
+static void str_free(char *s) { OPENSSL_free(s); }
+
+#define string_stack_free(sk) sk_OPENSSL_STRING_pop_free(sk, str_free)
+
+static int int_x509_param_set_hosts(X509_VERIFY_PARAM_ID *id, int mode,
+                                   const unsigned char *name, size_t namelen)
+       {
+       char *copy;
+
+       /*
+        * Refuse names with embedded NUL bytes, except perhaps as final byte.
+        * XXX: Do we need to push an error onto the error stack?
+        */
+       if (namelen == 0)
+               namelen = name ? strlen((char *)name) : 0;
+       else if (name && memchr(name, '\0', namelen > 1 ? namelen-1 : namelen))
+                return 0;
+       if (name && name[namelen-1] == '\0')
+               --namelen;
+
+       if (mode == SET_HOST && id->hosts)
+               {
+               string_stack_free(id->hosts);
+               id->hosts = NULL;
+               }
+       if (name == NULL || namelen == 0)
+               return 1;
+
+       copy = BUF_strndup((char *)name, namelen);
+       if (copy == NULL)
+               return 0;
+
+       if (id->hosts == NULL &&
+           (id->hosts = sk_OPENSSL_STRING_new_null()) == NULL)
+               {
+               OPENSSL_free(copy);
+               return 0;
+               }
+
+       if (!sk_OPENSSL_STRING_push(id->hosts, copy))
+               {
+               OPENSSL_free(copy);
+               if (sk_OPENSSL_STRING_num(id->hosts) == 0)
+                       {
+                       sk_OPENSSL_STRING_free(id->hosts);
+                       id->hosts = NULL;
+                       }
+               return 0;
+               }
+
+       return 1;
+       }
+
 static void x509_verify_param_zero(X509_VERIFY_PARAM *param)
        {
        X509_VERIFY_PARAM_ID *paramid;
@@ -87,10 +144,10 @@ static void x509_verify_param_zero(X509_VERIFY_PARAM *param)
                param->policies = NULL;
                }
        paramid = param->id;
-       if (paramid->host)
+       if (paramid->hosts)
                {
-               OPENSSL_free(paramid->host);
-               paramid->host = NULL;
+               string_stack_free(paramid->hosts);
+               paramid->hosts = NULL;
                }
        if (paramid->email)
                {
@@ -234,11 +291,23 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest,
                        return 0;
                }
 
-       if (test_x509_verify_param_copy_id(host, NULL))
+       /* Copy the host flags if and only if we're copying the host list */
+       if (test_x509_verify_param_copy_id(hosts, NULL))
                {
-               if (!X509_VERIFY_PARAM_set1_host(dest, id->host, 0))
-                       return 0;
-               dest->id->hostflags = id->hostflags;
+               if (dest->id->hosts)
+                       {
+                       string_stack_free(dest->id->hosts);
+                       dest->id->hosts = NULL;
+                       }
+               if (id->hosts)
+                       {
+                       dest->id->hosts =
+                           sk_OPENSSL_STRING_deep_copy(id->hosts,
+                                                       str_copy, str_free);
+                       if (dest->id->hosts == NULL)
+                               return 0;
+                       dest->id->hostflags = id->hostflags;
+                       }
                }
 
        if (test_x509_verify_param_copy_id(email, NULL))
@@ -398,7 +467,13 @@ 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)
        {
-       return int_x509_param_set1(&param->id->host, NULL, name, namelen);
+       return int_x509_param_set_hosts(param->id, SET_HOST, name, namelen);
+       }
+
+int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param,
+                               const unsigned char *name, size_t namelen)
+       {
+       return int_x509_param_set_hosts(param->id, ADD_HOST, name, namelen);
        }
 
 void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
index 7b1f294..18c0f6e 100644 (file)
@@ -2,7 +2,7 @@
 
 =head1 NAME
 
-X509_VERIFY_PARAM_set_flags, X509_VERIFY_PARAM_clear_flags, X509_VERIFY_PARAM_get_flags, X509_VERIFY_PARAM_set_purpose, X509_VERIFY_PARAM_set_trust, X509_VERIFY_PARAM_set_depth, X509_VERIFY_PARAM_get_depth, X509_VERIFY_PARAM_set_time, 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, X509_VERIFY_PARAM_set1_ip_asc - X509 verification parameters
+X509_VERIFY_PARAM_set_flags, X509_VERIFY_PARAM_clear_flags, X509_VERIFY_PARAM_get_flags, X509_VERIFY_PARAM_set_purpose, X509_VERIFY_PARAM_set_trust, X509_VERIFY_PARAM_set_depth, X509_VERIFY_PARAM_get_depth, X509_VERIFY_PARAM_set_time, X509_VERIFY_PARAM_add0_policy, X509_VERIFY_PARAM_set1_policies, X509_VERIFY_PARAM_set1_host, X509_VERIFY_PARAM_add1_host, X509_VERIFY_PARAM_set_hostflags, X509_VERIFY_PARAM_set1_email, X509_VERIFY_PARAM_set1_ip, X509_VERIFY_PARAM_set1_ip_asc - X509 verification parameters
 
 =head1 SYNOPSIS
 
@@ -28,6 +28,8 @@ X509_VERIFY_PARAM_set_flags, X509_VERIFY_PARAM_clear_flags, X509_VERIFY_PARAM_ge
 
  int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
                                 const unsigned char *name, size_t namelen);
+ int X509_VERIFY_PARAM_add1_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,
@@ -72,16 +74,26 @@ 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.
 
-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_host() sets the expected DNS hostname to
+B<name> clearing any previously specified host name or names.  If
+B<name> is NULL, or empty the list of hostnames is cleared, and
+name checks are not performed on the peer certificate.  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_add1_host() adds B<name> as an additional reference
+identifer that can match the peer's certificate.  Any previous names
+set via X509_VERIFY_PARAM_set1_host() or X509_VERIFY_PARAM_add1_host()
+are retained, no change is made if B<name> is NULL or empty.  When
+multiple names are configured, the peer is considered verified when
+any name matches.
 
 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
index 001b845..113861d 100644 (file)
@@ -109,6 +109,7 @@ X509_check_ip_asc() can also return -2 if the IP address string is malformed.
 
 L<SSL_get_verify_result(3)|SSL_get_verify_result(3)>,
 L<X509_VERIFY_PARAM_set1_host(3)|X509_VERIFY_PARAM_set1_host(3)>,
+L<X509_VERIFY_PARAM_add1_host(3)|X509_VERIFY_PARAM_add1_host(3)>,
 L<X509_VERIFY_PARAM_set1_email(3)|X509_VERIFY_PARAM_set1_email(3)>,
 L<X509_VERIFY_PARAM_set1_ip(3)|X509_VERIFY_PARAM_set1_ip(3)>,
 L<X509_VERIFY_PARAM_set1_ipasc(3)|X509_VERIFY_PARAM_set1_ipasc(3)>