On Thu, Sep 01, 2016 at 11:27:55AM +0200, Claudio Jeker wrote:
> 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.
> 

See comments below, otherwise OK

> -- 
> :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; }

the grammar should match the other options

        [no] session tickets

instead of
        
        session tickets disable

see manpage bits below

>  
>  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");

I think you missed some log_debug("XXX debug statements that should
not go in this way.

> +             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.

After updating the grammar, it would probably also be worth mentioning
that relayd support TLS tickets instead of session caching, something
like:

.It Xo
.Op Ic no
.Ic session tickets
.Xc
Disable TLS session tickets; enabled by default.
.Nm
supports stateless TLS session tickets instead of the older protocol
option of a shared TLS session cache.

Reyk

>  .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

-- 

Reply via email to