Enhance error handling for HTTP/2 file size, validate mmap cache entries, improve WebSocket connection handling, enforce maximum URI length, and limit WebSocket payload size.
This commit is contained in:
13
src/http2.c
13
src/http2.c
@@ -233,6 +233,19 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (st.st_size < 0 || st.st_size > 0x7FFFFFFFFFFFFFFF)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
log_event("HTTP/2: File size out of bounds");
|
||||||
|
|
||||||
|
// Send 500 error
|
||||||
|
nghttp2_nv hdrs[] = {
|
||||||
|
{(uint8_t *)":status", (uint8_t *)"500", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
||||||
|
{(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}};
|
||||||
|
nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Get MIME type
|
// Get MIME type
|
||||||
char *mime_type = get_mime_type(filepath);
|
char *mime_type = get_mime_type(filepath);
|
||||||
if (!mime_type)
|
if (!mime_type)
|
||||||
|
|||||||
@@ -122,6 +122,12 @@ mmap_cache_entry_t *get_cached_file(const char *path)
|
|||||||
{
|
{
|
||||||
if (mmap_cache[i].path && strcmp(mmap_cache[i].path, path) == 0)
|
if (mmap_cache[i].path && strcmp(mmap_cache[i].path, path) == 0)
|
||||||
{
|
{
|
||||||
|
if (mmap_cache[i].mmap_data == NULL || mmap_cache[i].size == 0)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&mmap_cache_mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
mmap_cache[i].last_access = time(NULL);
|
mmap_cache[i].last_access = time(NULL);
|
||||||
mmap_cache[i].ref_count++;
|
mmap_cache[i].ref_count++;
|
||||||
pthread_mutex_unlock(&mmap_cache_mutex);
|
pthread_mutex_unlock(&mmap_cache_mutex);
|
||||||
|
|||||||
50
src/server.c
50
src/server.c
@@ -453,6 +453,11 @@ static void *handle_websocket(void *arg)
|
|||||||
{
|
{
|
||||||
ws_connection_t *conn = (ws_connection_t *)arg;
|
ws_connection_t *conn = (ws_connection_t *)arg;
|
||||||
|
|
||||||
|
if (!conn)
|
||||||
|
{
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
log_event("WebSocket connection established");
|
log_event("WebSocket connection established");
|
||||||
|
|
||||||
uint8_t buffer[65536];
|
uint8_t buffer[65536];
|
||||||
@@ -471,7 +476,9 @@ static void *handle_websocket(void *arg)
|
|||||||
|
|
||||||
if (bytes_received <= 0)
|
if (bytes_received <= 0)
|
||||||
{
|
{
|
||||||
break;
|
ws_close_connection(conn, 1000);
|
||||||
|
free(conn);
|
||||||
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
ws_frame_header_t header;
|
ws_frame_header_t header;
|
||||||
@@ -482,7 +489,9 @@ static void *handle_websocket(void *arg)
|
|||||||
{
|
{
|
||||||
log_event("Failed to parse WebSocket frame");
|
log_event("Failed to parse WebSocket frame");
|
||||||
free(payload);
|
free(payload);
|
||||||
break;
|
ws_close_connection(conn, 1002);
|
||||||
|
free(conn);
|
||||||
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (header.opcode)
|
switch (header.opcode)
|
||||||
@@ -645,10 +654,19 @@ void *handle_http_client(void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char filepath[512];
|
char filepath[512];
|
||||||
snprintf(filepath, sizeof(filepath), "%s%s", config.www_path,
|
int written = snprintf(filepath, sizeof(filepath), "%s%s", config.www_path,
|
||||||
(*sanitized_url == '/' && sanitized_url[1] == '\0') ? "/index.html" : sanitized_url);
|
(*sanitized_url == '/' && sanitized_url[1] == '\0') ? "/index.html" : sanitized_url);
|
||||||
free(sanitized_url);
|
free(sanitized_url);
|
||||||
|
|
||||||
|
if (written < 0 || written >= (int)sizeof(filepath))
|
||||||
|
{
|
||||||
|
log_event("Path too long, potential buffer overflow attempt");
|
||||||
|
const char *error_response = "HTTP/1.1 414 URI Too Long\r\n\r\n";
|
||||||
|
send(client_socket, error_response, strlen(error_response), 0);
|
||||||
|
close(client_socket);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// Get MIME type
|
// Get MIME type
|
||||||
char *mime_type = get_mime_type(filepath);
|
char *mime_type = get_mime_type(filepath);
|
||||||
|
|
||||||
@@ -1530,10 +1548,22 @@ char *sanitize_url(const char *url)
|
|||||||
}
|
}
|
||||||
else if (c == '%')
|
else if (c == '%')
|
||||||
{
|
{
|
||||||
// URL encoding - only allow safe encoded characters
|
// URL encoding - decode and validate the character
|
||||||
if (i + 2 < url_len && isxdigit(url[i + 1]) && isxdigit(url[i + 2]))
|
if (i + 2 < url_len && isxdigit(url[i + 1]) && isxdigit(url[i + 2]))
|
||||||
{
|
{
|
||||||
sanitized[j++] = c;
|
char hex[3] = {url[i + 1], url[i + 2], 0};
|
||||||
|
int decoded = (int)strtol(hex, NULL, 16);
|
||||||
|
|
||||||
|
// Block encoded directory traversal characters and control characters
|
||||||
|
if (decoded == '.' || decoded == '/' || decoded == '\\' ||
|
||||||
|
decoded == 0x00 || decoded < 0x20 || decoded > 0x7E)
|
||||||
|
{
|
||||||
|
free(sanitized);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sanitized[j++] = (char)decoded;
|
||||||
|
i += 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1642,6 +1672,8 @@ void cleanup_thread_pool()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&thread_pool_mutex);
|
||||||
|
|
||||||
for (int i = 0; i < thread_pool_size; i++)
|
for (int i = 0; i < thread_pool_size; i++)
|
||||||
{
|
{
|
||||||
if (thread_pool[i].busy)
|
if (thread_pool[i].busy)
|
||||||
@@ -1650,9 +1682,15 @@ void cleanup_thread_pool()
|
|||||||
pthread_join(thread_pool[i].thread, NULL);
|
pthread_join(thread_pool[i].thread, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(thread_pool);
|
|
||||||
|
ThreadInfo *temp = thread_pool;
|
||||||
thread_pool = NULL;
|
thread_pool = NULL;
|
||||||
thread_pool_size = 0;
|
thread_pool_size = 0;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&thread_pool_mutex);
|
||||||
|
|
||||||
|
// Free after releasing lock and nullifying pointer
|
||||||
|
free(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cache_file(const char *path, const char *data, size_t size, const char *mime_type)
|
void cache_file(const char *path, const char *data, size_t size, const char *mime_type)
|
||||||
|
|||||||
@@ -116,6 +116,9 @@ int ws_handle_handshake_ssl(SSL *ssl, const char *request, char *response, size_
|
|||||||
// Parse WebSocket frame
|
// Parse WebSocket frame
|
||||||
int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, uint8_t **payload)
|
int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, uint8_t **payload)
|
||||||
{
|
{
|
||||||
|
// Maximum allowed WebSocket payload size (10MB)
|
||||||
|
#define MAX_WEBSOCKET_PAYLOAD (10 * 1024 * 1024)
|
||||||
|
|
||||||
if (len < 2)
|
if (len < 2)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
@@ -151,6 +154,11 @@ int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, u
|
|||||||
header->payload_length = payload_len;
|
header->payload_length = payload_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (header->payload_length > MAX_WEBSOCKET_PAYLOAD)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (header->mask)
|
if (header->mask)
|
||||||
{
|
{
|
||||||
if (len < offset + 4)
|
if (len < offset + 4)
|
||||||
|
|||||||
Reference in New Issue
Block a user