summaryrefslogtreecommitdiffstats
path: root/tcpd/libcouriergnutls.c
diff options
context:
space:
mode:
Diffstat (limited to 'tcpd/libcouriergnutls.c')
-rw-r--r--tcpd/libcouriergnutls.c238
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);