/* * Copyright (c) 2022 Joris Vink * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "kore.h" #include "http.h" /* * Disable deprecated declaration warnings if we're building against * OpenSSL 3 as they marked all low-level APIs as deprecated. * * Work is being done to replace these, but for now let things build. */ #if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #define TLS_SESSION_ID "kore_tls_sessionid" /* Helper for weird API designs (looking at you OpenSSL). */ union deconst { void *p; const void *cp; }; static int tls_domain_x509_verify(int, X509_STORE_CTX *); static X509 *tls_domain_load_certificate_chain(SSL_CTX *, const void *, size_t); static EVP_PKEY *tls_privsep_private_key(EVP_PKEY *, struct kore_domain *); 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 DH *dh_params = NULL; static RSA_METHOD *keymgr_rsa_meth = 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(); } void kore_tls_log_version(void) { kore_log(LOG_NOTICE, "TLS backend %s", OPENSSL_VERSION_TEXT); #if defined(LIBRESSL_VERSION_NUMBER) kore_log(LOG_NOTICE, "libressl does not support malloc-wrappers"); #endif #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); } 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"); 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); 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; EVP_PKEY *pkey; X509 *x509; STACK_OF(X509_NAME) *certs; const SSL_METHOD *method; if (dom->tls_ctx != NULL) SSL_CTX_free(dom->tls_ctx); if ((method = TLS_method()) == NULL) fatalx("TLS_method(): %s", ssl_errno_s); if ((dom->tls_ctx = SSL_CTX_new(method)) == NULL) fatalx("SSL_ctx_new(): %s", ssl_errno_s); if (!SSL_CTX_set_min_proto_version(dom->tls_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->tls_ctx, TLS1_3_VERSION)) fatalx("SSL_CTX_set_max_proto_version: %s", ssl_errno_s); #else if (!SSL_CTX_set_max_proto_version(dom->tls_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->tls_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->tls_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->tls_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->tls_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->tls_ctx); dom->tls_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: pkey = tls_privsep_private_key(pkey, dom); break; default: fatalx("unknown public key in certificate"); } if (!SSL_CTX_use_PrivateKey(dom->tls_ctx, pkey)) fatalx("SSL_CTX_use_PrivateKey(): %s", ssl_errno_s); if (!SSL_CTX_check_private_key(dom->tls_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->tls_ctx, dh_params); SSL_CTX_set_options(dom->tls_ctx, SSL_OP_SINGLE_DH_USE); if (!SSL_CTX_set_ecdh_auto(dom->tls_ctx, 1)) fatalx("SSL_CTX_set_ecdh_auto: %s", ssl_errno_s); SSL_CTX_set_options(dom->tls_ctx, SSL_OP_SINGLE_ECDH_USE); SSL_CTX_set_options(dom->tls_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->tls_ctx, dom->cafile, NULL); SSL_CTX_set_verify_depth(dom->tls_ctx, dom->x509_verify_depth); SSL_CTX_set_client_CA_list(dom->tls_ctx, certs); SSL_CTX_set_verify(dom->tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, tls_domain_x509_verify); } SSL_CTX_set_session_id_context(dom->tls_ctx, (unsigned char *)TLS_SESSION_ID, strlen(TLS_SESSION_ID)); SSL_CTX_set_mode(dom->tls_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); if (tls_version == KORE_TLS_VERSION_BOTH) { SSL_CTX_set_options(dom->tls_ctx, SSL_OP_NO_SSLv2); SSL_CTX_set_options(dom->tls_ctx, SSL_OP_NO_SSLv3); SSL_CTX_set_options(dom->tls_ctx, SSL_OP_NO_TLSv1); SSL_CTX_set_options(dom->tls_ctx, SSL_OP_NO_TLSv1_1); } SSL_CTX_set_options(dom->tls_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); SSL_CTX_set_cipher_list(dom->tls_ctx, tls_cipher_list); SSL_CTX_set_info_callback(dom->tls_ctx, tls_info_callback); SSL_CTX_set_tlsext_servername_callback(dom->tls_ctx, tls_sni_cb); #if defined(KORE_USE_ACME) SSL_CTX_set_alpn_select_cb(dom->tls_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_REVOKED *rev; X509_STORE *store; struct connection *c, *next; ERR_clear_error(); in = BIO_new_mem_buf(pem, pemlen); if ((store = SSL_CTX_get_cert_store(dom->tls_ctx)) == NULL) { BIO_free(in); kore_log(LOG_ERR, "SSL_CTX_get_cert_store(): %s", ssl_errno_s); return; } X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); 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; } /* * Check if any accepted connection authenticated themselves * with a now revoked certificate. */ for (c = TAILQ_FIRST(&connections); c != NULL; c = next) { next = TAILQ_NEXT(c, list); if (c->proto != CONN_PROTO_HTTP) continue; /* * Prune any connection that is currently not yet done * with the TLS handshake. This is to prevent a race * where a handshake could not yet be complete but * did pass the x509 verification step and their cert * was revoked in this CRL update. */ if (c->state == CONN_STATE_TLS_SHAKE) { kore_connection_disconnect(c); continue; } if (c->tls_cert == NULL) continue; if (X509_CRL_get0_by_cert(crl, &rev, c->tls_cert) != 1) continue; kore_connection_log(c, "connection removed, its certificate is revoked"); kore_connection_disconnect(c); } } BIO_free(in); } void kore_tls_domain_cleanup(struct kore_domain *dom) { if (dom->tls_ctx != NULL) SSL_CTX_free(dom->tls_ctx); } int kore_tls_connection_accept(struct connection *c) { int r; if (primary_dom == NULL) { kore_connection_log(c, "TLS handshake but no TLS configured on server"); return (KORE_RESULT_ERROR); } if (primary_dom->tls_ctx == NULL) { kore_connection_log(c, "TLS configuration for %s not yet complete", primary_dom->domain); return (KORE_RESULT_ERROR); } if (c->tls == NULL) { c->tls = SSL_new(primary_dom->tls_ctx); if (c->tls == NULL) return (KORE_RESULT_ERROR); SSL_set_fd(c->tls, c->fd); SSL_set_accept_state(c->tls); if (!SSL_set_ex_data(c->tls, 0, c)) { kore_connection_log(c, "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->tls); if (r <= 0) { r = SSL_get_error(c->tls, r); switch (r) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: kore_connection_start_idletimer(c); return (KORE_RESULT_RETRY); default: if (c->flags & CONN_LOG_TLS_FAILURE) { kore_connection_log(c, "SSL_accept: %s", ssl_errno_s); } return (KORE_RESULT_ERROR); } } #if defined(KORE_USE_ACME) if (c->proto == CONN_PROTO_ACME_ALPN) { kore_connection_log(c, "disconnecting ACME client"); kore_connection_disconnect(c); return (KORE_RESULT_ERROR); } #endif if (SSL_get_verify_mode(c->tls) & SSL_VERIFY_PEER) { c->tls_cert = SSL_get_peer_certificate(c->tls); if (c->tls_cert == NULL) { kore_connection_log(c, "no peer certificate returned"); return (KORE_RESULT_ERROR); } } else { c->tls_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->tls, (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->tls, 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: if (c->flags & CONN_LOG_TLS_FAILURE) { kore_connection_log(c, "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->tls, (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->tls, 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: if (c->flags & CONN_LOG_TLS_FAILURE) { kore_connection_log(c, "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->tls != NULL) { SSL_shutdown(c->tls); SSL_free(c->tls); } if (c->tls_cert != NULL) X509_free(c->tls_cert); if (c->tls_sni != NULL) kore_free(c->tls_sni); } KORE_PRIVATE_KEY * kore_tls_rsakey_load(const char *path) { FILE *fp; KORE_PRIVATE_KEY *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); } KORE_PRIVATE_KEY * kore_tls_rsakey_generate(const char *path) { FILE *fp; EVP_PKEY_CTX *ctx; KORE_PRIVATE_KEY *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); } KORE_X509_NAMES * kore_tls_x509_subject_name(struct connection *c) { X509_NAME *name; if ((name = X509_get_subject_name(c->tls_cert)) == NULL) { kore_connection_log(c, "X509_get_subject_name: %s", ssl_errno_s); } return (name); } KORE_X509_NAMES * kore_tls_x509_issuer_name(struct connection *c) { X509_NAME *name; if ((name = X509_get_issuer_name(c->tls_cert)) == NULL) kore_connection_log(c, "X509_get_issuer_name: %s", ssl_errno_s); return (name); } int kore_tls_x509name_foreach(KORE_X509_NAMES *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); } int kore_tls_x509_data(struct connection *c, u_int8_t **ptr, size_t *olen) { int len; u_int8_t *der, *pp; if ((len = i2d_X509(c->tls_cert, NULL)) <= 0) { kore_connection_log(c, "i2d_X509: %s", ssl_errno_s); return (KORE_RESULT_ERROR); } der = kore_calloc(1, len); pp = der; if (i2d_X509(c->tls_cert, &pp) <= 0) { kore_free(der); kore_connection_log(c, "i2d_X509: %s", ssl_errno_s); return (KORE_RESULT_ERROR); } *ptr = der; *olen = len; return (KORE_RESULT_OK); } 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); if (sname != NULL) c->tls_sni = kore_strdup(sname); if (sname != NULL && (dom = kore_domain_lookup(c->owner->server, sname)) != NULL) { if (dom->tls_ctx == NULL) { kore_log(LOG_NOTICE, "TLS configuration for %s not complete", dom->domain); return (SSL_TLSEXT_ERR_NOACK); } SSL_set_SSL_CTX(ssl, dom->tls_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 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) { struct connection *c; SSL *tls; X509 *cert; const char *text; int error, depth; if ((tls = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx())) == NULL) fatal("X509_STORE_CTX_get_ex_data: no data"); if ((c = SSL_get_ex_data(tls, 0)) == NULL) fatal("no connection data in %s", __func__); 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_connection_log(c, "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); } static EVP_PKEY * tls_privsep_private_key(EVP_PKEY *pub, struct kore_domain *dom) { EVP_PKEY *pkey; RSA *rsa_new; const RSA *rsa_cur; const BIGNUM *e_cur, *n_cur; BIGNUM *e_new, *n_new; if ((pkey = EVP_PKEY_new()) == NULL) fatalx("failed to create new EVP_PKEY data structure"); if ((rsa_new = RSA_new()) == NULL) fatalx("failed to create new RSA data structure"); if ((rsa_cur = EVP_PKEY_get0_RSA(pub)) == NULL) fatalx("no RSA public key present"); RSA_get0_key(rsa_cur, &n_cur, &e_cur, NULL); if ((n_new = BN_dup(n_cur)) == NULL) fatalx("BN_dup failed"); if ((e_new = BN_dup(e_cur)) == NULL) fatalx("BN_dup failed"); RSA_set_app_data(rsa_new, dom); RSA_set_method(rsa_new, keymgr_rsa_meth); RSA_set0_key(rsa_new, n_new, e_new, NULL); EVP_PKEY_set1_RSA(pkey, rsa_new); return (pkey); } #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 */