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