From 6ad8586770dd40a7ec8f82fdba187f5afc801dba Mon Sep 17 00:00:00 2001 From: retoor Date: Sun, 28 Dec 2025 05:16:15 +0100 Subject: [PATCH] feat: enhance authentication with constant-time comparisons and memory clearing feat: implement rate limiting for client requests feat: enable SSL hostname verification and set preferred cipher suites fix: deny requests on rate limit allocation failure test: suppress unused result warnings in connection tests docs: update test results and coverage requirements in README --- .gitea/workflows/build.yaml | 0 .gitignore | 0 CHANGELOG.md | 8 +++++ Makefile | 0 README.md | 4 +-- cJSON.c | 0 cJSON.h | 0 src/auth.c | 67 +++++++++++++++++++++++++++++-------- src/auth.h | 0 src/buffer.c | 0 src/buffer.h | 0 src/config.c | 0 src/config.h | 0 src/connection.c | 13 +++++-- src/connection.h | 0 src/dashboard.c | 0 src/dashboard.h | 0 src/health_check.c | 0 src/health_check.h | 0 src/http.c | 0 src/http.h | 0 src/logging.c | 0 src/logging.h | 0 src/main.c | 0 src/monitor.c | 0 src/monitor.h | 0 src/patch.c | 0 src/patch.h | 0 src/rate_limit.c | 3 +- src/rate_limit.h | 0 src/ssl_handler.c | 23 ++++++++++++- src/ssl_handler.h | 1 + src/types.h | 1 + tests/test_auth.c | 0 tests/test_buffer.c | 0 tests/test_config.c | 0 tests/test_connection.c | 26 +++++++------- tests/test_dashboard.c | 0 tests/test_framework.h | 0 tests/test_health_check.c | 0 tests/test_host_rewrite.c | 0 tests/test_http.c | 0 tests/test_http_helpers.c | 0 tests/test_main.c | 0 tests/test_monitor.c | 0 tests/test_patch.c | 0 tests/test_rate_limit.c | 0 tests/test_routing.c | 0 tests/test_ssl_handler.c | 0 49 files changed, 114 insertions(+), 32 deletions(-) mode change 100644 => 100755 .gitea/workflows/build.yaml mode change 100644 => 100755 .gitignore mode change 100644 => 100755 CHANGELOG.md mode change 100644 => 100755 Makefile mode change 100644 => 100755 README.md mode change 100644 => 100755 cJSON.c mode change 100644 => 100755 cJSON.h mode change 100644 => 100755 src/auth.c mode change 100644 => 100755 src/auth.h mode change 100644 => 100755 src/buffer.c mode change 100644 => 100755 src/buffer.h mode change 100644 => 100755 src/config.c mode change 100644 => 100755 src/config.h mode change 100644 => 100755 src/connection.c mode change 100644 => 100755 src/connection.h mode change 100644 => 100755 src/dashboard.c mode change 100644 => 100755 src/dashboard.h mode change 100644 => 100755 src/health_check.c mode change 100644 => 100755 src/health_check.h mode change 100644 => 100755 src/http.c mode change 100644 => 100755 src/http.h mode change 100644 => 100755 src/logging.c mode change 100644 => 100755 src/logging.h mode change 100644 => 100755 src/main.c mode change 100644 => 100755 src/monitor.c mode change 100644 => 100755 src/monitor.h mode change 100644 => 100755 src/patch.c mode change 100644 => 100755 src/patch.h mode change 100644 => 100755 src/rate_limit.c mode change 100644 => 100755 src/rate_limit.h mode change 100644 => 100755 src/ssl_handler.c mode change 100644 => 100755 src/ssl_handler.h mode change 100644 => 100755 src/types.h mode change 100644 => 100755 tests/test_auth.c mode change 100644 => 100755 tests/test_buffer.c mode change 100644 => 100755 tests/test_config.c mode change 100644 => 100755 tests/test_connection.c mode change 100644 => 100755 tests/test_dashboard.c mode change 100644 => 100755 tests/test_framework.h mode change 100644 => 100755 tests/test_health_check.c mode change 100644 => 100755 tests/test_host_rewrite.c mode change 100644 => 100755 tests/test_http.c mode change 100644 => 100755 tests/test_http_helpers.c mode change 100644 => 100755 tests/test_main.c mode change 100644 => 100755 tests/test_monitor.c mode change 100644 => 100755 tests/test_patch.c mode change 100644 => 100755 tests/test_rate_limit.c mode change 100644 => 100755 tests/test_routing.c mode change 100644 => 100755 tests/test_ssl_handler.c diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/CHANGELOG.md b/CHANGELOG.md old mode 100644 new mode 100755 index 646a6c4..1feb88c --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ + +## Version 0.5.0 - 2025-12-28 + +Enhances authentication security by preventing timing attacks and clearing sensitive memory, while adding rate limiting to protect against abusive client requests. Enables SSL hostname verification and preferred cipher suites for improved connection security, and fixes request denial when rate limit allocation fails. + +**Changes:** 49 files, 138 lines +**Languages:** C (134 lines), Markdown (4 lines) + ## Version 0.4.0 - 2025-12-15 Add comprehensive tests for the auth, buffer, and connection modules. Enhance the build process with logging test support and an increased minimum coverage threshold. diff --git a/Makefile b/Makefile old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 7eaabd0..237d5f3 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Compiles the source files in `src/` and produces the `rproxy` executable. ```bash make test # Run unit tests -make coverage # Run tests with coverage report (minimum 60% required) +make coverage # Run tests with coverage report (minimum 69% required) make coverage-html # Generate HTML coverage report make valgrind # Run tests with memory leak detection ``` @@ -52,7 +52,7 @@ make valgrind # Run tests with memory leak detection ### Test Results ``` -Test Results: 559/559 passed +Test Results: 741/741 passed HEAP SUMMARY: in use at exit: 0 bytes in 0 blocks diff --git a/cJSON.c b/cJSON.c old mode 100644 new mode 100755 diff --git a/cJSON.h b/cJSON.h old mode 100644 new mode 100755 diff --git a/src/auth.c b/src/auth.c old mode 100644 new mode 100755 index b4cd5ed..ede29f2 --- a/src/auth.c +++ b/src/auth.c @@ -4,11 +4,16 @@ #include #include #include +#include static char g_dashboard_username[128] = ""; static char g_dashboard_password_hash[256] = ""; static int g_auth_enabled = 0; +static int constant_time_compare(const char *a, const char *b, size_t len) { + return CRYPTO_memcmp(a, b, len) == 0; +} + static void compute_sha256(const char *input, char *output, size_t output_size) { EVP_MD_CTX *ctx = EVP_MD_CTX_new(); if (!ctx) return; @@ -48,12 +53,19 @@ int auth_check_credentials(const char *username, const char *password) { if (!g_auth_enabled) return 1; if (!username || !password) return 0; - if (strcmp(username, g_dashboard_username) != 0) return 0; - char password_hash[256]; compute_sha256(password, password_hash, sizeof(password_hash)); - return strcmp(password_hash, g_dashboard_password_hash) == 0; + size_t username_len = strlen(username); + size_t expected_username_len = strlen(g_dashboard_username); + int username_match = (username_len == expected_username_len) && + constant_time_compare(username, g_dashboard_username, username_len); + + int hash_match = constant_time_compare(password_hash, g_dashboard_password_hash, 64); + + memset(password_hash, 0, sizeof(password_hash)); + + return username_match && hash_match; } static int base64_decode_char(char c) { @@ -108,8 +120,18 @@ int auth_check_basic_auth(const char *auth_header, char *error_msg, size_t error return 0; } + const char *encoded = auth_header + 6; + size_t encoded_len = strlen(encoded); + if (encoded_len > 680) { + if (error_msg && error_size > 0) { + strncpy(error_msg, "Credentials too long", error_size - 1); + } + return 0; + } + char decoded[512]; - if (base64_decode(auth_header + 6, decoded, sizeof(decoded)) < 0) { + int decoded_len = base64_decode(encoded, decoded, sizeof(decoded)); + if (decoded_len < 0) { if (error_msg && error_size > 0) { strncpy(error_msg, "Invalid credentials format", error_size - 1); } @@ -128,7 +150,10 @@ int auth_check_basic_auth(const char *auth_header, char *error_msg, size_t error const char *username = decoded; const char *password = colon + 1; - if (!auth_check_credentials(username, password)) { + int result = auth_check_credentials(username, password); + memset(decoded, 0, sizeof(decoded)); + + if (!result) { if (error_msg && error_size > 0) { strncpy(error_msg, "Invalid username or password", error_size - 1); } @@ -155,8 +180,18 @@ int auth_check_route_basic_auth(const route_config_t *route, const char *auth_he return 0; } + const char *encoded = auth_header + 6; + size_t encoded_len = strlen(encoded); + if (encoded_len > 680) { + if (error_msg && error_size > 0) { + strncpy(error_msg, "Credentials too long", error_size - 1); + } + return 0; + } + char decoded[512]; - if (base64_decode(auth_header + 6, decoded, sizeof(decoded)) < 0) { + int decoded_len = base64_decode(encoded, decoded, sizeof(decoded)); + if (decoded_len < 0) { if (error_msg && error_size > 0) { strncpy(error_msg, "Invalid credentials format", error_size - 1); } @@ -165,6 +200,7 @@ int auth_check_route_basic_auth(const route_config_t *route, const char *auth_he char *colon = strchr(decoded, ':'); if (!colon) { + memset(decoded, 0, sizeof(decoded)); if (error_msg && error_size > 0) { strncpy(error_msg, "Invalid credentials format", error_size - 1); } @@ -175,17 +211,20 @@ int auth_check_route_basic_auth(const route_config_t *route, const char *auth_he const char *username = decoded; const char *password = colon + 1; - if (strcmp(username, route->username) != 0) { - if (error_msg && error_size > 0) { - strncpy(error_msg, "Invalid username or password", error_size - 1); - } - return 0; - } - char password_hash[256]; compute_sha256(password, password_hash, sizeof(password_hash)); - if (strcmp(password_hash, route->password_hash) != 0) { + size_t username_len = strlen(username); + size_t expected_username_len = strlen(route->username); + int username_match = (username_len == expected_username_len) && + constant_time_compare(username, route->username, username_len); + + int hash_match = constant_time_compare(password_hash, route->password_hash, 64); + + memset(decoded, 0, sizeof(decoded)); + memset(password_hash, 0, sizeof(password_hash)); + + if (!username_match || !hash_match) { if (error_msg && error_size > 0) { strncpy(error_msg, "Invalid username or password", error_size - 1); } diff --git a/src/auth.h b/src/auth.h old mode 100644 new mode 100755 diff --git a/src/buffer.c b/src/buffer.c old mode 100644 new mode 100755 diff --git a/src/buffer.h b/src/buffer.h old mode 100644 new mode 100755 diff --git a/src/config.c b/src/config.c old mode 100644 new mode 100755 diff --git a/src/config.h b/src/config.h old mode 100644 new mode 100755 diff --git a/src/connection.c b/src/connection.c old mode 100644 new mode 100755 index 8a920f6..167903c --- a/src/connection.c +++ b/src/connection.c @@ -8,6 +8,7 @@ #include "dashboard.h" #include "auth.h" #include "patch.h" +#include "rate_limit.h" #include #include @@ -204,6 +205,8 @@ void connection_accept(int listener_fd) { conn->state = CLIENT_STATE_READING_HEADERS; conn->fd = client_fd; conn->last_activity = cached_time; + strncpy(conn->client_ip, inet_ntoa(client_addr.sin_addr), sizeof(conn->client_ip) - 1); + conn->client_ip[sizeof(conn->client_ip) - 1] = '\0'; if (buffer_init(&conn->read_buf, CHUNK_SIZE) < 0 || buffer_init(&conn->write_buf, CHUNK_SIZE) < 0) { @@ -213,7 +216,7 @@ void connection_accept(int listener_fd) { monitor.active_connections++; log_debug("New connection on fd %d from %s, total: %d", - client_fd, inet_ntoa(client_addr.sin_addr), monitor.active_connections); + client_fd, conn->client_ip, monitor.active_connections); } } @@ -656,7 +659,7 @@ void connection_connect_to_upstream(connection_t *client, const char *data, size } const char *sni_hostname = route->rewrite_host ? route->upstream_host : client->request.host; - SSL_set_tlsext_host_name(up->ssl, sni_hostname); + ssl_set_hostname(up->ssl, sni_hostname); SSL_set_fd(up->ssl, up_fd); SSL_set_connect_state(up->ssl); @@ -739,6 +742,12 @@ static void handle_client_read(connection_t *conn) { return; } + if (!rate_limit_check(conn->client_ip)) { + log_info("[RATE-LIMIT] Request blocked for %s from %s", conn->request.host, conn->client_ip); + connection_send_error_response(conn, 429, "Too Many Requests", "Rate limit exceeded. Please try again later."); + return; + } + long long body_len = (conn->request.content_length > 0) ? conn->request.content_length : 0; size_t total_request_len = headers_len + body_len; diff --git a/src/connection.h b/src/connection.h old mode 100644 new mode 100755 diff --git a/src/dashboard.c b/src/dashboard.c old mode 100644 new mode 100755 diff --git a/src/dashboard.h b/src/dashboard.h old mode 100644 new mode 100755 diff --git a/src/health_check.c b/src/health_check.c old mode 100644 new mode 100755 diff --git a/src/health_check.h b/src/health_check.h old mode 100644 new mode 100755 diff --git a/src/http.c b/src/http.c old mode 100644 new mode 100755 diff --git a/src/http.h b/src/http.h old mode 100644 new mode 100755 diff --git a/src/logging.c b/src/logging.c old mode 100644 new mode 100755 diff --git a/src/logging.h b/src/logging.h old mode 100644 new mode 100755 diff --git a/src/main.c b/src/main.c old mode 100644 new mode 100755 diff --git a/src/monitor.c b/src/monitor.c old mode 100644 new mode 100755 diff --git a/src/monitor.h b/src/monitor.h old mode 100644 new mode 100755 diff --git a/src/patch.c b/src/patch.c old mode 100644 new mode 100755 diff --git a/src/patch.h b/src/patch.h old mode 100644 new mode 100755 diff --git a/src/rate_limit.c b/src/rate_limit.c old mode 100644 new mode 100755 index e9410fa..5c5caee --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -74,7 +74,8 @@ int rate_limit_check(const char *client_ip) { rate_limit_entry_t *new_entry = calloc(1, sizeof(rate_limit_entry_t)); if (!new_entry) { - return 1; + log_error("Rate limit entry allocation failed, denying request for safety"); + return 0; } strncpy(new_entry->client_ip, client_ip, sizeof(new_entry->client_ip) - 1); diff --git a/src/rate_limit.h b/src/rate_limit.h old mode 100644 new mode 100755 diff --git a/src/ssl_handler.c b/src/ssl_handler.c old mode 100644 new mode 100755 index e39a8d5..f688d91 --- a/src/ssl_handler.c +++ b/src/ssl_handler.c @@ -2,11 +2,12 @@ #include "logging.h" #include #include +#include #include #include SSL_CTX *ssl_ctx = NULL; -static int g_ssl_verify_enabled = 0; +static int g_ssl_verify_enabled = 1; static char g_ca_file[512] = ""; static char g_ca_path[512] = ""; @@ -63,6 +64,26 @@ void ssl_init(void) { SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + if (SSL_CTX_set_cipher_list(ssl_ctx, + "ECDHE+AESGCM:DHE+AESGCM:ECDHE+CHACHA20:DHE+CHACHA20:!aNULL:!MD5:!DSS") != 1) { + log_info("Warning: Could not set preferred cipher list, using defaults"); + } +} + +int ssl_set_hostname(SSL *ssl, const char *hostname) { + if (!ssl || !hostname || hostname[0] == '\0') return 0; + + SSL_set_tlsext_host_name(ssl, hostname); + + if (g_ssl_verify_enabled) { + SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + if (SSL_set1_host(ssl, hostname) != 1) { + log_debug("Failed to set hostname verification for %s", hostname); + return -1; + } + } + return 0; } void ssl_cleanup(void) { diff --git a/src/ssl_handler.h b/src/ssl_handler.h old mode 100644 new mode 100755 index 2a4b823..2e382d5 --- a/src/ssl_handler.h +++ b/src/ssl_handler.h @@ -10,6 +10,7 @@ void ssl_set_ca_file(const char *path); void ssl_set_ca_path(const char *path); void ssl_init(void); void ssl_cleanup(void); +int ssl_set_hostname(SSL *ssl, const char *hostname); int ssl_do_handshake(connection_t *conn); int ssl_read(connection_t *conn, char *buf, size_t len); int ssl_write(connection_t *conn, const char *buf, size_t len); diff --git a/src/types.h b/src/types.h old mode 100644 new mode 100755 index e74ed29..2b3a225 --- a/src/types.h +++ b/src/types.h @@ -80,6 +80,7 @@ typedef struct connection_s { conn_type_t type; client_state_t state; int fd; + char client_ip[64]; struct connection_s *pair; struct vhost_stats_s *vhost_stats; buffer_t read_buf; diff --git a/tests/test_auth.c b/tests/test_auth.c old mode 100644 new mode 100755 diff --git a/tests/test_buffer.c b/tests/test_buffer.c old mode 100644 new mode 100755 diff --git a/tests/test_config.c b/tests/test_config.c old mode 100644 new mode 100755 diff --git a/tests/test_connection.c b/tests/test_connection.c old mode 100644 new mode 100755 index d593877..da4d257 --- a/tests/test_connection.c +++ b/tests/test_connection.c @@ -15,6 +15,8 @@ #include #include +#define IGNORE_RESULT(x) do { if (x) {} } while(0) + extern connection_t connections[MAX_FDS]; extern int epoll_fd; @@ -938,7 +940,7 @@ void test_connection_buffer_compaction(void) { conn.read_buf.tail = 2500; const char *test_data = "Test data for compaction"; - write(sockfd[1], test_data, strlen(test_data)); + IGNORE_RESULT(write(sockfd[1], test_data, strlen(test_data))); int bytes = connection_do_read(&conn); TEST_ASSERT(bytes > 0 || (bytes < 0 && conn.read_buf.head < 2000), "Buffer compacted or data read"); @@ -1266,7 +1268,7 @@ void test_connection_do_read_grow_buffer(void) { char large_data[128]; memset(large_data, 'A', sizeof(large_data)); - write(sockfd[1], large_data, sizeof(large_data)); + IGNORE_RESULT(write(sockfd[1], large_data, sizeof(large_data))); int bytes = connection_do_read(&conn); TEST_ASSERT(bytes > 0 || conn.read_buf.capacity > 64, "Buffer grew or data read"); @@ -1297,7 +1299,7 @@ void test_connection_close_with_splice_pipes(void) { buffer_init(&conn->write_buf, 4096); int pipefd[2]; - pipe(pipefd); + IGNORE_RESULT(pipe(pipefd)); conn->splice_pipe[0] = pipefd[0]; conn->splice_pipe[1] = pipefd[1]; conn->can_splice = 1; @@ -1439,7 +1441,7 @@ void test_connection_handle_event_client_read(void) { conn->epoll_events = EPOLLIN; const char *partial_request = "GET / HTTP/1.1\r\n"; - write(sockfd[1], partial_request, strlen(partial_request)); + IGNORE_RESULT(write(sockfd[1], partial_request, strlen(partial_request))); struct epoll_event event = { .events = EPOLLIN, .data.fd = sockfd[0] }; connection_handle_event(&event); @@ -1485,7 +1487,7 @@ void test_connection_handle_complete_request(void) { conn->epoll_events = EPOLLIN; const char *request = "GET / HTTP/1.1\r\nHost: test.example.com\r\n\r\n"; - write(sockfd[1], request, strlen(request)); + IGNORE_RESULT(write(sockfd[1], request, strlen(request))); struct epoll_event event = { .events = EPOLLIN, .data.fd = sockfd[0] }; connection_handle_event(&event); @@ -1531,7 +1533,7 @@ void test_connection_handle_dashboard_request(void) { conn->epoll_events = EPOLLIN; const char *request = "GET /rproxy/dashboard HTTP/1.1\r\nHost: localhost\r\n\r\n"; - write(sockfd[1], request, strlen(request)); + IGNORE_RESULT(write(sockfd[1], request, strlen(request))); struct epoll_event event = { .events = EPOLLIN, .data.fd = sockfd[0] }; connection_handle_event(&event); @@ -1577,7 +1579,7 @@ void test_connection_handle_stats_api(void) { conn->epoll_events = EPOLLIN; const char *request = "GET /rproxy/api/stats HTTP/1.1\r\nHost: localhost\r\n\r\n"; - write(sockfd[1], request, strlen(request)); + IGNORE_RESULT(write(sockfd[1], request, strlen(request))); struct epoll_event event = { .events = EPOLLIN, .data.fd = sockfd[0] }; connection_handle_event(&event); @@ -1629,7 +1631,7 @@ void test_connection_handle_oversized_header(void) { huge_header[2] = 'T'; huge_header[3] = ' '; huge_header[sizeof(huge_header)-1] = '\0'; - write(sockfd[1], huge_header, sizeof(huge_header) - 1); + IGNORE_RESULT(write(sockfd[1], huge_header, sizeof(huge_header) - 1)); struct epoll_event event = { .events = EPOLLIN, .data.fd = sockfd[0] }; connection_handle_event(&event); @@ -1675,7 +1677,7 @@ void test_connection_handle_empty_host(void) { conn->epoll_events = EPOLLIN; const char *request = "GET / HTTP/1.1\r\n\r\n"; - write(sockfd[1], request, strlen(request)); + IGNORE_RESULT(write(sockfd[1], request, strlen(request))); struct epoll_event event = { .events = EPOLLIN, .data.fd = sockfd[0] }; connection_handle_event(&event); @@ -1721,7 +1723,7 @@ void test_connection_handle_malformed_request(void) { conn->epoll_events = EPOLLIN; const char *request = "INVALID REQUEST\r\n\r\n"; - write(sockfd[1], request, strlen(request)); + IGNORE_RESULT(write(sockfd[1], request, strlen(request))); struct epoll_event event = { .events = EPOLLIN, .data.fd = sockfd[0] }; connection_handle_event(&event); @@ -1839,7 +1841,7 @@ void test_connection_handle_forwarding_state(void) { upstream->epoll_events = EPOLLIN; const char *response = "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK"; - write(sockfd2[1], response, strlen(response)); + IGNORE_RESULT(write(sockfd2[1], response, strlen(response))); struct epoll_event event = { .events = EPOLLIN, .data.fd = sockfd2[0] }; connection_handle_event(&event); @@ -1976,7 +1978,7 @@ void test_connection_handle_client_forwarding_read(void) { upstream->epoll_events = EPOLLIN; const char *data = "More client data\r\n"; - write(sockfd1[1], data, strlen(data)); + IGNORE_RESULT(write(sockfd1[1], data, strlen(data))); struct epoll_event event = { .events = EPOLLIN, .data.fd = sockfd1[0] }; connection_handle_event(&event); diff --git a/tests/test_dashboard.c b/tests/test_dashboard.c old mode 100644 new mode 100755 diff --git a/tests/test_framework.h b/tests/test_framework.h old mode 100644 new mode 100755 diff --git a/tests/test_health_check.c b/tests/test_health_check.c old mode 100644 new mode 100755 diff --git a/tests/test_host_rewrite.c b/tests/test_host_rewrite.c old mode 100644 new mode 100755 diff --git a/tests/test_http.c b/tests/test_http.c old mode 100644 new mode 100755 diff --git a/tests/test_http_helpers.c b/tests/test_http_helpers.c old mode 100644 new mode 100755 diff --git a/tests/test_main.c b/tests/test_main.c old mode 100644 new mode 100755 diff --git a/tests/test_monitor.c b/tests/test_monitor.c old mode 100644 new mode 100755 diff --git a/tests/test_patch.c b/tests/test_patch.c old mode 100644 new mode 100755 diff --git a/tests/test_rate_limit.c b/tests/test_rate_limit.c old mode 100644 new mode 100755 diff --git a/tests/test_routing.c b/tests/test_routing.c old mode 100644 new mode 100755 diff --git a/tests/test_ssl_handler.c b/tests/test_ssl_handler.c old mode 100644 new mode 100755