untrusted comment: signature from openbsd 5.7 base secret key
RWSvUZXnw9gUb305RQbAZ3BNddG21lovpLcx/MxcRtwVkmTLMM3EO5tS5H8DVYUocvaFTDE31T/Ff2DeJJEaP/3qH88rtEaL+ww=

OpenBSD 5.7 errata 15, Sept 28, 2015:

Various problems were identified in relayd and merged back from
current to 5.7 in this maintanance update.

Apply by doing:
   signify -Vep /etc/signify/openbsd-57-base.pub -x 015_relayd.patch.sig \
       -m - | (cd /usr/src && patch -p0)

And then rebuild and install the patch utility:
   cd /usr/src/usr.sbin/relayd
   make obj
   make depend
   make
   make install

Index: usr.sbin/relayd/ca.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/ca.c,v
retrieving revision 1.12
diff -u -p -r1.12 ca.c
--- usr.sbin/relayd/ca.c        22 Jan 2015 17:42:09 -0000      1.12
+++ usr.sbin/relayd/ca.c        28 Sep 2015 17:43:06 -0000
@@ -417,11 +417,14 @@ rsae_keygen(RSA *rsa, int bits, BIGNUM *
void
ca_engine_init(struct relayd *x_env)
{
-       ENGINE          *e;
+       ENGINE          *e = NULL;
       const char      *errstr, *name;

       if (env == NULL)
               env = x_env;
+
+       if (rsa_default != NULL)
+               return;

       if ((e = ENGINE_get_default_RSA()) == NULL) {
               if ((e = ENGINE_new()) == NULL) {
Index: usr.sbin/relayd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/config.c,v
retrieving revision 1.24
diff -u -p -r1.24 config.c
--- usr.sbin/relayd/config.c    22 Jan 2015 17:42:09 -0000      1.24
+++ usr.sbin/relayd/config.c    28 Sep 2015 17:43:06 -0000
@@ -142,7 +142,7 @@ config_purge(struct relayd *env, u_int r

       if (what & CONFIG_TABLES && env->sc_tables != NULL) {
               while ((table = TAILQ_FIRST(env->sc_tables)) != NULL)
-                       purge_table(env->sc_tables, table);
+                       purge_table(env, env->sc_tables, table);
               env->sc_tablecount = 0;
       }
       if (what & CONFIG_RDRS && env->sc_rdrs != NULL) {
Index: usr.sbin/relayd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/parse.y,v
retrieving revision 1.203
diff -u -p -r1.203 parse.y
--- usr.sbin/relayd/parse.y     8 Feb 2015 04:50:32 -0000       1.203
+++ usr.sbin/relayd/parse.y     28 Sep 2015 17:43:06 -0000
@@ -531,12 +531,12 @@ rdroptsl  : forwardmode TO tablespec inte

                       if ($3->conf.check == CHECK_NOCHECK) {
                               yyerror("table %s has no check", $3->conf.name);
-                               purge_table(conf->sc_tables, $3);
+                               purge_table(conf, conf->sc_tables, $3);
                               YYERROR;
                       }
                       if (rdr->backup) {
                               yyerror("only one backup table is allowed");
-                               purge_table(conf->sc_tables, $3);
+                               purge_table(conf, conf->sc_tables, $3);
                               YYERROR;
                       }
                       if (rdr->table) {
@@ -1930,7 +1930,7 @@ routeoptsl        : ROUTE address '/' NUMBER {
                       if (router->rt_gwtable) {
                               yyerror("router %s table already specified",
                                   router->rt_conf.name);
-                               purge_table(conf->sc_tables, $3);
+                               purge_table(conf, conf->sc_tables, $3);
                               YYERROR;
                       }
                       router->rt_gwtable = $3;
@@ -3091,7 +3091,7 @@ table_inherit(struct table *tb)
               goto fail;
       }
       if ((oldtb = table_findbyconf(conf, tb)) != NULL) {
-               purge_table(NULL, tb);
+               purge_table(conf, NULL, tb);
               return (oldtb);
       }

@@ -3134,7 +3134,7 @@ table_inherit(struct table *tb)
       return (tb);

 fail:
-       purge_table(NULL, tb);
+       purge_table(conf, NULL, tb);
       return (NULL);
}

Index: usr.sbin/relayd/pfe.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/pfe.c,v
retrieving revision 1.79
diff -u -p -r1.79 pfe.c
--- usr.sbin/relayd/pfe.c       8 Feb 2015 01:39:06 -0000       1.79
+++ usr.sbin/relayd/pfe.c       28 Sep 2015 17:43:06 -0000
@@ -289,8 +289,11 @@ pfe_dispatch_relay(int fd, struct privse
                       return (0);             /* XXX */
               memcpy(s, imsg->data, sizeof(*s));
               TAILQ_FOREACH(t, &env->sc_sessions, se_entry) {
-                       if (t->se_id == s->se_id)       /* duplicate registration */
+                       /* duplicate registration */
+                       if (t->se_id == s->se_id) {
+                               free(s);
                               return (0);
+                       }
                       if (t->se_id > s->se_id)
                               break;
               }
Index: usr.sbin/relayd/relay.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay.c,v
retrieving revision 1.191
diff -u -p -r1.191 relay.c
--- usr.sbin/relayd/relay.c     6 Feb 2015 01:37:11 -0000       1.191
+++ usr.sbin/relayd/relay.c     28 Sep 2015 17:43:06 -0000
@@ -829,6 +829,12 @@ relay_read(struct bufferevent *bev, void
       relay_close(con, strerror(errno));
}

+/*
+ * Splice sockets from cre to cre->dst if applicable.  Returns:
+ * -1 socket splicing has failed
+ * 0 socket splicing is currently not possible
+ * 1 socket splicing was successful
+ */
int
relay_splice(struct ctl_relay_event *cre)
{
@@ -878,7 +884,7 @@ relay_splice(struct ctl_relay_event *cre
       DPRINTF("%s: session %d: splice dir %d, maximum %lld, successful",
           __func__, con->se_id, cre->dir, cre->toread);

-       return (0);
+       return (1);
}

int
@@ -988,7 +994,7 @@ relay_error(struct bufferevent *bev, sho
                       dst = EVBUFFER_OUTPUT(cre->dst->bev);
                       if (EVBUFFER_LENGTH(dst))
                               return;
-               } else
+               } else if (cre->toread == TOREAD_UNLIMITED || cre->toread == 0)
                       return;

               relay_close(con, "done");
@@ -1041,6 +1047,12 @@ relay_accept(int fd, short event, void *
       if ((con = calloc(1, sizeof(*con))) == NULL)
               goto err;

+       /* Pre-allocate log buffer */
+       con->se_haslog = 0;
+       con->se_log = evbuffer_new();
+       if (con->se_log == NULL)
+               goto err;
+
       con->se_in.s = s;
       con->se_in.ssl = NULL;
       con->se_out.s = -1;
@@ -1094,14 +1106,6 @@ relay_accept(int fd, short event, void *
               return;
       }

-       /* Pre-allocate log buffer */
-       con->se_haslog = 0;
-       con->se_log = evbuffer_new();
-       if (con->se_log == NULL) {
-               relay_close(con, "failed to allocate log buffer");
-               return;
-       }
-
       if (rlay->rl_conf.flags & F_DIVERT) {
               slen = sizeof(con->se_out.ss);
               if (getsockname(s, (struct sockaddr *)&con->se_out.ss,
@@ -1265,7 +1269,7 @@ relay_from_table(struct rsession *con)
                       return (-1);
       }
       host = rlt->rlt_host[idx];
-       DPRINTF("%s: session %d: table %s host %s, p 0x%08x, idx %d",
+       DPRINTF("%s: session %d: table %s host %s, p 0x%016llx, idx %d",
           __func__, con->se_id, table->conf.name, host->conf.name, p, idx);
       while (host != NULL) {
               DPRINTF("%s: session %d: host %s", __func__,
@@ -1404,8 +1408,10 @@ relay_connect_retry(int fd, short sig, v
       struct relay    *rlay = con->se_relay;
       int              bnds = -1;

-       if (relay_inflight < 1)
-               fatalx("relay_connect_retry: no connection in flight");
+       if (relay_inflight < 1) {
+               log_warnx("relay_connect_retry: no connection in flight");
+               relay_inflight = 1;
+       }

       DPRINTF("%s: retry %d of %d, inflight: %d",__func__,
           con->se_retrycount, con->se_retry, relay_inflight);
@@ -1462,6 +1468,10 @@ relay_connect_retry(int fd, short sig, v
               return;
       }

+       if (rlay->rl_conf.flags & F_TLSINSPECT)
+               con->se_out.state = STATE_PRECONNECT;
+       else
+               con->se_out.state = STATE_CONNECTED;
       relay_inflight--;
       DPRINTF("%s: inflight decremented, now %d",__func__, relay_inflight);

@@ -1480,9 +1490,14 @@ relay_connect_retry(int fd, short sig, v
int
relay_preconnect(struct rsession *con)
{
+       int rv;
+
       log_debug("%s: session %d: process %d", __func__,
           con->se_id, privsep_process);
-       return (relay_connect(con));
+       rv = relay_connect(con);
+       if (con->se_out.state == STATE_CONNECTED)
+               con->se_out.state = STATE_PRECONNECT;
+       return (rv);
}

int
@@ -1492,18 +1507,28 @@ relay_connect(struct rsession *con)
       struct timeval   evtpause = { 1, 0 };
       int              bnds = -1, ret;

+       /* relay_connect should only be called once per relay */
+       if (con->se_out.state == STATE_CONNECTED) {
+               log_debug("%s: connect already called once", __func__);
+               return (0);
+       }
+
       /* Connection is already established but session not active */
-       if ((rlay->rl_conf.flags & F_TLSINSPECT) && con->se_out.s != -1) {
+       if ((rlay->rl_conf.flags & F_TLSINSPECT) &&
+           con->se_out.state == STATE_PRECONNECT) {
               if (con->se_out.ssl == NULL) {
                       log_debug("%s: tls connect failed", __func__);
                       return (-1);
               }
               relay_connected(con->se_out.s, EV_WRITE, con);
+               con->se_out.state = STATE_CONNECTED;
               return (0);
       }

-       if (relay_inflight < 1)
-               fatalx("relay_connect: no connection in flight");
+       if (relay_inflight < 1) {
+               log_warnx("relay_connect: no connection in flight");
+               relay_inflight = 1;
+       }

       getmonotime(&con->se_tv_start);

@@ -1551,6 +1576,9 @@ relay_connect(struct rsession *con)
                       event_del(&rlay->rl_ev);
                       evtimer_add(&con->se_inflightevt, &evtpause);
                       evtimer_add(&rlay->rl_evt, &evtpause);
+
+                       /* this connect is pending */
+                       con->se_out.state = STATE_PENDING;
                       return (0);
               } else {
                       if (con->se_retry) {
@@ -1568,6 +1596,7 @@ relay_connect(struct rsession *con)
               }
       }

+       con->se_out.state = STATE_CONNECTED;
       relay_inflight--;
       DPRINTF("%s: inflight decremented, now %d",__func__,
           relay_inflight);
@@ -1669,6 +1698,7 @@ relay_close(struct rsession *con, const
                       event_add(&rlay->rl_ev, NULL);
               }
       }
+       con->se_out.state = STATE_INIT;

       if (con->se_out.buf != NULL)
               free(con->se_out.buf);
Index: usr.sbin/relayd/relay_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v
retrieving revision 1.43
diff -u -p -r1.43 relay_http.c
--- usr.sbin/relayd/relay_http.c        22 Jan 2015 17:42:09 -0000      1.43
+++ usr.sbin/relayd/relay_http.c        28 Sep 2015 17:43:06 -0000
@@ -35,6 +35,7 @@
#include <fnmatch.h>
#include <siphash.h>
#include <imsg.h>
+#include <unistd.h>

#include "relayd.h"
#include "http.h"
@@ -146,6 +147,7 @@ relay_httpdesc_free(struct http_descript
               desc->query_val = NULL;
       }
       kv_purge(&desc->http_headers);
+       desc->http_lastheader = NULL;
}

void
@@ -210,7 +212,7 @@ relay_read_http(struct bufferevent *bev,
               else
                       value = strchr(key, ':');
               if (value == NULL) {
-                       if (cre->line == 1) {
+                       if (cre->line <= 2) {
                               free(line);
                               relay_abort_http(con, 400, "malformed", 0);
                               return;
@@ -271,8 +273,10 @@ relay_read_http(struct bufferevent *bev,
                       goto lookup;
               } else if (cre->line == 1 && cre->dir == RELAY_DIR_REQUEST) {
                       if ((desc->http_method = relay_httpmethod_byname(key))
-                           == HTTP_METHOD_NONE)
+                           == HTTP_METHOD_NONE) {
+                               free(line);
                               goto fail;
+                       }
                       /*
                        * Decode request path and query
                        */
@@ -415,7 +419,7 @@ relay_read_http(struct bufferevent *bev,
               relay_reset_http(cre);
 done:
               if (cre->dir == RELAY_DIR_REQUEST && cre->toread <= 0 &&
-                   cre->dst->bev == NULL) {
+                   cre->dst->state != STATE_CONNECTED) {
                       if (rlay->rl_conf.fwdmode == FWD_TRANS) {
                               relay_bindanyreq(con, 0, IPPROTO_TCP);
                               return;
@@ -430,11 +434,18 @@ relay_read_http(struct bufferevent *bev,
               relay_close(con, "last http read (done)");
               return;
       }
+       switch (relay_splice(cre)) {
+       case -1:
+               relay_close(con, strerror(errno));
+       case 1:
+               return;
+       case 0:
+               break;
+       }
+       bufferevent_enable(bev, EV_READ);
       if (EVBUFFER_LENGTH(src) && bev->readcb != relay_read_http)
               bev->readcb(bev, arg);
-       bufferevent_enable(bev, EV_READ);
-       if (relay_splice(cre) == -1)
-               relay_close(con, strerror(errno));
+       /* The callback readcb() might have freed the session. */
       return;
 fail:
       relay_abort_http(con, 500, strerror(errno), 0);
@@ -484,9 +495,10 @@ relay_read_httpcontent(struct buffereven
       }
       if (con->se_done)
               goto done;
+       bufferevent_enable(bev, EV_READ);
       if (bev->readcb != relay_read_httpcontent)
               bev->readcb(bev, arg);
-       bufferevent_enable(bev, EV_READ);
+       /* The callback readcb() might have freed the session. */
       return;
 done:
       relay_close(con, "last http content read");
@@ -601,9 +613,10 @@ relay_read_httpchunks(struct bufferevent
 next:
       if (con->se_done)
               goto done;
+       bufferevent_enable(bev, EV_READ);
       if (EVBUFFER_LENGTH(src))
               bev->readcb(bev, arg);
-       bufferevent_enable(bev, EV_READ);
+       /* The callback readcb() might have freed the session. */
       return;

 done:
@@ -1363,7 +1376,7 @@ relay_match_actions(struct ctl_relay_eve
    struct kvlist *matches, struct kvlist *actions)
{
       struct rsession         *con = cre->con;
-       struct kv               *kv;
+       struct kv               *kv, *tmp;

       /*
        * Apply the following options instantly (action per match).
@@ -1382,7 +1395,7 @@ relay_match_actions(struct ctl_relay_eve
        */
       if (matches == NULL) {
               /* 'pass' or 'block' rule */
-               TAILQ_FOREACH(kv, &rule->rule_kvlist, kv_rule_entry) {
+               TAILQ_FOREACH_SAFE(kv, &rule->rule_kvlist, kv_rule_entry, tmp) {
                       TAILQ_INSERT_TAIL(actions, kv, kv_action_entry);
                       TAILQ_REMOVE(&rule->rule_kvlist, kv, kv_rule_entry);
               }
Index: usr.sbin/relayd/relayd.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.c,v
retrieving revision 1.138
diff -u -p -r1.138 relayd.c
--- usr.sbin/relayd/relayd.c    22 Jan 2015 17:42:09 -0000      1.138
+++ usr.sbin/relayd/relayd.c    28 Sep 2015 17:43:06 -0000
@@ -546,12 +546,13 @@ parent_dispatch_ca(int fd, struct privse
}

void
-purge_table(struct tablelist *head, struct table *table)
+purge_table(struct relayd *env, struct tablelist *head, struct table *table)
{
       struct host             *host;

       while ((host = TAILQ_FIRST(&table->hosts)) != NULL) {
               TAILQ_REMOVE(&table->hosts, host, entry);
+               TAILQ_REMOVE(&env->sc_hosts, host, globalentry);
               if (event_initialized(&host->cte.ev)) {
                       event_del(&host->cte.ev);
                       close(host->cte.s);
@@ -766,18 +767,13 @@ kv_purge(struct kvtree *keys)
void
kv_free(struct kv *kv)
{
-       if (kv->kv_type == KEY_TYPE_NONE)
-               return;
-       if (kv->kv_key != NULL) {
-               free(kv->kv_key);
-       }
-       kv->kv_key = NULL;
-       if (kv->kv_value != NULL) {
-               free(kv->kv_value);
-       }
-       kv->kv_value = NULL;
-       kv->kv_matchtree = NULL;
-       kv->kv_match = NULL;
+       /*
+        * This function does not clear memory referenced by
+        * kv_children or stuff on the tailqs. Use kv_delete() instead.
+        */
+
+       free(kv->kv_key);
+       free(kv->kv_value);
       memset(kv, 0, sizeof(*kv));
}

Index: usr.sbin/relayd/relayd.h
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.207
diff -u -p -r1.207 relayd.h
--- usr.sbin/relayd/relayd.h    22 Jan 2015 17:42:09 -0000      1.207
+++ usr.sbin/relayd/relayd.h    28 Sep 2015 17:43:06 -0000
@@ -180,6 +180,13 @@ enum tlsreneg_state {
       TLSRENEG_ABORT          = 3     /* the connection should be aborted */
};

+enum relay_state {
+       STATE_INIT,
+       STATE_PENDING,
+       STATE_PRECONNECT,
+       STATE_CONNECTED
+};
+
struct ctl_relay_event {
       int                      s;
       in_port_t                port;
@@ -200,6 +207,7 @@ struct ctl_relay_event {
       int                      line;
       int                      done;
       int                      timedout;
+       enum relay_state         state;
       enum direction           dir;

       u_int8_t                *buf;
@@ -1253,7 +1261,8 @@ struct ca_pkey    *pkey_add(struct relayd *
int             expand_string(char *, size_t, const char *, const char *);
void            translate_string(char *);
void            purge_key(char **, off_t);
-void            purge_table(struct tablelist *, struct table *);
+void            purge_table(struct relayd *, struct tablelist *,
+                   struct table *);
void            purge_relay(struct relayd *, struct relay *);
char           *digeststr(enum digest_type, const u_int8_t *, size_t, char *);
const char     *canonicalize_host(const char *, char *, size_t);
Index: usr.sbin/relayd/ssl.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/ssl.c,v
retrieving revision 1.28
diff -u -p -r1.28 ssl.c
--- usr.sbin/relayd/ssl.c       22 Jan 2015 17:42:09 -0000      1.28
+++ usr.sbin/relayd/ssl.c       28 Sep 2015 17:43:06 -0000
@@ -454,6 +454,7 @@ ssl_load_pkey(const void *data, size_t d
               EVP_PKEY_free(pkey);
       if (x509 != NULL)
               X509_free(x509);
+       free(exdata);

       return (0);
}