1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

strict host check vhost flag

https://github.com/warmcat/libwebsockets/issues/1423

If you vhost->options has the flag LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK,
then if the server is sent an upgrade request, the content of the Host: header is
required to match the vhost name + port.  The port is set to the well-known values
of 80 and 443 if no :port on the host: value, depending on tls or not on the
connection.

minimal-ws-server can now take a -h flag to set this.  lejp-conf (eg, lwsws) can now take
a flag strict-host-check on the vhost to enable it as well.
This commit is contained in:
Andy Green 2018-11-13 09:33:13 +08:00
parent 97f9af5e3b
commit f6ae0edf8d
7 changed files with 104 additions and 8 deletions

View file

@ -134,6 +134,17 @@ enum lws_context_options {
* example the ACME plugin was configured to fetch a cert, this lets
* you bootstrap your vhost from having no cert to start with.
*/
LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK = (1 << 27),
/**< (VH) On this vhost, if the connection is being upgraded, insist
* that there's a Host: header and that the contents match the vhost
* name + port (443 / 80 are assumed if no :port given based on if the
* connection is using TLS).
*
* By default, without this flag, on upgrade lws just checks that the
* Host: header was given without checking the contents... this is to
* allow lax hostname mappings like localhost / 127.0.0.1, and CNAME
* mappings like www.mysite.com / mysite.com
*/
/****** add new things just above ---^ ******/
};

View file

@ -109,6 +109,7 @@ static const char * const paths_vhosts[] = {
"vhosts[].ssl-client-option-clear",
"vhosts[].tls13-ciphers",
"vhosts[].client-tls13-ciphers",
"vhosts[].strict-host-check",
};
enum lejp_vhost_paths {
@ -164,6 +165,7 @@ enum lejp_vhost_paths {
LEJPVP_SSL_CLIENT_OPTION_CLEAR,
LEJPVP_TLS13_CIPHERS,
LEJPVP_CLIENT_TLS13_CIPHERS,
LEJPVP_FLAG_STRICT_HOST_CHECK,
};
static const char * const parser_errs[] = {
@ -754,6 +756,15 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
return 0;
case LEJPVP_FLAG_STRICT_HOST_CHECK:
if (arg_to_bool(ctx->buf))
a->info->options |=
LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK;
else
a->info->options &=
~(LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK);
return 0;
case LEJPVP_ERROR_DOCUMENT_404:
a->info->error_document_404 = a->p;
break;

View file

@ -1545,6 +1545,75 @@ transaction_result_n:
return lws_http_transaction_completed(wsi);
}
int
lws_confirm_host_header(struct lws *wsi)
{
struct lws_tokenize ts;
lws_tokenize_elem e;
char buf[128];
int port = 80;
/*
* this vhost wants us to validate what the
* client sent against our vhost name
*/
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
lwsl_info("%s: missing host on upgrade\n", __func__);
return 1;
}
#if defined(LWS_WITH_TLS)
if (wsi->tls.ssl)
port = 443;
#endif
lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_DOT_NONTERM /* server.com */|
LWS_TOKENIZE_F_NO_FLOATS /* 1.server.com */|
LWS_TOKENIZE_F_MINUS_NONTERM /* a-b.com */);
ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_HOST);
if (ts.len <= 0) {
lwsl_info("%s: missing or oversize host header\n", __func__);
return 1;
}
if (lws_tokenize(&ts) != LWS_TOKZE_TOKEN)
goto bad_format;
if (strncmp(ts.token, wsi->vhost->name, ts.token_len)) {
buf[(ts.token - buf) + ts.token_len] = '\0';
lwsl_info("%s: '%s' in host hdr but vhost name %s\n",
__func__, ts.token, wsi->vhost->name);
return 1;
}
e = lws_tokenize(&ts);
if (e == LWS_TOKZE_DELIMITER && ts.token[0] == ':') {
if (lws_tokenize(&ts) != LWS_TOKZE_INTEGER)
goto bad_format;
else
port = atoi(ts.token);
} else
if (e != LWS_TOKZE_ENDED)
goto bad_format;
if (wsi->vhost->listen_port != port) {
lwsl_info("%s: host port %d mismatches vhost port %d\n",
__func__, port, wsi->vhost->listen_port);
return 1;
}
lwsl_debug("%s: host header OK\n", __func__);
return 0;
bad_format:
lwsl_info("%s: bad host header format\n", __func__);
return 1;
}
int
lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
{
@ -1733,6 +1802,11 @@ raw_transition:
/* callback said 0, it was allowed */
if (wsi->vhost->options &
LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK &&
lws_confirm_host_header(wsi))
goto bail_nuke_ah;
if (!strcasecmp(up, "websocket")) {
#if defined(LWS_ROLE_WS)
wsi->vhost->conn_stats.ws_upg++;

View file

@ -315,14 +315,6 @@ bad_conn_format:
}
} while (e > 0);
/* let's also confirm that Host at least exists for h1 */
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
lwsl_err("%s: missing host: hdr on h1 ws upgrade\n", __func__);
return 1;
}
#if defined(LWS_WITH_HTTP2)
check_protocol:
#endif

View file

@ -76,6 +76,9 @@ int main(int argc, const char **argv)
info.ssl_cert_filepath = "localhost-100y.cert";
info.ssl_private_key_filepath = "localhost-100y.key";
if (lws_cmdline_option(argc, argv, "-h"))
info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK;
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");

View file

@ -12,6 +12,7 @@ Option|Meaning
---|---
-d|Set logging verbosity
-s|Serve using TLS selfsigned cert (ie, connect to it with https://...)
-h|Strict Host: header checking against vhost name (localhost) and port
## usage

View file

@ -79,6 +79,7 @@ int main(int argc, const char **argv)
info.port = 7681;
info.mounts = &mount;
info.protocols = protocols;
info.vhost_name = "localhost";
info.ws_ping_pong_interval = 10;
if (lws_cmdline_option(argc, argv, "-s")) {
@ -88,6 +89,9 @@ int main(int argc, const char **argv)
info.ssl_private_key_filepath = "localhost-100y.key";
}
if (lws_cmdline_option(argc, argv, "-h"))
info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK;
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");