Skip to content
Snippets Groups Projects
Commit 9aa28309 authored by Daniel Kahn Gillmor's avatar Daniel Kahn Gillmor
Browse files

Add ephemeral keying for TLS

If the administrator has not supplied any TLS key+cert configuration,
but has configured kresd to listen on TLS, then generate an ephemeral
key and cert for use while the daemon is running.
parent 9801e5ac
No related tags found
No related merge requests found
......@@ -445,6 +445,14 @@ struct tls_creds_t *network_get_current_tls_creds(struct network *net)
if (net->tls_creds_changed && new_creds == NULL && net->current_tls_creds) {
kr_log_error("[tls] failed to create new TLS credentials; continuing to use old ones\n");
}
if (net->current_tls_creds == NULL &&
net->tls_key == NULL &&
net->tls_cert == NULL &&
new_creds == NULL) {
kr_log_info("[tls] generating ephemeral TLS credentials since none have been "
"explictly specified with net.tls_key() or net.tls_cert()\n");
net->current_tls_creds = tls_creds_ephemeral(net->tls_servicename);
}
if (new_creds) {
kr_log_info("[tls] %s TLS credentials\n", net->current_tls_creds ? "reloaded" : "initialized");
tls_creds_log_pins(new_creds);
......
......@@ -23,6 +23,7 @@
#include <gnutls/crypto.h>
#include <nettle/base64.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <uv.h>
......@@ -345,14 +346,10 @@ void tls_creds_log_pins(struct tls_creds_t *creds)
}
}
/* TLS credentials are reference-counted since they may be used by
* multiple concurrent sessions and they could be updated
* dynamically. */
struct tls_creds_t* tls_creds_new(const char *key, const char *cert)
static struct tls_creds_t* tls_base_creds()
{
int err;
struct tls_creds_t *creds;
creds = calloc(1, sizeof(struct tls_creds_t));
if (creds == NULL) {
kr_log_error("[tls] failed to allocate TLS credentials\n");
......@@ -369,6 +366,21 @@ struct tls_creds_t* tls_creds_new(const char *key, const char *cert)
kr_log_error("[tls] warning: gnutls_certificate_set_x509_system_trust() failed: (%d) %s\n",
err, gnutls_strerror_name(err));
}
return creds;
}
/* TLS credentials are reference-counted since they may be used by
* multiple concurrent sessions and they could be updated
* dynamically. */
struct tls_creds_t* tls_creds_new(const char *key, const char *cert)
{
int err;
struct tls_creds_t *creds;
if ((creds = tls_base_creds()) == NULL) {
return NULL;
}
if ((err = gnutls_certificate_set_x509_key_file(creds->x509, cert,
key, GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS) {
kr_log_error("[tls] gnutls_certificate_set_x509_key_file(%s,%s) failed: %d (%s)\n",
......@@ -381,6 +393,68 @@ struct tls_creds_t* tls_creds_new(const char *key, const char *cert)
return creds;
}
struct tls_creds_t* tls_creds_ephemeral(const char *servicename)
{
int err;
struct tls_creds_t *creds;
gnutls_x509_privkey_t privkey;
gnutls_x509_crt_t cert;
if ((creds = tls_base_creds()) == NULL) {
return NULL;
}
time_t now = time(NULL);
/* need a random buffer of bytes */
uint8_t serial[16];
gnutls_rnd(GNUTLS_RND_NONCE, serial, sizeof(serial));
/* clear the left-most bit to avoid signedness confusion: */
serial[0] &= 0x8f;
char hostname[KNOT_DNAME_MAXLEN];
if (servicename == NULL) {
gethostname(hostname, sizeof(hostname));
servicename = hostname;
}
size_t namelen = strlen(servicename);
#define gtx(fn, ...) \
if ((err = fn ( __VA_ARGS__ )) != GNUTLS_E_SUCCESS) { \
kr_log_error("[tls] " #fn "() failed: %d (%s)\n", \
err, gnutls_strerror_name(err)); \
goto bad; }
gtx(gnutls_x509_privkey_init, &privkey);
gtx(gnutls_x509_privkey_generate, privkey, GNUTLS_PK_ECDSA, GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_SECP256R1), 0);
gtx(gnutls_x509_crt_init, &cert);
gtx(gnutls_x509_crt_set_activation_time, cert, now);
gtx(gnutls_x509_crt_set_ca_status, cert, 0);
gtx(gnutls_x509_crt_set_expiration_time, cert, now + 60*60*24*365*3);
gtx(gnutls_x509_crt_set_key, cert, privkey);
gtx(gnutls_x509_crt_set_key_purpose_oid, cert, GNUTLS_KP_TLS_WWW_CLIENT, 0);
gtx(gnutls_x509_crt_set_key_purpose_oid, cert, GNUTLS_KP_TLS_WWW_SERVER, 0);
gtx(gnutls_x509_crt_set_key_usage, cert, GNUTLS_KEY_DIGITAL_SIGNATURE);
gtx(gnutls_x509_crt_set_serial, cert, serial, sizeof(serial));
gtx(gnutls_x509_crt_set_subject_alt_name, cert, GNUTLS_SAN_DNSNAME, servicename, namelen, GNUTLS_FSAN_SET);
gtx(gnutls_x509_crt_set_dn_by_oid,cert, GNUTLS_OID_X520_COMMON_NAME, 0, servicename, namelen);
gtx(gnutls_x509_crt_set_version, cert, 3);
gtx(gnutls_x509_crt_sign2,cert, cert, privkey, GNUTLS_DIG_SHA256, 0); /* self-sign, since it doesn't look like we can just stub-sign */
gtx(gnutls_certificate_set_x509_key, creds->x509, &cert, 1, privkey);
#undef gtx
tls_creds_ref(creds);
gnutls_x509_privkey_deinit(privkey);
gnutls_x509_crt_deinit(cert);
return creds;
bad:
gnutls_x509_privkey_deinit(privkey);
gnutls_x509_crt_deinit(cert);
free(creds);
return NULL;
}
void tls_creds_ref(struct tls_creds_t *creds)
{
if (creds)
......
......@@ -37,6 +37,8 @@ struct tls_creds_t* tls_creds_new(const char *keyfile, const char *certfile);
void tls_creds_ref(struct tls_creds_t *creds);
void tls_creds_unref(struct tls_creds_t *creds);
struct tls_creds_t* tls_creds_ephemeral(const char *servicename);
/* Log DNS-over-TLS OOB key-pin form of current credentials:
* https://tools.ietf.org/html/rfc7858#appendix-A */
void tls_creds_log_pins(struct tls_creds_t *creds);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment