Ring Daemon
Loading...
Searching...
No Matches
tlsvalidator.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2026 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "tlsvalidator.h"
19
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif
23
24#include <opendht/infohash.h> // for toHex
25#include <dhtnet/certstore.h>
26
27#include "fileutils.h"
28#include "logger.h"
29#include "security_const.h"
30#include "string_utils.h"
31
32#include <sstream>
33
34#include <cstdio>
35#include <cerrno>
36#include <cassert>
37#include <ctime>
38
39#include <sys/types.h>
40#include <sys/stat.h>
41
42#ifndef _MSC_VER
43#include <libgen.h>
44#endif
45
46#ifndef _WIN32
47#include <sys/socket.h>
48#include <netinet/tcp.h>
49#include <netinet/in.h>
50#include <netdb.h>
51#else
52#ifndef _MSC_VER
53#define close(x) closesocket(x)
54#endif
55#endif
56#include <unistd.h>
57#include <fcntl.h>
58
59#ifdef _MSC_VER
60#include "windirent.h"
61#endif
62
63#include <filesystem>
64
65namespace jami {
66namespace tls {
67
68// Map the internal ring Enum class of the exported names
69
70const EnumClassNames<TlsValidator::CheckValues> TlsValidator::CheckValuesNames = {{
71 /* CheckValues Name */
78}};
79
81 TlsValidator::checkCallback = {{
82 /* CertificateCheck Callback */
83 /*HAS_PRIVATE_KEY */ &TlsValidator::hasPrivateKey,
84 /*EXPIRED */ &TlsValidator::notExpired,
85 /*STRONG_SIGNING */ &TlsValidator::strongSigning,
86 /*NOT_SELF_SIGNED */ &TlsValidator::notSelfSigned,
87 /*KEY_MATCH */ &TlsValidator::keyMatch,
88 /*PRIVATE_KEY_STORAGE_PERMISSION */ &TlsValidator::privateKeyStoragePermissions,
89 /*PUBLIC_KEY_STORAGE_PERMISSION */ &TlsValidator::publicKeyStoragePermissions,
90 /*PRIVATEKEY_DIRECTORY_PERMISSIONS */ &TlsValidator::privateKeyDirectoryPermissions,
91 /*PUBLICKEY_DIRECTORY_PERMISSIONS */ &TlsValidator::publicKeyDirectoryPermissions,
92 /*PRIVATE_KEY_STORAGE_LOCATION */ &TlsValidator::privateKeyStorageLocation,
93 /*PUBLIC_KEY_STORAGE_LOCATION */ &TlsValidator::publicKeyStorageLocation,
94 /*PRIVATE_KEY_SELINUX_ATTRIBUTES */ &TlsValidator::privateKeySelinuxAttributes,
95 /*PUBLIC_KEY_SELINUX_ATTRIBUTES */ &TlsValidator::publicKeySelinuxAttributes,
96 /*EXIST */ &TlsValidator::exist,
97 /*VALID */ &TlsValidator::valid,
98 /*VALID_AUTHORITY */ &TlsValidator::validAuthority,
99 /*KNOWN_AUTHORITY */ &TlsValidator::knownAuthority,
100 /*NOT_REVOKED */ &TlsValidator::notRevoked,
101 /*AUTHORITY_MISMATCH */ &TlsValidator::authorityMatch,
102 /*UNEXPECTED_OWNER */ &TlsValidator::expectedOwner,
103 /*NOT_ACTIVATED */ &TlsValidator::activated,
104 }};
105
107 TlsValidator::getterCallback = {{
108 /* EXPIRATION_DATE */ &TlsValidator::getExpirationDate,
109 /* ACTIVATION_DATE */ &TlsValidator::getActivationDate,
110 /* REQUIRE_PRIVATE_KEY_PASSWORD */ &TlsValidator::requirePrivateKeyPassword,
111 /* PUBLIC_SIGNATURE */ &TlsValidator::getPublicSignature,
112 /* VERSION_NUMBER */ &TlsValidator::getVersionNumber,
113 /* SERIAL_NUMBER */ &TlsValidator::getSerialNumber,
114 /* ISSUER */ &TlsValidator::getIssuer,
115 /* SUBJECT_KEY_ALGORITHM */ &TlsValidator::getSubjectKeyAlgorithm,
116 /* SUBJECT_KEY */ &TlsValidator::getSubjectKey,
117 /* CN */ &TlsValidator::getCN,
118 /* UID */ &TlsValidator::getUID,
119 /* N */ &TlsValidator::getN,
120 /* O */ &TlsValidator::getO,
121 /* SIGNATURE_ALGORITHM */ &TlsValidator::getSignatureAlgorithm,
122 /* MD5_FINGERPRINT */ &TlsValidator::getMd5Fingerprint,
123 /* SHA1_FINGERPRINT */ &TlsValidator::getSha1Fingerprint,
124 /* PUBLIC_KEY_ID */ &TlsValidator::getPublicKeyId,
125 /* ISSUER_DN */ &TlsValidator::getIssuerDN,
126 /* ISSUER_CN */ &TlsValidator::getIssuerCN,
127 /* ISSUER_UID */ &TlsValidator::getIssuerUID,
128 /* ISSUER_N */ &TlsValidator::getIssuerN,
129 /* ISSUER_O */ &TlsValidator::getIssuerO,
130 /* NEXT_EXPECTED_UPDATE_DATE */ &TlsValidator::getIssuerDN, // TODO
131 /* OUTGOING_SERVER */ &TlsValidator::outgoingServer,
132 /* IS_CA */ &TlsValidator::isCA,
133 }};
134
136 /* CertificateCheck Callback */
137 /*HAS_PRIVATE_KEY */ CheckValuesType::BOOLEAN,
138 /*EXPIRED */ CheckValuesType::BOOLEAN,
139 /*STRONG_SIGNING */ CheckValuesType::BOOLEAN,
140 /*NOT_SELF_SIGNED */ CheckValuesType::BOOLEAN,
141 /*KEY_MATCH */ CheckValuesType::BOOLEAN,
142 /*PRIVATE_KEY_STORAGE_PERMISSION */ CheckValuesType::BOOLEAN,
143 /*PUBLIC_KEY_STORAGE_PERMISSION */ CheckValuesType::BOOLEAN,
144 /*PRIVATEKEY_DIRECTORY_PERMISSIONS */ CheckValuesType::BOOLEAN,
145 /*PUBLICKEY_DIRECTORY_PERMISSIONS */ CheckValuesType::BOOLEAN,
146 /*PRIVATE_KEY_STORAGE_LOCATION */ CheckValuesType::BOOLEAN,
147 /*PUBLIC_KEY_STORAGE_LOCATION */ CheckValuesType::BOOLEAN,
148 /*PRIVATE_KEY_SELINUX_ATTRIBUTES */ CheckValuesType::BOOLEAN,
149 /*PUBLIC_KEY_SELINUX_ATTRIBUTES */ CheckValuesType::BOOLEAN,
150 /*EXIST */ CheckValuesType::BOOLEAN,
151 /*VALID */ CheckValuesType::BOOLEAN,
152 /*VALID_AUTHORITY */ CheckValuesType::BOOLEAN,
153 /*KNOWN_AUTHORITY */ CheckValuesType::BOOLEAN,
154 /*NOT_REVOKED */ CheckValuesType::BOOLEAN,
155 /*AUTHORITY_MISMATCH */ CheckValuesType::BOOLEAN,
156 /*UNEXPECTED_OWNER */ CheckValuesType::BOOLEAN,
157 /*NOT_ACTIVATED */ CheckValuesType::BOOLEAN,
158}};
159
160const EnumClassNames<TlsValidator::CertificateCheck> TlsValidator::CertificateCheckNames = {{
161 /* CertificateCheck Name */
183}};
184
185const EnumClassNames<TlsValidator::CertificateDetails> TlsValidator::CertificateDetailsNames = {{
211
212}};
213
214const EnumClassNames<const TlsValidator::CheckValuesType> TlsValidator::CheckValuesTypeNames = {{
215 /* Type Name */
220}};
221
222const Matrix2D<TlsValidator::CheckValuesType, TlsValidator::CheckValues, bool> TlsValidator::acceptedCheckValuesResult = {
223 {
224 /* Type PASSED FAILED UNSUPPORTED ISO_DATE CUSTOM NUMBER */
225 /* BOOLEAN */ {{true, true, true, false, false, false}},
226 /* ISO_DATE */ {{false, false, true, true, false, false}},
227 /* CUSTOM */ {{false, false, true, false, true, false}},
228 /* NUMBER */ {{false, false, true, false, false, true}},
229 }};
230
231TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore,
232 const std::vector<std::vector<uint8_t>>& crtChain)
233 : TlsValidator(certStore, std::make_shared<dht::crypto::Certificate>(crtChain.begin(), crtChain.end()))
234{}
235
236TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore,
237 const std::string& certificate,
238 const std::string& privatekey,
239 const std::string& privatekeyPasswd,
240 const std::string& caList)
241 : certStore_(certStore)
242 , certificatePath_(certificate)
243 , privateKeyPath_(privatekey)
244 , caListPath_(caList)
245 , certificateFound_(false)
246{
247 std::vector<uint8_t> certificate_raw;
248 try {
249 certificate_raw = fileutils::loadFile(certificatePath_);
250 certificateFileFound_ = true;
251 } catch (const std::exception& e) {
252 }
253
254 if (not certificate_raw.empty()) {
255 try {
256 x509crt_ = std::make_shared<dht::crypto::Certificate>(certificate_raw);
257 certificateContent_ = x509crt_->getPacked();
258 certificateFound_ = true;
259 } catch (const std::exception& e) {
260 }
261 }
262
263 try {
264 auto privateKeyContent = fileutils::loadFile(privateKeyPath_);
265 dht::crypto::PrivateKey key_tmp(privateKeyContent, privatekeyPasswd);
266 privateKeyFound_ = true;
267 privateKeyPassword_ = not privatekeyPasswd.empty();
268 privateKeyMatch_ = key_tmp.getPublicKey().getId() == x509crt_->getId();
269 } catch (const dht::crypto::DecryptError& d) {
270 // If we encounter a DecryptError, it means the private key exists and is encrypted,
271 // otherwise we would get some other exception.
272 JAMI_WARN("decryption error: %s", d.what());
273 privateKeyFound_ = true;
274 privateKeyPassword_ = true;
275 } catch (const std::exception& e) {
276 JAMI_WARN("creation failed: %s", e.what());
277 }
278}
279
280TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::vector<uint8_t>& certificate_raw)
281 : certStore_(certStore)
282{
283 try {
284 x509crt_ = std::make_shared<dht::crypto::Certificate>(certificate_raw);
285 certificateContent_ = x509crt_->getPacked();
286 certificateFound_ = true;
287 } catch (const std::exception& e) {
288 throw TlsValidatorException("Unable to load certificate");
289 }
290}
291
292TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore,
293 const std::shared_ptr<dht::crypto::Certificate>& crt)
294 : certStore_(certStore)
295 , certificateFound_(true)
296{
297 try {
298 if (not crt)
299 throw std::invalid_argument("Certificate must be set");
300 x509crt_ = crt;
301 certificateContent_ = x509crt_->getPacked();
302 } catch (const std::exception& e) {
303 throw TlsValidatorException("Unable to load certificate");
304 }
305}
306
308
314std::string
315TlsValidator::getStringValue([[maybe_unused]] const TlsValidator::CertificateCheck check,
316 const TlsValidator::CheckResult& result)
317{
318 assert(acceptedCheckValuesResult[enforcedCheckType[check]][result.first]);
319
320 switch (result.first) {
324 return std::string(CheckValuesNames[result.first]);
326 // TODO validate date
327 // return CheckValues::FAILED;
328 return result.second;
330 // TODO Validate numbers
332 return result.second;
333 default:
334 // Consider any other case (such as forced int->CheckValues casting) as failed
335 return std::string(CheckValuesNames[CheckValues::FAILED]);
336 };
337}
338
345bool
347{
349 if (enforcedCheckType[check] == CheckValuesType::BOOLEAN) {
350 if (((this->*(checkCallback[check]))()).first == CheckValues::FAILED) {
351 if (verbose)
352 JAMI_WARNING("Check failed: {}", CertificateCheckNames[check]);
353 return false;
354 }
355 }
356 }
357 return true;
358}
359
363std::map<std::string, std::string>
365{
366 std::map<std::string, std::string> ret;
367 if (not certificateFound_) {
368 // Instead of checking `certificateFound` everywhere, handle it once
369 ret[std::string(CertificateCheckNames[CertificateCheck::EXIST])] = getStringValue(CertificateCheck::EXIST,
370 exist());
371 } else {
373 ret[std::string(CertificateCheckNames[check])] = getStringValue(check, (this->*(checkCallback[check]))());
374 }
375
376 return ret;
377}
378
382std::map<std::string, std::string>
384{
385 std::map<std::string, std::string> ret;
386 if (certificateFound_) {
388 const CheckResult r = (this->*(getterCallback[det]))();
389 std::string val;
390 // TODO move this to a fuction
391 switch (r.first) {
395 val = CheckValuesNames[r.first];
396 break;
398 // TODO validate date
400 // TODO Validate numbers
402 default:
403 val = r.second;
404 break;
405 }
406 ret[std::string(CertificateDetailsNames[det])] = val;
407 }
408 }
409 return ret;
410}
411
416checkError(int err, char* copy_buffer, size_t size)
417{
418 return TlsValidator::TlsValidator::CheckResult(err == GNUTLS_E_SUCCESS ? TlsValidator::CheckValues::CUSTOM
420 err == GNUTLS_E_SUCCESS ? std::string(copy_buffer, size) : "");
421}
422
428{
429 char buffer[12];
430 struct tm* timeinfo = localtime(&time);
431 strftime(buffer, sizeof(buffer), "%F", timeinfo);
433}
434
441checkBinaryError(int err, char* copy_buffer, size_t resultSize)
442{
443 if (err == GNUTLS_E_SUCCESS)
445 dht::toHex(reinterpret_cast<uint8_t*>(copy_buffer), resultSize));
446 else
448}
449
453unsigned int
454TlsValidator::compareToCa()
455{
456 // Don't check unless the certificate changed
457 if (caChecked_)
458 return caValidationOutput_;
459
460 // build the CA trusted list
463
464 auto root_cas = certStore_.getTrustedCertificates();
465 auto err = gnutls_x509_trust_list_add_cas(trust, root_cas.data(), root_cas.size(), 0);
466 if (err)
467 JAMI_WARN("gnutls_x509_trust_list_add_cas failed: %s", gnutls_strerror(err));
468
469 if (not caListPath_.empty()) {
470 if (std::filesystem::is_directory(caListPath_))
471 gnutls_x509_trust_list_add_trust_dir(trust, caListPath_.c_str(), nullptr, GNUTLS_X509_FMT_PEM, 0, 0);
472 else
473 gnutls_x509_trust_list_add_trust_file(trust, caListPath_.c_str(), nullptr, GNUTLS_X509_FMT_PEM, 0, 0);
474 }
475
476 // build the certificate chain
477 auto crts = x509crt_->getChain();
479 crts.data(),
480 crts.size(),
481 nullptr,
482 0,
484 &caValidationOutput_,
485 nullptr);
486
488
489 if (err) {
490 JAMI_WARN("gnutls_x509_trust_list_verify_crt2 failed: %s", gnutls_strerror(err));
492 }
493
494 caChecked_ = true;
495 return caValidationOutput_;
496}
497
498#if 0 // disabled, see .h for reason
507int TlsValidator::verifyHostnameCertificate(const std::string& host, const uint16_t port)
508{
509 int err, arg, res = -1;
510 unsigned int status = (unsigned) -1;
511 const char *errptr = nullptr;
512 gnutls_session_t session = nullptr;
514 unsigned int certslen = 0;
515 const gnutls_datum_t *certs = nullptr;
516 gnutls_x509_crt_t cert = nullptr;
517
518 char buf[4096];
519 int sockfd;
520 struct sockaddr_in name;
521 struct hostent *hostinfo;
522 const int one = 1;
524 struct timeval tv;
525
526 if (!host.size() || !port) {
527 JAMI_ERR("Wrong parameters used - host %s, port %d.", host.c_str(), port);
528 return res;
529 }
530
531 /* Create the socket. */
532 sockfd = socket (PF_INET, SOCK_STREAM, 0);
533 if (sockfd < 0) {
534 JAMI_ERR("Unable to create socket.");
535 return res;
536 }
537 /* Set non-blocking so we can dected timeouts. */
538 arg = fcntl(sockfd, F_GETFL, nullptr);
539 if (arg < 0)
540 goto out;
541 arg |= O_NONBLOCK;
542 if (fcntl(sockfd, F_SETFL, arg) < 0)
543 goto out;
544
545 /* Give the socket a name. */
546 memset(&name, 0, sizeof(name));
547 name.sin_family = AF_INET;
548 name.sin_port = htons(port);
549 hostinfo = gethostbyname(host.c_str());
550 if (hostinfo == nullptr) {
551 JAMI_ERR("Unknown host %s.", host.c_str());
552 goto out;
553 }
554 name.sin_addr = *(struct in_addr *)hostinfo->h_addr;
555 /* Connect to the address specified in name struct. */
556 err = connect(sockfd, (struct sockaddr *)&name, sizeof(name));
557 if (err < 0) {
558 /* Connection in progress, use select to see if timeout is reached. */
559 if (errno == EINPROGRESS) {
560 do {
561 FD_ZERO(&fdset);
563 tv.tv_sec = 10; // 10 second timeout
564 tv.tv_usec = 0;
565 err = select(sockfd + 1, nullptr, &fdset, nullptr, &tv);
566 if (err < 0 && errno != EINTR) {
567 JAMI_ERR("Unable to connect to hostname %s at port %d",
568 host.c_str(), port);
569 goto out;
570 } else if (err > 0) {
571 /* Select returned, if so_error is clean we are ready. */
572 int so_error;
573 socklen_t len = sizeof(so_error);
575
576 if (so_error) {
577 JAMI_ERR("Connection delayed.");
578 goto out;
579 }
580 break; // exit do-while loop
581 } else {
582 JAMI_ERR("Connection timeout.");
583 goto out;
584 }
585 } while(1);
586 } else {
587 JAMI_ERR("Unable to connect to hostname %s at port %d", host.c_str(), port);
588 goto out;
589 }
590 }
591 /* Set the socked blocking again. */
592 arg = fcntl(sockfd, F_GETFL, nullptr);
593 if (arg < 0)
594 goto out;
595 arg &= ~O_NONBLOCK;
596 if (fcntl(sockfd, F_SETFL, arg) < 0)
597 goto out;
598
599 /* Disable Nagle algorithm that slows down the SSL handshake. */
601 if (err < 0) {
602 JAMI_ERR("Unable to set TCP_NODELAY.");
603 goto out;
604 }
605
606
607 /* Load the trusted CA certificates. */
609 if (err != GNUTLS_E_SUCCESS) {
610 JAMI_ERR("Unable to allocate credentials - %s", gnutls_strerror(err));
611 goto out;
612 }
614 if (err != GNUTLS_E_SUCCESS) {
615 JAMI_ERR("Unable to load credentials.");
616 goto out;
617 }
618
619 /* Create the session object. */
621 if (err != GNUTLS_E_SUCCESS) {
622 JAMI_ERR("Unable to init session -%s\n", gnutls_strerror(err));
623 goto out;
624 }
625
626 /* Configure the cipher preferences. The default set should be good enough. */
628 if (err != GNUTLS_E_SUCCESS) {
629 JAMI_ERR("Unable to set up ciphers - %s (%s)", gnutls_strerror(err), errptr);
630 goto out;
631 }
632
633 /* Install the trusted certificates. */
635 if (err != GNUTLS_E_SUCCESS) {
636 JAMI_ERR("Unable to set up credentials - %s", gnutls_strerror(err));
637 goto out;
638 }
639
640 /* Associate the socket with the session object and set the server name. */
643 if (err != GNUTLS_E_SUCCESS) {
644 JAMI_ERR("Unable to set server name - %s", gnutls_strerror(err));
645 goto out;
646 }
647
648 /* Establish the connection. */
650 if (err != GNUTLS_E_SUCCESS) {
651 JAMI_ERR("Handshake failed - %s", gnutls_strerror(err));
652 goto out;
653 }
654 /* Obtain the server certificate chain. The server certificate
655 * itself is stored in the first element of the array. */
657 if (certs == nullptr || certslen == 0) {
658 JAMI_ERR("Unable to obtain peer certificate - %s", gnutls_strerror(err));
659 goto out;
660 }
661
662 /* Validate the certificate chain. */
664 if (err != GNUTLS_E_SUCCESS) {
665 JAMI_ERR("Unable to verify the certificate chain - %s", gnutls_strerror(err));
666 goto out;
667 }
668 if (status != 0) {
670#if GNUTLS_VERSION_AT_LEAST_3_1_4
673#else
674 err = -1;
675#endif
676 if (err == 0) {
677 JAMI_ERR("Certificate validation failed - %s\n", msg.data);
678 gnutls_free(msg.data);
679 goto out;
680 } else {
681 JAMI_ERR("Certificate validation failed with code 0x%x.", status);
682 goto out;
683 }
684 }
685
686 /* Match the peer certificate against the hostname.
687 * We can only obtain a set of DER-encoded certificates from the
688 * session object, so we have to re-parse the peer certificate into
689 * a certificate object. */
690
692 if (err != GNUTLS_E_SUCCESS) {
693 JAMI_ERR("Unable to init certificate - %s", gnutls_strerror(err));
694 goto out;
695 }
696
697 /* The peer certificate is the first certificate in the list. */
699 if (err != GNUTLS_E_SUCCESS)
701 if (err != GNUTLS_E_SUCCESS) {
702 JAMI_ERR("Unable to read peer certificate - %s", gnutls_strerror(err));
703 goto out;
704 }
705 /* Finally check if the hostnames match. */
707 if (err == 0) {
708 JAMI_ERR("Hostname %s does not match certificate.", host.c_str());
709 goto out;
710 }
711
712 /* Try sending and receiving some data through. */
713 snprintf(buf, sizeof(buf), "GET / HTTP/1.0\r\nHost: %s\r\n\r\n", host.c_str());
715 if (err < 0) {
716 JAMI_ERR("Send failed - %s", gnutls_strerror(err));
717 goto out;
718 }
719 err = gnutls_record_recv(session, buf, sizeof(buf));
720 if (err < 0) {
721 JAMI_ERR("Recv failed - %s", gnutls_strerror(err));
722 goto out;
723 }
724
725 JAMI_DBG("Hostname %s seems to point to a valid server.", host.c_str());
726 res = 0;
727out:
728 if (session) {
731 }
732 if (cert)
734 if (cred)
736 close(sockfd);
737 return res;
738}
739#endif
740
746{
747 if (privateKeyFound_)
749
750 try {
751 dht::crypto::PrivateKey key_tmp(certificateContent_);
752 } catch (const std::exception& e) {
753 return CheckResult(CheckValues::FAILED, e.what());
754 }
755
756 JAMI_DBG("Key from %s seems valid.", certificatePath_.c_str());
758}
759
770{
773
774 // time_t expirationTime = gnutls_x509_crt_get_expiration_time(cert);
776 "");
777}
778
786{
789
790 // time_t activationTime = gnutls_x509_crt_get_activation_time(cert);
793 "");
794}
795
802{
805
806 // Doesn't seem to have the same value as
807 // certtool --infile /home/etudiant/Téléchargements/mynsauser.pem --key-inf
808 // TODO figure out why
811 "");
812}
813
822
836
839{
840 struct stat statbuf;
841 int err = stat(privateKeyPath_.c_str(), &statbuf);
842 if (err)
844
845 // clang-format off
847 (statbuf.st_mode & S_IFREG) && /* Regular file only */
848 /* READ WRITE EXECUTE */
849 /* Owner */ ( (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && !(statbuf.st_mode & S_IXUSR))
850 /* Group */ && (!(statbuf.st_mode & S_IRGRP) && !(statbuf.st_mode & S_IWGRP) && !(statbuf.st_mode & S_IXGRP))
851 /* Other */ && (!(statbuf.st_mode & S_IROTH) && !(statbuf.st_mode & S_IWOTH) && !(statbuf.st_mode & S_IXOTH))
853 // clang-format on
854}
855
858{
859 struct stat statbuf;
860 int err = stat(certificatePath_.c_str(), &statbuf);
861 if (err)
863
864 // clang-format off
866 (statbuf.st_mode & S_IFREG) && /* Regular file only */
867 /* READ WRITE EXECUTE */
868 /* Owner */ ( (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && !(statbuf.st_mode & S_IXUSR))
869 /* Group */ && ( /* read is not relevant */ !(statbuf.st_mode & S_IWGRP) && !(statbuf.st_mode & S_IXGRP))
870 /* Other */ && ( /* read is not relevant */ !(statbuf.st_mode & S_IWOTH) && !(statbuf.st_mode & S_IXOTH))
872 // clang-format on
873}
874
877{
878 namespace fs = std::filesystem;
879
880 if (privateKeyPath_.empty())
882
883 fs::path dir = fs::path(privateKeyPath_).parent_path();
884 if (dir.empty())
885 dir = fs::path("."); // mimic dirname() behavior when no separator
886
887 std::error_code ec;
888 auto st = fs::status(dir, ec);
889 if (ec)
891
892 if (!fs::is_directory(st))
894
895 auto perm = st.permissions();
896
897 bool ownerRead = (perm & fs::perms::owner_read) != fs::perms::none;
898 bool ownerExec = (perm & fs::perms::owner_exec) != fs::perms::none;
899 bool groupAny = (perm & (fs::perms::group_read | fs::perms::group_write | fs::perms::group_exec))
900 != fs::perms::none;
901 bool othersAny = (perm & (fs::perms::others_read | fs::perms::others_write | fs::perms::others_exec))
902 != fs::perms::none;
903
904 bool ok = ownerRead && ownerExec && !groupAny && !othersAny;
906}
907
910{
911 namespace fs = std::filesystem;
912
913 if (certificatePath_.empty())
915
916 fs::path dir = fs::path(certificatePath_).parent_path();
917 if (dir.empty())
919
920 std::error_code ec;
921 auto st = fs::status(dir, ec);
922 if (ec)
924
925 if (!fs::is_directory(st))
927
928 auto perm = st.permissions();
929
930 bool ownerRead = (perm & fs::perms::owner_read) != fs::perms::none;
931 bool ownerExec = (perm & fs::perms::owner_exec) != fs::perms::none;
932 bool groupAny = (perm & (fs::perms::group_read | fs::perms::group_write | fs::perms::group_exec))
933 != fs::perms::none;
934 bool othersAny = (perm & (fs::perms::others_read | fs::perms::others_write | fs::perms::others_exec))
935 != fs::perms::none;
936
937 bool ok = ownerRead && ownerExec && !groupAny && !othersAny;
939}
940
950
960
970
980
1001
1007{
1008 return TlsValidator::CheckResult((certificateFound_ or certificateFileFound_) ? CheckValues::PASSED
1010 "");
1011}
1012
1020{
1021 return TlsValidator::CheckResult(certificateFound_ ? CheckValues::PASSED : CheckValues::FAILED, "");
1022}
1023
1029{
1030 // TODO Merge with either above or below
1033 "");
1034}
1035
1046
1057{
1058 // TODO need a new boolean account setting "require trusted authority" or something defaulting
1059 // to true using GNUTLS_CERT_SIGNER_NOT_FOUND is a temporary placeholder as it is close enough
1062 "");
1063}
1064
1077
1081bool
1083{
1084 return (x509crt_ and x509crt_->issuer); /* or
1085 (caCert_ != nullptr and caCert_->certificateFound_);*/
1086}
1087
1088//
1089// Certificate details
1090//
1091
1092// TODO gnutls_x509_crl_get_this_update
1093
1099{
1100 size_t resultSize = sizeof(copy_buffer);
1101 int err = gnutls_x509_crt_get_signature(x509crt_->cert, copy_buffer, &resultSize);
1102 return checkBinaryError(err, copy_buffer, resultSize);
1103}
1104
1110{
1111 int version = gnutls_x509_crt_get_version(x509crt_->cert);
1112 if (version < 0)
1114
1115 std::ostringstream convert;
1116 convert << version;
1117
1119}
1120
1126{
1127 // gnutls_x509_crl_iter_crt_serial
1128 // gnutls_x509_crt_get_authority_key_gn_serial
1129 size_t resultSize = sizeof(copy_buffer);
1130 int err = gnutls_x509_crt_get_serial(x509crt_->cert, copy_buffer, &resultSize);
1131 return checkBinaryError(err, copy_buffer, resultSize);
1132}
1133
1139{
1140 if (not x509crt_->issuer) {
1141 auto icrt = certStore_.findIssuer(x509crt_);
1142 if (icrt)
1143 return TlsValidator::CheckResult(CheckValues::CUSTOM, icrt->getId().toString());
1145 }
1146 return TlsValidator::CheckResult(CheckValues::CUSTOM, x509crt_->issuer->getId().toString());
1147}
1148
1154{
1155 unsigned key_length = 0;
1157
1158 if (algo < 0)
1160
1161 const char* name = gnutls_pk_get_name(algo);
1162
1163 if (!name)
1165
1166 if (key_length)
1167 return TlsValidator::CheckResult(CheckValues::CUSTOM, fmt::format("{} ({} bits)", name, key_length));
1168 else
1170}
1171
1177{
1178 try {
1179 std::vector<uint8_t> data;
1180 x509crt_->getPublicKey().pack(data);
1181 return TlsValidator::CheckResult(CheckValues::CUSTOM, dht::toHex(data));
1182 } catch (const dht::crypto::CryptoException& e) {
1184 }
1185}
1186
1192{
1193 // TODO split, cache
1194 size_t resultSize = sizeof(copy_buffer);
1195 int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, copy_buffer, &resultSize);
1196 return checkError(err, copy_buffer, resultSize);
1197}
1198
1204{
1205 size_t resultSize = sizeof(copy_buffer);
1206 int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert, GNUTLS_OID_LDAP_UID, 0, 0, copy_buffer, &resultSize);
1207 return checkError(err, copy_buffer, resultSize);
1208}
1209
1215{
1216 // TODO split, cache
1217 size_t resultSize = sizeof(copy_buffer);
1218 int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert, GNUTLS_OID_X520_NAME, 0, 0, copy_buffer, &resultSize);
1219 return checkError(err, copy_buffer, resultSize);
1220}
1221
1227{
1228 // TODO split, cache
1229 size_t resultSize = sizeof(copy_buffer);
1230 int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
1232 0,
1233 0,
1234 copy_buffer,
1235 &resultSize);
1236 return checkError(err, copy_buffer, resultSize);
1237}
1238
1255
1263{
1264 size_t resultSize = sizeof(copy_buffer);
1265 int err = gnutls_x509_crt_get_fingerprint(x509crt_->cert, GNUTLS_DIG_MD5, copy_buffer, &resultSize);
1266 return checkBinaryError(err, copy_buffer, resultSize);
1267}
1268
1276{
1277 size_t resultSize = sizeof(copy_buffer);
1278 int err = gnutls_x509_crt_get_fingerprint(x509crt_->cert, GNUTLS_DIG_SHA1, copy_buffer, &resultSize);
1279 return checkBinaryError(err, copy_buffer, resultSize);
1280}
1281
1287{
1288 static unsigned char unsigned_copy_buffer[4096];
1289 size_t resultSize = sizeof(unsigned_copy_buffer);
1291
1292 // TODO check for GNUTLS_E_SHORT_MEMORY_BUFFER and increase the buffer size
1293 // TODO get rid of the cast, display a HEX or something, need research
1294
1296}
1297// gnutls_x509_crt_get_authority_key_id
1298
1304{
1305 size_t resultSize = sizeof(copy_buffer);
1306 int err = gnutls_x509_crt_get_issuer_dn(x509crt_->cert, copy_buffer, &resultSize);
1307 return checkError(err, copy_buffer, resultSize);
1308}
1309
1315{
1316 size_t resultSize = sizeof(copy_buffer);
1317 int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
1319 0,
1320 0,
1321 copy_buffer,
1322 &resultSize);
1323 return checkError(err, copy_buffer, resultSize);
1324}
1325
1331{
1332 size_t resultSize = sizeof(copy_buffer);
1333 int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert, GNUTLS_OID_LDAP_UID, 0, 0, copy_buffer, &resultSize);
1334 return checkError(err, copy_buffer, resultSize);
1335}
1336
1342{
1343 size_t resultSize = sizeof(copy_buffer);
1344 int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert, GNUTLS_OID_X520_NAME, 0, 0, copy_buffer, &resultSize);
1345 return checkError(err, copy_buffer, resultSize);
1346}
1347
1353{
1354 size_t resultSize = sizeof(copy_buffer);
1355 int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
1357 0,
1358 0,
1359 copy_buffer,
1360 &resultSize);
1361 return checkError(err, copy_buffer, resultSize);
1362}
1363
1371{
1372 if (not certificateFound_)
1374
1376
1377 return formatDate(expiration);
1378}
1379
1387{
1388 if (not certificateFound_)
1390
1392
1393 return formatDate(expiration);
1394}
1395
1408
1417
1418} // namespace tls
1419} // namespace jami
CheckResult getSignatureAlgorithm()
Return the algorithm used to sign the Key.
CheckResult privateKeyStoragePermissions()
CheckResult requirePrivateKeyPassword()
If the key need decryption.
CheckResult getIssuerUID()
If the certificate is not self signed, return the issuer UID.
@ NUMBER
The check value cannot be represented with a finite set of values.
@ ISO_DATE
The operating system doesn't support or require the check
@ UNSUPPORTED
Equivalent of a boolean "false"
@ CUSTOM
The check value is an ISO 8601 date YYYY-MM-DD[TH24:MM:SS+00:00]
@ FAILED
Equivalent of a boolean "true"
CheckResult privateKeyDirectoryPermissions()
CheckResult getSerialNumber()
Return the certificate serial number.
CheckResult validAuthority()
The provided authority is invalid.
CheckResult activated()
If the activation value is in the past.
CheckResult keyMatch()
The provided key can be used along with the certificate.
CheckResult getN()
The 'N' section of a DN (RFC4514)
CheckResult getO()
The 'O' section of a DN (RFC4514)
CheckResult notRevoked()
Check if the certificate has been revoked.
CheckResult getActivationDate()
Get the activation date.
CheckResult notSelfSigned()
The certificate is not self signed.
CheckResult getVersionNumber()
Return the certificate version.
CheckResult strongSigning()
If the algorithm used to sign the certificate is considered weak by modern standard.
CheckResult getPublicKeyId()
Return an hexadecimal identifier.
CertificateCheck
All validation fields.
@ EXIST
Some operating systems require keys to have extra attributes
CheckResult authorityMatch()
Check if the authority match the certificate.
CheckResult getIssuer()
If the certificate is not self signed, return the issuer.
bool hasCa() const
A certificate authority has been provided.
CheckResult isCA()
If the certificate is not self signed, return the issuer.
CheckResult notExpired()
Check if the certificate is not expired.
CheckResult getUID()
The 'UID' section of a DN (RFC4514)
CheckResult getIssuerO()
If the certificate is not self signed, return the issuer O.
CheckResult getSubjectKeyAlgorithm()
The algorithm used to sign the certificate details (rather than the certificate itself)
bool isValid(bool verbose=false)
Check if all boolean check passed return true if there was no FAILED checks.
CheckResult getIssuerDN()
If the certificate is not self signed, return the issuer DN (RFC4514)
CheckResult outgoingServer()
The expected outgoing server domain.
CheckResult knownAuthority()
When an account require an authority known by the system (like /usr/share/ssl/certs) then the whole c...
CheckResult privateKeySelinuxAttributes()
SELinux provide additional key protection mechanism.
std::pair< CheckValues, std::string > CheckResult
CheckResult getPublicSignature()
An hexadecimal representation of the signature.
CheckResult getExpirationDate()
Get the expiration date.
CertificateDetails
Informative fields about a certificate.
CheckResult exist()
The file has been found.
CheckResult valid()
The certificate is invalid compared to the authority.
TlsValidator(const dhtnet::tls::CertificateStore &certStore, const std::string &certificate, const std::string &privatekey="", const std::string &privatekeyPasswd="", const std::string &caList="")
Create a TlsValidator for a given certificate.
CheckResult getMd5Fingerprint()
Compute the key fingerprint.
CheckResult hasPrivateKey()
Check if the Validator have access to a private key.
std::map< std::string, std::string > getSerializedChecks()
Convert all checks results into a string map.
CheckResult getSubjectKey()
The subject public key.
CheckResult publicKeyStoragePermissions()
CheckResult getIssuerN()
If the certificate is not self signed, return the issuer N.
CheckResult privateKeyStorageLocation()
Certificate should be located in specific path on some operating systems.
CheckResult publicKeyStorageLocation()
Certificate should be located in specific path on some operating systems.
CheckResult getIssuerCN()
If the certificate is not self signed, return the issuer CN.
CheckResult getSha1Fingerprint()
Compute the key fingerprint.
std::map< std::string, std::string > getSerializedDetails()
Get a map with all common certificate details.
CheckResult publicKeySelinuxAttributes()
SELinux provide additional key protection mechanism.
CheckResult expectedOwner()
The CA and certificate provide conflicting ownership information.
CheckResult publicKeyDirectoryPermissions()
CheckResult getCN()
The 'CN' section of a DN (RFC4514)
#define JAMI_ERR(...)
Definition logger.h:230
#define JAMI_DBG(...)
Definition logger.h:228
#define JAMI_WARN(...)
Definition logger.h:229
#define JAMI_WARNING(formatstr,...)
Definition logger.h:242
Definition account.h:50
std::vector< uint8_t > loadFile(const std::filesystem::path &path, const std::filesystem::path &default_dir)
Read the full content of a file at path.
static constexpr int version
static TlsValidator::CheckResult checkBinaryError(int err, char *copy_buffer, size_t resultSize)
Helper method to return UNSUPPORTED when an error is detected.
static TlsValidator::CheckResult checkError(int err, char *copy_buffer, size_t size)
Helper method to return UNSUPPORTED when an error is detected.
static TlsValidator::CheckResult formatDate(const time_t time)
Convert a time_t to an ISO date string.
static constexpr const char TRUE_STR[]
void emitSignal(Args... args)
Definition jami_signal.h:64
static constexpr const char FALSE_STR[]
static constexpr char PUBLIC_KEY_STORAGE_LOCATION[]
static constexpr char NOT_SELF_SIGNED[]
static constexpr char AUTHORITY_MISMATCH[]
static constexpr char STRONG_SIGNING[]
static constexpr char HAS_PRIVATE_KEY[]
static constexpr char PRIVATE_KEY_STORAGE_LOCATION[]
static constexpr char KNOWN_AUTHORITY[]
static constexpr char PRIVATE_KEY_DIRECTORY_PERMISSIONS[]
static constexpr char KEY_MATCH[]
static constexpr char UNEXPECTED_OWNER[]
static constexpr char PRIVATE_KEY_SELINUX_ATTRIBUTES[]
static constexpr char VALID[]
static constexpr char NOT_ACTIVATED[]
static constexpr char PRIVATE_KEY_STORAGE_PERMISSION[]
static constexpr char EXIST[]
static constexpr char VALID_AUTHORITY[]
static constexpr char PUBLIC_KEY_STORAGE_PERMISSION[]
static constexpr char NOT_REVOKED[]
static constexpr char PUBLIC_KEY_SELINUX_ATTRIBUTES[]
static constexpr char EXPIRED[]
static constexpr char PUBLIC_KEY_DIRECTORY_PERMISSIONS[]
static constexpr char ISSUER_DN[]
static constexpr char ISSUER_CN[]
static constexpr char PUBLIC_SIGNATURE[]
static constexpr char NEXT_EXPECTED_UPDATE_DATE[]
static constexpr char ACTIVATION_DATE[]
static constexpr char OUTGOING_SERVER[]
static constexpr char SERIAL_NUMBER[]
static constexpr char EXPIRATION_DATE[]
static constexpr char MD5_FINGERPRINT[]
static constexpr char PUBLIC_KEY_ID[]
static constexpr char SHA1_FINGERPRINT[]
static constexpr char ISSUER_N[]
static constexpr char ISSUER_O[]
static constexpr char ISSUER[]
static constexpr char REQUIRE_PRIVATE_KEY_PASSWORD[]
static constexpr char SIGNATURE_ALGORITHM[]
static constexpr char SUBJECT_KEY[]
static constexpr char IS_CA[]
static constexpr char VERSION_NUMBER[]
static constexpr char SUBJECT_KEY_ALGORITHM[]
static constexpr char ISSUER_UID[]
This generic class represents a multidimensional enum class array.
#define S_IXGRP
#define S_IROTH
#define S_IXOTH
#define S_IRGRP
#define S_IWOTH
#define S_IWGRP
#define S_IFREG
Definition windirent.h:65
#define S_IRUSR
Definition windirent.h:105
#define S_IXUSR
Definition windirent.h:115