On Tue, Aug 30, 2016 at 03:51:04PM +0200, Claudio Jeker wrote: > On Tue, Aug 30, 2016 at 02:44:17PM +0200, Reyk Floeter wrote: > > On Tue, Aug 30, 2016 at 01:22:49PM +0200, Claudio Jeker wrote: > > > Here is the latest version of the ticket and tls session cache support. > > > Tickets can be disabled and also the session timeout is configurable. > > > Same code as before with man page diff > > > > > > > Nice work! I'm curious how this impact production, do you have any > > experience to share? > > > > > Will commit this soonish unless somebody complains > > > > I do complain, but just a bit. nit-picking below. > >
After some more talking to people here a new version that drops the session cache and only implements session tickets. Everything modern (aka the stuff we care about) implements tickets and they are so much easier to work with. The system in place now uses a primary key and a backup key and will refresh the tickets for everything that was encrypted with the backup key. The keys are rotated every 2h so at max a key is valid for 4h. We set the tls session timeout also to 2h so that this value is announced as TLS session ticket lifetime hint. It is still possible to disable the tickets per relay just in case. -- :wq Claudio Index: ca.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/ca.c,v retrieving revision 1.16 diff -u -p -r1.16 ca.c --- ca.c 5 Dec 2015 13:13:11 -0000 1.16 +++ ca.c 30 Aug 2016 12:59:36 -0000 @@ -23,6 +23,7 @@ #include <unistd.h> #include <string.h> #include <stdlib.h> +#include <poll.h> #include <imsg.h> #include <openssl/bio.h> @@ -256,6 +257,7 @@ static int rsae_send_imsg(int flen, const u_char *from, u_char *to, RSA *rsa, int padding, u_int cmd) { + struct pollfd pfd[1]; struct ctl_keyop cko; int ret = 0; objid_t *id; @@ -292,9 +294,21 @@ rsae_send_imsg(int flen, const u_char *f * operation in OpenSSL's engine layer. */ imsg_composev(ibuf, cmd, 0, 0, -1, iov, cnt); - imsg_flush(ibuf); + if (imsg_flush(ibuf) == -1) + log_warn("rsae_send_imsg: imsg_flush"); + pfd[0].fd = ibuf->fd; + pfd[0].events = POLLIN; while (!done) { + switch (poll(pfd, 1, TLS_PRIV_SEND_TIMEOUT)) { + case -1: + fatal("rsae_send_imsg: poll"); + case 0: + log_warnx("rsae_send_imsg: poll timeout"); + break; + default: + break; + } if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) fatalx("imsg_read"); if (n == 0) Index: config.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/config.c,v retrieving revision 1.27 diff -u -p -r1.27 config.c --- config.c 7 Dec 2015 04:03:27 -0000 1.27 +++ config.c 31 Aug 2016 12:53:03 -0000 @@ -94,7 +94,6 @@ config_init(struct relayd *env) bzero(&env->sc_proto_default, sizeof(env->sc_proto_default)); env->sc_proto_default.id = EMPTY_ID; env->sc_proto_default.flags = F_USED; - env->sc_proto_default.cache = RELAY_CACHESIZE; env->sc_proto_default.tcpflags = TCPFLAG_DEFAULT; env->sc_proto_default.tcpbacklog = RELAY_BACKLOG; env->sc_proto_default.tlsflags = TLSFLAG_DEFAULT; Index: parse.y =================================================================== RCS file: /cvs/src/usr.sbin/relayd/parse.y,v retrieving revision 1.207 diff -u -p -r1.207 parse.y --- parse.y 21 Jun 2016 21:35:25 -0000 1.207 +++ parse.y 1 Sep 2016 08:25:35 -0000 @@ -172,13 +172,13 @@ typedef struct { %token SOCKET SPLICE SSL STICKYADDR STYLE TABLE TAG TAGGED TCP TIMEOUT TLS TO %token ROUTER RTLABEL TRANSPARENT TRAP UPDATES URL VIRTUAL WITH TTL RTABLE %token MATCH PARAMS RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE PASSWORD ECDH -%token EDH CURVE +%token EDH CURVE TICKETS %token <v.string> STRING %token <v.number> NUMBER %type <v.string> hostname interface table value optstring %type <v.number> http_type loglevel quick trap %type <v.number> dstmode flag forwardmode retry -%type <v.number> opttls opttlsclient tlscache +%type <v.number> opttls opttlsclient tlstickets %type <v.number> redirect_proto relay_proto match %type <v.number> action ruleaf key_option %type <v.number> tlsdhparams tlsecdhcurve @@ -996,7 +996,6 @@ proto : relay_proto PROTO STRING { free($3); p->id = ++last_proto_id; p->type = $1; - p->cache = RELAY_CACHESIZE; p->tcpflags = TCPFLAG_DEFAULT; p->tlsflags = TLSFLAG_DEFAULT; p->tcpbacklog = RELAY_BACKLOG; @@ -1091,7 +1090,7 @@ tlsflags_l : tlsflags comma tlsflags_l | tlsflags ; -tlsflags : SESSION CACHE tlscache { proto->cache = $3; } +tlsflags : SESSION TICKETS tlstickets { proto->tickets = $3; } | CIPHERS STRING { if (strlcpy(proto->tlsciphers, $2, sizeof(proto->tlsciphers)) >= @@ -1180,15 +1179,7 @@ flag : STRING { } ; -tlscache : NUMBER { - if ($1 < 0) { - yyerror("invalid tlscache value: %d", $1); - YYERROR; - } - $$ = $1; - } - | DISABLE { $$ = -2; } - ; +tlstickets : DISABLE { $$ = -1; } filterrule : action dir quick ruleaf rulesrc ruledst { if ((rule = calloc(1, sizeof(*rule))) == NULL) @@ -2257,6 +2248,7 @@ lookup(char *s) { "tag", TAG }, { "tagged", TAGGED }, { "tcp", TCP }, + { "tickets", TICKETS }, { "timeout", TIMEOUT }, { "tls", TLS }, { "to", TO }, Index: relay.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relay.c,v retrieving revision 1.206 diff -u -p -r1.206 relay.c --- relay.c 30 Dec 2015 16:00:57 -0000 1.206 +++ relay.c 1 Sep 2016 08:54:00 -0000 @@ -28,6 +28,7 @@ #include <arpa/inet.h> #include <limits.h> +#include <poll.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> @@ -85,6 +86,10 @@ void relay_tls_connected(struct ctl_re void relay_tls_readcb(int, short, void *); void relay_tls_writecb(int, short, void *); +struct tls_ticket *relay_get_ticket_key(unsigned char [16]); +int relay_tls_session_ticket(SSL *, unsigned char [16], + 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 *); @@ -107,6 +112,7 @@ pid_t relay(struct privsep *ps, struct privsep_proc *p) { pid_t pid; + env = ps->ps_env; pid = proc_run(ps, p, procs, nitems(procs), relay_init, NULL); relay_http(env); @@ -260,8 +266,8 @@ relay_protodebug(struct relay *rlay) if ((rlay->rl_conf.flags & (F_TLS|F_TLSCLIENT)) && proto->tlsflags) fprintf(stderr, "\ttls flags: %s\n", printb_flags(proto->tlsflags, TLSFLAG_BITS)); - if (proto->cache != -1) - fprintf(stderr, "\ttls session cache: %d\n", proto->cache); + fprintf(stderr, "\ttls session tickets: %s\n", + (proto->tickets > -1) ? "enabled" : "disabled"); fprintf(stderr, "\ttype: "); switch (proto->type) { case RELAY_PROTO_TCP: @@ -1924,6 +1930,15 @@ relay_dispatch_parent(int fd, struct pri case IMSG_CTL_RESET: 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)); + break; default: return (-1); } @@ -2045,21 +2060,27 @@ relay_tls_ctx_create(struct relay *rlay) struct protocol *proto = rlay->rl_proto; SSL_CTX *ctx; EC_KEY *ecdhkey; - u_int8_t sid[SSL_MAX_SID_CTX_LENGTH]; ctx = SSL_CTX_new(SSLv23_method()); if (ctx == NULL) goto err; - /* Modify session timeout and cache size*/ - SSL_CTX_set_timeout(ctx, - (long)MINIMUM(rlay->rl_conf.timeout.tv_sec, LONG_MAX)); - if (proto->cache < -1) { - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); - } else if (proto->cache >= -1) { - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER); - if (proto->cache >= 0) - SSL_CTX_sess_set_cache_size(ctx, proto->cache); + /* + * Disable the session cache by default. + * Everything modern uses tickets + */ + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); + + + /* 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"); + /* set timeout to the ticket rekey time */ + SSL_CTX_set_timeout(ctx, TLS_TICKET_REKEY_TIME); } /* Enable all workarounds and set SSL options */ @@ -2142,12 +2163,11 @@ relay_tls_ctx_create(struct relay *rlay) } /* - * Set session ID context to a random value. We don't support - * persistent caching of sessions so it is OK to set a temporary - * session ID context that is valid during run time. + * Set session ID context to a random value. It needs to be the + * same accross all relay processes or session caching will fail. */ - arc4random_buf(sid, sizeof(sid)); - if (!SSL_CTX_set_session_id_context(ctx, sid, sizeof(sid))) + if (!SSL_CTX_set_session_id_context(ctx, env->sc_tls_sid, + sizeof(env->sc_tls_sid))) goto err; /* The text versions of the keys/certs are not needed anymore */ @@ -2534,6 +2554,57 @@ relay_tls_writecb(int fd, short event, v cre->buflen = 0; } (*bufev->errorcb)(bufev, what, bufev->cbarg); +} + +struct tls_ticket * +relay_get_ticket_key(unsigned char keyname[16]) +{ + if (keyname) { + if (timingsafe_memcmp(keyname, + env->sc_tls_ticket_bak.tt_key_name, sizeof(keyname)) == 0) + return &env->sc_tls_ticket_bak; + if (timingsafe_memcmp(keyname, + env->sc_tls_ticket.tt_key_name, sizeof(keyname)) == 0) + return &env->sc_tls_ticket; + return NULL; + } + return &env->sc_tls_ticket; +} + +int +relay_tls_session_ticket(SSL *ssl, unsigned char keyname[16], 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, 16); + 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, 16, EVP_sha256(), NULL); + log_debug("XXX create tls ticket"); + 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, 16, EVP_sha256(), NULL); + + /* time to renew the ticket? */ + if (key->tt_backup) { + log_debug("XXX rekey tls ticket"); + return 2; + } + log_debug("XXX tls ticket primary key"); + return 1; + } } int Index: relayd.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relayd.c,v retrieving revision 1.155 diff -u -p -r1.155 relayd.c --- relayd.c 29 Jul 2016 10:09:26 -0000 1.155 +++ relayd.c 1 Sep 2016 08:53:44 -0000 @@ -63,6 +63,7 @@ int parent_dispatch_relay(int, struct int parent_dispatch_ca(int, struct privsep_proc *, struct imsg *); int bindany(struct ctl_bindany *); +void parent_tls_ticket_rekey(int, short, void *); struct relayd *relayd_env; @@ -212,6 +213,8 @@ main(int argc, char *argv[]) TAILQ_INIT(&env->sc_hosts); TAILQ_INIT(&env->sc_sessions); env->sc_rtable = getrtable(); + /* initialize the TLS session id to a random key for all relay procs */ + arc4random_buf(env->sc_tls_sid, sizeof(env->sc_tls_sid)); if (parse_config(env->sc_conffile, env) == -1) exit(1); @@ -278,6 +281,8 @@ main(int argc, char *argv[]) if (env->sc_flags & (F_TLS|F_TLSCLIENT)) ssl_init(env); + /* rekey the TLS tickets before pushing the config */ + parent_tls_ticket_rekey(0, 0, env); if (parent_configure(env) == -1) fatalx("configuration failed"); @@ -1670,4 +1675,28 @@ accept_reserve(int sockfd, struct sockad DPRINTF("%s: inflight incremented, now %d",__func__, *counter); } return (ret); +} + +void +parent_tls_ticket_rekey(int fd, short events, void *arg) +{ + static struct event rekeyev; + struct relayd *env = arg; + struct timeval tv; + struct tls_ticket key; + + log_debug("relayd_tls_ticket_rekey: rekeying tickets"); + + arc4random_buf(key.tt_key_name, sizeof(key.tt_key_name)); + arc4random_buf(key.tt_hmac_key, sizeof(key.tt_hmac_key)); + arc4random_buf(key.tt_aes_key, sizeof(key.tt_aes_key)); + key.tt_backup = 0; + + proc_compose_imsg(env->sc_ps, PROC_RELAY, -1, IMSG_TLSTICKET_REKEY, + -1, -1, &key, sizeof(key)); + + evtimer_set(&rekeyev, parent_tls_ticket_rekey, env); + timerclear(&tv); + tv.tv_sec = TLS_TICKET_REKEY_TIME; + evtimer_add(&rekeyev, &tv); } Index: relayd.conf.5 =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relayd.conf.5,v retrieving revision 1.171 diff -u -p -r1.171 relayd.conf.5 --- relayd.conf.5 18 Aug 2016 14:12:51 -0000 1.171 +++ relayd.conf.5 1 Sep 2016 08:25:07 -0000 @@ -965,14 +965,8 @@ TLS clients. .It Ic no edh Disable EDH support. This is the default. -.It Ic session cache Ar value -Set the maximum size of the TLS session cache. -If the -.Ar value -is zero, the default size defined by the TLS library will be used. -A positive number will set the maximum size in bytes and the keyword -.Ic disable -will disable the TLS session cache. +.It Ic session tickets disable +Disable TLS session tickets. .It Xo .Op Ic no .Ic sslv3 Index: relayd.h =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v retrieving revision 1.225 diff -u -p -r1.225 relayd.h --- relayd.h 29 Jul 2016 10:09:27 -0000 1.225 +++ relayd.h 1 Sep 2016 08:52:08 -0000 @@ -693,6 +693,16 @@ TAILQ_HEAD(relay_rules, relay_rule); #define TLSDHPARAMS_DEFAULT 0 #define TLSDHPARAMS_MIN 1024 +struct tls_ticket { + /* The key, aes key and hmac key must be 16 bytes / 128bits */ + unsigned char tt_key_name[16]; + unsigned char tt_aes_key[16]; + unsigned char tt_hmac_key[16]; + int tt_backup; +}; +#define TLS_TICKET_REKEY_TIME (2 * 3600) +#define TLS_PRIV_SEND_TIMEOUT 5000 /* 5 sec */ + struct protocol { objid_t id; u_int32_t flags; @@ -710,7 +720,7 @@ struct protocol { char tlscakey[PATH_MAX]; char *tlscapass; char name[MAX_NAME_SIZE]; - int cache; + int tickets; enum prototype type; char *style; @@ -963,7 +973,8 @@ enum imsg_type { IMSG_CA_PRIVENC, IMSG_CA_PRIVDEC, IMSG_SESS_PUBLISH, /* from relay to hce */ - IMSG_SESS_UNPUBLISH + IMSG_SESS_UNPUBLISH, + IMSG_TLSTICKET_REKEY }; enum privsep_procid { @@ -1076,6 +1087,10 @@ struct relayd { struct privsep *sc_ps; int sc_reload; + + char sc_tls_sid[SSL_MAX_SID_CTX_LENGTH]; + struct tls_ticket sc_tls_ticket; + struct tls_ticket sc_tls_ticket_bak; }; #define FSNMP_TRAPONLY 0x01