diff options
| author | Sam Varshavchik | 2018-09-24 19:46:00 -0400 | 
|---|---|---|
| committer | GitHub | 2018-09-24 19:46:00 -0400 | 
| commit | ec5264047dfaf66c61cd0f4c9b8dd40463693722 (patch) | |
| tree | be38cfb21c064f485caa62a6dd7fdf1fd29ab4a4 /tcpd | |
| parent | 1d5b075408e8829006d84ba65b922101bd304a25 (diff) | |
| parent | 141976c61fd8e7cabe1bf1d0a6ea10dae7801a39 (diff) | |
| download | courier-libs-ec5264047dfaf66c61cd0f4c9b8dd40463693722.tar.bz2 | |
Merge pull request #13 from mumumu/new_feature_TLS_PRIVATE_KEYFILE
separate TLS private key from [imapd|pop3d].pem 
Diffstat (limited to 'tcpd')
| -rw-r--r-- | tcpd/couriertls.sgml | 10 | ||||
| -rw-r--r-- | tcpd/libcouriergnutls.c | 80 | ||||
| -rw-r--r-- | tcpd/libcouriertls.c | 138 | ||||
| -rw-r--r-- | tcpd/libcouriertls.h | 6 | 
4 files changed, 190 insertions, 44 deletions
| diff --git a/tcpd/couriertls.sgml b/tcpd/couriertls.sgml index a7a8e72..0711654 100644 --- a/tcpd/couriertls.sgml +++ b/tcpd/couriertls.sgml @@ -236,6 +236,16 @@ for SSL/TLS clients.        </varlistentry>        <varlistentry> +	<term>TLS_PRIVATE_KEYFILE=<replaceable>filename</replaceable></term> +	<listitem> +	  <para> +SSL/TLS private key for decrypting client data. +<envar>TLS_PRIVATE_KEY</envar> is optional because <term>TLS_CERTFILE</term> is generated including cert and private key both. +<replaceable>filename</replaceable> must not be world-readable.</para> +	</listitem> +      </varlistentry> + +      <varlistentry>  	<term>TLS_TRUSTCERTS=<replaceable>pathname</replaceable></term>  	<listitem>  	  <para> diff --git a/tcpd/libcouriergnutls.c b/tcpd/libcouriergnutls.c index f3c34d4..da57d1f 100644 --- a/tcpd/libcouriergnutls.c +++ b/tcpd/libcouriergnutls.c @@ -126,6 +126,7 @@ struct ssl_context_t {  	const char *priority_list;  	char *certfile; +	char *keyfile;  	char *dhfile;  	char *trustcerts; @@ -267,6 +268,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 +332,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) @@ -749,6 +763,14 @@ static char *check_cert(const char *filename,  	return NULL;  } +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 +812,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 +827,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 +889,8 @@ static int set_cert(ssl_handle ssl,  	}  	release_file(&filebuf); +	if (keyfilename) +		release_file(&keyfilebuf);  	return 0;  } @@ -862,6 +907,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); @@ -897,6 +943,11 @@ static int get_server_cert(gnutls_session_t session,  						st->cert_type,  						vhost_buf, 1); +		if (ssl->ctx->keyfile) +			keyfilename=check_key(ssl->ctx->keyfile, +						st->cert_type, +						vhost_buf, 1); +  		if (certfilename)  			break;  	} @@ -909,6 +960,12 @@ static int get_server_cert(gnutls_session_t session,  						safe_getenv(ssl->ctx,  							    "TCPLOCALIP", ""),  						0); +		if (ssl->ctx->keyfile) +			keyfilename=check_key(ssl->ctx->keyfile, +						st->cert_type, +						safe_getenv(ssl->ctx, +							    "TCPLOCALIP", ""), +						0);  	}  	if (!certfilename) @@ -918,8 +975,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 +1131,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,13 +1140,19 @@ 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); +		if (keyfilename) +			free(keyfilename);  	}  	else  	{ diff --git a/tcpd/libcouriertls.c b/tcpd/libcouriertls.c index 199015e..2eb2f96 100644 --- a/tcpd/libcouriertls.c +++ b/tcpd/libcouriertls.c @@ -332,10 +332,81 @@ static void load_dh_params(SSL_CTX *ctx, const char *filename,  		sslerror(info, filename, -1);  } +static int check_readable_file(const char *filename) +{ +	return (access(filename, R_OK) == 0) ? 1 : 0; +} + +#ifdef HAVE_OPENSSL_SNI +static char *get_servername_concated_readable_file(const char *filename, +				   const char *servername, +				   struct tls_info *info) +{ +	char *filename_buffer; +	char *p; + +	if (!filename || !servername) return NULL; + +	filename_buffer=malloc(strlen(filename)+strlen(servername)+2); +	if (!filename_buffer) +	{ +		nonsslerror(info, "malloc"); +		exit(1); +	} + +	strcat(strcpy(filename_buffer, filename), "."); + +	p=filename_buffer + strlen(filename_buffer); + +	while ((*p=*servername) != 0) +	{ +		if (*p == '/') +			*p='.'; /* Script kiddie check */ +		++p; +		++servername; +	} +	if (check_readable_file(filename_buffer)) { +		return filename_buffer; +	} + +	free(filename_buffer); +	return NULL; +} +#endif + +static char *get_ip_concated_readable_file(SSL_CTX *ctx, const char *filename, const char *ip) +{ +	if (!filename || !ip) return NULL; + +	char *test_file; +	const struct tls_info *info=SSL_CTX_get_app_data(ctx); + +	test_file= malloc(strlen(filename)+strlen(ip)+2); +	if (!test_file) +	{ +		nonsslerror(info, "malloc"); +		exit(1); +	} + +	strcpy(test_file, filename); +	strcat(test_file, "."); +	strcat(test_file, ip); + +	if (check_readable_file(test_file)) { +		return test_file; +	} + +	free(test_file); +	return NULL; +} +  static int read_certfile(SSL_CTX *ctx, const char *filename, +			      const char *private_key_filename,  			      int *cert_file_flags)  {  	const struct tls_info *info=SSL_CTX_get_app_data(ctx); +	const char *privatekey_file = (private_key_filename == NULL) +				    ? filename : private_key_filename;  	if(!SSL_CTX_use_certificate_chain_file(ctx, filename))  	{ @@ -345,7 +416,7 @@ static int read_certfile(SSL_CTX *ctx, const char *filename,  	load_dh_params(ctx, filename, cert_file_flags); -	if(!SSL_CTX_use_PrivateKey_file(ctx, filename, SSL_FILETYPE_PEM)) +	if(!SSL_CTX_use_PrivateKey_file(ctx, privatekey_file, SSL_FILETYPE_PEM))  	{  		sslerror(info, filename, -1);  		return (0); @@ -353,36 +424,40 @@ static int read_certfile(SSL_CTX *ctx, const char *filename,  	return (1);  } -static int process_certfile(SSL_CTX *ctx, const char *certfile, const char *ip, +static int process_certfile(SSL_CTX *ctx, const char *certfile, +			    const char *private_key_file, +			    const char *ip,  			    int (*func)(SSL_CTX *, const char *, +					const char *,  					int *),  			    int *cert_file_flags)  {  	if (ip && *ip)  	{  		char *test_file; +		char *test_private_key_file;  		if (strncmp(ip, "::ffff:", 7) == 0 && strchr(ip, '.')) -			return (process_certfile(ctx, certfile, ip+7, func, cert_file_flags)); - -		test_file= malloc(strlen(certfile)+strlen(ip)+2); - -		strcpy(test_file, certfile); -		strcat(test_file, "."); -		strcat(test_file, ip); +			return (process_certfile(ctx, certfile, private_key_file, ip+7, func, cert_file_flags)); -		if (access(test_file, R_OK) == 0) +		test_file= get_ip_concated_readable_file(ctx, certfile, ip); +		test_private_key_file = get_ip_concated_readable_file(ctx, private_key_file, ip); +		if (test_file != NULL)  		{ -			int rc= (*func)(ctx, test_file, +			int rc= (*func)(ctx, test_file, test_private_key_file,  					cert_file_flags);  			free(test_file); +			if (test_private_key_file) free(test_private_key_file); +  			return rc;  		}  		free(test_file); +		if (test_private_key_file) free(test_private_key_file);  	} -	return (*func)(ctx, certfile, cert_file_flags); +	private_key_file = check_readable_file(private_key_file) ? private_key_file : NULL; +	return (*func)(ctx, certfile, private_key_file, cert_file_flags);  }  static int client_cert_cb(ssl_handle ssl, X509 **x509, EVP_PKEY **pkey) @@ -502,33 +577,17 @@ static int server_cert_cb(ssl_handle ssl, int *ad, void *arg)  	const char *servername=SSL_get_servername(ssl,  						  TLSEXT_NAMETYPE_host_name);  	const char *certfile=safe_getenv(info, "TLS_CERTFILE"); +	const char *private_keyfile=safe_getenv(info, "TLS_PRIVATE_KEYFILE");  	int cert_file_flags=0; -	char *buffer; -	char *p; +	char *cert_file_buffer; +	char *private_keyfile_buffer;  	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) +	cert_file_buffer = get_servername_concated_readable_file(certfile, servername, info); +	private_keyfile_buffer = get_servername_concated_readable_file(private_keyfile, servername, info); +	if (cert_file_buffer != NULL)  	{  		SSL_CTX *orig_ctx=SSL_get_SSL_CTX(ssl);  		SSL_CTX *temp_ctx=tls_create_int(1, info, 1); @@ -541,7 +600,7 @@ static int server_cert_cb(ssl_handle ssl, int *ad, void *arg)  			exit(1);  		}  		SSL_set_SSL_CTX(ssl, temp_ctx); -		rc=read_certfile(orig_ctx, buffer, &cert_file_flags); +		rc=read_certfile(orig_ctx, cert_file_buffer, private_keyfile_buffer, &cert_file_flags);  		SSL_set_SSL_CTX(ssl, orig_ctx);  		tls_destroy(temp_ctx);  		if (!rc) @@ -551,7 +610,8 @@ static int server_cert_cb(ssl_handle ssl, int *ad, void *arg)  			exit(1);  		}  	} -	free(buffer); +	if (cert_file_buffer) free(cert_file_buffer); +	if (private_keyfile_buffer) free(private_keyfile_buffer);  #endif  	return SSL_TLSEXT_ERR_OK; @@ -571,6 +631,7 @@ SSL_CTX *tls_create_int(int isserver, const struct tls_info *info,  	int session_timeout=atoi(safe_getenv(info, "TLS_TIMEOUT"));  	const char *dhparamsfile=safe_getenv(info, "TLS_DHPARAMS");  	const char *certfile=safe_getenv(info, "TLS_CERTFILE"); +	const char *private_keyfile=safe_getenv(info, "TLS_PRIVATE_KEYFILE");  	const char *s;  	struct stat stat_buf;  	const char *peer_cert_dir=NULL; @@ -588,6 +649,9 @@ SSL_CTX *tls_create_int(int isserver, const struct tls_info *info,  	if (!*certfile)  		certfile=NULL; +	if (!*private_keyfile) +		private_keyfile=NULL; +  	if (!*dhparamsfile)  		dhparamsfile=NULL; @@ -697,7 +761,7 @@ SSL_CTX *tls_create_int(int isserver, const struct tls_info *info,  	if (dhparamsfile)  		load_dh_params(ctx, dhparamsfile, &cert_file_flags); -	if (certfile && !process_certfile(ctx, certfile, s, +	if (certfile && !process_certfile(ctx, certfile, private_keyfile, s,  					  read_certfile,  					  &cert_file_flags))  	{ diff --git a/tcpd/libcouriertls.h b/tcpd/libcouriertls.h index 17faabc..a45f910 100644 --- a/tcpd/libcouriertls.h +++ b/tcpd/libcouriertls.h @@ -325,6 +325,12 @@ TLS_CERTFILE is required for SSL/TLS servers, and is optional for SSL/TLS  clients.  TLS_CERTFILE is usually treated as confidential, and must not be  world-readable. +TLS_PRIVATE_KEYFILE - SSL/TLS private key for decrypting peer data. +By default, courier generates SSL/TLS certifice including private key +and install it in TLS_CERTFILE path, so TLS_PRIVATE_KEYFILE is completely +optional. If TLS_PRIVATE_KEYFILE is not set (default), TLS_CERTFILE is +treated as certificate including private key file. +  TLS_TRUSTCERTS=pathname - load trusted root certificates from pathname.  pathname can be a file or a directory. If a file, the file should  contain a list of trusted certificates, in PEM format. If a | 
