Skip to content

Commit f15a7b0

Browse files
etrclaude
andauthored
Add TLS-PSK authentication support via callback mechanism (#348)
* Add TLS-PSK authentication support via callback mechanism This adds support for TLS Pre-Shared Key (PSK) authentication, allowing secure connections without certificates using a shared secret key. Changes: - Add psk_cred_handler() builder method to create_webserver - Add psk_cred_handler_callback typedef for PSK credential lookup - Implement psk_cred_handler_func() static callback using GnuTLS - Add MHD_OPTION_GNUTLS_PSK_CRED_HANDLER option when PSK is configured - Add AM_CONDITIONAL for HAVE_GNUTLS in configure.ac - Remove deprecated AC_HEADER_STDC macro - Add minimal_https_psk example demonstrating PSK usage - Add conditional GnuTLS linking in test/Makefile.am - Update README.md with PSK documentation and example The callback receives a username and returns the hex-encoded PSK, or an empty string for unknown users. * Update GitHub Actions to v4 (cache and checkout) * Fix linker error: link all examples against gnutls when available The library now uses GnuTLS functions (gnutls_malloc, gnutls_free, gnutls_hex2bin) for PSK support, so all examples need to link against gnutls when HAVE_GNUTLS is defined, not just the PSK example. * Update CI for Ubuntu 24.04 compatibility - Update sanitizer builds (asan, lsan, tsan, ubsan) from clang-13 to clang-18 - Move clang-11, clang-12, clang-13 tests to ubuntu-22.04 - Add new clang-14 through clang-17 tests on ubuntu-latest - Add gcc-11 through gcc-14 tests - Remove obsolete ubuntu-20.04 jobs (gcc-7, gcc-8, clang-6 through clang-10) - Update IWYU job to use clang-18 on ubuntu-latest - Fix cpplint errors: add missing includes and fix namespace indentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix valgrind CI job for Ubuntu 24.04 - Remove valgrind-dbg package (debug symbols now included in main package) - Update valgrind job to use GCC 14 instead of GCC 10 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Use GCC 13 for valgrind job to avoid -Woverloaded-virtual error GCC 14 with -Werror catches a latent warning in littletest.hpp test framework that older compilers don't flag. Use GCC 13 for now. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix issues with valgrind running on g++-14 * FIx issue with overloads in tests * Fix issue with gnutls * Fix all cpplint issues * Added codacy suppressions * No need for codacy yaml. It is configured via UI --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent dff6af2 commit f15a7b0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+355
-77
lines changed

.github/workflows/verify-build.yml

Lines changed: 73 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ jobs:
5858
os-type: ubuntu
5959
build-type: asan
6060
compiler-family: clang
61-
c-compiler: clang-13
62-
cc-compiler: clang++-13
61+
c-compiler: clang-18
62+
cc-compiler: clang++-18
6363
debug: debug
6464
coverage: nocoverage
6565
# This test gives false positives on newer versions of clang
@@ -78,152 +78,161 @@ jobs:
7878
os-type: ubuntu
7979
build-type: lsan
8080
compiler-family: clang
81-
c-compiler: clang-13
82-
cc-compiler: clang++-13
81+
c-compiler: clang-18
82+
cc-compiler: clang++-18
8383
debug: debug
8484
coverage: nocoverage
8585
- test-group: extra
8686
os: ubuntu-latest
8787
os-type: ubuntu
8888
build-type: tsan
8989
compiler-family: clang
90-
c-compiler: clang-13
91-
cc-compiler: clang++-13
90+
c-compiler: clang-18
91+
cc-compiler: clang++-18
9292
debug: debug
9393
coverage: nocoverage
9494
- test-group: extra
9595
os: ubuntu-latest
9696
os-type: ubuntu
9797
build-type: ubsan
9898
compiler-family: clang
99-
c-compiler: clang-13
100-
cc-compiler: clang++-13
99+
c-compiler: clang-18
100+
cc-compiler: clang++-18
101101
debug: debug
102102
coverage: nocoverage
103103
- test-group: extra
104-
os: ubuntu-20.04
104+
os: ubuntu-latest
105105
os-type: ubuntu
106106
build-type: none
107107
compiler-family: gcc
108-
c-compiler: gcc-7
109-
cc-compiler: g++-7
108+
c-compiler: gcc-9
109+
cc-compiler: g++-9
110110
debug: nodebug
111111
coverage: nocoverage
112112
- test-group: extra
113-
os: ubuntu-20.04
113+
os: ubuntu-latest
114114
os-type: ubuntu
115115
build-type: none
116116
compiler-family: gcc
117-
c-compiler: gcc-8
118-
cc-compiler: g++-8
117+
c-compiler: gcc-10
118+
cc-compiler: g++-10
119119
debug: nodebug
120120
coverage: nocoverage
121121
- test-group: extra
122122
os: ubuntu-latest
123123
os-type: ubuntu
124124
build-type: none
125125
compiler-family: gcc
126-
c-compiler: gcc-9
127-
cc-compiler: g++-9
126+
c-compiler: gcc-11
127+
cc-compiler: g++-11
128128
debug: nodebug
129129
coverage: nocoverage
130130
- test-group: extra
131131
os: ubuntu-latest
132132
os-type: ubuntu
133133
build-type: none
134134
compiler-family: gcc
135-
c-compiler: gcc-10
136-
cc-compiler: g++-10
135+
c-compiler: gcc-12
136+
cc-compiler: g++-12
137137
debug: nodebug
138138
coverage: nocoverage
139139
- test-group: extra
140-
os: ubuntu-20.04
140+
os: ubuntu-latest
141141
os-type: ubuntu
142142
build-type: none
143-
compiler-family: clang
144-
c-compiler: clang-6.0
145-
cc-compiler: clang++-6.0
143+
compiler-family: gcc
144+
c-compiler: gcc-13
145+
cc-compiler: g++-13
146+
debug: nodebug
147+
coverage: nocoverage
148+
- test-group: extra
149+
os: ubuntu-latest
150+
os-type: ubuntu
151+
build-type: none
152+
compiler-family: gcc
153+
c-compiler: gcc-14
154+
cc-compiler: g++-14
146155
debug: nodebug
147156
coverage: nocoverage
148157
- test-group: extra
149-
os: ubuntu-20.04
158+
os: ubuntu-22.04
150159
os-type: ubuntu
151160
build-type: none
152161
compiler-family: clang
153-
c-compiler: clang-7
154-
cc-compiler: clang++-7
162+
c-compiler: clang-11
163+
cc-compiler: clang++-11
155164
debug: nodebug
156165
coverage: nocoverage
157166
- test-group: extra
158-
os: ubuntu-20.04
167+
os: ubuntu-22.04
159168
os-type: ubuntu
160169
build-type: none
161170
compiler-family: clang
162-
c-compiler: clang-8
163-
cc-compiler: clang++-8
171+
c-compiler: clang-12
172+
cc-compiler: clang++-12
164173
debug: nodebug
165174
coverage: nocoverage
166175
- test-group: extra
167-
os: ubuntu-20.04
176+
os: ubuntu-22.04
168177
os-type: ubuntu
169178
build-type: none
170179
compiler-family: clang
171-
c-compiler: clang-9
172-
cc-compiler: clang++-9
180+
c-compiler: clang-13
181+
cc-compiler: clang++-13
173182
debug: nodebug
174183
coverage: nocoverage
175184
- test-group: extra
176-
os: ubuntu-20.04
185+
os: ubuntu-latest
177186
os-type: ubuntu
178187
build-type: none
179188
compiler-family: clang
180-
c-compiler: clang-10
181-
cc-compiler: clang++-10
189+
c-compiler: clang-14
190+
cc-compiler: clang++-14
182191
debug: nodebug
183192
coverage: nocoverage
184193
- test-group: extra
185194
os: ubuntu-latest
186195
os-type: ubuntu
187196
build-type: none
188197
compiler-family: clang
189-
c-compiler: clang-11
190-
cc-compiler: clang++-11
198+
c-compiler: clang-15
199+
cc-compiler: clang++-15
191200
debug: nodebug
192201
coverage: nocoverage
193202
- test-group: extra
194203
os: ubuntu-latest
195204
os-type: ubuntu
196205
build-type: none
197206
compiler-family: clang
198-
c-compiler: clang-12
199-
cc-compiler: clang++-12
207+
c-compiler: clang-16
208+
cc-compiler: clang++-16
200209
debug: nodebug
201210
coverage: nocoverage
202211
- test-group: extra
203212
os: ubuntu-latest
204213
os-type: ubuntu
205214
build-type: none
206215
compiler-family: clang
207-
c-compiler: clang-13
208-
cc-compiler: clang++-13
216+
c-compiler: clang-17
217+
cc-compiler: clang++-17
209218
debug: nodebug
210219
coverage: nocoverage
211220
- test-group: extra
212221
os: ubuntu-latest
213222
os-type: ubuntu
214223
build-type: valgrind
215224
compiler-family: gcc
216-
c-compiler: gcc-10
217-
cc-compiler: g++-10
225+
c-compiler: gcc-14
226+
cc-compiler: g++-14
218227
debug: nodebug
219228
coverage: nocoverage
220229
- test-group: extra
221-
os: ubuntu-20.04
230+
os: ubuntu-latest
222231
os-type: ubuntu
223232
build-type: iwyu
224233
compiler-family: clang
225-
c-compiler: clang-9
226-
cc-compiler: clang++-9
234+
c-compiler: clang-18
235+
cc-compiler: clang++-18
227236
debug: nodebug
228237
coverage: nocoverage
229238
- test-group: performance
@@ -264,7 +273,7 @@ jobs:
264273
coverage: nocoverage
265274
steps:
266275
- name: Checkout repository
267-
uses: actions/checkout@v2
276+
uses: actions/checkout@v4
268277
with:
269278
# We must fetch at least the immediate parents so that if this is
270279
# a pull request then we can checkout the head.
@@ -294,7 +303,7 @@ jobs:
294303
if: ${{ matrix.compiler-family == 'gcc' && matrix.os-type == 'ubuntu' }}
295304

296305
- name: Install valgrind if needed
297-
run: sudo apt-get install valgrind valgrind-dbg
306+
run: sudo apt-get install valgrind
298307
if: ${{ matrix.build-type == 'valgrind' && matrix.os-type == 'ubuntu' }}
299308

300309
- name: Install cpplint if needed
@@ -303,16 +312,12 @@ jobs:
303312

304313
- name: Install IWYU dependencies if needed
305314
run: |
306-
# Use same deps used by iwyu in their setup for travis
307-
sudo apt-get install llvm-9-dev llvm-9-tools libclang-9-dev ;
308-
# Use same CMAKE used by iwyu in their setup for travis
309-
wget -O cmake.sh https://cmake.org/files/v3.10/cmake-3.10.0-Linux-x86_64.sh ;
310-
sudo sh cmake.sh --skip-license --exclude-subdir --prefix=/usr/local ;
315+
sudo apt-get install llvm-18-dev libclang-18-dev clang-18 ;
311316
if: ${{ matrix.build-type == 'iwyu' && matrix.os-type == 'ubuntu' }}
312317

313318
- name: IWYU from cache (for testing)
314319
id: cache-IWYU
315-
uses: actions/cache@v2
320+
uses: actions/cache@v4
316321
with:
317322
path: include-what-you-use
318323
key: ${{ matrix.os }}-${{ matrix.c-compiler }}-include-what-you-use-pre-built
@@ -321,11 +326,11 @@ jobs:
321326
# Installing iwyu manually because clang and iwyu paths won't match on Ubuntu otherwise.
322327
- name: Build IWYU if requested
323328
run: |
324-
CLANG_ROOT_PATH=`llvm-config-9 --prefix` ;
325-
CLANG_BIN_PATH=`llvm-config-9 --bindir` ;
326-
curl "https://libhttpserver.s3.amazonaws.com/travis_stuff/include-what-you-use-clang-9.tgz" -o "include-what-you-use-clang-9.tgz" ;
327-
tar -xzf "include-what-you-use-clang-9.tgz" ;
329+
CLANG_ROOT_PATH=`llvm-config-18 --prefix` ;
330+
CLANG_BIN_PATH=`llvm-config-18 --bindir` ;
331+
git clone https://github.com/include-what-you-use/include-what-you-use.git ;
328332
cd include-what-you-use ;
333+
git checkout clang_18 ;
329334
mkdir build_iwyu ;
330335
cd build_iwyu ;
331336
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:
341346

342347
- name: CURL from cache (for testing)
343348
id: cache-CURL
344-
uses: actions/cache@v2
349+
uses: actions/cache@v4
345350
with:
346351
path: curl-7.75.0
347352
key: ${{ matrix.os }}-CURL-pre-built
@@ -352,7 +357,7 @@ jobs:
352357
curl https://libhttpserver.s3.amazonaws.com/travis_stuff/curl-7.75.0.tar.gz -o curl-7.75.0.tar.gz ;
353358
tar -xzf curl-7.75.0.tar.gz ;
354359
cd curl-7.75.0 ;
355-
if [ "$matrix.os-type" = "ubuntu" ]; then ./configure ; else ./configure --with-darwinssl ; fi
360+
./configure --with-darwinssl ;
356361
make ;
357362
if: ${{ matrix.os == 'macos-latest' && steps.cache-CURL.outputs.cache-hit != 'true' }}
358363

@@ -386,22 +391,22 @@ jobs:
386391

387392
- name: Fetch libmicrohttpd from cache
388393
id: cache-libmicrohttpd
389-
uses: actions/cache@v2
394+
uses: actions/cache@v4
390395
with:
391-
path: libmicrohttpd-0.9.64
392-
key: ${{ matrix.os }}-${{ matrix.c-compiler }}-libmicrohttpd-pre-built
396+
path: libmicrohttpd-0.9.77
397+
key: ${{ matrix.os }}-${{ matrix.c-compiler }}-libmicrohttpd-0.9.77-pre-built
393398

394399
- name: Build libmicrohttpd dependency (if not cached)
395400
run: |
396-
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.64.tar.gz -o libmicrohttpd-0.9.64.tar.gz ;
397-
tar -xzf libmicrohttpd-0.9.64.tar.gz ;
398-
cd libmicrohttpd-0.9.64 ;
401+
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.77.tar.gz -o libmicrohttpd-0.9.77.tar.gz ;
402+
tar -xzf libmicrohttpd-0.9.77.tar.gz ;
403+
cd libmicrohttpd-0.9.77 ;
399404
./configure --disable-examples ;
400405
make ;
401406
if: steps.cache-libmicrohttpd.outputs.cache-hit != 'true'
402-
407+
403408
- name: Install libmicrohttpd
404-
run: cd libmicrohttpd-0.9.64 ; sudo make install ;
409+
run: cd libmicrohttpd-0.9.77 ; sudo make install ;
405410

406411
- name: Refresh links to shared libs
407412
run: sudo ldconfig ;
@@ -421,7 +426,7 @@ jobs:
421426
if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export CXXLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi
422427
423428
# Additional flags on mac. They need to stay in step as env variables don't propagate across steps.
424-
if [ "$matrix.os" = "macos-latest" ]; then
429+
if [ "${{ matrix.os }}" = "macos-latest" ]; then
425430
export CFLAGS='-mtune=generic' ;
426431
export IPV6_TESTS_ENABLED="true" ;
427432
fi

README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver
313313
* _.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`.
314314
* _.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.
315315
* _.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.
316+
* _.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.
316317

317318
#### Minimal example using HTTPS
318319
```cpp
@@ -346,6 +347,59 @@ To test the above example, you can run the following command from a terminal:
346347

347348
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_https.cpp).
348349

350+
#### Minimal example using TLS-PSK
351+
```cpp
352+
#include <httpserver.hpp>
353+
#include <map>
354+
#include <string>
355+
356+
using namespace httpserver;
357+
358+
// Simple PSK database - in production, use secure storage
359+
std::map<std::string, std::string> psk_database = {
360+
{"client1", "0123456789abcdef0123456789abcdef"},
361+
{"client2", "fedcba9876543210fedcba9876543210"}
362+
};
363+
364+
// PSK credential handler callback
365+
std::string psk_handler(const std::string& username) {
366+
auto it = psk_database.find(username);
367+
if (it != psk_database.end()) {
368+
return it->second;
369+
}
370+
return ""; // Return empty string for unknown users
371+
}
372+
373+
class hello_world_resource : public http_resource {
374+
public:
375+
std::shared_ptr<http_response> render(const http_request&) {
376+
return std::shared_ptr<http_response>(
377+
new string_response("Hello, World (via TLS-PSK)!"));
378+
}
379+
};
380+
381+
int main(int argc, char** argv) {
382+
webserver ws = create_webserver(8080)
383+
.use_ssl()
384+
.cred_type(http::http_utils::PSK)
385+
.psk_cred_handler(psk_handler)
386+
.https_priorities("NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2:+PSK:+DHE-PSK");
387+
388+
hello_world_resource hwr;
389+
ws.register_resource("/hello", &hwr);
390+
ws.start(true);
391+
392+
return 0;
393+
}
394+
```
395+
To test the above example, you can run the following command from a terminal using gnutls-cli:
396+
397+
gnutls-cli --pskusername=client1 --pskkey=0123456789abcdef0123456789abcdef -p 8080 localhost
398+
399+
Then type `GET /hello HTTP/1.1` followed by `Host: localhost` and two newlines.
400+
401+
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_https_psk.cpp).
402+
349403
### IP Blacklisting/Whitelisting
350404
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.
351405
* _.ban_system() and .no_ban_system:_ Can be used to enable/disable the ban system. `on` by default.

0 commit comments

Comments
 (0)