From: Andrew Kaster Date: Tue, 21 Jan 2025 16:57:46 +0000 (-0700) Subject: ws: Reject frames with unknown reserved bits set X-Git-Tag: curl-8_12_0~30 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1b740aedcdebe3c15e202c215c56a0ed12d53501;p=thirdparty%2Fcurl.git ws: Reject frames with unknown reserved bits set RFC 6455 Section 5.2 notes that for bits RSV1, RSV2, and RSV3 of the framing header, a non-zero value that is not defined by a negotiated extension MUST Fail the WebSocket connection. Test 2310 verifies Closes #16069 --- diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index caf2254f99..9778d4a138 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -327,7 +327,7 @@ jobs: run: | export TFLAGS='-j8 ${{ matrix.tflags }} ~SCP' if [ '${{ matrix.sys }}' != 'msys' ]; then - TFLAGS+=' ~2301 ~2302' # WebSockets' + TFLAGS+=' ~2301 ~2302 ~2310' # WebSockets' TFLAGS+=' ~612 ~613 ~616 ~618' # SFTP else TFLAGS+=' ~SFTP' @@ -370,14 +370,14 @@ jobs: url: 'https://github.com/brechtsanders/winlibs_mingw/releases/download/9.5.0-10.0.0-msvcrt-r1/winlibs-x86_64-posix-seh-gcc-9.5.0-mingw-w64msvcrt-10.0.0-r1.7z' config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF' type: 'Release' - tflags: '~2301 ~2302 ~3027' + tflags: '~2301 ~2302 ~2310 ~3027' - name: 'schannel U' env: '7.3.0-x86_64' dir: 'mingw64' url: 'https://downloads.sourceforge.net/mingw-w64/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/7.3.0/threads-win32/seh/x86_64-7.3.0-release-win32-seh-rt_v5-rev0.7z' config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON' type: 'Release' - tflags: '~2301 ~2302 ~3027 ~3023 ~3024 ~1451' + tflags: '~2301 ~2302 ~2310 ~3027 ~3023 ~3024 ~1451' - name: 'schannel !unity' env: '6.4.0-i686' dir: 'mingw32' @@ -579,7 +579,7 @@ jobs: arch: 'x64' plat: 'windows' type: 'Debug' - tflags: '~1516 ~2301 ~2302 ~2303 ~2307' + tflags: '~1516 ~2301 ~2302 ~2303 ~2307 ~2310' config: >- -DCURL_USE_LIBSSH2=ON -DCURL_USE_SCHANNEL=ON -DCURL_USE_OPENSSL=ON -DCURL_USE_MBEDTLS=ON -DCURL_USE_WOLFSSL=ON -DCURL_DEFAULT_SSL_BACKEND=schannel @@ -590,7 +590,7 @@ jobs: arch: 'x64' plat: 'windows' type: 'Debug' - tflags: '~1516 ~2301 ~2302 ~2303 ~2307' + tflags: '~1516 ~2301 ~2302 ~2303 ~2307 ~2310' config: >- -DCURL_USE_LIBSSH2=ON -DCURL_USE_SCHANNEL=OFF -DCURL_USE_OPENSSL=ON -DUSE_OPENSSL_QUIC=ON @@ -612,7 +612,7 @@ jobs: arch: 'x64' plat: 'windows' type: 'Debug' - tflags: '~1516 ~2301 ~2302 ~2303 ~2307' + tflags: '~1516 ~2301 ~2302 ~2303 ~2307 ~2310' config: >- -DCURL_USE_LIBSSH2=ON -DCURL_USE_SCHANNEL=OFF -DCURL_USE_OPENSSL=ON -DUSE_NGTCP2=ON @@ -623,7 +623,7 @@ jobs: arch: 'x64' plat: 'windows' type: 'Debug' - tflags: '~1516 ~2301 ~2302 ~2303 ~2307' + tflags: '~1516 ~2301 ~2302 ~2303 ~2307 ~2310' config: >- -DCURL_USE_LIBSSH2=ON -DCURL_USE_SCHANNEL=OFF -DCURL_USE_OPENSSL=ON diff --git a/lib/ws.c b/lib/ws.c index 2816739f78..25d19c6972 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -47,6 +47,10 @@ #define WSBIT_FIN 0x80 +#define WSBIT_RSV1 0x40 +#define WSBIT_RSV2 0x20 +#define WSBIT_RSV3 0x10 +#define WSBIT_RSV_MASK (WSBIT_RSV1 | WSBIT_RSV2 | WSBIT_RSV3) #define WSBIT_OPCODE_CONT 0 #define WSBIT_OPCODE_TEXT (1) #define WSBIT_OPCODE_BIN (2) @@ -108,6 +112,13 @@ static unsigned char ws_frame_flags2op(int flags) return 0; } +/* No extensions are supported. If any of the RSV bits are set, we must fail */ +static bool ws_frame_rsv_supported(int flags) +{ + unsigned char reserved_bits = flags & WSBIT_RSV_MASK; + return reserved_bits == 0; +} + static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, const char *msg) { @@ -175,9 +186,17 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, dec->head[0] = *inbuf; Curl_bufq_skip(inraw, 1); + if(!ws_frame_rsv_supported(dec->head[0])) { + failf(data, "WS: unknown reserved bit in frame header: %x", + dec->head[0] & WSBIT_RSV_MASK); + ws_dec_reset(dec); + return CURLE_RECV_ERROR; + } + dec->frame_flags = ws_frame_op2flags(dec->head[0]); if(!dec->frame_flags) { - failf(data, "WS: unknown opcode: %x", dec->head[0]); + failf(data, "WS: unknown opcode: %x", + dec->head[0] & WSBIT_OPCODE_MASK); ws_dec_reset(dec); return CURLE_RECV_ERROR; } diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 43c30d5e3f..76ba64e437 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -256,7 +256,7 @@ test2100 \ test2200 test2201 test2202 test2203 test2204 test2205 \ \ test2300 test2301 test2302 test2303 test2304 test2305 test2306 test2307 \ -test2308 test2309 \ +test2308 test2309 test2310 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 \ \ diff --git a/tests/data/test2310 b/tests/data/test2310 new file mode 100644 index 0000000000..a5338c5e72 --- /dev/null +++ b/tests/data/test2310 @@ -0,0 +1,68 @@ + + + +WebSockets + + + +# +# Sends a PING + a TEXT with RSV1 set + + +HTTP/1.1 101 Switching to WebSockets +Server: test-server/fake +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Accept: HkPsVga7+8LuxM4RGQ5p9tZHeYs= + +%hex[%89%00%C1%05hello]hex% + +# allow upgrade + +upgrade + + + +# +# Client-side + +# require debug for the forced CURL_ENTROPY + +Debug +ws + + +http + + +WebSockets unknown reserved bit set in frame header + + +lib%TESTNUMBER + + +ws://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# +# PONG with no data and the 32 bit mask +# + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: webbie-sox/3 +Accept: */* +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Version: 13 +Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ== + +%hex[%8a%808321]hex% + + +Returned 56, should be 56. + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index d0acf63778..01f94018b5 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -72,7 +72,7 @@ LIBTESTPROGS = libauthretry libntlmconnect libprereq \ lib1945 lib1946 lib1947 lib1948 lib1955 lib1956 lib1957 lib1958 lib1959 \ lib1960 lib1964 \ lib1970 lib1971 lib1972 lib1973 lib1974 lib1975 lib1977 \ - lib2301 lib2302 lib2304 lib2305 lib2306 lib2308 lib2309 \ + lib2301 lib2302 lib2304 lib2305 lib2306 lib2308 lib2309 lib2310 \ lib2402 lib2404 lib2405 \ lib2502 \ lib3010 lib3025 lib3026 lib3027 \ @@ -691,6 +691,9 @@ lib2308_LDADD = $(TESTUTIL_LIBS) lib2309_SOURCES = lib2309.c $(SUPPORTFILES) lib2309_LDADD = $(TESTUTIL_LIBS) +lib2310_SOURCES = lib2310.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) $(MULTIBYTE) +lib2310_LDADD = $(TESTUTIL_LIBS) + lib2402_SOURCES = lib2402.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib2402_LDADD = $(TESTUTIL_LIBS) diff --git a/tests/libtest/lib2310.c b/tests/libtest/lib2310.c new file mode 100644 index 0000000000..a3d163ae21 --- /dev/null +++ b/tests/libtest/lib2310.c @@ -0,0 +1,68 @@ +/*************************************************************************** +* _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "test.h" +#include "testtrace.h" +#include "memdebug.h" + +#ifndef CURL_DISABLE_WEBSOCKETS + +static size_t writecb(char *b, size_t size, size_t nitems, void *p) +{ + (void)b; + (void)size; + (void)nitems; + (void)p; + return 0; +} + +CURLcode test(char *URL) +{ + CURL *curl; + CURLcode res = CURLE_OK; + + global_init(CURL_GLOBAL_ALL); + + curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, URL); + + /* use the callback style */ + curl_easy_setopt(curl, CURLOPT_USERAGENT, "webbie-sox/3"); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl); + res = curl_easy_perform(curl); + printf("Returned %d, should be %d.\n", res, CURLE_RECV_ERROR); + + /* always cleanup */ + curl_easy_cleanup(curl); + } + curl_global_cleanup(); + return CURLE_OK; +} + +#else +NO_SUPPORT_BUILT_IN +#endif