Skip to content
Merged
4 changes: 2 additions & 2 deletions .github/workflows/verify-build.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -470,7 +470,7 @@ jobs:

- name: Setup gnutls dependency (only on linux)
run: |
sudo apt-get install libgnutls28-dev ;
sudo apt-get install libgnutls28-dev gnutls-bin ;
if: ${{ matrix.os-type == 'ubuntu' }}

- name: Fetch libmicrohttpd from cache
Expand DownExpand Up@@ -668,7 +668,7 @@ jobs:
- name: Generate coverage report
run: |
cd build
gcovr --root .. --exclude test/ --xml coverage.xml --xml-pretty
gcovr --root .. --filter '../src/' --xml coverage.xml --xml-pretty --print-summary
if: ${{ matrix.os-type == 'ubuntu' && matrix.c-compiler == 'gcc' && matrix.debug == 'debug' && matrix.coverage == 'coverage' && success() }}

- name: Upload coverage to Codecov
Expand Down
14 changes: 14 additions & 0 deletions src/httpserver/string_utilities.hpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,6 +42,20 @@ const std::string to_upper_copy(const std::string& str);
const std::string to_lower_copy(const std::string& str);
const std::vector<std::string> string_split(const std::string& s, char sep = ' ', bool collapse = true);

/**
* Validate that a string contains only valid hexadecimal characters (0-9, a-f, A-F)
* @param s The string to validate
* @return true if string contains only valid hex characters, false otherwise
*/
bool is_valid_hex(const std::string& s);

/**
* Convert a hex character to its numeric value (0-15)
* @param c The hex character to convert
* @return numeric value (0-15), or 0 for invalid characters
*/
unsigned char hex_char_to_val(char c);

} // namespace string_utilities

} // namespace httpserver
Expand Down
7 changes: 5 additions & 2 deletions src/httpserver/webserver.hpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,9 +227,12 @@ class webserver {
MHD_Result complete_request(MHD_Connection* connection, struct details::modded_request* mr, const char* version, const char* method);

#ifdef HAVE_GNUTLS
static int psk_cred_handler_func(gnutls_session_t session,
// MHD_PskServerCredentialsCallback signature
static int psk_cred_handler_func(void* cls,
struct MHD_Connection* connection,
const char* username,
gnutls_datum_t* key);
void** psk,
size_t* psk_size);
#endif // HAVE_GNUTLS

friend MHD_Result policy_callback(void *cls, const struct sockaddr* addr, socklen_t addrlen);
Expand Down
17 changes: 17 additions & 0 deletions src/string_utilities.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,5 +55,22 @@ const std::vector<std::string> string_split(const std::string& s, char sep, bool
return result;
}

bool is_valid_hex(const std::string& s) {
for (char c : s) {
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'))) {
return false;
}
}
return true;
}

unsigned char hex_char_to_val(char c) {
if (c >= '0' && c <= '9') return static_cast<unsigned char>(c - '0');
if (c >= 'a' && c <= 'f') return static_cast<unsigned char>(c - 'a' + 10);
if (c >= 'A' && c <= 'F') return static_cast<unsigned char>(c - 'A' + 10);
return 0;
}

} // namespace string_utilities
} // namespace httpserver
49 changes: 35 additions & 14 deletions src/webserver.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,12 +58,17 @@
#include "httpserver/http_resource.hpp"
#include "httpserver/http_response.hpp"
#include "httpserver/http_utils.hpp"
#include "httpserver/string_utilities.hpp"
#include "httpserver/string_response.hpp"

struct MHD_Connection;

#define _REENTRANT 1

#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
#endif // HAVE_GNUTLS

#ifndef SOCK_CLOEXEC
#define SOCK_CLOEXEC 02000000
#endif
Expand DownExpand Up@@ -425,11 +430,22 @@ void webserver::disallow_ip(const string& ip) {
}

#ifdef HAVE_GNUTLS
int webserver::psk_cred_handler_func(gnutls_session_t session,
// MHD_PskServerCredentialsCallback signature:
// The 'cls' parameter is our webserver pointer (passed via MHD_OPTION)
// Returns 0 on success, -1 on error
// The psk output should be allocated with malloc() - MHD will free it
int webserver::psk_cred_handler_func(void* cls,
struct MHD_Connection* connection,
const char* username,
gnutls_datum_t* key) {
webserver* ws = static_cast<webserver*>(
gnutls_session_get_ptr(session));
void** psk,
size_t* psk_size) {
std::ignore = connection; // Not needed - we get context from cls

webserver* ws = static_cast<webserver*>(cls);

// Initialize output to safe values
*psk = nullptr;
*psk_size = 0;

if (ws == nullptr || ws->psk_cred_handler == nullptr) {
return -1;
Expand All@@ -440,23 +456,28 @@ int webserver::psk_cred_handler_func(gnutls_session_t session,
return -1;
}

// Convert hex string to binary
// Validate hex string before allocating memory
size_t psk_len = psk_hex.size() / 2;
key->data = static_cast<unsigned char*>(gnutls_malloc(psk_len));
if (key->data == nullptr) {
if (psk_len == 0 || (psk_hex.size() % 2 != 0) ||
!string_utilities::is_valid_hex(psk_hex)) {
return -1;
}

size_t output_size = psk_len;
int ret = gnutls_hex2bin(psk_hex.c_str(), psk_hex.size(),
key->data, &output_size);
if (ret < 0) {
gnutls_free(key->data);
key->data = nullptr;
// Allocate with malloc - MHD will free this
unsigned char* psk_data = static_cast<unsigned char*>(malloc(psk_len));
if (psk_data == nullptr) {
return -1;
}

key->size = static_cast<unsigned int>(output_size);
// Convert hex string to binary
for (size_t i = 0; i < psk_len; i++) {
psk_data[i] = static_cast<unsigned char>(
(string_utilities::hex_char_to_val(psk_hex[i * 2]) << 4) |
string_utilities::hex_char_to_val(psk_hex[i * 2 + 1]));
}

*psk = psk_data;
*psk_size = psk_len;
return 0;
}
#endif // HAVE_GNUTLS
Expand Down
4 changes: 3 additions & 1 deletion test/Makefile.am
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,7 +26,7 @@ LDADD += -lcurl

AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/
METASOURCES = AUTO
check_PROGRAMS = basic file_upload http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred http_resource
check_PROGRAMS = basic file_upload http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred http_resource http_response create_webserver

MOSTLYCLEANFILES = *.gcda *.gcno *.gcov

Expand All@@ -42,6 +42,8 @@ string_utilities_SOURCES = unit/string_utilities_test.cpp
http_endpoint_SOURCES = unit/http_endpoint_test.cpp
nodelay_SOURCES = integ/nodelay.cpp
http_resource_SOURCES = unit/http_resource_test.cpp
http_response_SOURCES = unit/http_response_test.cpp
create_webserver_SOURCES = unit/create_webserver_test.cpp

noinst_HEADERS = littletest.hpp
AM_CXXFLAGS += -Wall -fPIC -Wno-overloaded-virtual
Expand Down
Loading
Loading