diff options
| -rw-r--r-- | imap/ChangeLog | 4 | ||||
| -rw-r--r-- | imap/imapd-ssl.dist.in.git | 35 | ||||
| -rw-r--r-- | imap/pop3d-ssl.dist.in.git | 35 | ||||
| -rw-r--r-- | tcpd/configure.ac | 17 | ||||
| -rw-r--r-- | tcpd/libcouriergnutls.c | 20 | ||||
| -rw-r--r-- | tcpd/libcouriertls.c | 79 | 
6 files changed, 137 insertions, 53 deletions
| diff --git a/imap/ChangeLog b/imap/ChangeLog index 660967b..f3ffcca 100644 --- a/imap/ChangeLog +++ b/imap/ChangeLog @@ -1,3 +1,7 @@ +2016-03-03  Sam Varshavchik  <mrsam@courier-mta.com> + +	* libs/tcpd/libcouriertls.c: Add support for TLS SNI. +  2016-01-22  Sam Varshavchik  <mrsam@courier-mta.com>  	* imaplogin.c (starttls): flush stdin after negotiating STARTTLS. diff --git a/imap/imapd-ssl.dist.in.git b/imap/imapd-ssl.dist.in.git index 7fede5d..20620cc 100644 --- a/imap/imapd-ssl.dist.in.git +++ b/imap/imapd-ssl.dist.in.git @@ -5,7 +5,7 @@  # Do not alter lines that begin with ##, they are used when upgrading  # this configuration.  # -#  Copyright 2000 - 2013 Double Precision, Inc.  See COPYING for +#  Copyright 2000 - 2016 Double Precision, Inc.  See COPYING for  #  distribution information.  #  #  This configuration file sets various options for the Courier-IMAP server @@ -220,30 +220,29 @@ COURIERTLS=@bindir@/couriertls  # treated as confidential, and must not be world-readable. Set TLS_CERTFILE  # instead of TLS_DHCERTFILE if this is a garden-variety certificate  # -# VIRTUAL HOSTS (servers only): +# VIRTUAL HOSTS ON THE SAME IP ADDRESS.  # -# Due to technical limitations in the original SSL/TLS protocol, a dedicated -# IP address is required for each virtual host certificate. If you have -# multiple certificates, install each certificate file as +# Install each certificate $TLS_CERTFILE.domain, so if TLS_CERTFILE is set to +# /etc/certificate.pem, then you'll need to install the actual certificate +# files as /etc/certificate.pem.www.example.com, +# /etc/certificate.pem.www.domain.com and so on. Then, create a link from +# $TLS_CERTFILE to whichever certificate you consider to be the main one, +# for example: +# /etc/certificate.pem => /etc/certificate.pem.www.example.com +# +# IP-BASED VIRTUAL HOSTS: +# +# There may be a need to support older SSL/TLS client that don't support +# virtual hosts on the same IP address, and require a dedicated IP address +# for each SSL/TLS host. If so, install each certificate file as  # $TLS_CERTFILE.aaa.bbb.ccc.ddd, where "aaa.bbb.ccc.ddd" is the IP address  # for the certificate's domain name. So, if TLS_CERTFILE is set to  # /etc/certificate.pem, then you'll need to install the actual certificate  # files as /etc/certificate.pem.192.168.0.2, /etc/certificate.pem.192.168.0.3  # and so on, for each IP address.  # -# GnuTLS only (servers only): -# -# GnuTLS implements a new TLS extension that eliminates the need to have a -# dedicated IP address for each SSL/TLS domain name. Install each certificate -# as $TLS_CERTFILE.domain, so if TLS_CERTFILE is set to /etc/certificate.pem, -# then you'll need to install the actual certificate files as -# /etc/certificate.pem.host1.example.com, /etc/certificate.pem.host2.example.com -# and so on. -# -# Note that this TLS extension also requires a corresponding support in the -# client. Older SSL/TLS clients may not support this feature. -# -# This is an experimental feature. +# In all cases, $TLS_CERTFILE needs to be linked to one of the existing +# certificate files.  TLS_CERTFILE=@certsdir@/imapd.pem diff --git a/imap/pop3d-ssl.dist.in.git b/imap/pop3d-ssl.dist.in.git index 89d6e7d..70ee341 100644 --- a/imap/pop3d-ssl.dist.in.git +++ b/imap/pop3d-ssl.dist.in.git @@ -5,7 +5,7 @@  # Do not alter lines that begin with ##, they are used when upgrading  # this configuration.  # -#  Copyright 2000-2013 Double Precision, Inc.  See COPYING for +#  Copyright 2000-2016 Double Precision, Inc.  See COPYING for  #  distribution information.  #  #  This configuration file sets various options for the Courier-IMAP server @@ -186,30 +186,29 @@ COURIERTLS=@bindir@/couriertls  # treated as confidential, and must not be world-readable. Set TLS_CERTFILE  # instead of TLS_DHCERTFILE if this is a garden-variety certificate  # -# VIRTUAL HOSTS (servers only): +# VIRTUAL HOSTS ON THE SAME IP ADDRESS.  # -# Due to technical limitations in the original SSL/TLS protocol, a dedicated -# IP address is required for each virtual host certificate. If you have -# multiple certificates, install each certificate file as +# Install each certificate $TLS_CERTFILE.domain, so if TLS_CERTFILE is set to +# /etc/certificate.pem, then you'll need to install the actual certificate +# files as /etc/certificate.pem.www.example.com, +# /etc/certificate.pem.www.domain.com and so on. Then, create a link from +# $TLS_CERTFILE to whichever certificate you consider to be the main one, +# for example: +# /etc/certificate.pem => /etc/certificate.pem.www.example.com +# +# IP-BASED VIRTUAL HOSTS: +# +# There may be a need to support older SSL/TLS client that don't support +# virtual hosts on the same IP address, and require a dedicated IP address +# for each SSL/TLS host. If so, install each certificate file as  # $TLS_CERTFILE.aaa.bbb.ccc.ddd, where "aaa.bbb.ccc.ddd" is the IP address  # for the certificate's domain name. So, if TLS_CERTFILE is set to  # /etc/certificate.pem, then you'll need to install the actual certificate  # files as /etc/certificate.pem.192.168.0.2, /etc/certificate.pem.192.168.0.3  # and so on, for each IP address.  # -# GnuTLS only (servers only): -# -# GnuTLS implements a new TLS extension that eliminates the need to have a -# dedicated IP address for each SSL/TLS domain name. Install each certificate -# as $TLS_CERTFILE.domain, so if TLS_CERTFILE is set to /etc/certificate.pem, -# then you'll need to install the actual certificate files as -# /etc/certificate.pem.host1.example.com, /etc/certificate.pem.host2.example.com -# and so on. -# -# Note that this TLS extension also requires a corresponding support in the -# client. Older SSL/TLS clients may not support this feature. -# -# This is an experimental feature. +# In all cases, $TLS_CERTFILE needs to be linked to one of the existing +# certificate files.  TLS_CERTFILE=@certsdir@/pop3d.pem diff --git a/tcpd/configure.ac b/tcpd/configure.ac index 6ba8acf..447ba72 100644 --- a/tcpd/configure.ac +++ b/tcpd/configure.ac @@ -134,12 +134,12 @@ AC_SYS_LARGEFILE  AC_CACHE_CHECK([for socklen_t],  	tcpd_cv_hassocklen_t, -  +  AC_COMPILE_IFELSE([  AC_LANG_SOURCE( [  #include <sys/types.h>  #include <sys/socket.h> -  +  socklen_t sl_t;  ],[  	accept(0, 0, &sl_t); @@ -147,9 +147,9 @@ socklen_t sl_t;  	tcpd_cv_hassocklen_t=yes,  	tcpd_cv_hassocklen_t=no)  ) -  +  socklen_t="int" -  +  if test $tcpd_cv_hassocklen_t = yes  then  	: @@ -510,6 +510,15 @@ RAND_pseudo_bytes(dummy, 1);  		AC_CHECK_FUNCS(TLSv1_1_method TLSv1_2_method)  		LIBS="$save_LIBS" +		AC_TRY_COMPILE( [ +#include <openssl/ssl.h> +], +[ +SSL_get_servername((SSL *)0, TLSEXT_NAMETYPE_host_name); +], [ +                AC_DEFINE_UNQUOTED(HAVE_OPENSSL_SNI,1,[ When OpenSSL supports SNI ]) +		]) +  		TLSLIBRARY="$LIBCOURIERTLSOPENSSL"  		STARTTLS=couriertls$EXEEXT  		BUILDLIBCOURIERTLS=libcouriertls.la diff --git a/tcpd/libcouriergnutls.c b/tcpd/libcouriergnutls.c index 20823a9..38b70ab 100644 --- a/tcpd/libcouriergnutls.c +++ b/tcpd/libcouriergnutls.c @@ -718,7 +718,7 @@ static int verify_client(ssl_handle ssl, int fd)  		    !gnutls_openpgp_key_check_hostname(cert,  						       ssl->info_cpy  						       .peer_verify_domain)) -						       +  		{  			char *hostname;  			size_t hostnamesiz=0; @@ -784,7 +784,7 @@ static int dohandshake(ssl_handle ssl, int fd, fd_set *r, fd_set *w)  	{  		ssl->info_cpy.connect_interrupted=0; -		 +  		if (verify_client(ssl, fd))  			return -1; @@ -1003,7 +1003,7 @@ static int get_server_cert(gnutls_session_t session,  		for (p=vhost_buf; *p; p++)  			if (*p == '/') -				*p='.'; +				*p='.'; /* Script kiddie check */  		if (ssl->ctx->certfile)  			certfilename=check_cert(ssl->ctx->certfile, @@ -1273,7 +1273,7 @@ static int do_cache_remove(void *rec, size_t recsize, int *doupdate, void *arg)  	}  	return 0;  } -	 +  static int db_remove_func(void *dummy, gnutls_datum_t key)  {  	tls_cache_walk(((ssl_handle)dummy)->info_cpy.tlscache, @@ -1444,9 +1444,9 @@ ssl_handle tls_connect(ssl_context ctx, int fd)  	gnutls_session_set_ptr(ssl->session, ssl);          gnutls_handshake_set_private_extensions(ssl->session, 1); -        gnutls_certificate_set_verify_flags(ssl->xcred,  +        gnutls_certificate_set_verify_flags(ssl->xcred,                                              GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT | -                                             +                                              /*                                              GNUTLS_VERIFY_DO_NOT_ALLOW_SAME |                                              GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_C @@ -1567,7 +1567,7 @@ int	tls_transfer(struct tls_transfer_info *t, ssl_handle ssl, int fd,  			return 1;  		} -			 +  		t->shutdown_interrupted=0;  		t->shutdown= -1;  		return -1; @@ -1717,7 +1717,7 @@ static const char *dump_dn(gnutls_x509_crt_t cert,  			free(oidname);  			return gnutls_strerror(rc);  		} -	 +  		vidx=0;  		while (bufsiz=0, @@ -1781,7 +1781,7 @@ static const char *dump_dn(gnutls_x509_crt_t cert,  			++vidx;  		}  	} -	 +  	free(oidval);  	free(oidname);  	return NULL; @@ -1870,7 +1870,7 @@ static void dump_cipher_name(gnutls_session_t session,  		gnutls_compression_method_t comp;  		(*dump_func)(gnutls_kx_get_name(kx_algo), -1, dump_arg); -		 +  		(*dump_func)("-", 1, dump_arg);  		(*dump_func)(gnutls_certificate_type_get_name(gnutls_certificate_type_get(session)),  			     -1, dump_arg); diff --git a/tcpd/libcouriertls.c b/tcpd/libcouriertls.c index 9252c05..985c76e 100644 --- a/tcpd/libcouriertls.c +++ b/tcpd/libcouriertls.c @@ -1,5 +1,5 @@  /* -** Copyright 2000-2014 Double Precision, Inc. +** Copyright 2000-2016 Double Precision, Inc.  ** See COPYING for distribution information.  */  #include	"config.h" @@ -308,7 +308,24 @@ static void load_dh_params(SSL_CTX *ctx, const char *filename,  			DH_free(dh);  		}  		else -			sslerror(info, filename, -1); +		{ +			/* +			** If the certificate file does not have DH parameters, +			** swallow the error. +			*/ + +			int err=ERR_peek_last_error(); + +			if (ERR_GET_LIB(err) == ERR_LIB_PEM +			    && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) +			{ +				ERR_clear_error(); +			} +			else +			{ +				sslerror(info, filename, -1); +			} +		}  		BIO_free(bio);  	}  	else @@ -476,6 +493,47 @@ static int client_cert_cb(ssl_handle ssl, X509 **x509, EVP_PKEY **pkey)  	return rc;  } +static int server_cert_cb(ssl_handle ssl, int *ad, void *arg) +{ +#ifdef HAVE_OPENSSL_SNI +	struct tls_info *info=(struct tls_info *)SSL_get_app_data(ssl); +	const char *servername=SSL_get_servername(ssl, +						  TLSEXT_NAMETYPE_host_name); +	const char *certfile=safe_getenv(info, "TLS_CERTFILE"); +	int cert_file_flags=0; +	char *buffer; +	char *p; + +	if (!servername || !certfile) +		return SSL_TLSEXT_ERR_OK; + +	buffer=malloc(strlen(certfile)+strlen(servername)+2); +	if (!buffer) +	{ +		nonsslerror(info, "malloc"); +		exit(1); +	} + +	strcat(strcpy(buffer, certfile), "."); + +	p=buffer + strlen(buffer); + +	while ((*p=*servername) != 0) +	{ +		if (*p == '/') +			*p='.'; /* Script kiddie check */ +		++p; +		++servername; +	} + +	if (access(buffer, R_OK) == 0) +		read_certfile(SSL_get_SSL_CTX(ssl), buffer, &cert_file_flags); + +	free(buffer); +#endif +	return SSL_TLSEXT_ERR_OK; +} +  SSL_CTX *tls_create(int isserver, const struct tls_info *info)  {  	SSL_CTX *ctx; @@ -691,8 +749,15 @@ SSL_CTX *tls_create(int isserver, const struct tls_info *info)  	}  	SSL_CTX_set_verify(ctx, get_peer_verify_level(info),  			   ssl_verify_callback); -	if (!isserver) + +	if (isserver) +	{ +		SSL_CTX_set_tlsext_servername_callback(ctx, server_cert_cb); +	} +	else +	{  		SSL_CTX_set_client_cert_cb(ctx, client_cert_cb); +	}  	return (ctx);  } @@ -997,6 +1062,14 @@ SSL *tls_connect(SSL_CTX *ctx, int fd)  	{  		SSL_set_connect_state(ssl); +#ifdef HAVE_OPENSSL_SNI +		if (info->peer_verify_domain) +		{ +			fprintf(stderr, "Requesting %s\n", info->peer_verify_domain); +			SSL_set_tlsext_host_name(ssl, info->peer_verify_domain); +		} +#endif +  		if ((rc=SSL_connect(ssl)) > 0)  		{  			if (!verifypeer(info, ssl)) | 
