From 8abffa4a73fcbf6536e0a42d736ed9211a8204ea Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Sun, 22 Jun 2014 01:38:57 -0400 Subject: [PATCH] Multiple verifier reference identities. Implemented as STACK_OF(OPENSSL_STRING). --- crypto/x509/x509_lcl.h | 2 +- crypto/x509/x509_vfy.c | 17 +++- crypto/x509/x509_vfy.h | 2 + crypto/x509/x509_vpm.c | 91 ++++++++++++++++++++-- doc/crypto/X509_VERIFY_PARAM_set_flags.pod | 34 +++++--- doc/crypto/X509_check_host.pod | 1 + 6 files changed, 126 insertions(+), 21 deletions(-) diff --git a/crypto/x509/x509_lcl.h b/crypto/x509/x509_lcl.h index a71af342dc..a20bed287d 100644 --- a/crypto/x509/x509_lcl.h +++ b/crypto/x509/x509_lcl.h @@ -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; diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index b6f16bb772..49425f4622 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -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; diff --git a/crypto/x509/x509_vfy.h b/crypto/x509/x509_vfy.h index 4b61d28d74..4aca857fda 100644 --- a/crypto/x509/x509_vfy.h +++ b/crypto/x509/x509_vfy.h @@ -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, diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c index d7cc231d58..d95943c22c 100644 --- a/crypto/x509/x509_vpm.c +++ b/crypto/x509/x509_vpm.c @@ -69,6 +69,63 @@ /* 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(¶m->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, diff --git a/doc/crypto/X509_VERIFY_PARAM_set_flags.pod b/doc/crypto/X509_VERIFY_PARAM_set_flags.pod index 7b1f294e89..18c0f6eac9 100644 --- a/doc/crypto/X509_VERIFY_PARAM_set_flags.pod +++ b/doc/crypto/X509_VERIFY_PARAM_set_flags.pod @@ -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. 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. If -B is NUL-terminated, B may be zero, otherwise B must -be set to the length of B. When a hostname is specified, certificate -verification automatically invokes L with flags equal to -the B argument given to B (default -zero). Applications are strongly advised to use this interface in preference -to explicitly calling L, 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 clearing any previously specified host name or names. If +B is NULL, or empty the list of hostnames is cleared, and +name checks are not performed on the peer certificate. If B +is NUL-terminated, B may be zero, otherwise B +must be set to the length of B. When a hostname is specified, +certificate verification automatically invokes L +with flags equal to the B argument given to +B (default zero). Applications +are strongly advised to use this interface in preference to explicitly +calling L, 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 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 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. If B is NUL-terminated, B may be zero, otherwise diff --git a/doc/crypto/X509_check_host.pod b/doc/crypto/X509_check_host.pod index 001b845957..113861d46d 100644 --- a/doc/crypto/X509_check_host.pod +++ b/doc/crypto/X509_check_host.pod @@ -109,6 +109,7 @@ X509_check_ip_asc() can also return -2 if the IP address string is malformed. L, L, +L, L, L, L -- 2.34.1