2016-06-08 13:55:14 +02:00
|
|
|
/*
|
2021-01-11 23:46:08 +01:00
|
|
|
* Copyright (c) 2017-2021 Joris Vink <joris@coders.se>
|
2016-06-08 13:55:14 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2018-07-11 11:53:56 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
2019-11-06 19:33:53 +01:00
|
|
|
*
|
|
|
|
* 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).
|
2018-07-11 11:53:56 +02:00
|
|
|
*/
|
|
|
|
|
2018-07-11 09:44:29 +02:00
|
|
|
#include <sys/types.h>
|
2019-11-06 19:33:53 +01:00
|
|
|
#include <sys/mman.h>
|
2017-02-28 05:58:04 +01:00
|
|
|
#include <sys/stat.h>
|
2016-06-08 13:55:14 +02:00
|
|
|
|
2016-06-08 16:31:14 +02:00
|
|
|
#include <openssl/evp.h>
|
2019-11-06 19:33:53 +01:00
|
|
|
#include <openssl/rsa.h>
|
2017-02-28 05:58:04 +01:00
|
|
|
#include <openssl/rand.h>
|
2019-11-06 19:33:53 +01:00
|
|
|
#include <openssl/sha.h>
|
|
|
|
#include <openssl/x509.h>
|
|
|
|
#include <openssl/x509v3.h>
|
2016-06-08 13:55:14 +02:00
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
#include <ctype.h>
|
2017-02-28 05:58:04 +01:00
|
|
|
#include <fcntl.h>
|
2016-06-08 13:55:14 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2019-05-09 20:55:49 +02:00
|
|
|
#include <stdint.h>
|
2016-06-08 13:55:14 +02:00
|
|
|
#include <signal.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "kore.h"
|
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
#if defined(KORE_USE_ACME)
|
|
|
|
#include "acme.h"
|
|
|
|
#endif
|
|
|
|
|
2017-02-28 05:58:04 +01:00
|
|
|
#define RAND_TMP_FILE "rnd.tmp"
|
|
|
|
#define RAND_POLL_INTERVAL (1800 * 1000)
|
|
|
|
#define RAND_FILE_SIZE 1024
|
|
|
|
|
2019-09-25 14:25:49 +02:00
|
|
|
#if defined(__linux__)
|
|
|
|
#include "seccomp.h"
|
|
|
|
|
|
|
|
/* The syscalls our keymgr is allowed to perform, only. */
|
|
|
|
static struct sock_filter filter_keymgr[] = {
|
2019-11-06 19:33:53 +01:00
|
|
|
/* Deny these, but with EACCESS instead of dying. */
|
|
|
|
KORE_SYSCALL_DENY(ioctl, EACCES),
|
|
|
|
|
2019-09-25 14:25:49 +02:00
|
|
|
/* Required to deal with private keys and certs. */
|
2019-11-05 13:12:43 +01:00
|
|
|
#if defined(SYS_open)
|
2019-09-25 14:25:49 +02:00
|
|
|
KORE_SYSCALL_ALLOW(open),
|
2019-11-05 13:12:43 +01:00
|
|
|
#endif
|
2019-09-25 14:25:49 +02:00
|
|
|
KORE_SYSCALL_ALLOW(read),
|
2019-11-06 19:33:53 +01:00
|
|
|
KORE_SYSCALL_ALLOW(lseek),
|
2019-09-26 09:52:31 +02:00
|
|
|
KORE_SYSCALL_ALLOW(write),
|
2019-09-25 14:25:49 +02:00
|
|
|
KORE_SYSCALL_ALLOW(close),
|
2020-06-09 17:07:23 +02:00
|
|
|
#if defined(SYS_stat)
|
2020-02-13 14:32:44 +01:00
|
|
|
KORE_SYSCALL_ALLOW(stat),
|
2020-06-09 17:07:23 +02:00
|
|
|
#endif
|
2019-09-25 14:40:44 +02:00
|
|
|
KORE_SYSCALL_ALLOW(fstat),
|
2020-09-17 12:17:57 +02:00
|
|
|
#if defined(SYS_fstat64)
|
|
|
|
KORE_SYSCALL_ALLOW(fstat64),
|
|
|
|
#endif
|
2019-09-25 14:40:44 +02:00
|
|
|
KORE_SYSCALL_ALLOW(futex),
|
2019-09-26 10:02:58 +02:00
|
|
|
KORE_SYSCALL_ALLOW(writev),
|
2019-09-25 14:40:44 +02:00
|
|
|
KORE_SYSCALL_ALLOW(openat),
|
2019-11-15 09:28:33 +01:00
|
|
|
#if defined(SYS_access)
|
|
|
|
KORE_SYSCALL_ALLOW(access),
|
|
|
|
#endif
|
|
|
|
KORE_SYSCALL_ALLOW(faccessat),
|
2019-09-25 14:25:49 +02:00
|
|
|
|
2019-09-25 14:40:44 +02:00
|
|
|
/* Net related. */
|
2019-11-05 13:12:43 +01:00
|
|
|
#if defined(SYS_poll)
|
2019-09-25 15:04:12 +02:00
|
|
|
KORE_SYSCALL_ALLOW(poll),
|
2020-09-17 12:17:57 +02:00
|
|
|
#endif
|
|
|
|
#if defined(SYS_send)
|
|
|
|
KORE_SYSCALL_ALLOW(send),
|
2019-11-05 13:12:43 +01:00
|
|
|
#endif
|
2019-09-26 09:52:31 +02:00
|
|
|
KORE_SYSCALL_ALLOW(sendto),
|
2020-09-17 12:17:57 +02:00
|
|
|
#if defined(SYS_recv)
|
|
|
|
KORE_SYSCALL_ALLOW(recv),
|
|
|
|
#endif
|
2019-09-26 09:52:31 +02:00
|
|
|
KORE_SYSCALL_ALLOW(recvfrom),
|
2019-11-05 13:12:43 +01:00
|
|
|
#if defined(SYS_epoll_wait)
|
2019-09-25 14:40:44 +02:00
|
|
|
KORE_SYSCALL_ALLOW(epoll_wait),
|
2019-11-05 13:12:43 +01:00
|
|
|
#endif
|
2019-09-26 10:20:30 +02:00
|
|
|
KORE_SYSCALL_ALLOW(epoll_pwait),
|
2019-09-25 14:25:49 +02:00
|
|
|
|
|
|
|
/* Process things. */
|
|
|
|
KORE_SYSCALL_ALLOW(exit),
|
2019-09-25 14:40:44 +02:00
|
|
|
KORE_SYSCALL_ALLOW(kill),
|
2019-09-25 15:04:12 +02:00
|
|
|
KORE_SYSCALL_ALLOW(getuid),
|
2019-09-25 14:40:44 +02:00
|
|
|
KORE_SYSCALL_ALLOW(getpid),
|
2019-11-05 13:12:43 +01:00
|
|
|
#if defined(SYS_arch_prctl)
|
2019-09-25 14:40:44 +02:00
|
|
|
KORE_SYSCALL_ALLOW(arch_prctl),
|
2019-11-05 13:12:43 +01:00
|
|
|
#endif
|
2019-09-25 14:40:44 +02:00
|
|
|
KORE_SYSCALL_ALLOW(exit_group),
|
2019-09-25 14:25:49 +02:00
|
|
|
KORE_SYSCALL_ALLOW(sigaltstack),
|
2020-09-17 12:17:57 +02:00
|
|
|
#if defined(SYS_sigreturn)
|
|
|
|
KORE_SYSCALL_ALLOW(sigreturn),
|
|
|
|
#endif
|
2019-09-25 14:40:44 +02:00
|
|
|
KORE_SYSCALL_ALLOW(rt_sigreturn),
|
2019-09-25 14:25:49 +02:00
|
|
|
KORE_SYSCALL_ALLOW(rt_sigaction),
|
2019-09-26 10:06:32 +02:00
|
|
|
KORE_SYSCALL_ALLOW(rt_sigprocmask),
|
2019-09-25 14:40:44 +02:00
|
|
|
|
|
|
|
/* Other things. */
|
2019-09-26 10:20:30 +02:00
|
|
|
KORE_SYSCALL_ALLOW(brk),
|
2020-09-17 12:16:13 +02:00
|
|
|
#if defined(SYS_mmap)
|
2019-09-25 15:04:12 +02:00
|
|
|
KORE_SYSCALL_ALLOW(mmap),
|
2020-09-17 12:17:57 +02:00
|
|
|
#endif
|
|
|
|
#if defined(SYS_mmap2)
|
|
|
|
KORE_SYSCALL_ALLOW(mmap2),
|
2021-05-10 14:51:30 +02:00
|
|
|
#endif
|
|
|
|
#if defined(SYS_madvise)
|
|
|
|
KORE_SYSCALL_ALLOW(madvise),
|
2020-09-17 12:16:13 +02:00
|
|
|
#endif
|
2019-09-25 14:40:44 +02:00
|
|
|
KORE_SYSCALL_ALLOW(munmap),
|
2019-10-03 15:55:19 +02:00
|
|
|
KORE_SYSCALL_ALLOW(clock_gettime),
|
2019-09-25 15:04:12 +02:00
|
|
|
#if defined(__NR_getrandom)
|
2019-09-25 14:40:44 +02:00
|
|
|
KORE_SYSCALL_ALLOW(getrandom),
|
2019-09-25 15:04:12 +02:00
|
|
|
#endif
|
2019-11-06 19:33:53 +01:00
|
|
|
|
|
|
|
#if defined(KORE_USE_ACME)
|
|
|
|
#if defined(SYS_mkdir)
|
|
|
|
KORE_SYSCALL_ALLOW(mkdir),
|
|
|
|
#endif
|
|
|
|
KORE_SYSCALL_ALLOW(mkdirat),
|
|
|
|
KORE_SYSCALL_ALLOW(umask),
|
|
|
|
#endif
|
2019-09-25 14:25:49 +02:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2016-06-08 13:55:14 +02:00
|
|
|
struct key {
|
2016-06-08 16:31:14 +02:00
|
|
|
EVP_PKEY *pkey;
|
2016-06-08 13:55:14 +02:00
|
|
|
struct kore_domain *dom;
|
|
|
|
TAILQ_ENTRY(key) list;
|
|
|
|
};
|
|
|
|
|
2017-02-28 05:58:04 +01:00
|
|
|
char *rand_file = NULL;
|
|
|
|
|
2016-06-08 13:55:14 +02:00
|
|
|
static TAILQ_HEAD(, key) keys;
|
|
|
|
static int initialized = 0;
|
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
#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;
|
|
|
|
|
2019-11-07 07:56:13 +01:00
|
|
|
/* oid for acme extension. */
|
|
|
|
static int acme_oid = -1;
|
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
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 */
|
|
|
|
|
2018-07-11 09:44:29 +02:00
|
|
|
static void keymgr_reload(void);
|
2017-02-28 05:58:04 +01:00
|
|
|
static void keymgr_load_randfile(void);
|
|
|
|
static void keymgr_save_randfile(void);
|
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
static struct key *keymgr_load_privatekey(const char *);
|
|
|
|
static void keymgr_load_domain_privatekey(struct kore_domain *);
|
|
|
|
|
2016-06-08 13:55:14 +02:00
|
|
|
static void keymgr_msg_recv(struct kore_msg *, const void *);
|
2017-02-28 06:28:35 +01:00
|
|
|
static void keymgr_entropy_request(struct kore_msg *, const void *);
|
2018-07-11 09:44:29 +02:00
|
|
|
static void keymgr_certificate_request(struct kore_msg *, const void *);
|
|
|
|
static void keymgr_submit_certificates(struct kore_domain *, u_int16_t);
|
2019-01-14 11:41:50 +01:00
|
|
|
static void keymgr_submit_file(u_int8_t, struct kore_domain *,
|
2019-01-14 20:57:40 +01:00
|
|
|
const char *, u_int16_t, int);
|
2019-11-06 19:33:53 +01:00
|
|
|
static void keymgr_x509_msg(const char *, const void *, size_t, int, int);
|
2016-06-08 13:55:14 +02:00
|
|
|
|
2016-06-08 16:31:14 +02:00
|
|
|
static void keymgr_rsa_encrypt(struct kore_msg *, const void *,
|
|
|
|
struct key *);
|
|
|
|
static void keymgr_ecdsa_sign(struct kore_msg *, const void *,
|
|
|
|
struct key *);
|
|
|
|
|
Rework worker startup/privsep config.
Starting with the privsep config, this commit changes the following:
- Removes the root, runas, keymgr_root, keymgr_runas, acme_root and
acme_runas configuration options.
Instead these are now configured via a privsep configuration context:
privsep worker {
root /tmp
runas nobody
}
This is also configurable via Python using the new kore.privsep() method:
kore.privsep("worker", root="/tmp", runas="nobody", skip=["chroot"])
Tied into this we also better handle worker startup:
- Per worker process, wait until it signalled it is ready.
- If a worker fails at startup, display its last log lines more clearly.
- Don't start acme process if no domain requires acme.
- Remove each process its individual startup log message in favour
of a generalized one that displays its PID, root and user.
- At startup, log the kore version and built-ins in a nicer way.
- The worker processes now check things they need to start running
before signaling they are ready (such as access to CA certs for
TLS client authentication).
2021-09-07 21:59:22 +02:00
|
|
|
struct kore_privsep keymgr_privsep;
|
|
|
|
int keymgr_active = 0;
|
2018-07-11 09:44:29 +02:00
|
|
|
|
2019-11-07 08:29:38 +01:00
|
|
|
#if defined(__OpenBSD__)
|
2019-11-07 07:56:13 +01:00
|
|
|
#if defined(KORE_USE_ACME)
|
|
|
|
static const char *keymgr_pledges = "stdio rpath wpath cpath";
|
|
|
|
#else
|
|
|
|
static const char *keymgr_pledges = "stdio rpath";
|
|
|
|
#endif
|
2019-11-07 08:29:38 +01:00
|
|
|
#endif
|
2019-11-07 07:56:13 +01:00
|
|
|
|
2016-06-08 13:55:14 +02:00
|
|
|
void
|
|
|
|
kore_keymgr_run(void)
|
|
|
|
{
|
|
|
|
int quit;
|
2019-11-06 19:33:53 +01:00
|
|
|
u_int64_t now, netwait, last_seed;
|
2017-02-28 05:58:04 +01:00
|
|
|
|
2019-10-07 10:31:35 +02:00
|
|
|
if (keymgr_active == 0)
|
2019-11-06 19:33:53 +01:00
|
|
|
fatalx("%s: called with keymgr_active == 0", __func__);
|
2019-10-07 10:31:35 +02:00
|
|
|
|
2018-07-11 09:44:29 +02:00
|
|
|
quit = 0;
|
|
|
|
|
2019-09-27 20:00:35 +02:00
|
|
|
kore_server_closeall();
|
2018-07-11 09:44:29 +02:00
|
|
|
kore_module_cleanup();
|
2018-08-13 13:07:32 +02:00
|
|
|
|
|
|
|
net_init();
|
2019-11-06 19:33:53 +01:00
|
|
|
kore_timer_init();
|
2018-08-13 13:07:32 +02:00
|
|
|
kore_connection_init();
|
|
|
|
kore_platform_event_init();
|
2019-11-06 19:33:53 +01:00
|
|
|
|
2018-08-13 13:07:32 +02:00
|
|
|
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);
|
|
|
|
|
2019-09-25 14:25:49 +02:00
|
|
|
#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
|
2019-10-16 12:05:27 +02:00
|
|
|
#if defined(KORE_USE_PYTHON)
|
|
|
|
kore_msg_unregister(KORE_PYTHON_SEND_OBJ);
|
|
|
|
#endif
|
Rework worker startup/privsep config.
Starting with the privsep config, this commit changes the following:
- Removes the root, runas, keymgr_root, keymgr_runas, acme_root and
acme_runas configuration options.
Instead these are now configured via a privsep configuration context:
privsep worker {
root /tmp
runas nobody
}
This is also configurable via Python using the new kore.privsep() method:
kore.privsep("worker", root="/tmp", runas="nobody", skip=["chroot"])
Tied into this we also better handle worker startup:
- Per worker process, wait until it signalled it is ready.
- If a worker fails at startup, display its last log lines more clearly.
- Don't start acme process if no domain requires acme.
- Remove each process its individual startup log message in favour
of a generalized one that displays its PID, root and user.
- At startup, log the kore version and built-ins in a nicer way.
- The worker processes now check things they need to start running
before signaling they are ready (such as access to CA certs for
TLS client authentication).
2021-09-07 21:59:22 +02:00
|
|
|
kore_worker_privsep();
|
2019-11-06 19:33:53 +01:00
|
|
|
|
2017-02-28 05:58:04 +01:00
|
|
|
if (rand_file != NULL) {
|
|
|
|
keymgr_load_randfile();
|
2017-03-06 10:55:25 +01:00
|
|
|
keymgr_save_randfile();
|
2019-02-27 19:59:31 +01:00
|
|
|
} else if (!kore_quiet) {
|
2017-02-28 05:58:04 +01:00
|
|
|
kore_log(LOG_WARNING, "no rand_file location specified");
|
|
|
|
}
|
2016-06-08 13:55:14 +02:00
|
|
|
|
2018-07-11 09:44:29 +02:00
|
|
|
RAND_poll();
|
2017-02-28 05:58:04 +01:00
|
|
|
last_seed = 0;
|
2018-07-11 09:44:29 +02:00
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
initialized = 1;
|
|
|
|
keymgr_reload();
|
|
|
|
|
2018-07-14 21:14:02 +02:00
|
|
|
#if defined(__OpenBSD__)
|
2019-11-07 07:56:13 +01:00
|
|
|
if (pledge(keymgr_pledges, NULL) == -1)
|
2019-11-06 19:33:53 +01:00
|
|
|
fatalx("failed to pledge keymgr process");
|
2018-07-14 21:14:02 +02:00
|
|
|
#endif
|
|
|
|
|
2019-11-07 07:56:13 +01:00
|
|
|
#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
|
|
|
|
|
Rework worker startup/privsep config.
Starting with the privsep config, this commit changes the following:
- Removes the root, runas, keymgr_root, keymgr_runas, acme_root and
acme_runas configuration options.
Instead these are now configured via a privsep configuration context:
privsep worker {
root /tmp
runas nobody
}
This is also configurable via Python using the new kore.privsep() method:
kore.privsep("worker", root="/tmp", runas="nobody", skip=["chroot"])
Tied into this we also better handle worker startup:
- Per worker process, wait until it signalled it is ready.
- If a worker fails at startup, display its last log lines more clearly.
- Don't start acme process if no domain requires acme.
- Remove each process its individual startup log message in favour
of a generalized one that displays its PID, root and user.
- At startup, log the kore version and built-ins in a nicer way.
- The worker processes now check things they need to start running
before signaling they are ready (such as access to CA certs for
TLS client authentication).
2021-09-07 21:59:22 +02:00
|
|
|
kore_worker_started();
|
|
|
|
|
2016-06-08 13:55:14 +02:00
|
|
|
while (quit != 1) {
|
2017-02-28 05:58:04 +01:00
|
|
|
now = kore_time_ms();
|
|
|
|
if ((now - last_seed) > RAND_POLL_INTERVAL) {
|
|
|
|
RAND_poll();
|
|
|
|
last_seed = now;
|
|
|
|
}
|
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
netwait = kore_timer_next_run(now);
|
|
|
|
kore_platform_event_wait(netwait);
|
|
|
|
|
2016-06-08 13:55:14 +02:00
|
|
|
if (sig_recv != 0) {
|
|
|
|
switch (sig_recv) {
|
|
|
|
case SIGQUIT:
|
|
|
|
case SIGINT:
|
|
|
|
case SIGTERM:
|
|
|
|
quit = 1;
|
|
|
|
break;
|
2018-07-11 09:44:29 +02:00
|
|
|
case SIGUSR1:
|
|
|
|
keymgr_reload();
|
|
|
|
break;
|
2016-06-08 13:55:14 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sig_recv = 0;
|
|
|
|
}
|
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
if (quit)
|
|
|
|
break;
|
|
|
|
|
|
|
|
now = kore_time_ms();
|
|
|
|
kore_timer_run(now);
|
2016-06-08 13:55:14 +02:00
|
|
|
kore_connection_prune(KORE_CONNECTION_PRUNE_DISCONNECT);
|
|
|
|
}
|
|
|
|
|
2018-07-11 09:44:29 +02:00
|
|
|
kore_keymgr_cleanup(1);
|
2016-06-08 13:55:14 +02:00
|
|
|
kore_platform_event_cleanup();
|
|
|
|
kore_connection_cleanup();
|
|
|
|
net_cleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2018-07-11 09:44:29 +02:00
|
|
|
kore_keymgr_cleanup(int final)
|
2016-06-08 13:55:14 +02:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2016-06-08 16:31:14 +02:00
|
|
|
EVP_PKEY_free(key->pkey);
|
2016-07-12 13:54:14 +02:00
|
|
|
kore_free(key);
|
2016-06-08 13:55:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-11 09:44:29 +02:00
|
|
|
static void
|
|
|
|
keymgr_reload(void)
|
|
|
|
{
|
2019-09-27 20:00:35 +02:00
|
|
|
struct kore_server *srv;
|
2018-07-11 09:44:29 +02:00
|
|
|
struct kore_domain *dom;
|
|
|
|
|
2019-02-27 19:59:31 +01:00
|
|
|
if (!kore_quiet)
|
|
|
|
kore_log(LOG_INFO, "(re)loading certificates, keys and CRLs");
|
2018-07-11 09:44:29 +02:00
|
|
|
|
|
|
|
kore_keymgr_cleanup(0);
|
|
|
|
TAILQ_INIT(&keys);
|
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
#if defined(KORE_USE_ACME)
|
|
|
|
keymgr_acme_init();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
kore_domain_callback(keymgr_load_domain_privatekey);
|
2018-07-11 09:44:29 +02:00
|
|
|
|
|
|
|
/* can't use kore_domain_callback() due to dst parameter. */
|
2019-09-27 20:00:35 +02:00
|
|
|
LIST_FOREACH(srv, &kore_servers, list) {
|
|
|
|
if (srv->tls == 0)
|
2019-09-27 12:22:35 +02:00
|
|
|
continue;
|
2019-09-27 20:00:35 +02:00
|
|
|
TAILQ_FOREACH(dom, &srv->domains, list)
|
2019-09-27 12:22:35 +02:00
|
|
|
keymgr_submit_certificates(dom, KORE_MSG_WORKER_ALL);
|
|
|
|
}
|
2018-07-11 09:44:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
keymgr_submit_certificates(struct kore_domain *dom, u_int16_t dst)
|
2019-01-14 11:41:50 +01:00
|
|
|
{
|
2019-11-06 19:33:53 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-01-14 20:57:40 +01:00
|
|
|
keymgr_submit_file(KORE_MSG_CERTIFICATE, dom, dom->certfile, dst, 0);
|
2019-01-14 11:41:50 +01:00
|
|
|
|
2019-01-14 20:57:40 +01:00
|
|
|
if (dom->crlfile != NULL)
|
|
|
|
keymgr_submit_file(KORE_MSG_CRL, dom, dom->crlfile, dst, 1);
|
2019-01-14 11:41:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
keymgr_submit_file(u_int8_t id, struct kore_domain *dom,
|
2019-01-14 20:57:40 +01:00
|
|
|
const char *file, u_int16_t dst, int can_fail)
|
2018-07-11 09:44:29 +02:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
struct stat st;
|
|
|
|
u_int8_t *payload;
|
|
|
|
|
2019-01-14 11:41:50 +01:00
|
|
|
if ((fd = open(file, O_RDONLY)) == -1) {
|
|
|
|
if (errno == ENOENT && can_fail)
|
|
|
|
return;
|
2019-11-06 19:33:53 +01:00
|
|
|
fatalx("open(%s): %s", file, errno_s);
|
2019-01-14 11:41:50 +01:00
|
|
|
}
|
|
|
|
|
2018-07-11 09:44:29 +02:00
|
|
|
if (fstat(fd, &st) == -1)
|
2019-11-06 19:33:53 +01:00
|
|
|
fatalx("stat(%s): %s", file, errno_s);
|
2019-01-14 11:41:50 +01:00
|
|
|
|
2018-07-11 09:44:29 +02:00
|
|
|
if (!S_ISREG(st.st_mode))
|
2019-11-06 19:33:53 +01:00
|
|
|
fatalx("%s is not a file", file);
|
2018-07-11 09:44:29 +02:00
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
payload = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
|
|
|
if (payload == MAP_FAILED)
|
|
|
|
fatalx("mmap(): %s", errno_s);
|
2018-07-11 09:44:29 +02:00
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
keymgr_x509_msg(dom->domain, payload, st.st_size, dst, id);
|
2018-07-11 09:44:29 +02:00
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
(void)munmap(payload, st.st_size);
|
2018-07-11 09:44:29 +02:00
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
2017-02-28 05:58:04 +01:00
|
|
|
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)
|
2019-11-06 19:33:53 +01:00
|
|
|
fatalx("open(%s): %s", rand_file, errno_s);
|
2017-02-28 05:58:04 +01:00
|
|
|
|
|
|
|
if (fstat(fd, &st) == -1)
|
2019-11-06 19:33:53 +01:00
|
|
|
fatalx("stat(%s): %s", rand_file, errno_s);
|
2017-02-28 05:58:04 +01:00
|
|
|
if (!S_ISREG(st.st_mode))
|
2019-11-06 19:33:53 +01:00
|
|
|
fatalx("%s is not a file", rand_file);
|
2017-02-28 05:58:04 +01:00
|
|
|
if (st.st_size != RAND_FILE_SIZE)
|
2019-11-06 19:33:53 +01:00
|
|
|
fatalx("%s has an invalid size", rand_file);
|
2017-02-28 05:58:04 +01:00
|
|
|
|
|
|
|
total = 0;
|
|
|
|
|
|
|
|
while (total != RAND_FILE_SIZE) {
|
|
|
|
ret = read(fd, buf, sizeof(buf));
|
|
|
|
if (ret == 0)
|
2019-11-06 19:33:53 +01:00
|
|
|
fatalx("EOF on %s", rand_file);
|
2017-02-28 05:58:04 +01:00
|
|
|
|
|
|
|
if (ret == -1) {
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
2019-11-06 19:33:53 +01:00
|
|
|
fatalx("read(%s): %s", rand_file, errno_s);
|
2017-02-28 05:58:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
total += (size_t)ret;
|
|
|
|
RAND_seed(buf, (int)ret);
|
2017-02-28 06:05:56 +01:00
|
|
|
OPENSSL_cleanse(buf, sizeof(buf));
|
2017-02-28 05:58:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
(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);
|
2017-02-28 06:05:56 +01:00
|
|
|
goto cleanup;
|
2017-02-28 05:58:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2017-02-28 06:05:56 +01:00
|
|
|
goto cleanup;
|
2017-02-28 05:58:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2017-02-28 06:05:56 +01:00
|
|
|
goto cleanup;
|
2017-02-28 05:58:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2017-02-28 06:05:56 +01:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
OPENSSL_cleanse(buf, sizeof(buf));
|
2017-02-28 05:58:04 +01:00
|
|
|
}
|
|
|
|
|
2016-06-08 13:55:14 +02:00
|
|
|
static void
|
2019-11-06 19:33:53 +01:00
|
|
|
keymgr_load_domain_privatekey(struct kore_domain *dom)
|
2016-06-08 13:55:14 +02:00
|
|
|
{
|
2019-11-06 19:33:53 +01:00
|
|
|
struct key *key;
|
2016-06-08 13:55:14 +02:00
|
|
|
|
2019-09-27 20:00:35 +02:00
|
|
|
if (dom->server->tls == 0)
|
2019-09-27 12:22:35 +02:00
|
|
|
return;
|
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2016-06-08 13:55:14 +02:00
|
|
|
|
|
|
|
key->dom = dom;
|
|
|
|
|
2021-09-06 13:26:54 +02:00
|
|
|
if (!kore_quiet)
|
|
|
|
kore_log(LOG_INFO, "loaded private key for '%s'", dom->domain);
|
2019-11-06 19:33:53 +01:00
|
|
|
}
|
2016-06-08 13:55:14 +02:00
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
static struct key *
|
|
|
|
keymgr_load_privatekey(const char *path)
|
|
|
|
{
|
|
|
|
struct key *key;
|
2016-06-08 13:55:14 +02:00
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
key = kore_calloc(1, sizeof(*key));
|
2016-06-08 13:55:14 +02:00
|
|
|
TAILQ_INSERT_TAIL(&keys, key, list);
|
2019-11-06 19:33:53 +01:00
|
|
|
|
|
|
|
/* Caller should check if pkey was loaded. */
|
|
|
|
if (path)
|
|
|
|
key->pkey = kore_rsakey_load(path);
|
|
|
|
|
|
|
|
return (key);
|
2016-06-08 13:55:14 +02:00
|
|
|
}
|
|
|
|
|
2018-07-11 09:44:29 +02:00
|
|
|
static void
|
|
|
|
keymgr_certificate_request(struct kore_msg *msg, const void *data)
|
|
|
|
{
|
2019-09-27 20:00:35 +02:00
|
|
|
struct kore_server *srv;
|
2018-07-11 09:44:29 +02:00
|
|
|
struct kore_domain *dom;
|
|
|
|
|
2019-09-27 20:00:35 +02:00
|
|
|
LIST_FOREACH(srv, &kore_servers, list) {
|
|
|
|
if (srv->tls == 0)
|
2019-09-27 12:22:35 +02:00
|
|
|
continue;
|
2019-09-27 20:00:35 +02:00
|
|
|
TAILQ_FOREACH(dom, &srv->domains, list)
|
2019-09-27 12:22:35 +02:00
|
|
|
keymgr_submit_certificates(dom, msg->src);
|
|
|
|
}
|
2018-07-11 09:44:29 +02:00
|
|
|
}
|
|
|
|
|
2017-02-28 06:28:35 +01:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2016-06-08 13:55:14 +02:00
|
|
|
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;
|
2018-07-11 09:44:29 +02:00
|
|
|
|
2016-06-08 13:55:14 +02:00
|
|
|
if (msg->length != (sizeof(*req) + req->data_len))
|
|
|
|
return;
|
2019-11-06 19:33:53 +01:00
|
|
|
|
|
|
|
if (req->domain[KORE_DOMAINNAME_LEN] != '\0')
|
2018-07-11 09:44:29 +02:00
|
|
|
return;
|
2016-06-08 13:55:14 +02:00
|
|
|
|
2016-06-08 16:31:14 +02:00
|
|
|
key = NULL;
|
2016-06-08 13:55:14 +02:00
|
|
|
TAILQ_FOREACH(key, &keys, list) {
|
2019-11-06 19:33:53 +01:00
|
|
|
if (key->dom == NULL)
|
|
|
|
continue;
|
|
|
|
if (!strcmp(key->dom->domain, req->domain))
|
2016-06-08 16:31:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2016-06-08 13:55:14 +02:00
|
|
|
|
2016-06-08 16:31:14 +02:00
|
|
|
if (key == NULL)
|
|
|
|
return;
|
2016-06-08 13:55:14 +02:00
|
|
|
|
2019-11-06 19:33:53 +01:00
|
|
|
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;
|
|
|
|
}
|
2016-06-08 16:31:14 +02:00
|
|
|
break;
|
2019-11-06 19:33:53 +01:00
|
|
|
#if defined(KORE_USE_ACME)
|
|
|
|
case KORE_ACME_CSR_REQUEST:
|
|
|
|
keymgr_acme_csr(req, key);
|
2016-06-08 16:31:14 +02:00
|
|
|
break;
|
2019-11-06 19:33:53 +01:00
|
|
|
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);
|
2016-06-08 13:55:14 +02:00
|
|
|
break;
|
2019-11-06 19:33:53 +01:00
|
|
|
case KORE_ACME_INSTALL_CERT:
|
|
|
|
keymgr_acme_install_cert(req->data, req->data_len, key);
|
|
|
|
break;
|
|
|
|
#endif
|
2016-06-08 13:55:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-08 16:31:14 +02:00
|
|
|
static void
|
|
|
|
keymgr_rsa_encrypt(struct kore_msg *msg, const void *data, struct key *key)
|
|
|
|
{
|
|
|
|
int ret;
|
2017-05-22 14:31:38 +02:00
|
|
|
RSA *rsa;
|
2016-06-08 16:31:14 +02:00
|
|
|
const struct kore_keyreq *req;
|
|
|
|
size_t keylen;
|
|
|
|
u_int8_t buf[1024];
|
|
|
|
|
|
|
|
req = (const struct kore_keyreq *)data;
|
2017-05-22 14:31:38 +02:00
|
|
|
rsa = EVP_PKEY_get0_RSA(key->pkey);
|
2021-07-10 10:02:53 +02:00
|
|
|
|
2017-05-22 14:31:38 +02:00
|
|
|
keylen = RSA_size(rsa);
|
2016-06-08 16:31:14 +02:00
|
|
|
if (req->data_len > keylen || keylen > sizeof(buf))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ret = RSA_private_encrypt(req->data_len, req->data,
|
2017-05-22 14:31:38 +02:00
|
|
|
buf, rsa, req->padding);
|
|
|
|
if (ret != RSA_size(rsa))
|
2016-06-08 16:31:14 +02:00
|
|
|
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;
|
2017-05-22 14:31:38 +02:00
|
|
|
EC_KEY *ec;
|
2016-06-08 16:31:14 +02:00
|
|
|
const struct kore_keyreq *req;
|
|
|
|
unsigned int siglen;
|
|
|
|
u_int8_t sig[1024];
|
|
|
|
|
|
|
|
req = (const struct kore_keyreq *)data;
|
2017-05-22 14:31:38 +02:00
|
|
|
ec = EVP_PKEY_get0_EC_KEY(key->pkey);
|
2021-07-10 10:02:53 +02:00
|
|
|
|
2017-05-22 14:31:38 +02:00
|
|
|
len = ECDSA_size(ec);
|
2016-06-08 16:31:14 +02:00
|
|
|
if (req->data_len > len || len > sizeof(sig))
|
|
|
|
return;
|
|
|
|
|
2017-05-22 14:31:38 +02:00
|
|
|
if (ECDSA_sign(EVP_PKEY_NONE, req->data, req->data_len,
|
|
|
|
sig, &siglen, ec) == 0)
|
2016-06-08 16:31:14 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (siglen > sizeof(sig))
|
|
|
|
return;
|
|
|
|
|
|
|
|
kore_msg_send(msg->src, KORE_MSG_KEYMGR_RESP, sig, siglen);
|
|
|
|
}
|
2019-11-06 19:33:53 +01:00
|
|
|
|
|
|
|
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) {
|
Rework worker startup/privsep config.
Starting with the privsep config, this commit changes the following:
- Removes the root, runas, keymgr_root, keymgr_runas, acme_root and
acme_runas configuration options.
Instead these are now configured via a privsep configuration context:
privsep worker {
root /tmp
runas nobody
}
This is also configurable via Python using the new kore.privsep() method:
kore.privsep("worker", root="/tmp", runas="nobody", skip=["chroot"])
Tied into this we also better handle worker startup:
- Per worker process, wait until it signalled it is ready.
- If a worker fails at startup, display its last log lines more clearly.
- Don't start acme process if no domain requires acme.
- Remove each process its individual startup log message in favour
of a generalized one that displays its PID, root and user.
- At startup, log the kore version and built-ins in a nicer way.
- The worker processes now check things they need to start running
before signaling they are ready (such as access to CA certs for
TLS client authentication).
2021-09-07 21:59:22 +02:00
|
|
|
kore_log(LOG_INFO, "generating new ACME account key");
|
2019-11-06 19:33:53 +01:00
|
|
|
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;
|
|
|
|
|
Rework worker startup/privsep config.
Starting with the privsep config, this commit changes the following:
- Removes the root, runas, keymgr_root, keymgr_runas, acme_root and
acme_runas configuration options.
Instead these are now configured via a privsep configuration context:
privsep worker {
root /tmp
runas nobody
}
This is also configurable via Python using the new kore.privsep() method:
kore.privsep("worker", root="/tmp", runas="nobody", skip=["chroot"])
Tied into this we also better handle worker startup:
- Per worker process, wait until it signalled it is ready.
- If a worker fails at startup, display its last log lines more clearly.
- Don't start acme process if no domain requires acme.
- Remove each process its individual startup log message in favour
of a generalized one that displays its PID, root and user.
- At startup, log the kore version and built-ins in a nicer way.
- The worker processes now check things they need to start running
before signaling they are ready (such as access to CA certs for
TLS client authentication).
2021-09-07 21:59:22 +02:00
|
|
|
kore_log(LOG_INFO, "generated new domain key for %s", dom->domain);
|
2019-11-06 19:33:53 +01:00
|
|
|
|
|
|
|
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)
|
Rework worker startup/privsep config.
Starting with the privsep config, this commit changes the following:
- Removes the root, runas, keymgr_root, keymgr_runas, acme_root and
acme_runas configuration options.
Instead these are now configured via a privsep configuration context:
privsep worker {
root /tmp
runas nobody
}
This is also configurable via Python using the new kore.privsep() method:
kore.privsep("worker", root="/tmp", runas="nobody", skip=["chroot"])
Tied into this we also better handle worker startup:
- Per worker process, wait until it signalled it is ready.
- If a worker fails at startup, display its last log lines more clearly.
- Don't start acme process if no domain requires acme.
- Remove each process its individual startup log message in favour
of a generalized one that displays its PID, root and user.
- At startup, log the kore version and built-ins in a nicer way.
- The worker processes now check things they need to start running
before signaling they are ready (such as access to CA certs for
TLS client authentication).
2021-09-07 21:59:22 +02:00
|
|
|
fatalx("open(%s): %s", key->dom->certfile, errno_s);
|
2019-11-06 19:33:53 +01:00
|
|
|
|
|
|
|
kore_log(LOG_INFO, "writing %zu bytes of data", len);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
ret = write(fd, data, len);
|
|
|
|
if (ret == -1) {
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
Rework worker startup/privsep config.
Starting with the privsep config, this commit changes the following:
- Removes the root, runas, keymgr_root, keymgr_runas, acme_root and
acme_runas configuration options.
Instead these are now configured via a privsep configuration context:
privsep worker {
root /tmp
runas nobody
}
This is also configurable via Python using the new kore.privsep() method:
kore.privsep("worker", root="/tmp", runas="nobody", skip=["chroot"])
Tied into this we also better handle worker startup:
- Per worker process, wait until it signalled it is ready.
- If a worker fails at startup, display its last log lines more clearly.
- Don't start acme process if no domain requires acme.
- Remove each process its individual startup log message in favour
of a generalized one that displays its PID, root and user.
- At startup, log the kore version and built-ins in a nicer way.
- The worker processes now check things they need to start running
before signaling they are ready (such as access to CA certs for
TLS client authentication).
2021-09-07 21:59:22 +02:00
|
|
|
fatalx("write(%s): %s", key->dom->certfile, errno_s);
|
2019-11-06 19:33:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((size_t)ret != len) {
|
Rework worker startup/privsep config.
Starting with the privsep config, this commit changes the following:
- Removes the root, runas, keymgr_root, keymgr_runas, acme_root and
acme_runas configuration options.
Instead these are now configured via a privsep configuration context:
privsep worker {
root /tmp
runas nobody
}
This is also configurable via Python using the new kore.privsep() method:
kore.privsep("worker", root="/tmp", runas="nobody", skip=["chroot"])
Tied into this we also better handle worker startup:
- Per worker process, wait until it signalled it is ready.
- If a worker fails at startup, display its last log lines more clearly.
- Don't start acme process if no domain requires acme.
- Remove each process its individual startup log message in favour
of a generalized one that displays its PID, root and user.
- At startup, log the kore version and built-ins in a nicer way.
- The worker processes now check things they need to start running
before signaling they are ready (such as access to CA certs for
TLS client authentication).
2021-09-07 21:59:22 +02:00
|
|
|
fatalx("incorrect write on %s (%zd/%zu)",
|
2019-11-06 19:33:53 +01:00
|
|
|
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;
|
2019-11-07 07:56:13 +01:00
|
|
|
int slen, i;
|
2019-11-06 19:33:53 +01:00
|
|
|
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))
|
Rework worker startup/privsep config.
Starting with the privsep config, this commit changes the following:
- Removes the root, runas, keymgr_root, keymgr_runas, acme_root and
acme_runas configuration options.
Instead these are now configured via a privsep configuration context:
privsep worker {
root /tmp
runas nobody
}
This is also configurable via Python using the new kore.privsep() method:
kore.privsep("worker", root="/tmp", runas="nobody", skip=["chroot"])
Tied into this we also better handle worker startup:
- Per worker process, wait until it signalled it is ready.
- If a worker fails at startup, display its last log lines more clearly.
- Don't start acme process if no domain requires acme.
- Remove each process its individual startup log message in favour
of a generalized one that displays its PID, root and user.
- At startup, log the kore version and built-ins in a nicer way.
- The worker processes now check things they need to start running
before signaling they are ready (such as access to CA certs for
TLS client authentication).
2021-09-07 21:59:22 +02:00
|
|
|
fatalx("failed to convert digest to hex");
|
2019-11-06 19:33:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2019-11-07 07:56:13 +01:00
|
|
|
keymgr_x509_ext(sk, acme_oid, "critical,%s", hex);
|
2019-11-06 19:33:53 +01:00
|
|
|
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)
|
Rework worker startup/privsep config.
Starting with the privsep config, this commit changes the following:
- Removes the root, runas, keymgr_root, keymgr_runas, acme_root and
acme_runas configuration options.
Instead these are now configured via a privsep configuration context:
privsep worker {
root /tmp
runas nobody
}
This is also configurable via Python using the new kore.privsep() method:
kore.privsep("worker", root="/tmp", runas="nobody", skip=["chroot"])
Tied into this we also better handle worker startup:
- Per worker process, wait until it signalled it is ready.
- If a worker fails at startup, display its last log lines more clearly.
- Don't start acme process if no domain requires acme.
- Remove each process its individual startup log message in favour
of a generalized one that displays its PID, root and user.
- At startup, log the kore version and built-ins in a nicer way.
- The worker processes now check things they need to start running
before signaling they are ready (such as access to CA certs for
TLS client authentication).
2021-09-07 21:59:22 +02:00
|
|
|
fatalx("X509_REQ_new: %s", ssl_errno_s);
|
2019-11-06 19:33:53 +01:00
|
|
|
|
|
|
|
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');
|
|
|
|
|
2020-09-08 12:49:46 +02:00
|
|
|
/* RFC 5280 says years >= 50 are interpreted as 19YY */
|
2019-11-06 19:33:53 +01:00
|
|
|
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
|