diff options
| author | Sam Varshavchik | 2019-02-03 11:34:32 -0500 |
|---|---|---|
| committer | Sam Varshavchik | 2019-02-03 12:10:03 -0500 |
| commit | 7dab8cfd4e751ff4f8d505bc4a5f6b4ec157406a (patch) | |
| tree | ef777c865d450e2fe30a97a242368b741b65a2ff | |
| parent | a6868f558661375ef3873ab54b39d282e09868fc (diff) | |
| download | courier-libs-7dab8cfd4e751ff4f8d505bc4a5f6b4ec157406a.tar.bz2 | |
Add subject alternative name checking. Implement override_vars option.
| -rw-r--r-- | tcpd/configure.ac | 7 | ||||
| -rw-r--r-- | tcpd/libcouriertls.c | 193 | ||||
| -rw-r--r-- | tcpd/tlsclient.c | 8 | ||||
| -rw-r--r-- | tcpd/tlsclient.h | 2 |
4 files changed, 176 insertions, 34 deletions
diff --git a/tcpd/configure.ac b/tcpd/configure.ac index 8f6d1fc..872a0ad 100644 --- a/tcpd/configure.ac +++ b/tcpd/configure.ac @@ -165,6 +165,13 @@ fi dnl Checks for library functions. +PKG_CHECK_MODULES(LIBIDN, libidn >= 0.0.0, [libidn=yes], [libidn=no]) + +if test "$libidn" != "yes" +then + AC_MSG_ERROR([libidn not found]) +fi + AC_CHECK_FUNCS(setpgrp setpgid) AC_CHECK_FUNC(setpgrp, [ diff --git a/tcpd/libcouriertls.c b/tcpd/libcouriertls.c index d62d722..1915ee0 100644 --- a/tcpd/libcouriertls.c +++ b/tcpd/libcouriertls.c @@ -9,6 +9,7 @@ #include "libcouriertls.h" #include <openssl/rand.h> #include <openssl/x509.h> +#include <openssl/x509v3.h> #include "tlscache.h" #include "rfc1035/rfc1035.h" #include "soxwrap/soxwrap.h" @@ -21,6 +22,7 @@ #include <stdlib.h> #include <ctype.h> #include <netdb.h> +#include <idna.h> #if HAVE_DIRENT_H #include <dirent.h> #define NAMLEN(dirent) strlen((dirent)->d_name) @@ -140,38 +142,141 @@ static int ssl_verify_callback(int goodcert, X509_STORE_CTX *x509) return (1); } +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; - X509_NAME *subj=NULL; - int nentries, j; - char domain[256]; - char *p; - char errmsg[1000]; + int rc; + STACK_OF(GENERAL_NAME) *subject_alt_names; if (!info->peer_verify_domain) return (1); - if (info->isserver) - { - x=SSL_get_peer_certificate(ssl); + if (SSL_get_verify_result(ssl) != X509_V_OK) + return (1); - if (x) - subj=X509_get_subject_name(x); - } - else + 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) { - STACK_OF(X509) *peer_cert_chain=SSL_get_peer_cert_chain(ssl); + int n=sk_GENERAL_NAME_num(subject_alt_names); + int i; - if (peer_cert_chain && sk_X509_num(peer_cert_chain) > 0) + for (i=0; i<n; i++) { - X509 *xx=sk_X509_value(peer_cert_chain, 0); + const GENERAL_NAME *gn= + sk_GENERAL_NAME_value(subject_alt_names, i); + const char *str; + int l; - if (xx) - subj=X509_get_subject_name(xx); + 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) @@ -216,23 +321,8 @@ static int verifypeer(const struct tls_info *info, SSL *ssl) } } - if (x) - X509_free(x); - p=domain; - - if (*p == '*') - { - int pl, l; - - pl=strlen(++p); - l=strlen(info->peer_verify_domain); - - if (*p == '.' && pl <= l && - strcasecmp(info->peer_verify_domain+l-pl, p) == 0) - return (1); - } - else if (strcasecmp(info->peer_verify_domain, p) == 0) - return (1); + if (domain[0] && hostmatch(info, domain)) + return 1; strcpy(errmsg, "couriertls: Mismatched SSL certificate: CN="); strcat(errmsg, domain); @@ -1474,6 +1564,8 @@ static void dump_x509(X509 *x509, X509_NAME *subj=X509_get_subject_name(x509); int nentries, j; time_t timestamp; + STACK_OF(GENERAL_NAME) *subject_alt_names; + static const char gcc_shutup[]="%Y-%m-%d %H:%M:%S"; if (!subj) @@ -1520,6 +1612,39 @@ static void dump_x509(X509 *x509, } (*dump_func)("\n", 1, dump_arg); + subject_alt_names= + X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL); + + 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 *domain; + int l; + + if (gn->type != GEN_DNS) + continue; + +#ifdef HAVE_OPENSSL110 + domain = (const char *)ASN1_STRING_get0_data(gn->d.ia5); +#else + domain = (const char *)ASN1_STRING_data(gn->d.ia5); +#endif + l=ASN1_STRING_length(gn->d.ia5); + + (*dump_func)("Subject-Alt-Name-DNS: ", -1, dump_arg); + (*dump_func)(domain, l, dump_arg); + (*dump_func)("\n", -1, dump_arg); + } + + GENERAL_NAMES_free(subject_alt_names); + } + timestamp=asn1toTime(X509_get_notBefore(x509)); if (timestamp) diff --git a/tcpd/tlsclient.c b/tcpd/tlsclient.c index 7efa574..c7be1f3 100644 --- a/tcpd/tlsclient.c +++ b/tcpd/tlsclient.c @@ -379,6 +379,14 @@ static int do_couriertls_start(char **args, struct couriertls_info *cinfo) if (!s || !*s) s="couriertls"; + if (cinfo->override_vars) + { + size_t i; + + for (i=0; cinfo->override_vars[i]; ++i) + putenv(cinfo->override_vars[i]); + } + execv(s, argvec); fprintf(fp, "500 Unable to start couriertls: %s\n", strerror(errno)); diff --git a/tcpd/tlsclient.h b/tcpd/tlsclient.h index b8eadbc..b624209 100644 --- a/tcpd/tlsclient.h +++ b/tcpd/tlsclient.h @@ -32,6 +32,8 @@ struct couriertls_info { size_t x509info_len; size_t x509info_size; + char **override_vars; + struct tls_subject *first_subject; const char *cipher; |
