Skip to content

Commit 86d4631

Browse files
committed
Cleaned-up validation issues
1 parent 6d47202 commit 86d4631

14 files changed

Lines changed: 546 additions & 259 deletions

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,9 +1260,9 @@ libhttpserver provides WebSocket support when libmicrohttpd is built with WebSoc
12601260
### The websocket_handler class
12611261
The `websocket_handler` class provides the following virtual methods:
12621262
* _**void** on_open(**websocket_session&** session):_ Called when a new WebSocket connection is established. Default implementation does nothing.
1263-
* _**void** on_message(**websocket_session&** session, **const std::string&** msg):_ Called when a text message is received. **This is the only pure virtual method and must be implemented.**
1263+
* _**void** on_message(**websocket_session&** session, **std::string_view** msg):_ Called when a text message is received. **This is the only pure virtual method and must be implemented.**
12641264
* _**void** on_binary(**websocket_session&** session, **const void*** data, **size_t** len):_ Called when a binary message is received. Default implementation does nothing.
1265-
* _**void** on_ping(**websocket_session&** session, **const std::string&** payload):_ Called when a ping frame is received. Default implementation sends a pong.
1265+
* _**void** on_ping(**websocket_session&** session, **std::string_view** payload):_ Called when a ping frame is received. Default implementation sends a pong.
12661266
* _**void** on_close(**websocket_session&** session, **uint16_t** code, **const std::string&** reason):_ Called when the WebSocket connection is closed. Default implementation does nothing.
12671267
12681268
### The websocket_session class
@@ -1283,8 +1283,8 @@ Register a WebSocket handler using `register_ws_resource`:
12831283
12841284
class echo_handler : public websocket_handler {
12851285
public:
1286-
void on_message(websocket_session& session, const std::string& msg) override {
1287-
session.send_text("Echo: " + msg);
1286+
void on_message(websocket_session& session, std::string_view msg) override {
1287+
session.send_text("Echo: " + std::string(msg));
12881288
}
12891289
};
12901290
@@ -1634,9 +1634,9 @@ You can also check this example on [github](https://github.com/etr/libhttpserver
16341634
session.send_text("Welcome to the echo server!");
16351635
}
16361636
1637-
void on_message(websocket_session& session, const std::string& msg) override {
1637+
void on_message(websocket_session& session, std::string_view msg) override {
16381638
std::cout << "Received: " << msg << std::endl;
1639-
session.send_text("Echo: " + msg);
1639+
session.send_text("Echo: " + std::string(msg));
16401640
}
16411641
16421642
void on_close(websocket_session& session, uint16_t code, const std::string& reason) override {

examples/websocket_echo.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <iostream>
2222
#include <string>
23+
#include <string_view>
2324

2425
#include <httpserver.hpp>
2526

@@ -30,9 +31,9 @@ class echo_handler : public httpserver::websocket_handler {
3031
session.send_text("Welcome to the echo server!");
3132
}
3233

33-
void on_message(httpserver::websocket_session& session, const std::string& msg) override {
34+
void on_message(httpserver::websocket_session& session, std::string_view msg) override {
3435
std::cout << "Received: " << msg << std::endl;
35-
session.send_text("Echo: " + msg);
36+
session.send_text("Echo: " + std::string(msg));
3637
}
3738

3839
void on_close(httpserver::websocket_session& session, uint16_t code, const std::string& reason) override {

src/http_request.cpp

Lines changed: 103 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,23 @@ class scoped_x509_cert {
7171
bool is_valid() const { return valid_; }
7272
gnutls_x509_crt_t get() const { return cert_; }
7373

74+
// Movable
75+
scoped_x509_cert(scoped_x509_cert&& other) noexcept
76+
: cert_(other.cert_), valid_(other.valid_) {
77+
other.cert_ = nullptr;
78+
other.valid_ = false;
79+
}
80+
scoped_x509_cert& operator=(scoped_x509_cert&& other) noexcept {
81+
if (this != &other) {
82+
if (cert_ != nullptr) gnutls_x509_crt_deinit(cert_);
83+
cert_ = other.cert_;
84+
valid_ = other.valid_;
85+
other.cert_ = nullptr;
86+
other.valid_ = false;
87+
}
88+
return *this;
89+
}
90+
7491
// Non-copyable
7592
scoped_x509_cert(const scoped_x509_cert&) = delete;
7693
scoped_x509_cert& operator=(const scoped_x509_cert&) = delete;
@@ -387,156 +404,126 @@ bool http_request::has_client_certificate() const {
387404
return (cert_list != nullptr && list_size > 0);
388405
}
389406

390-
std::string http_request::get_client_cert_dn() const {
391-
if (!has_tls_session()) {
392-
return "";
393-
}
394-
395-
scoped_x509_cert cert;
396-
if (!cert.init_from_session(get_tls_session())) {
397-
return "";
398-
}
399-
400-
size_t dn_size = 0;
401-
gnutls_x509_crt_get_dn(cert.get(), nullptr, &dn_size);
402-
403-
std::string dn(dn_size, '\0');
404-
if (gnutls_x509_crt_get_dn(cert.get(), &dn[0], &dn_size) != GNUTLS_E_SUCCESS) {
405-
return "";
407+
void http_request::populate_all_cert_fields() const {
408+
if (cache->client_cert_fields_cached) {
409+
return;
406410
}
407411

408-
// Remove trailing null if present
409-
if (!dn.empty() && dn.back() == '\0') {
410-
dn.pop_back();
411-
}
412+
cache->client_cert_fields_cached = true;
412413

413-
return dn;
414-
}
415-
416-
std::string http_request::get_client_cert_issuer_dn() const {
417-
if (!has_tls_session()) {
418-
return "";
414+
gnutls_session_t session = nullptr;
415+
if (has_tls_session()) {
416+
session = get_tls_session();
419417
}
420418

421419
scoped_x509_cert cert;
422-
if (!cert.init_from_session(get_tls_session())) {
423-
return "";
424-
}
425-
426-
size_t dn_size = 0;
427-
gnutls_x509_crt_get_issuer_dn(cert.get(), nullptr, &dn_size);
428-
429-
std::string dn(dn_size, '\0');
430-
if (gnutls_x509_crt_get_issuer_dn(cert.get(), &dn[0], &dn_size) != GNUTLS_E_SUCCESS) {
431-
return "";
420+
if (session != nullptr) {
421+
cert.init_from_session(session);
432422
}
433423

434-
// Remove trailing null if present
435-
if (!dn.empty() && dn.back() == '\0') {
436-
dn.pop_back();
424+
if (!cert.is_valid()) {
425+
// Default values (empty strings and -1) are already set by the
426+
// cache struct initializers; client_cert_verified defaults to false.
427+
return;
437428
}
438429

439-
return dn;
440-
}
441-
442-
std::string http_request::get_client_cert_cn() const {
443-
if (!has_tls_session()) {
444-
return "";
430+
// Client certificate verification
431+
{
432+
unsigned int status = 0;
433+
if (gnutls_certificate_verify_peers2(session, &status) == GNUTLS_E_SUCCESS) {
434+
cache->client_cert_verified = (status == 0);
435+
}
445436
}
446437

447-
scoped_x509_cert cert;
448-
if (!cert.init_from_session(get_tls_session())) {
449-
return "";
438+
// Subject DN
439+
{
440+
size_t dn_size = 0;
441+
gnutls_x509_crt_get_dn(cert.get(), nullptr, &dn_size);
442+
std::string dn(dn_size, '\0');
443+
if (gnutls_x509_crt_get_dn(cert.get(), &dn[0], &dn_size) == GNUTLS_E_SUCCESS) {
444+
if (!dn.empty() && dn.back() == '\0') dn.pop_back();
445+
cache->client_cert_dn = dn;
446+
}
450447
}
451448

452-
size_t cn_size = 0;
453-
gnutls_x509_crt_get_dn_by_oid(cert.get(), GNUTLS_OID_X520_COMMON_NAME, 0, 0, nullptr, &cn_size);
454-
455-
if (cn_size == 0) {
456-
return "";
449+
// Issuer DN
450+
{
451+
size_t dn_size = 0;
452+
gnutls_x509_crt_get_issuer_dn(cert.get(), nullptr, &dn_size);
453+
std::string dn(dn_size, '\0');
454+
if (gnutls_x509_crt_get_issuer_dn(cert.get(), &dn[0], &dn_size) == GNUTLS_E_SUCCESS) {
455+
if (!dn.empty() && dn.back() == '\0') dn.pop_back();
456+
cache->client_cert_issuer_dn = dn;
457+
}
457458
}
458459

459-
std::string cn(cn_size, '\0');
460-
if (gnutls_x509_crt_get_dn_by_oid(cert.get(), GNUTLS_OID_X520_COMMON_NAME, 0, 0, &cn[0], &cn_size) != GNUTLS_E_SUCCESS) {
461-
return "";
460+
// Common Name
461+
{
462+
size_t cn_size = 0;
463+
gnutls_x509_crt_get_dn_by_oid(cert.get(), GNUTLS_OID_X520_COMMON_NAME, 0, 0, nullptr, &cn_size);
464+
if (cn_size > 0) {
465+
std::string cn(cn_size, '\0');
466+
if (gnutls_x509_crt_get_dn_by_oid(cert.get(), GNUTLS_OID_X520_COMMON_NAME, 0, 0, &cn[0], &cn_size) == GNUTLS_E_SUCCESS) {
467+
if (!cn.empty() && cn.back() == '\0') cn.pop_back();
468+
cache->client_cert_cn = cn;
469+
}
470+
}
462471
}
463472

464-
// Remove trailing null if present
465-
if (!cn.empty() && cn.back() == '\0') {
466-
cn.pop_back();
473+
// SHA-256 fingerprint
474+
{
475+
unsigned char fingerprint[32];
476+
size_t fingerprint_size = sizeof(fingerprint);
477+
if (gnutls_x509_crt_get_fingerprint(cert.get(), GNUTLS_DIG_SHA256, fingerprint, &fingerprint_size) == GNUTLS_E_SUCCESS) {
478+
std::string hex_fingerprint;
479+
hex_fingerprint.reserve(fingerprint_size * 2);
480+
for (size_t i = 0; i < fingerprint_size; ++i) {
481+
char hex[3];
482+
snprintf(hex, sizeof(hex), "%02x", fingerprint[i]);
483+
hex_fingerprint += hex;
484+
}
485+
cache->client_cert_fingerprint_sha256 = hex_fingerprint;
486+
}
467487
}
468488

469-
return cn;
489+
// Validity times
490+
cache->client_cert_not_before = gnutls_x509_crt_get_activation_time(cert.get());
491+
cache->client_cert_not_after = gnutls_x509_crt_get_expiration_time(cert.get());
470492
}
471493

472-
bool http_request::is_client_cert_verified() const {
473-
if (!has_tls_session()) {
474-
return false;
475-
}
494+
std::string http_request::get_client_cert_dn() const {
495+
populate_all_cert_fields();
496+
return cache->client_cert_dn;
497+
}
476498

477-
gnutls_session_t session = get_tls_session();
478-
unsigned int status = 0;
499+
std::string http_request::get_client_cert_issuer_dn() const {
500+
populate_all_cert_fields();
501+
return cache->client_cert_issuer_dn;
502+
}
479503

480-
if (gnutls_certificate_verify_peers2(session, &status) != GNUTLS_E_SUCCESS) {
481-
return false;
482-
}
504+
std::string http_request::get_client_cert_cn() const {
505+
populate_all_cert_fields();
506+
return cache->client_cert_cn;
507+
}
483508

484-
return (status == 0);
509+
bool http_request::is_client_cert_verified() const {
510+
populate_all_cert_fields();
511+
return cache->client_cert_verified;
485512
}
486513

487514
std::string http_request::get_client_cert_fingerprint_sha256() const {
488-
if (!has_tls_session()) {
489-
return "";
490-
}
491-
492-
scoped_x509_cert cert;
493-
if (!cert.init_from_session(get_tls_session())) {
494-
return "";
495-
}
496-
497-
unsigned char fingerprint[32]; // SHA-256 is 32 bytes
498-
size_t fingerprint_size = sizeof(fingerprint);
499-
500-
if (gnutls_x509_crt_get_fingerprint(cert.get(), GNUTLS_DIG_SHA256, fingerprint, &fingerprint_size) != GNUTLS_E_SUCCESS) {
501-
return "";
502-
}
503-
504-
// Convert to hex string
505-
std::string hex_fingerprint;
506-
hex_fingerprint.reserve(fingerprint_size * 2);
507-
for (size_t i = 0; i < fingerprint_size; ++i) {
508-
char hex[3];
509-
snprintf(hex, sizeof(hex), "%02x", fingerprint[i]);
510-
hex_fingerprint += hex;
511-
}
512-
513-
return hex_fingerprint;
515+
populate_all_cert_fields();
516+
return cache->client_cert_fingerprint_sha256;
514517
}
515518

516519
time_t http_request::get_client_cert_not_before() const {
517-
if (!has_tls_session()) {
518-
return -1;
519-
}
520-
521-
scoped_x509_cert cert;
522-
if (!cert.init_from_session(get_tls_session())) {
523-
return -1;
524-
}
525-
526-
return gnutls_x509_crt_get_activation_time(cert.get());
520+
populate_all_cert_fields();
521+
return cache->client_cert_not_before;
527522
}
528523

529524
time_t http_request::get_client_cert_not_after() const {
530-
if (!has_tls_session()) {
531-
return -1;
532-
}
533-
534-
scoped_x509_cert cert;
535-
if (!cert.init_from_session(get_tls_session())) {
536-
return -1;
537-
}
538-
539-
return gnutls_x509_crt_get_expiration_time(cert.get());
525+
populate_all_cert_fields();
526+
return cache->client_cert_not_after;
540527
}
541528
#endif // HAVE_GNUTLS
542529

src/http_utils.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,8 +580,8 @@ const char* http_utils::reason_phrase(unsigned int status_code) {
580580
return MHD_get_reason_phrase_for(status_code);
581581
}
582582

583-
bool http_utils::is_feature_supported(int feature) {
584-
return MHD_is_feature_supported(static_cast<MHD_FEATURE>(feature)) == MHD_YES;
583+
bool http_utils::is_feature_supported(enum MHD_FEATURE feature) {
584+
return MHD_is_feature_supported(feature) == MHD_YES;
585585
}
586586

587587
const char* http_utils::get_mhd_version() {

src/httpserver/http_request.hpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <limits>
3939
#include <map>
4040
#include <memory>
41+
4142
#include <string>
4243
#include <utility>
4344
#include <vector>
@@ -320,7 +321,7 @@ class http_request {
320321
size_t userdigest_size,
321322
unsigned int nonce_timeout = 0,
322323
uint32_t max_nc = 0,
323-
http::http_utils::digest_algorithm algo = http::http_utils::digest_algorithm::MD5) const;
324+
http::http_utils::digest_algorithm algo = http::http_utils::digest_algorithm::SHA256) const;
324325
#endif // HAVE_DAUTH
325326

326327
friend std::ostream &operator<< (std::ostream &os, http_request &r);
@@ -382,6 +383,10 @@ class http_request {
382383
void fetch_user_pass() const;
383384
#endif // HAVE_BAUTH
384385

386+
#ifdef HAVE_GNUTLS
387+
void populate_all_cert_fields() const;
388+
#endif // HAVE_GNUTLS
389+
385390
/**
386391
* Method used to set an argument value by key.
387392
* @param key The name identifying the argument
@@ -499,6 +504,17 @@ class http_request {
499504

500505
bool args_populated = false;
501506
bool path_pieces_cached = false;
507+
508+
#ifdef HAVE_GNUTLS
509+
bool client_cert_fields_cached = false;
510+
std::string client_cert_dn;
511+
std::string client_cert_issuer_dn;
512+
std::string client_cert_cn;
513+
std::string client_cert_fingerprint_sha256;
514+
time_t client_cert_not_before = static_cast<time_t>(-1);
515+
time_t client_cert_not_after = static_cast<time_t>(-1);
516+
bool client_cert_verified = false;
517+
#endif // HAVE_GNUTLS
502518
};
503519
std::unique_ptr<http_request_data_cache> cache = std::make_unique<http_request_data_cache>();
504520
void ensure_path_pieces_cached() const {

src/httpserver/http_utils.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ class http_utils {
291291
static std::string sanitize_upload_filename(const std::string& filename);
292292

293293
static const char* reason_phrase(unsigned int status_code);
294-
static bool is_feature_supported(int feature);
294+
static bool is_feature_supported(enum MHD_FEATURE feature);
295295
static const char* get_mhd_version();
296296
};
297297

0 commit comments

Comments
 (0)