summaryrefslogtreecommitdiff
path: root/usr.sbin/relayd/relay.c
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2017-05-27 08:33:26 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2017-05-27 08:33:26 +0000
commit460b984bd245edf6c33f3e2a4f4c1f75cde903d3 (patch)
tree344c5c23e40425bf4eff1021ea92cf67231e6a51 /usr.sbin/relayd/relay.c
parent1b5fd98198f2adb6d2d152c115dca82f396d1c11 (diff)
Migrate relayd to use libtls for TLS. Still does the TLS privsep via the
engine but at least we can use a sane API for new features. Going in now so it is possible to work with this in tree. General agreement at d2k17.
Diffstat (limited to 'usr.sbin/relayd/relay.c')
-rw-r--r--usr.sbin/relayd/relay.c813
1 files changed, 338 insertions, 475 deletions
diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c
index 3ed127553b9..4b36e4a8a31 100644
--- a/usr.sbin/relayd/relay.c
+++ b/usr.sbin/relayd/relay.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: relay.c,v 1.219 2017/02/02 08:24:16 reyk Exp $ */
+/* $OpenBSD: relay.c,v 1.220 2017/05/27 08:33:25 claudio Exp $ */
/*
* Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -39,8 +39,7 @@
#include <siphash.h>
#include <imsg.h>
-#include <openssl/dh.h>
-#include <openssl/ssl.h>
+#include <tls.h>
#include "relayd.h"
@@ -73,22 +72,15 @@ void relay_input(struct rsession *);
void relay_hash_addr(SIPHASH_CTX *, struct sockaddr_storage *, int);
-DH * relay_tls_get_dhparams(int);
-DH *relay_tls_callback_dh(SSL *, int, int);
-SSL_CTX *relay_tls_ctx_create(struct relay *);
+int relay_tls_ctx_create(struct relay *);
void relay_tls_transaction(struct rsession *,
struct ctl_relay_event *);
-void relay_tls_accept(int, short, void *);
+void relay_tls_handshake(int, short, void *);
void relay_connect_retry(int, short, void *);
-void relay_tls_connect(int, short, void *);
void relay_tls_connected(struct ctl_relay_event *);
void relay_tls_readcb(int, short, void *);
void relay_tls_writecb(int, short, void *);
-struct tls_ticket *relay_get_ticket_key(unsigned char *);
-int relay_tls_session_ticket(SSL *, unsigned char *,
- unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int);
-
char *relay_load_file(const char *, off_t *);
extern void bufferevent_read_pressure_cb(struct evbuffer *, size_t,
size_t, void *);
@@ -262,7 +254,7 @@ relay_protodebug(struct relay *rlay)
fprintf(stderr, "\ttls flags: %s\n",
printb_flags(proto->tlsflags, TLSFLAG_BITS));
fprintf(stderr, "\ttls session tickets: %s\n",
- (proto->tickets > -1) ? "enabled" : "disabled");
+ (proto->tickets == 1) ? "enabled" : "disabled");
fprintf(stderr, "\ttype: ");
switch (proto->type) {
case RELAY_PROTO_TCP:
@@ -426,8 +418,8 @@ relay_launch(void)
TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
if ((rlay->rl_conf.flags & (F_TLS|F_TLSCLIENT)) &&
- (rlay->rl_ssl_ctx = relay_tls_ctx_create(rlay)) == NULL)
- fatal("relay_init: failed to create TLS context");
+ relay_tls_ctx_create(rlay) == -1)
+ fatalx("relay_launch: failed to create TLS context");
TAILQ_FOREACH(rlt, &rlay->rl_tables, rlt_entry) {
/*
@@ -687,7 +679,7 @@ relay_connected(int fd, short sig, void *arg)
return;
}
- if ((rlay->rl_conf.flags & F_TLSCLIENT) && (out->ssl == NULL)) {
+ if ((rlay->rl_conf.flags & F_TLSCLIENT) && (out->tls == NULL)) {
relay_tls_transaction(con, out);
return;
}
@@ -727,7 +719,7 @@ relay_connected(int fd, short sig, void *arg)
con->se_out.bev = bev;
/* Initialize the TLS wrapper */
- if ((rlay->rl_conf.flags & F_TLSCLIENT) && (out->ssl != NULL))
+ if ((rlay->rl_conf.flags & F_TLSCLIENT) && (out->tls != NULL))
relay_tls_connected(out);
bufferevent_settimeout(bev,
@@ -778,7 +770,7 @@ relay_input(struct rsession *con)
}
/* Initialize the TLS wrapper */
- if ((rlay->rl_conf.flags & F_TLS) && con->se_in.ssl != NULL)
+ if ((rlay->rl_conf.flags & F_TLS) && con->se_in.tls != NULL)
relay_tls_connected(&con->se_in);
bufferevent_settimeout(con->se_in.bev,
@@ -826,8 +818,8 @@ relay_dump(struct ctl_relay_event *cre, const void *buf, size_t len)
* of non-blocking events etc. This is useful to print an
* error message before gracefully closing the session.
*/
- if (cre->ssl != NULL)
- (void)SSL_write(cre->ssl, buf, len);
+ if (cre->tls != NULL)
+ (void)tls_write(cre->tls, buf, len);
else
(void)write(cre->s, buf, len);
}
@@ -1086,9 +1078,9 @@ relay_accept(int fd, short event, void *arg)
goto err;
con->se_in.s = s;
- con->se_in.ssl = NULL;
+ con->se_in.tls = NULL;
con->se_out.s = -1;
- con->se_out.ssl = NULL;
+ con->se_out.tls = NULL;
con->se_in.dst = &con->se_out;
con->se_out.dst = &con->se_in;
con->se_in.con = con;
@@ -1401,7 +1393,7 @@ relay_session(struct rsession *con)
return;
}
- if ((rlay->rl_conf.flags & F_TLS) && (in->ssl == NULL)) {
+ if ((rlay->rl_conf.flags & F_TLS) && (in->tls == NULL)) {
relay_tls_transaction(con, in);
return;
}
@@ -1570,7 +1562,7 @@ relay_connect(struct rsession *con)
/* Connection is already established but session not active */
if ((rlay->rl_conf.flags & F_TLSINSPECT) &&
con->se_out.state == STATE_PRECONNECT) {
- if (con->se_out.ssl == NULL) {
+ if (con->se_out.tls == NULL) {
log_debug("%s: tls connect failed", __func__);
return (-1);
}
@@ -1706,14 +1698,11 @@ relay_close(struct rsession *con, const char *msg)
bufferevent_free(con->se_in.bev);
else if (con->se_in.output != NULL)
evbuffer_free(con->se_in.output);
- if (con->se_in.ssl != NULL) {
- /* XXX handle non-blocking shutdown */
- if (SSL_shutdown(con->se_in.ssl) == 0)
- SSL_shutdown(con->se_in.ssl);
- SSL_free(con->se_in.ssl);
- }
- if (con->se_in.tlscert != NULL)
- X509_free(con->se_in.tlscert);
+ if (con->se_in.tls != NULL)
+ tls_close(con->se_in.tls);
+ tls_free(con->se_in.tls);
+ tls_config_free(con->se_in.tls_cfg);
+ free(con->se_in.tlscert);
if (con->se_in.s != -1) {
close(con->se_in.s);
if (con->se_out.s == -1) {
@@ -1726,20 +1715,16 @@ relay_close(struct rsession *con, const char *msg)
__func__, relay_inflight);
}
}
- free(con->se_in.buf);
if (con->se_out.bev != NULL)
bufferevent_free(con->se_out.bev);
else if (con->se_out.output != NULL)
evbuffer_free(con->se_out.output);
- if (con->se_out.ssl != NULL) {
- /* XXX handle non-blocking shutdown */
- if (SSL_shutdown(con->se_out.ssl) == 0)
- SSL_shutdown(con->se_out.ssl);
- SSL_free(con->se_out.ssl);
- }
- if (con->se_out.tlscert != NULL)
- X509_free(con->se_out.tlscert);
+ if (con->se_out.tls != NULL)
+ tls_close(con->se_out.tls);
+ tls_free(con->se_out.tls);
+ tls_config_free(con->se_out.tls_cfg);
+ free(con->se_out.tlscert);
if (con->se_out.s != -1) {
close(con->se_out.s);
@@ -1751,8 +1736,6 @@ relay_close(struct rsession *con, const char *msg)
}
con->se_out.state = STATE_INIT;
- free(con->se_out.buf);
-
if (con->se_log != NULL)
evbuffer_free(con->se_log);
@@ -1896,6 +1879,8 @@ relay_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg)
int
relay_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{
+ struct relay_ticket_key ticket;
+ struct relay *rlay;
struct rsession *con;
struct timeval tv;
objid_t id;
@@ -1945,13 +1930,15 @@ relay_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
config_getreset(env, imsg);
break;
case IMSG_TLSTICKET_REKEY:
- IMSG_SIZE_CHECK(imsg, (&env->sc_tls_ticket));
- /* rotate keys */
- memcpy(&env->sc_tls_ticket_bak, &env->sc_tls_ticket,
- sizeof(env->sc_tls_ticket));
- env->sc_tls_ticket_bak.tt_backup = 1;
- memcpy(&env->sc_tls_ticket, imsg->data,
- sizeof(env->sc_tls_ticket));
+ IMSG_SIZE_CHECK(imsg, (&ticket));
+ memcpy(&env->sc_ticket, imsg->data, sizeof(env->sc_ticket));
+ TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
+ if (rlay->rl_conf.flags & F_TLS)
+ tls_config_add_ticket_key(rlay->rl_tls_cfg,
+ env->sc_ticket.tt_keyrev,
+ env->sc_ticket.tt_key,
+ sizeof(env->sc_ticket.tt_key));
+ }
break;
default:
return (-1);
@@ -1960,69 +1947,6 @@ relay_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
return (0);
}
-DH *
-relay_tls_get_dhparams(int keylen)
-{
- DH *dh;
- BIGNUM *(*prime)(BIGNUM *);
- const char *gen;
-
- gen = "2";
- if (keylen >= 8192)
- prime = get_rfc3526_prime_8192;
- else if (keylen >= 4096)
- prime = get_rfc3526_prime_4096;
- else if (keylen >= 3072)
- prime = get_rfc3526_prime_3072;
- else if (keylen >= 2048)
- prime = get_rfc3526_prime_2048;
- else if (keylen >= 1536)
- prime = get_rfc3526_prime_1536;
- else
- prime = get_rfc2409_prime_1024;
-
- if ((dh = DH_new()) == NULL)
- return (NULL);
-
- dh->p = (*prime)(NULL);
- BN_dec2bn(&dh->g, gen);
-
- if (dh->p == NULL || dh->g == NULL) {
- DH_free(dh);
- return (NULL);
- }
-
- return (dh);
-}
-
-DH *
-relay_tls_callback_dh(SSL *ssl, int export, int keylen)
-{
- struct ctl_relay_event *cre;
- EVP_PKEY *pkey;
- int keytype, maxlen;
- DH *dh = NULL;
-
- /* Get maximum key length from config */
- if ((cre = (struct ctl_relay_event *)SSL_get_app_data(ssl)) == NULL)
- return (NULL);
- maxlen = cre->con->se_relay->rl_proto->tlsdhparams;
-
- /* Get the private key length from the cert */
- if ((pkey = SSL_get_privatekey(ssl))) {
- keytype = EVP_PKEY_type(pkey->type);
- if (keytype == EVP_PKEY_RSA || keytype == EVP_PKEY_DSA)
- keylen = EVP_PKEY_bits(pkey);
- else
- return (NULL);
- }
-
- /* get built-in params based on the shorter key length */
- dh = relay_tls_get_dhparams(MINIMUM(keylen, maxlen));
-
- return (dh);
-}
-
int
relay_dispatch_hce(int fd, struct privsep_proc *p, struct imsg *imsg)
{
@@ -2034,316 +1958,357 @@ relay_dispatch_hce(int fd, struct privsep_proc *p, struct imsg *imsg)
return (-1);
}
-SSL_CTX *
-relay_tls_ctx_create(struct relay *rlay)
+static int
+relay_tls_ctx_create_proto(struct protocol *proto, struct tls_config *tls_cfg)
{
- struct protocol *proto = rlay->rl_proto;
- SSL_CTX *ctx;
- EC_KEY *ecdhkey;
+ uint32_t protocols = 0;
- ctx = SSL_CTX_new(SSLv23_method());
- if (ctx == NULL)
- goto err;
+ /* Set the allowed SSL protocols */
+ if (proto->tlsflags & TLSFLAG_TLSV1_0)
+ protocols |= TLS_PROTOCOL_TLSv1_0;
+ if (proto->tlsflags & TLSFLAG_TLSV1_1)
+ protocols |= TLS_PROTOCOL_TLSv1_1;
+ if (proto->tlsflags & TLSFLAG_TLSV1_2)
+ protocols |= TLS_PROTOCOL_TLSv1_2;
+ if (tls_config_set_protocols(tls_cfg, protocols) == -1) {
+ log_warnx("could not set the TLS protocol: %s",
+ tls_config_error(tls_cfg));
+ return (-1);
+ }
+
+ if (tls_config_set_ciphers(tls_cfg, proto->tlsciphers)) {
+ log_warnx("could not set the TLS cypers: %s",
+ tls_config_error(tls_cfg));
+ return (-1);
+ }
+
+ if ((proto->tlsflags & TLSFLAG_CIPHER_SERVER_PREF) == 0)
+ tls_config_prefer_ciphers_client(tls_cfg);
/*
- * Disable the session cache by default.
- * Everything modern uses tickets
+ * Set session ID context to a random value. It needs to be the
+ * same accross all relay processes or session caching will fail.
*/
- SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+ if (tls_config_set_session_id(tls_cfg, env->sc_conf.tls_sid,
+ sizeof(env->sc_conf.tls_sid)) == -1) {
+ log_warnx("could not set the TLS session ID: %s",
+ tls_config_error(tls_cfg));
+ return (-1);
+ }
/* Set callback for TLS session tickets if enabled */
- if (proto->tickets == -1)
- SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
- else {
- if (!SSL_CTX_set_tlsext_ticket_key_cb(ctx,
- relay_tls_session_ticket))
- log_warnx("could not set the TLS ticket callback");
+ if (proto->tickets == 1) {
/* set timeout to the ticket rekey time */
- SSL_CTX_set_timeout(ctx, TLS_TICKET_REKEY_TIME);
+ tls_config_set_session_lifetime(tls_cfg, TLS_SESSION_LIFETIME);
+
+ tls_config_add_ticket_key(tls_cfg,
+ env->sc_ticket.tt_keyrev, env->sc_ticket.tt_key,
+ sizeof(env->sc_ticket.tt_key));
}
- /* Enable all workarounds and set SSL options */
- SSL_CTX_set_options(ctx, SSL_OP_ALL);
- SSL_CTX_set_options(ctx,
- SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
- if (proto->tlsflags & TLSFLAG_CIPHER_SERVER_PREF)
- SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
- if ((proto->tlsflags & TLSFLAG_CLIENT_RENEG) == 0)
- SSL_CTX_set_options(ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
+ if (tls_config_set_ecdhecurve(tls_cfg, proto->tlsecdhcurve) != 0) {
+ log_warnx("failed to set ecdh curve %s: %s",
+ proto->tlsecdhcurve, tls_config_error(tls_cfg));
+ return (-1);
+ }
- /* Set the allowed SSL protocols */
- SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
- SSL_CTX_clear_options(ctx, SSL_OP_NO_SSLv3);
- if ((proto->tlsflags & TLSFLAG_SSLV3) == 0)
- SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
- SSL_CTX_clear_options(ctx, SSL_OP_NO_TLSv1);
- if ((proto->tlsflags & TLSFLAG_TLSV1_0) == 0)
- SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
- SSL_CTX_clear_options(ctx, SSL_OP_NO_TLSv1_1);
- if ((proto->tlsflags & TLSFLAG_TLSV1_1) == 0)
- SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
- SSL_CTX_clear_options(ctx, SSL_OP_NO_TLSv1_2);
- if ((proto->tlsflags & TLSFLAG_TLSV1_2) == 0)
- SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
-
- if (proto->tlsecdhcurve > 0) {
- /* Enable ECDHE support for TLS perfect forward secrecy */
- if ((ecdhkey =
- EC_KEY_new_by_curve_name(proto->tlsecdhcurve)) == NULL)
- goto err;
- SSL_CTX_set_tmp_ecdh(ctx, ecdhkey);
- SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
- EC_KEY_free(ecdhkey);
+ if (tls_config_set_dheparams(tls_cfg, proto->tlsdhparams) != 0) {
+ log_warnx("failed to set dh params %s: %s",
+ proto->tlsdhparams, tls_config_error(tls_cfg));
+ return (-1);
}
- if (proto->tlsdhparams > 0) {
- /* Enable EDH params (forward secrecy for older clients) */
- SSL_CTX_set_tmp_dh_callback(ctx, relay_tls_callback_dh);
+ return (0);
+}
+
+/*
+ * This function is not publicy exported because it is a hack until libtls
+ * has a proper privsep setup
+ */
+void tls_config_skip_private_key_check(struct tls_config *config);
+
+int
+relay_tls_ctx_create(struct relay *rlay)
+{
+ struct tls_config *tls_cfg, *tls_client_cfg;
+ struct tls *tls = NULL;
+ const char *fake_key;
+ int fake_keylen;
+
+ if ((tls_cfg = tls_config_new()) == NULL) {
+ log_warnx("unable to allocate TLS config");
+ return (-1);
+ }
+ if ((tls_client_cfg = tls_config_new()) == NULL) {
+ log_warnx("unable to allocate TLS config");
+ return (-1);
}
- if (!SSL_CTX_set_cipher_list(ctx, proto->tlsciphers))
+ if (relay_tls_ctx_create_proto(rlay->rl_proto, tls_cfg) == -1)
+ goto err;
+ if (relay_tls_ctx_create_proto(rlay->rl_proto, tls_client_cfg) == -1)
goto err;
/* Verify the server certificate if we have a CA chain */
- if ((rlay->rl_conf.flags & F_TLSCLIENT) &&
- (rlay->rl_tls_ca != NULL)) {
- if (!SSL_CTX_load_verify_mem(ctx,
- rlay->rl_tls_ca, rlay->rl_conf.tls_ca_len))
- goto err;
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
+ if (rlay->rl_conf.flags & F_TLSCLIENT) {
+ /*
+ * Currently relayd can't verify the name of certs and changing
+ * this is non trivial. For now just disable name verification.
+ */
+ tls_config_insecure_noverifyname(tls_client_cfg);
+
+ if (rlay->rl_tls_ca != NULL) {
+ if (tls_config_set_ca_mem(tls_client_cfg,
+ rlay->rl_tls_ca, rlay->rl_conf.tls_ca_len) != 0) {
+ log_warnx("failed to set root certificates: %s",
+ tls_config_error(tls_client_cfg));
+ goto err;
+ }
+ } else {
+ /* No root cert available so disable the checking */
+ tls_config_insecure_noverifycert(tls_client_cfg);
+ }
+
+ rlay->rl_tls_client_cfg = tls_client_cfg;
}
- if ((rlay->rl_conf.flags & F_TLS) == 0)
- return (ctx);
+ if (rlay->rl_conf.flags & F_TLS) {
+ log_debug("%s: loading certificate", __func__);
+ /*
+ * Use the public key as the "private" key - the secret key
+ * parameters are hidden in an extra process that will be
+ * contacted by the RSA engine. The SSL/TLS library needs at
+ * least the public key parameters in the current process.
+ * For this we need to skip the private key check done by
+ * libtls.
+ */
+ tls_config_skip_private_key_check(tls_cfg);
- log_debug("%s: loading certificate", __func__);
- if (!SSL_CTX_use_certificate_chain_mem(ctx,
- rlay->rl_tls_cert, rlay->rl_conf.tls_cert_len))
- goto err;
+ if ((fake_keylen = ssl_ctx_fake_private_key(rlay->rl_tls_cert,
+ rlay->rl_conf.tls_cert_len, &fake_key)) == -1) {
+ /* error already printed */
+ goto err;
+ }
- log_debug("%s: loading private key", __func__);
- if (!ssl_ctx_fake_private_key(ctx,
- &rlay->rl_conf.tls_keyid, sizeof(rlay->rl_conf.tls_keyid),
- rlay->rl_tls_cert, rlay->rl_conf.tls_cert_len,
- &rlay->rl_tls_x509, &rlay->rl_tls_pkey))
- goto err;
+ if (tls_config_set_keypair_ocsp_mem(tls_cfg,
+ rlay->rl_tls_cert, rlay->rl_conf.tls_cert_len,
+ fake_key, fake_keylen, NULL, 0) != 0) {
+ log_warnx("failed to set tls certificate: %s",
+ tls_config_error(tls_cfg));
+ goto err;
+ }
- if (!SSL_CTX_check_private_key(ctx))
- goto err;
+ if (rlay->rl_conf.tls_cacert_len) {
+ log_debug("%s: loading CA certificate", __func__);
+ if (!ssl_load_pkey(&rlay->rl_conf.tls_cakeyid,
+ rlay->rl_tls_cacert, rlay->rl_conf.tls_cacert_len,
+ &rlay->rl_tls_cacertx509, &rlay->rl_tls_capkey))
+ goto err;
+ /* loading certificate public key */
+ if (!ssl_load_pkey(NULL,
+ rlay->rl_tls_cert, rlay->rl_conf.tls_cert_len,
+ NULL, &rlay->rl_tls_pkey))
+ goto err;
+ }
- if (rlay->rl_conf.tls_cacert_len) {
- log_debug("%s: loading CA private key", __func__);
- if (!ssl_load_pkey(&rlay->rl_conf.tls_cakeyid,
- sizeof(rlay->rl_conf.tls_cakeyid),
- rlay->rl_tls_cacert, rlay->rl_conf.tls_cacert_len,
- &rlay->rl_tls_cacertx509, &rlay->rl_tls_capkey))
+ tls = tls_server();
+ if (tls == NULL) {
+ log_warnx("unable to allocate TLS context");
+ goto err;
+ }
+ if (tls_configure(tls, tls_cfg) == -1) {
+ log_warnx("could not configure the TLS context: %s",
+ tls_error(tls));
+ tls_free(tls);
goto err;
+ }
+ rlay->rl_tls_cfg = tls_cfg;
+ rlay->rl_tls_ctx = tls;
}
- /*
- * Set session ID context to a random value. It needs to be the
- * same accross all relay processes or session caching will fail.
- */
- if (!SSL_CTX_set_session_id_context(ctx, env->sc_conf.tls_sid,
- sizeof(env->sc_conf.tls_sid)))
- goto err;
-
/* The text versions of the keys/certs are not needed anymore */
purge_key(&rlay->rl_tls_cert, rlay->rl_conf.tls_cert_len);
purge_key(&rlay->rl_tls_cacert, rlay->rl_conf.tls_cacert_len);
- return (ctx);
+ if (rlay->rl_tls_client_cfg == NULL)
+ tls_config_free(tls_client_cfg);
+ if (rlay->rl_tls_cfg == NULL)
+ tls_config_free(tls_cfg);
+ return (0);
err:
- SSL_CTX_free(ctx);
- ssl_error(rlay->rl_conf.name, "relay_tls_ctx_create");
- return (NULL);
+ tls_config_free(tls_client_cfg);
+ tls_config_free(tls_cfg);
+ return (-1);
}
-void
-relay_tls_transaction(struct rsession *con, struct ctl_relay_event *cre)
+static struct tls *
+relay_tls_inspect_create(struct relay *rlay, struct ctl_relay_event *cre)
{
- struct relay *rlay = con->se_relay;
- SSL *ssl;
- const SSL_METHOD *method;
- void (*cb)(int, short, void *);
- u_int flag;
-
- ssl = SSL_new(rlay->rl_ssl_ctx);
- if (ssl == NULL)
+ struct tls_config *tls_cfg;
+ struct tls *tls;
+ const char *fake_key;
+ int fake_keylen;
+
+ /* TLS inspection: use session-specific certificate */
+ if ((tls_cfg = tls_config_new()) == NULL) {
+ log_warnx("unable to allocate TLS config");
+ goto err;
+ }
+ if (relay_tls_ctx_create_proto(rlay->rl_proto,
+ tls_cfg) == -1) {
+ tls_config_free(tls_cfg);
goto err;
-
- if (cre->dir == RELAY_DIR_REQUEST) {
- cb = relay_tls_accept;
- method = SSLv23_server_method();
- flag = EV_READ;
-
- /* Use session-specific certificate for TLS inspection. */
- if (cre->tlscert != NULL)
- SSL_use_certificate(ssl, cre->tlscert);
- } else {
- cb = relay_tls_connect;
- method = SSLv23_client_method();
- flag = EV_WRITE;
}
- if (!SSL_set_ssl_method(ssl, method))
+ log_debug("%s: loading intercepted certificate", __func__);
+ if ((fake_keylen = ssl_ctx_fake_private_key(cre->tlscert,
+ cre->tlscert_len, &fake_key)) == -1) {
+ /* error already printed */
goto err;
- if (!SSL_set_fd(ssl, cre->s))
+ }
+ if (tls_config_set_keypair_ocsp_mem(tls_cfg,
+ cre->tlscert, cre->tlscert_len, fake_key, fake_keylen,
+ NULL, 0) != 0) {
+ log_warnx("failed to set tls certificate: %s",
+ tls_config_error(tls_cfg));
goto err;
+ }
- if (cre->dir == RELAY_DIR_REQUEST)
- SSL_set_accept_state(ssl);
- else
- SSL_set_connect_state(ssl);
-
- SSL_set_app_data(ssl, cre);
- cre->ssl = ssl;
-
- DPRINTF("%s: session %d: scheduling on %s", __func__, con->se_id,
- (flag == EV_READ) ? "EV_READ" : "EV_WRITE");
- event_again(&con->se_ev, cre->s, EV_TIMEOUT|flag, cb,
- &con->se_tv_start, &rlay->rl_conf.timeout, con);
- return;
+ tls = tls_server();
+ if (tls == NULL) {
+ log_warnx("unable to allocate TLS context");
+ goto err;
+ }
+ if (tls_configure(tls, tls_cfg) == -1) {
+ log_warnx("could not configure the TLS context: %s",
+ tls_error(tls));
+ tls_free(tls);
+ goto err;
+ }
+ cre->tls_cfg = tls_cfg;
+ return (tls);
err:
- SSL_free(ssl);
- ssl_error(rlay->rl_conf.name, "relay_tls_transaction");
- relay_close(con, "session tls failed");
+ tls_config_free(tls_cfg);
+ return (NULL);
}
void
-relay_tls_accept(int fd, short event, void *arg)
+relay_tls_transaction(struct rsession *con, struct ctl_relay_event *cre)
{
- struct rsession *con = arg;
- struct relay *rlay = con->se_relay;
- int retry_flag = 0;
- int tls_err = 0;
- int ret;
-
- if (event == EV_TIMEOUT) {
- relay_close(con, "TLS accept timeout");
- return;
- }
+ struct relay *rlay = con->se_relay;
+ struct tls *tls_server;
+ const char *errstr;
+ u_int flag;
- ret = SSL_accept(con->se_in.ssl);
- if (ret <= 0) {
- tls_err = SSL_get_error(con->se_in.ssl, ret);
+ if (cre->dir == RELAY_DIR_REQUEST) {
+ if (cre->tlscert != NULL)
+ tls_server = relay_tls_inspect_create(rlay, cre);
+ else
+ tls_server = rlay->rl_tls_ctx;
- switch (tls_err) {
- case SSL_ERROR_WANT_READ:
- retry_flag = EV_READ;
- goto retry;
- case SSL_ERROR_WANT_WRITE:
- retry_flag = EV_WRITE;
- goto retry;
- case SSL_ERROR_ZERO_RETURN:
- case SSL_ERROR_SYSCALL:
- if (ret == 0) {
- relay_close(con, "closed");
- return;
- }
- /* FALLTHROUGH */
- default:
- ssl_error(rlay->rl_conf.name, "relay_tls_accept");
- relay_close(con, "TLS accept error");
- return;
+ if (tls_accept_socket(tls_server, &cre->tls, cre->s) == -1) {
+ errstr = "could not accept the TLS connection";
+ goto err;
+ }
+ if (cre->tlscert != NULL)
+ tls_free(tls_server);
+ flag = EV_READ;
+ } else {
+ cre->tls = tls_client();
+ if (cre->tls == NULL ||
+ tls_configure(cre->tls, rlay->rl_tls_client_cfg) == -1) {
+ errstr = "could not configure the TLS client context";
+ goto err;
+ }
+ if (tls_connect_socket(cre->tls, cre->s, NULL) == -1) {
+ errstr = "could not connect the TLS connection";
+ goto err;
}
+ flag = EV_WRITE;
}
-
-#ifdef DEBUG
- log_info(
-#else
- log_debug(
-#endif
- "relay %s, session %d established (%d active)",
- rlay->rl_conf.name, con->se_id, relay_sessions);
-
- relay_session(con);
+ log_debug("%s: session %d: scheduling on %s", __func__, con->se_id,
+ (flag == EV_READ) ? "EV_READ" : "EV_WRITE");
+ event_again(&con->se_ev, cre->s, EV_TIMEOUT|flag, relay_tls_handshake,
+ &con->se_tv_start, &rlay->rl_conf.timeout, cre);
return;
-retry:
- DPRINTF("%s: session %d: scheduling on %s", __func__, con->se_id,
- (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE");
- event_again(&con->se_ev, fd, EV_TIMEOUT|retry_flag, relay_tls_accept,
- &con->se_tv_start, &rlay->rl_conf.timeout, con);
+ err:
+ relay_close(con, errstr);
}
void
-relay_tls_connect(int fd, short event, void *arg)
+relay_tls_handshake(int fd, short event, void *arg)
{
- struct rsession *con = arg;
- struct relay *rlay = con->se_relay;
- int retry_flag = 0;
- int tls_err = 0;
- int ret;
- X509 *servercert = NULL;
+ struct ctl_relay_event *cre = arg;
+ struct rsession *con = cre->con;
+ struct relay *rlay = con->se_relay;
+ int retry_flag = 0;
+ int ret;
if (event == EV_TIMEOUT) {
- relay_close(con, "TLS connect timeout");
+ relay_close(con, "TLS handshake timeout");
return;
}
- ret = SSL_connect(con->se_out.ssl);
- if (ret <= 0) {
- tls_err = SSL_get_error(con->se_out.ssl, ret);
-
- switch (tls_err) {
- case SSL_ERROR_WANT_READ:
- retry_flag = EV_READ;
- goto retry;
- case SSL_ERROR_WANT_WRITE:
- retry_flag = EV_WRITE;
- goto retry;
- case SSL_ERROR_ZERO_RETURN:
- case SSL_ERROR_SYSCALL:
- if (ret == 0) {
- relay_close(con, "closed");
- return;
- }
- /* FALLTHROUGH */
- default:
- ssl_error(rlay->rl_conf.name, "relay_tls_connect");
- relay_close(con, "TLS connect error");
- return;
- }
- }
-
+ ret = tls_handshake(cre->tls);
+ if (ret == 0) {
#ifdef DEBUG
- log_info(
+ log_info(
#else
- log_debug(
+ log_debug(
#endif
- "relay %s, tls session %d connected (%d active)",
- rlay->rl_conf.name, con->se_id, relay_sessions);
+ "relay %s, tls session %d %s (%d active)",
+ rlay->rl_conf.name, con->se_id,
+ cre->dir == RELAY_DIR_REQUEST ? "established" : "connected",
+ relay_sessions);
- if (rlay->rl_conf.flags & F_TLSINSPECT) {
- if ((servercert =
- SSL_get_peer_certificate(con->se_out.ssl)) != NULL) {
- con->se_in.tlscert =
- ssl_update_certificate(servercert,
- rlay->rl_tls_pkey, rlay->rl_tls_capkey,
- rlay->rl_tls_cacertx509);
- } else
- con->se_in.tlscert = NULL;
- if (servercert != NULL)
- X509_free(servercert);
- if (con->se_in.tlscert == NULL)
- relay_close(con, "could not create certificate");
- else
+ if (cre->dir == RELAY_DIR_REQUEST) {
relay_session(con);
+ return;
+ }
+
+ if (rlay->rl_conf.flags & F_TLSINSPECT) {
+ const uint8_t *servercert;
+ size_t len;
+
+ servercert = tls_peer_cert_chain_pem(con->se_out.tls,
+ &len);
+ if (servercert != NULL) {
+ con->se_in.tlscert = ssl_update_certificate(
+ servercert, len,
+ rlay->rl_tls_pkey, rlay->rl_tls_capkey,
+ rlay->rl_tls_cacertx509,
+ &con->se_in.tlscert_len);
+ } else
+ con->se_in.tlscert = NULL;
+ if (con->se_in.tlscert == NULL)
+ relay_close(con,
+ "could not create certificate");
+ else
+ relay_session(con);
+ return;
+ }
+ relay_connected(fd, EV_WRITE, con);
+ return;
+ } else if (ret == TLS_WANT_POLLIN) {
+ retry_flag = EV_READ;
+ } else if (ret == TLS_WANT_POLLOUT) {
+ retry_flag = EV_WRITE;
+ } else {
+ log_debug("TLS handshake failed: %s: %s: %s",
+ rlay->rl_conf.name, __func__,
+ tls_error(cre->tls));
+ relay_close(con, "TLS handshake error");
return;
}
- relay_connected(fd, EV_WRITE, con);
- return;
-
-retry:
DPRINTF("%s: session %d: scheduling on %s", __func__, con->se_id,
(retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE");
- event_again(&con->se_ev, fd, EV_TIMEOUT|retry_flag, relay_tls_connect,
- &con->se_tv_start, &rlay->rl_conf.timeout, con);
+ event_again(&con->se_ev, fd, EV_TIMEOUT|retry_flag, relay_tls_handshake,
+ &con->se_tv_start, &rlay->rl_conf.timeout, cre);
}
void
@@ -2365,11 +2330,9 @@ relay_tls_readcb(int fd, short event, void *arg)
char rbuf[IBUF_READ_SIZE];
struct bufferevent *bufev = arg;
struct ctl_relay_event *cre = bufev->cbarg;
- struct rsession *con = cre->con;
- struct relay *rlay = con->se_relay;
- int ret = 0, tls_err = 0;
short what = EVBUFFER_READ;
int howmuch = IBUF_READ_SIZE;
+ ssize_t ret;
size_t len;
if (event == EV_TIMEOUT) {
@@ -2380,29 +2343,18 @@ relay_tls_readcb(int fd, short event, void *arg)
if (bufev->wm_read.high != 0)
howmuch = MINIMUM(sizeof(rbuf), bufev->wm_read.high);
- ret = SSL_read(cre->ssl, rbuf, howmuch);
- if (ret <= 0) {
- tls_err = SSL_get_error(cre->ssl, ret);
+ ret = tls_read(cre->tls, rbuf, howmuch);
+ if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) {
+ goto retry;
+ } else if (ret < 0) {
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+ len = ret;
- switch (tls_err) {
- case SSL_ERROR_WANT_READ:
- DPRINTF("%s: session %d: want read",
- __func__, con->se_id);
- goto retry;
- case SSL_ERROR_WANT_WRITE:
- DPRINTF("%s: session %d: want write",
- __func__, con->se_id);
- goto retry;
- default:
- if (ret == 0)
- what |= EVBUFFER_EOF;
- else {
- ssl_error(rlay->rl_conf.name,
- "relay_tls_readcb");
- what |= EVBUFFER_ERROR;
- }
- goto err;
- }
+ if (len == 0) {
+ what |= EVBUFFER_EOF;
+ goto err;
}
if (evbuffer_add(bufev->input, rbuf, ret) == -1) {
@@ -2439,9 +2391,8 @@ relay_tls_writecb(int fd, short event, void *arg)
{
struct bufferevent *bufev = arg;
struct ctl_relay_event *cre = bufev->cbarg;
- struct rsession *con = cre->con;
- struct relay *rlay = con->se_relay;
- int ret = 0, tls_err;
+ ssize_t ret;
+ size_t len;
short what = EVBUFFER_WRITE;
if (event == EV_TIMEOUT) {
@@ -2450,46 +2401,16 @@ relay_tls_writecb(int fd, short event, void *arg)
}
if (EVBUFFER_LENGTH(bufev->output)) {
- if (cre->buf == NULL) {
- cre->buflen = EVBUFFER_LENGTH(bufev->output);
- if ((cre->buf = malloc(cre->buflen)) == NULL) {
- what |= EVBUFFER_ERROR;
- goto err;
- }
- bcopy(EVBUFFER_DATA(bufev->output),
- cre->buf, cre->buflen);
- }
-
- ret = SSL_write(cre->ssl, cre->buf, cre->buflen);
- if (ret <= 0) {
- tls_err = SSL_get_error(cre->ssl, ret);
-
- switch (tls_err) {
- case SSL_ERROR_WANT_READ:
- DPRINTF("%s: session %d: want read",
- __func__, con->se_id);
- goto retry;
- case SSL_ERROR_WANT_WRITE:
- DPRINTF("%s: session %d: want write",
- __func__, con->se_id);
- goto retry;
- default:
- if (ret == 0)
- what |= EVBUFFER_EOF;
- else {
- ssl_error(rlay->rl_conf.name,
- "relay_tls_writecb");
- what |= EVBUFFER_ERROR;
- }
- goto err;
- }
+ ret = tls_write(cre->tls, EVBUFFER_DATA(bufev->output),
+ EVBUFFER_LENGTH(bufev->output));
+ if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) {
+ goto retry;
+ } else if (ret < 0) {
+ what |= EVBUFFER_ERROR;
+ goto err;
}
- evbuffer_drain(bufev->output, ret);
- }
- if (cre->buf != NULL) {
- free(cre->buf);
- cre->buf = NULL;
- cre->buflen = 0;
+ len = ret;
+ evbuffer_drain(bufev->output, len);
}
if (EVBUFFER_LENGTH(bufev->output) != 0)
@@ -2501,71 +2422,13 @@ relay_tls_writecb(int fd, short event, void *arg)
return;
retry:
- if (cre->buflen != 0)
- relay_bufferevent_add(&bufev->ev_write, bufev->timeout_write);
+ relay_bufferevent_add(&bufev->ev_write, bufev->timeout_write);
return;
err:
- if (cre->buf != NULL) {
- free(cre->buf);
- cre->buf = NULL;
- cre->buflen = 0;
- }
(*bufev->errorcb)(bufev, what, bufev->cbarg);
}
-struct tls_ticket *
-relay_get_ticket_key(unsigned char *keyname)
-{
- if (keyname) {
- if (timingsafe_memcmp(keyname,
- env->sc_tls_ticket_bak.tt_key_name,
- sizeof(env->sc_tls_ticket_bak.tt_key_name)) == 0)
- return &env->sc_tls_ticket_bak;
- if (timingsafe_memcmp(keyname,
- env->sc_tls_ticket.tt_key_name,
- sizeof(env->sc_tls_ticket.tt_key_name)) == 0)
- return &env->sc_tls_ticket;
- return NULL;
- }
- return &env->sc_tls_ticket;
-}
-
-int
-relay_tls_session_ticket(SSL *ssl, unsigned char *keyname, unsigned char *iv,
- EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int mode)
-{
- struct tls_ticket *key;
-
- if (mode == 1) {
- /* create new session */
- key = relay_get_ticket_key(NULL);
- memcpy(keyname, key->tt_key_name, sizeof(key->tt_key_name));
- arc4random_buf(iv, EVP_MAX_IV_LENGTH);
- EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL,
- key->tt_aes_key, iv);
- HMAC_Init_ex(hctx, key->tt_hmac_key, sizeof(key->tt_hmac_key),
- EVP_sha256(), NULL);
- return 0;
- } else {
- /* get key by name */
- key = relay_get_ticket_key(keyname);
- if (!key)
- return 0;
-
- EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL,
- key->tt_aes_key, iv);
- HMAC_Init_ex(hctx, key->tt_hmac_key, sizeof(key->tt_hmac_key),
- EVP_sha256(), NULL);
-
- /* time to renew the ticket? */
- if (key->tt_backup) {
- return 2;
- }
- return 1;
- }
-}
-
int
relay_bufferevent_add(struct event *ev, int timeout)
{