From 4f04f3de92568fbfdd4cf4bf2999503333a36028 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 5 Aug 2021 12:43:51 +0100 Subject: [PATCH] Add `websockets_origin` option This allows Origin header checking when clients attempt to upgrade from http->websockets. --- ChangeLog.txt | 2 ++ man/mosquitto.conf.5.xml | 38 +++++++++++++++++++++++++-------- mosquitto.conf | 8 +++++++ src/conf.c | 27 ++++++++++++++++++++++- src/mosquitto_broker_internal.h | 2 ++ src/websockets.c | 22 +++++++++++++++++++ 6 files changed, 89 insertions(+), 10 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index fd86cb4d..329145ca 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -45,6 +45,8 @@ Broker: of `per_listener_settings`. - Protocol version numbers reported in the log when a client connects now match the MQTT protocol version numbers, not internal Mosquitto values. +- Add the `websockets_origin` option to allow optional enforcement of origin + when a connection attempts an upgrade to WebSockets. Client library: - Add MOSQ_OPT_DISABLE_SOCKETPAIR to allow the disabling of the socketpair diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index 9d36300c..5734b485 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -1285,6 +1285,19 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S Not reloaded on reload signal. + + size + + Change the websockets headers size. This is a + global option, it is not possible to set per + listener. This option sets the size of the buffer + used in the libwebsockets library when reading HTTP + headers. If you are passing large header data such + as cookies then you may need to increase this + value. If left unset, or set to 0, then the default + of 1024 bytes will be used. + + level @@ -1300,16 +1313,23 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S - size + level - Change the websockets headers size. This is a - global option, it is not possible to set per - listener. This option sets the size of the buffer - used in the libwebsockets library when reading HTTP - headers. If you are passing large header data such - as cookies then you may need to increase this - value. If left unset, or set to 0, then the default - of 1024 bytes will be used. + + If set, this will be compared to the http origin + header when a connection attempts to upgrade to + WebSockets. Only matching origins will be allowed. + Use the exact string provided by the browser in the + origin header, e.g. + http://example.com:8080. + Can be specified multiple times per listener. + + + If not set, connections from any origin will be allowed. + + + Requires libwebsockets 3.1.0 or later. + diff --git a/mosquitto.conf b/mosquitto.conf index 36bcb1c4..99282299 100644 --- a/mosquitto.conf +++ b/mosquitto.conf @@ -280,6 +280,14 @@ # unset, or set to 0, then the default of 1024 bytes will be used. #websockets_headers_size +# Enforce origin checking for websockets connections. This should be set to the +# string of the host that you wish to allow connections from, as set as the +# Origin header in the http request. +# For example: https://example.com:8080 +# If left unset will allow connections from any origin. +# May be set multiple times. +#websockets_origin + # ----------------------------------------------------------------- # Certificate based SSL/TLS support # ----------------------------------------------------------------- diff --git a/src/conf.c b/src/conf.c index f7c08100..307922ff 100644 --- a/src/conf.c +++ b/src/conf.c @@ -253,7 +253,7 @@ void config__init(struct mosquitto__config *config) void config__cleanup(struct mosquitto__config *config) { - int i; + int i, j; mosquitto__free(config->clientid_prefixes); mosquitto__free(config->persistence_location); @@ -298,6 +298,10 @@ void config__cleanup(struct mosquitto__config *config) #endif #ifdef WITH_WEBSOCKETS mosquitto__free(config->listeners[i].http_dir); + for(j=0; jlisteners[i].ws_origin_count; j++){ + mosquitto__free(config->listeners[i].ws_origins[j]); + } + mosquitto__free(config->listeners[i].ws_origins); #endif #ifdef WITH_UNIX_SOCKETS mosquitto__free(config->listeners[i].unix_socket_path); @@ -799,6 +803,9 @@ static int config__read_file_core(struct mosquitto__config *config, bool reload, char *kpass_sha = NULL, *kpass_sha_bin = NULL; char *keyform ; #endif +#ifdef WITH_WEBSOCKETS + char **ws_origins = NULL; +#endif *lineno = 0; @@ -2307,6 +2314,24 @@ static int config__read_file_core(struct mosquitto__config *config, bool reload, return MOSQ_ERR_INVAL; } config->websockets_headers_size = (uint16_t)tmp_int; +#else + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Websockets support not available."); +#endif + }else if(!strcmp(token, "websockets_origin")){ +#ifdef WITH_WEBSOCKETS +# if LWS_LIBRARY_VERSION_NUMBER >= 3001000 + ws_origins = mosquitto__realloc(cur_listener->ws_origins, sizeof(char *)*(size_t)(cur_listener->ws_origin_count+1)); + if(ws_origins == NULL){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); + return MOSQ_ERR_NOMEM; + } + ws_origins[cur_listener->ws_origin_count] = NULL; + if(conf__parse_string(&token, "websockets_origin", &ws_origins[cur_listener->ws_origin_count], &saveptr)) return MOSQ_ERR_INVAL; + cur_listener->ws_origins = ws_origins; + cur_listener->ws_origin_count++; +# else + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: websockets_origin support not available, libwebsockets version is too old."); +# endif #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Websockets support not available."); #endif diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index 86388b9e..3194a104 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -232,6 +232,8 @@ struct mosquitto__listener { bool ws_in_init; char *http_dir; struct lws_protocols *ws_protocol; + char **ws_origins; + int ws_origin_count; #endif struct mosquitto__security_options security_options; #ifdef WITH_UNIX_SOCKETS diff --git a/src/websockets.c b/src/websockets.c index 46688a2e..9b173ef4 100644 --- a/src/websockets.c +++ b/src/websockets.c @@ -447,6 +447,8 @@ static int callback_http( struct stat filestat; struct mosquitto *mosq; struct lws_pollargs *pollargs = (struct lws_pollargs *)in; + int hlen; + int i; /* FIXME - ssl cert verification is done here. */ @@ -538,6 +540,26 @@ static int callback_http( /* Access control here */ return 0; +#if LWS_LIBRARY_VERSION_NUMBER >= 3001000 + case LWS_CALLBACK_HTTP_CONFIRM_UPGRADE: + hack = lws_context_user(lws_get_context(wsi)); + if(hack->listener->ws_origin_count){ + hlen = lws_hdr_total_length(wsi, WSI_TOKEN_ORIGIN); + if(hlen <= 0 || hlen >= (int)sizeof(buf)){ + return -1; + } + for(i=0; ilistener->ws_origin_count; i++){ + lws_hdr_copy(wsi, (char *)buf, sizeof(buf), WSI_TOKEN_ORIGIN); + if((!strcmp(hack->listener->ws_origins[i], (char *)buf))){ + return 0; + } + } + return -1; + }else{ + return 0; + } +#endif + case LWS_CALLBACK_HTTP_WRITEABLE: /* Send our data here */ if(u && u->fptr){