Skip to content

Add client certificate authentication and SNI callback support#364

Open
etr wants to merge 8 commits intomasterfrom
feature/client-cert-auth
Open

Add client certificate authentication and SNI callback support#364
etr wants to merge 8 commits intomasterfrom
feature/client-cert-auth

Conversation

@etr
Copy link
Owner

@etr etr commented Feb 4, 2026

Summary

Closes #133

This PR adds convenience methods for client certificate authentication (mTLS) and Server Name Indication (SNI) callback support:

  • Client certificate methods (8 new methods on http_request):

    • has_client_certificate() - check if client presented a certificate
    • get_client_cert_dn() - get subject Distinguished Name
    • get_client_cert_issuer_dn() - get issuer DN
    • get_client_cert_cn() - get Common Name from subject
    • is_client_cert_verified() - check if certificate chain is verified
    • get_client_cert_fingerprint_sha256() - get hex-encoded SHA-256 fingerprint
    • get_client_cert_not_before() / get_client_cert_not_after() - get validity times
  • SNI callback support (requires libmicrohttpd 0.9.71+):

    • sni_callback() builder method on create_webserver
    • Callback receives server name from TLS ClientHello
    • Returns cert/key pair for the requested hostname

Files Changed

File Description
src/httpserver/http_request.hpp Added 8 client cert method declarations
src/http_request.cpp Implemented methods using GnuTLS APIs
src/httpserver/create_webserver.hpp Added SNI callback typedef and builder
src/httpserver/webserver.hpp Added SNI callback member and handler
src/webserver.cpp Implemented SNI callback registration
test/integ/ws_start_stop.cpp Added 5 new tests for client cert & SNI
examples/client_cert_auth.cpp New example for client cert authentication
README.md Documentation for mTLS and SNI features

Test plan

  • All existing tests pass (make check - 14/14 pass)
  • New client certificate tests pass
  • SNI callback configuration test passes
  • cpplint style checks pass
  • Manual testing with curl and client certificates

etr added 2 commits February 4, 2026 10:05
Add convenience methods for extracting client certificate information
from TLS connections (mTLS) and Server Name Indication (SNI) callback
support for hosting multiple certificates on a single server.

Client certificate methods added to http_request (requires GnuTLS):
- has_client_certificate() - check if client cert present
- get_client_cert_dn() - subject Distinguished Name
- get_client_cert_issuer_dn() - issuer DN
- get_client_cert_cn() - Common Name from subject
- is_client_cert_verified() - certificate chain verification status
- get_client_cert_fingerprint_sha256() - hex-encoded SHA-256 fingerprint
- get_client_cert_not_before() / get_client_cert_not_after() - validity times

SNI callback support (requires libmicrohttpd 0.9.71+):
- sni_callback() builder method on create_webserver
- Callback receives server name from TLS ClientHello
- Returns cert/key pair for the requested hostname

Closes #133
The client_cert.pem and client_key.pem files need to be copied to the
build directory during configure, similar to how other test certificate
files are handled.
@codecov
Copy link

codecov bot commented Feb 4, 2026

Codecov Report

❌ Patch coverage is 41.22807% with 67 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.62%. Comparing base (42e769c) to head (436bc41).

Files with missing lines Patch % Lines
src/http_request.cpp 40.36% 18 Missing and 47 partials ⚠️
src/webserver.cpp 0.00% 0 Missing and 2 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #364      +/-   ##
==========================================
- Coverage   70.54%   68.62%   -1.92%     
==========================================
  Files          28       28              
  Lines        1436     1549     +113     
  Branches      570      629      +59     
==========================================
+ Hits         1013     1063      +50     
- Misses         35       50      +15     
- Partials      388      436      +48     
Files with missing lines Coverage Δ
src/httpserver/create_webserver.hpp 96.98% <100.00%> (+0.04%) ⬆️
src/httpserver/http_request.hpp 84.21% <ø> (ø)
src/webserver.cpp 58.68% <0.00%> (-0.13%) ⬇️
src/http_request.cpp 60.06% <40.36%> (-10.33%) ⬇️

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 42e769c...436bc41. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

etr added 6 commits February 4, 2026 12:11
…urn value

The webserver::start(false) method returns false when started successfully
in non-blocking mode (by design). The tests were incorrectly interpreting
this as a failure. Fixed by catching the exception that's thrown when
the server actually fails to start.

Also regenerated test certificates with valid dates (the old ones expired
in 2009).

Changes:
- Fix https_webserver, tls_session_getters, and all client_cert tests
- Regenerate cert.pem, key.pem, client_cert.pem, client_key.pem
This test exercises all client certificate convenience methods on a
plain HTTP (non-TLS) connection, verifying they return appropriate
empty/false values when no TLS session is present.

This ensures the early-return code paths in the client certificate
methods are covered even when HTTPS tests may not run in all CI
environments.
webserver::start(false) always returns false for non-blocking mode
(returns value_onclose which is only set true for blocking mode).
Tests were checking `if (started)` which never executed the test code.

Changed affected tests to:
- Use try-catch for exception handling
- Check ws.is_running() instead of the return value
- Handle dual_stack curl failures gracefully (may not be available)

This fixes coverage for client cert and SNI tests that were silently
skipping without actually testing the functionality.
- client_cert_no_cn: Tests certificate without Common Name field,
  covering the cn_size == 0 branch in get_client_cert_cn()
- client_cert_untrusted: Tests certificate not in trust store,
  covering the status != 0 branch in is_client_cert_verified()

Also adds the new test certificate files to configure.ac.
Add to TLS/HTTPS configuration section:
- sni_callback() builder method for SNI support

Add to Parsing Requests section:
- has_client_certificate()
- get_client_cert_dn()
- get_client_cert_issuer_dn()
- get_client_cert_cn()
- is_client_cert_verified()
- get_client_cert_fingerprint_sha256()
- get_client_cert_not_before()
- get_client_cert_not_after()

Also fixed typo: "am underlying" -> "an underlying"
- Add scoped_x509_cert RAII wrapper to eliminate code duplication
  across 6 certificate extraction methods
- Add null check in get_tls_session() to prevent null pointer
  dereference when MHD_get_connection_info returns nullptr
- Fix TOCTOU race condition in SNI credential caching by re-checking
  cache after acquiring exclusive lock
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enable client-certificate authentication

1 participant