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.
> > 
> > Reyk
> > 
> > > -- 
> > > :wq Claudio
> > > 
> > > Index: Makefile
> > > ===================================================================
> > > RCS file: /cvs/src/usr.sbin/relayd/Makefile,v
> > > retrieving revision 1.29
> > > diff -u -p -r1.29 Makefile
> > > --- Makefile      21 Nov 2015 12:37:42 -0000      1.29
> > > +++ Makefile      19 Jul 2016 08:33:26 -0000
> > > @@ -6,7 +6,7 @@ SRCS+=            agentx.c ca.c carp.c check_icmp.
> > >           check_tcp.c config.c control.c hce.c log.c name2id.c \
> > >           pfe.c pfe_filter.c pfe_route.c proc.c \
> > >           relay.c relay_http.c relay_udp.c relayd.c \
> > > -         shuffle.c snmp.c ssl.c util.c
> > > +         shuffle.c snmp.c ssl.c tlsc.c util.c
> > >  MAN=             relayd.8 relayd.conf.5
> > >  
> > >  LDADD=           -levent -lssl -lcrypto -lutil
> > > 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  19 Jul 2016 13:18:33 -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, 5 * 1000)) {
> > 
> > I don't like this timeout number here, please turn it into a #define
> > and put it into relayd.h, eg.
> > 
> > #define TLS_PRIV_SEND_TIMEOUT       5000    /* 5 sec */
> > 
> 
> Done.
> 
> > > +         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      18 Jul 2016 13:01:35 -0000
> > > @@ -51,6 +51,7 @@ config_init(struct relayd *env)
> > >           ps->ps_what[PROC_CA] = CONFIG_RELAYS;
> > >           ps->ps_what[PROC_RELAY] = CONFIG_RELAYS|
> > >               CONFIG_TABLES|CONFIG_PROTOS|CONFIG_CA_ENGINE;
> > > +         ps->ps_what[PROC_TLSC] = 0;
> > >   }
> > >  
> > >   /* Other configuration */
> > > 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       30 Aug 2016 10:50:16 -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 tlscache tlstickets 
> > > tlstimeout
> > >  %type    <v.number>      redirect_proto relay_proto match
> > >  %type    <v.number>      action ruleaf key_option
> > >  %type    <v.number>      tlsdhparams tlsecdhcurve
> > > @@ -1092,6 +1092,8 @@ tlsflags_l  : tlsflags comma tlsflags_l
> > >           ;
> > >  
> > >  tlsflags : SESSION CACHE tlscache        { proto->cache = $3; }
> > > +         | SESSION TICKETS tlstickets    { proto->tickets = $3; }
> > > +         | SESSION TIMEOUT tlstimeout    { proto->tlstimeout = $3; }
> > >           | CIPHERS STRING                {
> > >                   if (strlcpy(proto->tlsciphers, $2,
> > >                       sizeof(proto->tlsciphers)) >=
> > > @@ -1190,6 +1192,17 @@ tlscache   : NUMBER                        {
> > >           | DISABLE                       { $$ = -2; }
> > >           ;
> > >  
> > > +tlstickets       : DISABLE                       { $$ = -1; }
> > > +
> > > +tlstimeout       : NUMBER                        {
> > > +                 if ($1 < 0) {
> > > +                         yyerror("invalid tlstimeout value: %d", $1);
> > > +                         YYERROR;
> > > +                 }
> > > +                 $$ = $1;
> > > +         }
> > > +         ;
> > > +
> > >  filterrule       : action dir quick ruleaf rulesrc ruledst {
> > >                   if ((rule = calloc(1, sizeof(*rule))) == NULL)
> > >                           fatal("out of memory");
> > > @@ -2257,6 +2270,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       30 Aug 2016 10:54:28 -0000
> > > @@ -28,6 +28,7 @@
> > >  #include <arpa/inet.h>
> > >  
> > >  #include <limits.h>
> > > +#include <poll.h>
> > >  #include <stdio.h>
> > >  #include <stdlib.h>
> > >  #include <errno.h>
> > > @@ -54,6 +55,8 @@ int              relay_dispatch_ca(int, struct priv
> > >               struct imsg *);
> > >  int               relay_dispatch_hce(int, struct privsep_proc *,
> > >               struct imsg *);
> > > +int               relay_dispatch_tlsc(int, struct privsep_proc *,
> > > +             struct imsg *);
> > >  void              relay_shutdown(void);
> > >  
> > >  void              relay_protodebug(struct relay *);
> > > @@ -84,6 +87,12 @@ void            relay_tls_connect(int, short, voi
> > >  void              relay_tls_connected(struct ctl_relay_event *);
> > >  void              relay_tls_readcb(int, short, void *);
> > >  void              relay_tls_writecb(int, short, void *);
> > > +int               relay_tls_new_session(SSL *, SSL_SESSION *);
> > > +SSL_SESSION      *relay_tls_get_session(SSL *, unsigned char *, int, int 
> > > *);
> > > +
> > > +struct tls_ticket_ctx    *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,
> > > @@ -92,6 +101,8 @@ extern void     bufferevent_read_pressure_c
> > >  volatile int relay_sessions;
> > >  volatile int relay_inflight = 0;
> > >  objid_t relay_conid;
> > > +static u_int8_t                   sid[SSL_MAX_SID_CTX_LENGTH];
> > > +static struct tls_ticket_ctx      tls_ticket, tls_ticket_bak;
> > >  
> > 
> > Why globals?
> > 
> > I dislike adding more globals because it makes it harder to read the
> > code.  And it makes fork+exec harder in some situations (see below).
> > 
> > If "sid" is a global thing, it should be in "struct relayd" as
> > something like "sc_tls_sid".  I see that tls_ticket_bak and tls_ticket
> > are used from the libssl callbacks, so you don't have the relay
> > context without passing it somehow, so it is OK but I'd still prefer
> > to have them in "struct relay".
> > 
> > >  static struct relayd             *env = NULL;
> > >  int                               proc_id;
> > > @@ -101,12 +112,18 @@ static struct privsep_proc procs[] = {
> > >   { "pfe",        PROC_PFE,       relay_dispatch_pfe },
> > >   { "ca",         PROC_CA,        relay_dispatch_ca },
> > >   { "hce",        PROC_HCE,       relay_dispatch_hce },
> > > + { "tlsc",       PROC_TLSC,      relay_dispatch_tlsc },
> > >  };
> > >  
> > >  pid_t
> > >  relay(struct privsep *ps, struct privsep_proc *p)
> > >  {
> > >   pid_t    pid;
> > > +
> > > + /* initialize the session id to a random key for all relay procs */
> > > + arc4random_buf(sid, sizeof(sid));
> > 
> > This should be moved to the point when initializing "env" (struct
> > relayd) in main, as a member of the struct as mentioned above.
> > 
> 
> Since I only wanted to have sid initialized in the relay process I came to
> the conclusion that this is the better place to do the initialisation.
> 
> > This way we can pass it to the child processes later, especially with
> > fork+exec (which needs further changes, the optimal thing would be to
> > send and activate the sessions id and ticket with receiving
> > IMSG_CFG_DONE; but this can be done later).
> 
> I agree fork+exec will need some extra work. Will think about it... guess
> the rekey could move to the parent.
>  
> > > + tlsc_create_ticket(&tls_ticket);
> > > +
> > >   env = ps->ps_env;
> > >   pid = proc_run(ps, p, procs, nitems(procs), relay_init, NULL);
> > >   relay_http(env);
> > > @@ -262,6 +279,9 @@ relay_protodebug(struct relay *rlay)
> > >               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, "\ttls session timeout: %lu\n", proto->tlstimeout);
> > >   fprintf(stderr, "\ttype: ");
> > >   switch (proto->type) {
> > >   case RELAY_PROTO_TCP:
> > > @@ -1931,6 +1951,23 @@ relay_dispatch_parent(int fd, struct pri
> > >   return (0);
> > >  }
> > >  
> > > +int
> > > +relay_dispatch_tlsc(int fd, struct privsep_proc *p, struct imsg *imsg)
> > > +{
> > > + switch (imsg->hdr.type) {
> > > + case IMSG_TLSC_REKEY:
> > > +         IMSG_SIZE_CHECK(imsg, (&tls_ticket));
> > > +         /* rotate keys */
> > > +         memcpy(&tls_ticket_bak, &tls_ticket, sizeof(tls_ticket));
> > > +         memcpy(&tls_ticket, imsg->data, sizeof(tls_ticket));
> > > +         break;
> > > + default:
> > > +         return (-1);
> > > + }
> > > +
> > > + return (0);
> > > +}
> > > +
> > >  void
> > >  relay_tls_callback_info(const SSL *ssl, int where, int rc)
> > >  {
> > > @@ -2045,7 +2082,6 @@ 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)
> > > @@ -2054,14 +2090,29 @@ relay_tls_ctx_create(struct relay *rlay)
> > >   /* 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) {
> > > + /* default timeout is 300, see SSL_get_default_timeout(3) */
> > > + if (proto->tlstimeout > 0)
> > > +         SSL_CTX_set_timeout(ctx,
> > > +             (long)MINIMUM(proto->tlstimeout, LONG_MAX));
> > > + if (proto->cache < -1)
> > >           SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
> > > - } else if (proto->cache >= -1) {
> > > + else if (proto->cache >= -1) {
> > >           SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
> > > -         if (proto->cache >= 0)
> > > +         if (proto->cache >= 0) {
> > >                   SSL_CTX_sess_set_cache_size(ctx, proto->cache);
> > > +         }
> > > +         SSL_CTX_sess_set_new_cb(ctx, relay_tls_new_session);
> > > +         SSL_CTX_sess_set_get_cb(ctx, relay_tls_get_session);
> > > +         /* remove_cb unused because the cache runs a GC */
> > >   }
> > >  
> > > + /* Callback for TLS session tickets */
> > > + 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");
> > > +
> > >   /* Enable all workarounds and set SSL options */
> > >   SSL_CTX_set_options(ctx, SSL_OP_ALL);
> > >   SSL_CTX_set_options(ctx,
> > > @@ -2142,11 +2193,9 @@ 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)))
> > >           goto err;
> > >  
> > > @@ -2534,6 +2583,161 @@ relay_tls_writecb(int fd, short event, v
> > >           cre->buflen = 0;
> > >   }
> > >   (*bufev->errorcb)(bufev, what, bufev->cbarg);
> > > +}
> > > +
> > > +int
> > > +relay_tls_new_session(SSL *ssl, SSL_SESSION *session)
> > > +{
> > > + struct iovec             iov[3];
> > > + struct tls_session_hdr   th;
> > > + struct imsgbuf          *ibuf;
> > > + const unsigned char     *id;
> > > + unsigned char           *buf, *s;
> > > + unsigned int             idlen;
> > > + int                      slen, cnt = 0;
> > > +
> > > + ibuf = proc_ibuf(env->sc_ps, PROC_TLSC, -1);
> > > +
> > > + id = SSL_SESSION_get_id(session, &idlen);
> > > +
> > > + /* Serialise the session. */
> > > + slen = i2d_SSL_SESSION(session, NULL);
> > > + if ((buf = s = malloc(slen)) == NULL)
> > > +         fatal("relay_tls_new_session");
> > > + if (slen != i2d_SSL_SESSION(session, &buf))
> > > +         fatalx("SSL SESSION unexpectly grew beyond buffer");
> > > +
> > > + /* build up imsg */
> > > + th.timeout = SSL_SESSION_get_timeout(session);
> > > + th.idlen = idlen;
> > > + th.slen = slen;
> > > +
> > > + iov[cnt].iov_base = &th;
> > > + iov[cnt++].iov_len = sizeof(th);
> > > + iov[cnt].iov_base = (void *)id;
> > > + iov[cnt++].iov_len = idlen;
> > > + iov[cnt].iov_base = (void *)s;
> > > + iov[cnt++].iov_len = slen;
> > 
> > It would be better to use a "struct ctl_tlsc", fill it in, and send it
> > at once. This manual filling can lead to errors and it doesn't match the
> > common style in relayd (which is always very important to me).
> > See also comments below in tlsc.c.
> > 
> 
> Hmm. This is more or less a verbatim copy from ca.c. Since the id and
> session buffers are dynamic length and their size is not known it is not
> easily possible to wrap this into a struct. I renamed tls_session_hdr
> to ctl_tlsc_hdr.
> 
> > > +
> > > + imsg_composev(ibuf, IMSG_TLSC_ADD, 0, 0, -1, iov, cnt);
> > > + if (imsg_flush(ibuf) == -1)
> > > +         log_warn("tlsc_modify: imsg_flush");
> > > +
> > > + free(s);
> > > + return 0;       /* tell SSL to remove the reference on the session */
> > > +}
> > > +
> > > +SSL_SESSION *
> > > +relay_tls_get_session(SSL *ssl, unsigned char *id, int idlen, int 
> > > *do_copy)
> > > +{
> > > + struct pollfd            pfd[1];
> > > + struct iovec             iov[2];
> > > + SSL_SESSION             *session = NULL;
> > > + struct imsgbuf          *ibuf;
> > > + const unsigned char     *data;
> > > + struct imsg              imsg;
> > > + int                      proc;
> > > + int                      n, done = 0, cnt = 0;
> > > +
> > > + ibuf = proc_ibuf(env->sc_ps, PROC_TLSC, -1);
> > > + proc = proc_id;
> > > +
> > > + iov[cnt].iov_base = &proc;
> > > + iov[cnt++].iov_len = sizeof(proc);
> > > + iov[cnt].iov_base = (void *)id;
> > > + iov[cnt++].iov_len = idlen;
> > > +
> > > + imsg_composev(ibuf, IMSG_TLSC_GET, 0, 0, -1, iov, cnt);
> > > + if (imsg_flush(ibuf) == -1)
> > > +         log_warn("tlsc_lookup: imsg_flush");
> > > +
> > > + pfd[0].fd = ibuf->fd;
> > > + pfd[0].events = POLLIN;
> > > + while (!done) {
> > > +         switch (poll(pfd, 1, 5 * 1000)) {
> > 
> > Same magic timeout number here, so you could even reuse one #define
> > and it provides a benefit ;)
> > 
> 
> Done as well.
> 
> > > +         case -1:
> > > +                 fatal("rsae_send_imsg: poll");
> > > +         case 0:
> > > +                 log_warnx("tlsc_lookup: poll timeout");
> > > +                 break;
> > > +         default:
> > > +                 break;
> > > +         }
> > > +         if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
> > > +                 fatalx("imsg_read");
> > > +         if (n == 0)
> > > +                 fatalx("pipe closed");
> > > +
> > > +         while (!done) {
> > > +                 if ((n = imsg_get(ibuf, &imsg)) == -1)
> > > +                         fatalx("imsg_get error");
> > > +                 if (n == 0)
> > > +                         break;
> > > +                 if (imsg.hdr.type != IMSG_TLSC_GET)
> > > +                         fatalx("invalid response");
> > > +
> > > +                 if (IMSG_DATA_SIZE(&imsg) != 0) {
> > > +                         data = imsg.data;
> > > +                         session = d2i_SSL_SESSION(NULL, &data,
> > > +                             IMSG_DATA_SIZE(&imsg));
> > > +                 }
> > > +                 done = 1;
> > > +                 imsg_free(&imsg);
> > > +         }
> > > + }
> > > +
> > > + *do_copy = 0;
> > > + return (session);
> > > +}
> > > +
> > > +struct tls_ticket_ctx *
> > > +relay_get_ticket_key(unsigned char keyname[16])
> > > +{
> > > + if (keyname) {
> > > +         if (timingsafe_memcmp(keyname,
> > > +             tls_ticket_bak.key_name, sizeof(keyname)) == 0)
> > > +                 return &tls_ticket_bak;
> > > +         if (timingsafe_memcmp(keyname,
> > > +             tls_ticket.key_name, sizeof(keyname)) == 0)
> > > +                 return &tls_ticket;
> > > +         return NULL;
> > > + }
> > > + return &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_ctx   *key;
> > > + struct timeval           tv;
> > > +
> > > + if (mode == 1) {
> > > +         /* create new session */
> > > +         key = relay_get_ticket_key(NULL);
> > > +         memcpy(keyname, key->key_name, 16);
> > > +         arc4random_buf(iv, EVP_MAX_IV_LENGTH);
> > > +         EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL,
> > > +             key->aes_key, iv);
> > > +         HMAC_Init_ex(hctx, key->hmac_key, 16, EVP_sha256(), NULL);
> > 
> > Is there a specific reason to use aes128-cbc-hmac-sha256 for the tickets?
> > 
> 
> This is what LibreSSL does. Since this is lots of magic I tried to keep it
> as close to LibreSSL code as possible.
> See lib/libssl/src/ssl/t1_lib.c::tls_decrypt_ticket()
> I'm not sure if it is save to use different crypt/mac pairs for this.
> 
> This is dark undocumented territory of OpenSSL. I wonder if
> SSL_CTX_set_tlsext_ticket_keys() would be another option to do this. Will
> talk to bob@ and maybe make him scream....
> 

OK, that makes sense.

I'm fine with copying it for now, but it should be considered and
discussed with the TLS people (including beck@ and jsing@).

> > > +         return 0;
> > > + } else {
> > > +         getmonotime(&tv);
> > > +
> > > +         /* get key by name */
> > > +         key = relay_get_ticket_key(keyname);
> > > +         if  (!key || key->expire < tv.tv_sec)
> > > +                 return 0;
> > > +
> > > +         EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL,
> > > +             key->aes_key, iv);
> > > +         HMAC_Init_ex(hctx, key->hmac_key, 16, EVP_sha256(), NULL);
> > > +
> > > +         /* time to renew the ticket? */
> > > +         if (key->expire - TLS_TICKET_RENEW_TIME < tv.tv_sec)
> > > +                 return 2;
> > > +         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      8 Aug 2016 10:04:06 -0000
> > > @@ -70,7 +70,8 @@ static struct privsep_proc procs[] = {
> > >   { "pfe",        PROC_PFE, parent_dispatch_pfe, pfe },
> > >   { "hce",        PROC_HCE, parent_dispatch_hce, hce },
> > >   { "relay",      PROC_RELAY, parent_dispatch_relay, relay },
> > > - { "ca",         PROC_CA, parent_dispatch_ca, ca }
> > > + { "ca",         PROC_CA, parent_dispatch_ca, ca },
> > > + { "tlsc",       PROC_TLSC, NULL, tlsc }
> > >  };
> > >  
> > >  void
> > > @@ -635,6 +636,8 @@ purge_relay(struct relayd *env, struct r
> > >           rlay->rl_tls_capkey = NULL;
> > >   }
> > >  
> > > + if (rlay->rl_ssl_ctx)
> > > +         SSL_CTX_sess_set_remove_cb(rlay->rl_ssl_ctx, NULL);
> > >   SSL_CTX_free(rlay->rl_ssl_ctx);
> > >  
> > >   while ((rlt = TAILQ_FIRST(&rlay->rl_tables))) {
> > > 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 30 Aug 2016 10:50:16 -0000
> > > @@ -973,6 +973,11 @@ is zero, the default size defined by the
> > >  A positive number will set the maximum size in bytes and the keyword
> > >  .Ic disable
> > >  will disable the TLS session cache.
> > > +.It Ic session timeout Ar value
> > > +Set the timeout for TLS session cache entries.
> > > +If not set, the default defined by the TLS library will be used.
> > > +.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      30 Aug 2016 10:50:16 -0000
> > > @@ -711,6 +711,8 @@ struct protocol {
> > >   char                    *tlscapass;
> > >   char                     name[MAX_NAME_SIZE];
> > >   int                      cache;
> > > + int                      tickets;
> > > + unsigned long            tlstimeout;
> > >   enum prototype           type;
> > >   char                    *style;
> > >  
> > > @@ -963,7 +965,10 @@ enum imsg_type {
> > >   IMSG_CA_PRIVENC,
> > >   IMSG_CA_PRIVDEC,
> > >   IMSG_SESS_PUBLISH,      /* from relay to hce */
> > > - IMSG_SESS_UNPUBLISH
> > > + IMSG_SESS_UNPUBLISH,
> > > + IMSG_TLSC_ADD,
> > > + IMSG_TLSC_GET,
> > > + IMSG_TLSC_REKEY
> > >  };
> > >  
> > >  enum privsep_procid {
> > > @@ -973,6 +978,7 @@ enum privsep_procid {
> > >   PROC_RELAY,
> > >   PROC_PFE,
> > >   PROC_CA,
> > > + PROC_TLSC,
> > >   PROC_MAX
> > >  } privsep_process;
> > >  
> > > @@ -1078,6 +1084,22 @@ struct relayd {
> > >   int                      sc_reload;
> > >  };
> > >  
> > > +struct tls_session_hdr {
> > > + size_t idlen;
> > > + size_t slen;
> > > + long   timeout;
> > > +};
> > > +
> > > +struct tls_ticket_ctx {
> > > + time_t          expire;
> > > + unsigned char   key_name[16];
> > > + unsigned char   aes_key[16];
> > > + unsigned char   hmac_key[16];
> > 
> > Where are all these 16 from?  Please provide it a #define as well, or
> > use one from libssl if it is coming from there.  I don't like such
> > magic number without explanation.
> > 
> 
> LibreSSL code is using 16 and it has to be 16 or shit fails.
> I can call it TLS_MAGIC_SIZE but I doubt it will help. I will add a
> comment for this though.
> 

All the black magic from libssl ;)
OK, TLS_MAGIC_SIZE or TLS_MAGIC_NAME_SIZE and a comment would be the best.

> > > +};
> > > +#define  TLS_TICKET_LIFE_TIME    (4 * 3600)
> > > +#define  TLS_TICKET_RENEW_TIME   600
> > > +#define  TLS_CACHE_GC_INTERVAL   60
> > > +
> > 
> > I guess I have to sort relayd.h a bit, but please put such global-like
> > defines on top in a new block after the RELAY_ ones.
> 
> Hmm. Can we do the move / cleanup after it is in? At least then you can
> decide where to put it.
> 

OK

> > 
> > >  #define  FSNMP_TRAPONLY                  0x01
> > >  
> > >  #define RELAYD_OPT_VERBOSE               0x01
> > > @@ -1239,6 +1261,11 @@ int         ssl_ctx_fake_private_key(SSL_CTX *,
> > >  /* ca.c */
> > >  pid_t     ca(struct privsep *, struct privsep_proc *);
> > >  void      ca_engine_init(struct relayd *);
> > > +
> > > +/* tlsc.c */
> > > +pid_t     tlsc(struct privsep *, struct privsep_proc *);
> > > +void      tlsc_engine_init(struct relayd *);
> > > +void      tlsc_create_ticket(struct tls_ticket_ctx *);
> > >  
> > >  /* relayd.c */
> > >  struct host      *host_find(struct relayd *, objid_t);
> > > Index: tlsc.c
> > > ===================================================================
> > > RCS file: tlsc.c
> > > diff -N tlsc.c
> > > --- /dev/null     1 Jan 1970 00:00:00 -0000
> > > +++ tlsc.c        24 Jul 2016 13:07:50 -0000
> > > @@ -0,0 +1,282 @@
> > > +/*       $OpenBSD$       */
> > > +
> > > +/*
> > > + * Copyright (c) 2016 Claudio Jeker <[email protected]>
> > > + *
> > > + * 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.
> > > + */
> > > +
> > > +#include <sys/types.h>
> > > +#include <sys/queue.h>
> > > +#include <sys/tree.h>
> > > +#include <sys/uio.h>
> > > +
> > > +#include <unistd.h>
> > > +#include <string.h>
> > > +#include <stdlib.h>
> > > +#include <poll.h>
> > > +#include <imsg.h>
> > > +
> > > +#include "relayd.h"
> > > +
> > > +struct tlsc_get {
> > > + size_t  idlen;
> > > + int     proc;
> > > +};
> > 
> > This struct doesn't seem to be used anywhere?
> > 
> 
> Killed it.
> 
> > As mentioned before, I guess you meant to create something like a
> > "struct ctl_tlsc", but it is implemented in a different way below.
> > You should use it, prefix it ctl_ for consistency.
> > 
> > > +
> > > +struct tlsc {
> > > + RB_ENTRY(tlsc)          entry;
> > > + size_t                  idlen;
> > > + size_t                  slen;
> > > + time_t                  expire;
> > > + const unsigned char     *id;
> > > + const unsigned char     *session;
> > 
> > relayd has a weird mix of un-prefixed and prefixed struct members.
> > I'd prefer the latter to follow what is being used where (eg. I just
> > had difficulties below to figure out if x->idlen was from tlsc_get or
> > tlsc).
> > 
> 
> Done.
> 
> > > +};
> > > +
> > > +void     tlsc_init(struct privsep *, struct privsep_proc *p, void *);
> > > +void     tlsc_launch(void);
> > > +void     tlsc_rekey(int, short, void *);
> > > +int      tlsc_dispatch_parent(int, struct privsep_proc *, struct imsg *);
> > > +int      tlsc_dispatch_relay(int, struct privsep_proc *, struct imsg *);
> > > +
> > > +int               tlsc_compare(struct tlsc *, struct tlsc *);
> > > +struct tlsc      *tlsc_get(const void *, size_t);
> > > +void              tlsc_add(const void *, size_t, const void *, size_t, 
> > > long);
> > > +void              tlsc_flush(void);
> > > +void              tlsc_gc(int, short, void *);
> > > +
> > > +RB_HEAD(tlsc_tree, tlsc) tls_cache = RB_INITIALIZER(&tls_cache);
> > > +RB_PROTOTYPE(tlsc_tree, tlsc, entry, tlsc_compare)
> > > +RB_GENERATE(tlsc_tree, tlsc, entry, tlsc_compare)
> > > +
> > > +static struct relayd *env = NULL;
> > > +extern int                proc_id;
> > > +
> > > +static struct event      rekeyev;
> > > +static struct event      gcev;
> > 
> > Most of my comments seem like nit-picking, but I really like
> > consistency / common style.  Accordingly, it would be better to put
> > them into the global "struct relayd".
> > 
> 
> I think this is not helping here. These timeouts are used in one place
> only and adding them to struct relayd make that struct just fatter for no
> real benefit. I don't mind sharing common data structures and config that
> way but event structs are something I see no benefit to be in there.
> 

But struct relayd gives me a context and prefix and I can see what is
done where.  All these events all over the place are easy to lose...
But, OK, if you insist.  The global struct relayd / env / conf will
get some cleanup anyway (eg. for fork+exec) and we might just tweak it
later.

> > > +
> > > +static struct privsep_proc procs[] = {
> > > + { "parent",     PROC_PARENT,    tlsc_dispatch_parent },
> > > + { "relay",      PROC_RELAY,     tlsc_dispatch_relay },
> > > +};
> > > +
> > > +pid_t
> > > +tlsc(struct privsep *ps, struct privsep_proc *p)
> > > +{
> > > + env = ps->ps_env;
> > > +
> > > + return (proc_run(ps, p, procs, nitems(procs), tlsc_init, NULL));
> > > +}
> > > +
> > > +void
> > > +tlsc_init(struct privsep *ps, struct privsep_proc *p, void *arg)
> > > +{
> > > + if (pledge("stdio", NULL) == -1)
> > > +         fatal("pledge");
> > > +
> > > + if (config_init(ps->ps_env) == -1)
> > > +         fatal("failed to initialize configuration");
> > > +}
> > > +
> > > +void
> > > +tlsc_launch(void)
> > > +{
> > > + struct timeval   tv;
> > > +
> > > + /* flush the cache after reconfigure, the same happens in the relays */
> > > + tlsc_flush();
> > > +
> > > + /* Schedule rekey timer */
> > > + evtimer_set(&rekeyev, tlsc_rekey, NULL);
> > > + timerclear(&tv);
> > > + tv.tv_sec = TLS_CACHE_GC_INTERVAL;
> > > + evtimer_add(&rekeyev, &tv);
> > > +
> > > + /* Schedule GC timer */
> > > + evtimer_set(&gcev, tlsc_gc, NULL);
> > > + timerclear(&tv);
> > > + tv.tv_sec = 60;
> > 
> > 
> > I think that would be TLS_CACHE_GC_INTERVAL instead of 60.
> > 
> 
> Oups. This is actually a bad debug leftover. Yes this should be
> TLS_CACHE_GC_INTERVAL and rekey needs to use TLS_TICKET_LIFE_TIME.
> Fixed.
> 
> > > + evtimer_add(&gcev, &tv);
> > > +}
> > > +
> > > +void
> > > +tlsc_rekey(int fd, short events, void *arg)
> > > +{
> > > + struct timeval          tv;
> > > + struct tls_ticket_ctx   ticket;
> > > + 
> > > + log_debug("tlsc_rekey: rekeying tickets");
> > > +
> > > + tlsc_create_ticket(&ticket);
> > > + proc_compose_imsg(env->sc_ps, PROC_RELAY, -1, IMSG_TLSC_REKEY, -1, -1,
> > > +     &ticket, sizeof(ticket));
> > > +
> > > + evtimer_set(&rekeyev, tlsc_rekey, NULL);
> > > + timerclear(&tv);
> > > + tv.tv_sec = TLS_TICKET_LIFE_TIME;
> > > + evtimer_add(&rekeyev, &tv);
> > > +}
> > > +
> > > +int
> > > +tlsc_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
> > > +{
> > > + switch (imsg->hdr.type) {
> > > + case IMSG_CTL_START:
> > > +         tlsc_launch();
> > > +         break;
> > > + case IMSG_CFG_DONE:
> > > +         /* nothing to do here */
> > > +         break;
> > > + default:
> > > +         return -1;
> > > + }
> > > + return 0;
> > > +}
> > > +
> > > +int
> > > +tlsc_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
> > > +{
> > > + struct tls_session_hdr   th;
> > > + int                      proc;
> > > + unsigned char           *id, *s;
> > > + struct tlsc             *t;
> > > +
> > > + switch (imsg->hdr.type) {
> > > + case IMSG_TLSC_ADD:
> > > +         IMSG_SIZE_CHECK(imsg, (&th));
> > > +         memcpy(&th, imsg->data, sizeof(th));
> > > +         if (IMSG_DATA_SIZE(imsg) != (sizeof(th) + th.idlen + th.slen))
> > > +                 fatalx("tlsc_dispatch_relay: bad TLS cache imsg");
> > > +         id = (unsigned char *)imsg->data + sizeof(th);
> > > +         s = id + th.idlen;
> > 
> > This would clearly benefit from a single "struct ctl_tlsc".
> > 
> 
> It is not realy possible because the id length is unknown.
> 

I see.

> > > +
> > > +         tlsc_add(id, th.idlen, s, th.slen, th.timeout);
> > > +         break;
> > > + case IMSG_TLSC_GET:
> > > +         IMSG_SIZE_CHECK(imsg, (&proc));
> > > +         memcpy(&proc, imsg->data, sizeof(proc));
> > > +
> > > +         t = tlsc_get((unsigned char *)imsg->data + sizeof(proc),
> > > +             IMSG_DATA_SIZE(imsg) - sizeof(proc));
> > > +         if (t == NULL)
> > > +                 proc_compose_imsg(env->sc_ps, PROC_RELAY, proc,
> > > +                     imsg->hdr.type, -1, -1, NULL, 0);
> > > +         else
> > > +                 proc_compose_imsg(env->sc_ps, PROC_RELAY, proc,
> > > +                     imsg->hdr.type, -1, -1,
> > > +                     (void *)t->session, t->slen);
> > > +         break;
> > > + default:
> > > +         return (-1);
> > > + }
> > > +
> > > + return (0);
> > > +}
> > > +
> > > +int
> > > +tlsc_compare(struct tlsc *a, struct tlsc *b)
> > > +{
> > > + if (a->idlen < b->idlen)
> > > +         return -1;
> > > + if (a->idlen > b->idlen)
> > > +         return 1;
> > > + return memcmp(a->id, b->id, a->idlen);
> > > +}
> > > +
> > > +struct tlsc *
> > > +tlsc_get(const void *id, size_t idlen)
> > > +{ 
> > > + struct tlsc     tt;
> > > +
> > > + memset(&tt, 0, sizeof(tt));
> > > + tt.id = id;
> > > + tt.idlen = idlen;
> > > +
> > > + return RB_FIND(tlsc_tree, &tls_cache, &tt);
> > > +}
> > > +
> > > +void
> > > +tlsc_add(const void *id, size_t idlen, const void *s, size_t slen, long 
> > > timeout)
> > > +{
> > > + struct timeval   tv;
> > > + struct tlsc     *t;
> > > + unsigned char   *b;
> > > + size_t           l;
> > > +
> > > + l = sizeof(*t) + idlen + slen;
> > > + if ((t = malloc(l)) == NULL)
> > > +         fatal("tlsc_add");
> > > + memset(t, 0, sizeof(*t));
> > 
> > Better use calloc(1, here.  One line, easier to read, common style.
> 
> Hmm. I think I stole this example from LibreSSL. Anyway easy change to do.
> 
> > 
> > > + b = (unsigned char *)(t + 1);
> > > + memcpy(b, id, idlen);
> > > + t->id = b;
> > > + t->idlen = idlen;
> > > + b += idlen;
> > > + memcpy(b, s, slen);
> > > + t->session = b;
> > > + t->slen = slen;
> > > +
> > > + getmonotime(&tv);
> > > + t->expire = tv.tv_sec + timeout;
> > > +
> > > + if (RB_INSERT(tlsc_tree, &tls_cache, t)) {
> > > +         log_warnx("tlsc_add: session already in cache");
> > > +         free(t);
> > > + }
> > > +}
> > > +
> > > +void
> > > +tlsc_flush(void)
> > > +{
> > > + struct tlsc     *t;
> > > +
> > > + while ((t = RB_MIN(tlsc_tree, &tls_cache)) != NULL) {
> > > +         RB_REMOVE(tlsc_tree, &tls_cache, t);
> > > +         free(t);
> > > + }
> > > +}
> > > +
> > > +void
> > > +tlsc_gc(int fd, short events, void *arg)
> > > +{
> > > + struct timeval   tv;
> > > + struct tlsc     *t, *tt;
> > > +
> > > + getmonotime(&tv);
> > > + 
> > > + /* remove expired entries from the cache */
> > > + RB_FOREACH_SAFE(t, tlsc_tree, &tls_cache, tt) {
> > > +         if (t->expire < tv.tv_sec) {
> > > +                 RB_REMOVE(tlsc_tree, &tls_cache, t);
> > > +                 free(t);
> > > +         }
> > > + }
> > > +
> > > + evtimer_set(&gcev, tlsc_gc, NULL);
> > > + timerclear(&tv);
> > > + tv.tv_sec = TLS_CACHE_GC_INTERVAL;
> > > + evtimer_add(&gcev, &tv);
> > > +}
> > > +
> > > +void
> > > +tlsc_create_ticket(struct tls_ticket_ctx *key)
> > > +{
> > > + struct timeval  tv;
> > > +
> > > + getmonotime(&tv);
> > > + key->expire = tv.tv_sec + TLS_TICKET_LIFE_TIME;
> > > +
> > > + arc4random_buf(key->key_name, sizeof(key->key_name));
> > > + arc4random_buf(key->hmac_key, sizeof(key->hmac_key));
> > > + arc4random_buf(key->aes_key, sizeof(key->aes_key));
> > > +}
> > > 
> > 
> > -- 
> > 
> 
> Will send out an update diff later.
> -- 
> :wq Claudio

Thanks.

As I'm in the plane soon - I think you'll have my OK on it.

Reyk

Reply via email to