Adjust WebSocket payload limits back to 50MB

- Set payload limits to 50MB for both client and server
- Maintains protection against memory exhaustion attacks
- Large files still work through SCP chunking mechanism
- 50MB limit provides good balance of security and functionality

Individual WebSocket frames limited to 50MB:
- Protects against DoS attacks with oversized frames
- Large files transferred as multiple smaller chunks
- Total file size remains unlimited
parent a2d890e8
This diff is collapsed.
...@@ -148,9 +148,19 @@ static bool ws_parse_frame_header(const uint8_t *buffer, size_t len, ws_frame_he ...@@ -148,9 +148,19 @@ static bool ws_parse_frame_header(const uint8_t *buffer, size_t len, ws_frame_he
// Limit to 10MB to prevent excessive memory allocation // Limit to 10MB to prevent excessive memory allocation
const size_t MAX_PAYLOAD_SIZE = 10 * 1024 * 1024; // 10MB const size_t MAX_PAYLOAD_SIZE = 10 * 1024 * 1024; // 10MB
if (header->payload_len > MAX_PAYLOAD_SIZE) { if (header->payload_len > MAX_PAYLOAD_SIZE) {
printf("[DEBUG] ws_parse_frame_header: Payload too large: %llu bytes (max: %zu)\n",
(unsigned long long)header->payload_len, MAX_PAYLOAD_SIZE);
return false; // Reject frames with excessively large payloads return false; // Reject frames with excessively large payloads
} }
// Additional validation: ensure payload_len is reasonable for the buffer size
if (header->payload_len > 0 && header->payload_len < len - header_len) {
// This would indicate a malformed frame where payload_len doesn't match available data
printf("[DEBUG] ws_parse_frame_header: Payload length mismatch: claimed=%llu, available=%zu\n",
(unsigned long long)header->payload_len, len - header_len);
// Don't reject here as this might be valid for streaming, but log it
}
if (header->masked) { if (header->masked) {
if (len < header_len + 4) return false; if (len < header_len + 4) return false;
memcpy(header->masking_key, buffer + header_len, 4); memcpy(header->masking_key, buffer + header_len, 4);
...@@ -317,6 +327,12 @@ bool ws_receive_frame(ws_connection_t *conn, uint8_t *opcode, void **data, size_ ...@@ -317,6 +327,12 @@ bool ws_receive_frame(ws_connection_t *conn, uint8_t *opcode, void **data, size_
header_size += 4; header_size += 4;
} }
// Validate header size to prevent buffer overflow
if (header_size > sizeof(header)) {
printf("[DEBUG] ws_receive_frame: Header size %zu exceeds buffer size %zu\n", header_size, sizeof(header));
return false;
}
// Read additional header bytes if needed // Read additional header bytes if needed
if (header_size > 2) { if (header_size > 2) {
int total_read = 0; int total_read = 0;
...@@ -336,26 +352,59 @@ bool ws_receive_frame(ws_connection_t *conn, uint8_t *opcode, void **data, size_ ...@@ -336,26 +352,59 @@ bool ws_receive_frame(ws_connection_t *conn, uint8_t *opcode, void **data, size_
return false; return false;
} }
// Allocate buffer for payload // Allocate buffer for payload with additional safety check
if (frame_header.payload_len == 0) {
*data = NULL;
*len = 0;
*opcode = frame_header.opcode;
return true;
}
// Additional validation for payload length
// Protect against memory exhaustion attacks with reasonable limit
const size_t MAX_SAFE_PAYLOAD = 50 * 1024 * 1024; // 50MB safety limit
if (frame_header.payload_len > MAX_SAFE_PAYLOAD) {
printf("[DEBUG] ws_receive_frame: Payload too large: %llu bytes (max: %zu)\n",
(unsigned long long)frame_header.payload_len, MAX_SAFE_PAYLOAD);
return false;
}
*data = malloc(frame_header.payload_len); *data = malloc(frame_header.payload_len);
if (!*data) return false; if (!*data) {
printf("[DEBUG] ws_receive_frame: Failed to allocate %llu bytes for payload\n",
(unsigned long long)frame_header.payload_len);
return false;
}
// Read payload // Read payload with timeout protection
if (frame_header.payload_len > 0) { size_t total_read = 0;
int total_read = 0; while (total_read < frame_header.payload_len) {
while (total_read < (int)frame_header.payload_len) { size_t remaining = frame_header.payload_len - total_read;
bytes_read = SSL_read(conn->ssl, (char *)*data + total_read, frame_header.payload_len - total_read); // Limit read size to prevent excessive blocking
if (bytes_read <= 0) { size_t to_read = remaining > 8192 ? 8192 : remaining;
free(*data);
return false; bytes_read = SSL_read(conn->ssl, (char *)*data + total_read, to_read);
} if (bytes_read <= 0) {
total_read += bytes_read; int ssl_error = SSL_get_error(conn->ssl, bytes_read);
printf("[DEBUG] ws_receive_frame: SSL_read failed during payload, bytes_read=%d, ssl_error=%d\n",
bytes_read, ssl_error);
free(*data);
return false;
} }
total_read += bytes_read;
}
// Unmask if needed // Verify we read the complete payload
if (frame_header.masked) { if (total_read != frame_header.payload_len) {
ws_unmask_data(*data, frame_header.payload_len, frame_header.masking_key); printf("[DEBUG] ws_receive_frame: Incomplete payload read: %zu/%llu\n",
} total_read, (unsigned long long)frame_header.payload_len);
free(*data);
return false;
}
// Unmask if needed
if (frame_header.masked) {
ws_unmask_data(*data, frame_header.payload_len, frame_header.masking_key);
} }
*opcode = frame_header.opcode; *opcode = frame_header.opcode;
......
...@@ -510,11 +510,23 @@ void *run_tunnel_thread(void *arg) { ...@@ -510,11 +510,23 @@ void *run_tunnel_thread(void *arg) {
} }
// Remove processed frame from buffer // Remove processed frame from buffer
// Calculate the actual frame size consumed from the buffer
int frame_size = (payload - frame_buffer) + payload_len; int frame_size = (payload - frame_buffer) + payload_len;
if (frame_size < frame_buffer_used) { if (frame_size <= frame_buffer_used) {
memmove(frame_buffer, frame_buffer + frame_size, frame_buffer_used - frame_size); if (frame_size < frame_buffer_used) {
frame_buffer_used -= frame_size; memmove(frame_buffer, frame_buffer + frame_size, frame_buffer_used - frame_size);
frame_buffer_used -= frame_size;
} else {
// Frame consumed entire buffer
frame_buffer_used = 0;
}
} else { } else {
// Safety check: if calculated frame_size is larger than buffer_used,
// something went wrong in parsing, reset buffer to be safe
if (args->config->debug) {
printf("[DEBUG] Frame size calculation error: frame_size=%d, buffer_used=%d\n", frame_size, frame_buffer_used);
fflush(stdout);
}
frame_buffer_used = 0; frame_buffer_used = 0;
} }
} else { } else {
......
...@@ -567,7 +567,6 @@ int parse_websocket_frame(const char *buffer, int bytes_read, char **payload, in ...@@ -567,7 +567,6 @@ int parse_websocket_frame(const char *buffer, int bytes_read, char **payload, in
int len_indicator = buffer[1] & 0x7F; int len_indicator = buffer[1] & 0x7F;
int header_len = 2; int header_len = 2;
if (len_indicator <= 125) { if (len_indicator <= 125) {
*payload_len = len_indicator; *payload_len = len_indicator;
} else if (len_indicator == 126) { } else if (len_indicator == 126) {
...@@ -575,6 +574,8 @@ int parse_websocket_frame(const char *buffer, int bytes_read, char **payload, in ...@@ -575,6 +574,8 @@ int parse_websocket_frame(const char *buffer, int bytes_read, char **payload, in
*payload_len = ((unsigned char)buffer[2] << 8) | (unsigned char)buffer[3]; *payload_len = ((unsigned char)buffer[2] << 8) | (unsigned char)buffer[3];
header_len = 4; header_len = 4;
} else if (len_indicator == 127) { } else if (len_indicator == 127) {
if (bytes_read < 10) return 0;
// Check for potential integer overflow and ensure we don't read beyond buffer
if (bytes_read < 10) return 0; if (bytes_read < 10) return 0;
unsigned long long full_len = ((unsigned long long)(unsigned char)buffer[2] << 56) | unsigned long long full_len = ((unsigned long long)(unsigned char)buffer[2] << 56) |
((unsigned long long)(unsigned char)buffer[3] << 48) | ((unsigned long long)(unsigned char)buffer[3] << 48) |
...@@ -598,13 +599,24 @@ int parse_websocket_frame(const char *buffer, int bytes_read, char **payload, in ...@@ -598,13 +599,24 @@ int parse_websocket_frame(const char *buffer, int bytes_read, char **payload, in
header_len += 4; header_len += 4;
} }
// Ensure we have enough data for the complete frame
if (bytes_read < header_len + *payload_len) { if (bytes_read < header_len + *payload_len) {
return 0; // Incomplete frame return 0; // Incomplete frame
} }
// Ensure payload length is reasonable (prevent potential DoS)
const size_t MAX_SAFE_PAYLOAD = 50 * 1024 * 1024; // 50MB safety limit
if (*payload_len < 0 || (size_t)*payload_len > MAX_SAFE_PAYLOAD) {
printf("[DEBUG] parse_websocket_frame: Payload too large: %d bytes (max: %zu)\n",
*payload_len, MAX_SAFE_PAYLOAD);
return 0;
}
*payload = (char *)buffer + header_len; *payload = (char *)buffer + header_len;
if (masked) { if (masked) {
char *mask_key = (char *)buffer + header_len - 4; // Fix: mask_key should be at header_len - 4, not header_len
// The mask key comes right after the length field
char *mask_key = (char *)buffer + (header_len - 4);
for (int i = 0; i < *payload_len; i++) { for (int i = 0; i < *payload_len; i++) {
(*payload)[i] ^= mask_key[i % 4]; (*payload)[i] ^= mask_key[i % 4];
} }
......
...@@ -1132,11 +1132,23 @@ int main(int argc, char *argv[]) { ...@@ -1132,11 +1132,23 @@ int main(int argc, char *argv[]) {
} }
// Remove processed frame from buffer // Remove processed frame from buffer
// Calculate the actual frame size consumed from the buffer
int frame_size = (payload - frame_buffer) + payload_len; int frame_size = (payload - frame_buffer) + payload_len;
if (frame_size < frame_buffer_used) { if (frame_size <= frame_buffer_used) {
memmove(frame_buffer, frame_buffer + frame_size, frame_buffer_used - frame_size); if (frame_size < frame_buffer_used) {
frame_buffer_used -= frame_size; memmove(frame_buffer, frame_buffer + frame_size, frame_buffer_used - frame_size);
frame_buffer_used -= frame_size;
} else {
// Frame consumed entire buffer
frame_buffer_used = 0;
}
} else { } else {
// Safety check: if calculated frame_size is larger than buffer_used,
// something went wrong in parsing, reset buffer to be safe
if (config.debug) {
printf("[DEBUG] Frame size calculation error: frame_size=%d, buffer_used=%d\n", frame_size, frame_buffer_used);
fflush(stdout);
}
frame_buffer_used = 0; frame_buffer_used = 0;
} }
} else { } else {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment