Ring Daemon 16.0.0
Loading...
Searching...
No Matches
tlsvalidator.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 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
31#include <sstream>
32#include <iomanip>
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
63namespace jami {
64namespace tls {
65
66// Map the internal ring Enum class of the exported names
67
68const EnumClassNames<TlsValidator::CheckValues> TlsValidator::CheckValuesNames = {{
69 /* CheckValues Name */
76}};
77
79 TlsValidator::checkCallback = {{
80 /* CertificateCheck Callback */
81 /*HAS_PRIVATE_KEY */ &TlsValidator::hasPrivateKey,
82 /*EXPIRED */ &TlsValidator::notExpired,
83 /*STRONG_SIGNING */ &TlsValidator::strongSigning,
84 /*NOT_SELF_SIGNED */ &TlsValidator::notSelfSigned,
85 /*KEY_MATCH */ &TlsValidator::keyMatch,
86 /*PRIVATE_KEY_STORAGE_PERMISSION */ &TlsValidator::privateKeyStoragePermissions,
87 /*PUBLIC_KEY_STORAGE_PERMISSION */ &TlsValidator::publicKeyStoragePermissions,
88 /*PRIVATEKEY_DIRECTORY_PERMISSIONS */ &TlsValidator::privateKeyDirectoryPermissions,
89 /*PUBLICKEY_DIRECTORY_PERMISSIONS */ &TlsValidator::publicKeyDirectoryPermissions,
90 /*PRIVATE_KEY_STORAGE_LOCATION */ &TlsValidator::privateKeyStorageLocation,
91 /*PUBLIC_KEY_STORAGE_LOCATION */ &TlsValidator::publicKeyStorageLocation,
92 /*PRIVATE_KEY_SELINUX_ATTRIBUTES */ &TlsValidator::privateKeySelinuxAttributes,
93 /*PUBLIC_KEY_SELINUX_ATTRIBUTES */ &TlsValidator::publicKeySelinuxAttributes,
94 /*EXIST */ &TlsValidator::exist,
95 /*VALID */ &TlsValidator::valid,
96 /*VALID_AUTHORITY */ &TlsValidator::validAuthority,
97 /*KNOWN_AUTHORITY */ &TlsValidator::knownAuthority,
98 /*NOT_REVOKED */ &TlsValidator::notRevoked,
99 /*AUTHORITY_MISMATCH */ &TlsValidator::authorityMatch,
100 /*UNEXPECTED_OWNER */ &TlsValidator::expectedOwner,
101 /*NOT_ACTIVATED */ &TlsValidator::activated,
102 }};
103
105 TlsValidator::getterCallback = {{
106 /* EXPIRATION_DATE */ &TlsValidator::getExpirationDate,
107 /* ACTIVATION_DATE */ &TlsValidator::getActivationDate,
108 /* REQUIRE_PRIVATE_KEY_PASSWORD */ &TlsValidator::requirePrivateKeyPassword,
109 /* PUBLIC_SIGNATURE */ &TlsValidator::getPublicSignature,
110 /* VERSION_NUMBER */ &TlsValidator::getVersionNumber,
111 /* SERIAL_NUMBER */ &TlsValidator::getSerialNumber,
112 /* ISSUER */ &TlsValidator::getIssuer,
113 /* SUBJECT_KEY_ALGORITHM */ &TlsValidator::getSubjectKeyAlgorithm,
114 /* SUBJECT_KEY */ &TlsValidator::getSubjectKey,
115 /* CN */ &TlsValidator::getCN,
116 /* UID */ &TlsValidator::getUID,
117 /* N */ &TlsValidator::getN,
118 /* O */ &TlsValidator::getO,
119 /* SIGNATURE_ALGORITHM */ &TlsValidator::getSignatureAlgorithm,
120 /* MD5_FINGERPRINT */ &TlsValidator::getMd5Fingerprint,
121 /* SHA1_FINGERPRINT */ &TlsValidator::getSha1Fingerprint,
122 /* PUBLIC_KEY_ID */ &TlsValidator::getPublicKeyId,
123 /* ISSUER_DN */ &TlsValidator::getIssuerDN,
124 /* ISSUER_CN */ &TlsValidator::getIssuerCN,
125 /* ISSUER_UID */ &TlsValidator::getIssuerUID,
126 /* ISSUER_N */ &TlsValidator::getIssuerN,
127 /* ISSUER_O */ &TlsValidator::getIssuerO,
128 /* NEXT_EXPECTED_UPDATE_DATE */ &TlsValidator::getIssuerDN, // TODO
129 /* OUTGOING_SERVER */ &TlsValidator::outgoingServer,
130 /* IS_CA */ &TlsValidator::isCA,
131 }};
132
134 TlsValidator::enforcedCheckType = {{
135 /* CertificateCheck Callback */
136 /*HAS_PRIVATE_KEY */ CheckValuesType::BOOLEAN,
137 /*EXPIRED */ CheckValuesType::BOOLEAN,
138 /*STRONG_SIGNING */ CheckValuesType::BOOLEAN,
139 /*NOT_SELF_SIGNED */ CheckValuesType::BOOLEAN,
140 /*KEY_MATCH */ CheckValuesType::BOOLEAN,
141 /*PRIVATE_KEY_STORAGE_PERMISSION */ CheckValuesType::BOOLEAN,
142 /*PUBLIC_KEY_STORAGE_PERMISSION */ CheckValuesType::BOOLEAN,
143 /*PRIVATEKEY_DIRECTORY_PERMISSIONS */ CheckValuesType::BOOLEAN,
144 /*PUBLICKEY_DIRECTORY_PERMISSIONS */ CheckValuesType::BOOLEAN,
145 /*PRIVATE_KEY_STORAGE_LOCATION */ CheckValuesType::BOOLEAN,
146 /*PUBLIC_KEY_STORAGE_LOCATION */ CheckValuesType::BOOLEAN,
147 /*PRIVATE_KEY_SELINUX_ATTRIBUTES */ CheckValuesType::BOOLEAN,
148 /*PUBLIC_KEY_SELINUX_ATTRIBUTES */ CheckValuesType::BOOLEAN,
149 /*EXIST */ CheckValuesType::BOOLEAN,
150 /*VALID */ CheckValuesType::BOOLEAN,
151 /*VALID_AUTHORITY */ CheckValuesType::BOOLEAN,
152 /*KNOWN_AUTHORITY */ CheckValuesType::BOOLEAN,
153 /*NOT_REVOKED */ CheckValuesType::BOOLEAN,
154 /*AUTHORITY_MISMATCH */ CheckValuesType::BOOLEAN,
155 /*UNEXPECTED_OWNER */ CheckValuesType::BOOLEAN,
156 /*NOT_ACTIVATED */ CheckValuesType::BOOLEAN,
157 }};
158
159const EnumClassNames<TlsValidator::CertificateCheck> TlsValidator::CertificateCheckNames = {{
160 /* CertificateCheck Name */
182}};
183
184const EnumClassNames<TlsValidator::CertificateDetails> TlsValidator::CertificateDetailsNames = {{
210
211}};
212
213const EnumClassNames<const TlsValidator::CheckValuesType> TlsValidator::CheckValuesTypeNames = {{
214 /* Type Name */
219}};
220
222 TlsValidator::acceptedCheckValuesResult = {{
223 /* Type PASSED FAILED UNSUPPORTED ISO_DATE CUSTOM NUMBER */
224 /* BOOLEAN */ {{true, true, true, false, false, false}},
225 /* ISO_DATE */ {{false, false, true, true, false, false}},
226 /* CUSTOM */ {{false, false, true, false, true, false}},
227 /* NUMBER */ {{false, false, true, false, false, true}},
228 }};
229
230TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::vector<std::vector<uint8_t>>& crtChain)
231 : TlsValidator(certStore, std::make_shared<dht::crypto::Certificate>(crtChain.begin(), crtChain.end()))
232{}
233
234TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore,
235 const std::string& certificate,
236 const std::string& privatekey,
237 const std::string& privatekeyPasswd,
238 const std::string& caList)
239 : certStore_(certStore)
240 , certificatePath_(certificate)
241 , privateKeyPath_(privatekey)
242 , caListPath_(caList)
243 , certificateFound_(false)
244{
245 std::vector<uint8_t> certificate_raw;
246 try {
247 certificate_raw = fileutils::loadFile(certificatePath_);
248 certificateFileFound_ = true;
249 } catch (const std::exception& e) {
250 }
251
252 if (not certificate_raw.empty()) {
253 try {
254 x509crt_ = std::make_shared<dht::crypto::Certificate>(certificate_raw);
255 certificateContent_ = x509crt_->getPacked();
256 certificateFound_ = true;
257 } catch (const std::exception& e) {
258 }
259 }
260
261 try {
262 auto privateKeyContent = fileutils::loadFile(privateKeyPath_);
263 dht::crypto::PrivateKey key_tmp(privateKeyContent, privatekeyPasswd);
264 privateKeyFound_ = true;
265 privateKeyPassword_ = not privatekeyPasswd.empty();
266 privateKeyMatch_ = key_tmp.getPublicKey().getId() == x509crt_->getId();
267 } catch (const dht::crypto::DecryptError& d) {
268 // If we encounter a DecryptError, it means the private key exists and is encrypted,
269 // otherwise we would get some other exception.
270 JAMI_WARN("decryption error: %s", d.what());
271 privateKeyFound_ = true;
272 privateKeyPassword_ = true;
273 } catch (const std::exception& e) {
274 JAMI_WARN("creation failed: %s", e.what());
275 }
276}
277
278TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::vector<uint8_t>& certificate_raw)
279 : certStore_(certStore)
280{
281 try {
282 x509crt_ = std::make_shared<dht::crypto::Certificate>(certificate_raw);
283 certificateContent_ = x509crt_->getPacked();
284 certificateFound_ = true;
285 } catch (const std::exception& e) {
286 throw TlsValidatorException("Unable to load certificate");
287 }
288}
289
290TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::shared_ptr<dht::crypto::Certificate>& crt)
291 : certStore_(certStore)
292 , certificateFound_(true)
293{
294 try {
295 if (not crt)
296 throw std::invalid_argument("Certificate must be set");
297 x509crt_ = crt;
298 certificateContent_ = x509crt_->getPacked();
299 } catch (const std::exception& e) {
300 throw TlsValidatorException("Unable to load certificate");
301 }
302}
303
305
311std::string
312TlsValidator::getStringValue([[maybe_unused]]const TlsValidator::CertificateCheck check,
313 const TlsValidator::CheckResult result)
314{
315 assert(acceptedCheckValuesResult[enforcedCheckType[check]][result.first]);
316
317 switch (result.first) {
321 return std::string(CheckValuesNames[result.first]);
323 // TODO validate date
324 // return CheckValues::FAILED;
325 return result.second;
327 // TODO Validate numbers
329 return result.second;
330 default:
331 // Consider any other case (such as forced int->CheckValues casting) as failed
332 return std::string(CheckValuesNames[CheckValues::FAILED]);
333 };
334}
335
342bool
344{
346 if (enforcedCheckType[check] == CheckValuesType::BOOLEAN) {
347 if (((this->*(checkCallback[check]))()).first == CheckValues::FAILED) {
348 if (verbose)
349 JAMI_WARNING("Check failed: {}", CertificateCheckNames[check]);
350 return false;
351 }
352 }
353 }
354 return true;
355}
356
360std::map<std::string, std::string>
362{
363 std::map<std::string, std::string> ret;
364 if (not certificateFound_) {
365 // Instead of checking `certificateFound` everywhere, handle it once
366 ret[std::string(CertificateCheckNames[CertificateCheck::EXIST])] = getStringValue(CertificateCheck::EXIST,
367 exist());
368 } else {
370 ret[std::string(CertificateCheckNames[check])] = getStringValue(check,
371 (this->*(checkCallback[check]))());
372 }
373
374 return ret;
375}
376
380std::map<std::string, std::string>
382{
383 std::map<std::string, std::string> ret;
384 if (certificateFound_) {
386 const CheckResult r = (this->*(getterCallback[det]))();
387 std::string val;
388 // TODO move this to a fuction
389 switch (r.first) {
393 val = CheckValuesNames[r.first];
394 break;
396 // TODO validate date
398 // TODO Validate numbers
400 default:
401 val = r.second;
402 break;
403 }
404 ret[std::string(CertificateDetailsNames[det])] = val;
405 }
406 }
407 return ret;
408}
409
414checkError(int err, char* copy_buffer, size_t size)
415{
416 return TlsValidator::TlsValidator::CheckResult(err == GNUTLS_E_SUCCESS
420 ? std::string(copy_buffer, size)
421 : "");
422}
423
429{
430 char buffer[12];
431 struct tm* timeinfo = localtime(&time);
432 strftime(buffer, sizeof(buffer), "%F", timeinfo);
434}
435
442checkBinaryError(int err, char* copy_buffer, size_t resultSize)
443{
444 if (err == GNUTLS_E_SUCCESS)
446 dht::toHex(reinterpret_cast<uint8_t*>(copy_buffer),
447 resultSize));
448 else
450}
451
455unsigned int
456TlsValidator::compareToCa()
457{
458 // Don't check unless the certificate changed
459 if (caChecked_)
460 return caValidationOutput_;
461
462 // build the CA trusted list
465
466 auto root_cas = certStore_.getTrustedCertificates();
467 auto err = gnutls_x509_trust_list_add_cas(trust, root_cas.data(), root_cas.size(), 0);
468 if (err)
469 JAMI_WARN("gnutls_x509_trust_list_add_cas failed: %s", gnutls_strerror(err));
470
471 if (not caListPath_.empty()) {
472 if (std::filesystem::is_directory(caListPath_))
474 caListPath_.c_str(),
475 nullptr,
477 0,
478 0);
479 else
481 caListPath_.c_str(),
482 nullptr,
484 0,
485 0);
486 }
487
488 // build the certificate chain
489 auto crts = x509crt_->getChain();
491 crts.data(),
492 crts.size(),
493 nullptr,
494 0,
496 &caValidationOutput_,
497 nullptr);
498
500
501 if (err) {
502 JAMI_WARN("gnutls_x509_trust_list_verify_crt2 failed: %s", gnutls_strerror(err));
504 }
505
506 caChecked_ = true;
507 return caValidationOutput_;
508}
509
510#if 0 // disabled, see .h for reason
519int TlsValidator::verifyHostnameCertificate(const std::string& host, const uint16_t port)
520{
521 int err, arg, res = -1;
522 unsigned int status = (unsigned) -1;
523 const char *errptr = nullptr;
524 gnutls_session_t session = nullptr;
526 unsigned int certslen = 0;
527 const gnutls_datum_t *certs = nullptr;
528 gnutls_x509_crt_t cert = nullptr;
529
530 char buf[4096];
531 int sockfd;
532 struct sockaddr_in name;
533 struct hostent *hostinfo;
534 const int one = 1;
536 struct timeval tv;
537
538 if (!host.size() || !port) {
539 JAMI_ERR("Wrong parameters used - host %s, port %d.", host.c_str(), port);
540 return res;
541 }
542
543 /* Create the socket. */
544 sockfd = socket (PF_INET, SOCK_STREAM, 0);
545 if (sockfd < 0) {
546 JAMI_ERR("Unable to create socket.");
547 return res;
548 }
549 /* Set non-blocking so we can dected timeouts. */
550 arg = fcntl(sockfd, F_GETFL, nullptr);
551 if (arg < 0)
552 goto out;
553 arg |= O_NONBLOCK;
554 if (fcntl(sockfd, F_SETFL, arg) < 0)
555 goto out;
556
557 /* Give the socket a name. */
558 memset(&name, 0, sizeof(name));
559 name.sin_family = AF_INET;
560 name.sin_port = htons(port);
561 hostinfo = gethostbyname(host.c_str());
562 if (hostinfo == nullptr) {
563 JAMI_ERR("Unknown host %s.", host.c_str());
564 goto out;
565 }
566 name.sin_addr = *(struct in_addr *)hostinfo->h_addr;
567 /* Connect to the address specified in name struct. */
568 err = connect(sockfd, (struct sockaddr *)&name, sizeof(name));
569 if (err < 0) {
570 /* Connection in progress, use select to see if timeout is reached. */
571 if (errno == EINPROGRESS) {
572 do {
573 FD_ZERO(&fdset);
575 tv.tv_sec = 10; // 10 second timeout
576 tv.tv_usec = 0;
577 err = select(sockfd + 1, nullptr, &fdset, nullptr, &tv);
578 if (err < 0 && errno != EINTR) {
579 JAMI_ERR("Unable to connect to hostname %s at port %d",
580 host.c_str(), port);
581 goto out;
582 } else if (err > 0) {
583 /* Select returned, if so_error is clean we are ready. */
584 int so_error;
585 socklen_t len = sizeof(so_error);
587
588 if (so_error) {
589 JAMI_ERR("Connection delayed.");
590 goto out;
591 }
592 break; // exit do-while loop
593 } else {
594 JAMI_ERR("Connection timeout.");
595 goto out;
596 }
597 } while(1);
598 } else {
599 JAMI_ERR("Unable to connect to hostname %s at port %d", host.c_str(), port);
600 goto out;
601 }
602 }
603 /* Set the socked blocking again. */
604 arg = fcntl(sockfd, F_GETFL, nullptr);
605 if (arg < 0)
606 goto out;
607 arg &= ~O_NONBLOCK;
608 if (fcntl(sockfd, F_SETFL, arg) < 0)
609 goto out;
610
611 /* Disable Nagle algorithm that slows down the SSL handshake. */
613 if (err < 0) {
614 JAMI_ERR("Unable to set TCP_NODELAY.");
615 goto out;
616 }
617
618
619 /* Load the trusted CA certificates. */
621 if (err != GNUTLS_E_SUCCESS) {
622 JAMI_ERR("Unable to allocate credentials - %s", gnutls_strerror(err));
623 goto out;
624 }
626 if (err != GNUTLS_E_SUCCESS) {
627 JAMI_ERR("Unable to load credentials.");
628 goto out;
629 }
630
631 /* Create the session object. */
633 if (err != GNUTLS_E_SUCCESS) {
634 JAMI_ERR("Unable to init session -%s\n", gnutls_strerror(err));
635 goto out;
636 }
637
638 /* Configure the cipher preferences. The default set should be good enough. */
640 if (err != GNUTLS_E_SUCCESS) {
641 JAMI_ERR("Unable to set up ciphers - %s (%s)", gnutls_strerror(err), errptr);
642 goto out;
643 }
644
645 /* Install the trusted certificates. */
647 if (err != GNUTLS_E_SUCCESS) {
648 JAMI_ERR("Unable to set up credentials - %s", gnutls_strerror(err));
649 goto out;
650 }
651
652 /* Associate the socket with the session object and set the server name. */
655 if (err != GNUTLS_E_SUCCESS) {
656 JAMI_ERR("Unable to set server name - %s", gnutls_strerror(err));
657 goto out;
658 }
659
660 /* Establish the connection. */
662 if (err != GNUTLS_E_SUCCESS) {
663 JAMI_ERR("Handshake failed - %s", gnutls_strerror(err));
664 goto out;
665 }
666 /* Obtain the server certificate chain. The server certificate
667 * itself is stored in the first element of the array. */
669 if (certs == nullptr || certslen == 0) {
670 JAMI_ERR("Unable to obtain peer certificate - %s", gnutls_strerror(err));
671 goto out;
672 }
673
674 /* Validate the certificate chain. */
676 if (err != GNUTLS_E_SUCCESS) {
677 JAMI_ERR("Unable to verify the certificate chain - %s", gnutls_strerror(err));
678 goto out;
679 }
680 if (status != 0) {
682#if GNUTLS_VERSION_AT_LEAST_3_1_4
685#else
686 err = -1;
687#endif
688 if (err == 0) {
689 JAMI_ERR("Certificate validation failed - %s\n", msg.data);
690 gnutls_free(msg.data);
691 goto out;
692 } else {
693 JAMI_ERR("Certificate validation failed with code 0x%x.", status);
694 goto out;
695 }
696 }
697
698 /* Match the peer certificate against the hostname.
699 * We can only obtain a set of DER-encoded certificates from the
700 * session object, so we have to re-parse the peer certificate into
701 * a certificate object. */
702
704 if (err != GNUTLS_E_SUCCESS) {
705 JAMI_ERR("Unable to init certificate - %s", gnutls_strerror(err));
706 goto out;
707 }
708
709 /* The peer certificate is the first certificate in the list. */
711 if (err != GNUTLS_E_SUCCESS)
713 if (err != GNUTLS_E_SUCCESS) {
714 JAMI_ERR("Unable to read peer certificate - %s", gnutls_strerror(err));
715 goto out;
716 }
717 /* Finally check if the hostnames match. */
719 if (err == 0) {
720 JAMI_ERR("Hostname %s does not match certificate.", host.c_str());
721 goto out;
722 }
723
724 /* Try sending and receiving some data through. */
725 snprintf(buf, sizeof(buf), "GET / HTTP/1.0\r\nHost: %s\r\n\r\n", host.c_str());
727 if (err < 0) {
728 JAMI_ERR("Send failed - %s", gnutls_strerror(err));
729 goto out;
730 }
731 err = gnutls_record_recv(session, buf, sizeof(buf));
732 if (err < 0) {
733 JAMI_ERR("Recv failed - %s", gnutls_strerror(err));
734 goto out;
735 }
736
737 JAMI_DBG("Hostname %s seems to point to a valid server.", host.c_str());
738 res = 0;
739out:
740 if (session) {
743 }
744 if (cert)
746 if (cred)
748 close(sockfd);
749 return res;
750}
751#endif
752
758{
759 if (privateKeyFound_)
761
762 try {
763 dht::crypto::PrivateKey key_tmp(certificateContent_);
764 } catch (const std::exception& e) {
765 return CheckResult(CheckValues::FAILED, e.what());
766 }
767
768 JAMI_DBG("Key from %s seems valid.", certificatePath_.c_str());
770}
771
782{
785
786 // time_t expirationTime = gnutls_x509_crt_get_expiration_time(cert);
789 "");
790}
791
799{
802
803 // time_t activationTime = gnutls_x509_crt_get_activation_time(cert);
807 "");
808}
809
816{
819
820 // Doesn't seem to have the same value as
821 // certtool --infile /home/etudiant/Téléchargements/mynsauser.pem --key-inf
822 // TODO figure out why
826 "");
827}
828
837
852
855{
856 struct stat statbuf;
857 int err = stat(privateKeyPath_.c_str(), &statbuf);
858 if (err)
860
861// clang-format off
863 (statbuf.st_mode & S_IFREG) && /* Regular file only */
864 /* READ WRITE EXECUTE */
865 /* Owner */ ( (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && !(statbuf.st_mode & S_IXUSR))
866 /* Group */ && (!(statbuf.st_mode & S_IRGRP) && !(statbuf.st_mode & S_IWGRP) && !(statbuf.st_mode & S_IXGRP))
867 /* Other */ && (!(statbuf.st_mode & S_IROTH) && !(statbuf.st_mode & S_IWOTH) && !(statbuf.st_mode & S_IXOTH))
869// clang-format on
870}
871
874{
875 struct stat statbuf;
876 int err = stat(certificatePath_.c_str(), &statbuf);
877 if (err)
879
880// clang-format off
882 (statbuf.st_mode & S_IFREG) && /* Regular file only */
883 /* READ WRITE EXECUTE */
884 /* Owner */ ( (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && !(statbuf.st_mode & S_IXUSR))
885 /* Group */ && ( /* read is not relevant */ !(statbuf.st_mode & S_IWGRP) && !(statbuf.st_mode & S_IXGRP))
886 /* Other */ && ( /* read is not relevant */ !(statbuf.st_mode & S_IWOTH) && !(statbuf.st_mode & S_IXOTH))
888// clang-format on
889}
890
893{
894 if (privateKeyPath_.empty())
896
897#ifndef _MSC_VER
898 auto path = std::unique_ptr<char, decltype(free)&>(strdup(privateKeyPath_.c_str()), free);
899 const char* dir = dirname(path.get());
900#else
901 char* dir;
902 _splitpath(certificatePath_.c_str(), nullptr, dir, nullptr, nullptr);
903#endif
904
905 struct stat statbuf;
906 int err = stat(dir, &statbuf);
907 if (err)
909
911 /* READ WRITE EXECUTE */
912 /* Owner */ (
913 (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && (statbuf.st_mode & S_IXUSR))
914 /* Group */
915 && (!(statbuf.st_mode & S_IRGRP) && !(statbuf.st_mode & S_IWGRP)
916 && !(statbuf.st_mode & S_IXGRP))
917 /* Other */
918 && (!(statbuf.st_mode & S_IROTH) && !(statbuf.st_mode & S_IWOTH)
919 && !(statbuf.st_mode & S_IXOTH))
920 && S_ISDIR(statbuf.st_mode)
923 "");
924}
925
928{
929#ifndef _MSC_VER
930 auto path = std::unique_ptr<char, decltype(free)&>(strdup(certificatePath_.c_str()), free);
931 const char* dir = dirname(path.get());
932#else
933 char* dir;
934 _splitpath(certificatePath_.c_str(), nullptr, dir, nullptr, nullptr);
935#endif
936
937 struct stat statbuf;
938 int err = stat(dir, &statbuf);
939 if (err)
941
943 /* READ WRITE EXECUTE */
944 /* Owner */ (
945 (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && (statbuf.st_mode & S_IXUSR))
946 /* Group */
947 && (!(statbuf.st_mode & S_IRGRP) && !(statbuf.st_mode & S_IWGRP)
948 && !(statbuf.st_mode & S_IXGRP))
949 /* Other */
950 && (!(statbuf.st_mode & S_IROTH) && !(statbuf.st_mode & S_IWOTH)
951 && !(statbuf.st_mode & S_IXOTH))
952 && S_ISDIR(statbuf.st_mode)
955 "");
956}
957
967
977
987
997
1020
1026{
1027 return TlsValidator::CheckResult((certificateFound_ or certificateFileFound_)
1030 "");
1031}
1032
1040{
1042 "");
1043}
1044
1050{
1051 // TODO Merge with either above or below
1055 "");
1056}
1057
1069
1080{
1081 // TODO need a new boolean account setting "require trusted authority" or something defaulting
1082 // to true using GNUTLS_CERT_SIGNER_NOT_FOUND is a temporary placeholder as it is close enough
1086 "");
1087}
1088
1094{
1095 return TlsValidator::CheckResult((compareToCa() & GNUTLS_CERT_REVOKED)
1096 || (compareToCa()
1100 "");
1101}
1102
1106bool
1108{
1109 return (x509crt_ and x509crt_->issuer); /* or
1110 (caCert_ != nullptr and caCert_->certificateFound_);*/
1111}
1112
1113//
1114// Certificate details
1115//
1116
1117// TODO gnutls_x509_crl_get_this_update
1118
1124{
1125 size_t resultSize = sizeof(copy_buffer);
1126 int err = gnutls_x509_crt_get_signature(x509crt_->cert, copy_buffer, &resultSize);
1127 return checkBinaryError(err, copy_buffer, resultSize);
1128}
1129
1135{
1136 int version = gnutls_x509_crt_get_version(x509crt_->cert);
1137 if (version < 0)
1139
1140 std::ostringstream convert;
1141 convert << version;
1142
1144}
1145
1151{
1152 // gnutls_x509_crl_iter_crt_serial
1153 // gnutls_x509_crt_get_authority_key_gn_serial
1154 size_t resultSize = sizeof(copy_buffer);
1155 int err = gnutls_x509_crt_get_serial(x509crt_->cert, copy_buffer, &resultSize);
1156 return checkBinaryError(err, copy_buffer, resultSize);
1157}
1158
1164{
1165 if (not x509crt_->issuer) {
1166 auto icrt = certStore_.findIssuer(x509crt_);
1167 if (icrt)
1168 return TlsValidator::CheckResult(CheckValues::CUSTOM, icrt->getId().toString());
1170 }
1171 return TlsValidator::CheckResult(CheckValues::CUSTOM, x509crt_->issuer->getId().toString());
1172}
1173
1179{
1180 unsigned key_length = 0;
1183
1184 if (algo < 0)
1186
1187 const char* name = gnutls_pk_get_name(algo);
1188
1189 if (!name)
1191
1192 if (key_length)
1193 return TlsValidator::CheckResult(CheckValues::CUSTOM, fmt::format("{} ({} bits)", name, key_length));
1194 else
1196}
1197
1203{
1204 try {
1205 std::vector<uint8_t> data;
1206 x509crt_->getPublicKey().pack(data);
1207 return TlsValidator::CheckResult(CheckValues::CUSTOM, dht::toHex(data));
1208 } catch (const dht::crypto::CryptoException& e) {
1210 }
1211}
1212
1218{
1219 // TODO split, cache
1220 size_t resultSize = sizeof(copy_buffer);
1221 int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
1223 0,
1224 0,
1225 copy_buffer,
1226 &resultSize);
1227 return checkError(err, copy_buffer, resultSize);
1228}
1229
1235{
1236 size_t resultSize = sizeof(copy_buffer);
1237 int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
1239 0,
1240 0,
1241 copy_buffer,
1242 &resultSize);
1243 return checkError(err, copy_buffer, resultSize);
1244}
1245
1251{
1252 // TODO split, cache
1253 size_t resultSize = sizeof(copy_buffer);
1254 int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
1256 0,
1257 0,
1258 copy_buffer,
1259 &resultSize);
1260 return checkError(err, copy_buffer, resultSize);
1261}
1262
1268{
1269 // TODO split, cache
1270 size_t resultSize = sizeof(copy_buffer);
1271 int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
1273 0,
1274 0,
1275 copy_buffer,
1276 &resultSize);
1277 return checkError(err, copy_buffer, resultSize);
1278}
1279
1297
1305{
1306 size_t resultSize = sizeof(copy_buffer);
1307 int err = gnutls_x509_crt_get_fingerprint(x509crt_->cert,
1309 copy_buffer,
1310 &resultSize);
1311 return checkBinaryError(err, copy_buffer, resultSize);
1312}
1313
1321{
1322 size_t resultSize = sizeof(copy_buffer);
1323 int err = gnutls_x509_crt_get_fingerprint(x509crt_->cert,
1325 copy_buffer,
1326 &resultSize);
1327 return checkBinaryError(err, copy_buffer, resultSize);
1328}
1329
1335{
1336 static unsigned char unsigned_copy_buffer[4096];
1337 size_t resultSize = sizeof(unsigned_copy_buffer);
1339
1340 // TODO check for GNUTLS_E_SHORT_MEMORY_BUFFER and increase the buffer size
1341 // TODO get rid of the cast, display a HEX or something, need research
1342
1344}
1345// gnutls_x509_crt_get_authority_key_id
1346
1352{
1353 size_t resultSize = sizeof(copy_buffer);
1354 int err = gnutls_x509_crt_get_issuer_dn(x509crt_->cert, copy_buffer, &resultSize);
1355 return checkError(err, copy_buffer, resultSize);
1356}
1357
1363{
1364 size_t resultSize = sizeof(copy_buffer);
1365 int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
1367 0,
1368 0,
1369 copy_buffer,
1370 &resultSize);
1371 return checkError(err, copy_buffer, resultSize);
1372}
1373
1379{
1380 size_t resultSize = sizeof(copy_buffer);
1381 int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
1383 0,
1384 0,
1385 copy_buffer,
1386 &resultSize);
1387 return checkError(err, copy_buffer, resultSize);
1388}
1389
1395{
1396 size_t resultSize = sizeof(copy_buffer);
1397 int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
1399 0,
1400 0,
1401 copy_buffer,
1402 &resultSize);
1403 return checkError(err, copy_buffer, resultSize);
1404}
1405
1411{
1412 size_t resultSize = sizeof(copy_buffer);
1413 int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
1415 0,
1416 0,
1417 copy_buffer,
1418 &resultSize);
1419 return checkError(err, copy_buffer, resultSize);
1420}
1421
1429{
1430 if (not certificateFound_)
1432
1434
1435 return formatDate(expiration);
1436}
1437
1445{
1446 if (not certificateFound_)
1448
1450
1451 return formatDate(expiration);
1452}
1453
1466
1475
1476} // namespace tls
1477} // 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.
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.
CertificateCheck
All validation fields.
@ EXIST
Some operating systems require keys to have extra attributes
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.
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.
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.
@ 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 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()
CertificateDetails
Informative fields about a certificate.
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:218
#define JAMI_DBG(...)
Definition logger.h:216
#define JAMI_WARN(...)
Definition logger.h:217
#define JAMI_WARNING(formatstr,...)
Definition logger.h:227
Definition account.h:55
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 ring_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_ISDIR(mode)
Definition windirent.h:183
#define S_IRUSR
Definition windirent.h:105
#define S_IXUSR
Definition windirent.h:115