From 1a9197ffeb8619698ffa8a60808c27623e9396db Mon Sep 17 00:00:00 2001 From: Joris Vink Date: Mon, 28 Oct 2019 09:51:03 +0100 Subject: [PATCH] more progress --- include/kore/kore.h | 12 +-- src/acme.c | 61 +++++++++++-- src/config.c | 12 +-- src/connection.c | 8 +- src/domain.c | 46 +++++++--- src/keymgr.c | 215 ++++++++++++++++++++++++++------------------ src/kore.c | 7 ++ src/worker.c | 32 +++++-- 8 files changed, 263 insertions(+), 130 deletions(-) diff --git a/include/kore/kore.h b/include/kore/kore.h index be55531..fd4ffd2 100644 --- a/include/kore/kore.h +++ b/include/kore/kore.h @@ -119,6 +119,9 @@ extern int daemon(int, int); #define X509_CN_LENGTH (ub_common_name + 1) +#define KORE_PEM_CERT_CHAIN 1 +#define KORE_DER_CERT_DATA 2 + /* XXX hackish. */ #if !defined(KORE_NO_HTTP) struct http_request; @@ -636,15 +639,13 @@ struct kore_msg { struct kore_keyreq { int padding; - char domain[KORE_DOMAINNAME_LEN]; - u_int16_t domain_len; + char domain[KORE_DOMAINNAME_LEN + 1]; u_int16_t data_len; u_int8_t data[]; }; struct kore_x509_msg { - char domain[KORE_DOMAINNAME_LEN]; - u_int16_t domain_len; + char domain[KORE_DOMAINNAME_LEN + 1]; size_t data_len; u_int8_t data[]; }; @@ -898,7 +899,8 @@ 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 *, const void *, size_t); +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) int kore_module_handler_new(struct kore_domain *, const char *, diff --git a/src/acme.c b/src/acme.c index fa913a3..622d6c3 100644 --- a/src/acme.c +++ b/src/acme.c @@ -21,6 +21,8 @@ #include #include +#include + #include #include @@ -129,6 +131,7 @@ static void acme_request_json(struct kore_buf *, const char *, const char *, const char *); static char *acme_nonce_fetch(void); +static char *acme_thumbprint_component(void); static char *acme_protected_component(const char *, const char *); static void acme_parse_directory(void); @@ -290,7 +293,7 @@ kore_acme_tls_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen, *out = in; *outlen = inlen; - printf("tls-alpn-01 selected\n"); + kore_acme_tls_challenge_use_cert(ssl, dom); return (SSL_TLSEXT_ERR_OK); } @@ -1064,9 +1067,11 @@ static void acme_challenge_tls_alpn_01_create(struct acme_order *order, struct acme_challenge *challenge) { - struct kore_buf buf; struct kore_keyreq kreq; + char *thumb; + struct kore_buf buf, auth; size_t len, token_len; + u_int8_t digest[SHA256_DIGEST_LENGTH]; if (challenge->flags & ACME_FLAG_CHALLENGE_CREATED) { kore_log(LOG_NOTICE, @@ -1089,21 +1094,31 @@ acme_challenge_tls_alpn_01_create(struct acme_order *order, if (token_len > ACME_CHALLENGE_TOKEN_MAXLEN) fatal("token exceeds length limit"); - kore_buf_init(&buf, sizeof(kreq) + len); + thumb = acme_thumbprint_component(); + + kore_buf_init(&auth, 128); + kore_buf_appendf(&auth, "%s.%s", challenge->token, thumb); + (void)SHA256(auth.data, auth.offset, digest); + + kore_buf_init(&buf, sizeof(kreq) + sizeof(digest)); memset(&kreq, 0, sizeof(kreq)); - kreq.domain_len = len; - kreq.data_len = token_len; - memcpy(kreq.domain, order->domain, kreq.domain_len); + kreq.data_len = sizeof(digest); + + if (kore_strlcpy(kreq.domain, order->domain, sizeof(kreq.domain)) >= + sizeof(kreq.domain)) + fatal("%s: domain truncated", __func__); kore_buf_append(&buf, &kreq, sizeof(kreq)); - kore_buf_append(&buf, challenge->token, token_len); + kore_buf_append(&buf, digest, sizeof(digest)); kore_msg_send(KORE_WORKER_KEYMGR, KORE_ACME_CHALLENGE_CERT, buf.data, buf.offset); acme_challenge_respond(order, challenge->url, "tls-alpn-01"); + kore_buf_cleanup(&auth); kore_buf_cleanup(&buf); + kore_free(thumb); } static void @@ -1348,7 +1363,7 @@ acme_protected_component(const char *nonce, const char *url) struct kore_buf payload; struct kore_json_item *root, *jwk; - root = kore_json_create_item(NULL, NULL, KORE_JSON_TYPE_OBJECT); + root = kore_json_create_object(NULL, NULL); kore_json_create_string(root, "url", url); kore_json_create_string(root, "alg", "RS256"); @@ -1376,6 +1391,36 @@ acme_protected_component(const char *nonce, const char *url) return (b64); } +static char * +acme_thumbprint_component(void) +{ + char *b64; + struct kore_json_item *json; + struct kore_buf payload; + u_int8_t digest[SHA256_DIGEST_LENGTH]; + + json = kore_json_create_object(NULL, NULL); + + /* Order matters here, see RFC7638. */ + kore_json_create_string(json, "e", rsakey_e); + kore_json_create_string(json, "kty", "RSA"); + kore_json_create_string(json, "n", rsakey_n); + + kore_buf_init(&payload, 128); + kore_json_item_tobuf(json, &payload); + + (void)SHA256(payload.data, payload.offset, digest); + + kore_json_item_free(json); + kore_buf_cleanup(&payload); + + if (!kore_base64url_encode(digest, + sizeof(digest), &b64, KORE_BASE64_RAW)) + fatal("failed to convert thumbprint JSON to b64"); + + return (b64); +} + static int acme_status_type(const char *status) { diff --git a/src/config.c b/src/config.c index d161fa4..02963cc 100644 --- a/src/config.c +++ b/src/config.c @@ -869,11 +869,7 @@ configure_certfile(char *path) return (KORE_RESULT_ERROR); } - if (current_domain->certfile != NULL) { - printf("certfile already set for %s\n", current_domain->domain); - return (KORE_RESULT_ERROR); - } - + kore_free(current_domain->certfile); current_domain->certfile = kore_strdup(path); return (KORE_RESULT_OK); } @@ -886,11 +882,7 @@ configure_certkey(char *path) return (KORE_RESULT_ERROR); } - if (current_domain->certkey != NULL) { - printf("certkey already set for %s\n", current_domain->domain); - return (KORE_RESULT_ERROR); - } - + kore_free(current_domain->certkey); current_domain->certkey = kore_strdup(path); return (KORE_RESULT_OK); } diff --git a/src/connection.c b/src/connection.c index e436943..b73c1bd 100644 --- a/src/connection.c +++ b/src/connection.c @@ -271,8 +271,12 @@ kore_connection_handle(struct connection *c) SSL_set_fd(c->ssl, c->fd); SSL_set_accept_state(c->ssl); - SSL_set_app_data(c->ssl, c); - SSL_set_ex_data(c->ssl, 0, c); + + if (!SSL_set_ex_data(c->ssl, 0, c)) { + kore_debug("SSL_set_ex_data(): %s", + ssl_errno_s); + return (KORE_RESULT_ERROR); + } } ERR_clear_error(); diff --git a/src/domain.c b/src/domain.c index 3c9c6f0..bd55270 100644 --- a/src/domain.c +++ b/src/domain.c @@ -263,8 +263,10 @@ kore_domain_free(struct kore_domain *dom) } void -kore_domain_tlsinit(struct kore_domain *dom, const void *pem, size_t pemlen) +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; @@ -336,7 +338,22 @@ kore_domain_tlsinit(struct kore_domain *dom, const void *pem, size_t pemlen) } #endif - x509 = domain_load_certificate_chain(dom->ssl_ctx, pem, pemlen); + 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 ((pkey = X509_get_pubkey(x509)) == NULL) fatalx("certificate has no public key"); @@ -369,8 +386,10 @@ kore_domain_tlsinit(struct kore_domain *dom, const void *pem, size_t pemlen) 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", dom->domain); + 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) fatalx("No DH parameters given"); @@ -610,18 +629,18 @@ keymgr_rsa_privenc(int flen, const unsigned char *from, unsigned char *to, if ((dom = RSA_get_app_data(rsa)) == NULL) fatal("RSA key has no domain attached"); - if (strlen(dom->domain) >= KORE_DOMAINNAME_LEN - 1) - fatal("domain name too long"); 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; - req->domain_len = strlen(dom->domain); - memcpy(&req->data[0], from, req->data_len); - memcpy(req->domain, dom->domain, req->domain_len); kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_KEYMGR_REQ, keymgr_buf, len); keymgr_await_data(); @@ -674,13 +693,14 @@ keymgr_ecdsa_sign(const unsigned char *dgst, int dgst_len, #endif memset(keymgr_buf, 0, sizeof(keymgr_buf)); - req = (struct kore_keyreq *)keymgr_buf; - req->data_len = dgst_len; - req->domain_len = strlen(dom->domain); + 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); - memcpy(req->domain, dom->domain, req->domain_len); kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_KEYMGR_REQ, keymgr_buf, len); keymgr_await_data(); diff --git a/src/keymgr.c b/src/keymgr.c index 20e1c06..4f6c7ac 100644 --- a/src/keymgr.c +++ b/src/keymgr.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -62,6 +63,11 @@ #define RAND_POLL_INTERVAL (1800 * 1000) #define RAND_FILE_SIZE 1024 +#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x3000000fL +#undef OPENSSL_VERSION_NUMBER +#define OPENSSL_VERSION_NUMBER 0x10100000L +#endif + #if defined(__linux__) #include "seccomp.h" @@ -146,6 +152,7 @@ static void keymgr_acme_domainkey(struct kore_domain *, struct key *); static void keymgr_acme_order_create(const char *); static void keymgr_acme_order_status(void *, u_int64_t); +static void keymgr_x509_ext(X509 *, int, const char *, ...); static void keymgr_acme_challenge_cert(const void *, size_t, struct key *); #endif /* KORE_USE_ACME */ @@ -180,7 +187,7 @@ kore_keymgr_run(void) u_int64_t now, netwait, last_seed; if (keymgr_active == 0) - fatal("%s: called with keymgr_active == 0", __func__); + fatalx("%s: called with keymgr_active == 0", __func__); quit = 0; @@ -226,7 +233,7 @@ kore_keymgr_run(void) #if defined(__OpenBSD__) if (pledge("stdio rpath", NULL) == -1) - fatal("failed to pledge keymgr process"); + fatalx("failed to pledge keymgr process"); #endif while (quit != 1) { @@ -326,14 +333,13 @@ keymgr_submit_certificates(struct kore_domain *dom, u_int16_t dst) return; } #endif - fatal("cannot read '%s' for %s: %s", + fatalx("cannot read '%s' for %s: %s", dom->certfile, dom->domain, errno_s); } - if (dom->certfile != NULL) { - keymgr_submit_file(KORE_MSG_CERTIFICATE, - dom, dom->certfile, dst, 0); - } + keymgr_acme_order_create(dom->domain); + printf("sending %s\n", dom->certfile); + 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); @@ -353,17 +359,17 @@ keymgr_submit_file(u_int8_t id, struct kore_domain *dom, if ((fd = open(file, O_RDONLY)) == -1) { if (errno == ENOENT && can_fail) return; - fatal("open(%s): %s", file, errno_s); + fatalx("open(%s): %s", file, errno_s); } if (fstat(fd, &st) == -1) - fatal("stat(%s): %s", file, errno_s); + fatalx("stat(%s): %s", file, errno_s); if (!S_ISREG(st.st_mode)) - fatal("%s is not a file", file); + fatalx("%s is not a file", file); if (st.st_size <= 0 || st.st_size > (1024 * 1024 * 10)) { - fatal("%s length is not valid (%jd)", file, + fatalx("%s length is not valid (%jd)", file, (intmax_t)st.st_size); } @@ -371,20 +377,20 @@ keymgr_submit_file(u_int8_t id, struct kore_domain *dom, payload = kore_calloc(1, len); msg = (struct kore_x509_msg *)payload; - msg->domain_len = strlen(dom->domain); - if (msg->domain_len > sizeof(msg->domain)) - fatal("domain name '%s' too long", dom->domain); - memcpy(msg->domain, dom->domain, msg->domain_len); + + if (kore_strlcpy(msg->domain, dom->domain, sizeof(msg->domain)) >= + sizeof(msg->domain)) + fatalx("%s: domain truncated", __func__); msg->data_len = st.st_size; ret = read(fd, &msg->data[0], msg->data_len); if (ret == -1) - fatal("failed to read from %s: %s", file, errno_s); + fatalx("failed to read from %s: %s", file, errno_s); if (ret == 0) - fatal("eof while reading %s", file); + fatalx("eof while reading %s", file); if ((size_t)ret != msg->data_len) { - fatal("bad read on %s: expected %zu, got %zd", + fatalx("bad read on %s: expected %zu, got %zd", file, msg->data_len, ret); } @@ -406,26 +412,26 @@ keymgr_load_randfile(void) return; if ((fd = open(rand_file, O_RDONLY)) == -1) - fatal("open(%s): %s", rand_file, errno_s); + fatalx("open(%s): %s", rand_file, errno_s); if (fstat(fd, &st) == -1) - fatal("stat(%s): %s", rand_file, errno_s); + fatalx("stat(%s): %s", rand_file, errno_s); if (!S_ISREG(st.st_mode)) - fatal("%s is not a file", rand_file); + fatalx("%s is not a file", rand_file); if (st.st_size != RAND_FILE_SIZE) - fatal("%s has an invalid size", rand_file); + fatalx("%s has an invalid size", rand_file); total = 0; while (total != RAND_FILE_SIZE) { ret = read(fd, buf, sizeof(buf)); if (ret == 0) - fatal("EOF on %s", rand_file); + fatalx("EOF on %s", rand_file); if (ret == -1) { if (errno == EINTR) continue; - fatal("read(%s): %s", rand_file, errno_s); + fatalx("read(%s): %s", rand_file, errno_s); } total += (size_t)ret; @@ -507,7 +513,7 @@ keymgr_load_domain_privatekey(struct kore_domain *dom) keymgr_acme_domainkey(dom, key); #endif if (key->pkey == NULL) { - fatal("failed to load private key for '%s' (%s)", + fatalx("failed to load private key for '%s' (%s)", dom->domain, errno_s); } } @@ -575,14 +581,15 @@ keymgr_msg_recv(struct kore_msg *msg, const void *data) if (msg->length != (sizeof(*req) + req->data_len)) return; - if (req->domain_len > KORE_DOMAINNAME_LEN) + + if (req->domain[KORE_DOMAINNAME_LEN] != '\0') return; key = NULL; TAILQ_FOREACH(key, &keys, list) { if (key->dom == NULL) continue; - if (!strncmp(key->dom->domain, req->domain, req->domain_len)) + if (!strcmp(key->dom->domain, req->domain)) break; } @@ -621,7 +628,7 @@ keymgr_rsa_encrypt(struct kore_msg *msg, const void *data, struct key *key) req = (const struct kore_keyreq *)data; -#if !defined(LIBRESSL_VERSION_TEXT) && OPENSSL_VERSION_NUMBER >= 0x10100000L +#if OPENSSL_VERSION_NUMBER >= 0x10100000L rsa = EVP_PKEY_get0_RSA(key->pkey); #else rsa = key->pkey->pkey.rsa; @@ -648,7 +655,7 @@ keymgr_ecdsa_sign(struct kore_msg *msg, const void *data, struct key *key) u_int8_t sig[1024]; req = (const struct kore_keyreq *)data; -#if !defined(LIBRESSL_VERSION_TEXT) && OPENSSL_VERSION_NUMBER >= 0x10100000L +#if OPENSSL_VERSION_NUMBER >= 0x10100000L ec = EVP_PKEY_get0_EC_KEY(key->pkey); #else ec = key->pkey->pkey.ec; @@ -680,7 +687,7 @@ keymgr_acme_init(void) if (mkdir(KORE_ACME_CERTDIR, 0700) == -1) { if (errno != EEXIST) - fatal("mkdir(%s): %s", KORE_ACME_CERTDIR, errno_s); + fatalx("mkdir(%s): %s", KORE_ACME_CERTDIR, errno_s); } umask(S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH); @@ -727,13 +734,13 @@ keymgr_acme_domainkey(struct kore_domain *dom, struct key *key) kore_log(LOG_NOTICE, "generated new domain key for %s", dom->domain); if ((p = strrchr(dom->certkey, '/')) == NULL) - fatal("invalid certkey path '%s'", dom->certkey); + fatalx("invalid certkey path '%s'", dom->certkey); *p = '\0'; if (mkdir(dom->certkey, 0700) == -1) { if (errno != EEXIST) - fatal("mkdir(%s): %s", dom->certkey, errno_s); + fatalx("mkdir(%s): %s", dom->certkey, errno_s); } *p = '/'; @@ -802,10 +809,10 @@ keymgr_acme_sign(struct kore_msg *msg, const void *data) } if (key == NULL) - fatal("%s: missing key", __func__); + fatalx("%s: missing key", __func__); if (msg->length < sizeof(id)) - fatal("%s: invalid length (%zu)", __func__, msg->length); + fatalx("%s: invalid length (%zu)", __func__, msg->length); ptr = data; memcpy(&id, ptr, sizeof(id)); @@ -816,19 +823,19 @@ keymgr_acme_sign(struct kore_msg *msg, const void *data) sig = kore_calloc(1, EVP_PKEY_size(key->pkey)); if ((ctx = EVP_MD_CTX_create()) == NULL) - fatal("EVP_MD_CTX_create: %s", ssl_errno_s); + fatalx("EVP_MD_CTX_create: %s", ssl_errno_s); if (!EVP_SignInit_ex(ctx, EVP_sha256(), NULL)) - fatal("EVP_SignInit_ex: %s", ssl_errno_s); + fatalx("EVP_SignInit_ex: %s", ssl_errno_s); if (!EVP_SignUpdate(ctx, ptr, msg->length)) - fatal("EVP_SignUpdate: %s", ssl_errno_s); + fatalx("EVP_SignUpdate: %s", ssl_errno_s); if (!EVP_SignFinal(ctx, sig, &siglen, key->pkey)) - fatal("EVP_SignFinal: %s", ssl_errno_s); + fatalx("EVP_SignFinal: %s", ssl_errno_s); if (!kore_base64url_encode(sig, siglen, &b64, KORE_BASE64_RAW)) - fatal("%s: failed to b64url encode signed data", __func__); + fatalx("%s: failed to b64url encode signed data", __func__); kore_buf_init(&buf, siglen + sizeof(id)); kore_buf_append(&buf, &id, sizeof(id)); @@ -845,87 +852,123 @@ keymgr_acme_sign(struct kore_msg *msg, const void *data) } static void -keymgr_acme_challenge_cert(const void *token, size_t len, struct key *key) +keymgr_acme_challenge_cert(const void *data, size_t len, struct key *key) { - int nid; - FILE *fp; + size_t idx; + struct kore_x509_msg hdr; struct kore_buf buf; time_t now; - X509_EXTENSION *ext; X509_NAME *name; X509 *x509; - char *altname; + const u_int8_t *digest; + int slen, acme; + 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 < len; idx++) { + slen = snprintf(hex + (idx * 2), sizeof(hex) - (idx * 2), + "%02x", digest[idx]); + if (slen == -1 || (size_t)slen >= sizeof(hex)) + fatalx("faild to convert digest to hex"); + } + if ((x509 = X509_new()) == NULL) - fatal("X509_new(): %s", ssl_errno_s); + fatalx("X509_new(): %s", ssl_errno_s); if (!X509_set_version(x509, 2)) - fatal("X509_set_version(): %s", ssl_errno_s); + fatalx("X509_set_version(): %s", ssl_errno_s); time(&now); if (!ASN1_INTEGER_set(X509_get_serialNumber(x509), now)) - fatal("ASN1_INTEGER_set(): %s", ssl_errno_s); + fatalx("ASN1_INTEGER_set(): %s", ssl_errno_s); if (!X509_gmtime_adj(X509_get_notBefore(x509), 0)) - fatal("X509_gmtime_adj(): %s", ssl_errno_s); + fatalx("X509_gmtime_adj(): %s", ssl_errno_s); + if (!X509_gmtime_adj(X509_get_notAfter(x509), ACME_X509_EXPIRATION)) - fatal("X509_gmtime_adj(): %s", ssl_errno_s); + fatalx("X509_gmtime_adj(): %s", ssl_errno_s); if (!X509_set_pubkey(x509, key->pkey)) - fatal("X509_set_pubkey(): %s", ssl_errno_s); + fatalx("X509_set_pubkey(): %s", ssl_errno_s); if ((name = X509_get_subject_name(x509)) == NULL) - fatal("X509_get_subject_name(): %s", ssl_errno_s); + 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)) - fatal("X509_NAME_add_entry_by_txt(): CN %s", ssl_errno_s); + fatalx("X509_NAME_add_entry_by_txt(): CN %s", ssl_errno_s); if (!X509_set_issuer_name(x509, name)) - fatal("X509_set_issuer_name(): %s", ssl_errno_s); + fatalx("X509_set_issuer_name(): %s", ssl_errno_s); - kore_buf_init(&buf, 128); - kore_buf_appendf(&buf, "DNS:%s", key->dom->domain); - altname = kore_buf_stringify(&buf, NULL); + acme = OBJ_create(ACME_TLS_ALPN_01_OID, "acme", "acmeIdentifier"); + X509V3_EXT_add_alias(acme, NID_subject_key_identifier); - ext = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, altname); - if (ext == NULL) - fatal("X509V3_EXT_conf_nid: %s", ssl_errno_s); - - if (!X509_add_ext(x509, ext, -1)) - fatal("X509_add_ext: %s", ssl_errno_s); - - X509_EXTENSION_free(ext); - kore_buf_cleanup(&buf); - - nid = OBJ_create(ACME_TLS_ALPN_01_OID, "acme", "acmeIdentifier"); - X509V3_EXT_add_alias(nid, NID_subject_key_identifier); - - /* XXX - add SHA256 digest over key authoritization. */ - ext = X509V3_EXT_conf_nid(NULL, NULL, nid, "critical,62636465"); - if (ext == NULL) - fatal("X509V3_EXT_conf_nid: %s", ssl_errno_s); - - if (!X509_add_ext(x509, ext, -1)) - fatal("X509_add_ext: %s", ssl_errno_s); - - X509_EXTENSION_free(ext); + keymgr_x509_ext(x509, acme, "critical,%s", hex); + keymgr_x509_ext(x509, NID_subject_alt_name, "DNS:%s", key->dom->domain); if (!X509_sign(x509, key->pkey, EVP_sha256())) - fatal("X509_sign(): %s", ssl_errno_s); + fatalx("X509_sign(): %s", ssl_errno_s); - if ((fp = fopen("foobar.pem", "w")) == NULL) - fatal("fopen(cert/server.pem): %s", errno_s); - if (!PEM_write_X509(fp, x509)) - fatal("PEM_write_X509(%s)", errno_s); - fclose(fp); + 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); + + memset(&hdr, 0, sizeof(hdr)); + hdr.data_len = slen; + + if (kore_strlcpy(hdr.domain, key->dom->domain, sizeof(hdr.domain)) >= + sizeof(hdr.domain)) + fatalx("%s: domain truncated", __func__); + + kore_buf_init(&buf, sizeof(hdr) + slen); + kore_buf_append(&buf, &hdr, sizeof(hdr)); + kore_buf_append(&buf, cert, slen); + + kore_msg_send(KORE_MSG_WORKER_ALL, KORE_ACME_CHALLENGE_SET_CERT, + buf.data, buf.offset); + + kore_buf_cleanup(&buf); + kore_free(cert); X509_free(x509); } +static void +keymgr_x509_ext(X509 *x509, int extnid, const char *fmt, ...) +{ + int len; + X509_EXTENSION *ext; + va_list args; + 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: %s", ssl_errno_s); + + if (!X509_add_ext(x509, ext, -1)) + fatalx("X509_add_ext: %s", ssl_errno_s); + + X509_EXTENSION_free(ext); +} + static char * keymgr_bignum_base64(BIGNUM *bn) { @@ -937,10 +980,10 @@ keymgr_bignum_base64(BIGNUM *bn) buf = kore_calloc(1, len); if (BN_bn2bin(bn, buf) != len) - fatal("BN_bn2bin: %s", ssl_errno_s); + fatalx("BN_bn2bin: %s", ssl_errno_s); if (!kore_base64url_encode(buf, len, &encoded, KORE_BASE64_RAW)) - fatal("failed to base64 encode BIGNUM"); + fatalx("failed to base64 encode BIGNUM"); return (encoded); } diff --git a/src/kore.c b/src/kore.c index 95071c5..2a5b805 100644 --- a/src/kore.c +++ b/src/kore.c @@ -348,6 +348,8 @@ kore_tls_sni_cb(SSL *ssl, int *ad, void *arg) struct kore_domain *dom; const char *sname; + printf("%s\n", __func__); + if ((c = SSL_get_ex_data(ssl, 0)) == NULL) fatal("no connection data in %s", __func__); @@ -366,6 +368,11 @@ kore_tls_sni_cb(SSL *ssl, int *ad, void *arg) kore_debug("kore_ssl_sni_cb(): Using %s CTX", sname); SSL_set_SSL_CTX(ssl, dom->ssl_ctx); + if (!SSL_set_ex_data(ssl, 1, dom)) { + kore_debug("SSL_set_ex_data(): %s", ssl_errno_s); + return (KORE_RESULT_ERROR); + } + #if defined(KORE_USE_ACME) if (kore_acme_tls_challenge_selected(ssl, dom)) { kore_acme_tls_challenge_use_cert(ssl, dom); diff --git a/src/worker.c b/src/worker.c index b09567d..0c2f902 100644 --- a/src/worker.c +++ b/src/worker.c @@ -402,6 +402,12 @@ kore_worker_entry(struct kore_worker *kw) kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_CERTIFICATE_REQ, NULL, 0); } +#if defined(KORE_USE_ACME) + kore_msg_register(KORE_ACME_CHALLENGE_SET_CERT, + worker_keymgr_response); + kore_msg_register(KORE_ACME_CHALLENGE_CLEAR_CERT, + worker_keymgr_response); +#endif } kore_msg_register(KORE_MSG_ACCEPT_AVAILABLE, worker_accept_avail); @@ -770,18 +776,33 @@ worker_keymgr_response(struct kore_msg *msg, const void *data) switch (msg->id) { case KORE_MSG_CERTIFICATE: - kore_domain_tlsinit(dom, req->data, req->data_len); + kore_domain_tlsinit(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); break; #if defined(KORE_USE_ACME) case KORE_ACME_CHALLENGE_SET_CERT: + if (dom->ssl_ctx != NULL) { + kore_free(dom->acme_cert); + dom->acme_cert_len = req->data_len; + dom->acme_cert = kore_calloc(1, req->data_len); + memcpy(dom->acme_cert, req->data, req->data_len); + } else { + kore_domain_tlsinit(dom, KORE_DER_CERT_DATA, + req->data, req->data_len); + } + kore_log(LOG_NOTICE, "[%s] tls-alpn-01 challenge active", + dom->domain); + dom->acme_challenge = 1; break; case KORE_ACME_CHALLENGE_CLEAR_CERT: dom->acme_cert_len = 0; dom->acme_challenge = 0; kore_free(dom->acme_cert); + kore_log(LOG_NOTICE, "[%s] tls-alpn-01 challenge disabled", + dom->domain); break; #endif default: @@ -811,11 +832,10 @@ worker_keymgr_response_verify(struct kore_msg *msg, const void *data, return (KORE_RESULT_ERROR); } - if (req->domain_len > KORE_DOMAINNAME_LEN) { - kore_log(LOG_WARNING, - "invalid keymgr domain (%u)", - req->domain_len); + if (req->domain[KORE_DOMAINNAME_LEN] != '\0') { + kore_log(LOG_WARNING, "domain not NUL-terminated"); return (KORE_RESULT_ERROR); + } LIST_FOREACH(srv, &kore_servers, list) { @@ -825,7 +845,7 @@ worker_keymgr_response_verify(struct kore_msg *msg, const void *data, continue; TAILQ_FOREACH(dom, &srv->domains, list) { - if (!strncmp(dom->domain, req->domain, req->domain_len)) + if (!strcmp(dom->domain, req->domain)) break; }