3131
3232#ifdef HAVE_GNUTLS
3333#include < gnutls/x509.h>
34+
35+ // RAII wrapper for gnutls_x509_crt_t to ensure proper cleanup
36+ class scoped_x509_cert {
37+ public:
38+ scoped_x509_cert () : cert_(nullptr ), valid_(false ) {}
39+
40+ ~scoped_x509_cert () {
41+ if (cert_ != nullptr ) {
42+ gnutls_x509_crt_deinit (cert_);
43+ }
44+ }
45+
46+ // Initialize from a TLS session's peer certificate
47+ // Returns true if certificate was successfully loaded
48+ bool init_from_session (gnutls_session_t session) {
49+ unsigned int list_size = 0 ;
50+ const gnutls_datum_t * cert_list = gnutls_certificate_get_peers (session, &list_size);
51+
52+ if (cert_list == nullptr || list_size == 0 ) {
53+ return false ;
54+ }
55+
56+ if (gnutls_x509_crt_init (&cert_) != GNUTLS_E_SUCCESS) {
57+ cert_ = nullptr ;
58+ return false ;
59+ }
60+
61+ if (gnutls_x509_crt_import (cert_, &cert_list[0 ], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
62+ gnutls_x509_crt_deinit (cert_);
63+ cert_ = nullptr ;
64+ return false ;
65+ }
66+
67+ valid_ = true ;
68+ return true ;
69+ }
70+
71+ bool is_valid () const { return valid_; }
72+ gnutls_x509_crt_t get () const { return cert_; }
73+
74+ // Non-copyable
75+ scoped_x509_cert (const scoped_x509_cert&) = delete ;
76+ scoped_x509_cert& operator =(const scoped_x509_cert&) = delete ;
77+
78+ private:
79+ gnutls_x509_crt_t cert_;
80+ bool valid_;
81+ };
3482#endif // HAVE_GNUTLS
3583
3684namespace httpserver {
@@ -319,6 +367,10 @@ bool http_request::has_tls_session() const {
319367gnutls_session_t http_request::get_tls_session () const {
320368 const MHD_ConnectionInfo * conninfo = MHD_get_connection_info (underlying_connection, MHD_CONNECTION_INFO_GNUTLS_SESSION);
321369
370+ if (conninfo == nullptr ) {
371+ return nullptr ;
372+ }
373+
322374 return static_cast <gnutls_session_t >(conninfo->tls_session );
323375}
324376
@@ -335,35 +387,23 @@ bool http_request::has_client_certificate() const {
335387}
336388
337389std::string http_request::get_client_cert_dn () const {
338- if (!has_client_certificate ()) {
339- return " " ;
340- }
341-
342- gnutls_session_t session = get_tls_session ();
343- unsigned int list_size = 0 ;
344- const gnutls_datum_t * cert_list = gnutls_certificate_get_peers (session, &list_size);
345-
346- gnutls_x509_crt_t cert;
347- if (gnutls_x509_crt_init (&cert) != GNUTLS_E_SUCCESS) {
390+ if (!has_tls_session ()) {
348391 return " " ;
349392 }
350393
351- if ( gnutls_x509_crt_import ( cert, &cert_list[ 0 ], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
352- gnutls_x509_crt_deinit ( cert);
394+ scoped_x509_cert cert;
395+ if (! cert. init_from_session ( get_tls_session ())) {
353396 return " " ;
354397 }
355398
356399 size_t dn_size = 0 ;
357- gnutls_x509_crt_get_dn (cert, nullptr , &dn_size);
400+ gnutls_x509_crt_get_dn (cert. get () , nullptr , &dn_size);
358401
359402 std::string dn (dn_size, ' \0 ' );
360- if (gnutls_x509_crt_get_dn (cert, &dn[0 ], &dn_size) != GNUTLS_E_SUCCESS) {
361- gnutls_x509_crt_deinit (cert);
403+ if (gnutls_x509_crt_get_dn (cert.get (), &dn[0 ], &dn_size) != GNUTLS_E_SUCCESS) {
362404 return " " ;
363405 }
364406
365- gnutls_x509_crt_deinit (cert);
366-
367407 // Remove trailing null if present
368408 if (!dn.empty () && dn.back () == ' \0 ' ) {
369409 dn.pop_back ();
@@ -373,35 +413,23 @@ std::string http_request::get_client_cert_dn() const {
373413}
374414
375415std::string http_request::get_client_cert_issuer_dn () const {
376- if (!has_client_certificate ()) {
377- return " " ;
378- }
379-
380- gnutls_session_t session = get_tls_session ();
381- unsigned int list_size = 0 ;
382- const gnutls_datum_t * cert_list = gnutls_certificate_get_peers (session, &list_size);
383-
384- gnutls_x509_crt_t cert;
385- if (gnutls_x509_crt_init (&cert) != GNUTLS_E_SUCCESS) {
416+ if (!has_tls_session ()) {
386417 return " " ;
387418 }
388419
389- if ( gnutls_x509_crt_import ( cert, &cert_list[ 0 ], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
390- gnutls_x509_crt_deinit ( cert);
420+ scoped_x509_cert cert;
421+ if (! cert. init_from_session ( get_tls_session ())) {
391422 return " " ;
392423 }
393424
394425 size_t dn_size = 0 ;
395- gnutls_x509_crt_get_issuer_dn (cert, nullptr , &dn_size);
426+ gnutls_x509_crt_get_issuer_dn (cert. get () , nullptr , &dn_size);
396427
397428 std::string dn (dn_size, ' \0 ' );
398- if (gnutls_x509_crt_get_issuer_dn (cert, &dn[0 ], &dn_size) != GNUTLS_E_SUCCESS) {
399- gnutls_x509_crt_deinit (cert);
429+ if (gnutls_x509_crt_get_issuer_dn (cert.get (), &dn[0 ], &dn_size) != GNUTLS_E_SUCCESS) {
400430 return " " ;
401431 }
402432
403- gnutls_x509_crt_deinit (cert);
404-
405433 // Remove trailing null if present
406434 if (!dn.empty () && dn.back () == ' \0 ' ) {
407435 dn.pop_back ();
@@ -411,40 +439,27 @@ std::string http_request::get_client_cert_issuer_dn() const {
411439}
412440
413441std::string http_request::get_client_cert_cn () const {
414- if (!has_client_certificate ()) {
415- return " " ;
416- }
417-
418- gnutls_session_t session = get_tls_session ();
419- unsigned int list_size = 0 ;
420- const gnutls_datum_t * cert_list = gnutls_certificate_get_peers (session, &list_size);
421-
422- gnutls_x509_crt_t cert;
423- if (gnutls_x509_crt_init (&cert) != GNUTLS_E_SUCCESS) {
442+ if (!has_tls_session ()) {
424443 return " " ;
425444 }
426445
427- if ( gnutls_x509_crt_import ( cert, &cert_list[ 0 ], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
428- gnutls_x509_crt_deinit ( cert);
446+ scoped_x509_cert cert;
447+ if (! cert. init_from_session ( get_tls_session ())) {
429448 return " " ;
430449 }
431450
432451 size_t cn_size = 0 ;
433- gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0 , 0 , nullptr , &cn_size);
452+ gnutls_x509_crt_get_dn_by_oid (cert. get () , GNUTLS_OID_X520_COMMON_NAME, 0 , 0 , nullptr , &cn_size);
434453
435454 if (cn_size == 0 ) {
436- gnutls_x509_crt_deinit (cert);
437455 return " " ;
438456 }
439457
440458 std::string cn (cn_size, ' \0 ' );
441- if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0 , 0 , &cn[0 ], &cn_size) != GNUTLS_E_SUCCESS) {
442- gnutls_x509_crt_deinit (cert);
459+ if (gnutls_x509_crt_get_dn_by_oid (cert.get (), GNUTLS_OID_X520_COMMON_NAME, 0 , 0 , &cn[0 ], &cn_size) != GNUTLS_E_SUCCESS) {
443460 return " " ;
444461 }
445462
446- gnutls_x509_crt_deinit (cert);
447-
448463 // Remove trailing null if present
449464 if (!cn.empty () && cn.back () == ' \0 ' ) {
450465 cn.pop_back ();
@@ -469,34 +484,22 @@ bool http_request::is_client_cert_verified() const {
469484}
470485
471486std::string http_request::get_client_cert_fingerprint_sha256 () const {
472- if (!has_client_certificate ()) {
473- return " " ;
474- }
475-
476- gnutls_session_t session = get_tls_session ();
477- unsigned int list_size = 0 ;
478- const gnutls_datum_t * cert_list = gnutls_certificate_get_peers (session, &list_size);
479-
480- gnutls_x509_crt_t cert;
481- if (gnutls_x509_crt_init (&cert) != GNUTLS_E_SUCCESS) {
487+ if (!has_tls_session ()) {
482488 return " " ;
483489 }
484490
485- if ( gnutls_x509_crt_import ( cert, &cert_list[ 0 ], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
486- gnutls_x509_crt_deinit ( cert);
491+ scoped_x509_cert cert;
492+ if (! cert. init_from_session ( get_tls_session ())) {
487493 return " " ;
488494 }
489495
490496 unsigned char fingerprint[32 ]; // SHA-256 is 32 bytes
491497 size_t fingerprint_size = sizeof (fingerprint);
492498
493- if (gnutls_x509_crt_get_fingerprint (cert, GNUTLS_DIG_SHA256, fingerprint, &fingerprint_size) != GNUTLS_E_SUCCESS) {
494- gnutls_x509_crt_deinit (cert);
499+ if (gnutls_x509_crt_get_fingerprint (cert.get (), GNUTLS_DIG_SHA256, fingerprint, &fingerprint_size) != GNUTLS_E_SUCCESS) {
495500 return " " ;
496501 }
497502
498- gnutls_x509_crt_deinit (cert);
499-
500503 // Convert to hex string
501504 std::string hex_fingerprint;
502505 hex_fingerprint.reserve (fingerprint_size * 2 );
@@ -510,53 +513,29 @@ std::string http_request::get_client_cert_fingerprint_sha256() const {
510513}
511514
512515time_t http_request::get_client_cert_not_before () const {
513- if (!has_client_certificate ()) {
514- return -1 ;
515- }
516-
517- gnutls_session_t session = get_tls_session ();
518- unsigned int list_size = 0 ;
519- const gnutls_datum_t * cert_list = gnutls_certificate_get_peers (session, &list_size);
520-
521- gnutls_x509_crt_t cert;
522- if (gnutls_x509_crt_init (&cert) != GNUTLS_E_SUCCESS) {
516+ if (!has_tls_session ()) {
523517 return -1 ;
524518 }
525519
526- if ( gnutls_x509_crt_import ( cert, &cert_list[ 0 ], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
527- gnutls_x509_crt_deinit ( cert);
520+ scoped_x509_cert cert;
521+ if (! cert. init_from_session ( get_tls_session ())) {
528522 return -1 ;
529523 }
530524
531- time_t not_before = gnutls_x509_crt_get_activation_time (cert);
532- gnutls_x509_crt_deinit (cert);
533-
534- return not_before;
525+ return gnutls_x509_crt_get_activation_time (cert.get ());
535526}
536527
537528time_t http_request::get_client_cert_not_after () const {
538- if (!has_client_certificate ()) {
539- return -1 ;
540- }
541-
542- gnutls_session_t session = get_tls_session ();
543- unsigned int list_size = 0 ;
544- const gnutls_datum_t * cert_list = gnutls_certificate_get_peers (session, &list_size);
545-
546- gnutls_x509_crt_t cert;
547- if (gnutls_x509_crt_init (&cert) != GNUTLS_E_SUCCESS) {
529+ if (!has_tls_session ()) {
548530 return -1 ;
549531 }
550532
551- if ( gnutls_x509_crt_import ( cert, &cert_list[ 0 ], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
552- gnutls_x509_crt_deinit ( cert);
533+ scoped_x509_cert cert;
534+ if (! cert. init_from_session ( get_tls_session ())) {
553535 return -1 ;
554536 }
555537
556- time_t not_after = gnutls_x509_crt_get_expiration_time (cert);
557- gnutls_x509_crt_deinit (cert);
558-
559- return not_after;
538+ return gnutls_x509_crt_get_expiration_time (cert.get ());
560539}
561540#endif // HAVE_GNUTLS
562541
0 commit comments