diff options
| -rw-r--r-- | imap/configure.ac | 2 | ||||
| -rw-r--r-- | tcpd/configure.ac | 2 | ||||
| -rw-r--r-- | tcpd/libcouriertls.c | 219 |
3 files changed, 211 insertions, 12 deletions
diff --git a/imap/configure.ac b/imap/configure.ac index 9bf0201..6ac8577 100644 --- a/imap/configure.ac +++ b/imap/configure.ac @@ -4,7 +4,7 @@ dnl dnl Copyright 1998 - 2019 Double Precision, Inc. See COPYING for dnl distribution information. -AC_INIT(courier-imap, 5.0.8.20191205, [courier-users@lists.sourceforge.net]) +AC_INIT(courier-imap, 5.0.8.20191210, [courier-users@lists.sourceforge.net]) >confdefs.h # Kill PACKAGE_ macros diff --git a/tcpd/configure.ac b/tcpd/configure.ac index 872a0ad..08ba7d0 100644 --- a/tcpd/configure.ac +++ b/tcpd/configure.ac @@ -552,7 +552,7 @@ RAND_bytes(dummy, 1); ] ) LIBS="-lssl $LIBS" - AC_CHECK_FUNCS(TLSv1_1_method TLSv1_2_method) + AC_CHECK_FUNCS(TLSv1_1_method TLSv1_2_method X509_VERIFY_PARAM_set1_host) LIBS="$save_LIBS" AC_TRY_COMPILE( [ 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) { |
