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 #include #include +#include #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); }