kore

a fork of the worlds most advanced web framework
Log | Files | Refs | README | LICENSE

commit 73cdbd1a0147a9377bef7c8363f9749f413bf2dc
parent c070e77ea55cd531452d51e5945f9551c4fa3b67
Author: Joris Vink <joris@coders.se>
Date:   Mon, 14 Jan 2019 11:41:50 +0100

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.

Diffstat:
Minclude/kore/kore.h | 4++++
Msrc/domain.c | 98+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/keymgr.c | 49+++++++++++++++++++++++++++++++++++++++----------
Msrc/worker.c | 56++++++++++++++++++++++++++++++++++++++++++--------------
4 files changed, 139 insertions(+), 68 deletions(-)

diff --git a/include/kore/kore.h b/include/kore/kore.h @@ -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); diff --git a/src/domain.c b/src/domain.c @@ -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 @@ -500,17 +553,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) { #if !defined(KORE_NO_TLS) @@ -521,38 +563,6 @@ kore_domain_keymgr_init(void) #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) { const RSA_METHOD *meth; diff --git a/src/keymgr.c b/src/keymgr.c @@ -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); @@ -195,6 +197,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; ssize_t ret; @@ -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 ((fd = open(file, O_RDONLY)) == -1) { + if (errno == ENOENT && can_fail) + return; + fatal("open(%s): %s", file, errno_s); + } + if (fstat(fd, &st) == -1) - fatal("stat(%s): %s", dom->certfile, errno_s); + fatal("stat(%s): %s", file, errno_s); + if (!S_ISREG(st.st_mode)) - fatal("%s is not a file", dom->certfile); + fatal("%s is not a file", file); - if (st.st_size <= 0 || st.st_size > (1024 * 1024 * 5)) { - fatal("%s length is not valid (%jd)", dom->certfile, + 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); } diff --git a/src/worker.c b/src/worker.c @@ -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