kore

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

commit cffb7ec3798684c3a59326de9c1e1f286db34334
parent bf6c0e150fb864c83e8796be0a3577071b1f51ae
Author: Joris Vink <joris@coders.se>
Date:   Wed, 11 Jul 2018 09:44:29 +0200

Allow on-the-fly reloading of certificates/keys.

This commit introduces the ability for the keymgr process
to reload the certificates/keys for domains when receiving
a SIGUSR1 signal.

The keymgr receives 2 new configuration options:
	- keymgr_root_path
		The root path where the keymgr will live.
		If -n is not specified when the application starts the
		keymgr process will chroot into here.

	- keymgr_runas_user
		The user the keymgr will drop privileges towards if
		-r was not specified.

All certfile and certkey configuration options are now relative to the
keymgr_root_path configuration setting.

The keymgr process will now also load the certificate for the domain
(rather then the workers) and submit these to the worker processes so
they can be reloaded when required.

Worker processes will refuse connections until the TLS configuration
for a given domain is completed (aka: the workers receive the certificate
for that domain).

Other changes:
	- client_certificates renamed to client_verify.
	- the chroot configuration option is now called root.
	- kore is a little more verbose if privsep options are missing.
	- filemaps are now relative to the root configuration option.

Diffstat:
Mconf/kore.conf.example | 45++++++++++++++++++++++++++++++++++++++-------
Mexamples/messaging/src/messaging.c | 4++--
Minclude/kore/kore.h | 42+++++++++++++++++++++++++++---------------
Msrc/config.c | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/connection.c | 5+++++
Msrc/domain.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/filemap.c | 8++++++--
Msrc/keymgr.c | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/kore.c | 14++++++++++----
Msrc/msg.c | 2+-
Msrc/utils.c | 2+-
Msrc/worker.c | 64+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
12 files changed, 382 insertions(+), 101 deletions(-)

diff --git a/conf/kore.conf.example b/conf/kore.conf.example @@ -12,10 +12,15 @@ # Server configuration. bind 127.0.0.1 443 -# The path worker processes will chroot into after starting. -chroot /home/joris/src/kore +# The worker process root directory. If chrooting was not disabled +# at startup the worker processes will chroot into this directory. +# +# If this configuration option is not set, Kore will take the current +# working directory as the root. +root /home/joris/src/kore -# Worker processes will run as the specified user. +# Worker processes will run as the specified user. If this option is +# missing Kore will run as the current user. runas joris # How many worker processes Kore will spawn. If the directive @@ -68,6 +73,25 @@ workers 4 # NOTE: This file location must be inside your chrooted environment. #rand_file random.data +# Key manager specific options. +# If TLS is enabled you will need to specify paths to the domain +# certificate and key that Kore will load. This loading is done +# from the keymgr (separate process) and all paths must be relative +# to the keymgr_root_path configuration option. +# +# keymgr_root_path The root path the keymgr will chdir into. +# If chroot was not disable at startup time +# the keymgr process will chroot into here. +# +# keymgr_runas_user The user to run the keymgr as. +# +# If privsep and chrooting is enabled at startup time but these +# configuration options were not set, they will take over the +# values from the 'root' and 'runas' configuration options. +# +#keymgr_root_path +#keymgr_runas_user + # Filemap settings # filemap_index Name of the file to be used as the directory # index for a filemap. @@ -203,9 +227,13 @@ authentication auth_example { # # accesslog # - File where all requests are logged. -# client_certificates [CA] [optional CRL] -# - Require client certificates to be sent for the given -# CA with an optional CRL file. +# +# NOTE: due to current limitations the client_verify CA path +# MUST be in the 'root' of the Kore workers, not the keymgr. +# +# client_verify [CA] [optional CRL] +# - Turns on client verification, requiring the client to +# send a certificate that will be verified by the given CA. # client_verify_depth [depth] # - Configure the depth for x509 chain validation. # By default 1. @@ -244,6 +272,9 @@ domain localhost { # Allow access to files from the directory static_files via # the /files/ URI. + # + # Note the directory given must be relative to the root configuration + # option. filemap /files/ static_files # Configure /params-test POST to only accept the following parameters. @@ -279,7 +310,7 @@ domain localhost { # certfile cert/other/server.crt # certkey cert/other/server.key # accesslog /var/log/other_kore_access.log -# client_certificates cert/other/ca.crt +# client_verify /other/ca.crt # client_verify_depth 1 # static /css/style.css serve_style_css diff --git a/examples/messaging/src/messaging.c b/examples/messaging/src/messaging.c @@ -54,8 +54,8 @@ init(int state) void received_message(struct kore_msg *msg, const void *data) { - kore_log(LOG_INFO, "got message from %u (%d bytes): %.*s", msg->src, - msg->length, msg->length, (const char *)data); + kore_log(LOG_INFO, "got message from %u (%zu bytes): %.*s", msg->src, + msg->length, (int)msg->length, (const char *)data); } /* diff --git a/include/kore/kore.h b/include/kore/kore.h @@ -363,6 +363,7 @@ struct kore_worker { int pipe[2]; struct connection *msg[2]; u_int8_t has_lock; + int restarted; u_int64_t time_locked; struct kore_module_handle *active_hdlr; }; @@ -446,13 +447,15 @@ struct kore_timer { #define KORE_WORKER_KEYMGR 0 /* Reserved message ids, registered on workers. */ -#define KORE_MSG_ACCESSLOG 1 -#define KORE_MSG_WEBSOCKET 2 -#define KORE_MSG_KEYMGR_REQ 3 -#define KORE_MSG_KEYMGR_RESP 4 -#define KORE_MSG_SHUTDOWN 5 -#define KORE_MSG_ENTROPY_REQ 6 -#define KORE_MSG_ENTROPY_RESP 7 +#define KORE_MSG_ACCESSLOG 1 +#define KORE_MSG_WEBSOCKET 2 +#define KORE_MSG_KEYMGR_REQ 3 +#define KORE_MSG_KEYMGR_RESP 4 +#define KORE_MSG_SHUTDOWN 5 +#define KORE_MSG_ENTROPY_REQ 6 +#define KORE_MSG_ENTROPY_RESP 7 +#define KORE_MSG_CERTIFICATE 8 +#define KORE_MSG_CERTIFICATE_REQ 9 /* Predefined message targets. */ #define KORE_MSG_PARENT 1000 @@ -462,17 +465,24 @@ struct kore_msg { u_int8_t id; u_int16_t src; u_int16_t dst; - u_int32_t length; + size_t length; }; #if !defined(KORE_NO_TLS) struct kore_keyreq { int padding; char domain[KORE_DOMAINNAME_LEN]; - u_int8_t domain_len; + u_int16_t domain_len; u_int16_t data_len; u_int8_t data[]; }; + +struct kore_x509_msg { + char domain[KORE_DOMAINNAME_LEN]; + u_int16_t domain_len; + size_t data_len; + u_int8_t data[]; +}; #endif #if !defined(KORE_SINGLE_BINARY) @@ -483,16 +493,18 @@ extern pid_t kore_pid; extern int foreground; extern int kore_debug; extern int skip_chroot; -extern char *chroot_path; extern int skip_runas; -extern char *runas_user; extern char *kore_pidfile; +extern char *kore_root_path; +extern char *kore_runas_user; extern char *kore_tls_cipher_list; -extern int tls_version; #if !defined(KORE_NO_TLS) +extern int tls_version; extern DH *tls_dhparam; extern char *rand_file; +extern char *keymgr_runas_user; +extern char *keymgr_root_path; #endif extern u_int8_t nlisteners; @@ -641,7 +653,7 @@ void kore_msg_worker_init(void); void kore_msg_parent_init(void); void kore_msg_parent_add(struct kore_worker *); void kore_msg_parent_remove(struct kore_worker *); -void kore_msg_send(u_int16_t, u_int8_t, const void *, u_int32_t); +void kore_msg_send(u_int16_t, u_int8_t, const void *, size_t); int kore_msg_register(u_int8_t, void (*cb)(struct kore_msg *, const void *)); @@ -672,9 +684,9 @@ void kore_domain_closelogs(void); void *kore_module_getsym(const char *, struct kore_runtime **); void kore_domain_load_crl(void); void kore_domain_keymgr_init(void); -void kore_domain_tlsinit(struct kore_domain *); void kore_module_load(const char *, const char *, int); void kore_domain_callback(void (*cb)(struct kore_domain *)); +void kore_domain_tlsinit(struct kore_domain *, const void *, size_t); int kore_module_handler_new(const char *, const char *, const char *, const char *, int); void kore_module_handler_free(struct kore_module_handle *); @@ -757,7 +769,7 @@ void kore_buf_appendv(struct kore_buf *, const char *, va_list); void kore_buf_replace_string(struct kore_buf *, char *, void *, size_t); void kore_keymgr_run(void); -void kore_keymgr_cleanup(void); +void kore_keymgr_cleanup(int); void kore_worker_configure(void); void kore_parent_configure(int, char **); diff --git a/src/config.c b/src/config.c @@ -53,7 +53,7 @@ extern u_int32_t asset_len_builtin_kore_conf; static int configure_include(char *); static int configure_bind(char *); static int configure_domain(char *); -static int configure_chroot(char *); +static int configure_root(char *); static int configure_runas(char *); static int configure_workers(char *); static int configure_pidfile(char *); @@ -70,8 +70,10 @@ static int configure_certkey(char *); static int configure_tls_version(char *); static int configure_tls_cipher(char *); static int configure_tls_dhparam(char *); +static int configure_keymgr_root(char *); +static int configure_keymgr_runas(char *); +static int configure_client_verify(char *); static int configure_client_verify_depth(char *); -static int configure_client_certificates(char *); #endif #if !defined(KORE_NO_HTTP) @@ -117,8 +119,6 @@ static int configure_python_path(char *); static int configure_python_import(char *); #endif -static void domain_tls_init(void); - static struct { const char *name; int (*configure)(char *); @@ -130,8 +130,9 @@ static struct { { "python_path", configure_python_path }, { "python_import", configure_python_import }, #endif + { "root", configure_root }, + { "chroot", configure_root }, { "domain", configure_domain }, - { "chroot", configure_chroot }, { "runas", configure_runas }, { "workers", configure_workers }, { "worker_max_connections", configure_max_connections }, @@ -145,9 +146,11 @@ static struct { { "tls_cipher", configure_tls_cipher }, { "tls_dhparam", configure_tls_dhparam }, { "rand_file", configure_rand_file }, + { "keymgr_runas", configure_keymgr_runas }, + { "keymgr_root", configure_keymgr_root }, { "certfile", configure_certfile }, { "certkey", configure_certkey }, - { "client_certificates", configure_client_certificates }, + { "client_verify", configure_client_verify }, { "client_verify_depth", configure_client_verify_depth }, #endif #if !defined(KORE_NO_HTTP) @@ -205,6 +208,7 @@ void kore_parse_config(void) { FILE *fp; + char path[PATH_MAX]; #if !defined(KORE_SINGLE_BINARY) if ((fp = fopen(config_file, "r")) == NULL) @@ -219,21 +223,48 @@ kore_parse_config(void) if (!kore_module_loaded()) fatal("no application module was loaded"); - if (skip_chroot != 1 && chroot_path == NULL) { - fatal("missing a chroot path"); + if (kore_root_path == NULL) { + if (getcwd(path, sizeof(path)) == NULL) + fatal("getcwd: %s", errno_s); + kore_root_path = kore_strdup(path); + kore_log(LOG_NOTICE, + "privsep: no root path set, using working directory"); } if (getuid() != 0 && skip_chroot == 0) { fatal("cannot chroot, use -n to skip it"); } - if (skip_runas != 1 && runas_user == NULL) { + if (skip_runas != 1 && kore_runas_user == NULL) { fatal("missing runas user, use -r to skip it"); } if (getuid() != 0 && skip_runas == 0) { fatal("cannot drop privileges, use -r to skip it"); } + + if (skip_runas) { + kore_log(LOG_WARNING, "privsep: will not change user"); + } else { +#if !defined(KORE_NO_TLS) + if (keymgr_runas_user == NULL) { + kore_log(LOG_NOTICE, + "privsep: no keymgr_runas set, using 'runas` user"); + keymgr_runas_user = kore_strdup(kore_runas_user); + } +#endif + } + +#if !defined(KORE_NO_TLS) + if (keymgr_root_path == NULL) { + kore_log(LOG_NOTICE, + "privsep: no keymgr_root set, using 'root` directory"); + keymgr_root_path = kore_strdup(kore_root_path); + } +#endif + + if (skip_chroot) + kore_log(LOG_WARNING, "privsep: will not chroot"); } void @@ -271,7 +302,7 @@ kore_parse_config_file(FILE *fp) #endif if (!strcmp(p, "}") && current_domain != NULL) - domain_tls_init(); + current_domain = NULL; if (!strcmp(p, "}")) { lineno++; @@ -466,23 +497,23 @@ configure_client_verify_depth(char *value) } static int -configure_client_certificates(char *options) +configure_client_verify(char *options) { char *argv[3]; if (current_domain == NULL) { - printf("client_certificates not specified in domain context\n"); + printf("client_verify not specified in domain context\n"); return (KORE_RESULT_ERROR); } kore_split_string(options, " ", argv, 3); if (argv[0] == NULL) { - printf("client_certificate is missing a parameter\n"); + printf("client_verify is missing a parameter\n"); return (KORE_RESULT_ERROR); } if (current_domain->cafile != NULL) { - printf("client_certificate already set for %s\n", + printf("client_verify already set for %s\n", current_domain->domain); return (KORE_RESULT_ERROR); } @@ -541,6 +572,26 @@ configure_certkey(char *path) return (KORE_RESULT_OK); } +static int +configure_keymgr_runas(char *user) +{ + if (keymgr_runas_user != NULL) + kore_free(keymgr_runas_user); + keymgr_runas_user = kore_strdup(user); + + return (KORE_RESULT_OK); +} + +static int +configure_keymgr_root(char *root) +{ + if (keymgr_root_path != NULL) + kore_free(keymgr_root_path); + keymgr_root_path = kore_strdup(root); + + return (KORE_RESULT_OK); +} + #endif /* !KORE_NO_TLS */ static int @@ -1090,11 +1141,11 @@ configure_websocket_timeout(char *option) #endif /* !KORE_NO_HTTP */ static int -configure_chroot(char *path) +configure_root(char *path) { - if (chroot_path != NULL) - kore_free(chroot_path); - chroot_path = kore_strdup(path); + if (kore_root_path != NULL) + kore_free(kore_root_path); + kore_root_path = kore_strdup(path); return (KORE_RESULT_OK); } @@ -1102,9 +1153,9 @@ configure_chroot(char *path) static int configure_runas(char *user) { - if (runas_user != NULL) - kore_free(runas_user); - runas_user = kore_strdup(user); + if (kore_runas_user != NULL) + kore_free(kore_runas_user); + kore_runas_user = kore_strdup(user); return (KORE_RESULT_OK); } @@ -1203,13 +1254,6 @@ configure_socket_backlog(char *option) return (KORE_RESULT_OK); } -static void -domain_tls_init(void) -{ - kore_domain_tlsinit(current_domain); - current_domain = NULL; -} - #if defined(KORE_USE_PGSQL) static int configure_pgsql_conn_max(char *option) diff --git a/src/connection.c b/src/connection.c @@ -223,6 +223,11 @@ kore_connection_handle(struct connection *c) switch (c->state) { #if !defined(KORE_NO_TLS) case CONN_STATE_TLS_SHAKE: + if (primary_dom->ssl_ctx == NULL) { + kore_log(LOG_NOTICE, "TLS setup not yet complete"); + return (KORE_RESULT_ERROR); + } + if (c->ssl == NULL) { c->ssl = SSL_new(primary_dom->ssl_ctx); if (c->ssl == NULL) { diff --git a/src/domain.c b/src/domain.c @@ -54,6 +54,7 @@ int tls_version = KORE_TLS_VERSION_1_2; #if !defined(KORE_NO_TLS) 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 char *, size_t); static void keymgr_init(void); static void keymgr_await_data(void); @@ -236,11 +237,10 @@ kore_domain_free(struct kore_domain *dom) kore_free(dom); } +#if !defined(KORE_NO_TLS) void -kore_domain_tlsinit(struct kore_domain *dom) +kore_domain_tlsinit(struct kore_domain *dom, const void *pem, size_t pemlen) { -#if !defined(KORE_NO_TLS) - BIO *in; RSA *rsa; X509 *x509; EVP_PKEY *pkey; @@ -251,7 +251,10 @@ kore_domain_tlsinit(struct kore_domain *dom) EC_KEY *ecdh; #endif - kore_debug("kore_domain_sslstart(%s)", dom->domain); + kore_debug("kore_domain_tlsinit(%s)", dom->domain); + + if (dom->ssl_ctx != NULL) + SSL_CTX_free(dom->ssl_ctx); #if !defined(LIBRESSL_VERSION_TEXT) && OPENSSL_VERSION_NUMBER >= 0x10100000L if ((method = TLS_method()) == NULL) @@ -299,20 +302,8 @@ kore_domain_tlsinit(struct kore_domain *dom) return; } #endif - if (!SSL_CTX_use_certificate_chain_file(dom->ssl_ctx, dom->certfile)) { - fatal("SSL_CTX_use_certificate_chain_file(%s): %s", - dom->certfile, ssl_errno_s); - } - - if ((in = BIO_new(BIO_s_file())) == NULL) - fatal("BIO_new: %s", ssl_errno_s); - if (BIO_read_filename(in, dom->certfile) <= 0) - fatal("BIO_read_filename: %s", ssl_errno_s); - if ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL) - fatal("PEM_read_bio_X509: %s", ssl_errno_s); - - BIO_free(in); + x509 = domain_load_certificate_chain(dom->ssl_ctx, pem, pemlen); if ((pkey = X509_get_pubkey(x509)) == NULL) fatal("certificate has no public key"); @@ -392,10 +383,9 @@ kore_domain_tlsinit(struct kore_domain *dom) SSL_CTX_set_info_callback(dom->ssl_ctx, kore_tls_info_callback); SSL_CTX_set_tlsext_servername_callback(dom->ssl_ctx, kore_tls_sni_cb); - kore_free(dom->certfile); - dom->certfile = NULL; -#endif + X509_free(x509); } +#endif void kore_domain_callback(void (*cb)(struct kore_domain *)) @@ -756,4 +746,52 @@ domain_x509_verify(int ok, X509_STORE_CTX *ctx) return (ok); } + +/* + * What follows is basically a reimplementation of + * SSL_CTX_use_certificate_chain_file() from OpenSSL but with our + * BIO set to the pem data that we received. + */ +static X509 * +domain_load_certificate_chain(SSL_CTX *ctx, const char *data, size_t len) +{ + BIO *in; + unsigned long err; + X509 *x, *ca; + + /* because OpenSSL likes taking ints as buffer sizes. */ + if (len > INT_MAX) + fatal("domain_load_certificate_chain: len > INT_MAX"); + + ERR_clear_error(); + + if ((in = BIO_new_mem_buf(data, len)) == NULL) + fatal("BIO_new_mem_buf: %s", ssl_errno_s); + + if ((x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL)) == NULL) + fatal("PEM_read_bio_X509_AUX: %s", ssl_errno_s); + + /* refcount for x509 will go up one. */ + if (SSL_CTX_use_certificate(ctx, x) == 0) + fatal("SSL_CTX_use_certificate: %s", ssl_errno_s); + + SSL_CTX_clear_chain_certs(ctx); + + ERR_clear_error(); + while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL) { + /* ca its reference count won't be increased. */ + if (SSL_CTX_add0_chain_cert(ctx, ca) == 0) + fatal("SSL_CTX_add0_chain_cert: %s", ssl_errno_s); + } + + err = ERR_peek_last_error(); + + if (ERR_GET_LIB(err) != ERR_LIB_PEM || + ERR_GET_REASON(err) != PEM_R_NO_START_LINE) + fatal("PEM_read_bio_X509: %s", ssl_errno_s); + + BIO_free(in); + + return (x); +} #endif diff --git a/src/filemap.c b/src/filemap.c @@ -59,7 +59,7 @@ kore_filemap_create(struct kore_domain *dom, const char *path, const char *root) struct stat st; int len; struct filemap_entry *entry; - char regex[1024]; + char regex[1024], fpath[PATH_MAX]; sz = strlen(root); if (sz == 0) @@ -68,7 +68,11 @@ kore_filemap_create(struct kore_domain *dom, const char *path, const char *root) if (root[0] != '/' || root[sz - 1] != '/') return (KORE_RESULT_ERROR); - if (stat(path, &st) == -1) + len = snprintf(fpath, sizeof(fpath), "%s/%s", kore_root_path, path); + if (len == -1 || (size_t)len >= sizeof(regex)) + fatal("kore_filemap_create: failed to concat paths"); + + if (stat(fpath, &st) == -1) return (KORE_RESULT_ERROR); len = snprintf(regex, sizeof(regex), "^%s.*$", root); diff --git a/src/keymgr.c b/src/keymgr.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/param.h> +#include <sys/types.h> #include <sys/stat.h> #include <openssl/evp.h> @@ -46,24 +46,36 @@ static TAILQ_HEAD(, key) keys; extern volatile sig_atomic_t sig_recv; static int initialized = 0; +static void keymgr_reload(void); static void keymgr_load_randfile(void); static void keymgr_save_randfile(void); static void keymgr_load_privatekey(struct kore_domain *); 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_rsa_encrypt(struct kore_msg *, const void *, struct key *); static void keymgr_ecdsa_sign(struct kore_msg *, const void *, struct key *); +char *keymgr_root_path = NULL; +char *keymgr_runas_user = NULL; + void kore_keymgr_run(void) { int quit; u_int64_t now, last_seed; + quit = 0; + + kore_listener_cleanup(); + kore_module_cleanup(); + kore_worker_privdrop(keymgr_runas_user, keymgr_root_path); + if (rand_file != NULL) { keymgr_load_randfile(); keymgr_save_randfile(); @@ -71,15 +83,7 @@ kore_keymgr_run(void) kore_log(LOG_WARNING, "no rand_file location specified"); } - quit = 0; initialized = 1; - TAILQ_INIT(&keys); - - kore_listener_cleanup(); - kore_module_cleanup(); - - kore_domain_callback(keymgr_load_privatekey); - kore_worker_privdrop(runas_user, chroot_path); net_init(); kore_connection_init(); @@ -88,8 +92,13 @@ kore_keymgr_run(void) kore_msg_worker_init(); kore_msg_register(KORE_MSG_KEYMGR_REQ, keymgr_msg_recv); kore_msg_register(KORE_MSG_ENTROPY_REQ, keymgr_entropy_request); + kore_msg_register(KORE_MSG_CERTIFICATE_REQ, keymgr_certificate_request); + + keymgr_reload(); + RAND_poll(); last_seed = 0; + kore_log(LOG_NOTICE, "key manager started"); while (quit != 1) { @@ -106,6 +115,9 @@ kore_keymgr_run(void) case SIGTERM: quit = 1; break; + case SIGUSR1: + keymgr_reload(); + break; default: break; } @@ -116,18 +128,19 @@ kore_keymgr_run(void) kore_connection_prune(KORE_CONNECTION_PRUNE_DISCONNECT); } - kore_keymgr_cleanup(); + kore_keymgr_cleanup(1); kore_platform_event_cleanup(); kore_connection_cleanup(); net_cleanup(); } void -kore_keymgr_cleanup(void) +kore_keymgr_cleanup(int final) { struct key *key, *next; - kore_log(LOG_NOTICE, "cleaning up keys"); + if (final) + kore_log(LOG_NOTICE, "cleaning up keys"); if (initialized == 0) return; @@ -142,6 +155,69 @@ kore_keymgr_cleanup(void) } static void +keymgr_reload(void) +{ + struct kore_domain *dom; + + kore_log(LOG_INFO, "(re)loading certificates and keys"); + + kore_keymgr_cleanup(0); + TAILQ_INIT(&keys); + + kore_domain_callback(keymgr_load_privatekey); + + /* can't use kore_domain_callback() due to dst parameter. */ + TAILQ_FOREACH(dom, &domains, list) + keymgr_submit_certificates(dom, KORE_MSG_WORKER_ALL); +} + +static void +keymgr_submit_certificates(struct kore_domain *dom, u_int16_t dst) +{ + int fd; + struct stat st; + ssize_t ret; + size_t len; + 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 (st.st_size <= 0 || st.st_size > (1024 * 1024 * 5)) { + fatal("%s length is not valid (%jd)", dom->certfile, + (intmax_t)st.st_size); + } + + len = sizeof(*msg) + st.st_size; + payload = kore_calloc(1, len); + + msg = (struct kore_x509_msg *)payload; + msg->domain_len = strlen(dom->domain); + if (msg->domain_len > sizeof(msg->domain)) + fatal("domain name '%s' too long", dom->domain); + memcpy(msg->domain, dom->domain, msg->domain_len); + + msg->data_len = st.st_size; + ret = read(fd, &msg->data[0], msg->data_len); + if (ret == 0) + fatal("eof while reading %s", dom->certfile); + + if ((size_t)ret != msg->data_len) { + fatal("bad read on %s: expected %zu, got %zd", + dom->certfile, msg->data_len, ret); + } + + kore_msg_send(dst, KORE_MSG_CERTIFICATE, payload, len); + kore_free(payload); + close(fd); +} + +static void keymgr_load_randfile(void) { int fd; @@ -245,26 +321,30 @@ keymgr_load_privatekey(struct kore_domain *dom) FILE *fp; struct key *key; - if (dom->certkey == NULL) - return; - if ((fp = fopen(dom->certkey, "r")) == NULL) fatal("failed to open private key: %s", dom->certkey); - key = kore_malloc(sizeof(*key)); + key = kore_calloc(1, sizeof(*key)); key->dom = dom; if ((key->pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) fatal("PEM_read_PrivateKey: %s", ssl_errno_s); (void)fclose(fp); - kore_free(dom->certkey); - dom->certkey = NULL; TAILQ_INSERT_TAIL(&keys, key, list); } static void +keymgr_certificate_request(struct kore_msg *msg, const void *data) +{ + struct kore_domain *dom; + + TAILQ_FOREACH(dom, &domains, list) + keymgr_submit_certificates(dom, msg->src); +} + +static void keymgr_entropy_request(struct kore_msg *msg, const void *data) { u_int8_t buf[RAND_FILE_SIZE]; @@ -290,8 +370,11 @@ keymgr_msg_recv(struct kore_msg *msg, const void *data) return; req = (const struct kore_keyreq *)data; + if (msg->length != (sizeof(*req) + req->data_len)) return; + if (req->domain_len > KORE_DOMAINNAME_LEN) + return; key = NULL; TAILQ_FOREACH(key, &keys, list) { diff --git a/src/kore.c b/src/kore.c @@ -41,11 +41,11 @@ pid_t kore_pid = -1; u_int16_t cpu_count = 1; int foreground = 0; int kore_debug = 0; -u_int8_t worker_count = 0; -int skip_chroot = 0; -char *chroot_path = NULL; int skip_runas = 0; -char *runas_user = NULL; +int skip_chroot = 0; +u_int8_t worker_count = 0; +char *kore_root_path = NULL; +char *kore_runas_user = NULL; u_int32_t kore_socket_backlog = 5000; char *kore_pidfile = KORE_PIDFILE_DEFAULT; char *kore_tls_cipher_list = KORE_DEFAULT_CIPHER_LIST; @@ -394,6 +394,8 @@ kore_signal_setup(void) fatal("sigaction: %s", errno_s); if (sigaction(SIGTERM, &sa, NULL) == -1) fatal("sigaction: %s", errno_s); + if (sigaction(SIGUSR1, &sa, NULL) == -1) + fatal("sigaction: %s", errno_s); if (foreground) { if (sigaction(SIGINT, &sa, NULL) == -1) @@ -486,6 +488,7 @@ kore_server_start(void) quit = 0; worker_max_connections = tmp; + while (quit != 1) { if (sig_recv != 0) { switch (sig_recv) { @@ -499,6 +502,9 @@ kore_server_start(void) quit = 1; kore_worker_dispatch_signal(sig_recv); continue; + case SIGUSR1: + kore_worker_dispatch_signal(sig_recv); + break; default: break; } diff --git a/src/msg.c b/src/msg.c @@ -129,7 +129,7 @@ kore_msg_register(u_int8_t id, void (*cb)(struct kore_msg *, const void *)) } void -kore_msg_send(u_int16_t dst, u_int8_t id, const void *data, u_int32_t len) +kore_msg_send(u_int16_t dst, u_int8_t id, const void *data, size_t len) { struct kore_msg m; diff --git a/src/utils.c b/src/utils.c @@ -620,7 +620,7 @@ fatal(const char *fmt, ...) #if !defined(KORE_NO_TLS) if (worker != NULL && worker->id == KORE_WORKER_KEYMGR) - kore_keymgr_cleanup(); + kore_keymgr_cleanup(1); #endif printf("%s: %s\n", __progname, buf); diff --git a/src/worker.c b/src/worker.c @@ -76,7 +76,8 @@ static inline int kore_worker_acceptlock_obtain(u_int64_t); static inline int kore_worker_acceptlock_release(u_int64_t); #if !defined(KORE_NO_TLS) -static void worker_entropy_recv(struct kore_msg *, const void *); +static void worker_entropy_recv(struct kore_msg *, const void *); +static void worker_certificate_recv(struct kore_msg *, const void *); #endif static struct kore_worker *kore_workers; @@ -225,8 +226,13 @@ kore_worker_privdrop(const char *runas, const char *root) struct rlimit rl; struct passwd *pw = NULL; + if (root == NULL) + fatal("no root directory for kore_worker_privdrop"); + /* Must happen before chroot. */ if (skip_runas == 0) { + if (runas == NULL) + fatal("no runas user given and -r not specified"); pw = getpwnam(runas); if (pw == NULL) { fatal("cannot getpwnam(\"%s\") for user: %s", @@ -242,6 +248,9 @@ kore_worker_privdrop(const char *runas, const char *root) if (chdir("/") == -1) fatal("cannot chdir(\"/\"): %s", errno_s); + } else { + if (chdir(root) == -1) + fatal("cannot chdir(\"/\"): %s", errno_s); } if (getrlimit(RLIMIT_NOFILE, &rl) == -1) { @@ -309,7 +318,7 @@ kore_worker_entry(struct kore_worker *kw) } #endif - kore_worker_privdrop(runas_user, chroot_path); + kore_worker_privdrop(kore_runas_user, kore_root_path); net_init(); #if !defined(KORE_NO_HTTP) @@ -343,6 +352,11 @@ kore_worker_entry(struct kore_worker *kw) #if !defined(KORE_NO_TLS) last_seed = 0; kore_msg_register(KORE_MSG_ENTROPY_RESP, worker_entropy_recv); + kore_msg_register(KORE_MSG_CERTIFICATE, worker_certificate_recv); + if (worker->restarted) { + kore_msg_send(KORE_WORKER_KEYMGR, + KORE_MSG_CERTIFICATE_REQ, NULL, 0); + } #endif if (nlisteners == 0) @@ -357,6 +371,7 @@ kore_worker_entry(struct kore_worker *kw) } kore_module_onload(); + worker->restarted = 0; for (;;) { if (sig_recv != 0) { @@ -530,6 +545,7 @@ kore_worker_wait(int final) } kore_log(LOG_NOTICE, "restarting worker %d", kw->id); + kw->restarted = 1; kore_msg_parent_remove(kw); kore_worker_spawn(kw->id, kw->cpu); kore_msg_parent_add(kw); @@ -631,11 +647,53 @@ worker_entropy_recv(struct kore_msg *msg, const void *data) { if (msg->length != 1024) { kore_log(LOG_WARNING, - "invalid entropy response (got:%u - wanted:1024)", + "invalid entropy response (got:%zu - wanted:1024)", msg->length); } RAND_poll(); RAND_seed(data, msg->length); } + +static void +worker_certificate_recv(struct kore_msg *msg, const void *data) +{ + 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; + } + + 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; + } + + if (req->domain_len > KORE_DOMAINNAME_LEN) { + kore_log(LOG_WARNING, + "invalid KORE_MSG_CERTIFICATE domain (%u)", + req->domain_len); + return; + } + + dom = NULL; + TAILQ_FOREACH(dom, &domains, list) { + if (!strncmp(dom->domain, req->domain, req->domain_len)) + break; + } + + if (dom == NULL) { + kore_log(LOG_WARNING, + "got KORE_MSG_CERTIFICATE for domain that does not exist"); + return; + } + + /* reinitialize the domain TLS context. */ + kore_domain_tlsinit(dom, req->data, req->data_len); +} #endif