diff options
Diffstat (limited to 'tcpd/libcouriertls.c')
| -rw-r--r-- | tcpd/libcouriertls.c | 219 |
1 files changed, 209 insertions, 10 deletions
diff --git a/tcpd/libcouriertls.c b/tcpd/libcouriertls.c index d4d1643..ac05caf 100644 --- a/tcpd/libcouriertls.c +++ b/tcpd/libcouriertls.c @@ -142,6 +142,7 @@ static int ssl_verify_callback(int goodcert, X509_STORE_CTX *x509) return (1); } +#if HAVE_X509_VERIFY_PARAM_SET1_HOST static int verifypeer(const struct tls_info *info, SSL *ssl) { if (!info->peer_verify_domain) @@ -149,6 +150,199 @@ static int verifypeer(const struct tls_info *info, SSL *ssl) return SSL_get_verify_result(ssl) == X509_V_OK; } +#else + +static int hostmatch_utf8(const char *a, const char *b) +{ + while (*a || *b) + { + char ca=*a; + char cb=*b; + + if (ca >= 'A' && ca <= 'Z') + ca += 'a'-'A'; + if (cb >= 'A' && cb <= 'Z') + cb += 'a'-'A'; + if (ca != cb) + return 0; + + ++a; + ++b; + } + return 1; +} + +static int hostmatch(const struct tls_info *info, const char *domain) +{ + const char *p=domain; + const char *verify_domain=info->peer_verify_domain; + char *idn_domain1; + char *idn_domain2; + int rc; + + if (*p == '*') + { + ++p; + + if (*p != '.') + return 0; + + while (*verify_domain) + { + if (*verify_domain++ == '.') + break; + } + } + + if (idna_to_unicode_8z8z(info->peer_verify_domain, &idn_domain1, 0) + != IDNA_SUCCESS) + idn_domain1=0; + + if (idna_to_unicode_8z8z(p, &idn_domain2, 0) + != IDNA_SUCCESS) + idn_domain2=0; + + rc=hostmatch_utf8(idn_domain1 ? idn_domain1:info->peer_verify_domain, + idn_domain2 ? idn_domain2:p); + + if (idn_domain1) + free(idn_domain1); + if (idn_domain2) + free(idn_domain2); + return rc; +} + +static int verifypeer1(const struct tls_info *info, X509 *x, + STACK_OF(GENERAL_NAME) *subject_alt_names); + +static int verifypeer(const struct tls_info *info, SSL *ssl) +{ + X509 *x=NULL; + int rc; + STACK_OF(GENERAL_NAME) *subject_alt_names; + + if (!info->peer_verify_domain) + return (1); + + if (SSL_get_verify_result(ssl) != X509_V_OK) + return (1); + + x=SSL_get_peer_certificate(ssl); + + if (!x) + return (0); + + subject_alt_names= + X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); + + rc=verifypeer1(info, x, subject_alt_names); + + if (subject_alt_names) + GENERAL_NAMES_free(subject_alt_names); + + X509_free(x); + + return rc; +} + +static int verifypeer1(const struct tls_info *info, X509 *x, + STACK_OF(GENERAL_NAME) *subject_alt_names) +{ + X509_NAME *subj=NULL; + int nentries, j; + char domain[256]; + char errmsg[1000]; + + if(subject_alt_names) + { + int n=sk_GENERAL_NAME_num(subject_alt_names); + int i; + + for (i=0; i<n; i++) + { + const GENERAL_NAME *gn= + sk_GENERAL_NAME_value(subject_alt_names, i); + const char *str; + int l; + + if (gn->type != GEN_DNS) + continue; + +#ifdef HAVE_OPENSSL110 + str = (const char *)ASN1_STRING_get0_data(gn->d.ia5); +#else + str = (const char *)ASN1_STRING_data(gn->d.ia5); +#endif + l=ASN1_STRING_length(gn->d.ia5); + + if (l >= sizeof(domain)-1) + l=sizeof(domain)-1; + + memcpy(domain, str, l); + domain[l]=0; + + if (hostmatch(info, domain)) + return 1; + } + } + + subj=X509_get_subject_name(x); + + nentries=0; + if (subj) + nentries=X509_NAME_entry_count(subj); + + domain[0]=0; + for (j=0; j<nentries; j++) + { + const char *obj_name; + X509_NAME_ENTRY *e; + ASN1_OBJECT *o; + ASN1_STRING *d; + + int dlen; + const unsigned char *ddata; + + e=X509_NAME_get_entry(subj, j); + if (!e) + continue; + + o=X509_NAME_ENTRY_get_object(e); + d=X509_NAME_ENTRY_get_data(e); + + if (!o || !d) + continue; + + obj_name=OBJ_nid2sn(OBJ_obj2nid(o)); + + dlen=ASN1_STRING_length(d); +#ifdef HAVE_OPENSSL110 + ddata=ASN1_STRING_get0_data(d); +#else + ddata=ASN1_STRING_data(d); +#endif + if (strcasecmp(obj_name, "CN") == 0) + { + if (dlen >= sizeof(domain)-1) + dlen=sizeof(domain)-1; + + memcpy(domain, ddata, dlen); + domain[dlen]=0; + } + } + + if (domain[0] && hostmatch(info, domain)) + return 1; + + strcpy(errmsg, "couriertls: Mismatched SSL certificate: CN="); + strcat(errmsg, domain); + strcat(errmsg, " (expected "); + strncat(errmsg, info->peer_verify_domain, 256); + strcat(errmsg, ")"); + (*info->tls_err_msg)(errmsg, info->app_data); + return (0); +} +#endif static void nonsslerror(const struct tls_info *info, const char *pfix) { @@ -1083,32 +1277,37 @@ SSL *tls_connect(SSL_CTX *ctx, int fd) } else { + char *idn_domain1; + + if (idna_to_unicode_8z8z(info->peer_verify_domain, + &idn_domain1, 0) + != IDNA_SUCCESS) + idn_domain1=0; + SSL_set_connect_state(ssl); #ifdef HAVE_OPENSSL_SNI if (info->peer_verify_domain) { - SSL_set_tlsext_host_name(ssl, info->peer_verify_domain); + SSL_set_tlsext_host_name(ssl, + idn_domain1 ? idn_domain1 + : (char *) + info->peer_verify_domain); } #endif +#if HAVE_X509_VERIFY_PARAM_SET1_HOST if (info->peer_verify_domain) { - char *idn_domain1; - - if (idna_to_unicode_8z8z(info->peer_verify_domain, - &idn_domain1, 0) - != IDNA_SUCCESS) - idn_domain1=0; - X509_VERIFY_PARAM *param = SSL_get0_param(ssl); X509_VERIFY_PARAM_set1_host(param, idn_domain1 ? idn_domain1 : info->peer_verify_domain, 0); - if (idn_domain1) - free(idn_domain1); } +#endif + if (idn_domain1) + free(idn_domain1); if ((rc=SSL_connect(ssl)) > 0) { |
