diff options
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 |
