summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Varshavchik2019-02-03 11:34:32 -0500
committerSam Varshavchik2019-02-03 12:10:03 -0500
commit7dab8cfd4e751ff4f8d505bc4a5f6b4ec157406a (patch)
treeef777c865d450e2fe30a97a242368b741b65a2ff
parenta6868f558661375ef3873ab54b39d282e09868fc (diff)
downloadcourier-libs-7dab8cfd4e751ff4f8d505bc4a5f6b4ec157406a.tar.bz2
Add subject alternative name checking. Implement override_vars option.
-rw-r--r--tcpd/configure.ac7
-rw-r--r--tcpd/libcouriertls.c193
-rw-r--r--tcpd/tlsclient.c8
-rw-r--r--tcpd/tlsclient.h2
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;