diff options
Diffstat (limited to 'tcpd/libcouriergnutls.c')
| -rw-r--r-- | tcpd/libcouriergnutls.c | 238 | 
1 files changed, 211 insertions, 27 deletions
| diff --git a/tcpd/libcouriergnutls.c b/tcpd/libcouriergnutls.c index f3c34d4..5fc0ef9 100644 --- a/tcpd/libcouriergnutls.c +++ b/tcpd/libcouriergnutls.c @@ -1,5 +1,5 @@  /* -** Copyright 2007-2018 Double Precision, Inc. +** Copyright 2007-2019 Double Precision, Inc.  ** See COPYING for distribution information.  */  #include	"config.h" @@ -18,6 +18,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) @@ -126,6 +127,7 @@ struct ssl_context_t {  	const char *priority_list;  	char *certfile; +	char *keyfile;  	char *dhfile;  	char *trustcerts; @@ -267,6 +269,17 @@ ssl_context tls_create(int isserver, const struct tls_info *info)  			free(certfile);  	} +	if ((certfile=strdup(safe_getenv(p, "TLS_PRIVATE_KEYFILE", ""))) != NULL && +	    *certfile) +	{ +		p->keyfile=certfile; +	} +	else +	{ +		if (certfile) +			free(certfile); +	} +  	switch (*safe_getenv(p, "TLS_VERIFYPEER", "P")) {  	case 'n':  	case 'N': @@ -320,6 +333,8 @@ void tls_destroy(ssl_context p)  {  	if (p->certfile)  		free(p->certfile); +	if (p->keyfile) +		free(p->keyfile);  	if (p->dhfile)  		free(p->dhfile);  	if (p->trustcerts) @@ -363,7 +378,7 @@ static int read_cert_dir(const char *cert_dir,  		strcat(strcat(strcpy(buf, cert_dir), "/"), de->d_name); -		if (lstat(buf, &stat_buf) < 0 || !S_ISREG(stat_buf.st_mode)) +		if (stat(buf, &stat_buf) < 0 || !S_ISREG(stat_buf.st_mode))  		{  			free(buf);  			continue; @@ -539,6 +554,37 @@ static int chk_error(int rc, ssl_handle ssl, int fd, fd_set *r, fd_set *w,  	return 0;  } +static int name_check(ssl_handle ssl, +		      gnutls_x509_crt_t cert) +{ +	char *idn_domain; +	const char *p; +	int rc; + +	if (idna_to_unicode_8z8z(ssl->info_cpy.peer_verify_domain, +				 &idn_domain, 0) != IDNA_SUCCESS) +		idn_domain=0; + +	p=idn_domain ? idn_domain:ssl->info_cpy.peer_verify_domain; + +	rc=gnutls_x509_crt_check_hostname(cert, p); + +	if (idn_domain) +	{ +		free(idn_domain); + +		/* Now try the ACE-encoded hostname for a filename. */ + +		if (rc == 0) +		{ +			p=ssl->info_cpy.peer_verify_domain; +			rc=gnutls_x509_crt_check_hostname(cert, p); +		} +	} +	return rc; +} + +  static int verify_client(ssl_handle ssl, int fd)  {  	unsigned int status; @@ -632,10 +678,7 @@ static int verify_client(ssl_handle ssl, int fd)  		if (ssl->info_cpy.peer_verify_domain &&  		    *ssl->info_cpy.peer_verify_domain && -		    !gnutls_x509_crt_check_hostname(cert, -						    ssl->info_cpy -						    .peer_verify_domain -						    )) +		    !name_check(ssl, cert))  		{  			char hostname[256];  			size_t hostname_size=sizeof(hostname); @@ -703,10 +746,10 @@ static int dohandshake(ssl_handle ssl, int fd, fd_set *r, fd_set *w)  	return rc;  } -static char *check_cert(const char *filename, -			gnutls_certificate_type_t cert_type, -			const char *req_dn, -			int isvirtual) +static char *check_cert_unicode(const char *filename, +				gnutls_certificate_type_t cert_type, +				const char *req_dn, +				int isvirtual)  {  	if (!filename || !*filename)  		return NULL; @@ -733,6 +776,12 @@ static char *check_cert(const char *filename,  		++req_dn;  	} +	/* +	** We're called with a hostname first. Don't check the defualt +	** filename, we'll be called again with an IP address +	*/ + +	if (!isvirtual)  	{  		char *p=malloc(strlen(filename)+10); @@ -749,6 +798,41 @@ static char *check_cert(const char *filename,  	return NULL;  } +static char *check_cert(const char *filename, +			gnutls_certificate_type_t cert_type, +			const char *req_dn, +			int isvirtual) +{ +	if (isvirtual) +	{ +		char *p; +		char *retfile; + +		if (idna_to_ascii_8z(req_dn, &p, 0) != IDNA_SUCCESS) +			p=0; + +		if (p) +		{ +			retfile=check_cert_unicode(filename, cert_type, p, +						    isvirtual); +			free(p); + +			if (retfile) +				return retfile; +		} +	} + +	return check_cert_unicode(filename, cert_type, req_dn, isvirtual); +} + +static char *check_key(const char *filename, +			gnutls_certificate_type_t cert_type, +			const char *req_dn, +			int isvirtual) +{ +	return check_cert(filename, cert_type, req_dn, isvirtual); +} +  static int read_file(const char *file,  		     gnutls_datum_t *filebuf)  { @@ -790,10 +874,12 @@ static void release_file(gnutls_datum_t *filebuf)  static int set_cert(ssl_handle ssl,  		    gnutls_session_t session,  		    gnutls_retr2_st *st, -		    const char *certfilename) +		    const char *certfilename, +		    const char *keyfilename)  {  	int rc;  	gnutls_datum_t filebuf; +	gnutls_datum_t keyfilebuf;  	unsigned int cert_cnt;  	st->ncerts=0; @@ -803,15 +889,34 @@ static int set_cert(ssl_handle ssl,  	if ((rc=read_file(certfilename, &filebuf)) < 0)  		return rc; +	if (keyfilename) +	{ +		if ((rc=read_file(keyfilename, &keyfilebuf)) < 0) +		{ +			release_file(&filebuf); +			return rc; +		} +	} +  	switch (st->cert_type) {  	case GNUTLS_CRT_X509:  		cert_cnt=0; -		if ((rc=gnutls_x509_privkey_init(&ssl->x509_key)) < 0 || -		    (rc=gnutls_x509_privkey_import(ssl->x509_key, &filebuf, -						   GNUTLS_X509_FMT_PEM)) < 0) -			break; +		if (keyfilename) +		{ +			if ((rc=gnutls_x509_privkey_init(&ssl->x509_key)) < 0 || +			    (rc=gnutls_x509_privkey_import(ssl->x509_key, &keyfilebuf, +							   GNUTLS_X509_FMT_PEM)) < 0) +				break; +		} +		else +		{ +			if ((rc=gnutls_x509_privkey_init(&ssl->x509_key)) < 0 || +			    (rc=gnutls_x509_privkey_import(ssl->x509_key, &filebuf, +							   GNUTLS_X509_FMT_PEM)) < 0) +				break; +		}  		rc=gnutls_x509_crt_list_import(NULL, &cert_cnt,  					       &filebuf, @@ -846,6 +951,8 @@ static int set_cert(ssl_handle ssl,  	}  	release_file(&filebuf); +	if (keyfilename) +		release_file(&keyfilebuf);  	return 0;  } @@ -862,6 +969,7 @@ static int get_server_cert(gnutls_session_t session,  	size_t vhost_size;  	unsigned int type=GNUTLS_NAME_DNS;  	char *certfilename=NULL; +	char *keyfilename=NULL;  	int rc;  	st->cert_type=gnutls_certificate_type_get(session); @@ -887,28 +995,51 @@ static int get_server_cert(gnutls_session_t session,  	     ++vhost_idx)  	{  		char *p; +		char *utf8; +		char *namebuf; + +		/* Convert to UTF8 */ +		if (idna_to_unicode_8z8z(vhost_buf, &utf8, 0) +		    != IDNA_SUCCESS) +			utf8=0; -		for (p=vhost_buf; *p; p++) +		namebuf=utf8 ? utf8:vhost_buf; + +		for (p=namebuf; *p; p++)  			if (*p == '/')  				*p='.'; /* Script kiddie check */  		if (ssl->ctx->certfile)  			certfilename=check_cert(ssl->ctx->certfile,  						st->cert_type, -						vhost_buf, 1); +						namebuf, 1); + +		if (ssl->ctx->keyfile) +			keyfilename=check_key(ssl->ctx->keyfile, +						st->cert_type, +						namebuf, 1); +		if (utf8) +			free(utf8);  		if (certfilename)  			break;  	}  	if (!certfilename)  	{ +		const char *ip= +			safe_getenv(ssl->ctx, +				    "TCPLOCALIP", ""); + +		if (strncmp(ip, "::ffff:", 7) == 0 && strchr(ip, '.')) +			ip += 7; +  		if (ssl->ctx->certfile)  			certfilename=check_cert(ssl->ctx->certfile, -						st->cert_type, -						safe_getenv(ssl->ctx, -							    "TCPLOCALIP", ""), -						0); +						st->cert_type, ip, 0); +		if (ssl->ctx->keyfile) +			keyfilename=check_key(ssl->ctx->keyfile, +					      st->cert_type, ip, 0);  	}  	if (!certfilename) @@ -918,8 +1049,10 @@ static int get_server_cert(gnutls_session_t session,  		return 0;  	} -	rc=set_cert(ssl, session, st, certfilename); +	rc=set_cert(ssl, session, st, certfilename, keyfilename);  	free(certfilename); +	if (keyfilename) +		free(keyfilename);  	return rc;  } @@ -1072,6 +1205,7 @@ static int get_client_cert(gnutls_session_t session,  	ssl_handle ssl=(ssl_handle)gnutls_session_get_ptr(session);  	int rc;  	char *certfilename=NULL; +	char *keyfilename=NULL;  	rc= 0;  	st->cert_type=gnutls_certificate_type_get(session); @@ -1080,12 +1214,16 @@ static int get_client_cert(gnutls_session_t session,  		certfilename=check_cert(ssl->ctx->certfile,  					st->cert_type, "", 0); +	if (ssl->ctx->keyfile) +		keyfilename=check_key(ssl->ctx->keyfile, +					st->cert_type, "", 0); +  	st->ncerts=0;  	st->deinit_all=0;  	if (certfilename)  	{ -		rc=set_cert(ssl, session, st, certfilename); +		rc=set_cert(ssl, session, st, certfilename, keyfilename);  		free(certfilename);  	}  	else @@ -1095,6 +1233,10 @@ static int get_client_cert(gnutls_session_t session,  		if (rc > 0)  			rc=0;  	} + +	if (keyfilename) +		free(keyfilename); +  	return rc;  } @@ -1225,6 +1367,26 @@ static gnutls_datum_t db_retrieve_func(void *dummy, gnutls_datum_t key)  	return drs.ret;  } +static int name_set(ssl_handle ssl, ssl_context ctx) +{ +	char *idn_domain; +	const char *p; +	int rc; + +	if (idna_to_unicode_8z8z(ctx->info_cpy.peer_verify_domain, +				 &idn_domain, 0) != IDNA_SUCCESS) +		idn_domain=0; + +	p=idn_domain ? idn_domain:ctx->info_cpy.peer_verify_domain; + +	rc=gnutls_server_name_set(ssl->session, GNUTLS_NAME_DNS, +				  p, strlen(p)); + +	if (idn_domain) +		free(idn_domain); +	return rc; +} +  ssl_handle tls_connect(ssl_context ctx, int fd)  {  	ssl_handle ssl=malloc(sizeof(struct ssl_handle_t)); @@ -1366,10 +1528,7 @@ RT |  				   ssl->xcred) < 0 ||  	    (ctx->info_cpy.peer_verify_domain && -	     gnutls_server_name_set(ssl->session, GNUTLS_NAME_DNS, -				    ctx->info_cpy.peer_verify_domain, -				    strlen(ctx->info_cpy.peer_verify_domain)) -	     < 0) +	     name_set(ssl, ctx) < 0)  	    )  	{  		tls_free_session(ssl); @@ -1806,6 +1965,8 @@ static void tls_dump_connection_info_x509(ssl_handle ssl,  		{  			time_t notbefore;  			time_t notafter; +			char buffer[256]; +			unsigned j;  			(*dump_func)("Subject:\n", -1, dump_arg); @@ -1815,6 +1976,29 @@ static void tls_dump_connection_info_x509(ssl_handle ssl,  				dump_func, dump_arg);  			(*dump_func)("\n", 1, dump_arg); +			for (j=0; ; ++j) +			{ +				size_t s=sizeof(buffer); +				enum gnutls_x509_subject_alt_name_t t; + +				t=gnutls_x509_crt_get_subject_alt_name +					(cert[i], j, buffer, &s, 0); + +				if (t == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) +					break; + +				if (t != GNUTLS_SAN_DNSNAME) +					continue; + +				if (s == sizeof(buffer)) +					--s; /* The API is not clear */ + +				buffer[s]=0; +				(*dump_func)("Subject-Alt-Name-DNS: ", -1, +					     dump_arg); +				(*dump_func)(buffer, -1, dump_arg); +				(*dump_func)("\n", -1, dump_arg); +			}  #if 0  			(*dump_func)("Issuer:\n", -1, dump_arg); | 
