On Mon, Mar 04, 2019 at 07:53:04PM +0100, Sebastian Benoit wrote:
> > The RFC says it must be a GET request.  We should check at least
> > this.  If we check more, an attacker can create less dubious states.
> 
> thx, I was looking for something like that and could not find it.
> Where?

RFC 6455, The WebSocket Protocol, Page 16

   2.   The method of the request MUST be GET, and the HTTP version MUST
        be at least 1.1.

There are a lot of other MUST, but let's only do the obvious and
easy ones now.  Can be extended later.

> (relayd_101Switching_policy4.diff)

OK bluhm@

> diff --git usr.sbin/relayd/http.h usr.sbin/relayd/http.h
> index 052bc0ce326..135ca5bbcb7 100644
> --- usr.sbin/relayd/http.h
> +++ usr.sbin/relayd/http.h
> @@ -251,4 +251,10 @@ 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 usr.sbin/relayd/parse.y usr.sbin/relayd/parse.y
> index 9875973fd80..66a568d5e62 100644
> --- usr.sbin/relayd/parse.y
> +++ usr.sbin/relayd/parse.y
> @@ -176,6 +176,7 @@ typedef struct {
>  %token       TO ROUTER RTLABEL TRANSPARENT TRAP UPDATES URL VIRTUAL WITH TTL 
> RTABLE
>  %token       MATCH PARAMS RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE 
> PASSWORD ECDHE
>  %token       EDH TICKETS CONNECTION CONNECTIONS ERRORS STATE CHANGES CHECKS
> +%token       WEBSOCKETS
>  %token       <v.string>      STRING
>  %token  <v.number>   NUMBER
>  %type        <v.string>      hostname interface table value optstring
> @@ -1064,8 +1065,20 @@ protoptsl      : ssltls tlsflags
>               | ssltls '{' tlsflags_l '}'
>               | TCP tcpflags
>               | TCP '{' tcpflags_l '}'
> -             | HTTP httpflags
> -             | HTTP '{' httpflags_l '}'
> +             | HTTP {
> +                     if (proto->type != RELAY_PROTO_HTTP) {
> +                             yyerror("can set http options only for "
> +                                 "http protocol");
> +                             YYERROR;
> +                     }
> +             } httpflags
> +             | HTTP  {
> +                     if (proto->type != RELAY_PROTO_HTTP) {
> +                             yyerror("can set http options only for "
> +                                 "http protocol");
> +                             YYERROR;
> +                     }
> +             } '{' httpflags_l '}'
>               | RETURN ERROR opteflags        { proto->flags |= F_RETURN; }
>               | RETURN ERROR '{' eflags_l '}' { proto->flags |= F_RETURN; }
>               | filterrule
> @@ -1078,17 +1091,14 @@ httpflags_l   : httpflags comma httpflags_l
>               ;
>  
>  httpflags    : HEADERLEN NUMBER      {
> -                     if (proto->type != RELAY_PROTO_HTTP) {
> -                             yyerror("can set http options only for "
> -                                 "http protocol");
> -                             YYERROR;
> -                     }
>                       if ($2 < 0 || $2 > RELAY_MAXHEADERLENGTH) {
>                               yyerror("invalid headerlen: %d", $2);
>                               YYERROR;
>                       }
>                       proto->httpheaderlen = $2;
>               }
> +             | WEBSOCKETS    { proto->httpflags |= HTTPFLAG_WEBSOCKETS; }
> +             | NO WEBSOCKETS { proto->httpflags &= ~HTTPFLAG_WEBSOCKETS; }
>               ;
>  
>  tcpflags_l   : tcpflags comma tcpflags_l
> @@ -2338,6 +2348,7 @@ lookup(char *s)
>               { "url",                URL },
>               { "value",              VALUE },
>               { "virtual",            VIRTUAL },
> +             { "websockets",         WEBSOCKETS },
>               { "with",               WITH }
>       };
>       const struct keywords   *p;
> diff --git usr.sbin/relayd/relay.c usr.sbin/relayd/relay.c
> index 739c9226b65..bf662b9a1df 100644
> --- usr.sbin/relayd/relay.c
> +++ usr.sbin/relayd/relay.c
> @@ -1410,7 +1410,13 @@ relay_session(struct rsession *con)
>               return;
>       }
>  
> -     if (rlay->rl_proto->type != RELAY_PROTO_HTTP) {
> +     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_conf.fwdmode == FWD_TRANS)
>                       relay_bindanyreq(con, 0, IPPROTO_TCP);
>               else if (relay_connect(con) == -1) {
> diff --git usr.sbin/relayd/relay_http.c usr.sbin/relayd/relay_http.c
> index a9d27bfe605..e431d2b8f44 100644
> --- usr.sbin/relayd/relay_http.c
> +++ usr.sbin/relayd/relay_http.c
> @@ -109,6 +109,17 @@ 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)
>  {
> @@ -152,6 +163,7 @@ 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;
> @@ -386,6 +398,17 @@ 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,
> @@ -422,6 +445,43 @@ 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)) {
> +                             relay_abort_http(con, 403,
> +                                 "Websocket Forbidden", 0);
> +                             return;
> +                     }
> +                     if ((priv->http_upgrade_req & HTTP_UPGRADE_WEBSOCKET) &&
> +                         !(priv->http_upgrade_req & HTTP_CONNECTION_UPGRADE))
> +                     {
> +                             relay_abort_http(con, 400,
> +                                 "Bad Websocket Request", 0);
> +                             return;
> +                     }
> +                     if ((priv->http_upgrade_req & HTTP_UPGRADE_WEBSOCKET) &&
> +                         (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) {
> +                             cre->dst->toread = TOREAD_UNLIMITED;
> +                             cre->dst->bev->readcb = relay_read;
> +                     }  else  {
> +                             relay_abort_http(con, 502,
> +                                 "Bad Websocket Gateway", 0);
> +                             return;
> +                     }
> +             }
> +
>               switch (desc->http_method) {
>               case HTTP_METHOD_CONNECT:
>                       /* Data stream */
> diff --git usr.sbin/relayd/relayd.conf.5 usr.sbin/relayd/relayd.conf.5
> index d43ffa06627..5a651784060 100644
> --- usr.sbin/relayd/relayd.conf.5
> +++ usr.sbin/relayd/relayd.conf.5
> @@ -1006,6 +1006,10 @@ Valid options are:
>  .It Ic headerlen Ar number
>  Set the maximum size of all HTTP headers in bytes.
>  The default value is 8192 and it is limited to a maximum of 131072.
> +.It Ic websockets
> +Allow connection upgrade to websocket protocol.
> +The default is
> +.Ic no websockets .
>  .El
>  .El
>  .Sh FILTER RULES
> diff --git usr.sbin/relayd/relayd.h usr.sbin/relayd/relayd.h
> index fe55c3a8478..121aa5bd13e 100644
> --- usr.sbin/relayd/relayd.h
> +++ usr.sbin/relayd/relayd.h
> @@ -702,6 +702,8 @@ struct relay_ticket_key {
>  };
>  #define      TLS_SESSION_LIFETIME    (2 * 3600)
>  
> +#define HTTPFLAG_WEBSOCKETS  0x01
> +
>  struct protocol {
>       objid_t                  id;
>       u_int32_t                flags;
> @@ -711,6 +713,7 @@ struct protocol {
>       u_int8_t                 tcpipttl;
>       u_int8_t                 tcpipminttl;
>       size_t                   httpheaderlen;
> +     int                      httpflags;
>       u_int8_t                 tlsflags;
>       char                     tlsciphers[768];
>       char                     tlsdhparams[128];
> @@ -1227,6 +1230,7 @@ const char
>       *relay_httpmethod_byid(u_int);
>  const char
>       *relay_httperror_byid(u_int);
> +int   relay_http_priv_init(struct rsession *);
>  int   relay_httpdesc_init(struct ctl_relay_event *);
>  ssize_t       relay_http_time(time_t, char *, size_t);
>  

Reply via email to