untrusted comment: signature from openbsd 6.0 base secret key RWSho3oKSqgLQz5yMCgijPP8NK5lgFOuTCaBS/1ls877CCT5dqYFarm6EcmFP8PBQLYFN6D0BjlMrwArvGZOokKnQ9M4fMrYcQA= OpenBSD 6.0 errata 3, Aug 06, 2016: Improve parsing of the Host-header by following RFC 7230 Section 5.4 more strictly. Apply by doing: signify -Vep /etc/signify/openbsd-60-base.pub -x 003_relayd.patch.sig \ -m - | (cd /usr/src && patch -p0) And then rebuild and install relayd: cd /usr/src/usr.sbin/relayd make obj make depend make make install Index: usr.sbin/relayd/relay_http.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v retrieving revision 1.56 diff -u -p -r1.56 relay_http.c --- usr.sbin/relayd/relay_http.c 22 Jul 2016 09:30:36 -0000 1.56 +++ usr.sbin/relayd/relay_http.c 2 Aug 2016 19:46:28 -0000 @@ -150,7 +150,8 @@ relay_read_http(struct bufferevent *bev, struct protocol *proto = rlay->rl_proto; struct evbuffer *src = EVBUFFER_INPUT(bev); char *line = NULL, *key, *value; - int action; + char *urlproto, *host, *path; + int action, unique, ret; const char *errstr; size_t size, linelen; struct kv *hdr = NULL; @@ -331,11 +332,35 @@ relay_read_http(struct bufferevent *bev, strcasecmp("chunked", value) == 0) desc->http_chunked = 1; + /* The following header should only occur once */ + if (strcasecmp("Host", key) == 0) { + unique = 1; + + /* + * The path may contain a URL. The host in the + * URL has to match the Host: value. + */ + if (parse_url(desc->http_path, + &urlproto, &host, &path) == 0) { + ret = strcasecmp(host, value); + free(urlproto); + free(host); + free(path); + if (ret != 0) { + relay_abort_http(con, 400, + "malformed host", 0); + goto abort; + } + } + } else + unique = 0; + if (cre->line != 1) { if ((hdr = kv_add(&desc->http_headers, key, - value)) == NULL) { - free(line); - goto fail; + value, unique)) == NULL) { + relay_abort_http(con, 400, + "malformed header", 0); + goto abort; } desc->http_lastheader = hdr; } @@ -1537,7 +1562,7 @@ relay_apply_actions(struct ctl_relay_eve if (addkv && kv->kv_matchtree != NULL) { /* Add new entry to the list (eg. new HTTP header) */ if ((match = kv_add(kv->kv_matchtree, kp->kv_key, - kp->kv_value)) == NULL) + kp->kv_value, 0)) == NULL) goto fail; match->kv_option = kp->kv_option; match->kv_type = kp->kv_type; Index: usr.sbin/relayd/relayd.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relayd.c,v retrieving revision 1.153 diff -u -p -r1.153 relayd.c --- usr.sbin/relayd/relayd.c 2 Feb 2016 17:51:11 -0000 1.153 +++ usr.sbin/relayd/relayd.c 2 Aug 2016 19:46:28 -0000 @@ -645,9 +645,8 @@ purge_relay(struct relayd *env, struct r free(rlay); } - struct kv * -kv_add(struct kvtree *keys, char *key, char *value) +kv_add(struct kvtree *keys, char *key, char *value, int unique) { struct kv *kv, *oldkv; @@ -655,24 +654,30 @@ kv_add(struct kvtree *keys, char *key, c return (NULL); if ((kv = calloc(1, sizeof(*kv))) == NULL) return (NULL); - if ((kv->kv_key = strdup(key)) == NULL) { - free(kv); - return (NULL); - } + if ((kv->kv_key = strdup(key)) == NULL) + goto fail; if (value != NULL && - (kv->kv_value = strdup(value)) == NULL) { - free(kv->kv_key); - free(kv); - return (NULL); - } + (kv->kv_value = strdup(value)) == NULL) + goto fail; TAILQ_INIT(&kv->kv_children); if ((oldkv = RB_INSERT(kvtree, keys, kv)) != NULL) { + /* + * return error if the key should occur only once, + * or add it to a list attached to the key's node. + */ + if (unique) + goto fail; TAILQ_INSERT_TAIL(&oldkv->kv_children, kv, kv_entry); kv->kv_parent = oldkv; } return (kv); + fail: + free(kv->kv_key); + free(kv->kv_value); + free(kv); + return (NULL); } int @@ -1379,6 +1384,52 @@ canonicalize_host(const char *host, char fail: errno = EINVAL; return (NULL); +} + +int +parse_url(const char *url, char **protoptr, char **hostptr, char **pathptr) +{ + char *p, *proto = NULL, *host = NULL, *path = NULL; + + /* return error if it is not a URL */ + if ((p = strstr(url, ":/")) == NULL || + (strcspn(url, ":/") != (size_t)(p - url))) + return (-1); + + /* get protocol */ + if ((proto = strdup(url)) == NULL) + goto fail; + p = proto + (p - url); + + /* get host */ + p += strspn(p, ":/"); + if (*p == '\0' || (host = strdup(p)) == NULL) + goto fail; + *p = '\0'; + + /* find and copy path or default to "/" */ + if ((p = strchr(host, '/')) == NULL) + p = "/"; + if ((path = strdup(p)) == NULL) + goto fail; + + /* strip path after host */ + host[strcspn(host, "/")] = '\0'; + + DPRINTF("%s: %s proto %s, host %s, path %s", __func__, + url, proto, host, path); + + *protoptr = proto; + *hostptr = host; + *pathptr = path; + + return (0); + + fail: + free(proto); + free(host); + free(path); + return (-1); } int Index: usr.sbin/relayd/relayd.h =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v retrieving revision 1.223 diff -u -p -r1.223 relayd.h --- usr.sbin/relayd/relayd.h 22 Jul 2016 09:30:36 -0000 1.223 +++ usr.sbin/relayd/relayd.h 2 Aug 2016 19:46:28 -0000 @@ -1268,6 +1268,7 @@ void purge_table(struct relayd *, stru 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); +int parse_url(const char *, char **, char **, char **); int map6to4(struct sockaddr_storage *); int map4to6(struct sockaddr_storage *, struct sockaddr_storage *); void imsg_event_add(struct imsgev *); @@ -1281,7 +1282,7 @@ struct in6_addr *prefixlen2mask6(u_int8_ u_int32_t prefixlen2mask(u_int8_t); int accept_reserve(int, struct sockaddr *, socklen_t *, int, volatile int *); -struct kv *kv_add(struct kvtree *, char *, char *); +struct kv *kv_add(struct kvtree *, char *, char *, int); int kv_set(struct kv *, char *, ...); int kv_setkey(struct kv *, char *, ...); void kv_delete(struct kvtree *, struct kv *);