From ced3d9158a7a8c676be504bb6cd3b5ffb7cc7f13 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Sun, 6 Jul 2014 01:47:29 +1000 Subject: [PATCH] Set optional peername when X509_check_host() succeeds. Pass address of X509_VERIFY_PARAM_ID peername to X509_check_host(). Document modified interface. --- apps/apps.c | 3 +- crypto/x509/x509_vfy.c | 3 +- crypto/x509v3/v3_utl.c | 54 +++++++++++++++++++--------------- crypto/x509v3/v3nametest.c | 5 ++-- crypto/x509v3/x509v3.h | 2 +- doc/crypto/X509_check_host.pod | 17 +++++++---- 6 files changed, 49 insertions(+), 35 deletions(-) diff --git a/apps/apps.c b/apps/apps.c index 7e1cf9cba2..188b2373aa 100644 --- a/apps/apps.c +++ b/apps/apps.c @@ -2969,7 +2969,8 @@ void print_cert_checks(BIO *bio, X509 *x, if (checkhost) { BIO_printf(bio, "Hostname %s does%s match certificate\n", - checkhost, X509_check_host(x, checkhost, 0, 0) + checkhost, + X509_check_host(x, checkhost, 0, 0, NULL) ? "" : " NOT"); } diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index b0e1dc036a..7e2916ce09 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -752,7 +752,8 @@ static int check_hosts(X509 *x, X509_VERIFY_PARAM_ID *id) 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) + if (X509_check_host(x, name, 0, id->hostflags, + &id->peername) > 0) return 1; } return n == 0; diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c index ea260f3c95..981e602037 100644 --- a/crypto/x509v3/v3_utl.c +++ b/crypto/x509v3/v3_utl.c @@ -853,8 +853,11 @@ 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, unsigned int flags, - const unsigned char *b, size_t blen) + const unsigned char *b, size_t blen, + char **peername) { + int rv = 0; + if (!a->data || !a->length) return 0; if (cmp_type > 0) @@ -862,27 +865,30 @@ 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) - return equal(a->data, a->length, b, blen, flags); - if (a->length == (int)blen && !memcmp(a->data, b, blen)) - return 1; - else - return 0; + rv = equal(a->data, a->length, b, blen, flags); + else if (a->length == (int)blen && !memcmp(a->data, b, blen)) + rv = 1; + if (rv > 0 && peername) + *peername = BUF_strndup((char *)a->data, a->length); } else { - int astrlen, rv; + int astrlen; unsigned char *astr; astrlen = ASN1_STRING_to_UTF8(&astr, a); if (astrlen < 0) return -1; rv = equal(astr, astrlen, b, blen, flags); OPENSSL_free(astr); - return rv; + if (rv > 0 && peername) + *peername = BUF_strndup((char *)astr, astrlen); } + return rv; } static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen, - unsigned int flags, int check_type) + unsigned int flags, int check_type, + char **peername) { GENERAL_NAMES *gens = NULL; X509_NAME *name = NULL; @@ -890,6 +896,7 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen, int cnid; int alt_type; int san_present = 0; + int rv = 0; equal_fn equal; /* See below, this flag is internal-only */ @@ -925,7 +932,6 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen, gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); if (gens) { - int rv = 0; for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { GENERAL_NAME *gen; @@ -940,16 +946,14 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen, cstr = gen->d.dNSName; else cstr = gen->d.iPAddress; - if (do_check_string(cstr, alt_type, equal, flags, - chk, chklen)) - { - rv = 1; + /* Positive on success, negative on error! */ + if ((rv = do_check_string(cstr, alt_type, equal, flags, + chk, chklen, peername)) != 0) break; - } } GENERAL_NAMES_free(gens); - if (rv) - return 1; + if (rv != 0) + return rv; if (!cnid || (san_present && !(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT))) @@ -963,14 +967,16 @@ 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); - if (do_check_string(str, -1, equal, flags, chk, chklen)) - return 1; + /* Positive on success, negative on error! */ + if ((rv = do_check_string(str, -1, equal, flags, + chk, chklen, peername)) != 0) + return rv; } return 0; } int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen, - unsigned int flags) + unsigned int flags, char **peername) { if (chk == NULL) return -2; @@ -985,7 +991,7 @@ int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen, return -2; if (chklen > 1 && chk[chklen-1] == '\0') --chklen; - return do_x509_check(x, chk, chklen, flags, GEN_DNS); + return do_x509_check(x, chk, chklen, flags, GEN_DNS, peername); } int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen, @@ -1004,7 +1010,7 @@ int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen, return -2; if (chklen > 1 && chk[chklen-1] == '\0') --chklen; - return do_x509_check(x, chk, chklen, flags, GEN_EMAIL); + return do_x509_check(x, chk, chklen, flags, GEN_EMAIL, NULL); } int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, @@ -1012,7 +1018,7 @@ int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, { if (chk == NULL) return -2; - return do_x509_check(x, chk, chklen, flags, GEN_IPADD); + return do_x509_check(x, chk, chklen, flags, GEN_IPADD, NULL); } int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags) @@ -1024,7 +1030,7 @@ int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags) iplen = a2i_ipadd(ipout, ipasc); if (iplen == 0) return -2; - return do_x509_check(x, ipout, (size_t)iplen, flags, GEN_IPADD); + return do_x509_check(x, ipout, (size_t)iplen, flags, GEN_IPADD, NULL); } /* Convert IP addresses both IPv4 and IPv6 into an diff --git a/crypto/x509v3/v3nametest.c b/crypto/x509v3/v3nametest.c index ad820fdfd9..fc84b27c41 100644 --- a/crypto/x509v3/v3nametest.c +++ b/crypto/x509v3/v3nametest.c @@ -276,7 +276,7 @@ static void run_cert(X509 *crt, const char *nameincert, memcpy(name, *pname, namelen); ret = X509_check_host(crt, (const unsigned char *)name, - namelen, 0); + namelen, 0, NULL); match = -1; if (ret < 0) { @@ -295,7 +295,8 @@ static void run_cert(X509 *crt, const char *nameincert, check_message(fn, "host", nameincert, match, *pname); ret = X509_check_host(crt, (const unsigned char *)name, - namelen, X509_CHECK_FLAG_NO_WILDCARDS); + namelen, X509_CHECK_FLAG_NO_WILDCARDS, + NULL); match = -1; if (ret < 0) { diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h index f22c0ef5f9..971bc3084e 100644 --- a/crypto/x509v3/x509v3.h +++ b/crypto/x509v3/x509v3.h @@ -720,7 +720,7 @@ STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x); #define _X509_CHECK_FLAG_DOT_SUBDOMAINS 0x8000 int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen, - unsigned int flags); + unsigned int flags, char **peername); int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen, unsigned int flags); int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, diff --git a/doc/crypto/X509_check_host.pod b/doc/crypto/X509_check_host.pod index 113861d46d..87ea54303a 100644 --- a/doc/crypto/X509_check_host.pod +++ b/doc/crypto/X509_check_host.pod @@ -9,7 +9,7 @@ X509_check_host, X509_check_email, X509_check_ip, X509_check_ip_asc - X.509 cert #include int X509_check_host(X509 *, const unsigned char *name, - size_t namelen, unsigned int flags); + size_t namelen, unsigned int flags, char **peername); int X509_check_email(X509 *, const unsigned char *address, size_t addresslen, unsigned int flags); int X509_check_ip(X509 *, const unsigned char *address, @@ -32,11 +32,16 @@ characters in the name string or zero in which case the length is calculated with strlen(name). When B starts with a dot (e.g ".example.com"), it will be matched by a certificate valid for any sub-domain of B, (see also B -below). Applications are strongly advised to use -X509_VERIFY_PARAM_set1_host() 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. +below). When the certificate is matched and B is not +NULL a pointer to a copy of the matching hostname or CommonName +from the peer certificate is stored at the address passed in +B. The application is responsible for freeing the peername +via OPENSSL_free() when it is no longer needed. Applications are +advised to use X509_VERIFY_PARAM_set1_host() 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_check_email() checks if the certificate matches the specified email address. Only the mailbox syntax of RFC 822 is supported, -- 2.25.1