Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/verify-build.yml
Original file line number Diff line number Diff 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 Down Expand 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 Down Expand 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 number Diff line number Diff 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