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:
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