summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Varshavchik2018-09-24 19:46:00 -0400
committerGitHub2018-09-24 19:46:00 -0400
commitec5264047dfaf66c61cd0f4c9b8dd40463693722 (patch)
treebe38cfb21c064f485caa62a6dd7fdf1fd29ab4a4
parent1d5b075408e8829006d84ba65b922101bd304a25 (diff)
parent141976c61fd8e7cabe1bf1d0a6ea10dae7801a39 (diff)
downloadcourier-libs-ec5264047dfaf66c61cd0f4c9b8dd40463693722.tar.bz2
Merge pull request #13 from mumumu/new_feature_TLS_PRIVATE_KEYFILE
separate TLS private key from [imapd|pop3d].pem
-rw-r--r--imap/imapd-ssl.dist.in.git38
-rw-r--r--imap/pop3d-ssl.dist.in.git38
-rw-r--r--tcpd/couriertls.sgml10
-rw-r--r--tcpd/libcouriergnutls.c80
-rw-r--r--tcpd/libcouriertls.c138
-rw-r--r--tcpd/libcouriertls.h6
6 files changed, 266 insertions, 44 deletions
diff --git a/imap/imapd-ssl.dist.in.git b/imap/imapd-ssl.dist.in.git
index 50f1879..5204818 100644
--- a/imap/imapd-ssl.dist.in.git
+++ b/imap/imapd-ssl.dist.in.git
@@ -229,6 +229,44 @@ TLS_STARTTLS_PROTOCOL="$TLS_PROTOCOL"
TLS_CERTFILE=@certsdir@/imapd.pem
+##NAME: TLS_PRIVATE_KEYFILE:0
+#
+# TLS_PRIVATE_KEYFILE - SSL/TLS private key for decrypting peer data.
+# This file must be owned by the "@mailuser@" user, and must not be world
+# readable.
+#
+# 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.
+#
+# If you get SSL/TLS certificate and private key from trusted certificate
+# authority(CA) and want to install them separately, TLS_PRIVATE_KEYFILE can
+# be used as private key file path setting.
+#
+# VIRTUAL HOSTS ON THE SAME IP ADDRESS.
+#
+# $TLS_PRIVATE_KEYFILE.domain and $TLS_CERTFILE.domain are a pair.
+# If you use VIRTUAL HOST feature on TLS_CERTFILE setting, you must set pair
+# private key as $TLS_PRIVATE_KEYFILE.domain. Then, create a link from
+# $TLS_PRIVATE_KEYFILE to whichever private key you consider to be the main one.
+# for example:
+# /etc/tls_private_keyfile.pem => /etc/tls_private_keyfile.pem.www.example.com
+#
+# IP-BASED VIRTUAL HOSTS:
+#
+# Just described on "VIRTUAL HOSTS ON THE SAME IP ADDRESS" above,
+# $TLS_PRIVATE_KEYFILE.aaa.bbb.ccc.ddd and $TLS_CERTFILE.aaa.bbb.ccc.ddd are
+# a pair. If TLS_PRIVATE_KEYFILE is set to /etc/tls_private_keyfile.pem,
+# then you'll need to install the actual certificate files as
+# /etc/tls_private_keyfile.pem.192.168.0.2, /etc/tls_private_keyfile.192.168.0.3
+# and so on, for each IP address.
+#
+# In all cases, $TLS_PRIVATE_KEYFILE needs to be linked to one of the existing
+# certificate files.
+#
+#TLS_PRIVATE_KEYFILE=@certsdir@/imapd_private_key.pem
+
##NAME: TLS_DHPARAMS:0
#
# TLS_DHPARAMS - DH parameter file.
diff --git a/imap/pop3d-ssl.dist.in.git b/imap/pop3d-ssl.dist.in.git
index ec16ce8..9611524 100644
--- a/imap/pop3d-ssl.dist.in.git
+++ b/imap/pop3d-ssl.dist.in.git
@@ -223,6 +223,44 @@ TLS_STARTTLS_PROTOCOL="$TLS_PROTOCOL"
TLS_CERTFILE=@certsdir@/pop3d.pem
+##NAME: TLS_PRIVATE_KEYFILE:0
+#
+# TLS_PRIVATE_KEYFILE - SSL/TLS private key for decrypting peer data.
+# This file must be owned by the "@mailuser@" user, and must not be world
+# readable.
+#
+# 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.
+#
+# If you get SSL/TLS certificate and private key from trusted certificate
+# authority(CA) and want to install them separately, TLS_PRIVATE_KEYFILE can
+# be used as private key file path setting.
+#
+# VIRTUAL HOSTS ON THE SAME IP ADDRESS.
+#
+# $TLS_PRIVATE_KEYFILE.domain and $TLS_CERTFILE.domain are a pair.
+# If you use VIRTUAL HOST feature on TLS_CERTFILE setting, you must set pair
+# private key as $TLS_PRIVATE_KEYFILE.domain. Then, create a link from
+# $TLS_PRIVATE_KEYFILE to whichever private key you consider to be the main one.
+# for example:
+# /etc/tls_private_keyfile.pem => /etc/tls_private_keyfile.pem.www.example.com
+#
+# IP-BASED VIRTUAL HOSTS:
+#
+# Just described on "VIRTUAL HOSTS ON THE SAME IP ADDRESS" above,
+# $TLS_PRIVATE_KEYFILE.aaa.bbb.ccc.ddd and $TLS_CERTFILE.aaa.bbb.ccc.ddd are
+# a pair. If TLS_PRIVATE_KEYFILE is set to /etc/tls_private_keyfile.pem,
+# then you'll need to install the actual certificate files as
+# /etc/tls_private_keyfile.pem.192.168.0.2, /etc/tls_private_keyfile.192.168.0.3
+# and so on, for each IP address.
+#
+# In all cases, $TLS_PRIVATE_KEYFILE needs to be linked to one of the existing
+# certificate files.
+#
+#TLS_PRIVATE_KEYFILE=@certsdir@/pop3d_private_key.pem
+
##NAME: TLS_DHPARAMS:0
#
# TLS_DHPARAMS - DH parameter file.
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