kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

commit 99a1581e19afd58b79a5eb8d97445142aed81a06
parent 6dc162e7ee2724cabdcbfc93ccd25f696a83572f
Author: Joris Vink <joris@coders.se>
Date:   Thu, 17 Feb 2022 13:45:28 +0100

Initial work splitting OpenSSL code away.

This work moves all TLS / crypto related code into a tls_openssl.c
file and adds a tls_none.c which contains just stubs.

Allows compilation of Kore with TLS_BACKEND=none to remove building
against OpenSSL.

Also adds code for SHA1/SHA2 taken from openssh-portable so we don't
depend on those being present anymore in libcrypto.

Diffstat:
Makefile | 28++++++++++++++++++----------
include/kore/acme.h | 4----
include/kore/http.h | 4++--
include/kore/kore.h | 167+++++++++++++++++++++++++++++++++++++++++++------------------------------------
src/acme.c | 73-------------------------------------------------------------------------
src/config.c | 71+++++++++++++++++++++++++----------------------------------------------
src/connection.c | 85++++---------------------------------------------------------------------------
src/domain.c | 590+------------------------------------------------------------------------------
src/http.c | 6+++---
src/keymgr.c | 1373-------------------------------------------------------------------------------
src/keymgr_openssl.c | 1372+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/kore.c | 100++++++++++++++-----------------------------------------------------------------
src/msg.c | 2+-
src/net.c | 98-------------------------------------------------------------------------------
src/sha1.c | 171+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/sha2.c | 885+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/tls_none.c | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/tls_openssl.c | 1156++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/utils.c | 125++++++-------------------------------------------------------------------------
src/websocket.c | 13++++++-------
src/worker.c | 21++++++++++-----------
21 files changed, 3934 insertions(+), 2571 deletions(-)

diff --git a/Makefile b/Makefile @@ -12,6 +12,7 @@ INSTALL_DIR=$(PREFIX)/bin MAN_DIR?=$(PREFIX)/share/man SHARE_DIR=$(PREFIX)/share/kore INCLUDE_DIR=$(PREFIX)/include/kore +TLS_BACKEND?=openssl TOOLS= kore-serve @@ -23,7 +24,8 @@ PYTHON_CURLOPT=misc/curl/python_curlopt.h S_SRC= src/kore.c src/buf.c src/config.c src/connection.c \ src/domain.c src/filemap.c src/fileref.c src/json.c src/log.c \ src/mem.c src/msg.c src/module.c src/net.c src/pool.c src/runtime.c \ - src/timer.c src/utils.c src/worker.c src/keymgr.c + src/sha1.c src/sha2.c src/timer.c src/utils.c src/worker.c +S_SRC+= src/tls_$(TLS_BACKEND).c FEATURES= FEATURES_INC= @@ -34,11 +36,15 @@ CFLAGS+=-Wsign-compare -Iinclude/kore -I$(OBJDIR) -std=c99 -pedantic CFLAGS+=-Wtype-limits -fno-common CFLAGS+=-DPREFIX='"$(PREFIX)"' -fstack-protector-all -ifneq ("$(OPENSSL_PATH)", "") -CFLAGS+=-I$(OPENSSL_PATH)/include -LDFLAGS+=-rdynamic -L$(OPENSSL_PATH)/lib -lssl -l$(KORE_CRYPTO) -else -LDFLAGS+=-rdynamic -lssl -l$(KORE_CRYPTO) +ifeq ("$(TLS_BACKEND)", "openssl") + S_SRC+=src/keymgr_openssl.c + + ifneq ("$(OPENSSL_PATH)", "") + CFLAGS+=-I$(OPENSSL_PATH)/include + LDFLAGS+=-rdynamic -L$(OPENSSL_PATH)/lib -lssl -l$(KORE_CRYPTO) + else + LDFLAGS+=-rdynamic -lssl -l$(KORE_CRYPTO) + endif endif ifneq ("$(KORE_SINGLE_BINARY)", "") @@ -132,10 +138,12 @@ ifneq ("$(SANITIZE)", "") endif ifeq ("$(OSNAME)", "darwin") - OSSL_INCL=$(shell pkg-config openssl --cflags) - CFLAGS+=$(OSSL_INCL) - LDFLAGS+=$(shell pkg-config openssl --libs) - FEATURES_INC+=$(OSSL_INCL) + ifeq ("$(TLS_BACKEND)", "openssl") + OSSL_INCL=$(shell pkg-config openssl --cflags) + CFLAGS+=$(OSSL_INCL) + LDFLAGS+=$(shell pkg-config openssl --libs) + FEATURES_INC+=$(OSSL_INCL) + endif S_SRC+=src/bsd.c else ifeq ("$(OSNAME)", "linux") CFLAGS+=-D_GNU_SOURCE=1 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 diff --git a/include/kore/acme.h b/include/kore/acme.h @@ -49,10 +49,6 @@ void kore_acme_run(void); void kore_acme_setup(void); void kore_acme_get_paths(const char *, char **, char **); -void kore_acme_tls_challenge_use_cert(SSL *, struct kore_domain *); -int kore_acme_tls_alpn(SSL *, const unsigned char **, unsigned char *, - const unsigned char *, unsigned int, void *); - extern char *acme_email; extern int acme_domains; extern char *acme_provider; diff --git a/include/kore/http.h b/include/kore/http.h @@ -22,7 +22,7 @@ #include <sys/types.h> #include <sys/queue.h> -#include <openssl/sha.h> +#include "sha2.h" #if defined(__cplusplus) extern "C" { @@ -281,7 +281,7 @@ struct http_request { const char *agent; const char *referer; struct connection *owner; - SHA256_CTX hashctx; + SHA2_CTX hashctx; u_int8_t *headers; struct kore_buf *http_body; int http_body_fd; diff --git a/include/kore/kore.h b/include/kore/kore.h @@ -30,10 +30,6 @@ #include <netinet/in.h> #include <arpa/inet.h> -#include <openssl/err.h> -#include <openssl/dh.h> -#include <openssl/ssl.h> - #include <errno.h> #include <regex.h> #include <stdarg.h> @@ -61,24 +57,6 @@ extern int daemon(int, int); #endif #endif -/* - * Figure out what type of OpenSSL API we are dealing with. - */ -#if defined(LIBRESSL_VERSION_NUMBER) -#if LIBRESSL_VERSION_NUMBER >= 0x3000000fL -#define KORE_OPENSSL_NEWER_API 1 -#endif - -#if LIBRESSL_VERSION_NUMBER >= 0x3020200fL -#define TLS1_3_VERSION 0x0304 -#endif - -#else -#if OPENSSL_VERSION_NUMBER >= 0x10100000L -#define KORE_OPENSSL_NEWER_API 1 -#endif -#endif - #if defined(__OpenBSD__) #define KORE_USE_PLATFORM_PLEDGE 1 #endif @@ -236,8 +214,8 @@ struct connection { u_int8_t state; u_int8_t proto; struct listener *owner; - X509 *cert; - SSL *ssl; + void *ssl; + void *cert; char *tls_sni; int tls_reneg; u_int16_t flags; @@ -359,7 +337,7 @@ struct kore_domain { char *crlfile; char *certfile; char *certkey; - SSL_CTX *ssl_ctx; + void *ssl_ctx; int x509_verify_depth; #if !defined(KORE_NO_HTTP) TAILQ_HEAD(, kore_route) routes; @@ -610,8 +588,8 @@ struct kore_json_item { u_int64_t u64; } data; - int (*parse)(struct kore_json *, - struct kore_json_item *); + int (*parse)(struct kore_json *, + struct kore_json_item *); TAILQ_ENTRY(kore_json_item) list; }; @@ -720,14 +698,11 @@ extern int skip_runas; extern int kore_foreground; extern char *kore_pidfile; -extern char *kore_tls_cipher_list; extern volatile sig_atomic_t sig_recv; -extern int tls_version; -extern DH *tls_dhparam; -extern char *rand_file; -extern int keymgr_active; +extern char *kore_rand_file; +extern int kore_keymgr_active; extern struct kore_privsep worker_privsep; extern struct kore_privsep keymgr_privsep; @@ -753,6 +728,7 @@ extern struct kore_pool nb_pool; extern struct kore_domain *primary_dom; extern struct kore_server_list kore_servers; +/* kore.c */ void kore_signal(int); void kore_shutdown(void); void kore_signal_trap(int); @@ -760,6 +736,26 @@ void kore_signal_setup(void); void kore_proctitle(const char *); void kore_default_getopt(int, char **); +void kore_server_closeall(void); +void kore_server_cleanup(void); +void kore_server_free(struct kore_server *); +void kore_server_finalize(struct kore_server *); + +struct kore_server *kore_server_create(const char *); +struct kore_server *kore_server_lookup(const char *); + +void kore_listener_accept(void *, int); +struct listener *kore_listener_lookup(const char *); +void kore_listener_free(struct listener *); +struct listener *kore_listener_create(struct kore_server *); +int kore_listener_init(struct listener *, int, const char *); + +int kore_sockopt(int, int, int); +int kore_server_bind_unix(struct kore_server *, + const char *, const char *); +int kore_server_bind(struct kore_server *, + const char *, const char *, const char *); +/* worker.c */ void kore_worker_reap(void); int kore_worker_init(void); void kore_worker_privsep(void); @@ -776,6 +772,7 @@ void kore_worker_entry(struct kore_worker *) __attribute__((noreturn)); struct kore_worker *kore_worker_data(u_int8_t); struct kore_worker *kore_worker_data_byid(u_int16_t); +/* platform code (linux.c, bsd.c) */ void kore_platform_init(void); void kore_platform_sandbox(void); void kore_platform_event_init(void); @@ -803,12 +800,43 @@ void kore_platform_pledge(void); void kore_platform_add_pledge(const char *); #endif +/* tls variants. */ +#define KORE_X509_NAME_COMMON_NAME 1 + +void kore_tls_init(void); +void kore_tls_cleanup(void); +void kore_tls_dh_check(void); +int kore_tls_supported(void); +void kore_tls_version_set(int); +void kore_tls_keymgr_init(void); +int kore_tls_dh_load(const char *); +void kore_tls_seed(const void *, size_t); +int kore_tls_ciphersuite_set(const char *); +int kore_tls_read(struct connection *, size_t *); +void kore_tls_domain_cleanup(struct kore_domain *); +int kore_tls_connection_accept(struct connection *); +void kore_tls_connection_cleanup(struct connection *); +int kore_tls_write(struct connection *, size_t, size_t *); +void kore_tls_domain_crl(struct kore_domain *, const void *, size_t); +void kore_tls_domain_setup(struct kore_domain *, + int, const void *, size_t); + +void *kore_tls_rsakey_load(const char *); +void *kore_tls_rsakey_generate(const char *); + +void *kore_tls_x509_issuer_name(struct connection *); +void *kore_tls_x509_subject_name(struct connection *); +int kore_tls_x509name_foreach(void *, int, void *, + int (*)(void *, int, int, const char *, + const void *, size_t, int)); +/* accesslog.c */ void kore_accesslog_init(u_int16_t); void kore_accesslog_worker_init(void); void kore_accesslog_run(void *, u_int64_t); void kore_accesslog_gather(void *, u_int64_t, int); #if !defined(KORE_NO_HTTP) +/* auth.c */ int kore_auth_run(struct http_request *, struct kore_auth *); int kore_auth_cookie(struct http_request *, struct kore_auth *); int kore_auth_header(struct http_request *, struct kore_auth *); @@ -818,6 +846,7 @@ int kore_auth_new(const char *); struct kore_auth *kore_auth_lookup(const char *); #endif +/* timer.c */ void kore_timer_init(void); void kore_timer_run(u_int64_t); u_int64_t kore_timer_next_run(u_int64_t); @@ -825,29 +854,7 @@ void kore_timer_remove(struct kore_timer *); struct kore_timer *kore_timer_add(void (*cb)(void *, u_int64_t), u_int64_t, void *, int); -void kore_server_closeall(void); -void kore_server_cleanup(void); -void kore_server_free(struct kore_server *); -void kore_server_finalize(struct kore_server *); - -struct kore_server *kore_server_create(const char *); -struct kore_server *kore_server_lookup(const char *); - -void kore_listener_accept(void *, int); -struct listener *kore_listener_lookup(const char *); -void kore_listener_free(struct listener *); -struct listener *kore_listener_create(struct kore_server *); -int kore_listener_init(struct listener *, int, const char *); - -int kore_sockopt(int, int, int); -int kore_server_bind_unix(struct kore_server *, - const char *, const char *); -int kore_server_bind(struct kore_server *, - const char *, const char *, const char *); - -int kore_tls_sni_cb(SSL *, int *, void *); -void kore_tls_info_callback(const SSL *, int, int); - +/* connection.c */ void kore_connection_init(void); void kore_connection_cleanup(void); void kore_connection_prune(int); @@ -865,7 +872,6 @@ void kore_connection_check_idletimer(u_int64_t, int kore_connection_accept(struct listener *, struct connection **); -u_int64_t kore_time_ms(void); void kore_log_init(void); void kore_log_file(const char *); @@ -873,9 +879,12 @@ void kore_log_file(const char *); int kore_configure_setting(const char *, char *); #endif -void *kore_malloc(size_t); +/* config.c */ void kore_parse_config(void); void kore_parse_config_file(FILE *); + +/* mem.c */ +void *kore_malloc(size_t); void *kore_calloc(size_t, size_t); void *kore_realloc(void *, size_t); void kore_free(void *); @@ -886,12 +895,19 @@ void *kore_mem_lookup(u_int32_t); void kore_mem_tag(void *, u_int32_t); void *kore_malloc_tagged(size_t, u_int32_t); +/* pool.c */ void *kore_pool_get(struct kore_pool *); void kore_pool_put(struct kore_pool *, void *); void kore_pool_init(struct kore_pool *, const char *, size_t, size_t); void kore_pool_cleanup(struct kore_pool *); +/* utils.c */ +void kore_debug_internal(char *, int, const char *, ...); +void fatal(const char *, ...) __attribute__((noreturn)); +void fatalx(const char *, ...) __attribute__((noreturn)); + +u_int64_t kore_time_ms(void); char *kore_time_to_date(time_t); char *kore_strdup(const char *); time_t kore_date_to_time(const char *); @@ -909,19 +925,15 @@ int kore_base64_encode(const void *, size_t, char **); int kore_base64_decode(const char *, u_int8_t **, size_t *); int kore_base64url_encode(const void *, size_t, char **, int); int kore_base64url_decode(const char *, u_int8_t **, size_t *, int); +int kore_x509_issuer_name(struct connection *, char **, int); +int kore_x509_subject_name(struct connection *, char **, int); + void *kore_mem_find(void *, size_t, const void *, size_t); char *kore_text_trim(char *, size_t); char *kore_read_line(FILE *, char *, size_t); -EVP_PKEY *kore_rsakey_load(const char *); -EVP_PKEY *kore_rsakey_generate(const char *); -int kore_x509_issuer_name(struct connection *, char **, int); -int kore_x509_subject_name(struct connection *, char **, int); -int kore_x509name_foreach(X509_NAME *, int, void *, - int (*)(void *, int, int, const char *, - const void *, size_t, int)); - #if !defined(KORE_NO_HTTP) +/* websocket.c */ void kore_websocket_handshake(struct http_request *, const char *, const char *, const char *); int kore_websocket_send_clean(struct netbuf *); @@ -931,6 +943,7 @@ void kore_websocket_broadcast(struct connection *, u_int8_t, const void *, size_t, int); #endif +/* msg.c */ void kore_msg_init(void); void kore_msg_worker_init(void); void kore_msg_parent_init(void); @@ -942,6 +955,7 @@ int kore_msg_register(u_int8_t, void (*cb)(struct kore_msg *, const void *)); #if !defined(KORE_NO_HTTP) +/* filemap.c */ void kore_filemap_init(void); void kore_filemap_resolve_paths(void); int kore_filemap_create(struct kore_domain *, const char *, @@ -950,13 +964,17 @@ extern char *kore_filemap_ext; extern char *kore_filemap_index; #endif +/* fileref.c */ void kore_fileref_init(void); struct kore_fileref *kore_fileref_get(const char *, int); struct kore_fileref *kore_fileref_create(struct kore_server *, const char *, int, off_t, struct timespec *); void kore_fileref_release(struct kore_fileref *); +/* domain.c */ struct kore_domain *kore_domain_new(const char *); +struct kore_domain *kore_domain_byid(u_int16_t); +struct kore_domain *kore_domain_lookup(struct kore_server *, const char *); void kore_domain_init(void); void kore_domain_cleanup(void); @@ -972,11 +990,9 @@ void kore_domain_load_crl(void); void kore_domain_keymgr_init(void); void kore_domain_callback(void (*cb)(struct kore_domain *)); int kore_domain_attach(struct kore_domain *, struct kore_server *); -void kore_domain_tlsinit(struct kore_domain *, int, - const void *, size_t); -void kore_domain_crl_add(struct kore_domain *, const void *, size_t); #if !defined(KORE_NO_HTTP) +/* route.c */ void kore_route_reload(void); void kore_route_free(struct kore_route *); void kore_route_callback(struct kore_route *, const char *); @@ -987,6 +1003,7 @@ int kore_route_lookup(struct http_request *, struct kore_domain *, int, struct kore_route **); #endif +/* runtime.c */ struct kore_runtime_call *kore_runtime_getcall(const char *); struct kore_module *kore_module_load(const char *, const char *, int); @@ -1012,10 +1029,8 @@ void kore_runtime_wsmessage(struct kore_runtime_call *, struct connection *, u_int8_t, const void *, size_t); #endif -struct kore_domain *kore_domain_byid(u_int16_t); -struct kore_domain *kore_domain_lookup(struct kore_server *, const char *); - #if !defined(KORE_NO_HTTP) +/* validator.c */ void kore_validator_init(void); void kore_validator_reload(void); int kore_validator_add(const char *, u_int8_t, const char *); @@ -1025,12 +1040,9 @@ int kore_validator_check(struct http_request *, struct kore_validator *kore_validator_lookup(const char *); #endif -void fatal(const char *, ...) __attribute__((noreturn)); -void fatalx(const char *, ...) __attribute__((noreturn)); - const char *kore_worker_name(int); -void kore_debug_internal(char *, int, const char *, ...); +/* net.c */ u_int16_t net_read16(u_int8_t *); u_int32_t net_read32(u_int8_t *); u_int64_t net_read64(u_int8_t *); @@ -1045,9 +1057,7 @@ int net_send(struct connection *); int net_send_flush(struct connection *); int net_recv_flush(struct connection *); int net_read(struct connection *, size_t *); -int net_read_tls(struct connection *, size_t *); int net_write(struct connection *, size_t, size_t *); -int net_write_tls(struct connection *, size_t, size_t *); void net_recv_reset(struct connection *, size_t, int (*cb)(struct netbuf *)); void net_remove_netbuf(struct connection *, struct netbuf *); @@ -1060,6 +1070,7 @@ void net_send_stream(struct connection *, void *, size_t, int (*cb)(struct netbuf *), struct netbuf **); void net_send_fileref(struct connection *, struct kore_fileref *); +/* buf.c */ void kore_buf_free(struct kore_buf *); struct kore_buf *kore_buf_alloc(size_t); void kore_buf_init(struct kore_buf *, size_t); @@ -1074,6 +1085,7 @@ void kore_buf_appendv(struct kore_buf *, const char *, va_list); void kore_buf_replace_string(struct kore_buf *, const char *, const void *, size_t); +/* json.c */ int kore_json_errno(void); int kore_json_parse(struct kore_json *); void kore_json_cleanup(struct kore_json *); @@ -1088,6 +1100,7 @@ struct kore_json_item *kore_json_find(struct kore_json_item *, struct kore_json_item *kore_json_create_item(struct kore_json_item *, const char *, u_int32_t, ...); +/* keymgr.c */ void kore_keymgr_run(void); void kore_keymgr_cleanup(int); diff --git a/src/acme.c b/src/acme.c @@ -262,9 +262,6 @@ static char *revoke_url = NULL; static char *account_id = NULL; static char *account_url = NULL; -static u_int8_t acme_alpn_name[] = - { 0xa, 'a', 'c', 'm', 'e', '-', 't', 'l', 's', '/', '1' }; - struct kore_privsep acme_privsep; int acme_domains = 0; char *acme_email = NULL; @@ -358,39 +355,6 @@ kore_acme_run(void) net_cleanup(); } -int -kore_acme_tls_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, void *udata) -{ - struct connection *c; - - if ((c = SSL_get_ex_data(ssl, 0)) == NULL) - fatal("%s: no connection data present", __func__); - - if (inlen != sizeof(acme_alpn_name)) - return (SSL_TLSEXT_ERR_NOACK); - - if (memcmp(acme_alpn_name, in, sizeof(acme_alpn_name))) - return (SSL_TLSEXT_ERR_NOACK); - - *out = in + 1; - *outlen = inlen - 1; - - c->flags |= CONN_TLS_ALPN_ACME_SEEN; - - /* - * If SNI was already done, we can continue, otherwise we mark - * that we saw the right ALPN negotiation on this connection - * and wait for the SNI extension to be parsed. - */ - if (c->flags & CONN_TLS_SNI_SEEN) { - /* SNI was seen, we are on the right domain. */ - kore_acme_tls_challenge_use_cert(ssl, udata); - } - - return (SSL_TLSEXT_ERR_OK); -} - void kore_acme_get_paths(const char *domain, char **key, char **cert) { @@ -412,43 +376,6 @@ kore_acme_get_paths(const char *domain, char **key, char **cert) *key = kore_strdup(path); } -void -kore_acme_tls_challenge_use_cert(SSL *ssl, struct kore_domain *dom) -{ - struct connection *c; - const unsigned char *ptr; - X509 *x509; - - if (dom->acme == 0) { - kore_log(LOG_NOTICE, "[%s] ACME not active", dom->domain); - return; - } - - if (dom->acme_challenge == 0) { - kore_log(LOG_NOTICE, - "[%s] ACME auth challenge not active", dom->domain); - return; - } - - kore_log(LOG_INFO, "[%s] acme-tls/1 challenge requested", - dom->domain); - - if ((c = SSL_get_ex_data(ssl, 0)) == NULL) - fatal("%s: no connection data present", __func__); - - ptr = dom->acme_cert; - if ((x509 = d2i_X509(NULL, &ptr, dom->acme_cert_len)) == NULL) - fatal("d2i_X509: %s", ssl_errno_s); - - if (SSL_use_certificate(ssl, x509) == 0) - fatal("SSL_use_certificate: %s", ssl_errno_s); - - SSL_clear_chain_certs(ssl); - SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); - - c->proto = CONN_PROTO_ACME_ALPN; -} - static void acme_parse_directory(void) { diff --git a/src/config.c b/src/config.c @@ -309,7 +309,6 @@ void kore_parse_config(void) { FILE *fp; - BIO *bio; struct passwd *pwd; char path[PATH_MAX]; @@ -334,16 +333,7 @@ kore_parse_config(void) (void)fclose(fp); } - if (tls_dhparam == NULL) { - if ((bio = BIO_new_file(KORE_DHPARAM_PATH, "r")) == NULL) - fatal("failed to open %s", KORE_DHPARAM_PATH); - - tls_dhparam = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); - BIO_free(bio); - - if (tls_dhparam == NULL) - fatal("PEM_read_bio_DHparams(): %s", ssl_errno_s); - } + kore_tls_dh_check(); if (!kore_module_loaded()) fatal("no application module was loaded"); @@ -631,8 +621,19 @@ configure_server(char *options) static int configure_tls(char *yesno) { + if (!kore_tls_supported()) { + current_server->tls = 0; + + if (!strcmp(yesno, "yes")) { + kore_log(LOG_ERR, "TLS not supported in this build"); + return (KORE_RESULT_ERROR); + } + + return (KORE_RESULT_OK); + } + if (current_server == NULL) { - kore_log(LOG_ERR, "bind keyword not inside a server context"); + kore_log(LOG_ERR, "tls keyword not inside a server context"); return (KORE_RESULT_ERROR); } @@ -805,12 +806,14 @@ configure_file(char *file) static int configure_tls_version(char *version) { + int ver; + if (!strcmp(version, "1.3")) { - tls_version = KORE_TLS_VERSION_1_3; + ver = KORE_TLS_VERSION_1_3; } else if (!strcmp(version, "1.2")) { - tls_version = KORE_TLS_VERSION_1_2; + ver = KORE_TLS_VERSION_1_2; } else if (!strcmp(version, "both")) { - tls_version = KORE_TLS_VERSION_BOTH; + ver = KORE_TLS_VERSION_BOTH; } else { kore_log(LOG_ERR, "unknown value for tls_version: %s (use 1.3, 1.2, both)", @@ -818,45 +821,21 @@ configure_tls_version(char *version) return (KORE_RESULT_ERROR); } + kore_tls_version_set(ver); + return (KORE_RESULT_OK); } static int configure_tls_cipher(char *cipherlist) { - if (strcmp(kore_tls_cipher_list, KORE_DEFAULT_CIPHER_LIST)) { - kore_log(LOG_ERR, "tls_cipher specified twice"); - return (KORE_RESULT_ERROR); - } - - kore_tls_cipher_list = kore_strdup(cipherlist); - return (KORE_RESULT_OK); + return (kore_tls_ciphersuite_set(cipherlist)); } static int configure_tls_dhparam(char *path) { - BIO *bio; - - if (tls_dhparam != NULL) { - kore_log(LOG_ERR, "tls_dhparam specified twice"); - return (KORE_RESULT_ERROR); - } - - if ((bio = BIO_new_file(path, "r")) == NULL) { - kore_log(LOG_ERR, "tls_dhparam file '%s' not accessible", path); - return (KORE_RESULT_ERROR); - } - - tls_dhparam = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); - BIO_free(bio); - - if (tls_dhparam == NULL) { - kore_log(LOG_ERR, "PEM_read_bio_DHparams(): %s", ssl_errno_s); - return (KORE_RESULT_ERROR); - } - - return (KORE_RESULT_OK); + return (kore_tls_dh_load(path)); } static int @@ -914,10 +893,10 @@ configure_client_verify(char *options) static int configure_rand_file(char *path) { - if (rand_file != NULL) - kore_free(rand_file); + if (kore_rand_file != NULL) + kore_free(kore_rand_file); - rand_file = kore_strdup(path); + kore_rand_file = kore_strdup(path); return (KORE_RESULT_OK); } diff --git a/src/connection.c b/src/connection.c @@ -147,8 +147,8 @@ kore_connection_accept(struct listener *listener, struct connection **out) if (listener->server->tls) { c->state = CONN_STATE_TLS_SHAKE; - c->write = net_write_tls; - c->read = net_read_tls; + c->write = kore_tls_write; + c->read = kore_tls_read; } else { c->state = CONN_STATE_ESTABLISHED; c->write = net_write; @@ -250,7 +250,6 @@ kore_connection_event(void *arg, int error) int kore_connection_handle(struct connection *c) { - int r; struct listener *listener; kore_debug("kore_connection_handle(%p) -> %d", c, c->state); @@ -258,75 +257,8 @@ kore_connection_handle(struct connection *c) switch (c->state) { case CONN_STATE_TLS_SHAKE: - if (primary_dom == NULL) { - kore_log(LOG_NOTICE, - "TLS handshake but no TLS configured on server"); - return (KORE_RESULT_ERROR); - } - - if (primary_dom->ssl_ctx == NULL) { - kore_log(LOG_NOTICE, - "TLS configuration for %s not yet complete", - primary_dom->domain); + if (!kore_tls_connection_accept(c)) return (KORE_RESULT_ERROR); - } - - if (c->ssl == NULL) { - c->ssl = SSL_new(primary_dom->ssl_ctx); - if (c->ssl == NULL) { - kore_debug("SSL_new(): %s", ssl_errno_s); - return (KORE_RESULT_ERROR); - } - - SSL_set_fd(c->ssl, c->fd); - SSL_set_accept_state(c->ssl); - - if (!SSL_set_ex_data(c->ssl, 0, c)) { - kore_debug("SSL_set_ex_data(): %s", - ssl_errno_s); - return (KORE_RESULT_ERROR); - } - - if (primary_dom->cafile != NULL) - c->flags |= CONN_LOG_TLS_FAILURE; - } - - ERR_clear_error(); - r = SSL_accept(c->ssl); - if (r <= 0) { - r = SSL_get_error(c->ssl, r); - switch (r) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - kore_connection_start_idletimer(c); - return (KORE_RESULT_OK); - default: - kore_debug("SSL_accept(): %s", ssl_errno_s); - if (c->flags & CONN_LOG_TLS_FAILURE) { - kore_log(LOG_NOTICE, - "SSL_accept: %s", ssl_errno_s); - } - return (KORE_RESULT_ERROR); - } - } - -#if defined(KORE_USE_ACME) - if (c->proto == CONN_PROTO_ACME_ALPN) { - kore_log(LOG_INFO, "disconnecting acme client"); - kore_connection_disconnect(c); - return (KORE_RESULT_OK); - } -#endif - - if (SSL_get_verify_mode(c->ssl) & SSL_VERIFY_PEER) { - c->cert = SSL_get_peer_certificate(c->ssl); - if (c->cert == NULL) { - kore_log(LOG_NOTICE, "no peer certificate"); - return (KORE_RESULT_ERROR); - } - } else { - c->cert = NULL; - } if (c->owner != NULL) { listener = (struct listener *)c->owner; @@ -383,16 +315,7 @@ kore_connection_remove(struct connection *c) kore_debug("kore_connection_remove(%p)", c); - if (c->ssl != NULL) { - SSL_shutdown(c->ssl); - SSL_free(c->ssl); - } - - if (c->cert != NULL) - X509_free(c->cert); - - if (c->tls_sni != NULL) - kore_free(c->tls_sni); + kore_tls_connection_cleanup(c); close(c->fd); diff --git a/src/domain.c b/src/domain.c @@ -17,13 +17,6 @@ #include <sys/param.h> #include <sys/types.h> -#include <openssl/x509.h> -#include <openssl/bio.h> -#include <openssl/evp.h> -#include <openssl/ec.h> -#include <openssl/ecdsa.h> -#include <poll.h> - #include <fnmatch.h> #include "kore.h" @@ -37,35 +30,9 @@ #endif #define KORE_DOMAIN_CACHE 16 -#define SSL_SESSION_ID "kore_ssl_sessionid" - -struct kore_domain *primary_dom = NULL; - -static u_int8_t keymgr_buf[2048]; -static size_t keymgr_buflen = 0; -static int keymgr_response = 0; -DH *tls_dhparam = NULL; -int tls_version = KORE_TLS_VERSION_BOTH; - -static int domain_x509_verify(int, X509_STORE_CTX *); -static X509 *domain_load_certificate_chain(SSL_CTX *, const void *, size_t); - -static void keymgr_init(void); -static void keymgr_await_data(void); -static void keymgr_msg_response(struct kore_msg *, const void *); - -static int keymgr_rsa_init(RSA *); -static int keymgr_rsa_finish(RSA *); -static int keymgr_rsa_privenc(int, const unsigned char *, - unsigned char *, RSA *, int); - -static ECDSA_SIG *keymgr_ecdsa_sign(const unsigned char *, int, - const BIGNUM *, const BIGNUM *, EC_KEY *); - -static RSA_METHOD *keymgr_rsa_meth = NULL; -static EC_KEY_METHOD *keymgr_ec_meth = NULL; static u_int16_t domain_id = 0; +struct kore_domain *primary_dom = NULL; static struct kore_domain *cached[KORE_DOMAIN_CACHE]; void @@ -75,45 +42,11 @@ kore_domain_init(void) for (i = 0; i < KORE_DOMAIN_CACHE; i++) cached[i] = NULL; - - if (keymgr_rsa_meth == NULL) { - if ((keymgr_rsa_meth = RSA_meth_new("kore RSA keymgr method", - RSA_METHOD_FLAG_NO_CHECK)) == NULL) - fatal("failed to allocate RSA method"); - } - - RSA_meth_set_init(keymgr_rsa_meth, keymgr_rsa_init); - RSA_meth_set_finish(keymgr_rsa_meth, keymgr_rsa_finish); - RSA_meth_set_priv_enc(keymgr_rsa_meth, keymgr_rsa_privenc); - - if (keymgr_ec_meth == NULL) { - if ((keymgr_ec_meth = EC_KEY_METHOD_new(NULL)) == NULL) - fatal("failed to allocate EC KEY method"); - } - - EC_KEY_METHOD_set_sign(keymgr_ec_meth, NULL, NULL, keymgr_ecdsa_sign); - -#if !defined(TLS1_3_VERSION) - if (!kore_quiet) { - kore_log(LOG_NOTICE, - "%s has no TLS 1.3 - will only use TLS 1.2", - OPENSSL_VERSION_TEXT); - } -#endif } void kore_domain_cleanup(void) { - if (keymgr_rsa_meth != NULL) { - RSA_meth_free(keymgr_rsa_meth); - keymgr_rsa_meth = NULL; - } - - if (keymgr_ec_meth != NULL) { - EC_KEY_METHOD_free(keymgr_ec_meth); - keymgr_ec_meth = NULL; - } } struct kore_domain * @@ -125,9 +58,10 @@ kore_domain_new(const char *domain) dom = kore_calloc(1, sizeof(*dom)); dom->id = domain_id++; - dom->accesslog = -1; + dom->accesslog = -1; dom->x509_verify_depth = 1; + dom->domain = kore_strdup(domain); #if !defined(KORE_NO_HTTP) @@ -188,8 +122,7 @@ kore_domain_free(struct kore_domain *dom) if (dom->domain != NULL) kore_free(dom->domain); - if (dom->ssl_ctx != NULL) - SSL_CTX_free(dom->ssl_ctx); + kore_tls_domain_cleanup(dom); kore_free(dom->cafile); kore_free(dom->certkey); @@ -214,216 +147,6 @@ kore_domain_free(struct kore_domain *dom) } void -kore_domain_tlsinit(struct kore_domain *dom, int type, - const void *data, size_t datalen) -{ - const u_int8_t *ptr; - RSA *rsa; - X509 *x509; - EVP_PKEY *pkey; - STACK_OF(X509_NAME) *certs; - EC_KEY *eckey; - const SSL_METHOD *method; - - kore_debug("kore_domain_tlsinit(%s)", dom->domain); - - if (dom->ssl_ctx != NULL) - SSL_CTX_free(dom->ssl_ctx); - - if ((method = TLS_method()) == NULL) - fatalx("TLS_method(): %s", ssl_errno_s); - - if ((dom->ssl_ctx = SSL_CTX_new(method)) == NULL) - fatalx("SSL_ctx_new(): %s", ssl_errno_s); - - if (!SSL_CTX_set_min_proto_version(dom->ssl_ctx, TLS1_2_VERSION)) - fatalx("SSL_CTX_set_min_proto_version: %s", ssl_errno_s); - -#if defined(TLS1_3_VERSION) - if (!SSL_CTX_set_max_proto_version(dom->ssl_ctx, TLS1_3_VERSION)) - fatalx("SSL_CTX_set_max_proto_version: %s", ssl_errno_s); -#else - if (!SSL_CTX_set_max_proto_version(dom->ssl_ctx, TLS1_2_VERSION)) - fatalx("SSL_CTX_set_min_proto_version: %s", ssl_errno_s); -#endif - - switch (tls_version) { - case KORE_TLS_VERSION_1_3: -#if defined(TLS1_3_VERSION) - if (!SSL_CTX_set_min_proto_version(dom->ssl_ctx, - TLS1_3_VERSION)) { - fatalx("SSL_CTX_set_min_proto_version: %s", - ssl_errno_s); - } - break; -#endif - case KORE_TLS_VERSION_1_2: - if (!SSL_CTX_set_max_proto_version(dom->ssl_ctx, - TLS1_2_VERSION)) { - fatalx("SSL_CTX_set_min_proto_version: %s", - ssl_errno_s); - } - break; - case KORE_TLS_VERSION_BOTH: - break; - default: - fatalx("unknown tls_version: %d", tls_version); - return; - } - - switch (type) { - case KORE_PEM_CERT_CHAIN: - x509 = domain_load_certificate_chain(dom->ssl_ctx, - data, datalen); - break; - case KORE_DER_CERT_DATA: - ptr = data; - if ((x509 = d2i_X509(NULL, &ptr, datalen)) == NULL) - fatalx("d2i_X509: %s", ssl_errno_s); - if (SSL_CTX_use_certificate(dom->ssl_ctx, x509) == 0) - fatalx("SSL_CTX_use_certificate: %s", ssl_errno_s); - break; - default: - fatalx("%s: unknown type %d", __func__, type); - } - - if (x509 == NULL) { - kore_log(LOG_NOTICE, "failed to load certificate for '%s': %s", - dom->domain, ssl_errno_s); - SSL_CTX_free(dom->ssl_ctx); - dom->ssl_ctx = NULL; - return; - } - - if ((pkey = X509_get_pubkey(x509)) == NULL) - fatalx("certificate has no public key"); - - switch (EVP_PKEY_id(pkey)) { - case EVP_PKEY_RSA: - if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) - fatalx("no RSA public key present"); - RSA_set_app_data(rsa, dom); - RSA_set_method(rsa, keymgr_rsa_meth); - break; - case EVP_PKEY_EC: - if ((eckey = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) - fatalx("no EC public key present"); - EC_KEY_set_ex_data(eckey, 0, dom); - EC_KEY_set_method(eckey, keymgr_ec_meth); - break; - default: - fatalx("unknown public key in certificate"); - } - - if (!SSL_CTX_use_PrivateKey(dom->ssl_ctx, pkey)) - fatalx("SSL_CTX_use_PrivateKey(): %s", ssl_errno_s); - - if (!SSL_CTX_check_private_key(dom->ssl_ctx)) { - fatalx("Public/Private key for %s do not match (%s)", - dom->domain, ssl_errno_s); - } - - if (tls_dhparam == NULL) - fatal("no DH parameters specified"); - - SSL_CTX_set_tmp_dh(dom->ssl_ctx, tls_dhparam); - SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_SINGLE_DH_USE); - - if (!SSL_CTX_set_ecdh_auto(dom->ssl_ctx, 1)) - fatalx("SSL_CTX_set_ecdh_auto: %s", ssl_errno_s); - - SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_SINGLE_ECDH_USE); - SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_COMPRESSION); - - if (dom->cafile != NULL) { - if ((certs = SSL_load_client_CA_file(dom->cafile)) == NULL) { - fatalx("SSL_load_client_CA_file(%s): %s", - dom->cafile, ssl_errno_s); - } - - SSL_CTX_load_verify_locations(dom->ssl_ctx, dom->cafile, NULL); - SSL_CTX_set_verify_depth(dom->ssl_ctx, dom->x509_verify_depth); - SSL_CTX_set_client_CA_list(dom->ssl_ctx, certs); - SSL_CTX_set_verify(dom->ssl_ctx, SSL_VERIFY_PEER | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT, domain_x509_verify); - } - - SSL_CTX_set_session_id_context(dom->ssl_ctx, - (unsigned char *)SSL_SESSION_ID, strlen(SSL_SESSION_ID)); - SSL_CTX_set_mode(dom->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); - - if (tls_version == KORE_TLS_VERSION_BOTH) { - SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_SSLv2); - SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_SSLv3); - SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_TLSv1); - SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_TLSv1_1); - } - - SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - SSL_CTX_set_cipher_list(dom->ssl_ctx, kore_tls_cipher_list); - - 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); - -#if defined(KORE_USE_ACME) - SSL_CTX_set_alpn_select_cb(dom->ssl_ctx, kore_acme_tls_alpn, dom); -#endif - - 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 = BIO_new_mem_buf(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); -} - -void kore_domain_callback(void (*cb)(struct kore_domain *)) { struct kore_server *srv; @@ -495,308 +218,3 @@ kore_domain_closelogs(void) } } } - -void -kore_domain_keymgr_init(void) -{ - keymgr_init(); - kore_msg_register(KORE_MSG_KEYMGR_RESP, keymgr_msg_response); -} - -static void -keymgr_init(void) -{ - const RSA_METHOD *meth; - - if ((meth = RSA_get_default_method()) == NULL) - fatal("failed to obtain RSA method"); - - RSA_meth_set_pub_enc(keymgr_rsa_meth, RSA_meth_get_pub_enc(meth)); - RSA_meth_set_pub_dec(keymgr_rsa_meth, RSA_meth_get_pub_dec(meth)); - RSA_meth_set_bn_mod_exp(keymgr_rsa_meth, RSA_meth_get_bn_mod_exp(meth)); -} - -static int -keymgr_rsa_init(RSA *rsa) -{ - if (rsa != NULL) { - RSA_set_flags(rsa, RSA_flags(rsa) | - RSA_FLAG_EXT_PKEY | RSA_METHOD_FLAG_NO_CHECK); - return (1); - } - - return (0); -} - -static int -keymgr_rsa_privenc(int flen, const unsigned char *from, unsigned char *to, - RSA *rsa, int padding) -{ - int ret; - size_t len; - struct kore_keyreq *req; - struct kore_domain *dom; - - len = sizeof(*req) + flen; - if (len > sizeof(keymgr_buf)) - fatal("keymgr_buf too small"); - - if ((dom = RSA_get_app_data(rsa)) == NULL) - fatal("RSA key has no domain attached"); - - memset(keymgr_buf, 0, sizeof(keymgr_buf)); - - req = (struct kore_keyreq *)keymgr_buf; - - if (kore_strlcpy(req->domain, dom->domain, sizeof(req->domain)) >= - sizeof(req->domain)) - fatal("%s: domain truncated", __func__); - - req->data_len = flen; - req->padding = padding; - memcpy(&req->data[0], from, req->data_len); - - kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_KEYMGR_REQ, keymgr_buf, len); - keymgr_await_data(); - - ret = -1; - if (keymgr_response) { - if (keymgr_buflen < INT_MAX && - (int)keymgr_buflen == RSA_size(rsa)) { - ret = RSA_size(rsa); - memcpy(to, keymgr_buf, RSA_size(rsa)); - } - } - - keymgr_buflen = 0; - keymgr_response = 0; - kore_platform_event_all(worker->msg[1]->fd, worker->msg[1]); - - return (ret); -} - -static int -keymgr_rsa_finish(RSA *rsa) -{ - return (1); -} - -static ECDSA_SIG * -keymgr_ecdsa_sign(const unsigned char *dgst, int dgst_len, - const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *eckey) -{ - size_t len; - ECDSA_SIG *sig; - const u_int8_t *ptr; - struct kore_domain *dom; - struct kore_keyreq *req; - - if (in_kinv != NULL || in_r != NULL) - return (NULL); - - len = sizeof(*req) + dgst_len; - if (len > sizeof(keymgr_buf)) - fatal("keymgr_buf too small"); - - if ((dom = EC_KEY_get_ex_data(eckey, 0)) == NULL) - fatal("EC_KEY has no domain"); - - memset(keymgr_buf, 0, sizeof(keymgr_buf)); - req = (struct kore_keyreq *)keymgr_buf; - - if (kore_strlcpy(req->domain, dom->domain, sizeof(req->domain)) >= - sizeof(req->domain)) - fatal("%s: domain truncated", __func__); - - req->data_len = dgst_len; - memcpy(&req->data[0], dgst, req->data_len); - - kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_KEYMGR_REQ, keymgr_buf, len); - keymgr_await_data(); - - if (keymgr_response) { - ptr = keymgr_buf; - sig = d2i_ECDSA_SIG(NULL, &ptr, keymgr_buflen); - } else { - sig = NULL; - } - - keymgr_buflen = 0; - keymgr_response = 0; - kore_platform_event_all(worker->msg[1]->fd, worker->msg[1]); - - return (sig); -} - -static void -keymgr_await_data(void) -{ - int ret; - struct pollfd pfd[1]; - u_int64_t start, cur; -#if !defined(KORE_NO_HTTP) - int process_requests; -#endif - - /* - * We need to wait until the keymgr responds to us, so keep doing - * net_recv_flush() until our callback for KORE_MSG_KEYMGR_RESP - * tells us that we have obtained the response. - * - * This means other internal messages can still be delivered by - * this worker process to the appropriate callbacks but we do not - * drop out until we've either received an answer from the keymgr - * or until the timeout has been reached (1 second currently). - * - * If we end up waiting for the keymgr process we will call - * http_process (if not built with NOHTTP=1) to further existing - * requests so those do not block too much. - * - * This means that all incoming data will stop being processed - * while existing requests will get processed until we return - * from this call. - */ - start = kore_time_ms(); - kore_platform_disable_read(worker->msg[1]->fd); - - keymgr_response = 0; - memset(keymgr_buf, 0, sizeof(keymgr_buf)); - -#if !defined(KORE_NO_HTTP) - process_requests = 0; -#endif - - for (;;) { -#if !defined(KORE_NO_HTTP) - if (process_requests) { - http_process(); - process_requests = 0; - } -#endif - pfd[0].fd = worker->msg[1]->fd; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - - ret = poll(pfd, 1, 100); - if (ret == -1) { - if (errno == EINTR) - continue; - fatal("poll: %s", errno_s); - } - - cur = kore_time_ms(); - if ((cur - start) > 1000) - break; - - if (ret == 0) { -#if !defined(KORE_NO_HTTP) - /* No activity on channel, process HTTP requests. */ - process_requests = 1; -#endif - continue; - } - - if (pfd[0].revents & (POLLERR | POLLHUP)) - break; - if (!(pfd[0].revents & POLLIN)) - break; - - worker->msg[1]->evt.flags |= KORE_EVENT_READ; - if (!net_recv_flush(worker->msg[1])) - break; - - if (keymgr_response) - break; - -#if !defined(KORE_NO_HTTP) - /* If we've spent 100ms already, process HTTP requests. */ - if ((cur - start) > 100) { - process_requests = 1; - } -#endif - } -} - -static void -keymgr_msg_response(struct kore_msg *msg, const void *data) -{ - keymgr_response = 1; - keymgr_buflen = msg->length; - - if (keymgr_buflen > sizeof(keymgr_buf)) - return; - - memcpy(keymgr_buf, data, keymgr_buflen); -} - -static int -domain_x509_verify(int ok, X509_STORE_CTX *ctx) -{ - X509 *cert; - const char *text; - int error, depth; - - error = X509_STORE_CTX_get_error(ctx); - cert = X509_STORE_CTX_get_current_cert(ctx); - - if (ok == 0 && cert != NULL) { - text = X509_verify_cert_error_string(error); - depth = X509_STORE_CTX_get_error_depth(ctx); - - kore_log(LOG_WARNING, "X509 verification error depth:%d - %s", - depth, text); - - /* Continue on CRL validity errors. */ - switch (error) { - case X509_V_ERR_CRL_HAS_EXPIRED: - case X509_V_ERR_CRL_NOT_YET_VALID: - case X509_V_ERR_UNABLE_TO_GET_CRL: - ok = 1; - break; - } - } - - 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 void *data, size_t len) -{ - unsigned long err; - BIO *in; - X509 *x, *ca; - - ERR_clear_error(); - in = BIO_new_mem_buf(data, len); - - if ((x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL)) == NULL) - return (NULL); - - /* refcount for x509 will go up one. */ - if (SSL_CTX_use_certificate(ctx, x) == 0) - return (NULL); - - 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) - return (NULL); - } - - err = ERR_peek_last_error(); - - if (ERR_GET_LIB(err) != ERR_LIB_PEM || - ERR_GET_REASON(err) != PEM_R_NO_START_LINE) - return (NULL); - - BIO_free(in); - - return (x); -} diff --git a/src/http.c b/src/http.c @@ -976,7 +976,7 @@ http_header_recv(struct netbuf *nb) req->http_body = kore_buf_alloc(req->content_length); } - SHA256_Init(&req->hashctx); + SHA256Init(&req->hashctx); c->http_timeout = http_body_timeout * 1000; if (!http_body_update(req, end_headers, nb->s_off - len)) { @@ -2349,7 +2349,7 @@ http_body_update(struct http_request *req, const void *data, size_t len) ssize_t ret; u_int64_t bytes_left; - SHA256_Update(&req->hashctx, data, len); + SHA256Update(&req->hashctx, data, len); if (req->http_body_fd != -1) { ret = write(req->http_body_fd, data, len); @@ -2382,7 +2382,7 @@ http_body_update(struct http_request *req, const void *data, size_t len) HTTP_STATUS_INTERNAL_ERROR); return (KORE_RESULT_ERROR); } - SHA256_Final(req->http_body_digest, &req->hashctx); + SHA256Final(req->http_body_digest, &req->hashctx); } else { bytes_left = req->content_length; net_recv_reset(req->owner, diff --git a/src/keymgr.c b/src/keymgr.c @@ -1,1373 +0,0 @@ -/* - * Copyright (c) 2017-2022 Joris Vink <joris@coders.se> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - * The kore keymgr process is responsible for managing certificates - * and their matching private keys. - * - * It is the only process in Kore that holds the private keys (the workers - * do not have a copy of them in memory). - * - * When a worker requires the private key for signing it will send a message - * to the keymgr with the to-be-signed data (KORE_MSG_KEYMGR_REQ). The keymgr - * will perform the signing and respond with a KORE_MSG_KEYMGR_RESP message. - * - * The keymgr can transparently reload the private keys and certificates - * for a configured domain when it receives a SIGUSR1. It it reloads them - * it will send the newly loaded certificate chains to the worker processes - * which will update their TLS contexts accordingly. - * - * If ACME is turned on the keymgr will also hold all account and domain - * keys and will initiate the process of acquiring new certificates against - * the ACME provider that is configured if those certificates do not exist - * or are expired (or are expiring soon). - */ - -#include <sys/types.h> -#include <sys/mman.h> -#include <sys/stat.h> - -#include <openssl/evp.h> -#include <openssl/rsa.h> -#include <openssl/rand.h> -#include <openssl/sha.h> -#include <openssl/x509.h> -#include <openssl/x509v3.h> - -#include <ctype.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <signal.h> -#include <unistd.h> - -#include "kore.h" - -#if defined(KORE_USE_ACME) -#include "acme.h" -#endif - -#define RAND_TMP_FILE "rnd.tmp" -#define RAND_POLL_INTERVAL (1800 * 1000) -#define RAND_FILE_SIZE 1024 - -#if defined(__linux__) -#include "seccomp.h" - -/* The syscalls our keymgr is allowed to perform, only. */ -static struct sock_filter filter_keymgr[] = { - /* Deny these, but with EACCESS instead of dying. */ - KORE_SYSCALL_DENY(ioctl, EACCES), - - /* Required to deal with private keys and certs. */ -#if defined(SYS_open) - KORE_SYSCALL_ALLOW(open), -#endif - KORE_SYSCALL_ALLOW(read), - KORE_SYSCALL_ALLOW(lseek), - KORE_SYSCALL_ALLOW(write), - KORE_SYSCALL_ALLOW(close), -#if defined(SYS_stat) - KORE_SYSCALL_ALLOW(stat), -#endif - KORE_SYSCALL_ALLOW(fstat), -#if defined(SYS_fstat64) - KORE_SYSCALL_ALLOW(fstat64), -#endif -#if defined(SYS_newfstatat) - KORE_SYSCALL_ALLOW(newfstatat), -#endif - KORE_SYSCALL_ALLOW(futex), - KORE_SYSCALL_ALLOW(writev), - KORE_SYSCALL_ALLOW(openat), -#if defined(SYS_access) - KORE_SYSCALL_ALLOW(access), -#endif - KORE_SYSCALL_ALLOW(faccessat), - - /* Net related. */ -#if defined(SYS_poll) - KORE_SYSCALL_ALLOW(poll), -#endif -#if defined(SYS_send) - KORE_SYSCALL_ALLOW(send), -#endif - KORE_SYSCALL_ALLOW(sendto), -#if defined(SYS_recv) - KORE_SYSCALL_ALLOW(recv), -#endif - KORE_SYSCALL_ALLOW(recvfrom), -#if defined(SYS_epoll_wait) - KORE_SYSCALL_ALLOW(epoll_wait), -#endif - KORE_SYSCALL_ALLOW(epoll_pwait), - - /* Process things. */ - KORE_SYSCALL_ALLOW(exit), - KORE_SYSCALL_ALLOW(kill), - KORE_SYSCALL_ALLOW(getuid), - KORE_SYSCALL_ALLOW(getpid), -#if defined(SYS_arch_prctl) - KORE_SYSCALL_ALLOW(arch_prctl), -#endif - KORE_SYSCALL_ALLOW(exit_group), - KORE_SYSCALL_ALLOW(sigaltstack), -#if defined(SYS_sigreturn) - KORE_SYSCALL_ALLOW(sigreturn), -#endif - KORE_SYSCALL_ALLOW(rt_sigreturn), - KORE_SYSCALL_ALLOW(rt_sigaction), - KORE_SYSCALL_ALLOW(rt_sigprocmask), - - /* Other things. */ - KORE_SYSCALL_ALLOW(brk), -#if defined(SYS_mmap) - KORE_SYSCALL_ALLOW(mmap), -#endif -#if defined(SYS_mmap2) - KORE_SYSCALL_ALLOW(mmap2), -#endif -#if defined(SYS_madvise) - KORE_SYSCALL_ALLOW(madvise), -#endif - KORE_SYSCALL_ALLOW(munmap), - KORE_SYSCALL_ALLOW(clock_gettime), -#if defined(__NR_getrandom) - KORE_SYSCALL_ALLOW(getrandom), -#endif - -#if defined(KORE_USE_ACME) -#if defined(SYS_mkdir) - KORE_SYSCALL_ALLOW(mkdir), -#endif - KORE_SYSCALL_ALLOW(mkdirat), - KORE_SYSCALL_ALLOW(umask), -#endif -}; -#endif - -struct key { - EVP_PKEY *pkey; - struct kore_domain *dom; - TAILQ_ENTRY(key) list; -}; - -char *rand_file = NULL; - -static TAILQ_HEAD(, key) keys; -static int initialized = 0; - -#if defined(KORE_USE_ACME) - -#define ACME_ORDER_STATE_INIT 1 -#define ACME_ORDER_STATE_SUBMIT 2 - -#define ACME_X509_EXPIRATION 120 -#define ACME_TLS_ALPN_01_OID "1.3.6.1.5.5.7.1.31" - -#define ACME_RENEWAL_THRESHOLD 5 -#define ACME_RENEWAL_TIMER (3600 * 1000) - -/* UTCTIME in format of YYMMDDHHMMSSZ */ -#define ASN1_UTCTIME_LEN 13 - -/* GENERALIZEDTIME in format of YYYYMMDDHHMMSSZ */ -#define ASN1_GENERALIZEDTIME_LEN 15 - -/* Set to 1 when we receive KORE_ACME_PROC_READY. */ -static int acmeproc_ready = 0; - -/* Renewal timer for all domains under acme control. */ -static struct kore_timer *acme_renewal = NULL; - -/* oid for acme extension. */ -static int acme_oid = -1; - -struct acme_order { - int state; - struct kore_timer *timer; - char *domain; -}; - -static char *keymgr_bignum_base64(const BIGNUM *); - -static void keymgr_acme_init(void); -static void keymgr_acme_renewal(void *, u_int64_t); -static void keymgr_acme_check(struct kore_domain *); -static void keymgr_acme_sign(struct kore_msg *, const void *); -static void keymgr_acme_ready(struct kore_msg *, const void *); -static void keymgr_acme_domainkey(struct kore_domain *, struct key *); - -static void keymgr_acme_order_create(const char *); -static void keymgr_acme_order_redo(void *, u_int64_t); -static void keymgr_acme_order_start(void *, u_int64_t); - -static void keymgr_x509_ext(STACK_OF(X509_EXTENSION) *, - int, const char *, ...); - -static void keymgr_acme_csr(const struct kore_keyreq *, struct key *); -static void keymgr_acme_install_cert(const void *, size_t, struct key *); -static void keymgr_acme_order_failed(const void *, size_t, struct key *); -static void keymgr_acme_challenge_cert(const void *, size_t, struct key *); - -static int keymgr_x509_not_after(X509 *, time_t *); -static int keymgr_asn1_convert_utctime(const ASN1_TIME *, time_t *); -static int keymgr_asn1_convert_generalizedtime(const void *, - size_t, time_t *); - -#endif /* KORE_USE_ACME */ - -static void keymgr_reload(void); -static void keymgr_load_randfile(void); -static void keymgr_save_randfile(void); - -static struct key *keymgr_load_privatekey(const char *); -static void keymgr_load_domain_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_submit_file(u_int8_t, struct kore_domain *, - const char *, u_int16_t, int); -static void keymgr_x509_msg(const char *, const void *, size_t, int, int); - -static void keymgr_rsa_encrypt(struct kore_msg *, const void *, - struct key *); -static void keymgr_ecdsa_sign(struct kore_msg *, const void *, - struct key *); - -struct kore_privsep keymgr_privsep; -int keymgr_active = 0; - -#if defined(__OpenBSD__) -#if defined(KORE_USE_ACME) -static const char *keymgr_pledges = "stdio rpath wpath cpath"; -#else -static const char *keymgr_pledges = "stdio rpath"; -#endif -#endif - -void -kore_keymgr_run(void) -{ - int quit; - u_int64_t now, netwait, last_seed; - - if (keymgr_active == 0) - fatalx("%s: called with keymgr_active == 0", __func__); - - quit = 0; - - kore_server_closeall(); - kore_module_cleanup(); - - net_init(); - kore_timer_init(); - kore_connection_init(); - kore_platform_event_init(); - - 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); - -#if defined(__linux__) - /* Drop all enabled seccomp filters, and add only ours. */ - kore_seccomp_drop(); - kore_seccomp_filter("keymgr", filter_keymgr, - KORE_FILTER_LEN(filter_keymgr)); -#endif -#if defined(KORE_USE_PYTHON) - kore_msg_unregister(KORE_PYTHON_SEND_OBJ); -#endif - kore_worker_privsep(); - - if (rand_file != NULL) { - keymgr_load_randfile(); - keymgr_save_randfile(); - } else if (!kore_quiet) { - kore_log(LOG_WARNING, "no rand_file location specified"); - } - - RAND_poll(); - last_seed = 0; - - initialized = 1; - keymgr_reload(); - -#if defined(__OpenBSD__) - if (pledge(keymgr_pledges, NULL) == -1) - fatalx("failed to pledge keymgr process"); -#endif - -#if defined(KORE_USE_ACME) - acme_oid = OBJ_create(ACME_TLS_ALPN_01_OID, "acme", "acmeIdentifier"); - X509V3_EXT_add_alias(acme_oid, NID_subject_key_identifier); -#endif - - kore_worker_started(); - - while (quit != 1) { - now = kore_time_ms(); - if ((now - last_seed) > RAND_POLL_INTERVAL) { - RAND_poll(); - last_seed = now; - } - - netwait = kore_timer_next_run(now); - kore_platform_event_wait(netwait); - - if (sig_recv != 0) { - switch (sig_recv) { - case SIGQUIT: - case SIGINT: - case SIGTERM: - quit = 1; - break; - case SIGUSR1: - keymgr_reload(); - break; - default: - break; - } - sig_recv = 0; - } - - if (quit) - break; - - now = kore_time_ms(); - kore_timer_run(now); - kore_connection_prune(KORE_CONNECTION_PRUNE_DISCONNECT); - } - - kore_keymgr_cleanup(1); - kore_platform_event_cleanup(); - kore_connection_cleanup(); - net_cleanup(); -} - -void -kore_keymgr_cleanup(int final) -{ - struct key *key, *next; - - if (initialized == 0) - return; - - for (key = TAILQ_FIRST(&keys); key != NULL; key = next) { - next = TAILQ_NEXT(key, list); - TAILQ_REMOVE(&keys, key, list); - - EVP_PKEY_free(key->pkey); - kore_free(key); - } -} - -static void -keymgr_reload(void) -{ - struct kore_server *srv; - struct kore_domain *dom; - - if (!kore_quiet) - kore_log(LOG_INFO, "(re)loading certificates, keys and CRLs"); - - kore_keymgr_cleanup(0); - TAILQ_INIT(&keys); - -#if defined(KORE_USE_ACME) - keymgr_acme_init(); -#endif - - kore_domain_callback(keymgr_load_domain_privatekey); - - /* can't use kore_domain_callback() due to dst parameter. */ - LIST_FOREACH(srv, &kore_servers, list) { - if (srv->tls == 0) - continue; - TAILQ_FOREACH(dom, &srv->domains, list) - keymgr_submit_certificates(dom, KORE_MSG_WORKER_ALL); - } -} - -static void -keymgr_submit_certificates(struct kore_domain *dom, u_int16_t dst) -{ - if (access(dom->certfile, R_OK) == -1) { -#if defined(KORE_USE_ACME) - if (dom->acme && errno == ENOENT) - return; -#endif - fatalx("cannot read '%s' for %s: %s", - dom->certfile, dom->domain, errno_s); - } - - keymgr_submit_file(KORE_MSG_CERTIFICATE, dom, dom->certfile, dst, 0); - - if (dom->crlfile != NULL) - keymgr_submit_file(KORE_MSG_CRL, dom, dom->crlfile, dst, 1); -} - -static void -keymgr_submit_file(u_int8_t id, struct kore_domain *dom, - const char *file, u_int16_t dst, int can_fail) -{ - int fd; - struct stat st; - u_int8_t *payload; - - if ((fd = open(file, O_RDONLY)) == -1) { - if (errno == ENOENT && can_fail) - return; - fatalx("open(%s): %s", file, errno_s); - } - - if (fstat(fd, &st) == -1) - fatalx("stat(%s): %s", file, errno_s); - - if (!S_ISREG(st.st_mode)) - fatalx("%s is not a file", file); - - payload = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (payload == MAP_FAILED) - fatalx("mmap(): %s", errno_s); - - keymgr_x509_msg(dom->domain, payload, st.st_size, dst, id); - - (void)munmap(payload, st.st_size); - close(fd); -} - -static void -keymgr_load_randfile(void) -{ - int fd; - struct stat st; - ssize_t ret; - size_t total; - u_int8_t buf[RAND_FILE_SIZE]; - - if (rand_file == NULL) - return; - - if ((fd = open(rand_file, O_RDONLY)) == -1) - fatalx("open(%s): %s", rand_file, errno_s); - - if (fstat(fd, &st) == -1) - fatalx("stat(%s): %s", rand_file, errno_s); - if (!S_ISREG(st.st_mode)) - fatalx("%s is not a file", rand_file); - if (st.st_size != RAND_FILE_SIZE) - fatalx("%s has an invalid size", rand_file); - - total = 0; - - while (total != RAND_FILE_SIZE) { - ret = read(fd, buf, sizeof(buf)); - if (ret == 0) - fatalx("EOF on %s", rand_file); - - if (ret == -1) { - if (errno == EINTR) - continue; - fatalx("read(%s): %s", rand_file, errno_s); - } - - total += (size_t)ret; - RAND_seed(buf, (int)ret); - OPENSSL_cleanse(buf, sizeof(buf)); - } - - (void)close(fd); - if (unlink(rand_file) == -1) { - kore_log(LOG_WARNING, "failed to unlink %s: %s", - rand_file, errno_s); - } -} - -static void -keymgr_save_randfile(void) -{ - int fd; - struct stat st; - ssize_t ret; - u_int8_t buf[RAND_FILE_SIZE]; - - if (rand_file == NULL) - return; - - if (stat(RAND_TMP_FILE, &st) != -1) { - kore_log(LOG_WARNING, "removing stale %s file", RAND_TMP_FILE); - (void)unlink(RAND_TMP_FILE); - } - - if (RAND_bytes(buf, sizeof(buf)) != 1) { - kore_log(LOG_WARNING, "RAND_bytes: %s", ssl_errno_s); - goto cleanup; - } - - if ((fd = open(RAND_TMP_FILE, - O_CREAT | O_TRUNC | O_WRONLY, 0400)) == -1) { - kore_log(LOG_WARNING, - "failed to open %s: %s - random data not written", - RAND_TMP_FILE, errno_s); - goto cleanup; - } - - ret = write(fd, buf, sizeof(buf)); - if (ret == -1 || (size_t)ret != sizeof(buf)) { - kore_log(LOG_WARNING, "failed to write random data"); - (void)close(fd); - (void)unlink(RAND_TMP_FILE); - goto cleanup; - } - - if (close(fd) == -1) - kore_log(LOG_WARNING, "close(%s): %s", RAND_TMP_FILE, errno_s); - - if (rename(RAND_TMP_FILE, rand_file) == -1) { - kore_log(LOG_WARNING, "rename(%s, %s): %s", - RAND_TMP_FILE, rand_file, errno_s); - (void)unlink(rand_file); - (void)unlink(RAND_TMP_FILE); - } - -cleanup: - OPENSSL_cleanse(buf, sizeof(buf)); -} - -static void -keymgr_load_domain_privatekey(struct kore_domain *dom) -{ - struct key *key; - - if (dom->server->tls == 0) - return; - - key = keymgr_load_privatekey(dom->certkey); - - if (key->pkey == NULL) { -#if defined(KORE_USE_ACME) - if (dom->acme) - keymgr_acme_domainkey(dom, key); -#endif - if (key->pkey == NULL) { - fatalx("failed to load private key for '%s' (%s)", - dom->domain, errno_s); - } - } - - key->dom = dom; - - if (!kore_quiet) - kore_log(LOG_INFO, "loaded private key for '%s'", dom->domain); -} - -static struct key * -keymgr_load_privatekey(const char *path) -{ - struct key *key; - - key = kore_calloc(1, sizeof(*key)); - TAILQ_INSERT_TAIL(&keys, key, list); - - /* Caller should check if pkey was loaded. */ - if (path) - key->pkey = kore_rsakey_load(path); - - return (key); -} - -static void -keymgr_certificate_request(struct kore_msg *msg, const void *data) -{ - struct kore_server *srv; - struct kore_domain *dom; - - LIST_FOREACH(srv, &kore_servers, list) { - if (srv->tls == 0) - continue; - TAILQ_FOREACH(dom, &srv->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]; - - if (RAND_bytes(buf, sizeof(buf)) != 1) { - kore_log(LOG_WARNING, - "failed to generate entropy for worker %u: %s", - msg->src, ssl_errno_s); - return; - } - - /* No cleanse, this stuff is leaked in the kernel path anyway. */ - kore_msg_send(msg->src, KORE_MSG_ENTROPY_RESP, buf, sizeof(buf)); -} - -static void -keymgr_msg_recv(struct kore_msg *msg, const void *data) -{ - const struct kore_keyreq *req; - struct key *key; - - if (msg->length < sizeof(*req)) - return; - - req = (const struct kore_keyreq *)data; - - if (msg->length != (sizeof(*req) + req->data_len)) - return; - - if (req->domain[KORE_DOMAINNAME_LEN] != '\0') - return; - - key = NULL; - TAILQ_FOREACH(key, &keys, list) { - if (key->dom == NULL) - continue; - if (!strcmp(key->dom->domain, req->domain)) - break; - } - - if (key == NULL) - return; - - switch (msg->id) { - case KORE_MSG_KEYMGR_REQ: - switch (EVP_PKEY_id(key->pkey)) { - case EVP_PKEY_RSA: - keymgr_rsa_encrypt(msg, data, key); - break; - case EVP_PKEY_EC: - keymgr_ecdsa_sign(msg, data, key); - break; - default: - break; - } - break; -#if defined(KORE_USE_ACME) - case KORE_ACME_CSR_REQUEST: - keymgr_acme_csr(req, key); - break; - case KORE_ACME_ORDER_FAILED: - keymgr_acme_order_failed(req->data, req->data_len, key); - break; - case KORE_ACME_CHALLENGE_CERT: - keymgr_acme_challenge_cert(req->data, req->data_len, key); - break; - case KORE_ACME_INSTALL_CERT: - keymgr_acme_install_cert(req->data, req->data_len, key); - break; -#endif - } -} - -static void -keymgr_rsa_encrypt(struct kore_msg *msg, const void *data, struct key *key) -{ - int ret; - RSA *rsa; - const struct kore_keyreq *req; - size_t keylen; - u_int8_t buf[1024]; - - req = (const struct kore_keyreq *)data; - rsa = EVP_PKEY_get0_RSA(key->pkey); - - keylen = RSA_size(rsa); - if (req->data_len > keylen || keylen > sizeof(buf)) - return; - - ret = RSA_private_encrypt(req->data_len, req->data, - buf, rsa, req->padding); - if (ret != RSA_size(rsa)) - return; - - kore_msg_send(msg->src, KORE_MSG_KEYMGR_RESP, buf, ret); -} - -static void -keymgr_ecdsa_sign(struct kore_msg *msg, const void *data, struct key *key) -{ - size_t len; - EC_KEY *ec; - const struct kore_keyreq *req; - unsigned int siglen; - u_int8_t sig[1024]; - - req = (const struct kore_keyreq *)data; - ec = EVP_PKEY_get0_EC_KEY(key->pkey); - - len = ECDSA_size(ec); - if (req->data_len > len || len > sizeof(sig)) - return; - - if (ECDSA_sign(EVP_PKEY_NONE, req->data, req->data_len, - sig, &siglen, ec) == 0) - return; - - if (siglen > sizeof(sig)) - return; - - kore_msg_send(msg->src, KORE_MSG_KEYMGR_RESP, sig, siglen); -} - -static void -keymgr_x509_msg(const char *domain, const void *data, size_t len, - int target, int msg) -{ - struct kore_buf buf; - struct kore_x509_msg hdr; - - memset(&hdr, 0, sizeof(hdr)); - - hdr.data_len = len; - - if (kore_strlcpy(hdr.domain, domain, sizeof(hdr.domain)) >= - sizeof(hdr.domain)) - fatalx("%s: domain truncated", __func__); - - kore_buf_init(&buf, sizeof(hdr) + len); - kore_buf_append(&buf, &hdr, sizeof(hdr)); - kore_buf_append(&buf, data, len); - - kore_msg_send(target, msg, buf.data, buf.offset); - kore_buf_cleanup(&buf); -} - -#if defined(KORE_USE_ACME) -static void -keymgr_acme_init(void) -{ - RSA *rsa; - struct key *key; - char *e, *n; - int needsreg; - const BIGNUM *be, *bn; - - if (acme_provider == NULL) - return; - - if (mkdir(KORE_ACME_CERTDIR, 0700) == -1) { - if (errno != EEXIST) - fatalx("mkdir(%s): %s", KORE_ACME_CERTDIR, errno_s); - } - - umask(S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH); - - needsreg = 0; - acmeproc_ready = 0; - key = keymgr_load_privatekey(KORE_ACME_ACCOUNT_KEY); - - if (acme_renewal != NULL) - kore_timer_remove(acme_renewal); - - acme_renewal = kore_timer_add(keymgr_acme_renewal, - ACME_RENEWAL_TIMER, NULL, 0); - - if (key->pkey == NULL) { - kore_log(LOG_INFO, "generating new ACME account key"); - key->pkey = kore_rsakey_generate(KORE_ACME_ACCOUNT_KEY); - needsreg = 1; - } else { - kore_log(LOG_INFO, "loaded existing ACME account key"); - } - - rsa = EVP_PKEY_get0_RSA(key->pkey); - RSA_get0_key(rsa, &bn, &be, NULL); - - e = keymgr_bignum_base64(be); - n = keymgr_bignum_base64(bn); - - kore_msg_send(KORE_WORKER_ACME, KORE_ACME_RSAKEY_E, e, strlen(e)); - kore_msg_send(KORE_WORKER_ACME, KORE_ACME_RSAKEY_N, n, strlen(n)); - - kore_free(e); - kore_free(n); - - if (needsreg) { - kore_msg_send(KORE_WORKER_ACME, - KORE_ACME_ACCOUNT_CREATE, NULL, 0); - } else { - kore_msg_send(KORE_WORKER_ACME, - KORE_ACME_ACCOUNT_RESOLVE, NULL, 0); - } - - kore_msg_register(KORE_ACME_SIGN, keymgr_acme_sign); - kore_msg_register(KORE_ACME_CSR_REQUEST, keymgr_msg_recv); - kore_msg_register(KORE_ACME_PROC_READY, keymgr_acme_ready); - kore_msg_register(KORE_ACME_ORDER_FAILED, keymgr_msg_recv); - kore_msg_register(KORE_ACME_INSTALL_CERT, keymgr_msg_recv); - kore_msg_register(KORE_ACME_CHALLENGE_CERT, keymgr_msg_recv); -} - -static void -keymgr_acme_domainkey(struct kore_domain *dom, struct key *key) -{ - char *p; - - kore_log(LOG_INFO, "generated new domain key for %s", dom->domain); - - if ((p = strrchr(dom->certkey, '/')) == NULL) - fatalx("invalid certkey path '%s'", dom->certkey); - - *p = '\0'; - - if (mkdir(dom->certkey, 0700) == -1) { - if (errno != EEXIST) - fatalx("mkdir(%s): %s", dom->certkey, errno_s); - } - - *p = '/'; - key->pkey = kore_rsakey_generate(dom->certkey); -} - -static void -keymgr_acme_order_create(const char *domain) -{ - struct acme_order *order; - - order = kore_calloc(1, sizeof(*order)); - - order->state = ACME_ORDER_STATE_INIT; - order->domain = kore_strdup(domain); - order->timer = kore_timer_add(keymgr_acme_order_start, - 1000, order, KORE_TIMER_ONESHOT); -} - -static void -keymgr_acme_order_redo(void *udata, u_int64_t now) -{ - struct kore_domain *dom = udata; - - kore_log(LOG_INFO, "[%s] redoing order", dom->domain); - keymgr_acme_order_create(dom->domain); -} - -static void -keymgr_acme_order_start(void *udata, u_int64_t now) -{ - struct acme_order *order = udata; - - switch (order->state) { - case ACME_ORDER_STATE_INIT: - if (acmeproc_ready == 0) - break; - order->state = ACME_ORDER_STATE_SUBMIT; - /* fallthrough */ - case ACME_ORDER_STATE_SUBMIT: - kore_msg_send(KORE_WORKER_ACME, KORE_ACME_ORDER_CREATE, - order->domain, strlen(order->domain)); - kore_free(order->domain); - kore_free(order); - order = NULL; - break; - default: - fatalx("%s: unknown order state %d", __func__, order->state); - } - - if (order != NULL) { - order->timer = kore_timer_add(keymgr_acme_order_start, - 5000, order, KORE_TIMER_ONESHOT); - } -} - -static void -keymgr_acme_ready(struct kore_msg *msg, const void *data) -{ - acmeproc_ready = 1; - kore_log(LOG_INFO, "acme process ready to receive orders"); - - keymgr_acme_renewal(NULL, kore_time_ms()); -} - -static void -keymgr_acme_check(struct kore_domain *dom) -{ - FILE *fp; - int days; - X509 *x509; - time_t expires, now; - - if (dom->acme == 0) - return; - - if (access(dom->certfile, R_OK) == -1) { - if (errno == ENOENT) { - keymgr_acme_order_create(dom->domain); - return; - } - kore_log(LOG_ERR, "access(%s): %s", dom->certfile, errno_s); - return; - } - - if ((fp = fopen(dom->certfile, "r")) == NULL) { - kore_log(LOG_ERR, "fopen(%s): %s", dom->certfile, errno_s); - return; - } - - if ((x509 = PEM_read_X509(fp, NULL, NULL, NULL)) == NULL) { - fclose(fp); - kore_log(LOG_ERR, "PEM_read_X509: %s", ssl_errno_s); - return; - } - - fclose(fp); - - if (!keymgr_x509_not_after(x509, &expires)) { - X509_free(x509); - return; - } - - time(&now); - days = (expires - now) / 86400; - - kore_log(LOG_INFO, "%s certificate expires in %d days", - dom->domain, days); - - if (days <= ACME_RENEWAL_THRESHOLD) { - kore_log(LOG_INFO, "%s renewing certificate", dom->domain); - keymgr_acme_order_create(dom->domain); - } - - X509_free(x509); -} - -static void -keymgr_acme_renewal(void *udata, u_int64_t now) -{ - kore_domain_callback(keymgr_acme_check); -} - -static void -keymgr_acme_sign(struct kore_msg *msg, const void *data) -{ - u_int32_t id; - struct kore_buf buf; - const u_int8_t *ptr; - u_int8_t *sig; - EVP_MD_CTX *ctx; - struct key *key; - char *b64; - unsigned int siglen; - - TAILQ_FOREACH(key, &keys, list) { - if (key->dom == NULL) - break; - } - - if (key == NULL) - fatalx("%s: missing key", __func__); - - if (msg->length < sizeof(id)) - fatalx("%s: invalid length (%zu)", __func__, msg->length); - - ptr = data; - memcpy(&id, ptr, sizeof(id)); - - ptr += sizeof(id); - msg->length -= sizeof(id); - - sig = kore_calloc(1, EVP_PKEY_size(key->pkey)); - - if ((ctx = EVP_MD_CTX_create()) == NULL) - fatalx("EVP_MD_CTX_create: %s", ssl_errno_s); - - if (!EVP_SignInit_ex(ctx, EVP_sha256(), NULL)) - fatalx("EVP_SignInit_ex: %s", ssl_errno_s); - - if (!EVP_SignUpdate(ctx, ptr, msg->length)) - fatalx("EVP_SignUpdate: %s", ssl_errno_s); - - if (!EVP_SignFinal(ctx, sig, &siglen, key->pkey)) - fatalx("EVP_SignFinal: %s", ssl_errno_s); - - if (!kore_base64url_encode(sig, siglen, &b64, KORE_BASE64_RAW)) - fatalx("%s: failed to b64url encode signed data", __func__); - - kore_buf_init(&buf, siglen + sizeof(id)); - kore_buf_append(&buf, &id, sizeof(id)); - kore_buf_append(&buf, b64, strlen(b64)); - - kore_msg_send(KORE_WORKER_ACME, - KORE_ACME_SIGN_RESULT, buf.data, buf.offset); - - EVP_MD_CTX_destroy(ctx); - - kore_free(sig); - kore_free(b64); - kore_buf_cleanup(&buf); -} - -static void -keymgr_acme_install_cert(const void *data, size_t len, struct key *key) -{ - int fd; - ssize_t ret; - - fd = open(key->dom->certfile, O_CREAT | O_TRUNC | O_WRONLY, 0700); - if (fd == -1) - fatalx("open(%s): %s", key->dom->certfile, errno_s); - - kore_log(LOG_INFO, "writing %zu bytes of data", len); - - for (;;) { - ret = write(fd, data, len); - if (ret == -1) { - if (errno == EINTR) - continue; - fatalx("write(%s): %s", key->dom->certfile, errno_s); - } - - break; - } - - if ((size_t)ret != len) { - fatalx("incorrect write on %s (%zd/%zu)", - key->dom->certfile, ret, len); - } - - if (close(fd) == -1) { - kore_log(LOG_NOTICE, - "close error on '%s' (%s)", key->dom->certfile, errno_s); - } - - keymgr_submit_certificates(key->dom, KORE_MSG_WORKER_ALL); - - keymgr_x509_msg(key->dom->domain, NULL, 0, - KORE_MSG_WORKER_ALL, KORE_ACME_CHALLENGE_CLEAR_CERT); -} - -static void -keymgr_acme_order_failed(const void *data, size_t len, struct key *key) -{ - u_int32_t retry; - - if (len != sizeof(retry)) { - kore_log(LOG_ERR, "%s: invalid payload (%zu)", __func__, len); - return; - } - - memcpy(&retry, data, len); - - kore_timer_add(keymgr_acme_order_redo, retry, key->dom, - KORE_TIMER_ONESHOT); -} - -static void -keymgr_acme_challenge_cert(const void *data, size_t len, struct key *key) -{ - STACK_OF(X509_EXTENSION) *sk; - size_t idx; - time_t now; - X509_EXTENSION *ext; - X509_NAME *name; - X509 *x509; - const u_int8_t *digest; - int slen, i; - u_int8_t *cert, *uptr; - char hex[(SHA256_DIGEST_LENGTH * 2) + 1]; - - kore_log(LOG_INFO, "[%s] generating tls-alpn-01 challenge cert", - key->dom->domain); - - if (len != SHA256_DIGEST_LENGTH) - fatalx("invalid digest length of %zu bytes", len); - - digest = data; - - for (idx = 0; idx < SHA256_DIGEST_LENGTH; idx++) { - slen = snprintf(hex + (idx * 2), sizeof(hex) - (idx * 2), - "%02x", digest[idx]); - if (slen == -1 || (size_t)slen >= sizeof(hex)) - fatalx("failed to convert digest to hex"); - } - - if ((x509 = X509_new()) == NULL) - fatalx("X509_new(): %s", ssl_errno_s); - - if (!X509_set_version(x509, 2)) - fatalx("X509_set_version(): %s", ssl_errno_s); - - time(&now); - if (!ASN1_INTEGER_set(X509_get_serialNumber(x509), now)) - fatalx("ASN1_INTEGER_set(): %s", ssl_errno_s); - - if (!X509_gmtime_adj(X509_get_notBefore(x509), 0)) - fatalx("X509_gmtime_adj(): %s", ssl_errno_s); - - if (!X509_gmtime_adj(X509_get_notAfter(x509), ACME_X509_EXPIRATION)) - fatalx("X509_gmtime_adj(): %s", ssl_errno_s); - - if (!X509_set_pubkey(x509, key->pkey)) - fatalx("X509_set_pubkey(): %s", ssl_errno_s); - - if ((name = X509_get_subject_name(x509)) == NULL) - fatalx("X509_get_subject_name(): %s", ssl_errno_s); - - if (!X509_NAME_add_entry_by_txt(name, "CN", - MBSTRING_ASC, (const unsigned char *)key->dom->domain, -1, -1, 0)) - fatalx("X509_NAME_add_entry_by_txt(): CN %s", ssl_errno_s); - - if (!X509_set_issuer_name(x509, name)) - fatalx("X509_set_issuer_name(): %s", ssl_errno_s); - - sk = sk_X509_EXTENSION_new_null(); - keymgr_x509_ext(sk, acme_oid, "critical,%s", hex); - keymgr_x509_ext(sk, NID_subject_alt_name, "DNS:%s", key->dom->domain); - - for (i = 0; i < sk_X509_EXTENSION_num(sk); i++) { - ext = sk_X509_EXTENSION_value(sk, i); - if (!X509_add_ext(x509, ext, 0)) - fatalx("X509_add_ext(): %s", ssl_errno_s); - } - - if (!X509_sign(x509, key->pkey, EVP_sha256())) - fatalx("X509_sign(): %s", ssl_errno_s); - - if ((slen = i2d_X509(x509, NULL)) <= 0) - fatalx("i2d_X509: %s", ssl_errno_s); - - cert = kore_calloc(1, slen); - uptr = cert; - - if (i2d_X509(x509, &uptr) <= 0) - fatalx("i2d_X509: %s", ssl_errno_s); - - keymgr_x509_msg(key->dom->domain, cert, slen, - KORE_MSG_WORKER_ALL, KORE_ACME_CHALLENGE_SET_CERT); - - kore_free(cert); - X509_free(x509); - sk_X509_EXTENSION_pop_free(sk, X509_EXTENSION_free); -} - -static void -keymgr_acme_csr(const struct kore_keyreq *req, struct key *key) -{ - int len; - STACK_OF(X509_EXTENSION) *sk; - X509_REQ *csr; - X509_NAME *name; - u_int8_t *data, *uptr; - - kore_log(LOG_INFO, "[%s] creating CSR", req->domain); - - if ((csr = X509_REQ_new()) == NULL) - fatalx("X509_REQ_new: %s", ssl_errno_s); - - if (!X509_REQ_set_version(csr, 3)) - fatalx("X509_REQ_set_version(): %s", ssl_errno_s); - - if (!X509_REQ_set_pubkey(csr, key->pkey)) - fatalx("X509_REQ_set_pubkey(): %s", ssl_errno_s); - - if ((name = X509_REQ_get_subject_name(csr)) == NULL) - fatalx("X509_REQ_get_subject_name(): %s", ssl_errno_s); - - if (!X509_NAME_add_entry_by_txt(name, "CN", - MBSTRING_ASC, (const unsigned char *)key->dom->domain, -1, -1, 0)) - fatalx("X509_NAME_add_entry_by_txt(): %s", ssl_errno_s); - - sk = sk_X509_EXTENSION_new_null(); - keymgr_x509_ext(sk, NID_subject_alt_name, "DNS:%s", key->dom->domain); - - if (!X509_REQ_add_extensions(csr, sk)) - fatalx("X509_REQ_add_extensions(): %s", ssl_errno_s); - - if (!X509_REQ_sign(csr, key->pkey, EVP_sha256())) - fatalx("X509_REQ_sign(): %s", ssl_errno_s); - - if ((len = i2d_X509_REQ(csr, NULL)) <= 0) - fatalx("i2d_X509_REQ: %s", ssl_errno_s); - - data = kore_calloc(1, len); - uptr = data; - - if (i2d_X509_REQ(csr, &uptr) <= 0) - fatalx("i2d_X509_REQ: %s", ssl_errno_s); - - keymgr_x509_msg(key->dom->domain, data, len, - KORE_WORKER_ACME, KORE_ACME_CSR_RESPONSE); - - kore_free(data); - X509_REQ_free(csr); - - sk_X509_EXTENSION_pop_free(sk, X509_EXTENSION_free); -} - -static void -keymgr_x509_ext(STACK_OF(X509_EXTENSION) *sk, int extnid, const char *fmt, ...) -{ - int len; - va_list args; - X509_EXTENSION *ext; - char buf[1024]; - - va_start(args, fmt); - len = vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - - if (len == -1 || (size_t)len >= sizeof(buf)) - fatalx("failed to create buffer for extension %d", extnid); - - if ((ext = X509V3_EXT_conf_nid(NULL, NULL, extnid, buf)) == NULL) { - fatalx("X509V3_EXT_conf_nid(%d, %s): %s", - extnid, buf, ssl_errno_s); - } - - sk_X509_EXTENSION_push(sk, ext); -} - -static char * -keymgr_bignum_base64(const BIGNUM *bn) -{ - int len; - void *buf; - char *encoded; - - len = BN_num_bytes(bn); - buf = kore_calloc(1, len); - - if (BN_bn2bin(bn, buf) != len) - fatalx("BN_bn2bin: %s", ssl_errno_s); - - if (!kore_base64url_encode(buf, len, &encoded, KORE_BASE64_RAW)) - fatalx("failed to base64 encode BIGNUM"); - - return (encoded); -} - -static int -keymgr_x509_not_after(X509 *x509, time_t *out) -{ - const ASN1_TIME *na; - int ret; - - ret = KORE_RESULT_ERROR; - - if ((na = X509_get_notAfter(x509)) == NULL) { - kore_log(LOG_ERR, "no notAfter date in x509"); - return (KORE_RESULT_ERROR); - } - - switch (na->type) { - case V_ASN1_UTCTIME: - ret = keymgr_asn1_convert_utctime(na, out); - break; - case V_ASN1_GENERALIZEDTIME: - ret = keymgr_asn1_convert_generalizedtime(na->data, - na->length, out); - break; - default: - kore_log(LOG_ERR, "invalid notAfter type (%d)", na->type); - break; - } - - return (ret); -} - -static int -keymgr_asn1_convert_utctime(const ASN1_TIME *na, time_t *out) -{ - int len, year; - char buf[ASN1_GENERALIZEDTIME_LEN + 1]; - - if (na->length != ASN1_UTCTIME_LEN) { - kore_log(LOG_ERR, "invalid UTCTIME: too short (%d)", - na->length); - return (KORE_RESULT_ERROR); - } - - if (!isdigit(na->data[0]) || !isdigit(na->data[1])) { - kore_log(LOG_ERR, "invalid UTCTIME: YY are not digits"); - return (KORE_RESULT_ERROR); - } - - year = (na->data[0] - '0') * 10 + (na->data[1] - '0'); - - /* RFC 5280 says years >= 50 are interpreted as 19YY */ - if (year >= 50) - year = 1900 + year; - else - year = 2000 + year; - - /* Convert it to GENERALIZEDTIME format and call that parser. */ - len = snprintf(buf, sizeof(buf), "%04d%.*s", year, - na->length - 2, (const char *)na->data+ 2); - if (len == -1 || (size_t)len >= sizeof(buf)) { - kore_log(LOG_ERR, "invalid UTCTIME: failed to convert"); - return (KORE_RESULT_ERROR); - } - - return (keymgr_asn1_convert_generalizedtime(buf, len, out)); -} - -static int -keymgr_asn1_convert_generalizedtime(const void *ptr, size_t len, time_t *out) -{ - size_t i; - struct tm tm; - const u_int8_t *buf; - - if (len != ASN1_GENERALIZEDTIME_LEN) { - kore_log(LOG_ERR, "invalid GENERALIZEDTIME: too short (%zu)", - len); - return (KORE_RESULT_ERROR); - } - - buf = ptr; - - for (i = 0; i < len - 1; i++) { - if (!isdigit(buf[i])) { - kore_log(LOG_ERR, - "invalid GENERALIZEDTIME: invalid bytes"); - return (KORE_RESULT_ERROR); - } - } - - /* RFC 5280 states that Zulu time must be used (Z). */ - if (buf[i] != 'Z') { - kore_log(LOG_ERR, "invalid GENERALIZEDTIME: not Zulu time"); - return (KORE_RESULT_ERROR); - } - - memset(&tm, 0, sizeof(tm)); - - tm.tm_year = (buf[0] - '0') * 1000 + (buf[1] - '0') * 100 + - (buf[2] - '0') * 10 + (buf[3] - '0'); - - tm.tm_mon = (buf[4] - '0') * 10 + (buf[5] - '0'); - tm.tm_mday = (buf[6] - '0') * 10 + (buf[7] - '0'); - tm.tm_hour = (buf[8] - '0') * 10 + (buf[9] - '0'); - tm.tm_min = (buf[10] - '0') * 10 + (buf[11] - '0'); - tm.tm_sec = (buf[12] - '0') * 10 + (buf[13] - '0'); - - tm.tm_mon = tm.tm_mon - 1; - tm.tm_year = tm.tm_year - 1900; - - *out = mktime(&tm); - - return (KORE_RESULT_OK); -} -#endif diff --git a/src/keymgr_openssl.c b/src/keymgr_openssl.c @@ -0,0 +1,1372 @@ +/* + * Copyright (c) 2017-2022 Joris Vink <joris@coders.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * The kore keymgr process is responsible for managing certificates + * and their matching private keys. + * + * It is the only process in Kore that holds the private keys (the workers + * do not have a copy of them in memory). + * + * When a worker requires the private key for signing it will send a message + * to the keymgr with the to-be-signed data (KORE_MSG_KEYMGR_REQ). The keymgr + * will perform the signing and respond with a KORE_MSG_KEYMGR_RESP message. + * + * The keymgr can transparently reload the private keys and certificates + * for a configured domain when it receives a SIGUSR1. It it reloads them + * it will send the newly loaded certificate chains to the worker processes + * which will update their TLS contexts accordingly. + * + * If ACME is turned on the keymgr will also hold all account and domain + * keys and will initiate the process of acquiring new certificates against + * the ACME provider that is configured if those certificates do not exist + * or are expired (or are expiring soon). + */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/rsa.h> +#include <openssl/rand.h> +#include <openssl/pem.h> +#include <openssl/sha.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> + +#include <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <signal.h> +#include <unistd.h> + +#include "kore.h" + +#if defined(KORE_USE_ACME) +#include "acme.h" +#endif + +#define RAND_TMP_FILE "rnd.tmp" +#define RAND_POLL_INTERVAL (1800 * 1000) +#define RAND_FILE_SIZE 1024 + +#if defined(__linux__) +#include "seccomp.h" + +/* The syscalls our keymgr is allowed to perform, only. */ +static struct sock_filter filter_keymgr[] = { + /* Deny these, but with EACCESS instead of dying. */ + KORE_SYSCALL_DENY(ioctl, EACCES), + + /* Required to deal with private keys and certs. */ +#if defined(SYS_open) + KORE_SYSCALL_ALLOW(open), +#endif + KORE_SYSCALL_ALLOW(read), + KORE_SYSCALL_ALLOW(lseek), + KORE_SYSCALL_ALLOW(write), + KORE_SYSCALL_ALLOW(close), +#if defined(SYS_stat) + KORE_SYSCALL_ALLOW(stat), +#endif + KORE_SYSCALL_ALLOW(fstat), +#if defined(SYS_fstat64) + KORE_SYSCALL_ALLOW(fstat64), +#endif +#if defined(SYS_newfstatat) + KORE_SYSCALL_ALLOW(newfstatat), +#endif + KORE_SYSCALL_ALLOW(futex), + KORE_SYSCALL_ALLOW(writev), + KORE_SYSCALL_ALLOW(openat), +#if defined(SYS_access) + KORE_SYSCALL_ALLOW(access), +#endif + KORE_SYSCALL_ALLOW(faccessat), + + /* Net related. */ +#if defined(SYS_poll) + KORE_SYSCALL_ALLOW(poll), +#endif +#if defined(SYS_send) + KORE_SYSCALL_ALLOW(send), +#endif + KORE_SYSCALL_ALLOW(sendto), +#if defined(SYS_recv) + KORE_SYSCALL_ALLOW(recv), +#endif + KORE_SYSCALL_ALLOW(recvfrom), +#if defined(SYS_epoll_wait) + KORE_SYSCALL_ALLOW(epoll_wait), +#endif + KORE_SYSCALL_ALLOW(epoll_pwait), + + /* Process things. */ + KORE_SYSCALL_ALLOW(exit), + KORE_SYSCALL_ALLOW(kill), + KORE_SYSCALL_ALLOW(getuid), + KORE_SYSCALL_ALLOW(getpid), +#if defined(SYS_arch_prctl) + KORE_SYSCALL_ALLOW(arch_prctl), +#endif + KORE_SYSCALL_ALLOW(exit_group), + KORE_SYSCALL_ALLOW(sigaltstack), +#if defined(SYS_sigreturn) + KORE_SYSCALL_ALLOW(sigreturn), +#endif + KORE_SYSCALL_ALLOW(rt_sigreturn), + KORE_SYSCALL_ALLOW(rt_sigaction), + KORE_SYSCALL_ALLOW(rt_sigprocmask), + + /* Other things. */ + KORE_SYSCALL_ALLOW(brk), +#if defined(SYS_mmap) + KORE_SYSCALL_ALLOW(mmap), +#endif +#if defined(SYS_mmap2) + KORE_SYSCALL_ALLOW(mmap2), +#endif +#if defined(SYS_madvise) + KORE_SYSCALL_ALLOW(madvise), +#endif + KORE_SYSCALL_ALLOW(munmap), + KORE_SYSCALL_ALLOW(clock_gettime), +#if defined(__NR_getrandom) + KORE_SYSCALL_ALLOW(getrandom), +#endif + +#if defined(KORE_USE_ACME) +#if defined(SYS_mkdir) + KORE_SYSCALL_ALLOW(mkdir), +#endif + KORE_SYSCALL_ALLOW(mkdirat), + KORE_SYSCALL_ALLOW(umask), +#endif +}; +#endif + +struct key { + EVP_PKEY *pkey; + struct kore_domain *dom; + TAILQ_ENTRY(key) list; +}; + +char *kore_rand_file = NULL; + +static TAILQ_HEAD(, key) keys; +static int initialized = 0; + +#if defined(KORE_USE_ACME) + +#define ACME_ORDER_STATE_INIT 1 +#define ACME_ORDER_STATE_SUBMIT 2 + +#define ACME_X509_EXPIRATION 120 +#define ACME_TLS_ALPN_01_OID "1.3.6.1.5.5.7.1.31" + +#define ACME_RENEWAL_THRESHOLD 5 +#define ACME_RENEWAL_TIMER (3600 * 1000) + +/* UTCTIME in format of YYMMDDHHMMSSZ */ +#define ASN1_UTCTIME_LEN 13 + +/* GENERALIZEDTIME in format of YYYYMMDDHHMMSSZ */ +#define ASN1_GENERALIZEDTIME_LEN 15 + +/* Set to 1 when we receive KORE_ACME_PROC_READY. */ +static int acmeproc_ready = 0; + +/* Renewal timer for all domains under acme control. */ +static struct kore_timer *acme_renewal = NULL; + +/* oid for acme extension. */ +static int acme_oid = -1; + +struct acme_order { + int state; + struct kore_timer *timer; + char *domain; +}; + +static char *keymgr_bignum_base64(const BIGNUM *); + +static void keymgr_acme_init(void); +static void keymgr_acme_renewal(void *, u_int64_t); +static void keymgr_acme_check(struct kore_domain *); +static void keymgr_acme_sign(struct kore_msg *, const void *); +static void keymgr_acme_ready(struct kore_msg *, const void *); +static void keymgr_acme_domainkey(struct kore_domain *, struct key *); + +static void keymgr_acme_order_create(const char *); +static void keymgr_acme_order_redo(void *, u_int64_t); +static void keymgr_acme_order_start(void *, u_int64_t); + +static void keymgr_x509_ext(STACK_OF(X509_EXTENSION) *, + int, const char *, ...); + +static void keymgr_acme_csr(const struct kore_keyreq *, struct key *); +static void keymgr_acme_install_cert(const void *, size_t, struct key *); +static void keymgr_acme_order_failed(const void *, size_t, struct key *); +static void keymgr_acme_challenge_cert(const void *, size_t, struct key *); + +static int keymgr_x509_not_after(X509 *, time_t *); +static int keymgr_asn1_convert_utctime(const ASN1_TIME *, time_t *); +static int keymgr_asn1_convert_generalizedtime(const void *, + size_t, time_t *); + +#endif /* KORE_USE_ACME */ + +static void keymgr_reload(void); +static void keymgr_load_randfile(void); +static void keymgr_save_randfile(void); + +static struct key *keymgr_load_privatekey(const char *); +static void keymgr_load_domain_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_submit_file(u_int8_t, struct kore_domain *, + const char *, u_int16_t, int); +static void keymgr_x509_msg(const char *, const void *, size_t, int, int); + +static void keymgr_rsa_encrypt(struct kore_msg *, const void *, + struct key *); +static void keymgr_ecdsa_sign(struct kore_msg *, const void *, + struct key *); + +#if defined(__OpenBSD__) +#if defined(KORE_USE_ACME) +static const char *keymgr_pledges = "stdio rpath wpath cpath"; +#else +static const char *keymgr_pledges = "stdio rpath"; +#endif +#endif + +void +kore_keymgr_run(void) +{ + int quit; + u_int64_t now, netwait, last_seed; + + if (kore_keymgr_active == 0) + fatalx("%s: called with kore_keymgr_active == 0", __func__); + + quit = 0; + + kore_server_closeall(); + kore_module_cleanup(); + + net_init(); + kore_timer_init(); + kore_connection_init(); + kore_platform_event_init(); + + 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); + +#if defined(__linux__) + /* Drop all enabled seccomp filters, and add only ours. */ + kore_seccomp_drop(); + kore_seccomp_filter("keymgr", filter_keymgr, + KORE_FILTER_LEN(filter_keymgr)); +#endif +#if defined(KORE_USE_PYTHON) + kore_msg_unregister(KORE_PYTHON_SEND_OBJ); +#endif + kore_worker_privsep(); + + if (kore_rand_file != NULL) { + keymgr_load_randfile(); + keymgr_save_randfile(); + } else if (!kore_quiet) { + kore_log(LOG_WARNING, "no rand_file location specified"); + } + + RAND_poll(); + last_seed = 0; + + initialized = 1; + keymgr_reload(); + +#if defined(__OpenBSD__) + if (pledge(keymgr_pledges, NULL) == -1) + fatalx("failed to pledge keymgr process"); +#endif + +#if defined(KORE_USE_ACME) + acme_oid = OBJ_create(ACME_TLS_ALPN_01_OID, "acme", "acmeIdentifier"); + X509V3_EXT_add_alias(acme_oid, NID_subject_key_identifier); +#endif + + kore_worker_started(); + + while (quit != 1) { + now = kore_time_ms(); + if ((now - last_seed) > RAND_POLL_INTERVAL) { + RAND_poll(); + last_seed = now; + } + + netwait = kore_timer_next_run(now); + kore_platform_event_wait(netwait); + + if (sig_recv != 0) { + switch (sig_recv) { + case SIGQUIT: + case SIGINT: + case SIGTERM: + quit = 1; + break; + case SIGUSR1: + keymgr_reload(); + break; + default: + break; + } + sig_recv = 0; + } + + if (quit) + break; + + now = kore_time_ms(); + kore_timer_run(now); + kore_connection_prune(KORE_CONNECTION_PRUNE_DISCONNECT); + } + + kore_keymgr_cleanup(1); + kore_platform_event_cleanup(); + kore_connection_cleanup(); + net_cleanup(); +} + +void +kore_keymgr_cleanup(int final) +{ + struct key *key, *next; + + if (initialized == 0) + return; + + for (key = TAILQ_FIRST(&keys); key != NULL; key = next) { + next = TAILQ_NEXT(key, list); + TAILQ_REMOVE(&keys, key, list); + + EVP_PKEY_free(key->pkey); + kore_free(key); + } +} + +static void +keymgr_reload(void) +{ + struct kore_server *srv; + struct kore_domain *dom; + + if (!kore_quiet) + kore_log(LOG_INFO, "(re)loading certificates, keys and CRLs"); + + kore_keymgr_cleanup(0); + TAILQ_INIT(&keys); + +#if defined(KORE_USE_ACME) + keymgr_acme_init(); +#endif + + kore_domain_callback(keymgr_load_domain_privatekey); + + /* can't use kore_domain_callback() due to dst parameter. */ + LIST_FOREACH(srv, &kore_servers, list) { + if (srv->tls == 0) + continue; + TAILQ_FOREACH(dom, &srv->domains, list) + keymgr_submit_certificates(dom, KORE_MSG_WORKER_ALL); + } +} + +static void +keymgr_submit_certificates(struct kore_domain *dom, u_int16_t dst) +{ + if (access(dom->certfile, R_OK) == -1) { +#if defined(KORE_USE_ACME) + if (dom->acme && errno == ENOENT) + return; +#endif + fatalx("cannot read '%s' for %s: %s", + dom->certfile, dom->domain, errno_s); + } + + keymgr_submit_file(KORE_MSG_CERTIFICATE, dom, dom->certfile, dst, 0); + + if (dom->crlfile != NULL) + keymgr_submit_file(KORE_MSG_CRL, dom, dom->crlfile, dst, 1); +} + +static void +keymgr_submit_file(u_int8_t id, struct kore_domain *dom, + const char *file, u_int16_t dst, int can_fail) +{ + int fd; + struct stat st; + u_int8_t *payload; + + if ((fd = open(file, O_RDONLY)) == -1) { + if (errno == ENOENT && can_fail) + return; + fatalx("open(%s): %s", file, errno_s); + } + + if (fstat(fd, &st) == -1) + fatalx("stat(%s): %s", file, errno_s); + + if (!S_ISREG(st.st_mode)) + fatalx("%s is not a file", file); + + payload = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (payload == MAP_FAILED) + fatalx("mmap(): %s", errno_s); + + keymgr_x509_msg(dom->domain, payload, st.st_size, dst, id); + + (void)munmap(payload, st.st_size); + close(fd); +} + +static void +keymgr_load_randfile(void) +{ + int fd; + struct stat st; + ssize_t ret; + size_t total; + u_int8_t buf[RAND_FILE_SIZE]; + + if (kore_rand_file == NULL) + return; + + if ((fd = open(kore_rand_file, O_RDONLY)) == -1) + fatalx("open(%s): %s", kore_rand_file, errno_s); + + if (fstat(fd, &st) == -1) + fatalx("stat(%s): %s", kore_rand_file, errno_s); + if (!S_ISREG(st.st_mode)) + fatalx("%s is not a file", kore_rand_file); + if (st.st_size != RAND_FILE_SIZE) + fatalx("%s has an invalid size", kore_rand_file); + + total = 0; + + while (total != RAND_FILE_SIZE) { + ret = read(fd, buf, sizeof(buf)); + if (ret == 0) + fatalx("EOF on %s", kore_rand_file); + + if (ret == -1) { + if (errno == EINTR) + continue; + fatalx("read(%s): %s", kore_rand_file, errno_s); + } + + total += (size_t)ret; + RAND_seed(buf, (int)ret); + OPENSSL_cleanse(buf, sizeof(buf)); + } + + (void)close(fd); + if (unlink(kore_rand_file) == -1) { + kore_log(LOG_WARNING, "failed to unlink %s: %s", + kore_rand_file, errno_s); + } +} + +static void +keymgr_save_randfile(void) +{ + int fd; + struct stat st; + ssize_t ret; + u_int8_t buf[RAND_FILE_SIZE]; + + if (kore_rand_file == NULL) + return; + + if (stat(RAND_TMP_FILE, &st) != -1) { + kore_log(LOG_WARNING, "removing stale %s file", RAND_TMP_FILE); + (void)unlink(RAND_TMP_FILE); + } + + if (RAND_bytes(buf, sizeof(buf)) != 1) { + kore_log(LOG_WARNING, "RAND_bytes: %s", ssl_errno_s); + goto cleanup; + } + + if ((fd = open(RAND_TMP_FILE, + O_CREAT | O_TRUNC | O_WRONLY, 0400)) == -1) { + kore_log(LOG_WARNING, + "failed to open %s: %s - random data not written", + RAND_TMP_FILE, errno_s); + goto cleanup; + } + + ret = write(fd, buf, sizeof(buf)); + if (ret == -1 || (size_t)ret != sizeof(buf)) { + kore_log(LOG_WARNING, "failed to write random data"); + (void)close(fd); + (void)unlink(RAND_TMP_FILE); + goto cleanup; + } + + if (close(fd) == -1) + kore_log(LOG_WARNING, "close(%s): %s", RAND_TMP_FILE, errno_s); + + if (rename(RAND_TMP_FILE, kore_rand_file) == -1) { + kore_log(LOG_WARNING, "rename(%s, %s): %s", + RAND_TMP_FILE, kore_rand_file, errno_s); + (void)unlink(kore_rand_file); + (void)unlink(RAND_TMP_FILE); + } + +cleanup: + OPENSSL_cleanse(buf, sizeof(buf)); +} + +static void +keymgr_load_domain_privatekey(struct kore_domain *dom) +{ + struct key *key; + + if (dom->server->tls == 0) + return; + + key = keymgr_load_privatekey(dom->certkey); + + if (key->pkey == NULL) { +#if defined(KORE_USE_ACME) + if (dom->acme) + keymgr_acme_domainkey(dom, key); +#endif + if (key->pkey == NULL) { + fatalx("failed to load private key for '%s' (%s)", + dom->domain, errno_s); + } + } + + key->dom = dom; + + if (!kore_quiet) + kore_log(LOG_INFO, "loaded private key for '%s'", dom->domain); +} + +static struct key * +keymgr_load_privatekey(const char *path) +{ + struct key *key; + + key = kore_calloc(1, sizeof(*key)); + TAILQ_INSERT_TAIL(&keys, key, list); + + /* Caller should check if pkey was loaded. */ + if (path) + key->pkey = kore_tls_rsakey_load(path); + + return (key); +} + +static void +keymgr_certificate_request(struct kore_msg *msg, const void *data) +{ + struct kore_server *srv; + struct kore_domain *dom; + + LIST_FOREACH(srv, &kore_servers, list) { + if (srv->tls == 0) + continue; + TAILQ_FOREACH(dom, &srv->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]; + + if (RAND_bytes(buf, sizeof(buf)) != 1) { + kore_log(LOG_WARNING, + "failed to generate entropy for worker %u: %s", + msg->src, ssl_errno_s); + return; + } + + /* No cleanse, this stuff is leaked in the kernel path anyway. */ + kore_msg_send(msg->src, KORE_MSG_ENTROPY_RESP, buf, sizeof(buf)); +} + +static void +keymgr_msg_recv(struct kore_msg *msg, const void *data) +{ + const struct kore_keyreq *req; + struct key *key; + + if (msg->length < sizeof(*req)) + return; + + req = (const struct kore_keyreq *)data; + + if (msg->length != (sizeof(*req) + req->data_len)) + return; + + if (req->domain[KORE_DOMAINNAME_LEN] != '\0') + return; + + key = NULL; + TAILQ_FOREACH(key, &keys, list) { + if (key->dom == NULL) + continue; + if (!strcmp(key->dom->domain, req->domain)) + break; + } + + if (key == NULL) + return; + + switch (msg->id) { + case KORE_MSG_KEYMGR_REQ: + switch (EVP_PKEY_id(key->pkey)) { + case EVP_PKEY_RSA: + keymgr_rsa_encrypt(msg, data, key); + break; + case EVP_PKEY_EC: + keymgr_ecdsa_sign(msg, data, key); + break; + default: + break; + } + break; +#if defined(KORE_USE_ACME) + case KORE_ACME_CSR_REQUEST: + keymgr_acme_csr(req, key); + break; + case KORE_ACME_ORDER_FAILED: + keymgr_acme_order_failed(req->data, req->data_len, key); + break; + case KORE_ACME_CHALLENGE_CERT: + keymgr_acme_challenge_cert(req->data, req->data_len, key); + break; + case KORE_ACME_INSTALL_CERT: + keymgr_acme_install_cert(req->data, req->data_len, key); + break; +#endif + } +} + +static void +keymgr_rsa_encrypt(struct kore_msg *msg, const void *data, struct key *key) +{ + int ret; + RSA *rsa; + const struct kore_keyreq *req; + size_t keylen; + u_int8_t buf[1024]; + + req = (const struct kore_keyreq *)data; + rsa = EVP_PKEY_get0_RSA(key->pkey); + + keylen = RSA_size(rsa); + if (req->data_len > keylen || keylen > sizeof(buf)) + return; + + ret = RSA_private_encrypt(req->data_len, req->data, + buf, rsa, req->padding); + if (ret != RSA_size(rsa)) + return; + + kore_msg_send(msg->src, KORE_MSG_KEYMGR_RESP, buf, ret); +} + +static void +keymgr_ecdsa_sign(struct kore_msg *msg, const void *data, struct key *key) +{ + size_t len; + EC_KEY *ec; + const struct kore_keyreq *req; + unsigned int siglen; + u_int8_t sig[1024]; + + req = (const struct kore_keyreq *)data; + ec = EVP_PKEY_get0_EC_KEY(key->pkey); + + len = ECDSA_size(ec); + if (req->data_len > len || len > sizeof(sig)) + return; + + if (ECDSA_sign(EVP_PKEY_NONE, req->data, req->data_len, + sig, &siglen, ec) == 0) + return; + + if (siglen > sizeof(sig)) + return; + + kore_msg_send(msg->src, KORE_MSG_KEYMGR_RESP, sig, siglen); +} + +static void +keymgr_x509_msg(const char *domain, const void *data, size_t len, + int target, int msg) +{ + struct kore_buf buf; + struct kore_x509_msg hdr; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.data_len = len; + + if (kore_strlcpy(hdr.domain, domain, sizeof(hdr.domain)) >= + sizeof(hdr.domain)) + fatalx("%s: domain truncated", __func__); + + kore_buf_init(&buf, sizeof(hdr) + len); + kore_buf_append(&buf, &hdr, sizeof(hdr)); + kore_buf_append(&buf, data, len); + + kore_msg_send(target, msg, buf.data, buf.offset); + kore_buf_cleanup(&buf); +} + +#if defined(KORE_USE_ACME) +static void +keymgr_acme_init(void) +{ + RSA *rsa; + struct key *key; + char *e, *n; + int needsreg; + const BIGNUM *be, *bn; + + if (acme_provider == NULL) + return; + + if (mkdir(KORE_ACME_CERTDIR, 0700) == -1) { + if (errno != EEXIST) + fatalx("mkdir(%s): %s", KORE_ACME_CERTDIR, errno_s); + } + + umask(S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH); + + needsreg = 0; + acmeproc_ready = 0; + key = keymgr_load_privatekey(KORE_ACME_ACCOUNT_KEY); + + if (acme_renewal != NULL) + kore_timer_remove(acme_renewal); + + acme_renewal = kore_timer_add(keymgr_acme_renewal, + ACME_RENEWAL_TIMER, NULL, 0); + + if (key->pkey == NULL) { + kore_log(LOG_INFO, "generating new ACME account key"); + key->pkey = kore_tls_rsakey_generate(KORE_ACME_ACCOUNT_KEY); + needsreg = 1; + } else { + kore_log(LOG_INFO, "loaded existing ACME account key"); + } + + rsa = EVP_PKEY_get0_RSA(key->pkey); + RSA_get0_key(rsa, &bn, &be, NULL); + + e = keymgr_bignum_base64(be); + n = keymgr_bignum_base64(bn); + + kore_msg_send(KORE_WORKER_ACME, KORE_ACME_RSAKEY_E, e, strlen(e)); + kore_msg_send(KORE_WORKER_ACME, KORE_ACME_RSAKEY_N, n, strlen(n)); + + kore_free(e); + kore_free(n); + + if (needsreg) { + kore_msg_send(KORE_WORKER_ACME, + KORE_ACME_ACCOUNT_CREATE, NULL, 0); + } else { + kore_msg_send(KORE_WORKER_ACME, + KORE_ACME_ACCOUNT_RESOLVE, NULL, 0); + } + + kore_msg_register(KORE_ACME_SIGN, keymgr_acme_sign); + kore_msg_register(KORE_ACME_CSR_REQUEST, keymgr_msg_recv); + kore_msg_register(KORE_ACME_PROC_READY, keymgr_acme_ready); + kore_msg_register(KORE_ACME_ORDER_FAILED, keymgr_msg_recv); + kore_msg_register(KORE_ACME_INSTALL_CERT, keymgr_msg_recv); + kore_msg_register(KORE_ACME_CHALLENGE_CERT, keymgr_msg_recv); +} + +static void +keymgr_acme_domainkey(struct kore_domain *dom, struct key *key) +{ + char *p; + + kore_log(LOG_INFO, "generated new domain key for %s", dom->domain); + + if ((p = strrchr(dom->certkey, '/')) == NULL) + fatalx("invalid certkey path '%s'", dom->certkey); + + *p = '\0'; + + if (mkdir(dom->certkey, 0700) == -1) { + if (errno != EEXIST) + fatalx("mkdir(%s): %s", dom->certkey, errno_s); + } + + *p = '/'; + key->pkey = kore_tls_rsakey_generate(dom->certkey); +} + +static void +keymgr_acme_order_create(const char *domain) +{ + struct acme_order *order; + + order = kore_calloc(1, sizeof(*order)); + + order->state = ACME_ORDER_STATE_INIT; + order->domain = kore_strdup(domain); + order->timer = kore_timer_add(keymgr_acme_order_start, + 1000, order, KORE_TIMER_ONESHOT); +} + +static void +keymgr_acme_order_redo(void *udata, u_int64_t now) +{ + struct kore_domain *dom = udata; + + kore_log(LOG_INFO, "[%s] redoing order", dom->domain); + keymgr_acme_order_create(dom->domain); +} + +static void +keymgr_acme_order_start(void *udata, u_int64_t now) +{ + struct acme_order *order = udata; + + switch (order->state) { + case ACME_ORDER_STATE_INIT: + if (acmeproc_ready == 0) + break; + order->state = ACME_ORDER_STATE_SUBMIT; + /* fallthrough */ + case ACME_ORDER_STATE_SUBMIT: + kore_msg_send(KORE_WORKER_ACME, KORE_ACME_ORDER_CREATE, + order->domain, strlen(order->domain)); + kore_free(order->domain); + kore_free(order); + order = NULL; + break; + default: + fatalx("%s: unknown order state %d", __func__, order->state); + } + + if (order != NULL) { + order->timer = kore_timer_add(keymgr_acme_order_start, + 5000, order, KORE_TIMER_ONESHOT); + } +} + +static void +keymgr_acme_ready(struct kore_msg *msg, const void *data) +{ + acmeproc_ready = 1; + kore_log(LOG_INFO, "acme process ready to receive orders"); + + keymgr_acme_renewal(NULL, kore_time_ms()); +} + +static void +keymgr_acme_check(struct kore_domain *dom) +{ + FILE *fp; + int days; + X509 *x509; + time_t expires, now; + + if (dom->acme == 0) + return; + + if (access(dom->certfile, R_OK) == -1) { + if (errno == ENOENT) { + keymgr_acme_order_create(dom->domain); + return; + } + kore_log(LOG_ERR, "access(%s): %s", dom->certfile, errno_s); + return; + } + + if ((fp = fopen(dom->certfile, "r")) == NULL) { + kore_log(LOG_ERR, "fopen(%s): %s", dom->certfile, errno_s); + return; + } + + if ((x509 = PEM_read_X509(fp, NULL, NULL, NULL)) == NULL) { + fclose(fp); + kore_log(LOG_ERR, "PEM_read_X509: %s", ssl_errno_s); + return; + } + + fclose(fp); + + if (!keymgr_x509_not_after(x509, &expires)) { + X509_free(x509); + return; + } + + time(&now); + days = (expires - now) / 86400; + + kore_log(LOG_INFO, "%s certificate expires in %d days", + dom->domain, days); + + if (days <= ACME_RENEWAL_THRESHOLD) { + kore_log(LOG_INFO, "%s renewing certificate", dom->domain); + keymgr_acme_order_create(dom->domain); + } + + X509_free(x509); +} + +static void +keymgr_acme_renewal(void *udata, u_int64_t now) +{ + kore_domain_callback(keymgr_acme_check); +} + +static void +keymgr_acme_sign(struct kore_msg *msg, const void *data) +{ + u_int32_t id; + struct kore_buf buf; + const u_int8_t *ptr; + u_int8_t *sig; + EVP_MD_CTX *ctx; + struct key *key; + char *b64; + unsigned int siglen; + + TAILQ_FOREACH(key, &keys, list) { + if (key->dom == NULL) + break; + } + + if (key == NULL) + fatalx("%s: missing key", __func__); + + if (msg->length < sizeof(id)) + fatalx("%s: invalid length (%zu)", __func__, msg->length); + + ptr = data; + memcpy(&id, ptr, sizeof(id)); + + ptr += sizeof(id); + msg->length -= sizeof(id); + + sig = kore_calloc(1, EVP_PKEY_size(key->pkey)); + + if ((ctx = EVP_MD_CTX_create()) == NULL) + fatalx("EVP_MD_CTX_create: %s", ssl_errno_s); + + if (!EVP_SignInit_ex(ctx, EVP_sha256(), NULL)) + fatalx("EVP_SignInit_ex: %s", ssl_errno_s); + + if (!EVP_SignUpdate(ctx, ptr, msg->length)) + fatalx("EVP_SignUpdate: %s", ssl_errno_s); + + if (!EVP_SignFinal(ctx, sig, &siglen, key->pkey)) + fatalx("EVP_SignFinal: %s", ssl_errno_s); + + if (!kore_base64url_encode(sig, siglen, &b64, KORE_BASE64_RAW)) + fatalx("%s: failed to b64url encode signed data", __func__); + + kore_buf_init(&buf, siglen + sizeof(id)); + kore_buf_append(&buf, &id, sizeof(id)); + kore_buf_append(&buf, b64, strlen(b64)); + + kore_msg_send(KORE_WORKER_ACME, + KORE_ACME_SIGN_RESULT, buf.data, buf.offset); + + EVP_MD_CTX_destroy(ctx); + + kore_free(sig); + kore_free(b64); + kore_buf_cleanup(&buf); +} + +static void +keymgr_acme_install_cert(const void *data, size_t len, struct key *key) +{ + int fd; + ssize_t ret; + + fd = open(key->dom->certfile, O_CREAT | O_TRUNC | O_WRONLY, 0700); + if (fd == -1) + fatalx("open(%s): %s", key->dom->certfile, errno_s); + + kore_log(LOG_INFO, "writing %zu bytes of data", len); + + for (;;) { + ret = write(fd, data, len); + if (ret == -1) { + if (errno == EINTR) + continue; + fatalx("write(%s): %s", key->dom->certfile, errno_s); + } + + break; + } + + if ((size_t)ret != len) { + fatalx("incorrect write on %s (%zd/%zu)", + key->dom->certfile, ret, len); + } + + if (close(fd) == -1) { + kore_log(LOG_NOTICE, + "close error on '%s' (%s)", key->dom->certfile, errno_s); + } + + keymgr_submit_certificates(key->dom, KORE_MSG_WORKER_ALL); + + keymgr_x509_msg(key->dom->domain, NULL, 0, + KORE_MSG_WORKER_ALL, KORE_ACME_CHALLENGE_CLEAR_CERT); +} + +static void +keymgr_acme_order_failed(const void *data, size_t len, struct key *key) +{ + u_int32_t retry; + + if (len != sizeof(retry)) { + kore_log(LOG_ERR, "%s: invalid payload (%zu)", __func__, len); + return; + } + + memcpy(&retry, data, len); + + kore_timer_add(keymgr_acme_order_redo, retry, key->dom, + KORE_TIMER_ONESHOT); +} + +static void +keymgr_acme_challenge_cert(const void *data, size_t len, struct key *key) +{ + STACK_OF(X509_EXTENSION) *sk; + size_t idx; + time_t now; + X509_EXTENSION *ext; + X509_NAME *name; + X509 *x509; + const u_int8_t *digest; + int slen, i; + u_int8_t *cert, *uptr; + char hex[(SHA256_DIGEST_LENGTH * 2) + 1]; + + kore_log(LOG_INFO, "[%s] generating tls-alpn-01 challenge cert", + key->dom->domain); + + if (len != SHA256_DIGEST_LENGTH) + fatalx("invalid digest length of %zu bytes", len); + + digest = data; + + for (idx = 0; idx < SHA256_DIGEST_LENGTH; idx++) { + slen = snprintf(hex + (idx * 2), sizeof(hex) - (idx * 2), + "%02x", digest[idx]); + if (slen == -1 || (size_t)slen >= sizeof(hex)) + fatalx("failed to convert digest to hex"); + } + + if ((x509 = X509_new()) == NULL) + fatalx("X509_new(): %s", ssl_errno_s); + + if (!X509_set_version(x509, 2)) + fatalx("X509_set_version(): %s", ssl_errno_s); + + time(&now); + if (!ASN1_INTEGER_set(X509_get_serialNumber(x509), now)) + fatalx("ASN1_INTEGER_set(): %s", ssl_errno_s); + + if (!X509_gmtime_adj(X509_get_notBefore(x509), 0)) + fatalx("X509_gmtime_adj(): %s", ssl_errno_s); + + if (!X509_gmtime_adj(X509_get_notAfter(x509), ACME_X509_EXPIRATION)) + fatalx("X509_gmtime_adj(): %s", ssl_errno_s); + + if (!X509_set_pubkey(x509, key->pkey)) + fatalx("X509_set_pubkey(): %s", ssl_errno_s); + + if ((name = X509_get_subject_name(x509)) == NULL) + fatalx("X509_get_subject_name(): %s", ssl_errno_s); + + if (!X509_NAME_add_entry_by_txt(name, "CN", + MBSTRING_ASC, (const unsigned char *)key->dom->domain, -1, -1, 0)) + fatalx("X509_NAME_add_entry_by_txt(): CN %s", ssl_errno_s); + + if (!X509_set_issuer_name(x509, name)) + fatalx("X509_set_issuer_name(): %s", ssl_errno_s); + + sk = sk_X509_EXTENSION_new_null(); + keymgr_x509_ext(sk, acme_oid, "critical,%s", hex); + keymgr_x509_ext(sk, NID_subject_alt_name, "DNS:%s", key->dom->domain); + + for (i = 0; i < sk_X509_EXTENSION_num(sk); i++) { + ext = sk_X509_EXTENSION_value(sk, i); + if (!X509_add_ext(x509, ext, 0)) + fatalx("X509_add_ext(): %s", ssl_errno_s); + } + + if (!X509_sign(x509, key->pkey, EVP_sha256())) + fatalx("X509_sign(): %s", ssl_errno_s); + + if ((slen = i2d_X509(x509, NULL)) <= 0) + fatalx("i2d_X509: %s", ssl_errno_s); + + cert = kore_calloc(1, slen); + uptr = cert; + + if (i2d_X509(x509, &uptr) <= 0) + fatalx("i2d_X509: %s", ssl_errno_s); + + keymgr_x509_msg(key->dom->domain, cert, slen, + KORE_MSG_WORKER_ALL, KORE_ACME_CHALLENGE_SET_CERT); + + kore_free(cert); + X509_free(x509); + sk_X509_EXTENSION_pop_free(sk, X509_EXTENSION_free); +} + +static void +keymgr_acme_csr(const struct kore_keyreq *req, struct key *key) +{ + int len; + STACK_OF(X509_EXTENSION) *sk; + X509_REQ *csr; + X509_NAME *name; + u_int8_t *data, *uptr; + + kore_log(LOG_INFO, "[%s] creating CSR", req->domain); + + if ((csr = X509_REQ_new()) == NULL) + fatalx("X509_REQ_new: %s", ssl_errno_s); + + if (!X509_REQ_set_version(csr, 3)) + fatalx("X509_REQ_set_version(): %s", ssl_errno_s); + + if (!X509_REQ_set_pubkey(csr, key->pkey)) + fatalx("X509_REQ_set_pubkey(): %s", ssl_errno_s); + + if ((name = X509_REQ_get_subject_name(csr)) == NULL) + fatalx("X509_REQ_get_subject_name(): %s", ssl_errno_s); + + if (!X509_NAME_add_entry_by_txt(name, "CN", + MBSTRING_ASC, (const unsigned char *)key->dom->domain, -1, -1, 0)) + fatalx("X509_NAME_add_entry_by_txt(): %s", ssl_errno_s); + + sk = sk_X509_EXTENSION_new_null(); + keymgr_x509_ext(sk, NID_subject_alt_name, "DNS:%s", key->dom->domain); + + if (!X509_REQ_add_extensions(csr, sk)) + fatalx("X509_REQ_add_extensions(): %s", ssl_errno_s); + + if (!X509_REQ_sign(csr, key->pkey, EVP_sha256())) + fatalx("X509_REQ_sign(): %s", ssl_errno_s); + + if ((len = i2d_X509_REQ(csr, NULL)) <= 0) + fatalx("i2d_X509_REQ: %s", ssl_errno_s); + + data = kore_calloc(1, len); + uptr = data; + + if (i2d_X509_REQ(csr, &uptr) <= 0) + fatalx("i2d_X509_REQ: %s", ssl_errno_s); + + keymgr_x509_msg(key->dom->domain, data, len, + KORE_WORKER_ACME, KORE_ACME_CSR_RESPONSE); + + kore_free(data); + X509_REQ_free(csr); + + sk_X509_EXTENSION_pop_free(sk, X509_EXTENSION_free); +} + +static void +keymgr_x509_ext(STACK_OF(X509_EXTENSION) *sk, int extnid, const char *fmt, ...) +{ + int len; + va_list args; + X509_EXTENSION *ext; + char buf[1024]; + + va_start(args, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + if (len == -1 || (size_t)len >= sizeof(buf)) + fatalx("failed to create buffer for extension %d", extnid); + + if ((ext = X509V3_EXT_conf_nid(NULL, NULL, extnid, buf)) == NULL) { + fatalx("X509V3_EXT_conf_nid(%d, %s): %s", + extnid, buf, ssl_errno_s); + } + + sk_X509_EXTENSION_push(sk, ext); +} + +static char * +keymgr_bignum_base64(const BIGNUM *bn) +{ + int len; + void *buf; + char *encoded; + + len = BN_num_bytes(bn); + buf = kore_calloc(1, len); + + if (BN_bn2bin(bn, buf) != len) + fatalx("BN_bn2bin: %s", ssl_errno_s); + + if (!kore_base64url_encode(buf, len, &encoded, KORE_BASE64_RAW)) + fatalx("failed to base64 encode BIGNUM"); + + return (encoded); +} + +static int +keymgr_x509_not_after(X509 *x509, time_t *out) +{ + const ASN1_TIME *na; + int ret; + + ret = KORE_RESULT_ERROR; + + if ((na = X509_get_notAfter(x509)) == NULL) { + kore_log(LOG_ERR, "no notAfter date in x509"); + return (KORE_RESULT_ERROR); + } + + switch (na->type) { + case V_ASN1_UTCTIME: + ret = keymgr_asn1_convert_utctime(na, out); + break; + case V_ASN1_GENERALIZEDTIME: + ret = keymgr_asn1_convert_generalizedtime(na->data, + na->length, out); + break; + default: + kore_log(LOG_ERR, "invalid notAfter type (%d)", na->type); + break; + } + + return (ret); +} + +static int +keymgr_asn1_convert_utctime(const ASN1_TIME *na, time_t *out) +{ + int len, year; + char buf[ASN1_GENERALIZEDTIME_LEN + 1]; + + if (na->length != ASN1_UTCTIME_LEN) { + kore_log(LOG_ERR, "invalid UTCTIME: too short (%d)", + na->length); + return (KORE_RESULT_ERROR); + } + + if (!isdigit(na->data[0]) || !isdigit(na->data[1])) { + kore_log(LOG_ERR, "invalid UTCTIME: YY are not digits"); + return (KORE_RESULT_ERROR); + } + + year = (na->data[0] - '0') * 10 + (na->data[1] - '0'); + + /* RFC 5280 says years >= 50 are interpreted as 19YY */ + if (year >= 50) + year = 1900 + year; + else + year = 2000 + year; + + /* Convert it to GENERALIZEDTIME format and call that parser. */ + len = snprintf(buf, sizeof(buf), "%04d%.*s", year, + na->length - 2, (const char *)na->data+ 2); + if (len == -1 || (size_t)len >= sizeof(buf)) { + kore_log(LOG_ERR, "invalid UTCTIME: failed to convert"); + return (KORE_RESULT_ERROR); + } + + return (keymgr_asn1_convert_generalizedtime(buf, len, out)); +} + +static int +keymgr_asn1_convert_generalizedtime(const void *ptr, size_t len, time_t *out) +{ + size_t i; + struct tm tm; + const u_int8_t *buf; + + if (len != ASN1_GENERALIZEDTIME_LEN) { + kore_log(LOG_ERR, "invalid GENERALIZEDTIME: too short (%zu)", + len); + return (KORE_RESULT_ERROR); + } + + buf = ptr; + + for (i = 0; i < len - 1; i++) { + if (!isdigit(buf[i])) { + kore_log(LOG_ERR, + "invalid GENERALIZEDTIME: invalid bytes"); + return (KORE_RESULT_ERROR); + } + } + + /* RFC 5280 states that Zulu time must be used (Z). */ + if (buf[i] != 'Z') { + kore_log(LOG_ERR, "invalid GENERALIZEDTIME: not Zulu time"); + return (KORE_RESULT_ERROR); + } + + memset(&tm, 0, sizeof(tm)); + + tm.tm_year = (buf[0] - '0') * 1000 + (buf[1] - '0') * 100 + + (buf[2] - '0') * 10 + (buf[3] - '0'); + + tm.tm_mon = (buf[4] - '0') * 10 + (buf[5] - '0'); + tm.tm_mday = (buf[6] - '0') * 10 + (buf[7] - '0'); + tm.tm_hour = (buf[8] - '0') * 10 + (buf[9] - '0'); + tm.tm_min = (buf[10] - '0') * 10 + (buf[11] - '0'); + tm.tm_sec = (buf[12] - '0') * 10 + (buf[13] - '0'); + + tm.tm_mon = tm.tm_mon - 1; + tm.tm_year = tm.tm_year - 1900; + + *out = mktime(&tm); + + return (KORE_RESULT_OK); +} +#endif diff --git a/src/kore.c b/src/kore.c @@ -67,7 +67,6 @@ int kore_foreground = 0; char *kore_progname = NULL; u_int32_t kore_socket_backlog = 5000; char *kore_pidfile = KORE_PIDFILE_DEFAULT; -char *kore_tls_cipher_list = KORE_DEFAULT_CIPHER_LIST; struct kore_privsep worker_privsep; @@ -80,7 +79,6 @@ static void version(void); static void kore_write_kore_pid(void); static void kore_proctitle_setup(void); -static void kore_server_sslstart(void); static void kore_server_shutdown(void); static void kore_server_start(int, char *[]); static void kore_call_parent_configure(int, char **); @@ -150,6 +148,8 @@ version(void) #if defined(KORE_USE_ACME) printf("acme "); #endif + if (!kore_tls_supported()) + printf("notls "); printf("\n"); exit(0); } @@ -223,7 +223,7 @@ main(int argc, char *argv[]) #endif kore_domain_init(); kore_module_init(); - kore_server_sslstart(); + kore_tls_init(); #if !defined(KORE_SINGLE_BINARY) && !defined(KORE_USE_PYTHON) if (config_file == NULL) @@ -346,74 +346,6 @@ kore_default_getopt(int argc, char **argv) } int -kore_tls_sni_cb(SSL *ssl, int *ad, void *arg) -{ - struct connection *c; - struct kore_domain *dom; - const char *sname; - - if ((c = SSL_get_ex_data(ssl, 0)) == NULL) - fatal("no connection data in %s", __func__); - - sname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - kore_debug("kore_tls_sni_cb(): received host %s", sname); - - if (sname != NULL) - c->tls_sni = kore_strdup(sname); - - if (sname != NULL && - (dom = kore_domain_lookup(c->owner->server, sname)) != NULL) { - if (dom->ssl_ctx == NULL) { - kore_log(LOG_NOTICE, - "TLS configuration for %s not complete", - dom->domain); - return (SSL_TLSEXT_ERR_NOACK); - } - - kore_debug("kore_ssl_sni_cb(): Using %s CTX", sname); - SSL_set_SSL_CTX(ssl, dom->ssl_ctx); - - if (dom->cafile != NULL) { - SSL_set_verify(ssl, SSL_VERIFY_PEER | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); - c->flags |= CONN_LOG_TLS_FAILURE; - } else { - SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); - } - -#if defined(KORE_USE_ACME) - /* - * If ALPN callback was called before SNI was parsed we - * must make sure we swap to the correct certificate now. - */ - if (c->flags & CONN_TLS_ALPN_ACME_SEEN) - kore_acme_tls_challenge_use_cert(ssl, dom); - - c->flags |= CONN_TLS_SNI_SEEN; -#endif - return (SSL_TLSEXT_ERR_OK); - } - - return (SSL_TLSEXT_ERR_NOACK); -} - -void -kore_tls_info_callback(const SSL *ssl, int flags, int ret) -{ - struct connection *c; - - if (flags & SSL_CB_HANDSHAKE_START) { - if ((c = SSL_get_app_data(ssl)) == NULL) - fatal("no SSL_get_app_data"); - -#if defined(TLS1_3_VERSION) - if (SSL_version(ssl) != TLS1_3_VERSION) -#endif - c->tls_reneg++; - } -} - -int kore_server_bind(struct kore_server *srv, const char *ip, const char *port, const char *ccb) { @@ -523,7 +455,11 @@ kore_server_create(const char *name) srv = kore_calloc(1, sizeof(struct kore_server)); srv->name = kore_strdup(name); - srv->tls = 1; + + if (kore_tls_supported()) + srv->tls = 1; + else + srv->tls = 0; TAILQ_INIT(&srv->domains); LIST_INIT(&srv->listeners); @@ -844,13 +780,6 @@ kore_proctitle_setup(void) } static void -kore_server_sslstart(void) -{ - SSL_library_init(); - SSL_load_error_strings(); -} - -static void kore_server_start(int argc, char *argv[]) { u_int32_t tmp; @@ -922,11 +851,15 @@ kore_server_start(int argc, char *argv[]) #endif /* Check if keymgr will be active. */ - LIST_FOREACH(srv, &kore_servers, list) { - if (srv->tls) { - keymgr_active = 1; - break; + if (kore_tls_supported()) { + LIST_FOREACH(srv, &kore_servers, list) { + if (srv->tls) { + kore_keymgr_active = 1; + break; + } } + } else { + kore_keymgr_active = 0; } kore_platform_proctitle("[parent]"); @@ -1033,6 +966,7 @@ kore_server_shutdown(void) kore_platform_event_cleanup(); kore_connection_cleanup(); kore_domain_cleanup(); + kore_tls_cleanup(); net_cleanup(); } diff --git a/src/msg.c b/src/msg.c @@ -237,7 +237,7 @@ msg_recv_data(struct netbuf *nb) dst = *(u_int16_t *)c->hdlr_extra; if (destination == KORE_MSG_WORKER_ALL) { - if (keymgr_active && dst == 0) + if (kore_keymgr_active && dst == 0) deliver = 0; } else { if (dst != destination) diff --git a/src/net.c b/src/net.c @@ -341,104 +341,6 @@ net_remove_netbuf(struct connection *c, struct netbuf *nb) } int -net_write_tls(struct connection *c, size_t len, size_t *written) -{ - int r; - - if (len > INT_MAX) - return (KORE_RESULT_ERROR); - - ERR_clear_error(); - r = SSL_write(c->ssl, (c->snb->buf + c->snb->s_off), len); - if (c->tls_reneg > 1) - return (KORE_RESULT_ERROR); - - if (r <= 0) { - r = SSL_get_error(c->ssl, r); - switch (r) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - c->evt.flags &= ~KORE_EVENT_WRITE; - c->snb->flags |= NETBUF_MUST_RESEND; - return (KORE_RESULT_OK); - case SSL_ERROR_SYSCALL: - switch (errno) { - case EINTR: - *written = 0; - return (KORE_RESULT_OK); - case EAGAIN: - c->evt.flags &= ~KORE_EVENT_WRITE; - c->snb->flags |= NETBUF_MUST_RESEND; - return (KORE_RESULT_OK); - default: - break; - } - /* FALLTHROUGH */ - default: - kore_debug("SSL_write(): %s", ssl_errno_s); - if (c->flags & CONN_LOG_TLS_FAILURE) { - kore_log(LOG_NOTICE, - "SSL_write(): %s", ssl_errno_s); - } - return (KORE_RESULT_ERROR); - } - } - - *written = (size_t)r; - - return (KORE_RESULT_OK); -} - -int -net_read_tls(struct connection *c, size_t *bytes) -{ - int r; - - ERR_clear_error(); - r = SSL_read(c->ssl, (c->rnb->buf + c->rnb->s_off), - (c->rnb->b_len - c->rnb->s_off)); - - if (c->tls_reneg > 1) - return (KORE_RESULT_ERROR); - - if (r <= 0) { - r = SSL_get_error(c->ssl, r); - switch (r) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - c->evt.flags &= ~KORE_EVENT_READ; - return (KORE_RESULT_OK); - case SSL_ERROR_ZERO_RETURN: - return (KORE_RESULT_ERROR); - case SSL_ERROR_SYSCALL: - switch (errno) { - case EINTR: - *bytes = 0; - return (KORE_RESULT_OK); - case EAGAIN: - c->evt.flags &= ~KORE_EVENT_READ; - c->snb->flags |= NETBUF_MUST_RESEND; - return (KORE_RESULT_OK); - default: - break; - } - /* FALLTHROUGH */ - default: - kore_debug("SSL_read(): %s", ssl_errno_s); - if (c->flags & CONN_LOG_TLS_FAILURE) { - kore_log(LOG_NOTICE, - "SSL_read(): %s", ssl_errno_s); - } - return (KORE_RESULT_ERROR); - } - } - - *bytes = (size_t)r; - - return (KORE_RESULT_OK); -} - -int net_write(struct connection *c, size_t len, size_t *written) { ssize_t r; diff --git a/src/sha1.c b/src/sha1.c @@ -0,0 +1,171 @@ +/* $OpenBSD: sha1.c,v 1.27 2019/06/07 22:56:36 dtucker Exp $ */ + +/* + * SHA-1 in C + * By Steve Reid <steve@edmweb.com> + * 100% Public Domain + * + * Test Vectors (from FIPS PUB 180-1) + * "abc" + * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + * A million repetitions of "a" + * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + */ + +#include <sys/types.h> +#include <string.h> + +#include "sha1.h" + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* + * blk0() and blk() perform the initial expand. + * I got the idea of expanding during the round function from SSLeay + */ +#if BYTE_ORDER == LITTLE_ENDIAN +# define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#else +# define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* + * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 + */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + +typedef union { + u_int8_t c[64]; + u_int32_t l[16]; +} CHAR64LONG16; + +/* + * Hash a single 512-bit block. This is the core of the algorithm. + */ +void +SHA1Transform(u_int32_t state[5], const u_int8_t buffer[SHA1_BLOCK_LENGTH]) +{ + u_int32_t a, b, c, d, e; + u_int8_t workspace[SHA1_BLOCK_LENGTH]; + CHAR64LONG16 *block = (CHAR64LONG16 *)workspace; + + (void)memcpy(block, buffer, SHA1_BLOCK_LENGTH); + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} + +/* + * SHA1Init - Initialize new context + */ +void +SHA1Init(SHA1_CTX *context) +{ + /* SHA1 initialization constants */ + context->count = 0; + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; +} + +/* + * Run your data through this. + */ +void +SHA1Update(SHA1_CTX *context, const u_int8_t *data, size_t len) +{ + size_t i, j; + + j = (size_t)((context->count >> 3) & 63); + context->count += ((u_int64_t)len << 3); + if ((j + len) > 63) { + (void)memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) + SHA1Transform(context->state, &data[i]); + j = 0; + } else { + i = 0; + } + (void)memcpy(&context->buffer[j], &data[i], len - i); +} + +/* + * Add padding and return the message digest. + */ +void +SHA1Pad(SHA1_CTX *context) +{ + u_int8_t finalcount[8]; + u_int i; + + for (i = 0; i < 8; i++) { + finalcount[i] = (u_int8_t)((context->count >> + ((7 - (i & 7)) * 8)) & 255); /* Endian independent */ + } + SHA1Update(context, (u_int8_t *)"\200", 1); + while ((context->count & 504) != 448) + SHA1Update(context, (u_int8_t *)"\0", 1); + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ +} + +void +SHA1Final(u_int8_t digest[SHA1_DIGEST_LENGTH], SHA1_CTX *context) +{ + u_int i; + + SHA1Pad(context); + for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { + digest[i] = (u_int8_t) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + + //explicit_bzero(context, sizeof(*context)); +} diff --git a/src/sha2.c b/src/sha2.c @@ -0,0 +1,885 @@ +/* $OpenBSD: sha2.c,v 1.28 2019/07/23 12:35:22 dtucker Exp $ */ + +/* + * FILE: sha2.c + * AUTHOR: Aaron D. Gifford <me@aarongifford.com> + * + * Copyright (c) 2000-2001, Aaron D. Gifford + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $From: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $ + */ + +/* OPENBSD ORIGINAL: lib/libc/hash/sha2.c */ + +#include <sys/types.h> +#include <string.h> + +#if defined(__APPLE__) +#include <machine/endian.h> +#else +#include <endian.h> +#endif + +#include "sha2.h" + +/* no-op out, similar to DEF_WEAK but only needed here */ +#define MAKE_CLONE(x, y) void __ssh_compat_make_clone_##x_##y(void) + +/* + * UNROLLED TRANSFORM LOOP NOTE: + * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform + * loop version for the hash transform rounds (defined using macros + * later in this file). Either define on the command line, for example: + * + * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c + * + * or define below: + * + * #define SHA2_UNROLL_TRANSFORM + * + */ +#if defined(__amd64__) || defined(__i386__) +#define SHA2_UNROLL_TRANSFORM +#endif + +/*** SHA-224/256/384/512 Machine Architecture Definitions *****************/ +/* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivalent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * #define LITTLE_ENDIAN 1234 + * #define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * #define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * #define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including <sys/types.h> (which in turn includes + * <machine/endian.h> where the appropriate definitions are actually + * made). + */ +#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) +#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN +#endif + + +/*** SHA-224/256/384/512 Various Length Definitions ***********************/ +/* NOTE: Most of these are in sha2.h */ +#define SHA224_SHORT_BLOCK_LENGTH (SHA224_BLOCK_LENGTH - 8) +#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) +#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16) +#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) + +/*** ENDIAN SPECIFIC COPY MACROS **************************************/ +#define BE_8_TO_32(dst, cp) do { \ + (dst) = (u_int32_t)(cp)[3] | ((u_int32_t)(cp)[2] << 8) | \ + ((u_int32_t)(cp)[1] << 16) | ((u_int32_t)(cp)[0] << 24); \ +} while(0) + +#define BE_8_TO_64(dst, cp) do { \ + (dst) = (u_int64_t)(cp)[7] | ((u_int64_t)(cp)[6] << 8) | \ + ((u_int64_t)(cp)[5] << 16) | ((u_int64_t)(cp)[4] << 24) | \ + ((u_int64_t)(cp)[3] << 32) | ((u_int64_t)(cp)[2] << 40) | \ + ((u_int64_t)(cp)[1] << 48) | ((u_int64_t)(cp)[0] << 56); \ +} while (0) + +#define BE_64_TO_8(cp, src) do { \ + (cp)[0] = (src) >> 56; \ + (cp)[1] = (src) >> 48; \ + (cp)[2] = (src) >> 40; \ + (cp)[3] = (src) >> 32; \ + (cp)[4] = (src) >> 24; \ + (cp)[5] = (src) >> 16; \ + (cp)[6] = (src) >> 8; \ + (cp)[7] = (src); \ +} while (0) + +#define BE_32_TO_8(cp, src) do { \ + (cp)[0] = (src) >> 24; \ + (cp)[1] = (src) >> 16; \ + (cp)[2] = (src) >> 8; \ + (cp)[3] = (src); \ +} while (0) + +/* + * Macro for incrementally adding the unsigned 64-bit integer n to the + * unsigned 128-bit integer (represented using a two-element array of + * 64-bit words): + */ +#define ADDINC128(w,n) do { \ + (w)[0] += (u_int64_t)(n); \ + if ((w)[0] < (n)) { \ + (w)[1]++; \ + } \ +} while (0) + +/*** THE SIX LOGICAL FUNCTIONS ****************************************/ +/* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: The naming of R and S appears backwards here (R is a SHIFT and + * S is a ROTATION) because the SHA-224/256/384/512 description document + * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this + * same "backwards" definition. + */ +/* Shift-right (used in SHA-224, SHA-256, SHA-384, and SHA-512): */ +#define R(b,x) ((x) >> (b)) +/* 32-bit Rotate-right (used in SHA-224 and SHA-256): */ +#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) +/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ +#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) + +/* Two of six logical functions used in SHA-224, SHA-256, SHA-384, and SHA-512: */ +#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/* Four of six logical functions used in SHA-224 and SHA-256: */ +#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) +#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) +#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) +#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) + +/* Four of six logical functions used in SHA-384 and SHA-512: */ +#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) +#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) +#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) +#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) + + +/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ +/* Hash constant words K for SHA-224 and SHA-256: */ +static const u_int32_t K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Initial hash value H for SHA-256: */ +static const u_int32_t sha256_initial_hash_value[8] = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL +}; + +/* Hash constant words K for SHA-384 and SHA-512: */ +static const u_int64_t K512[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +/* Initial hash value H for SHA-512 */ +static const u_int64_t sha512_initial_hash_value[8] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL +}; + +/* Initial hash value H for SHA-384 */ +static const u_int64_t sha384_initial_hash_value[8] = { + 0xcbbb9d5dc1059ed8ULL, + 0x629a292a367cd507ULL, + 0x9159015a3070dd17ULL, + 0x152fecd8f70e5939ULL, + 0x67332667ffc00b31ULL, + 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, + 0x47b5481dbefa4fa4ULL +}; + +/*** SHA-256: *********************************************************/ +void +SHA256Init(SHA2_CTX *context) +{ + memcpy(context->state.st32, sha256_initial_hash_value, + sizeof(sha256_initial_hash_value)); + memset(context->buffer, 0, sizeof(context->buffer)); + context->bitcount[0] = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-256 round macros: */ + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) do { \ + BE_8_TO_32(W256[j], data); \ + data += 4; \ + T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + W256[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + +#define ROUND256(a,b,c,d,e,f,g,h) do { \ + s0 = W256[(j+1)&0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j+14)&0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + +void +SHA256Transform(u_int32_t state[8], const u_int8_t data[SHA256_BLOCK_LENGTH]) +{ + u_int32_t a, b, c, d, e, f, g, h, s0, s1; + u_int32_t T1, W256[16]; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + f = state[5]; + g = state[6]; + h = state[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a,b,c,d,e,f,g,h); + ROUND256_0_TO_15(h,a,b,c,d,e,f,g); + ROUND256_0_TO_15(g,h,a,b,c,d,e,f); + ROUND256_0_TO_15(f,g,h,a,b,c,d,e); + ROUND256_0_TO_15(e,f,g,h,a,b,c,d); + ROUND256_0_TO_15(d,e,f,g,h,a,b,c); + ROUND256_0_TO_15(c,d,e,f,g,h,a,b); + ROUND256_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds up to 63: */ + do { + ROUND256(a,b,c,d,e,f,g,h); + ROUND256(h,a,b,c,d,e,f,g); + ROUND256(g,h,a,b,c,d,e,f); + ROUND256(f,g,h,a,b,c,d,e); + ROUND256(e,f,g,h,a,b,c,d); + ROUND256(d,e,f,g,h,a,b,c); + ROUND256(c,d,e,f,g,h,a,b); + ROUND256(b,c,d,e,f,g,h,a); + } while (j < 64); + + /* Compute the current intermediate hash value */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void +SHA256Transform(u_int32_t state[8], const u_int8_t data[SHA256_BLOCK_LENGTH]) +{ + u_int32_t a, b, c, d, e, f, g, h, s0, s1; + u_int32_t T1, T2, W256[16]; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + f = state[5]; + g = state[6]; + h = state[7]; + + j = 0; + do { + BE_8_TO_32(W256[j], data); + data += 4; + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W256[(j+1)&0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j+14)&0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 64); + + /* Compute the current intermediate hash value */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} +#endif /* SHA2_UNROLL_TRANSFORM */ + +void +SHA256Update(SHA2_CTX *context, const u_int8_t *data, size_t len) +{ + u_int64_t freespace, usedspace; + + /* Calling with no data is valid (we do nothing) */ + if (len == 0) + return; + + usedspace = (context->bitcount[0] >> 3) % SHA256_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA256_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + memcpy(&context->buffer[usedspace], data, freespace); + context->bitcount[0] += freespace << 3; + len -= freespace; + data += freespace; + SHA256Transform(context->state.st32, context->buffer); + } else { + /* The buffer is not yet full */ + memcpy(&context->buffer[usedspace], data, len); + context->bitcount[0] += (u_int64_t)len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA256_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA256Transform(context->state.st32, data); + context->bitcount[0] += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + memcpy(context->buffer, data, len); + context->bitcount[0] += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; +} + +void +SHA256Pad(SHA2_CTX *context) +{ + unsigned int usedspace; + + usedspace = (context->bitcount[0] >> 3) % SHA256_BLOCK_LENGTH; + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + memset(&context->buffer[usedspace], 0, + SHA256_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA256_BLOCK_LENGTH) { + memset(&context->buffer[usedspace], 0, + SHA256_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA256Transform(context->state.st32, context->buffer); + + /* Prepare for last transform: */ + memset(context->buffer, 0, SHA256_SHORT_BLOCK_LENGTH); + } + } else { + /* Set-up for the last transform: */ + memset(context->buffer, 0, SHA256_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Store the length of input data (in bits) in big endian format: */ + BE_64_TO_8(&context->buffer[SHA256_SHORT_BLOCK_LENGTH], + context->bitcount[0]); + + /* Final transform: */ + SHA256Transform(context->state.st32, context->buffer); + + /* Clean up: */ + usedspace = 0; +} + +void +SHA256Final(u_int8_t digest[SHA256_DIGEST_LENGTH], SHA2_CTX *context) +{ + SHA256Pad(context); + +#if BYTE_ORDER == LITTLE_ENDIAN + int i; + + /* Convert TO host byte order */ + for (i = 0; i < 8; i++) + BE_32_TO_8(digest + i * 4, context->state.st32[i]); +#else + memcpy(digest, context->state.st32, SHA256_DIGEST_LENGTH); +#endif + //explicit_bzero(context, sizeof(*context)); +} + + +/*** SHA-512: *********************************************************/ +void +SHA512Init(SHA2_CTX *context) +{ + memcpy(context->state.st64, sha512_initial_hash_value, + sizeof(sha512_initial_hash_value)); + memset(context->buffer, 0, sizeof(context->buffer)); + context->bitcount[0] = context->bitcount[1] = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-512 round macros: */ + +#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) do { \ + BE_8_TO_64(W512[j], data); \ + data += 8; \ + T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + W512[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + + +#define ROUND512(a,b,c,d,e,f,g,h) do { \ + s0 = W512[(j+1)&0x0f]; \ + s0 = sigma0_512(s0); \ + s1 = W512[(j+14)&0x0f]; \ + s1 = sigma1_512(s1); \ + T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + \ + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + +void +SHA512Transform(u_int64_t state[8], const u_int8_t data[SHA512_BLOCK_LENGTH]) +{ + u_int64_t a, b, c, d, e, f, g, h, s0, s1; + u_int64_t T1, W512[16]; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + f = state[5]; + g = state[6]; + h = state[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND512_0_TO_15(a,b,c,d,e,f,g,h); + ROUND512_0_TO_15(h,a,b,c,d,e,f,g); + ROUND512_0_TO_15(g,h,a,b,c,d,e,f); + ROUND512_0_TO_15(f,g,h,a,b,c,d,e); + ROUND512_0_TO_15(e,f,g,h,a,b,c,d); + ROUND512_0_TO_15(d,e,f,g,h,a,b,c); + ROUND512_0_TO_15(c,d,e,f,g,h,a,b); + ROUND512_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds up to 79: */ + do { + ROUND512(a,b,c,d,e,f,g,h); + ROUND512(h,a,b,c,d,e,f,g); + ROUND512(g,h,a,b,c,d,e,f); + ROUND512(f,g,h,a,b,c,d,e); + ROUND512(e,f,g,h,a,b,c,d); + ROUND512(d,e,f,g,h,a,b,c); + ROUND512(c,d,e,f,g,h,a,b); + ROUND512(b,c,d,e,f,g,h,a); + } while (j < 80); + + /* Compute the current intermediate hash value */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void +SHA512Transform(u_int64_t state[8], const u_int8_t data[SHA512_BLOCK_LENGTH]) +{ + u_int64_t a, b, c, d, e, f, g, h, s0, s1; + u_int64_t T1, T2, W512[16]; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + f = state[5]; + g = state[6]; + h = state[7]; + + j = 0; + do { + BE_8_TO_64(W512[j], data); + data += 8; + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j]; + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W512[(j+1)&0x0f]; + s0 = sigma0_512(s0); + s1 = W512[(j+14)&0x0f]; + s1 = sigma1_512(s1); + + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 80); + + /* Compute the current intermediate hash value */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void +SHA512Update(SHA2_CTX *context, const u_int8_t *data, size_t len) +{ + size_t freespace, usedspace; + + /* Calling with no data is valid (we do nothing) */ + if (len == 0) + return; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA512_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + memcpy(&context->buffer[usedspace], data, freespace); + ADDINC128(context->bitcount, freespace << 3); + len -= freespace; + data += freespace; + SHA512Transform(context->state.st64, context->buffer); + } else { + /* The buffer is not yet full */ + memcpy(&context->buffer[usedspace], data, len); + ADDINC128(context->bitcount, len << 3); + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA512_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA512Transform(context->state.st64, data); + ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); + len -= SHA512_BLOCK_LENGTH; + data += SHA512_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + memcpy(context->buffer, data, len); + ADDINC128(context->bitcount, len << 3); + } + /* Clean up: */ + usedspace = freespace = 0; +} + +void +SHA512Pad(SHA2_CTX *context) +{ + unsigned int usedspace; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + memset(&context->buffer[usedspace], 0, SHA512_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA512_BLOCK_LENGTH) { + memset(&context->buffer[usedspace], 0, SHA512_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA512Transform(context->state.st64, context->buffer); + + /* And set-up for the last transform: */ + memset(context->buffer, 0, SHA512_BLOCK_LENGTH - 2); + } + } else { + /* Prepare for final transform: */ + memset(context->buffer, 0, SHA512_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Store the length of input data (in bits) in big endian format: */ + BE_64_TO_8(&context->buffer[SHA512_SHORT_BLOCK_LENGTH], + context->bitcount[1]); + BE_64_TO_8(&context->buffer[SHA512_SHORT_BLOCK_LENGTH + 8], + context->bitcount[0]); + + /* Final transform: */ + SHA512Transform(context->state.st64, context->buffer); + + /* Clean up: */ + usedspace = 0; +} + +void +SHA512Final(u_int8_t digest[SHA512_DIGEST_LENGTH], SHA2_CTX *context) +{ + SHA512Pad(context); + +#if BYTE_ORDER == LITTLE_ENDIAN + int i; + + /* Convert TO host byte order */ + for (i = 0; i < 8; i++) + BE_64_TO_8(digest + i * 8, context->state.st64[i]); +#else + memcpy(digest, context->state.st64, SHA512_DIGEST_LENGTH); +#endif + //explicit_bzero(context, sizeof(*context)); +} + +/*** SHA-384: *********************************************************/ +void +SHA384Init(SHA2_CTX *context) +{ + memcpy(context->state.st64, sha384_initial_hash_value, + sizeof(sha384_initial_hash_value)); + memset(context->buffer, 0, sizeof(context->buffer)); + context->bitcount[0] = context->bitcount[1] = 0; +} + +MAKE_CLONE(SHA384Transform, SHA512Transform); +MAKE_CLONE(SHA384Update, SHA512Update); +MAKE_CLONE(SHA384Pad, SHA512Pad); + +/* Equivalent of MAKE_CLONE (which is a no-op) for SHA384 funcs */ +void +SHA384Transform(u_int64_t state[8], const u_int8_t data[SHA512_BLOCK_LENGTH]) +{ + SHA512Transform(state, data); +} + +void +SHA384Update(SHA2_CTX *context, const u_int8_t *data, size_t len) +{ + SHA512Update(context, data, len); +} + +void +SHA384Pad(SHA2_CTX *context) +{ + SHA512Pad(context); +} + +void +SHA384Final(u_int8_t digest[SHA384_DIGEST_LENGTH], SHA2_CTX *context) +{ + SHA384Pad(context); + +#if BYTE_ORDER == LITTLE_ENDIAN + int i; + + /* Convert TO host byte order */ + for (i = 0; i < 6; i++) + BE_64_TO_8(digest + i * 8, context->state.st64[i]); +#else + memcpy(digest, context->state.st64, SHA384_DIGEST_LENGTH); +#endif + /* Zero out state data */ + //explicit_bzero(context, sizeof(*context)); +} diff --git a/src/tls_none.c b/src/tls_none.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2022 Joris Vink <joris@coders.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * An empty TLS backend that does nothing, useful if you do + * not require any TLS stuff in Kore. + */ + +#include <sys/types.h> + +#include "kore.h" + +struct kore_privsep keymgr_privsep; +char *kore_rand_file = NULL; +int kore_keymgr_active = 0; + +int +kore_tls_supported(void) +{ + return (KORE_RESULT_ERROR); +} + +void +kore_keymgr_cleanup(int final) +{ +} + +void +kore_tls_init(void) +{ + kore_log(LOG_ERR, "No compiled in TLS backend"); +} + +void +kore_tls_cleanup(void) +{ +} + +void +kore_tls_dh_check(void) +{ +} + +void +kore_tls_keymgr_init(void) +{ +} + +void +kore_tls_connection_cleanup(struct connection *c) +{ +} + +void +kore_tls_domain_cleanup(struct kore_domain *dom) +{ +} + +void +kore_tls_seed(const void *data, size_t len) +{ +} + +void +kore_keymgr_run(void) +{ + fatal("%s: not supported", __func__); +} + +void +kore_tls_version_set(int version) +{ + fatal("%s: not supported", __func__); +} + +int +kore_tls_dh_load(const char *path) +{ + fatal("%s: not supported", __func__); +} + +int +kore_tls_ciphersuite_set(const char *list) +{ + fatal("%s: not supported", __func__); +} + +void +kore_tls_domain_setup(struct kore_domain *dom, int type, + const void *data, size_t datalen) +{ + fatal("%s: not supported", __func__); +} + +void +kore_tls_domain_crl(struct kore_domain *dom, const void *pem, size_t pemlen) +{ + fatal("%s: not supported", __func__); +} + +int +kore_tls_connection_accept(struct connection *c) +{ + fatal("%s: not supported", __func__); +} + +int +kore_tls_read(struct connection *c, size_t *bytes) +{ + fatal("%s: not supported", __func__); +} + +int +kore_tls_write(struct connection *c, size_t len, size_t *written) +{ + fatal("%s: not supported", __func__); +} + +void * +kore_tls_rsakey_load(const char *path) +{ + fatal("%s: not supported", __func__); +} + +void * +kore_tls_rsakey_generate(const char *path) +{ + fatal("%s: not supported", __func__); +} + +void * +kore_tls_x509_subject_name(struct connection *c) +{ + fatal("%s: not supported", __func__); +} + +void * +kore_tls_x509_issuer_name(struct connection *c) +{ + fatal("%s: not supported", __func__); +} + +int +kore_tls_x509name_foreach(void *name, int flags, void *udata, + int (*cb)(void *, int, int, const char *, const void *, size_t, int)) +{ + fatal("%s: not supported", __func__); +} diff --git a/src/tls_openssl.c b/src/tls_openssl.c @@ -0,0 +1,1156 @@ +/* + * Copyright (c) 2022 Joris Vink <joris@coders.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This TLS backend is the original TLS code used in Kore. + */ + +#include <sys/types.h> + +#include <openssl/bio.h> +#include <openssl/dh.h> +#include <openssl/ec.h> +#include <openssl/ecdsa.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include <openssl/evp.h> +#include <openssl/rand.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> + +#include <poll.h> + +#include "kore.h" +#include "http.h" + +#define TLS_SESSION_ID "kore_tls_sessionid" + +static int tls_domain_x509_verify(int, X509_STORE_CTX *); +static X509 *tls_domain_load_certificate_chain(SSL_CTX *, + const void *, size_t); + +static int tls_sni_cb(SSL *, int *, void *); +static void tls_info_callback(const SSL *, int, int); + +#if defined(KORE_USE_ACME) +static void tls_acme_challenge_set_cert(SSL *, struct kore_domain *); +static int tls_acme_alpn(SSL *, const unsigned char **, unsigned char *, + const unsigned char *, unsigned int, void *); +#endif + +static void tls_keymgr_await_data(void); +static void tls_keymgr_msg_response(struct kore_msg *, const void *); + +static int tls_keymgr_rsa_init(RSA *); +static int tls_keymgr_rsa_finish(RSA *); +static int tls_keymgr_rsa_privenc(int, const unsigned char *, + unsigned char *, RSA *, int); + +static ECDSA_SIG *tls_keymgr_ecdsa_sign(const unsigned char *, int, + const BIGNUM *, const BIGNUM *, EC_KEY *); + +static RSA_METHOD *keymgr_rsa_meth = NULL; +static EC_KEY_METHOD *keymgr_ec_meth = NULL; + +static DH *dh_params = NULL; +static int tls_version = KORE_TLS_VERSION_BOTH; +static char *tls_cipher_list = KORE_DEFAULT_CIPHER_LIST; + +static u_int8_t keymgr_buf[2048]; +static size_t keymgr_buflen = 0; +static int keymgr_response = 0; + +#if defined(KORE_USE_ACME) +static u_int8_t acme_alpn_name[] = + { 0xa, 'a', 'c', 'm', 'e', '-', 't', 'l', 's', '/', '1' }; +#endif + +struct kore_privsep keymgr_privsep; +int kore_keymgr_active = 0; + +int +kore_tls_supported(void) +{ + return (KORE_RESULT_OK); +} + +void +kore_tls_init(void) +{ + SSL_library_init(); + SSL_load_error_strings(); + ERR_load_crypto_strings(); + + if ((keymgr_rsa_meth = RSA_meth_new("kore RSA keymgr method", + RSA_METHOD_FLAG_NO_CHECK)) == NULL) + fatal("failed to setup RSA method"); + + RSA_meth_set_init(keymgr_rsa_meth, tls_keymgr_rsa_init); + RSA_meth_set_finish(keymgr_rsa_meth, tls_keymgr_rsa_finish); + RSA_meth_set_priv_enc(keymgr_rsa_meth, tls_keymgr_rsa_privenc); + + if ((keymgr_ec_meth = EC_KEY_METHOD_new(NULL)) == NULL) + fatal("failed to allocate EC KEY method"); + + EC_KEY_METHOD_set_sign(keymgr_ec_meth, + NULL, NULL, tls_keymgr_ecdsa_sign); + + kore_log(LOG_NOTICE, "TLS backend %s", OPENSSL_VERSION_TEXT); +#if !defined(TLS1_3_VERSION) + if (!kore_quiet) { + kore_log(LOG_NOTICE, + "%s has no TLS 1.3 - will only use TLS 1.2", + OPENSSL_VERSION_TEXT); + } +#endif +} + +void +kore_tls_cleanup(void) +{ + RSA_meth_free(keymgr_rsa_meth); + EC_KEY_METHOD_free(keymgr_ec_meth); +} + +void +kore_tls_version_set(int version) +{ + tls_version = version; +} + +void +kore_tls_dh_check(void) +{ + if (dh_params != NULL) + return; + + if (!kore_tls_dh_load(KORE_DHPARAM_PATH)) + fatal("failed to load default DH parameters"); +} + +int +kore_tls_dh_load(const char *path) +{ + BIO *bio; + + if (dh_params != NULL) { + kore_log(LOG_ERR, "tls_dhparam already specified"); + return (KORE_RESULT_ERROR); + } + + if ((bio = BIO_new_file(path, "r")) == NULL) { + kore_log(LOG_ERR, "tls_dhparam file '%s' not accessible", path); + return (KORE_RESULT_ERROR); + } + + dh_params = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + + if (dh_params == NULL) { + kore_log(LOG_ERR, "PEM_read_bio_DHparams(): %s", ssl_errno_s); + return (KORE_RESULT_ERROR); + } + + return (KORE_RESULT_OK); +} + +int +kore_tls_ciphersuite_set(const char *list) +{ + if (strcmp(tls_cipher_list, KORE_DEFAULT_CIPHER_LIST)) { + kore_log(LOG_ERR, "tls_cipher specified twice"); + return (KORE_RESULT_ERROR); + } + + tls_cipher_list = kore_strdup(list); + + return (KORE_RESULT_OK); +} + +void +kore_tls_keymgr_init(void) +{ + const RSA_METHOD *meth; + + if ((meth = RSA_get_default_method()) == NULL) + fatal("failed to obtain RSA method"); + + RSA_meth_set_pub_enc(keymgr_rsa_meth, RSA_meth_get_pub_enc(meth)); + RSA_meth_set_pub_dec(keymgr_rsa_meth, RSA_meth_get_pub_dec(meth)); + RSA_meth_set_bn_mod_exp(keymgr_rsa_meth, RSA_meth_get_bn_mod_exp(meth)); + + kore_msg_register(KORE_MSG_KEYMGR_RESP, tls_keymgr_msg_response); +} + +void +kore_tls_domain_setup(struct kore_domain *dom, int type, + const void *data, size_t datalen) +{ + const u_int8_t *ptr; + RSA *rsa; + X509 *x509; + EVP_PKEY *pkey; + STACK_OF(X509_NAME) *certs; + EC_KEY *eckey; + const SSL_METHOD *method; + + kore_debug("kore_domain_tlsinit(%s)", dom->domain); + + if (dom->ssl_ctx != NULL) + SSL_CTX_free(dom->ssl_ctx); + + if ((method = TLS_method()) == NULL) + fatalx("TLS_method(): %s", ssl_errno_s); + + if ((dom->ssl_ctx = SSL_CTX_new(method)) == NULL) + fatalx("SSL_ctx_new(): %s", ssl_errno_s); + + if (!SSL_CTX_set_min_proto_version(dom->ssl_ctx, TLS1_2_VERSION)) + fatalx("SSL_CTX_set_min_proto_version: %s", ssl_errno_s); + +#if defined(TLS1_3_VERSION) + if (!SSL_CTX_set_max_proto_version(dom->ssl_ctx, TLS1_3_VERSION)) + fatalx("SSL_CTX_set_max_proto_version: %s", ssl_errno_s); +#else + if (!SSL_CTX_set_max_proto_version(dom->ssl_ctx, TLS1_2_VERSION)) + fatalx("SSL_CTX_set_min_proto_version: %s", ssl_errno_s); +#endif + + switch (tls_version) { + case KORE_TLS_VERSION_1_3: +#if defined(TLS1_3_VERSION) + if (!SSL_CTX_set_min_proto_version(dom->ssl_ctx, + TLS1_3_VERSION)) { + fatalx("SSL_CTX_set_min_proto_version: %s", + ssl_errno_s); + } + break; +#endif + case KORE_TLS_VERSION_1_2: + if (!SSL_CTX_set_max_proto_version(dom->ssl_ctx, + TLS1_2_VERSION)) { + fatalx("SSL_CTX_set_min_proto_version: %s", + ssl_errno_s); + } + break; + case KORE_TLS_VERSION_BOTH: + break; + default: + fatalx("unknown tls_version: %d", tls_version); + return; + } + + switch (type) { + case KORE_PEM_CERT_CHAIN: + x509 = tls_domain_load_certificate_chain(dom->ssl_ctx, + data, datalen); + break; + case KORE_DER_CERT_DATA: + ptr = data; + if ((x509 = d2i_X509(NULL, &ptr, datalen)) == NULL) + fatalx("d2i_X509: %s", ssl_errno_s); + if (SSL_CTX_use_certificate(dom->ssl_ctx, x509) == 0) + fatalx("SSL_CTX_use_certificate: %s", ssl_errno_s); + break; + default: + fatalx("%s: unknown type %d", __func__, type); + } + + if (x509 == NULL) { + kore_log(LOG_NOTICE, "failed to load certificate for '%s': %s", + dom->domain, ssl_errno_s); + SSL_CTX_free(dom->ssl_ctx); + dom->ssl_ctx = NULL; + return; + } + + if ((pkey = X509_get_pubkey(x509)) == NULL) + fatalx("certificate has no public key"); + + switch (EVP_PKEY_id(pkey)) { + case EVP_PKEY_RSA: + if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) + fatalx("no RSA public key present"); + RSA_set_app_data(rsa, dom); + RSA_set_method(rsa, keymgr_rsa_meth); + break; + case EVP_PKEY_EC: + if ((eckey = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) + fatalx("no EC public key present"); + EC_KEY_set_ex_data(eckey, 0, dom); + EC_KEY_set_method(eckey, keymgr_ec_meth); + break; + default: + fatalx("unknown public key in certificate"); + } + + if (!SSL_CTX_use_PrivateKey(dom->ssl_ctx, pkey)) + fatalx("SSL_CTX_use_PrivateKey(): %s", ssl_errno_s); + + if (!SSL_CTX_check_private_key(dom->ssl_ctx)) { + fatalx("Public/Private key for %s do not match (%s)", + dom->domain, ssl_errno_s); + } + + if (dh_params == NULL) + fatal("no DH parameters specified"); + + SSL_CTX_set_tmp_dh(dom->ssl_ctx, dh_params); + SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_SINGLE_DH_USE); + + if (!SSL_CTX_set_ecdh_auto(dom->ssl_ctx, 1)) + fatalx("SSL_CTX_set_ecdh_auto: %s", ssl_errno_s); + + SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_SINGLE_ECDH_USE); + SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_COMPRESSION); + + if (dom->cafile != NULL) { + if ((certs = SSL_load_client_CA_file(dom->cafile)) == NULL) { + fatalx("SSL_load_client_CA_file(%s): %s", + dom->cafile, ssl_errno_s); + } + + SSL_CTX_load_verify_locations(dom->ssl_ctx, dom->cafile, NULL); + SSL_CTX_set_verify_depth(dom->ssl_ctx, dom->x509_verify_depth); + SSL_CTX_set_client_CA_list(dom->ssl_ctx, certs); + SSL_CTX_set_verify(dom->ssl_ctx, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, tls_domain_x509_verify); + } + + SSL_CTX_set_session_id_context(dom->ssl_ctx, + (unsigned char *)TLS_SESSION_ID, strlen(TLS_SESSION_ID)); + SSL_CTX_set_mode(dom->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); + + if (tls_version == KORE_TLS_VERSION_BOTH) { + SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_SSLv2); + SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_SSLv3); + SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_TLSv1); + SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_TLSv1_1); + } + + SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + SSL_CTX_set_cipher_list(dom->ssl_ctx, tls_cipher_list); + + SSL_CTX_set_info_callback(dom->ssl_ctx, tls_info_callback); + SSL_CTX_set_tlsext_servername_callback(dom->ssl_ctx, tls_sni_cb); + +#if defined(KORE_USE_ACME) + SSL_CTX_set_alpn_select_cb(dom->ssl_ctx, tls_acme_alpn, dom); +#endif + + X509_free(x509); +} + +void +kore_tls_domain_crl(struct kore_domain *dom, const void *pem, size_t pemlen) +{ + int err; + BIO *in; + X509_CRL *crl; + X509_STORE *store; + + ERR_clear_error(); + in = BIO_new_mem_buf(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); +} + +void +kore_tls_domain_cleanup(struct kore_domain *dom) +{ + if (dom->ssl_ctx != NULL) + SSL_CTX_free(dom->ssl_ctx); +} + +int +kore_tls_connection_accept(struct connection *c) +{ + int r; + + if (primary_dom == NULL) { + kore_log(LOG_NOTICE, + "TLS handshake but no TLS configured on server"); + return (KORE_RESULT_ERROR); + } + + if (primary_dom->ssl_ctx == NULL) { + kore_log(LOG_NOTICE, + "TLS configuration for %s not yet complete", + primary_dom->domain); + return (KORE_RESULT_ERROR); + } + + if (c->ssl == NULL) { + c->ssl = SSL_new(primary_dom->ssl_ctx); + if (c->ssl == NULL) + return (KORE_RESULT_ERROR); + + SSL_set_fd(c->ssl, c->fd); + SSL_set_accept_state(c->ssl); + + if (!SSL_set_ex_data(c->ssl, 0, c)) + return (KORE_RESULT_ERROR); + + if (primary_dom->cafile != NULL) + c->flags |= CONN_LOG_TLS_FAILURE; + } + + ERR_clear_error(); + r = SSL_accept(c->ssl); + if (r <= 0) { + r = SSL_get_error(c->ssl, r); + switch (r) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + kore_connection_start_idletimer(c); + return (KORE_RESULT_OK); + default: + if (c->flags & CONN_LOG_TLS_FAILURE) { + kore_log(LOG_NOTICE, + "SSL_accept: %s", ssl_errno_s); + } + return (KORE_RESULT_ERROR); + } + } + +#if defined(KORE_USE_ACME) + if (c->proto == CONN_PROTO_ACME_ALPN) { + kore_log(LOG_INFO, "disconnecting acme client"); + kore_connection_disconnect(c); + return (KORE_RESULT_OK); + } +#endif + + if (SSL_get_verify_mode(c->ssl) & SSL_VERIFY_PEER) { + c->cert = SSL_get_peer_certificate(c->ssl); + if (c->cert == NULL) { + kore_log(LOG_NOTICE, "no peer certificate"); + return (KORE_RESULT_ERROR); + } + } else { + c->cert = NULL; + } + + return (KORE_RESULT_OK); +} + +int +kore_tls_read(struct connection *c, size_t *bytes) +{ + int r; + + ERR_clear_error(); + r = SSL_read(c->ssl, (c->rnb->buf + c->rnb->s_off), + (c->rnb->b_len - c->rnb->s_off)); + + if (c->tls_reneg > 1) + return (KORE_RESULT_ERROR); + + if (r <= 0) { + r = SSL_get_error(c->ssl, r); + switch (r) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + c->evt.flags &= ~KORE_EVENT_READ; + return (KORE_RESULT_OK); + case SSL_ERROR_ZERO_RETURN: + return (KORE_RESULT_ERROR); + case SSL_ERROR_SYSCALL: + switch (errno) { + case EINTR: + *bytes = 0; + return (KORE_RESULT_OK); + case EAGAIN: + c->evt.flags &= ~KORE_EVENT_READ; + c->snb->flags |= NETBUF_MUST_RESEND; + return (KORE_RESULT_OK); + default: + break; + } + /* FALLTHROUGH */ + default: + kore_debug("SSL_read(): %s", ssl_errno_s); + if (c->flags & CONN_LOG_TLS_FAILURE) { + kore_log(LOG_NOTICE, + "SSL_read(): %s", ssl_errno_s); + } + return (KORE_RESULT_ERROR); + } + } + + *bytes = (size_t)r; + + return (KORE_RESULT_OK); +} + +int +kore_tls_write(struct connection *c, size_t len, size_t *written) +{ + int r; + + if (len > INT_MAX) + return (KORE_RESULT_ERROR); + + ERR_clear_error(); + r = SSL_write(c->ssl, (c->snb->buf + c->snb->s_off), len); + if (c->tls_reneg > 1) + return (KORE_RESULT_ERROR); + + if (r <= 0) { + r = SSL_get_error(c->ssl, r); + switch (r) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + c->evt.flags &= ~KORE_EVENT_WRITE; + c->snb->flags |= NETBUF_MUST_RESEND; + return (KORE_RESULT_OK); + case SSL_ERROR_SYSCALL: + switch (errno) { + case EINTR: + *written = 0; + return (KORE_RESULT_OK); + case EAGAIN: + c->evt.flags &= ~KORE_EVENT_WRITE; + c->snb->flags |= NETBUF_MUST_RESEND; + return (KORE_RESULT_OK); + default: + break; + } + /* FALLTHROUGH */ + default: + kore_debug("SSL_write(): %s", ssl_errno_s); + if (c->flags & CONN_LOG_TLS_FAILURE) { + kore_log(LOG_NOTICE, + "SSL_write(): %s", ssl_errno_s); + } + return (KORE_RESULT_ERROR); + } + } + + *written = (size_t)r; + + return (KORE_RESULT_OK); +} + +void +kore_tls_connection_cleanup(struct connection *c) +{ + if (c->ssl != NULL) { + SSL_shutdown(c->ssl); + SSL_free(c->ssl); + } + + if (c->cert != NULL) + X509_free(c->cert); + + if (c->tls_sni != NULL) + kore_free(c->tls_sni); +} + + +void * +kore_tls_rsakey_load(const char *path) +{ + FILE *fp; + EVP_PKEY *pkey; + + if (access(path, R_OK) == -1) + return (NULL); + + if ((fp = fopen(path, "r")) == NULL) + fatalx("%s(%s): %s", __func__, path, errno_s); + + if ((pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) + fatalx("PEM_read_PrivateKey: %s", ssl_errno_s); + + fclose(fp); + + return (pkey); +} + +void * +kore_tls_rsakey_generate(const char *path) +{ + FILE *fp; + EVP_PKEY_CTX *ctx; + EVP_PKEY *pkey; + + if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) == NULL) + fatalx("EVP_PKEY_CTX_new_id: %s", ssl_errno_s); + + if (EVP_PKEY_keygen_init(ctx) <= 0) + fatalx("EVP_PKEY_keygen_init: %s", ssl_errno_s); + + if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, KORE_RSAKEY_BITS) <= 0) + fatalx("EVP_PKEY_CTX_set_rsa_keygen_bits: %s", ssl_errno_s); + + pkey = NULL; + if (EVP_PKEY_keygen(ctx, &pkey) <= 0) + fatalx("EVP_PKEY_keygen: %s", ssl_errno_s); + + if (path != NULL) { + if ((fp = fopen(path, "w")) == NULL) + fatalx("fopen(%s): %s", path, errno_s); + + if (!PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL)) + fatalx("PEM_write_PrivateKey: %s", ssl_errno_s); + + fclose(fp); + } + + return (pkey); +} + +void * +kore_tls_x509_subject_name(struct connection *c) +{ + return (X509_get_subject_name(c->cert)); +} + +void * +kore_tls_x509_issuer_name(struct connection *c) +{ + return (X509_get_issuer_name(c->cert)); +} + +int +kore_tls_x509name_foreach(void *name, int flags, void *udata, + int (*cb)(void *, int, int, const char *, const void *, size_t, int)) +{ + u_int8_t *data; + ASN1_STRING *astr; + X509_NAME_ENTRY *entry; + const char *field; + int islast, ret, idx, namelen, nid, len; + + data = NULL; + ret = KORE_RESULT_ERROR; + + if ((namelen = X509_NAME_entry_count(name)) == 0) + goto cleanup; + + for (idx = 0; idx < namelen; idx++) { + if ((entry = X509_NAME_get_entry(name, idx)) == NULL) + goto cleanup; + + nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry)); + if ((field = OBJ_nid2sn(nid)) == NULL) + goto cleanup; + + switch (nid) { + case NID_commonName: + nid = KORE_X509_NAME_COMMON_NAME; + break; + default: + nid = -1; + break; + } + + if ((astr = X509_NAME_ENTRY_get_data(entry)) == NULL) + goto cleanup; + + data = NULL; + if ((len = ASN1_STRING_to_UTF8(&data, astr)) < 0) + goto cleanup; + + if (idx != (namelen - 1)) + islast = 0; + else + islast = 1; + + if (!cb(udata, islast, nid, field, data, len, flags)) + goto cleanup; + + OPENSSL_free(data); + data = NULL; + } + + ret = KORE_RESULT_OK; + +cleanup: + if (data != NULL) + OPENSSL_free(data); + + return (ret); +} + +void +kore_tls_seed(const void *data, size_t len) +{ + RAND_poll(); + RAND_seed(data, len); +} + +static void +tls_info_callback(const SSL *ssl, int flags, int ret) +{ + struct connection *c; + + if (flags & SSL_CB_HANDSHAKE_START) { + if ((c = SSL_get_app_data(ssl)) == NULL) + fatal("no SSL_get_app_data"); + +#if defined(TLS1_3_VERSION) + if (SSL_version(ssl) != TLS1_3_VERSION) +#endif + c->tls_reneg++; + } +} + +static int +tls_sni_cb(SSL *ssl, int *ad, void *arg) +{ + struct connection *c; + struct kore_domain *dom; + const char *sname; + + if ((c = SSL_get_ex_data(ssl, 0)) == NULL) + fatal("no connection data in %s", __func__); + + sname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + kore_debug("kore_tls_sni_cb(): received host %s", sname); + + if (sname != NULL) + c->tls_sni = kore_strdup(sname); + + if (sname != NULL && + (dom = kore_domain_lookup(c->owner->server, sname)) != NULL) { + if (dom->ssl_ctx == NULL) { + kore_log(LOG_NOTICE, + "TLS configuration for %s not complete", + dom->domain); + return (SSL_TLSEXT_ERR_NOACK); + } + + kore_debug("kore_ssl_sni_cb(): Using %s CTX", sname); + SSL_set_SSL_CTX(ssl, dom->ssl_ctx); + + if (dom->cafile != NULL) { + SSL_set_verify(ssl, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + c->flags |= CONN_LOG_TLS_FAILURE; + } else { + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); + } + +#if defined(KORE_USE_ACME) + /* + * If ALPN callback was called before SNI was parsed we + * must make sure we swap to the correct certificate now. + */ + if (c->flags & CONN_TLS_ALPN_ACME_SEEN) + tls_acme_challenge_set_cert(ssl, dom); + + c->flags |= CONN_TLS_SNI_SEEN; +#endif + return (SSL_TLSEXT_ERR_OK); + } + + return (SSL_TLSEXT_ERR_NOACK); +} + +static int +tls_keymgr_rsa_init(RSA *rsa) +{ + if (rsa != NULL) { + RSA_set_flags(rsa, RSA_flags(rsa) | + RSA_FLAG_EXT_PKEY | RSA_METHOD_FLAG_NO_CHECK); + return (1); + } + + return (0); +} + +static int +tls_keymgr_rsa_privenc(int flen, const unsigned char *from, unsigned char *to, + RSA *rsa, int padding) +{ + int ret; + size_t len; + struct kore_keyreq *req; + struct kore_domain *dom; + + len = sizeof(*req) + flen; + if (len > sizeof(keymgr_buf)) + fatal("keymgr_buf too small"); + + if ((dom = RSA_get_app_data(rsa)) == NULL) + fatal("RSA key has no domain attached"); + + memset(keymgr_buf, 0, sizeof(keymgr_buf)); + + req = (struct kore_keyreq *)keymgr_buf; + + if (kore_strlcpy(req->domain, dom->domain, sizeof(req->domain)) >= + sizeof(req->domain)) + fatal("%s: domain truncated", __func__); + + req->data_len = flen; + req->padding = padding; + memcpy(&req->data[0], from, req->data_len); + + kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_KEYMGR_REQ, keymgr_buf, len); + tls_keymgr_await_data(); + + ret = -1; + if (keymgr_response) { + if (keymgr_buflen < INT_MAX && + (int)keymgr_buflen == RSA_size(rsa)) { + ret = RSA_size(rsa); + memcpy(to, keymgr_buf, RSA_size(rsa)); + } + } + + keymgr_buflen = 0; + keymgr_response = 0; + kore_platform_event_all(worker->msg[1]->fd, worker->msg[1]); + + return (ret); +} + +static int +tls_keymgr_rsa_finish(RSA *rsa) +{ + return (1); +} + +static ECDSA_SIG * +tls_keymgr_ecdsa_sign(const unsigned char *dgst, int dgst_len, + const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *eckey) +{ + size_t len; + ECDSA_SIG *sig; + const u_int8_t *ptr; + struct kore_domain *dom; + struct kore_keyreq *req; + + if (in_kinv != NULL || in_r != NULL) + return (NULL); + + len = sizeof(*req) + dgst_len; + if (len > sizeof(keymgr_buf)) + fatal("keymgr_buf too small"); + + if ((dom = EC_KEY_get_ex_data(eckey, 0)) == NULL) + fatal("EC_KEY has no domain"); + + memset(keymgr_buf, 0, sizeof(keymgr_buf)); + req = (struct kore_keyreq *)keymgr_buf; + + if (kore_strlcpy(req->domain, dom->domain, sizeof(req->domain)) >= + sizeof(req->domain)) + fatal("%s: domain truncated", __func__); + + req->data_len = dgst_len; + memcpy(&req->data[0], dgst, req->data_len); + + kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_KEYMGR_REQ, keymgr_buf, len); + tls_keymgr_await_data(); + + if (keymgr_response) { + ptr = keymgr_buf; + sig = d2i_ECDSA_SIG(NULL, &ptr, keymgr_buflen); + } else { + sig = NULL; + } + + keymgr_buflen = 0; + keymgr_response = 0; + kore_platform_event_all(worker->msg[1]->fd, worker->msg[1]); + + return (sig); +} + +static void +tls_keymgr_await_data(void) +{ + int ret; + struct pollfd pfd[1]; + u_int64_t start, cur; +#if !defined(KORE_NO_HTTP) + int process_requests; +#endif + + /* + * We need to wait until the keymgr responds to us, so keep doing + * net_recv_flush() until our callback for KORE_MSG_KEYMGR_RESP + * tells us that we have obtained the response. + * + * This means other internal messages can still be delivered by + * this worker process to the appropriate callbacks but we do not + * drop out until we've either received an answer from the keymgr + * or until the timeout has been reached (1 second currently). + * + * If we end up waiting for the keymgr process we will call + * http_process (if not built with NOHTTP=1) to further existing + * requests so those do not block too much. + * + * This means that all incoming data will stop being processed + * while existing requests will get processed until we return + * from this call. + */ + start = kore_time_ms(); + kore_platform_disable_read(worker->msg[1]->fd); + + keymgr_response = 0; + memset(keymgr_buf, 0, sizeof(keymgr_buf)); + +#if !defined(KORE_NO_HTTP) + process_requests = 0; +#endif + + for (;;) { +#if !defined(KORE_NO_HTTP) + if (process_requests) { + http_process(); + process_requests = 0; + } +#endif + pfd[0].fd = worker->msg[1]->fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + + ret = poll(pfd, 1, 100); + if (ret == -1) { + if (errno == EINTR) + continue; + fatal("poll: %s", errno_s); + } + + cur = kore_time_ms(); + if ((cur - start) > 1000) + break; + if (ret == 0) { +#if !defined(KORE_NO_HTTP) + /* No activity on channel, process HTTP requests. */ + process_requests = 1; +#endif + continue; + } + + if (pfd[0].revents & (POLLERR | POLLHUP)) + break; + if (!(pfd[0].revents & POLLIN)) + break; + + worker->msg[1]->evt.flags |= KORE_EVENT_READ; + if (!net_recv_flush(worker->msg[1])) + break; + + if (keymgr_response) + break; + +#if !defined(KORE_NO_HTTP) + /* If we've spent 100ms already, process HTTP requests. */ + if ((cur - start) > 100) { + process_requests = 1; + } +#endif + } +} + +static void +tls_keymgr_msg_response(struct kore_msg *msg, const void *data) +{ + keymgr_response = 1; + keymgr_buflen = msg->length; + + if (keymgr_buflen > sizeof(keymgr_buf)) + return; + + memcpy(keymgr_buf, data, keymgr_buflen); +} + +static int +tls_domain_x509_verify(int ok, X509_STORE_CTX *ctx) +{ + X509 *cert; + const char *text; + int error, depth; + + error = X509_STORE_CTX_get_error(ctx); + cert = X509_STORE_CTX_get_current_cert(ctx); + + if (ok == 0 && cert != NULL) { + text = X509_verify_cert_error_string(error); + depth = X509_STORE_CTX_get_error_depth(ctx); + + kore_log(LOG_WARNING, "X509 verification error depth:%d - %s", + depth, text); + + /* Continue on CRL validity errors. */ + switch (error) { + case X509_V_ERR_CRL_HAS_EXPIRED: + case X509_V_ERR_CRL_NOT_YET_VALID: + case X509_V_ERR_UNABLE_TO_GET_CRL: + ok = 1; + break; + } + } + + 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 * +tls_domain_load_certificate_chain(SSL_CTX *ctx, const void *data, size_t len) +{ + unsigned long err; + BIO *in; + X509 *x, *ca; + + ERR_clear_error(); + in = BIO_new_mem_buf(data, len); + + if ((x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL)) == NULL) + return (NULL); + + /* refcount for x509 will go up one. */ + if (SSL_CTX_use_certificate(ctx, x) == 0) + return (NULL); + + 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) + return (NULL); + } + + err = ERR_peek_last_error(); + + if (ERR_GET_LIB(err) != ERR_LIB_PEM || + ERR_GET_REASON(err) != PEM_R_NO_START_LINE) + return (NULL); + + BIO_free(in); + + return (x); +} + +#if defined(KORE_USE_ACME) +static int +tls_acme_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *udata) +{ + struct connection *c; + + if ((c = SSL_get_ex_data(ssl, 0)) == NULL) + fatal("%s: no connection data present", __func__); + + if (inlen != sizeof(acme_alpn_name)) + return (SSL_TLSEXT_ERR_NOACK); + + if (memcmp(acme_alpn_name, in, sizeof(acme_alpn_name))) + return (SSL_TLSEXT_ERR_NOACK); + + *out = in + 1; + *outlen = inlen - 1; + + c->flags |= CONN_TLS_ALPN_ACME_SEEN; + + /* + * If SNI was already done, we can continue, otherwise we mark + * that we saw the right ALPN negotiation on this connection + * and wait for the SNI extension to be parsed. + */ + if (c->flags & CONN_TLS_SNI_SEEN) { + /* SNI was seen, we are on the right domain. */ + tls_acme_challenge_set_cert(ssl, udata); + } + + return (SSL_TLSEXT_ERR_OK); +} + +static void +tls_acme_challenge_set_cert(SSL *ssl, struct kore_domain *dom) +{ + struct connection *c; + const unsigned char *ptr; + X509 *x509; + + if (dom->acme == 0) { + kore_log(LOG_NOTICE, "[%s] ACME not active", dom->domain); + return; + } + + if (dom->acme_challenge == 0) { + kore_log(LOG_NOTICE, + "[%s] ACME auth challenge not active", dom->domain); + return; + } + + kore_log(LOG_INFO, "[%s] acme-tls/1 challenge requested", + dom->domain); + + if ((c = SSL_get_ex_data(ssl, 0)) == NULL) + fatal("%s: no connection data present", __func__); + + ptr = dom->acme_cert; + if ((x509 = d2i_X509(NULL, &ptr, dom->acme_cert_len)) == NULL) + fatal("d2i_X509: %s", ssl_errno_s); + + if (SSL_use_certificate(ssl, x509) == 0) + fatal("SSL_use_certificate: %s", ssl_errno_s); + + SSL_clear_chain_certs(ssl); + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); + + c->proto = CONN_PROTO_ACME_ALPN; +} +#endif /* KORE_USE_ACME */ diff --git a/src/utils.c b/src/utils.c @@ -17,9 +17,6 @@ #include <sys/types.h> #include <sys/time.h> -#include <openssl/evp.h> -#include <openssl/rsa.h> - #include <ctype.h> #include <stdio.h> #include <stdarg.h> @@ -57,7 +54,6 @@ static int utils_base64_decode(const char *, u_int8_t **, static int utils_x509name_tobuf(void *, int, int, const char *, const void *, size_t, int); - static char b64_table[] = \ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -475,59 +471,6 @@ kore_read_line(FILE *fp, char *in, size_t len) return (p); } -EVP_PKEY * -kore_rsakey_load(const char *path) -{ - FILE *fp; - EVP_PKEY *pkey; - - if (access(path, R_OK) == -1) - return (NULL); - - if ((fp = fopen(path, "r")) == NULL) - fatalx("%s(%s): %s", __func__, path, errno_s); - - if ((pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) - fatalx("PEM_read_PrivateKey: %s", ssl_errno_s); - - fclose(fp); - - return (pkey); -} - -EVP_PKEY * -kore_rsakey_generate(const char *path) -{ - FILE *fp; - EVP_PKEY_CTX *ctx; - EVP_PKEY *pkey; - - if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) == NULL) - fatalx("EVP_PKEY_CTX_new_id: %s", ssl_errno_s); - - if (EVP_PKEY_keygen_init(ctx) <= 0) - fatalx("EVP_PKEY_keygen_init: %s", ssl_errno_s); - - if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, KORE_RSAKEY_BITS) <= 0) - fatalx("EVP_PKEY_CTX_set_rsa_keygen_bits: %s", ssl_errno_s); - - pkey = NULL; - if (EVP_PKEY_keygen(ctx, &pkey) <= 0) - fatalx("EVP_PKEY_keygen: %s", ssl_errno_s); - - if (path != NULL) { - if ((fp = fopen(path, "w")) == NULL) - fatalx("fopen(%s): %s", path, errno_s); - - if (!PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL)) - fatalx("PEM_write_PrivateKey: %s", ssl_errno_s); - - fclose(fp); - } - - return (pkey); -} - const char * kore_worker_name(int id) { @@ -552,14 +495,15 @@ int kore_x509_issuer_name(struct connection *c, char **out, int flags) { struct kore_buf buf; - X509_NAME *name; + void *name; - if ((name = X509_get_issuer_name(c->cert)) == NULL) + if ((name = kore_tls_x509_issuer_name(c->cert)) == NULL) return (KORE_RESULT_ERROR); kore_buf_init(&buf, 1024); - if (!kore_x509name_foreach(name, flags, &buf, utils_x509name_tobuf)) { + if (!kore_tls_x509name_foreach(name, flags, &buf, + utils_x509name_tobuf)) { kore_buf_cleanup(&buf); return (KORE_RESULT_ERROR); } @@ -576,14 +520,15 @@ int kore_x509_subject_name(struct connection *c, char **out, int flags) { struct kore_buf buf; - X509_NAME *name; + void *name; - if ((name = X509_get_subject_name(c->cert)) == NULL) + if ((name = kore_tls_x509_subject_name(c)) == NULL) return (KORE_RESULT_ERROR); kore_buf_init(&buf, 1024); - if (!kore_x509name_foreach(name, flags, &buf, utils_x509name_tobuf)) { + if (!kore_tls_x509name_foreach(name, flags, &buf, + utils_x509name_tobuf)) { kore_buf_cleanup(&buf); return (KORE_RESULT_ERROR); } @@ -596,58 +541,6 @@ kore_x509_subject_name(struct connection *c, char **out, int flags) return (KORE_RESULT_OK); } -int -kore_x509name_foreach(X509_NAME *name, int flags, void *udata, - int (*cb)(void *, int, int, const char *, const void *, size_t, int)) -{ - u_int8_t *data; - ASN1_STRING *astr; - X509_NAME_ENTRY *entry; - const char *field; - int islast, ret, idx, namelen, nid, len; - - data = NULL; - ret = KORE_RESULT_ERROR; - - if ((namelen = X509_NAME_entry_count(name)) == 0) - goto cleanup; - - for (idx = 0; idx < namelen; idx++) { - if ((entry = X509_NAME_get_entry(name, idx)) == NULL) - goto cleanup; - - nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry)); - if ((field = OBJ_nid2sn(nid)) == NULL) - goto cleanup; - - if ((astr = X509_NAME_ENTRY_get_data(entry)) == NULL) - goto cleanup; - - data = NULL; - if ((len = ASN1_STRING_to_UTF8(&data, astr)) < 0) - goto cleanup; - - if (idx != (namelen - 1)) - islast = 0; - else - islast = 1; - - if (!cb(udata, islast, nid, field, data, len, flags)) - goto cleanup; - - OPENSSL_free(data); - data = NULL; - } - - ret = KORE_RESULT_OK; - -cleanup: - if (data != NULL) - OPENSSL_free(data); - - return (ret); -} - void fatal(const char *fmt, ...) { @@ -695,7 +588,7 @@ utils_x509name_tobuf(void *udata, int islast, int nid, const char *field, struct kore_buf *buf = udata; if (flags & KORE_X509_COMMON_NAME_ONLY) { - if (nid == NID_commonName) + if (nid == KORE_X509_NAME_COMMON_NAME) kore_buf_append(buf, data, len); } else { kore_buf_appendf(buf, "%s=", field); diff --git a/src/websocket.c b/src/websocket.c @@ -17,13 +17,12 @@ #include <sys/param.h> #include <sys/types.h> -#include <openssl/sha.h> - #include <limits.h> #include <string.h> #include "kore.h" #include "http.h" +#include "sha1.h" #define WEBSOCKET_FRAME_HDR 2 #define WEBSOCKET_MASK_LEN 4 @@ -53,11 +52,11 @@ void kore_websocket_handshake(struct http_request *req, const char *onconnect, const char *onmessage, const char *ondisconnect) { - SHA_CTX sctx; + SHA1_CTX sctx; struct kore_buf *buf; char *base64; const char *key, *version; - u_int8_t digest[SHA_DIGEST_LENGTH]; + u_int8_t digest[SHA1_DIGEST_LENGTH]; if (!http_request_header(req, "sec-websocket-key", &key)) { http_response(req, HTTP_STATUS_BAD_REQUEST, NULL, 0); @@ -79,9 +78,9 @@ kore_websocket_handshake(struct http_request *req, const char *onconnect, buf = kore_buf_alloc(128); kore_buf_appendf(buf, "%s%s", key, WEBSOCKET_SERVER_RESPONSE); - (void)SHA1_Init(&sctx); - (void)SHA1_Update(&sctx, buf->data, buf->offset); - (void)SHA1_Final(digest, &sctx); + SHA1Init(&sctx); + SHA1Update(&sctx, buf->data, buf->offset); + SHA1Final(digest, &sctx); kore_buf_free(buf); diff --git a/src/worker.c b/src/worker.c @@ -23,8 +23,6 @@ #include <sys/resource.h> #include <sys/socket.h> -#include <openssl/rand.h> - #include <fcntl.h> #include <grp.h> #include <pwd.h> @@ -163,7 +161,7 @@ kore_worker_init(void) return (KORE_RESULT_ERROR); } - if (keymgr_active) { + if (kore_keymgr_active) { #if defined(KORE_USE_ACME) /* The ACME process is only started if we need it. */ if (acme_domains) { @@ -491,7 +489,7 @@ kore_worker_entry(struct kore_worker *kw) #endif kore_timer_init(); kore_fileref_init(); - kore_domain_keymgr_init(); + kore_tls_keymgr_init(); quit = 0; had_lock = 0; @@ -501,7 +499,7 @@ kore_worker_entry(struct kore_worker *kw) last_seed = 0; - if (keymgr_active) { + if (kore_keymgr_active) { 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_keymgr_response); @@ -536,7 +534,8 @@ kore_worker_entry(struct kore_worker *kw) for (;;) { now = kore_time_ms(); - if (keymgr_active && (now - last_seed) > KORE_RESEED_TIME) { + if (kore_keymgr_active && + (now - last_seed) > KORE_RESEED_TIME) { kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_ENTROPY_REQ, NULL, 0); last_seed = now; @@ -636,6 +635,7 @@ kore_worker_entry(struct kore_worker *kw) kore_platform_event_cleanup(); kore_connection_cleanup(); kore_domain_cleanup(); + kore_tls_cleanup(); kore_module_cleanup(); #if !defined(KORE_NO_HTTP) http_cleanup(); @@ -1035,8 +1035,7 @@ worker_entropy_recv(struct kore_msg *msg, const void *data) msg->length); } - RAND_poll(); - RAND_seed(data, msg->length); + kore_tls_seed(data, msg->length); } static void @@ -1052,16 +1051,16 @@ worker_keymgr_response(struct kore_msg *msg, const void *data) switch (msg->id) { case KORE_MSG_CERTIFICATE: - kore_domain_tlsinit(dom, KORE_PEM_CERT_CHAIN, + kore_tls_domain_setup(dom, KORE_PEM_CERT_CHAIN, req->data, req->data_len); break; case KORE_MSG_CRL: - kore_domain_crl_add(dom, req->data, req->data_len); + kore_tls_domain_crl(dom, req->data, req->data_len); break; #if defined(KORE_USE_ACME) case KORE_ACME_CHALLENGE_SET_CERT: if (dom->ssl_ctx == NULL) { - kore_domain_tlsinit(dom, KORE_DER_CERT_DATA, + kore_tls_domain_setup(dom, KORE_DER_CERT_DATA, req->data, req->data_len); }