Added 10MiB recv limit for WS frames, also a small nit

- Added WS_MAX_RECV_FRAME_SZ, if a received frame exceeds this limit (10
  MiB), error out. This is a DoS prevention measure.
- Also redid the unmasking code in get_ws_frame(), to avoid assumptions
  about the size of an unsigned int and some casting, and avoid
  memcpy(), just use the mask bytes from the buffer that are already there
  to unmask.
This commit is contained in:
Calin Culianu 2023-10-16 15:25:12 +03:00
parent 7fbbe86ff3
commit b828c4ab41
No known key found for this signature in database
GPG Key ID: 21810A542031C02C

24
ws.c
View File

@ -38,6 +38,11 @@
#endif
#define WS_UUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
/*
* We limit the size of received WS frames to 10 MiB,
* as a DoS prevention measure.
*/
static const size_t WS_MAX_RECV_FRAME_SZ = 10485760;
struct evws_connection {
TAILQ_ENTRY(evws_connection) next;
@ -205,7 +210,6 @@ get_ws_frame(unsigned char *in_buffer, size_t buf_len,
size_t payload_len;
size_t pos;
int length_field;
unsigned int mask;
if (buf_len < 2) {
return INCOMPLETE_DATA;
@ -237,13 +241,15 @@ get_ws_frame(unsigned char *in_buffer, size_t buf_len,
for (i = 56; i >= 0; i -= 8) {
tmp64 |= (uint64_t)in_buffer[pos++] << i;
}
if (tmp64 > (uint64_t)SIZE_MAX) {
/* Implementation limitation, we support up to size_t length.
* This branch can only be taken on <64-bit platforms.
if (tmp64 > WS_MAX_RECV_FRAME_SZ) {
/* Implementation limitation, we support up to 10 MiB
* length, as a DoS prevention measure.
*/
event_warn("%s: frame length %" PRIu64 " exceeds %" PRIu64 "\n",
__func__, tmp64, (uint64_t)SIZE_MAX);
/* calling code needs these values, do the best we can here */
__func__, tmp64, (uint64_t)WS_MAX_RECV_FRAME_SZ);
/* Calling code needs these values; do the best we can here.
* Caller will close the connection anyway.
*/
*payload_ptr = in_buffer + pos;
*out_len = 0;
return ERROR_FRAME;
@ -258,16 +264,16 @@ get_ws_frame(unsigned char *in_buffer, size_t buf_len,
* but we support it for nonconformant clients
*/
if (masked) {
unsigned char *c;
unsigned char *c, *mask;
size_t i;
memcpy(&mask, in_buffer + pos, sizeof(mask));
mask = in_buffer + pos; /* first 4 bytes are mask bytes */
pos += 4;
/* unmask data */
c = in_buffer + pos;
for (i = 0; i < payload_len; i++) {
c[i] = c[i] ^ ((unsigned char *)(&mask))[i % 4u];
c[i] = c[i] ^ mask[i % 4u];
}
}