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
8 changes: 7 additions & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
Version 0.19.0 - 2023-06-15
Version 0.20.0

Added conditional compilation for basic auth (HAVE_BAUTH), mirroring
existing HAVE_DAUTH pattern for digest auth. Basic auth support
is auto-detected via AC_CHECK_LIB and can be disabled at build time.
Fixed path traversal vulnerability in file uploads when
generate_random_filename_on_upload is disabled.
Fixed TOCTOU race in file_response by replacing stat-then-open with
Expand All @@ -12,6 +15,9 @@ Version 0.19.0 - 2023-06-15
Fixed auth skip path bypass via path traversal (e.g. /public/../protected).
Fixed use of free() instead of MHD_free() for digest auth username.
Fixed unchecked write error during file upload.

Version 0.19.0 - 2023-06-15

Considering family_url as part of the priority when selecting a URL to match.
More explicit selection of C++ version.
Ability to handle multiple parameters with the same name on the URL.
Expand Down
15 changes: 14 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

AC_PREREQ(2.57)
m4_define([libhttpserver_MAJOR_VERSION],[0])dnl
m4_define([libhttpserver_MINOR_VERSION],[19])dnl
m4_define([libhttpserver_MINOR_VERSION],[20])dnl
m4_define([libhttpserver_REVISION],[0])dnl
m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl
m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl
Expand Down Expand Up @@ -149,6 +149,11 @@ fi
AM_CONDITIONAL([COND_CROSS_COMPILE],[test x"$cond_cross_compile" = x"yes"])
AC_SUBST(COND_CROSS_COMPILE)

# Check for basic auth support in libmicrohttpd
AC_CHECK_LIB([microhttpd], [MHD_queue_basic_auth_fail_response],
[have_bauth="yes"],
[have_bauth="no"; AC_MSG_WARN("libmicrohttpd basic auth support not found. Basic auth will be disabled")])

# Check for digest auth support in libmicrohttpd
AC_CHECK_LIB([microhttpd], [MHD_queue_auth_fail_response],
[have_dauth="yes"],
Expand Down Expand Up @@ -264,6 +269,13 @@ fi

AM_CONDITIONAL([HAVE_GNUTLS],[test x"$have_gnutls" = x"yes"])

if test x"$have_bauth" = x"yes"; then
AM_CXXFLAGS="$AM_CXXFLAGS -DHAVE_BAUTH"
AM_CFLAGS="$AM_CXXFLAGS -DHAVE_BAUTH"
fi

AM_CONDITIONAL([HAVE_BAUTH],[test x"$have_bauth" = x"yes"])

if test x"$have_dauth" = x"yes"; then
AM_CXXFLAGS="$AM_CXXFLAGS -DHAVE_DAUTH"
AM_CFLAGS="$AM_CXXFLAGS -DHAVE_DAUTH"
Expand Down Expand Up @@ -327,6 +339,7 @@ AC_MSG_NOTICE([Configuration Summary:
License : LGPL only
Debug : ${debugit}
TLS Enabled : ${have_gnutls}
Basic Auth : ${have_bauth}
Digest Auth : ${have_dauth}
TCP_FASTOPEN : ${is_fastopen_supported}
Static : ${static}
Expand Down
9 changes: 7 additions & 2 deletions examples/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
LDADD = $(top_builddir)/src/libhttpserver.la
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/
METASOURCES = AUTO
noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg args_processing setting_headers custom_access_log basic_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator file_upload file_upload_with_callback
noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg args_processing setting_headers custom_access_log minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator file_upload file_upload_with_callback

hello_world_SOURCES = hello_world.cpp
service_SOURCES = service.cpp
Expand All @@ -31,7 +31,6 @@ hello_with_get_arg_SOURCES = hello_with_get_arg.cpp
args_processing_SOURCES = args_processing.cpp
setting_headers_SOURCES = setting_headers.cpp
custom_access_log_SOURCES = custom_access_log.cpp
basic_authentication_SOURCES = basic_authentication.cpp
minimal_https_SOURCES = minimal_https.cpp
minimal_file_response_SOURCES = minimal_file_response.cpp
minimal_deferred_SOURCES = minimal_deferred.cpp
Expand All @@ -44,6 +43,12 @@ benchmark_nodelay_SOURCES = benchmark_nodelay.cpp
file_upload_SOURCES = file_upload.cpp
file_upload_with_callback_SOURCES = file_upload_with_callback.cpp

if HAVE_BAUTH
noinst_PROGRAMS += basic_authentication centralized_authentication
basic_authentication_SOURCES = basic_authentication.cpp
centralized_authentication_SOURCES = centralized_authentication.cpp
endif

if HAVE_GNUTLS
LDADD += -lgnutls
noinst_PROGRAMS += minimal_https_psk
Expand Down
9 changes: 7 additions & 2 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@
AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/
METASOURCES = AUTO
lib_LTLIBRARIES = libhttpserver.la
libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp file_info.cpp http_request.cpp http_response.cpp string_response.cpp basic_auth_fail_response.cpp digest_auth_fail_response.cpp deferred_response.cpp file_response.cpp http_resource.cpp create_webserver.cpp details/http_endpoint.cpp
libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp file_info.cpp http_request.cpp http_response.cpp string_response.cpp digest_auth_fail_response.cpp deferred_response.cpp file_response.cpp http_resource.cpp create_webserver.cpp details/http_endpoint.cpp
noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp gettext.h
nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/file_info.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/string_response.hpp httpserver/basic_auth_fail_response.hpp httpserver/digest_auth_fail_response.hpp httpserver/deferred_response.hpp httpserver/file_response.hpp httpserver/http_arg_value.hpp
nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/file_info.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/string_response.hpp httpserver/digest_auth_fail_response.hpp httpserver/deferred_response.hpp httpserver/file_response.hpp httpserver/http_arg_value.hpp

if HAVE_BAUTH
libhttpserver_la_SOURCES += basic_auth_fail_response.cpp
nobase_include_HEADERS += httpserver/basic_auth_fail_response.hpp
endif

AM_CXXFLAGS += -fPIC -Wall

Expand Down
4 changes: 4 additions & 0 deletions src/basic_auth_fail_response.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
USA
*/

#ifdef HAVE_BAUTH

#include "httpserver/basic_auth_fail_response.hpp"
#include <microhttpd.h>
#include <iosfwd>
Expand All @@ -32,3 +34,5 @@ int basic_auth_fail_response::enqueue_response(MHD_Connection* connection, MHD_R
}

} // namespace httpserver

#endif // HAVE_BAUTH
2 changes: 2 additions & 0 deletions src/create_test_request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ http_request create_test_request::build() {
req.cache->querystring = std::move(_querystring);
}

#ifdef HAVE_BAUTH
req.cache->username = std::move(_user);
req.cache->password = std::move(_pass);
#endif // HAVE_BAUTH

#ifdef HAVE_DAUTH
req.cache->digested_user = std::move(_digested_user);
Expand Down
9 changes: 7 additions & 2 deletions src/http_request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ MHD_Result http_request::build_request_querystring(void *cls, enum MHD_ValueKind
return MHD_YES;
}

#ifdef HAVE_BAUTH
void http_request::fetch_user_pass() const {
char* password = nullptr;
auto* username = MHD_basic_auth_get_username_password(underlying_connection, &password);
Expand Down Expand Up @@ -339,6 +340,7 @@ std::string_view http_request::get_pass() const {
fetch_user_pass();
return cache->password;
}
#endif // HAVE_BAUTH

#ifdef HAVE_DAUTH
std::string_view http_request::get_digested_user() const {
Expand Down Expand Up @@ -557,8 +559,11 @@ uint16_t http_request::get_requestor_port() const {
}

std::ostream &operator<< (std::ostream &os, const http_request &r) {
os << r.get_method() << " Request [user:\"" << r.get_user() << "\" pass:\"" << r.get_pass() << "\"] path:\""
<< r.get_path() << "\"" << std::endl;
os << r.get_method() << " Request [";
#ifdef HAVE_BAUTH
os << "user:\"" << r.get_user() << "\" pass:\"" << r.get_pass() << "\"";
#endif // HAVE_BAUTH
os << "] path:\"" << r.get_path() << "\"" << std::endl;

http::dump_header_map(os, "Headers", r.get_headers());
http::dump_header_map(os, "Footers", r.get_footers());
Expand Down
2 changes: 2 additions & 0 deletions src/httpserver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@

#define _HTTPSERVER_HPP_INSIDE_

#ifdef HAVE_BAUTH
#include "httpserver/basic_auth_fail_response.hpp"
#endif // HAVE_BAUTH
#include "httpserver/deferred_response.hpp"
#ifdef HAVE_DAUTH
#include "httpserver/digest_auth_fail_response.hpp"
Expand Down
5 changes: 5 additions & 0 deletions src/httpserver/basic_auth_fail_response.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#ifndef SRC_HTTPSERVER_BASIC_AUTH_FAIL_RESPONSE_HPP_
#define SRC_HTTPSERVER_BASIC_AUTH_FAIL_RESPONSE_HPP_

#ifdef HAVE_BAUTH

#include <string>
#include "httpserver/http_utils.hpp"
#include "httpserver/string_response.hpp"
Expand Down Expand Up @@ -60,4 +62,7 @@ class basic_auth_fail_response : public string_response {
};

} // namespace httpserver

#endif // HAVE_BAUTH

#endif // SRC_HTTPSERVER_BASIC_AUTH_FAIL_RESPONSE_HPP_
4 changes: 4 additions & 0 deletions src/httpserver/create_test_request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class create_test_request {
return *this;
}

#ifdef HAVE_BAUTH
create_test_request& user(const std::string& user) {
_user = user;
return *this;
Expand All @@ -92,6 +93,7 @@ class create_test_request {
_pass = pass;
return *this;
}
#endif // HAVE_BAUTH

#ifdef HAVE_DAUTH
create_test_request& digested_user(const std::string& digested_user) {
Expand Down Expand Up @@ -129,8 +131,10 @@ class create_test_request {
http::header_map _cookies;
std::map<std::string, std::vector<std::string>, http::arg_comparator> _args;
std::string _querystring;
#ifdef HAVE_BAUTH
std::string _user;
std::string _pass;
#endif // HAVE_BAUTH
#ifdef HAVE_DAUTH
std::string _digested_user;
#endif // HAVE_DAUTH
Expand Down
4 changes: 4 additions & 0 deletions src/httpserver/create_webserver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ class create_webserver {
return *this;
}

#ifdef HAVE_BAUTH
create_webserver& basic_auth() {
_basic_auth_enabled = true;
return *this;
Expand All @@ -270,6 +271,7 @@ class create_webserver {
_basic_auth_enabled = false;
return *this;
}
#endif // HAVE_BAUTH

create_webserver& digest_auth() {
_digest_auth_enabled = true;
Expand Down Expand Up @@ -438,7 +440,9 @@ class create_webserver {
std::string _digest_auth_random = "";
int _nonce_nc_size = 0;
http::http_utils::policy_T _default_policy = http::http_utils::ACCEPT;
#ifdef HAVE_BAUTH
bool _basic_auth_enabled = true;
#endif // HAVE_BAUTH
bool _digest_auth_enabled = true;
bool _regex_checking = true;
bool _ban_system_enabled = true;
Expand Down
8 changes: 8 additions & 0 deletions src/httpserver/http_request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ class http_request {
public:
static const char EMPTY[];

#ifdef HAVE_BAUTH
/**
* Method used to get the username eventually passed through basic authentication.
* @return string representation of the username.
**/
std::string_view get_user() const;
#endif // HAVE_BAUTH

#ifdef HAVE_DAUTH
/**
Expand All @@ -74,11 +76,13 @@ class http_request {
std::string_view get_digested_user() const;
#endif // HAVE_DAUTH

#ifdef HAVE_BAUTH
/**
* Method used to get the password eventually passed through basic authentication.
* @return string representation of the password.
**/
std::string_view get_pass() const;
#endif // HAVE_BAUTH

/**
* Method used to get the path requested
Expand Down Expand Up @@ -380,7 +384,9 @@ class http_request {

static MHD_Result build_request_querystring(void *cls, enum MHD_ValueKind kind, const char *key, const char *value);

#ifdef HAVE_BAUTH
void fetch_user_pass() const;
#endif // HAVE_BAUTH

/**
* Method used to set an argument value by key.
Expand Down Expand Up @@ -485,8 +491,10 @@ class http_request {
// Others (username, password, digested_user) MHD returns as char* that we need
// to make a copy of and free anyway.
struct http_request_data_cache {
#ifdef HAVE_BAUTH
std::string username;
std::string password;
#endif // HAVE_BAUTH
std::string querystring;
std::string requestor_ip;
#ifdef HAVE_DAUTH
Expand Down
2 changes: 2 additions & 0 deletions src/httpserver/webserver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ class webserver {
const int nonce_nc_size;
bool running;
const http::http_utils::policy_T default_policy;
#ifdef HAVE_BAUTH
const bool basic_auth_enabled;
#endif // HAVE_BAUTH
const bool digest_auth_enabled;
const bool regex_checking;
const bool ban_system_enabled;
Expand Down
2 changes: 2 additions & 0 deletions src/webserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ webserver::webserver(const create_webserver& params):
nonce_nc_size(params._nonce_nc_size),
running(false),
default_policy(params._default_policy),
#ifdef HAVE_BAUTH
basic_auth_enabled(params._basic_auth_enabled),
#endif // HAVE_BAUTH
digest_auth_enabled(params._digest_auth_enabled),
regex_checking(params._regex_checking),
ban_system_enabled(params._ban_system_enabled),
Expand Down
8 changes: 8 additions & 0 deletions test/integ/authentication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ using std::shared_ptr;
using httpserver::webserver;
using httpserver::create_webserver;
using httpserver::http_response;
#ifdef HAVE_BAUTH
using httpserver::basic_auth_fail_response;
#endif // HAVE_BAUTH
#ifdef HAVE_DAUTH
using httpserver::digest_auth_fail_response;
#endif // HAVE_DAUTH
Expand All @@ -66,6 +68,7 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) {
return size*nmemb;
}

#ifdef HAVE_BAUTH
class user_pass_resource : public http_resource {
public:
shared_ptr<http_response> render_GET(const http_request& req) {
Expand All @@ -75,6 +78,7 @@ class user_pass_resource : public http_resource {
return std::make_shared<string_response>(std::string(req.get_user()) + " " + std::string(req.get_pass()), 200, "text/plain");
}
};
#endif // HAVE_BAUTH

#ifdef HAVE_DAUTH
class digest_resource : public http_resource {
Expand All @@ -101,6 +105,7 @@ LT_BEGIN_SUITE(authentication_suite)
}
LT_END_SUITE(authentication_suite)

#ifdef HAVE_BAUTH
LT_BEGIN_AUTO_TEST(authentication_suite, base_auth)
webserver ws = create_webserver(PORT);

Expand Down Expand Up @@ -150,6 +155,7 @@ LT_BEGIN_AUTO_TEST(authentication_suite, base_auth_fail)

ws.stop();
LT_END_AUTO_TEST(base_auth_fail)
#endif // HAVE_BAUTH

// do not run the digest auth tests on windows as curl
// appears to have problems with it.
Expand Down Expand Up @@ -555,6 +561,7 @@ LT_END_AUTO_TEST(digest_user_cache_with_auth)

#endif

#ifdef HAVE_BAUTH
// Simple resource for centralized auth tests
class simple_resource : public http_resource {
public:
Expand Down Expand Up @@ -1122,6 +1129,7 @@ LT_BEGIN_AUTO_TEST(authentication_suite, auth_skip_path_traversal_bypass)

ws.stop();
LT_END_AUTO_TEST(auth_skip_path_traversal_bypass)
#endif // HAVE_BAUTH

LT_BEGIN_AUTO_TEST_ENV()
AUTORUN_TESTS()
Expand Down
4 changes: 4 additions & 0 deletions test/integ/basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2640,6 +2640,7 @@ class null_value_query_resource : public http_resource {
}
};

#ifdef HAVE_BAUTH
// Resource that tests auth caching (get_user/get_pass called multiple times)
class auth_cache_resource : public http_resource {
public:
Expand All @@ -2654,7 +2655,9 @@ class auth_cache_resource : public http_resource {
return std::make_shared<string_response>(result, 200, "text/plain");
}
};
#endif // HAVE_BAUTH

#ifdef HAVE_BAUTH
LT_BEGIN_AUTO_TEST(basic_suite, auth_caching)
auth_cache_resource resource;
LT_ASSERT_EQ(true, ws->register_resource("auth_cache", &resource));
Expand All @@ -2672,6 +2675,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, auth_caching)
LT_CHECK_EQ(s, "NO_AUTH");
curl_easy_cleanup(curl);
LT_END_AUTO_TEST(auth_caching)
#endif // HAVE_BAUTH

// Test query parameters with null/empty values (e.g., ?keyonly&normal=value)
// This covers http_request.cpp lines 234 and 248 (arg_value == nullptr branches)
Expand Down
Loading
Loading