feat: add constant-time credential comparison, rate limiting, SSL hostname verification, and update coverage threshold to 69%
This commit is contained in:
Regular → Executable
+53
-14
@@ -4,11 +4,16 @@
|
||||
#include <string.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+11
-2
@@ -8,6 +8,7 @@
|
||||
#include "dashboard.h"
|
||||
#include "auth.h"
|
||||
#include "patch.h"
|
||||
#include "rate_limit.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -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;
|
||||
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+2
-1
@@ -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);
|
||||
|
||||
Regular → Executable
Regular → Executable
+22
-1
@@ -2,11 +2,12 @@
|
||||
#include "logging.h"
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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) {
|
||||
|
||||
Regular → Executable
+1
@@ -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);
|
||||
|
||||
Regular → Executable
+1
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user