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);
}