diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2016-07-27 06:55:45 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2016-07-27 06:55:45 +0000 |
commit | 19b20ae8ae29e6041ddd4d8cf903d9e41ab5ddcb (patch) | |
tree | f68f99067f9037c8fd1684a04f1b9fadc4820cd7 /usr.sbin/relayd | |
parent | a096341ccb5d36010f58c11156debc8355afd42a (diff) |
Improve parsing of the Host by following RFC 7230 Section 5.4 more strictly:
- Respond with a 400 (Bad Request) if there is more than one Host:
header to prevent ambiguities.
- Make sure that the host in the optional absolute form of
request-target (eg. GET http://www.target.com/ HTTP/1.1) matches the
Host: value. Proxies are supposed to ignore the Host: value if the
request-target exists, but relayd used to ignore the absolute
request-target form instead. In HTTP terminology, relayd is a gateway
and not a proxy, but it has to make sure that the host is validated
consistently.
OK benno@ bluhm@
Diffstat (limited to 'usr.sbin/relayd')
-rw-r--r-- | usr.sbin/relayd/relay_http.c | 37 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.c | 75 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 5 |
3 files changed, 97 insertions, 20 deletions
diff --git a/usr.sbin/relayd/relay_http.c b/usr.sbin/relayd/relay_http.c index 3d4066b71cd..bab21355b2d 100644 --- a/usr.sbin/relayd/relay_http.c +++ b/usr.sbin/relayd/relay_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay_http.c,v 1.56 2016/07/22 09:30:36 benno Exp $ */ +/* $OpenBSD: relay_http.c,v 1.57 2016/07/27 06:55:44 reyk Exp $ */ /* * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org> @@ -150,7 +150,8 @@ relay_read_http(struct bufferevent *bev, void *arg) 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, void *arg) 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_event *cre, struct kvlist *actions) 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; diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c index 8fdedcbfbfa..65c7ee20b85 100644 --- a/usr.sbin/relayd/relayd.c +++ b/usr.sbin/relayd/relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.153 2016/02/02 17:51:11 sthen Exp $ */ +/* $OpenBSD: relayd.c,v 1.154 2016/07/27 06:55:44 reyk Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org> @@ -645,9 +645,8 @@ purge_relay(struct relayd *env, struct relay *rlay) 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, char *value) 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 @@ -1382,6 +1387,52 @@ canonicalize_host(const char *host, char *name, size_t len) } 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 bindany(struct ctl_bindany *bnd) { int s, v; diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index 2430617a16f..e051b8ee798 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.223 2016/07/22 09:30:36 benno Exp $ */ +/* $OpenBSD: relayd.h,v 1.224 2016/07/27 06:55:44 reyk Exp $ */ /* * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org> @@ -1268,6 +1268,7 @@ void purge_table(struct relayd *, struct tablelist *, 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_t, u_int32_t *); 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 *); |