diff --git a/.github/workflows/verify-build.yml b/.github/workflows/verify-build.yml index 775304499..e4842605d 100644 --- a/.github/workflows/verify-build.yml +++ b/.github/workflows/verify-build.yml @@ -58,8 +58,8 @@ jobs: os-type: ubuntu build-type: asan compiler-family: clang - c-compiler: clang-13 - cc-compiler: clang++-13 + c-compiler: clang-18 + cc-compiler: clang++-18 debug: debug coverage: nocoverage # This test gives false positives on newer versions of clang @@ -78,8 +78,8 @@ jobs: os-type: ubuntu build-type: lsan compiler-family: clang - c-compiler: clang-13 - cc-compiler: clang++-13 + c-compiler: clang-18 + cc-compiler: clang++-18 debug: debug coverage: nocoverage - test-group: extra @@ -87,8 +87,8 @@ jobs: os-type: ubuntu build-type: tsan compiler-family: clang - c-compiler: clang-13 - cc-compiler: clang++-13 + c-compiler: clang-18 + cc-compiler: clang++-18 debug: debug coverage: nocoverage - test-group: extra @@ -96,26 +96,26 @@ jobs: os-type: ubuntu build-type: ubsan compiler-family: clang - c-compiler: clang-13 - cc-compiler: clang++-13 + c-compiler: clang-18 + cc-compiler: clang++-18 debug: debug coverage: nocoverage - test-group: extra - os: ubuntu-20.04 + os: ubuntu-latest os-type: ubuntu build-type: none compiler-family: gcc - c-compiler: gcc-7 - cc-compiler: g++-7 + c-compiler: gcc-9 + cc-compiler: g++-9 debug: nodebug coverage: nocoverage - test-group: extra - os: ubuntu-20.04 + os: ubuntu-latest os-type: ubuntu build-type: none compiler-family: gcc - c-compiler: gcc-8 - cc-compiler: g++-8 + c-compiler: gcc-10 + cc-compiler: g++-10 debug: nodebug coverage: nocoverage - test-group: extra @@ -123,8 +123,8 @@ jobs: os-type: ubuntu build-type: none compiler-family: gcc - c-compiler: gcc-9 - cc-compiler: g++-9 + c-compiler: gcc-11 + cc-compiler: g++-11 debug: nodebug coverage: nocoverage - test-group: extra @@ -132,53 +132,62 @@ jobs: os-type: ubuntu build-type: none compiler-family: gcc - c-compiler: gcc-10 - cc-compiler: g++-10 + c-compiler: gcc-12 + cc-compiler: g++-12 debug: nodebug coverage: nocoverage - test-group: extra - os: ubuntu-20.04 + os: ubuntu-latest os-type: ubuntu build-type: none - compiler-family: clang - c-compiler: clang-6.0 - cc-compiler: clang++-6.0 + compiler-family: gcc + c-compiler: gcc-13 + cc-compiler: g++-13 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + os-type: ubuntu + build-type: none + compiler-family: gcc + c-compiler: gcc-14 + cc-compiler: g++-14 debug: nodebug coverage: nocoverage - test-group: extra - os: ubuntu-20.04 + os: ubuntu-22.04 os-type: ubuntu build-type: none compiler-family: clang - c-compiler: clang-7 - cc-compiler: clang++-7 + c-compiler: clang-11 + cc-compiler: clang++-11 debug: nodebug coverage: nocoverage - test-group: extra - os: ubuntu-20.04 + os: ubuntu-22.04 os-type: ubuntu build-type: none compiler-family: clang - c-compiler: clang-8 - cc-compiler: clang++-8 + c-compiler: clang-12 + cc-compiler: clang++-12 debug: nodebug coverage: nocoverage - test-group: extra - os: ubuntu-20.04 + os: ubuntu-22.04 os-type: ubuntu build-type: none compiler-family: clang - c-compiler: clang-9 - cc-compiler: clang++-9 + c-compiler: clang-13 + cc-compiler: clang++-13 debug: nodebug coverage: nocoverage - test-group: extra - os: ubuntu-20.04 + os: ubuntu-latest os-type: ubuntu build-type: none compiler-family: clang - c-compiler: clang-10 - cc-compiler: clang++-10 + c-compiler: clang-14 + cc-compiler: clang++-14 debug: nodebug coverage: nocoverage - test-group: extra @@ -186,8 +195,8 @@ jobs: os-type: ubuntu build-type: none compiler-family: clang - c-compiler: clang-11 - cc-compiler: clang++-11 + c-compiler: clang-15 + cc-compiler: clang++-15 debug: nodebug coverage: nocoverage - test-group: extra @@ -195,8 +204,8 @@ jobs: os-type: ubuntu build-type: none compiler-family: clang - c-compiler: clang-12 - cc-compiler: clang++-12 + c-compiler: clang-16 + cc-compiler: clang++-16 debug: nodebug coverage: nocoverage - test-group: extra @@ -204,8 +213,8 @@ jobs: os-type: ubuntu build-type: none compiler-family: clang - c-compiler: clang-13 - cc-compiler: clang++-13 + c-compiler: clang-17 + cc-compiler: clang++-17 debug: nodebug coverage: nocoverage - test-group: extra @@ -213,17 +222,17 @@ jobs: os-type: ubuntu build-type: valgrind compiler-family: gcc - c-compiler: gcc-10 - cc-compiler: g++-10 + c-compiler: gcc-14 + cc-compiler: g++-14 debug: nodebug coverage: nocoverage - test-group: extra - os: ubuntu-20.04 + os: ubuntu-latest os-type: ubuntu build-type: iwyu compiler-family: clang - c-compiler: clang-9 - cc-compiler: clang++-9 + c-compiler: clang-18 + cc-compiler: clang++-18 debug: nodebug coverage: nocoverage - test-group: performance @@ -264,7 +273,7 @@ jobs: coverage: nocoverage steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. @@ -294,7 +303,7 @@ jobs: if: ${{ matrix.compiler-family == 'gcc' && matrix.os-type == 'ubuntu' }} - name: Install valgrind if needed - run: sudo apt-get install valgrind valgrind-dbg + run: sudo apt-get install valgrind if: ${{ matrix.build-type == 'valgrind' && matrix.os-type == 'ubuntu' }} - name: Install cpplint if needed @@ -303,16 +312,12 @@ jobs: - name: Install IWYU dependencies if needed run: | - # Use same deps used by iwyu in their setup for travis - sudo apt-get install llvm-9-dev llvm-9-tools libclang-9-dev ; - # Use same CMAKE used by iwyu in their setup for travis - wget -O cmake.sh https://cmake.org/files/v3.10/cmake-3.10.0-Linux-x86_64.sh ; - sudo sh cmake.sh --skip-license --exclude-subdir --prefix=/usr/local ; + sudo apt-get install llvm-18-dev libclang-18-dev clang-18 ; if: ${{ matrix.build-type == 'iwyu' && matrix.os-type == 'ubuntu' }} - name: IWYU from cache (for testing) id: cache-IWYU - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: include-what-you-use key: ${{ matrix.os }}-${{ matrix.c-compiler }}-include-what-you-use-pre-built @@ -321,11 +326,11 @@ jobs: # Installing iwyu manually because clang and iwyu paths won't match on Ubuntu otherwise. - name: Build IWYU if requested run: | - CLANG_ROOT_PATH=`llvm-config-9 --prefix` ; - CLANG_BIN_PATH=`llvm-config-9 --bindir` ; - curl "https://libhttpserver.s3.amazonaws.com/travis_stuff/include-what-you-use-clang-9.tgz" -o "include-what-you-use-clang-9.tgz" ; - tar -xzf "include-what-you-use-clang-9.tgz" ; + CLANG_ROOT_PATH=`llvm-config-18 --prefix` ; + CLANG_BIN_PATH=`llvm-config-18 --bindir` ; + git clone https://github.com/include-what-you-use/include-what-you-use.git ; cd include-what-you-use ; + git checkout clang_18 ; mkdir build_iwyu ; cd build_iwyu ; cmake -G "Unix Makefiles" -DCMAKE_PREFIX_PATH=$CLANG_ROOT_PATH -DCMAKE_C_COMPILER=$CLANG_BIN_PATH/clang -DCMAKE_CXX_COMPILER=$CLANG_BIN_PATH/clang++ ../ ; @@ -341,7 +346,7 @@ jobs: - name: CURL from cache (for testing) id: cache-CURL - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: curl-7.75.0 key: ${{ matrix.os }}-CURL-pre-built @@ -352,7 +357,7 @@ jobs: curl https://libhttpserver.s3.amazonaws.com/travis_stuff/curl-7.75.0.tar.gz -o curl-7.75.0.tar.gz ; tar -xzf curl-7.75.0.tar.gz ; cd curl-7.75.0 ; - if [ "$matrix.os-type" = "ubuntu" ]; then ./configure ; else ./configure --with-darwinssl ; fi + ./configure --with-darwinssl ; make ; if: ${{ matrix.os == 'macos-latest' && steps.cache-CURL.outputs.cache-hit != 'true' }} @@ -386,22 +391,22 @@ jobs: - name: Fetch libmicrohttpd from cache id: cache-libmicrohttpd - uses: actions/cache@v2 + uses: actions/cache@v4 with: - path: libmicrohttpd-0.9.64 - key: ${{ matrix.os }}-${{ matrix.c-compiler }}-libmicrohttpd-pre-built + path: libmicrohttpd-0.9.77 + key: ${{ matrix.os }}-${{ matrix.c-compiler }}-libmicrohttpd-0.9.77-pre-built - name: Build libmicrohttpd dependency (if not cached) run: | - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.64.tar.gz -o libmicrohttpd-0.9.64.tar.gz ; - tar -xzf libmicrohttpd-0.9.64.tar.gz ; - cd libmicrohttpd-0.9.64 ; + curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.77.tar.gz -o libmicrohttpd-0.9.77.tar.gz ; + tar -xzf libmicrohttpd-0.9.77.tar.gz ; + cd libmicrohttpd-0.9.77 ; ./configure --disable-examples ; make ; if: steps.cache-libmicrohttpd.outputs.cache-hit != 'true' - + - name: Install libmicrohttpd - run: cd libmicrohttpd-0.9.64 ; sudo make install ; + run: cd libmicrohttpd-0.9.77 ; sudo make install ; - name: Refresh links to shared libs run: sudo ldconfig ; @@ -421,7 +426,7 @@ jobs: if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export CXXLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi # Additional flags on mac. They need to stay in step as env variables don't propagate across steps. - if [ "$matrix.os" = "macos-latest" ]; then + if [ "${{ matrix.os }}" = "macos-latest" ]; then export CFLAGS='-mtune=generic' ; export IPV6_TESTS_ENABLED="true" ; fi diff --git a/README.md b/README.md index b0a4c0e8f..83f0b5b60 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver * _.https_mem_cert(**const std::string&** filename):_ String representing the path to a file containing the certificate to be used by the HTTPS daemon. This must be used in conjunction with `https_mem_key`. * _.https_mem_trust(**const std::string&** filename):_ String representing the path to a file containing the CA certificate to be used by the HTTPS daemon to authenticate and trust clients certificates. The presence of this option activates the request of certificate to the client. The request to the client is marked optional, and it is the responsibility of the server to check the presence of the certificate if needed. Note that most browsers will only present a client certificate only if they have one matching the specified CA, not sending any certificate otherwise. * _.https_priorities(**const std::string&** priority_string):_ SSL/TLS protocol version and ciphers. Must be followed by a string specifying the SSL/TLS protocol versions and ciphers that are acceptable for the application. The string is passed unchanged to gnutls_priority_init. If this option is not specified, `"NORMAL"` is used. +* _.psk_cred_handler(**psk_cred_handler_callback** handler):_ Sets a callback function for TLS-PSK (Pre-Shared Key) authentication. The callback receives a username and should return the corresponding hex-encoded PSK, or an empty string if the user is unknown. This option requires `use_ssl()`, `cred_type(http::http_utils::PSK)`, and an appropriate `https_priorities()` string that enables PSK cipher suites. PSK authentication allows TLS without certificates by using a shared secret key. #### Minimal example using HTTPS ```cpp @@ -346,6 +347,59 @@ To test the above example, you can run the following command from a terminal: You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_https.cpp). +#### Minimal example using TLS-PSK +```cpp + #include + #include + #include + + using namespace httpserver; + + // Simple PSK database - in production, use secure storage + std::map psk_database = { + {"client1", "0123456789abcdef0123456789abcdef"}, + {"client2", "fedcba9876543210fedcba9876543210"} + }; + + // PSK credential handler callback + std::string psk_handler(const std::string& username) { + auto it = psk_database.find(username); + if (it != psk_database.end()) { + return it->second; + } + return ""; // Return empty string for unknown users + } + + class hello_world_resource : public http_resource { + public: + std::shared_ptr render(const http_request&) { + return std::shared_ptr( + new string_response("Hello, World (via TLS-PSK)!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .use_ssl() + .cred_type(http::http_utils::PSK) + .psk_cred_handler(psk_handler) + .https_priorities("NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2:+PSK:+DHE-PSK"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } +``` +To test the above example, you can run the following command from a terminal using gnutls-cli: + + gnutls-cli --pskusername=client1 --pskkey=0123456789abcdef0123456789abcdef -p 8080 localhost + +Then type `GET /hello HTTP/1.1` followed by `Host: localhost` and two newlines. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_https_psk.cpp). + ### IP Blacklisting/Whitelisting libhttpserver supports IP blacklisting and whitelisting as an internal feature. This section explains the startup options related with IP blacklisting/whitelisting. See the [specific section](#ip-blacklisting-and-whitelisting) to read more about the topic. * _.ban_system() and .no_ban_system:_ Can be used to enable/disable the ban system. `on` by default. diff --git a/configure.ac b/configure.ac index 66b8e7577..1197c1369 100644 --- a/configure.ac +++ b/configure.ac @@ -83,7 +83,6 @@ case "$host" in esac # Checks for header files. -AC_HEADER_STDC AC_CHECK_HEADER([stdint.h],[],[AC_MSG_ERROR("stdint.h not found")]) AC_CHECK_HEADER([inttypes.h],[],[AC_MSG_ERROR("inttypes.h not found")]) AC_CHECK_HEADER([errno.h],[],[AC_MSG_ERROR("errno.h not found")]) @@ -250,6 +249,8 @@ if test x"$have_gnutls" = x"yes"; then AM_CFLAGS="$AM_CXXFLAGS -DHAVE_GNUTLS" fi +AM_CONDITIONAL([HAVE_GNUTLS],[test x"$have_gnutls" = x"yes"]) + DX_HTML_FEATURE(ON) DX_CHM_FEATURE(OFF) DX_CHI_FEATURE(OFF) diff --git a/examples/Makefile.am b/examples/Makefile.am index 0fe116afb..cf838c307 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -42,3 +42,9 @@ benchmark_select_SOURCES = benchmark_select.cpp benchmark_threads_SOURCES = benchmark_threads.cpp benchmark_nodelay_SOURCES = benchmark_nodelay.cpp file_upload_SOURCES = file_upload.cpp + +if HAVE_GNUTLS +LDADD += -lgnutls +noinst_PROGRAMS += minimal_https_psk +minimal_https_psk_SOURCES = minimal_https_psk.cpp +endif diff --git a/examples/allowing_disallowing_methods.cpp b/examples/allowing_disallowing_methods.cpp index 733891428..50efa4fd7 100644 --- a/examples/allowing_disallowing_methods.cpp +++ b/examples/allowing_disallowing_methods.cpp @@ -18,6 +18,8 @@ USA */ +#include + #include class hello_world_resource : public httpserver::http_resource { diff --git a/examples/basic_authentication.cpp b/examples/basic_authentication.cpp index 7fb823408..661bbb3c6 100644 --- a/examples/basic_authentication.cpp +++ b/examples/basic_authentication.cpp @@ -18,6 +18,9 @@ USA */ +#include +#include + #include class user_pass_resource : public httpserver::http_resource { diff --git a/examples/custom_access_log.cpp b/examples/custom_access_log.cpp index f1a59d53e..8f596c900 100644 --- a/examples/custom_access_log.cpp +++ b/examples/custom_access_log.cpp @@ -19,6 +19,8 @@ */ #include +#include +#include #include diff --git a/examples/custom_error.cpp b/examples/custom_error.cpp index a82d59728..c38fb1696 100644 --- a/examples/custom_error.cpp +++ b/examples/custom_error.cpp @@ -18,6 +18,8 @@ USA */ +#include + #include std::shared_ptr not_found_custom(const httpserver::http_request&) { diff --git a/examples/deferred_with_accumulator.cpp b/examples/deferred_with_accumulator.cpp index 3d3a4e690..a43677735 100644 --- a/examples/deferred_with_accumulator.cpp +++ b/examples/deferred_with_accumulator.cpp @@ -18,11 +18,14 @@ USA */ +#include #include #include // cpplint errors on chrono and thread because they are replaced (in Chromium) by other google libraries. // This is not an issue here. #include // NOLINT [build/c++11] +#include +#include #include // NOLINT [build/c++11] #include diff --git a/examples/digest_authentication.cpp b/examples/digest_authentication.cpp index 40767dc2f..fb87cd4b9 100644 --- a/examples/digest_authentication.cpp +++ b/examples/digest_authentication.cpp @@ -18,6 +18,8 @@ USA */ +#include + #include #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" diff --git a/examples/file_upload.cpp b/examples/file_upload.cpp index 7e1afd5af..0916a4fc1 100644 --- a/examples/file_upload.cpp +++ b/examples/file_upload.cpp @@ -19,6 +19,9 @@ */ #include +#include +#include + #include class file_upload_resource : public httpserver::http_resource { diff --git a/examples/handlers.cpp b/examples/handlers.cpp index 10778aafc..4fc703031 100644 --- a/examples/handlers.cpp +++ b/examples/handlers.cpp @@ -18,6 +18,8 @@ USA */ +#include + #include class hello_world_resource : public httpserver::http_resource { diff --git a/examples/hello_with_get_arg.cpp b/examples/hello_with_get_arg.cpp index 268d00c37..41829a4d9 100644 --- a/examples/hello_with_get_arg.cpp +++ b/examples/hello_with_get_arg.cpp @@ -18,6 +18,9 @@ USA */ +#include +#include + #include class hello_world_resource : public httpserver::http_resource { diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index 391a600fc..9c06f87af 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -19,6 +19,8 @@ */ #include +#include +#include #include diff --git a/examples/minimal_deferred.cpp b/examples/minimal_deferred.cpp index e7c77f50b..d7a61d902 100644 --- a/examples/minimal_deferred.cpp +++ b/examples/minimal_deferred.cpp @@ -18,7 +18,11 @@ USA */ +#include #include +#include +#include + #include static int counter = 0; diff --git a/examples/minimal_file_response.cpp b/examples/minimal_file_response.cpp index a5dc81060..34776993a 100644 --- a/examples/minimal_file_response.cpp +++ b/examples/minimal_file_response.cpp @@ -18,6 +18,8 @@ USA */ +#include + #include class file_response_resource : public httpserver::http_resource { diff --git a/examples/minimal_hello_world.cpp b/examples/minimal_hello_world.cpp index f8fb27f04..fc166535d 100644 --- a/examples/minimal_hello_world.cpp +++ b/examples/minimal_hello_world.cpp @@ -18,6 +18,8 @@ USA */ +#include + #include class hello_world_resource : public httpserver::http_resource { diff --git a/examples/minimal_https.cpp b/examples/minimal_https.cpp index d5b8d443e..79cd710c8 100644 --- a/examples/minimal_https.cpp +++ b/examples/minimal_https.cpp @@ -18,6 +18,8 @@ USA */ +#include + #include class hello_world_resource : public httpserver::http_resource { diff --git a/examples/minimal_https_psk.cpp b/examples/minimal_https_psk.cpp new file mode 100644 index 000000000..9bb02ef6f --- /dev/null +++ b/examples/minimal_https_psk.cpp @@ -0,0 +1,63 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2024 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include +#include + +#include + +// Simple PSK database - in production, use secure storage +std::map psk_database = { + {"client1", "0123456789abcdef0123456789abcdef"}, + {"client2", "fedcba9876543210fedcba9876543210"} +}; + +// PSK credential handler callback +// Returns the hex-encoded PSK for the given username, or empty string if not found +std::string psk_handler(const std::string& username) { + auto it = psk_database.find(username); + if (it != psk_database.end()) { + return it->second; + } + return ""; // Return empty string for unknown users +} + +class hello_world_resource : public httpserver::http_resource { + public: + std::shared_ptr render(const httpserver::http_request&) { + return std::shared_ptr( + new httpserver::string_response("Hello, World (via TLS-PSK)!")); + } +}; + +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080) + .use_ssl() + .cred_type(httpserver::http::http_utils::PSK) + .psk_cred_handler(psk_handler) + .https_priorities("NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2:+PSK:+DHE-PSK"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/minimal_ip_ban.cpp b/examples/minimal_ip_ban.cpp index 7be3cd177..4b95b5f0a 100644 --- a/examples/minimal_ip_ban.cpp +++ b/examples/minimal_ip_ban.cpp @@ -18,6 +18,8 @@ USA */ +#include + #include class hello_world_resource : public httpserver::http_resource { diff --git a/examples/service.cpp b/examples/service.cpp index 2bc9d4ad0..309628bc5 100644 --- a/examples/service.cpp +++ b/examples/service.cpp @@ -22,6 +22,7 @@ #include #include +#include #include diff --git a/examples/setting_headers.cpp b/examples/setting_headers.cpp index ea678b92a..f92b76c18 100644 --- a/examples/setting_headers.cpp +++ b/examples/setting_headers.cpp @@ -18,6 +18,8 @@ USA */ +#include + #include class hello_world_resource : public httpserver::http_resource { diff --git a/examples/url_registration.cpp b/examples/url_registration.cpp index be6d1ce39..e6eef4582 100644 --- a/examples/url_registration.cpp +++ b/examples/url_registration.cpp @@ -18,6 +18,9 @@ USA */ +#include +#include + #include class hello_world_resource : public httpserver::http_resource { diff --git a/src/file_info.cpp b/src/file_info.cpp index 88a215833..d37e7aeb9 100644 --- a/src/file_info.cpp +++ b/src/file_info.cpp @@ -19,6 +19,7 @@ */ #include +#include #include "httpserver/file_info.hpp" namespace httpserver { diff --git a/src/http_request.cpp b/src/http_request.cpp index c49c3b8ef..68ced7620 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include #include "httpserver/http_utils.hpp" #include "httpserver/string_utilities.hpp" diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 886884845..430c4e655 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -21,7 +21,9 @@ #include "httpserver/http_resource.hpp" #include #include +#include #include +#include #include "httpserver/string_response.hpp" namespace httpserver { class http_response; } diff --git a/src/http_response.cpp b/src/http_response.cpp index cb357b24c..f12589f75 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -21,6 +21,8 @@ #include "httpserver/http_response.hpp" #include #include +#include +#include #include #include "httpserver/http_utils.hpp" diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 975f1a8f2..138c44ef1 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -44,7 +44,9 @@ #include #include #include +#include #include +#include #include "httpserver/string_utilities.hpp" @@ -206,9 +208,9 @@ const char* http_utils::text_plain = "text/plain"; const char* http_utils::upload_filename_template = "libhttpserver.XXXXXX"; #if defined(_WIN32) - const char http_utils::path_separator = '\\'; +const char http_utils::path_separator = '\\'; #else // _WIN32 - const char http_utils::path_separator = '/'; +const char http_utils::path_separator = '/'; #endif // _WIN32 std::vector http_utils::tokenize_url(const std::string& str, const char separator) { diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index c6d61e4d5..fca96d916 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -46,6 +46,7 @@ typedef std::function(const http_request&)> rende typedef std::function validator_ptr; typedef std::function log_access_ptr; typedef std::function log_error_ptr; +typedef std::function psk_cred_handler_callback; class create_webserver { public: @@ -223,6 +224,11 @@ class create_webserver { return *this; } + create_webserver& psk_cred_handler(psk_cred_handler_callback handler) { + _psk_cred_handler = handler; + return *this; + } + create_webserver& digest_auth_random(const std::string& digest_auth_random) { _digest_auth_random = digest_auth_random; return *this; @@ -384,6 +390,7 @@ class create_webserver { std::string _https_mem_trust = ""; std::string _https_priorities = ""; http::http_utils::cred_type_T _cred_type = http::http_utils::NONE; + psk_cred_handler_callback _psk_cred_handler = nullptr; std::string _digest_auth_random = ""; int _nonce_nc_size = 0; http::http_utils::policy_T _default_policy = http::http_utils::ACCEPT; diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index 85f8791f6..76a2127a3 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -38,8 +38,8 @@ struct MHD_Response; namespace httpserver { namespace details { - MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)); -} +MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)); +} // namespace details template class deferred_response : public string_response { diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index b2cbb1a62..05b43a32d 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -45,6 +45,10 @@ #include #include +#ifdef HAVE_GNUTLS +#include +#endif // HAVE_GNUTLS + #include "httpserver/http_utils.hpp" #include "httpserver/create_webserver.hpp" #include "httpserver/details/http_endpoint.hpp" @@ -153,6 +157,7 @@ class webserver { const std::string https_mem_trust; const std::string https_priorities; const http::http_utils::cred_type_T cred_type; + const psk_cred_handler_callback psk_cred_handler; const std::string digest_auth_random; const int nonce_nc_size; bool running; @@ -210,6 +215,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, + const char* username, + gnutls_datum_t* key); +#endif // HAVE_GNUTLS + friend MHD_Result policy_callback(void *cls, const struct sockaddr* addr, socklen_t addrlen); friend void error_log(void* cls, const char* fmt, va_list ap); friend void access_log(webserver* cls, std::string uri); diff --git a/src/webserver.cpp b/src/webserver.cpp index 6e0c7ead6..749efea1f 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -40,10 +40,13 @@ #include #include #include -#include #include +#include +#include #include +#include #include +#include #include #include @@ -142,6 +145,7 @@ webserver::webserver(const create_webserver& params): https_mem_trust(params._https_mem_trust), https_priorities(params._https_priorities), cred_type(params._cred_type), + psk_cred_handler(params._psk_cred_handler), digest_auth_random(params._digest_auth_random), nonce_nc_size(params._nonce_nc_size), running(false), @@ -276,6 +280,11 @@ bool webserver::start(bool blocking) { if (cred_type != http_utils::NONE) { iov.push_back(gen(MHD_OPTION_HTTPS_CRED_TYPE, cred_type)); } + + if (psk_cred_handler != nullptr && use_ssl) { + iov.push_back(gen(MHD_OPTION_GNUTLS_PSK_CRED_HANDLER, + (intptr_t)&psk_cred_handler_func, this)); + } #endif // HAVE_GNUTLS iov.push_back(gen(MHD_OPTION_END, 0, nullptr)); @@ -396,6 +405,43 @@ void webserver::disallow_ip(const string& ip) { allowances.erase(ip_representation(ip)); } +#ifdef HAVE_GNUTLS +int webserver::psk_cred_handler_func(gnutls_session_t session, + const char* username, + gnutls_datum_t* key) { + webserver* ws = static_cast( + gnutls_session_get_ptr(session)); + + if (ws == nullptr || ws->psk_cred_handler == nullptr) { + return -1; + } + + std::string psk_hex = ws->psk_cred_handler(std::string(username)); + if (psk_hex.empty()) { + return -1; + } + + // Convert hex string to binary + size_t psk_len = psk_hex.size() / 2; + key->data = static_cast(gnutls_malloc(psk_len)); + if (key->data == nullptr) { + 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; + return -1; + } + + key->size = static_cast(output_size); + return 0; +} +#endif // HAVE_GNUTLS + MHD_Result policy_callback(void *cls, const struct sockaddr* addr, socklen_t addrlen) { // Parameter needed to respect MHD interface, but not needed here. std::ignore = addrlen; diff --git a/test/Makefile.am b/test/Makefile.am index 68ddb5549..eb07a25ab 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -17,6 +17,13 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA LDADD = $(top_builddir)/src/libhttpserver.la + +if HAVE_GNUTLS +LDADD += -lgnutls +endif + +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 @@ -37,7 +44,7 @@ nodelay_SOURCES = integ/nodelay.cpp http_resource_SOURCES = unit/http_resource_test.cpp noinst_HEADERS = littletest.hpp -AM_CXXFLAGS += -lcurl -Wall -fPIC +AM_CXXFLAGS += -Wall -fPIC -Wno-overloaded-virtual if COND_GCOV AM_CFLAGS += -O0 --coverage --no-inline diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index bcc1c55b5..4f4096d3b 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -32,6 +32,7 @@ #include #include +#include #include "./httpserver.hpp" #include "./littletest.hpp" diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 3e680cb6a..177797d10 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -19,12 +19,16 @@ */ #include +#include #include +#include #include #include #include #include #include +#include +#include #include "./httpserver.hpp" #include "httpserver/string_utilities.hpp" diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index 6bece022c..bc5e33f6b 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -34,8 +34,10 @@ #include #include +#include #include #include +#include #include "./httpserver.hpp" #include "./littletest.hpp" diff --git a/test/integ/file_upload.cpp b/test/integ/file_upload.cpp index 57b6942b4..81ab4de8e 100644 --- a/test/integ/file_upload.cpp +++ b/test/integ/file_upload.cpp @@ -22,13 +22,16 @@ #include #include #include +#include #include -#include #include +#include #include #include #include #include +#include +#include #include "./httpserver.hpp" #include "httpserver/string_utilities.hpp" diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index cb77dc59f..6e7171961 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "./httpserver.hpp" #include "./littletest.hpp" @@ -509,7 +510,11 @@ void* start_ws_blocking(void* par) { httpserver::webserver* ws = (httpserver::webserver*) par; ok_resource ok; if (!ws->register_resource("base", &ok)) return PTHREAD_CANCELED; - try { ws->start(true); } catch (...) { return PTHREAD_CANCELED; } + try { + ws->start(true); + } catch (...) { + return PTHREAD_CANCELED; + } return nullptr; } diff --git a/test/littletest.hpp b/test/littletest.hpp index c26f61254..93b0e2567 100644 --- a/test/littletest.hpp +++ b/test/littletest.hpp @@ -70,6 +70,7 @@ #define LT_BEGIN_TEST(__lt_suite_name__, __lt_test_name__) \ struct __lt_test_name__ ## _class: public __lt_suite_name__, littletest::test<__lt_test_name__ ## _class> \ { \ + using littletest::test_base::operator(); \ __lt_test_name__ ## _class() \ { \ __lt_name__ = #__lt_test_name__; \ diff --git a/test/unit/http_endpoint_test.cpp b/test/unit/http_endpoint_test.cpp index 1cce2bcde..a22aa0501 100644 --- a/test/unit/http_endpoint_test.cpp +++ b/test/unit/http_endpoint_test.cpp @@ -20,6 +20,10 @@ #include "httpserver/details/http_endpoint.hpp" +#include +#include +#include + #include "./littletest.hpp" using httpserver::details::http_endpoint; diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 0bf5e6ea8..5fd3b97bd 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -32,6 +32,10 @@ #include #include +#include +#include +#include +#include #include "./littletest.hpp" diff --git a/test/unit/string_utilities_test.cpp b/test/unit/string_utilities_test.cpp index 0bc7a213b..f0b57e803 100644 --- a/test/unit/string_utilities_test.cpp +++ b/test/unit/string_utilities_test.cpp @@ -21,6 +21,8 @@ #include "httpserver/string_utilities.hpp" #include +#include +#include #include "./littletest.hpp"