Let CRLs be reloadable via keymgr.

With these changes CRLs can be reloaded like certificates
by sending a SIGUSR1 to the parent process.

Track mtime on both certificate files and CRL files as well
and only submit them to the workers if this has changed.
This commit is contained in:
Joris Vink 2019-01-14 11:41:50 +01:00
parent c070e77ea5
commit 73cdbd1a01
4 changed files with 141 additions and 70 deletions

View File

@ -411,7 +411,9 @@ struct kore_domain {
#if !defined(KORE_NO_TLS)
char *cafile;
char *crlfile;
time_t crl_mtime;
char *certfile;
time_t cert_mtime;
char *certkey;
SSL_CTX *ssl_ctx;
int x509_verify_depth;
@ -495,6 +497,7 @@ struct kore_timer {
#define KORE_MSG_ENTROPY_RESP 6
#define KORE_MSG_CERTIFICATE 7
#define KORE_MSG_CERTIFICATE_REQ 8
#define KORE_MSG_CRL 9
/* Predefined message targets. */
#define KORE_MSG_PARENT 1000
@ -747,6 +750,7 @@ void kore_domain_load_crl(void);
void kore_domain_keymgr_init(void);
void kore_domain_callback(void (*cb)(struct kore_domain *));
void kore_domain_tlsinit(struct kore_domain *, const void *, size_t);
void kore_domain_crl_add(struct kore_domain *, const void *, size_t);
#if !defined(KORE_NO_HTTP)
int kore_module_handler_new(const char *, const char *,
const char *, const char *, int);

View File

@ -56,7 +56,6 @@ int tls_version = KORE_TLS_VERSION_BOTH;
#if !defined(KORE_NO_TLS)
static BIO *domain_bio_mem(const void *, size_t);
static int domain_x509_verify(int, X509_STORE_CTX *);
static void domain_load_crl(struct kore_domain *);
static X509 *domain_load_certificate_chain(SSL_CTX *, const void *, size_t);
static void keymgr_init(void);
@ -211,6 +210,9 @@ kore_domain_new(char *domain)
dom->ssl_ctx = NULL;
dom->certfile = NULL;
dom->crlfile = NULL;
dom->crl_mtime = 0;
dom->cert_mtime = 0;
dom->x509_verify_depth = 1;
#endif
dom->domain = kore_strdup(domain);
@ -436,6 +438,57 @@ kore_domain_tlsinit(struct kore_domain *dom, const void *pem, size_t pemlen)
X509_free(x509);
}
void
kore_domain_crl_add(struct kore_domain *dom, const void *pem, size_t pemlen)
{
int err;
BIO *in;
X509_CRL *crl;
X509_STORE *store;
ERR_clear_error();
in = domain_bio_mem(pem, pemlen);
if ((store = SSL_CTX_get_cert_store(dom->ssl_ctx)) == NULL) {
BIO_free(in);
kore_log(LOG_ERR, "SSL_CTX_get_cert_store(): %s", ssl_errno_s);
return;
}
for (;;) {
crl = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL);
if (crl == NULL) {
err = ERR_GET_REASON(ERR_peek_last_error());
if (err == PEM_R_NO_START_LINE) {
ERR_clear_error();
break;
}
kore_log(LOG_WARNING, "failed to read CRL %s: %s",
dom->crlfile, ssl_errno_s);
continue;
}
if (!X509_STORE_add_crl(store, crl)) {
err = ERR_GET_REASON(ERR_peek_last_error());
if (err == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
X509_CRL_free(crl);
continue;
}
kore_log(LOG_WARNING, "failed to add CRL %s: %s",
dom->crlfile, ssl_errno_s);
X509_CRL_free(crl);
continue;
}
}
BIO_free(in);
X509_STORE_set_flags(store,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
}
#endif
void
@ -499,17 +552,6 @@ kore_domain_closelogs(void)
}
}
void
kore_domain_load_crl(void)
{
#if !defined(KORE_NO_TLS)
struct kore_domain *dom;
TAILQ_FOREACH(dom, &domains, list)
domain_load_crl(dom);
#endif
}
void
kore_domain_keymgr_init(void)
{
@ -519,38 +561,6 @@ kore_domain_keymgr_init(void)
#endif
}
#if !defined(KORE_NO_TLS)
static void
domain_load_crl(struct kore_domain *dom)
{
X509_STORE *store;
if (dom->cafile == NULL)
return;
if (dom->crlfile == NULL) {
kore_log(LOG_WARNING, "WARNING: no CRL configured for '%s'",
dom->domain);
return;
}
ERR_clear_error();
if ((store = SSL_CTX_get_cert_store(dom->ssl_ctx)) == NULL) {
kore_log(LOG_ERR, "SSL_CTX_get_cert_store(): %s", ssl_errno_s);
return;
}
if (!X509_STORE_load_locations(store, dom->crlfile, NULL)) {
kore_log(LOG_ERR, "X509_STORE_load_locations(): %s",
ssl_errno_s);
return;
}
X509_STORE_set_flags(store,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
}
#endif
#if !defined(KORE_NO_TLS)
static void
keymgr_init(void)

View File

@ -72,6 +72,8 @@ static void keymgr_msg_recv(struct kore_msg *, const void *);
static void keymgr_entropy_request(struct kore_msg *, const void *);
static void keymgr_certificate_request(struct kore_msg *, const void *);
static void keymgr_submit_certificates(struct kore_domain *, u_int16_t);
static void keymgr_submit_file(u_int8_t, struct kore_domain *,
const char *, u_int16_t, time_t *, int);
static void keymgr_rsa_encrypt(struct kore_msg *, const void *,
struct key *);
@ -180,7 +182,7 @@ keymgr_reload(void)
{
struct kore_domain *dom;
kore_log(LOG_INFO, "(re)loading certificates and keys");
kore_log(LOG_INFO, "(re)loading certificates, keys and CRLs");
kore_keymgr_cleanup(0);
TAILQ_INIT(&keys);
@ -194,6 +196,19 @@ keymgr_reload(void)
static void
keymgr_submit_certificates(struct kore_domain *dom, u_int16_t dst)
{
keymgr_submit_file(KORE_MSG_CERTIFICATE,
dom, dom->certfile, dst, &dom->cert_mtime, 0);
if (dom->crlfile != NULL) {
keymgr_submit_file(KORE_MSG_CRL,
dom, dom->crlfile, dst, &dom->crl_mtime, 1);
}
}
static void
keymgr_submit_file(u_int8_t id, struct kore_domain *dom,
const char *file, u_int16_t dst, time_t *mtime, int can_fail)
{
int fd;
struct stat st;
@ -202,18 +217,30 @@ keymgr_submit_certificates(struct kore_domain *dom, u_int16_t dst)
struct kore_x509_msg *msg;
u_int8_t *payload;
if ((fd = open(dom->certfile, O_RDONLY)) == -1)
fatal("open(%s): %s", dom->certfile, errno_s);
if (fstat(fd, &st) == -1)
fatal("stat(%s): %s", dom->certfile, errno_s);
if (!S_ISREG(st.st_mode))
fatal("%s is not a file", dom->certfile);
if ((fd = open(file, O_RDONLY)) == -1) {
if (errno == ENOENT && can_fail)
return;
fatal("open(%s): %s", file, errno_s);
}
if (st.st_size <= 0 || st.st_size > (1024 * 1024 * 5)) {
fatal("%s length is not valid (%jd)", dom->certfile,
if (fstat(fd, &st) == -1)
fatal("stat(%s): %s", file, errno_s);
if (!S_ISREG(st.st_mode))
fatal("%s is not a file", file);
if (st.st_size <= 0 || st.st_size > (1024 * 1024 * 10)) {
fatal("%s length is not valid (%jd)", file,
(intmax_t)st.st_size);
}
if (st.st_mtime == *mtime) {
close(fd);
return;
}
*mtime = st.st_mtime;
len = sizeof(*msg) + st.st_size;
payload = kore_calloc(1, len);
@ -225,15 +252,17 @@ keymgr_submit_certificates(struct kore_domain *dom, u_int16_t dst)
msg->data_len = st.st_size;
ret = read(fd, &msg->data[0], msg->data_len);
if (ret == -1)
fatal("failed to read from %s: %s", file, errno_s);
if (ret == 0)
fatal("eof while reading %s", dom->certfile);
fatal("eof while reading %s", file);
if ((size_t)ret != msg->data_len) {
fatal("bad read on %s: expected %zu, got %zd",
dom->certfile, msg->data_len, ret);
file, msg->data_len, ret);
}
kore_msg_send(dst, KORE_MSG_CERTIFICATE, payload, len);
kore_msg_send(dst, id, payload, len);
kore_free(payload);
close(fd);
}

View File

@ -77,7 +77,9 @@ static inline int worker_acceptlock_release(u_int64_t);
#if !defined(KORE_NO_TLS)
static void worker_entropy_recv(struct kore_msg *, const void *);
static void worker_certificate_recv(struct kore_msg *, const void *);
static void worker_keymgr_response(struct kore_msg *, const void *);
static int worker_keymgr_response_verify(struct kore_msg *, const void *,
struct kore_domain **);
#endif
static u_int64_t next_lock;
@ -348,7 +350,6 @@ kore_worker_entry(struct kore_worker *kw)
#endif
kore_timer_init();
kore_fileref_init();
kore_domain_load_crl();
kore_domain_keymgr_init();
quit = 0;
@ -367,8 +368,9 @@ kore_worker_entry(struct kore_worker *kw)
#if !defined(KORE_NO_TLS)
last_seed = 0;
kore_msg_register(KORE_MSG_CRL, worker_keymgr_response);
kore_msg_register(KORE_MSG_ENTROPY_RESP, worker_entropy_recv);
kore_msg_register(KORE_MSG_CERTIFICATE, worker_certificate_recv);
kore_msg_register(KORE_MSG_CERTIFICATE, worker_keymgr_response);
if (worker->restarted) {
kore_msg_send(KORE_WORKER_KEYMGR,
KORE_MSG_CERTIFICATE_REQ, NULL, 0);
@ -711,29 +713,54 @@ worker_entropy_recv(struct kore_msg *msg, const void *data)
}
static void
worker_certificate_recv(struct kore_msg *msg, const void *data)
worker_keymgr_response(struct kore_msg *msg, const void *data)
{
struct kore_domain *dom;
const struct kore_x509_msg *req;
if (!worker_keymgr_response_verify(msg, data, &dom))
return;
req = (const struct kore_x509_msg *)data;
switch (msg->id) {
case KORE_MSG_CERTIFICATE:
kore_domain_tlsinit(dom, req->data, req->data_len);
break;
case KORE_MSG_CRL:
kore_domain_crl_add(dom, req->data, req->data_len);
break;
default:
kore_log(LOG_WARNING, "unknown keymgr request %u", msg->id);
break;
}
}
static int
worker_keymgr_response_verify(struct kore_msg *msg, const void *data,
struct kore_domain **out)
{
struct kore_domain *dom;
const struct kore_x509_msg *req;
if (msg->length < sizeof(*req)) {
kore_log(LOG_WARNING,
"short KORE_MSG_CERTIFICATE message (%zu)", msg->length);
return;
"short keymgr message (%zu)", msg->length);
return (KORE_RESULT_ERROR);
}
req = (const struct kore_x509_msg *)data;
if (msg->length != (sizeof(*req) + req->data_len)) {
kore_log(LOG_WARNING,
"invalid KORE_MSG_CERTIFICATE payload (%zu)", msg->length);
return;
"invalid keymgr payload (%zu)", msg->length);
return (KORE_RESULT_ERROR);
}
if (req->domain_len > KORE_DOMAINNAME_LEN) {
kore_log(LOG_WARNING,
"invalid KORE_MSG_CERTIFICATE domain (%u)",
"invalid keymgr domain (%u)",
req->domain_len);
return;
return (KORE_RESULT_ERROR);
}
dom = NULL;
@ -744,11 +771,12 @@ worker_certificate_recv(struct kore_msg *msg, const void *data)
if (dom == NULL) {
kore_log(LOG_WARNING,
"got KORE_MSG_CERTIFICATE for domain that does not exist");
return;
"got keymgr response for domain that does not exist");
return (KORE_RESULT_ERROR);
}
/* reinitialize the domain TLS context. */
kore_domain_tlsinit(dom, req->data, req->data_len);
*out = dom;
return (KORE_RESULT_OK);
}
#endif