From cc1037c514e36abc8abb09bd484a68786d96567b Mon Sep 17 00:00:00 2001 From: Reyk Floeter Date: Wed, 8 May 2019 23:22:20 +0000 Subject: Fix and tweak websocket upgrade handling. - Don't expect the Connection header to equal Upgrade, it may include Upgrade - Reshuffle the code to check the Upgrade/Connection headers in one place Reported and tested by Rivo Nurges OK and input from benno@ Cvs: ---------------------------------------------------------------------- --- usr.sbin/relayd/http.h | 8 +------ usr.sbin/relayd/relay.c | 10 ++------ usr.sbin/relayd/relay_http.c | 57 +++++++++++++------------------------------- usr.sbin/relayd/relayd.c | 44 +++++++++++++++++++++++++++++++++- usr.sbin/relayd/relayd.h | 4 +++- 5 files changed, 65 insertions(+), 58 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/relayd/http.h b/usr.sbin/relayd/http.h index 0fae818c87c..e7688165275 100644 --- a/usr.sbin/relayd/http.h +++ b/usr.sbin/relayd/http.h @@ -1,4 +1,4 @@ -/* $OpenBSD: http.h,v 1.10 2019/03/04 21:25:03 benno Exp $ */ +/* $OpenBSD: http.h,v 1.11 2019/05/08 23:22:19 reyk Exp $ */ /* * Copyright (c) 2012 - 2015 Reyk Floeter @@ -251,10 +251,4 @@ struct http_descriptor { struct kvtree http_headers; }; -struct relay_http_priv { -#define HTTP_CONNECTION_UPGRADE 0x01 -#define HTTP_UPGRADE_WEBSOCKET 0x02 - int http_upgrade_req; -}; - #endif /* HTTP_H */ diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c index 4e8f3f24f78..41bb8936caf 100644 --- a/usr.sbin/relayd/relay.c +++ b/usr.sbin/relayd/relay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay.c,v 1.242 2019/03/04 21:25:03 benno Exp $ */ +/* $OpenBSD: relay.c,v 1.243 2019/05/08 23:22:19 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -1410,13 +1410,7 @@ relay_session(struct rsession *con) return; } - if (rlay->rl_proto->type == RELAY_PROTO_HTTP) { - if (relay_http_priv_init(con) == -1) { - relay_close(con, - "failed to allocate http session data", 1); - return; - } - } else { + if (rlay->rl_proto->type != RELAY_PROTO_HTTP) { if (rlay->rl_conf.fwdmode == FWD_TRANS) relay_bindanyreq(con, 0, IPPROTO_TCP); else if (relay_connect(con) == -1) { diff --git a/usr.sbin/relayd/relay_http.c b/usr.sbin/relayd/relay_http.c index 9b83eb5ba8a..f31c7e6e183 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.72 2019/03/04 21:25:03 benno Exp $ */ +/* $OpenBSD: relay_http.c,v 1.73 2019/05/08 23:22:19 reyk Exp $ */ /* * Copyright (c) 2006 - 2016 Reyk Floeter @@ -109,17 +109,6 @@ relay_http_init(struct relay *rlay) relay_calc_skip_steps(&rlay->rl_proto->rules); } -int -relay_http_priv_init(struct rsession *con) -{ - struct relay_http_priv *p; - if ((p = calloc(1, sizeof(struct relay_http_priv))) == NULL) - return (-1); - - con->se_priv = p; - return (0); -} - int relay_httpdesc_init(struct ctl_relay_event *cre) { @@ -163,13 +152,13 @@ relay_read_http(struct bufferevent *bev, void *arg) struct relay *rlay = con->se_relay; struct protocol *proto = rlay->rl_proto; struct evbuffer *src = EVBUFFER_INPUT(bev); - struct relay_http_priv *priv = con->se_priv; char *line = NULL, *key, *value; char *urlproto, *host, *path; int action, unique, ret; const char *errstr; size_t size, linelen; struct kv *hdr = NULL; + struct kv *upgrade = NULL, *upgrade_ws = NULL; getmonotime(&con->se_tv_last); cre->timedout = 0; @@ -398,17 +387,6 @@ relay_read_http(struct bufferevent *bev, void *arg) unique = 0; if (cre->line != 1) { - if (cre->dir == RELAY_DIR_REQUEST) { - if (strcasecmp("Connection", key) == 0 && - strcasecmp("Upgrade", value) == 0) - priv->http_upgrade_req |= - HTTP_CONNECTION_UPGRADE; - if (strcasecmp("Upgrade", key) == 0 && - strcasecmp("websocket", value) == 0) - priv->http_upgrade_req |= - HTTP_UPGRADE_WEBSOCKET; - } - if ((hdr = kv_add(&desc->http_headers, key, value, unique)) == NULL) { relay_abort_http(con, 400, @@ -445,37 +423,34 @@ relay_read_http(struct bufferevent *bev, void *arg) return; } - /* HTTP 101 Switching Protocols */ - if (cre->dir == RELAY_DIR_REQUEST) { - if ((priv->http_upgrade_req & HTTP_UPGRADE_WEBSOCKET) && - !(proto->httpflags & HTTPFLAG_WEBSOCKETS)) { + /* + * HTTP 101 Switching Protocols + */ + upgrade = kv_find_value(&desc->http_headers, + "Connection", "upgrade", ","); + upgrade_ws = kv_find_value(&desc->http_headers, + "Upgrade", "websocket", ","); + if (cre->dir == RELAY_DIR_REQUEST && upgrade_ws != NULL) { + if ((proto->httpflags & HTTPFLAG_WEBSOCKETS) == 0) { relay_abort_http(con, 403, "Websocket Forbidden", 0); return; - } - if ((priv->http_upgrade_req & HTTP_UPGRADE_WEBSOCKET) && - !(priv->http_upgrade_req & HTTP_CONNECTION_UPGRADE)) - { + } else if (upgrade == NULL) { relay_abort_http(con, 400, "Bad Websocket Request", 0); return; - } - if ((priv->http_upgrade_req & HTTP_UPGRADE_WEBSOCKET) && - (desc->http_method != HTTP_METHOD_GET)) { + } else if (desc->http_method != HTTP_METHOD_GET) { relay_abort_http(con, 405, "Websocket Method Not Allowed", 0); return; } } else if (cre->dir == RELAY_DIR_RESPONSE && desc->http_status == 101) { - if (((priv->http_upgrade_req & - (HTTP_CONNECTION_UPGRADE | HTTP_UPGRADE_WEBSOCKET)) - == - (HTTP_CONNECTION_UPGRADE | HTTP_UPGRADE_WEBSOCKET)) - && proto->httpflags & HTTPFLAG_WEBSOCKETS) { + if (upgrade_ws != NULL && upgrade != NULL && + (proto->httpflags & HTTPFLAG_WEBSOCKETS)) { cre->dst->toread = TOREAD_UNLIMITED; cre->dst->bev->readcb = relay_read; - } else { + } else { relay_abort_http(con, 502, "Bad Websocket Gateway", 0); return; diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c index 9e80bed2d09..6cbb734757f 100644 --- a/usr.sbin/relayd/relayd.c +++ b/usr.sbin/relayd/relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.175 2019/04/24 19:13:49 mestre Exp $ */ +/* $OpenBSD: relayd.c,v 1.176 2019/05/08 23:22:19 reyk Exp $ */ /* * Copyright (c) 2007 - 2016 Reyk Floeter @@ -812,6 +812,48 @@ kv_find(struct kvtree *keys, struct kv *kv) return (match); } +struct kv * +kv_find_value(struct kvtree *keys, char *key, const char *value, + const char *delim) +{ + struct kv *match, kv; + char *val = NULL, *next, *ptr; + size_t len; + + kv.kv_key = key; + if ((match = RB_FIND(kvtree, keys, &kv)) == NULL) + return (NULL); + + if (match->kv_value == NULL) + return (NULL); + + if (delim == NULL) { + if (strcasecmp(match->kv_value, value) == 0) + goto done; + } else { + if ((val = strdup(match->kv_value)) == NULL) + return (NULL); + for (next = ptr = val; ptr != NULL; + ptr = strsep(&next, delim)) { + /* strip whitespace */ + ptr += strspn(ptr, " \t"); + len = strcspn(ptr, " \t"); + if (strncasecmp(ptr, value, len) == 0) + goto done; + } + } + + /* not matched */ + match = NULL; + done: +#ifdef DEBUG + if (match != NULL) + DPRINTF("%s: matched %s: %s", __func__, key, value); +#endif + free(val); + return (match); +} + int kv_cmp(struct kv *a, struct kv *b) { diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index b05b2ad7603..65ffe714aba 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.252 2019/03/04 21:25:03 benno Exp $ */ +/* $OpenBSD: relayd.h,v 1.253 2019/05/08 23:22:19 reyk Exp $ */ /* * Copyright (c) 2006 - 2016 Reyk Floeter @@ -1324,6 +1324,8 @@ void relay_log(struct rsession *, char *); int kv_log(struct rsession *, struct kv *, u_int16_t, enum direction); struct kv *kv_find(struct kvtree *, struct kv *); +struct kv *kv_find_value(struct kvtree *, char *, const char *, + const char *); int kv_cmp(struct kv *, struct kv *); int rule_add(struct protocol *, struct relay_rule *, const char *); -- cgit v1.2.3