LCOV - code coverage report
Current view: top level - src/connectivity/security - tlsvalidator.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 0 357 0.0 %
Date: 2024-05-12 08:41:20 Functions: 0 54 0.0 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
       5             :  *  Author: Vittorio Giovara <vittorio.giovara@savoirfairelinux.com>
       6             :  *  Author: Emmanuel Lepage <emmanuel.lepage@savoirfairelinux.com>
       7             :  *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
       8             :  *
       9             :  *  This program is free software; you can redistribute it and/or modify
      10             :  *  it under the terms of the GNU General Public License as published by
      11             :  *  the Free Software Foundation; either version 3 of the License, or
      12             :  *  (at your option) any later version.
      13             :  *
      14             :  *  This program is distributed in the hope that it will be useful,
      15             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :  *  GNU General Public License for more details.
      18             :  *
      19             :  *  You should have received a copy of the GNU General Public License
      20             :  *  along with this program; if not, write to the Free Software
      21             :  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
      22             :  */
      23             : 
      24             : #include "tlsvalidator.h"
      25             : 
      26             : #ifdef HAVE_CONFIG_H
      27             : #include "config.h"
      28             : #endif
      29             : 
      30             : #include <dhtnet/certstore.h>
      31             : 
      32             : #include "fileutils.h"
      33             : #include "logger.h"
      34             : #include "security_const.h"
      35             : 
      36             : #include <sstream>
      37             : #include <iomanip>
      38             : 
      39             : #include <cstdio>
      40             : #include <cerrno>
      41             : #include <cassert>
      42             : #include <ctime>
      43             : 
      44             : #include <sys/types.h>
      45             : #include <sys/stat.h>
      46             : 
      47             : #ifndef _MSC_VER
      48             : #include <libgen.h>
      49             : #endif
      50             : 
      51             : #ifndef _WIN32
      52             : #include <sys/socket.h>
      53             : #include <netinet/tcp.h>
      54             : #include <netinet/in.h>
      55             : #include <netdb.h>
      56             : #else
      57             : #ifndef _MSC_VER
      58             : #define close(x) closesocket(x)
      59             : #endif
      60             : #endif
      61             : #include <unistd.h>
      62             : #include <fcntl.h>
      63             : 
      64             : #ifdef _MSC_VER
      65             : #include "windirent.h"
      66             : #endif
      67             : 
      68             : namespace jami {
      69             : namespace tls {
      70             : 
      71             : // Map the internal ring Enum class of the exported names
      72             : 
      73             : const EnumClassNames<TlsValidator::CheckValues> TlsValidator::CheckValuesNames = {{
      74             :     /* CheckValues                        Name                         */
      75             :     /* PASSED      */ libjami::Certificate::CheckValuesNames::PASSED,
      76             :     /* FAILED      */ libjami::Certificate::CheckValuesNames::FAILED,
      77             :     /* UNSUPPORTED */ libjami::Certificate::CheckValuesNames::UNSUPPORTED,
      78             :     /* ISO_DATE    */ libjami::Certificate::CheckValuesNames::ISO_DATE,
      79             :     /* CUSTOM      */ libjami::Certificate::CheckValuesNames::CUSTOM,
      80             :     /* CUSTOM      */ libjami::Certificate::CheckValuesNames::DATE,
      81             : }};
      82             : 
      83             : const CallbackMatrix1D<TlsValidator::CertificateCheck, TlsValidator, TlsValidator::CheckResult>
      84             :     TlsValidator::checkCallback = {{
      85             :         /*      CertificateCheck                       Callback                            */
      86             :         /*HAS_PRIVATE_KEY                  */ &TlsValidator::hasPrivateKey,
      87             :         /*EXPIRED                          */ &TlsValidator::notExpired,
      88             :         /*STRONG_SIGNING                   */ &TlsValidator::strongSigning,
      89             :         /*NOT_SELF_SIGNED                  */ &TlsValidator::notSelfSigned,
      90             :         /*KEY_MATCH                        */ &TlsValidator::keyMatch,
      91             :         /*PRIVATE_KEY_STORAGE_PERMISSION   */ &TlsValidator::privateKeyStoragePermissions,
      92             :         /*PUBLIC_KEY_STORAGE_PERMISSION    */ &TlsValidator::publicKeyStoragePermissions,
      93             :         /*PRIVATEKEY_DIRECTORY_PERMISSIONS */ &TlsValidator::privateKeyDirectoryPermissions,
      94             :         /*PUBLICKEY_DIRECTORY_PERMISSIONS  */ &TlsValidator::publicKeyDirectoryPermissions,
      95             :         /*PRIVATE_KEY_STORAGE_LOCATION     */ &TlsValidator::privateKeyStorageLocation,
      96             :         /*PUBLIC_KEY_STORAGE_LOCATION      */ &TlsValidator::publicKeyStorageLocation,
      97             :         /*PRIVATE_KEY_SELINUX_ATTRIBUTES   */ &TlsValidator::privateKeySelinuxAttributes,
      98             :         /*PUBLIC_KEY_SELINUX_ATTRIBUTES    */ &TlsValidator::publicKeySelinuxAttributes,
      99             :         /*EXIST                            */ &TlsValidator::exist,
     100             :         /*VALID                            */ &TlsValidator::valid,
     101             :         /*VALID_AUTHORITY                  */ &TlsValidator::validAuthority,
     102             :         /*KNOWN_AUTHORITY                  */ &TlsValidator::knownAuthority,
     103             :         /*NOT_REVOKED                      */ &TlsValidator::notRevoked,
     104             :         /*AUTHORITY_MISMATCH               */ &TlsValidator::authorityMatch,
     105             :         /*UNEXPECTED_OWNER                 */ &TlsValidator::expectedOwner,
     106             :         /*NOT_ACTIVATED                    */ &TlsValidator::activated,
     107             :     }};
     108             : 
     109             : const CallbackMatrix1D<TlsValidator::CertificateDetails, TlsValidator, TlsValidator::CheckResult>
     110             :     TlsValidator::getterCallback = {{
     111             :         /* EXPIRATION_DATE              */ &TlsValidator::getExpirationDate,
     112             :         /* ACTIVATION_DATE              */ &TlsValidator::getActivationDate,
     113             :         /* REQUIRE_PRIVATE_KEY_PASSWORD */ &TlsValidator::requirePrivateKeyPassword,
     114             :         /* PUBLIC_SIGNATURE             */ &TlsValidator::getPublicSignature,
     115             :         /* VERSION_NUMBER               */ &TlsValidator::getVersionNumber,
     116             :         /* SERIAL_NUMBER                */ &TlsValidator::getSerialNumber,
     117             :         /* ISSUER                       */ &TlsValidator::getIssuer,
     118             :         /* SUBJECT_KEY_ALGORITHM        */ &TlsValidator::getSubjectKeyAlgorithm,
     119             :         /* CN                           */ &TlsValidator::getCN,
     120             :         /* N                            */ &TlsValidator::getN,
     121             :         /* O                            */ &TlsValidator::getO,
     122             :         /* SIGNATURE_ALGORITHM          */ &TlsValidator::getSignatureAlgorithm,
     123             :         /* MD5_FINGERPRINT              */ &TlsValidator::getMd5Fingerprint,
     124             :         /* SHA1_FINGERPRINT             */ &TlsValidator::getSha1Fingerprint,
     125             :         /* PUBLIC_KEY_ID                */ &TlsValidator::getPublicKeyId,
     126             :         /* ISSUER_DN                    */ &TlsValidator::getIssuerDN,
     127             :         /* NEXT_EXPECTED_UPDATE_DATE    */ &TlsValidator::getIssuerDN, // TODO
     128             :         /* OUTGOING_SERVER              */ &TlsValidator::outgoingServer,
     129             :         /* IS_CA                        */ &TlsValidator::isCA,
     130             :     }};
     131             : 
     132             : const Matrix1D<TlsValidator::CertificateCheck, TlsValidator::CheckValuesType>
     133             :     TlsValidator::enforcedCheckType = {{
     134             :         /*      CertificateCheck                    Callback        */
     135             :         /*HAS_PRIVATE_KEY                  */ CheckValuesType::BOOLEAN,
     136             :         /*EXPIRED                          */ CheckValuesType::BOOLEAN,
     137             :         /*STRONG_SIGNING                   */ CheckValuesType::BOOLEAN,
     138             :         /*NOT_SELF_SIGNED                  */ CheckValuesType::BOOLEAN,
     139             :         /*KEY_MATCH                        */ CheckValuesType::BOOLEAN,
     140             :         /*PRIVATE_KEY_STORAGE_PERMISSION   */ CheckValuesType::BOOLEAN,
     141             :         /*PUBLIC_KEY_STORAGE_PERMISSION    */ CheckValuesType::BOOLEAN,
     142             :         /*PRIVATEKEY_DIRECTORY_PERMISSIONS */ CheckValuesType::BOOLEAN,
     143             :         /*PUBLICKEY_DIRECTORY_PERMISSIONS  */ CheckValuesType::BOOLEAN,
     144             :         /*PRIVATE_KEY_STORAGE_LOCATION     */ CheckValuesType::BOOLEAN,
     145             :         /*PUBLIC_KEY_STORAGE_LOCATION      */ CheckValuesType::BOOLEAN,
     146             :         /*PRIVATE_KEY_SELINUX_ATTRIBUTES   */ CheckValuesType::BOOLEAN,
     147             :         /*PUBLIC_KEY_SELINUX_ATTRIBUTES    */ CheckValuesType::BOOLEAN,
     148             :         /*EXIST                            */ CheckValuesType::BOOLEAN,
     149             :         /*VALID                            */ CheckValuesType::BOOLEAN,
     150             :         /*VALID_AUTHORITY                  */ CheckValuesType::BOOLEAN,
     151             :         /*KNOWN_AUTHORITY                  */ CheckValuesType::BOOLEAN,
     152             :         /*NOT_REVOKED                      */ CheckValuesType::BOOLEAN,
     153             :         /*AUTHORITY_MISMATCH               */ CheckValuesType::BOOLEAN,
     154             :         /*UNEXPECTED_OWNER                 */ CheckValuesType::BOOLEAN,
     155             :         /*NOT_ACTIVATED                    */ CheckValuesType::BOOLEAN,
     156             :     }};
     157             : 
     158             : const EnumClassNames<TlsValidator::CertificateCheck> TlsValidator::CertificateCheckNames = {{
     159             :     /*      CertificateCheck                                   Name */
     160             :     /*HAS_PRIVATE_KEY                  */ libjami::Certificate::ChecksNames::HAS_PRIVATE_KEY,
     161             :     /*EXPIRED                          */ libjami::Certificate::ChecksNames::EXPIRED,
     162             :     /*STRONG_SIGNING                   */ libjami::Certificate::ChecksNames::STRONG_SIGNING,
     163             :     /*NOT_SELF_SIGNED                  */ libjami::Certificate::ChecksNames::NOT_SELF_SIGNED,
     164             :     /*KEY_MATCH                        */ libjami::Certificate::ChecksNames::KEY_MATCH,
     165             :     /*PRIVATE_KEY_STORAGE_PERMISSION   */ libjami::Certificate::ChecksNames::PRIVATE_KEY_STORAGE_PERMISSION,
     166             :     /*PUBLIC_KEY_STORAGE_PERMISSION    */ libjami::Certificate::ChecksNames::PUBLIC_KEY_STORAGE_PERMISSION,
     167             :     /*PRIVATEKEY_DIRECTORY_PERMISSIONS */ libjami::Certificate::ChecksNames::PRIVATE_KEY_DIRECTORY_PERMISSIONS,
     168             :     /*PUBLICKEY_DIRECTORY_PERMISSIONS  */ libjami::Certificate::ChecksNames::PUBLIC_KEY_DIRECTORY_PERMISSIONS,
     169             :     /*PRIVATE_KEY_STORAGE_LOCATION     */ libjami::Certificate::ChecksNames::PRIVATE_KEY_STORAGE_LOCATION,
     170             :     /*PUBLIC_KEY_STORAGE_LOCATION      */ libjami::Certificate::ChecksNames::PUBLIC_KEY_STORAGE_LOCATION,
     171             :     /*PRIVATE_KEY_SELINUX_ATTRIBUTES   */ libjami::Certificate::ChecksNames::PRIVATE_KEY_SELINUX_ATTRIBUTES,
     172             :     /*PUBLIC_KEY_SELINUX_ATTRIBUTES    */ libjami::Certificate::ChecksNames::PUBLIC_KEY_SELINUX_ATTRIBUTES,
     173             :     /*EXIST                            */ libjami::Certificate::ChecksNames::EXIST,
     174             :     /*VALID                            */ libjami::Certificate::ChecksNames::VALID,
     175             :     /*VALID_AUTHORITY                  */ libjami::Certificate::ChecksNames::VALID_AUTHORITY,
     176             :     /*KNOWN_AUTHORITY                  */ libjami::Certificate::ChecksNames::KNOWN_AUTHORITY,
     177             :     /*NOT_REVOKED                      */ libjami::Certificate::ChecksNames::NOT_REVOKED,
     178             :     /*AUTHORITY_MISMATCH               */ libjami::Certificate::ChecksNames::AUTHORITY_MISMATCH,
     179             :     /*UNEXPECTED_OWNER                 */ libjami::Certificate::ChecksNames::UNEXPECTED_OWNER,
     180             :     /*NOT_ACTIVATED                    */ libjami::Certificate::ChecksNames::NOT_ACTIVATED,
     181             : }};
     182             : 
     183             : const EnumClassNames<TlsValidator::CertificateDetails> TlsValidator::CertificateDetailsNames = {{
     184             :     /* EXPIRATION_DATE              */ libjami::Certificate::DetailsNames::EXPIRATION_DATE,
     185             :     /* ACTIVATION_DATE              */ libjami::Certificate::DetailsNames::ACTIVATION_DATE,
     186             :     /* REQUIRE_PRIVATE_KEY_PASSWORD */ libjami::Certificate::DetailsNames::REQUIRE_PRIVATE_KEY_PASSWORD,
     187             :     /* PUBLIC_SIGNATURE             */ libjami::Certificate::DetailsNames::PUBLIC_SIGNATURE,
     188             :     /* VERSION_NUMBER               */ libjami::Certificate::DetailsNames::VERSION_NUMBER,
     189             :     /* SERIAL_NUMBER                */ libjami::Certificate::DetailsNames::SERIAL_NUMBER,
     190             :     /* ISSUER                       */ libjami::Certificate::DetailsNames::ISSUER,
     191             :     /* SUBJECT_KEY_ALGORITHM        */ libjami::Certificate::DetailsNames::SUBJECT_KEY_ALGORITHM,
     192             :     /* CN                           */ libjami::Certificate::DetailsNames::CN,
     193             :     /* N                            */ libjami::Certificate::DetailsNames::N,
     194             :     /* O                            */ libjami::Certificate::DetailsNames::O,
     195             :     /* SIGNATURE_ALGORITHM          */ libjami::Certificate::DetailsNames::SIGNATURE_ALGORITHM,
     196             :     /* MD5_FINGERPRINT              */ libjami::Certificate::DetailsNames::MD5_FINGERPRINT,
     197             :     /* SHA1_FINGERPRINT             */ libjami::Certificate::DetailsNames::SHA1_FINGERPRINT,
     198             :     /* PUBLIC_KEY_ID                */ libjami::Certificate::DetailsNames::PUBLIC_KEY_ID,
     199             :     /* ISSUER_DN                    */ libjami::Certificate::DetailsNames::ISSUER_DN,
     200             :     /* NEXT_EXPECTED_UPDATE_DATE    */ libjami::Certificate::DetailsNames::NEXT_EXPECTED_UPDATE_DATE,
     201             :     /* OUTGOING_SERVER              */ libjami::Certificate::DetailsNames::OUTGOING_SERVER,
     202             :     /* IS_CA                        */ libjami::Certificate::DetailsNames::IS_CA,
     203             : 
     204             : }};
     205             : 
     206             : const EnumClassNames<const TlsValidator::CheckValuesType> TlsValidator::CheckValuesTypeNames = {{
     207             :     /*   Type                            Name                          */
     208             :     /* BOOLEAN  */ libjami::Certificate::ChecksValuesTypesNames::BOOLEAN,
     209             :     /* ISO_DATE */ libjami::Certificate::ChecksValuesTypesNames::ISO_DATE,
     210             :     /* CUSTOM   */ libjami::Certificate::ChecksValuesTypesNames::CUSTOM,
     211             :     /* NUMBER   */ libjami::Certificate::ChecksValuesTypesNames::NUMBER,
     212             : }};
     213             : 
     214             : const Matrix2D<TlsValidator::CheckValuesType, TlsValidator::CheckValues, bool>
     215             :     TlsValidator::acceptedCheckValuesResult = {{
     216             :         /*   Type          PASSED    FAILED   UNSUPPORTED   ISO_DATE    CUSTOM    NUMBER */
     217             :         /* BOOLEAN  */ {{true, true, true, false, false, false}},
     218             :         /* ISO_DATE */ {{false, false, true, true, false, false}},
     219             :         /* CUSTOM   */ {{false, false, true, false, true, false}},
     220             :         /* NUMBER   */ {{false, false, true, false, false, true}},
     221             :     }};
     222             : 
     223           0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::vector<std::vector<uint8_t>>& crtChain)
     224           0 :     : TlsValidator(certStore, std::make_shared<dht::crypto::Certificate>(crtChain.begin(), crtChain.end()))
     225           0 : {}
     226             : 
     227           0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore,
     228             :                            const std::string& certificate,
     229             :                            const std::string& privatekey,
     230             :                            const std::string& privatekeyPasswd,
     231           0 :                            const std::string& caList)
     232           0 :     : certStore_(certStore)
     233           0 :     , certificatePath_(certificate)
     234           0 :     , privateKeyPath_(privatekey)
     235           0 :     , caListPath_(caList)
     236           0 :     , certificateFound_(false)
     237             : {
     238           0 :     std::vector<uint8_t> certificate_raw;
     239             :     try {
     240           0 :         certificate_raw = fileutils::loadFile(certificatePath_);
     241           0 :         certificateFileFound_ = true;
     242           0 :     } catch (const std::exception& e) {
     243           0 :     }
     244             : 
     245           0 :     if (not certificate_raw.empty()) {
     246             :         try {
     247           0 :             x509crt_ = std::make_shared<dht::crypto::Certificate>(certificate_raw);
     248           0 :             certificateContent_ = x509crt_->getPacked();
     249           0 :             certificateFound_ = true;
     250           0 :         } catch (const std::exception& e) {
     251           0 :         }
     252             :     }
     253             : 
     254             :     try {
     255           0 :         auto privateKeyContent = fileutils::loadFile(privateKeyPath_);
     256           0 :         dht::crypto::PrivateKey key_tmp(privateKeyContent, privatekeyPasswd);
     257           0 :         privateKeyFound_ = true;
     258           0 :         privateKeyPassword_ = not privatekeyPasswd.empty();
     259           0 :         privateKeyMatch_ = key_tmp.getPublicKey().getId() == x509crt_->getId();
     260           0 :     } catch (const dht::crypto::DecryptError& d) {
     261             :         // If we encounter a DecryptError, it means the private key exists and is encrypted,
     262             :         // otherwise we would get some other exception.
     263           0 :         JAMI_WARN("decryption error: %s", d.what());
     264           0 :         privateKeyFound_ = true;
     265           0 :         privateKeyPassword_ = true;
     266           0 :     } catch (const std::exception& e) {
     267           0 :         JAMI_WARN("creation failed: %s", e.what());
     268           0 :     }
     269           0 : }
     270             : 
     271           0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::vector<uint8_t>& certificate_raw)
     272           0 :     : certStore_(certStore)
     273             : {
     274             :     try {
     275           0 :         x509crt_ = std::make_shared<dht::crypto::Certificate>(certificate_raw);
     276           0 :         certificateContent_ = x509crt_->getPacked();
     277           0 :         certificateFound_ = true;
     278           0 :     } catch (const std::exception& e) {
     279           0 :         throw TlsValidatorException("Can't load certificate");
     280           0 :     }
     281           0 : }
     282             : 
     283           0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::shared_ptr<dht::crypto::Certificate>& crt)
     284           0 :     : certStore_(certStore)
     285           0 :     , certificateFound_(true)
     286             : {
     287             :     try {
     288           0 :         if (not crt)
     289           0 :             throw std::invalid_argument("Certificate must be set");
     290           0 :         x509crt_ = crt;
     291           0 :         certificateContent_ = x509crt_->getPacked();
     292           0 :     } catch (const std::exception& e) {
     293           0 :         throw TlsValidatorException("Can't load certificate");
     294           0 :     }
     295           0 : }
     296             : 
     297           0 : TlsValidator::~TlsValidator() {}
     298             : 
     299             : /**
     300             :  * This method convert results into validated strings
     301             :  *
     302             :  * @todo The date should be validated, this is currently not an issue
     303             :  */
     304             : std::string
     305           0 : TlsValidator::getStringValue(const TlsValidator::CertificateCheck check,
     306             :                              const TlsValidator::CheckResult result)
     307             : {
     308           0 :     assert(acceptedCheckValuesResult[enforcedCheckType[check]][result.first]);
     309             : 
     310           0 :     switch (result.first) {
     311           0 :     case CheckValues::PASSED:
     312             :     case CheckValues::FAILED:
     313             :     case CheckValues::UNSUPPORTED:
     314           0 :         return CheckValuesNames[result.first];
     315           0 :     case CheckValues::ISO_DATE:
     316             :         // TODO validate date
     317             :         // return CheckValues::FAILED;
     318           0 :         return result.second;
     319           0 :     case CheckValues::NUMBER:
     320             :         // TODO Validate numbers
     321             :     case CheckValues::CUSTOM:
     322           0 :         return result.second;
     323           0 :     default:
     324             :         // Consider any other case (such as forced int->CheckValues casting) as failed
     325           0 :         return CheckValuesNames[CheckValues::FAILED];
     326             :     };
     327             : }
     328             : 
     329             : /**
     330             :  * Check if all boolean check passed
     331             :  * return true if there was no ::FAILED checks
     332             :  *
     333             :  * Checks functions are not "const", so this function isn't
     334             :  */
     335             : bool
     336           0 : TlsValidator::isValid(bool verbose)
     337             : {
     338           0 :     for (const CertificateCheck check : Matrix0D<CertificateCheck>()) {
     339           0 :         if (enforcedCheckType[check] == CheckValuesType::BOOLEAN) {
     340           0 :             if (((this->*(checkCallback[check]))()).first == CheckValues::FAILED) {
     341           0 :                 if (verbose)
     342           0 :                     JAMI_WARN("Check failed: %s", CertificateCheckNames[check]);
     343           0 :                 return false;
     344             :             }
     345             :         }
     346             :     }
     347           0 :     return true;
     348             : }
     349             : 
     350             : /**
     351             :  * Convert all checks results into a string map
     352             :  */
     353             : std::map<std::string, std::string>
     354           0 : TlsValidator::getSerializedChecks()
     355             : {
     356           0 :     std::map<std::string, std::string> ret;
     357           0 :     if (not certificateFound_) {
     358             :         // Instead of checking `certificateFound` everywhere, handle it once
     359           0 :         ret[CertificateCheckNames[CertificateCheck::EXIST]] = getStringValue(CertificateCheck::EXIST,
     360           0 :                                                                              exist());
     361             :     } else {
     362           0 :         for (const CertificateCheck check : Matrix0D<CertificateCheck>())
     363           0 :             ret[CertificateCheckNames[check]] = getStringValue(check,
     364           0 :                                                                (this->*(checkCallback[check]))());
     365             :     }
     366             : 
     367           0 :     return ret;
     368           0 : }
     369             : 
     370             : /**
     371             :  * Get a map with all common certificate details
     372             :  */
     373             : std::map<std::string, std::string>
     374           0 : TlsValidator::getSerializedDetails()
     375             : {
     376           0 :     std::map<std::string, std::string> ret;
     377           0 :     if (certificateFound_) {
     378           0 :         for (const CertificateDetails det : Matrix0D<CertificateDetails>()) {
     379           0 :             const CheckResult r = (this->*(getterCallback[det]))();
     380           0 :             std::string val;
     381             :             // TODO move this to a fuction
     382           0 :             switch (r.first) {
     383           0 :             case CheckValues::PASSED:
     384             :             case CheckValues::FAILED:
     385             :             case CheckValues::UNSUPPORTED:
     386           0 :                 val = CheckValuesNames[r.first];
     387           0 :                 break;
     388           0 :             case CheckValues::ISO_DATE:
     389             :                 // TODO validate date
     390             :             case CheckValues::NUMBER:
     391             :                 // TODO Validate numbers
     392             :             case CheckValues::CUSTOM:
     393             :             default:
     394           0 :                 val = r.second;
     395           0 :                 break;
     396             :             };
     397           0 :             ret[CertificateDetailsNames[det]] = val;
     398           0 :         }
     399             :     }
     400           0 :     return ret;
     401           0 : }
     402             : 
     403             : /**
     404             :  * Helper method to return UNSUPPORTED when an error is detected
     405             :  */
     406             : static TlsValidator::CheckResult
     407           0 : checkError(int err, char* copy_buffer, size_t size)
     408             : {
     409             :     return TlsValidator::TlsValidator::CheckResult(err == GNUTLS_E_SUCCESS
     410           0 :                                                        ? TlsValidator::CheckValues::CUSTOM
     411             :                                                        : TlsValidator::CheckValues::UNSUPPORTED,
     412             :                                                    err == GNUTLS_E_SUCCESS
     413           0 :                                                        ? std::string(copy_buffer, size)
     414           0 :                                                        : "");
     415             : }
     416             : 
     417             : /**
     418             :  * Some fields, such as the binary signature need to be converted to an
     419             :  * ASCII-hexadecimal representation before being sent to DBus as it will cause the
     420             :  * process to assert
     421             :  */
     422             : static std::string
     423           0 : binaryToHex(const uint8_t* input, size_t input_sz)
     424             : {
     425           0 :     std::ostringstream ret;
     426           0 :     ret << std::hex;
     427           0 :     for (size_t i = 0; i < input_sz; i++)
     428           0 :         ret << std::setfill('0') << std::setw(2) << (unsigned) input[i];
     429           0 :     return ret.str();
     430           0 : }
     431             : 
     432             : /**
     433             :  * Convert a time_t to an ISO date string
     434             :  */
     435             : static TlsValidator::CheckResult
     436           0 : formatDate(const time_t time)
     437             : {
     438             :     char buffer[12];
     439           0 :     struct tm* timeinfo = localtime(&time);
     440           0 :     strftime(buffer, sizeof(buffer), "%F", timeinfo);
     441           0 :     return TlsValidator::CheckResult(TlsValidator::CheckValues::ISO_DATE, buffer);
     442             : }
     443             : 
     444             : /**
     445             :  * Helper method to return UNSUPPORTED when an error is detected
     446             :  *
     447             :  * This method also convert the output to binary
     448             :  */
     449             : static TlsValidator::CheckResult
     450           0 : checkBinaryError(int err, char* copy_buffer, size_t resultSize)
     451             : {
     452           0 :     if (err == GNUTLS_E_SUCCESS)
     453           0 :         return TlsValidator::CheckResult(TlsValidator::CheckValues::CUSTOM,
     454           0 :                                          binaryToHex(reinterpret_cast<uint8_t*>(copy_buffer),
     455           0 :                                                      resultSize));
     456             :     else
     457           0 :         return TlsValidator::CheckResult(TlsValidator::CheckValues::UNSUPPORTED, "");
     458             : }
     459             : 
     460             : /**
     461             :  * Check if a certificate has been signed with the authority
     462             :  */
     463             : unsigned int
     464           0 : TlsValidator::compareToCa()
     465             : {
     466             :     // Don't check unless the certificate changed
     467           0 :     if (caChecked_)
     468           0 :         return caValidationOutput_;
     469             : 
     470             :     // build the CA trusted list
     471             :     gnutls_x509_trust_list_t trust;
     472           0 :     gnutls_x509_trust_list_init(&trust, 0);
     473             : 
     474           0 :     auto root_cas = certStore_.getTrustedCertificates();
     475           0 :     auto err = gnutls_x509_trust_list_add_cas(trust, root_cas.data(), root_cas.size(), 0);
     476           0 :     if (err)
     477           0 :         JAMI_WARN("gnutls_x509_trust_list_add_cas failed: %s", gnutls_strerror(err));
     478             : 
     479           0 :     if (not caListPath_.empty()) {
     480           0 :         if (std::filesystem::is_directory(caListPath_))
     481           0 :             gnutls_x509_trust_list_add_trust_dir(trust,
     482             :                                                  caListPath_.c_str(),
     483             :                                                  nullptr,
     484             :                                                  GNUTLS_X509_FMT_PEM,
     485             :                                                  0,
     486             :                                                  0);
     487             :         else
     488           0 :             gnutls_x509_trust_list_add_trust_file(trust,
     489             :                                                   caListPath_.c_str(),
     490             :                                                   nullptr,
     491             :                                                   GNUTLS_X509_FMT_PEM,
     492             :                                                   0,
     493             :                                                   0);
     494             :     }
     495             : 
     496             :     // build the certificate chain
     497           0 :     auto crts = x509crt_->getChain();
     498           0 :     err = gnutls_x509_trust_list_verify_crt2(trust,
     499             :                                              crts.data(),
     500           0 :                                              crts.size(),
     501             :                                              nullptr,
     502             :                                              0,
     503             :                                              GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_MEDIUM),
     504             :                                              &caValidationOutput_,
     505             :                                              nullptr);
     506             : 
     507           0 :     gnutls_x509_trust_list_deinit(trust, true);
     508             : 
     509           0 :     if (err) {
     510           0 :         JAMI_WARN("gnutls_x509_trust_list_verify_crt2 failed: %s", gnutls_strerror(err));
     511           0 :         return GNUTLS_CERT_SIGNER_NOT_FOUND;
     512             :     }
     513             : 
     514           0 :     caChecked_ = true;
     515           0 :     return caValidationOutput_;
     516           0 : }
     517             : 
     518             : #if 0 // disabled, see .h for reason
     519             : /**
     520             :  * Verify if a hostname is valid
     521             :  *
     522             :  * @warning This function is blocking
     523             :  *
     524             :  * Mainly based on Fedora Defensive Coding tutorial
     525             :  * https://docs.fedoraproject.org/en-US/Fedora_Security_Team/1/html/Defensive_Coding/sect-Defensive_Coding-TLS-Client-GNUTLS.html
     526             :  */
     527             : int TlsValidator::verifyHostnameCertificate(const std::string& host, const uint16_t port)
     528             : {
     529             :     int err, arg, res = -1;
     530             :     unsigned int status = (unsigned) -1;
     531             :     const char *errptr = nullptr;
     532             :     gnutls_session_t session = nullptr;
     533             :     gnutls_certificate_credentials_t cred = nullptr;
     534             :     unsigned int certslen = 0;
     535             :     const gnutls_datum_t *certs = nullptr;
     536             :     gnutls_x509_crt_t cert = nullptr;
     537             : 
     538             :     char buf[4096];
     539             :     int sockfd;
     540             :     struct sockaddr_in name;
     541             :     struct hostent *hostinfo;
     542             :     const int one = 1;
     543             :     fd_set fdset;
     544             :     struct timeval tv;
     545             : 
     546             :     if (!host.size() || !port) {
     547             :         JAMI_ERR("Wrong parameters used - host %s, port %d.", host.c_str(), port);
     548             :         return res;
     549             :     }
     550             : 
     551             :     /* Create the socket. */
     552             :     sockfd = socket (PF_INET, SOCK_STREAM, 0);
     553             :     if (sockfd < 0) {
     554             :         JAMI_ERR("Could not create socket.");
     555             :         return res;
     556             :     }
     557             :     /* Set non-blocking so we can dected timeouts. */
     558             :     arg = fcntl(sockfd, F_GETFL, nullptr);
     559             :     if (arg < 0)
     560             :         goto out;
     561             :     arg |= O_NONBLOCK;
     562             :     if (fcntl(sockfd, F_SETFL, arg) < 0)
     563             :         goto out;
     564             : 
     565             :     /* Give the socket a name. */
     566             :     memset(&name, 0, sizeof(name));
     567             :     name.sin_family = AF_INET;
     568             :     name.sin_port = htons(port);
     569             :     hostinfo = gethostbyname(host.c_str());
     570             :     if (hostinfo == nullptr) {
     571             :         JAMI_ERR("Unknown host %s.", host.c_str());
     572             :         goto out;
     573             :     }
     574             :     name.sin_addr = *(struct in_addr *)hostinfo->h_addr;
     575             :     /* Connect to the address specified in name struct. */
     576             :     err = connect(sockfd, (struct sockaddr *)&name, sizeof(name));
     577             :     if (err < 0) {
     578             :         /* Connection in progress, use select to see if timeout is reached. */
     579             :         if (errno == EINPROGRESS) {
     580             :             do {
     581             :                 FD_ZERO(&fdset);
     582             :                 FD_SET(sockfd, &fdset);
     583             :                 tv.tv_sec = 10;     // 10 second timeout
     584             :                 tv.tv_usec = 0;
     585             :                 err = select(sockfd + 1, nullptr, &fdset, nullptr, &tv);
     586             :                 if (err < 0 && errno != EINTR) {
     587             :                     JAMI_ERR("Could not connect to hostname %s at port %d",
     588             :                           host.c_str(), port);
     589             :                     goto out;
     590             :                 } else if (err > 0) {
     591             :                     /* Select returned, if so_error is clean we are ready. */
     592             :                     int so_error;
     593             :                     socklen_t len = sizeof(so_error);
     594             :                     getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len);
     595             : 
     596             :                     if (so_error) {
     597             :                         JAMI_ERR("Connection delayed.");
     598             :                         goto out;
     599             :                     }
     600             :                     break;  // exit do-while loop
     601             :                 } else {
     602             :                     JAMI_ERR("Connection timeout.");
     603             :                     goto out;
     604             :                 }
     605             :             } while(1);
     606             :         } else {
     607             :             JAMI_ERR("Could not connect to hostname %s at port %d", host.c_str(), port);
     608             :             goto out;
     609             :         }
     610             :     }
     611             :     /* Set the socked blocking again. */
     612             :     arg = fcntl(sockfd, F_GETFL, nullptr);
     613             :     if (arg < 0)
     614             :         goto out;
     615             :     arg &= ~O_NONBLOCK;
     616             :     if (fcntl(sockfd, F_SETFL, arg) < 0)
     617             :         goto out;
     618             : 
     619             :     /* Disable Nagle algorithm that slows down the SSL handshake. */
     620             :     err = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
     621             :     if (err < 0) {
     622             :         JAMI_ERR("Could not set TCP_NODELAY.");
     623             :         goto out;
     624             :     }
     625             : 
     626             : 
     627             :     /* Load the trusted CA certificates. */
     628             :     err = gnutls_certificate_allocate_credentials(&cred);
     629             :     if (err != GNUTLS_E_SUCCESS) {
     630             :         JAMI_ERR("Could not allocate credentials - %s", gnutls_strerror(err));
     631             :         goto out;
     632             :     }
     633             :     err = gnutls_certificate_set_x509_system_trust(cred);
     634             :     if (err != GNUTLS_E_SUCCESS) {
     635             :         JAMI_ERR("Could not load credentials.");
     636             :         goto out;
     637             :     }
     638             : 
     639             :     /* Create the session object. */
     640             :     err = gnutls_init(&session, GNUTLS_CLIENT);
     641             :     if (err != GNUTLS_E_SUCCESS) {
     642             :         JAMI_ERR("Could not init session -%s\n", gnutls_strerror(err));
     643             :         goto out;
     644             :     }
     645             : 
     646             :     /* Configure the cipher preferences. The default set should be good enough. */
     647             :     err = gnutls_priority_set_direct(session, "NORMAL", &errptr);
     648             :     if (err != GNUTLS_E_SUCCESS) {
     649             :         JAMI_ERR("Could not set up ciphers - %s (%s)", gnutls_strerror(err), errptr);
     650             :         goto out;
     651             :     }
     652             : 
     653             :     /* Install the trusted certificates. */
     654             :     err = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred);
     655             :     if (err != GNUTLS_E_SUCCESS) {
     656             :         JAMI_ERR("Could not set up credentials - %s", gnutls_strerror(err));
     657             :         goto out;
     658             :     }
     659             : 
     660             :     /* Associate the socket with the session object and set the server name. */
     661             :     gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) (uintptr_t) sockfd);
     662             :     err = gnutls_server_name_set(session, GNUTLS_NAME_DNS, host.c_str(), host.size());
     663             :     if (err != GNUTLS_E_SUCCESS) {
     664             :         JAMI_ERR("Could not set server name - %s", gnutls_strerror(err));
     665             :         goto out;
     666             :     }
     667             : 
     668             :     /* Establish the connection. */
     669             :     err = gnutls_handshake(session);
     670             :     if (err != GNUTLS_E_SUCCESS) {
     671             :         JAMI_ERR("Handshake failed - %s", gnutls_strerror(err));
     672             :         goto out;
     673             :     }
     674             :     /* Obtain the server certificate chain. The server certificate
     675             :      * itself is stored in the first element of the array. */
     676             :     certs = gnutls_certificate_get_peers(session, &certslen);
     677             :     if (certs == nullptr || certslen == 0) {
     678             :         JAMI_ERR("Could not obtain peer certificate - %s", gnutls_strerror(err));
     679             :         goto out;
     680             :     }
     681             : 
     682             :     /* Validate the certificate chain. */
     683             :     err = gnutls_certificate_verify_peers2(session, &status);
     684             :     if (err != GNUTLS_E_SUCCESS) {
     685             :         JAMI_ERR("Could not verify the certificate chain - %s", gnutls_strerror(err));
     686             :         goto out;
     687             :     }
     688             :     if (status != 0) {
     689             :         gnutls_datum_t msg;
     690             : #if GNUTLS_VERSION_AT_LEAST_3_1_4
     691             :         int type = gnutls_certificate_type_get(session);
     692             :         err = gnutls_certificate_verification_status_print(status, type, &out, 0);
     693             : #else
     694             :         err = -1;
     695             : #endif
     696             :         if (err == 0) {
     697             :             JAMI_ERR("Certificate validation failed - %s\n", msg.data);
     698             :             gnutls_free(msg.data);
     699             :             goto out;
     700             :         } else {
     701             :             JAMI_ERR("Certificate validation failed with code 0x%x.", status);
     702             :             goto out;
     703             :         }
     704             :     }
     705             : 
     706             :     /* Match the peer certificate against the hostname.
     707             :      * We can only obtain a set of DER-encoded certificates from the
     708             :      * session object, so we have to re-parse the peer certificate into
     709             :      * a certificate object. */
     710             : 
     711             :     err = gnutls_x509_crt_init(&cert);
     712             :     if (err != GNUTLS_E_SUCCESS) {
     713             :         JAMI_ERR("Could not init certificate - %s", gnutls_strerror(err));
     714             :         goto out;
     715             :     }
     716             : 
     717             :     /* The peer certificate is the first certificate in the list. */
     718             :     err = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_PEM);
     719             :     if (err != GNUTLS_E_SUCCESS)
     720             :         err = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER);
     721             :     if (err != GNUTLS_E_SUCCESS) {
     722             :         JAMI_ERR("Could not read peer certificate - %s", gnutls_strerror(err));
     723             :         goto out;
     724             :     }
     725             :     /* Finally check if the hostnames match. */
     726             :     err = gnutls_x509_crt_check_hostname(cert, host.c_str());
     727             :     if (err == 0) {
     728             :         JAMI_ERR("Hostname %s does not match certificate.", host.c_str());
     729             :         goto out;
     730             :     }
     731             : 
     732             :     /* Try sending and receiving some data through. */
     733             :     snprintf(buf, sizeof(buf), "GET / HTTP/1.0\r\nHost: %s\r\n\r\n", host.c_str());
     734             :     err = gnutls_record_send(session, buf, strlen(buf));
     735             :     if (err < 0) {
     736             :         JAMI_ERR("Send failed - %s", gnutls_strerror(err));
     737             :         goto out;
     738             :     }
     739             :     err = gnutls_record_recv(session, buf, sizeof(buf));
     740             :     if (err < 0) {
     741             :         JAMI_ERR("Recv failed - %s", gnutls_strerror(err));
     742             :         goto out;
     743             :     }
     744             : 
     745             :     JAMI_DBG("Hostname %s seems to point to a valid server.", host.c_str());
     746             :     res = 0;
     747             : out:
     748             :     if (session) {
     749             :         gnutls_bye(session, GNUTLS_SHUT_RDWR);
     750             :         gnutls_deinit(session);
     751             :     }
     752             :     if (cert)
     753             :         gnutls_x509_crt_deinit(cert);
     754             :     if (cred)
     755             :         gnutls_certificate_free_credentials(cred);
     756             :     close(sockfd);
     757             :     return res;
     758             : }
     759             : #endif
     760             : 
     761             : /**
     762             :  * Check if the Validator have access to a private key
     763             :  */
     764             : TlsValidator::CheckResult
     765           0 : TlsValidator::hasPrivateKey()
     766             : {
     767           0 :     if (privateKeyFound_)
     768           0 :         return TlsValidator::CheckResult(CheckValues::PASSED, "");
     769             : 
     770             :     try {
     771           0 :         dht::crypto::PrivateKey key_tmp(certificateContent_);
     772           0 :     } catch (const std::exception& e) {
     773           0 :         return CheckResult(CheckValues::FAILED, e.what());
     774           0 :     }
     775             : 
     776           0 :     JAMI_DBG("Key from %s seems valid.", certificatePath_.c_str());
     777           0 :     return CheckResult(CheckValues::PASSED, "");
     778             : }
     779             : 
     780             : /**
     781             :  * Check if the certificate is not expired
     782             :  *
     783             :  * The double negative is used because all boolean checks need to have
     784             :  * a consistent return value semantic
     785             :  *
     786             :  * @fixme Handle both "with ca" and "without ca" case
     787             :  */
     788             : TlsValidator::CheckResult
     789           0 : TlsValidator::notExpired()
     790             : {
     791           0 :     if (exist().first == CheckValues::FAILED)
     792           0 :         TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     793             : 
     794             :     // time_t expirationTime = gnutls_x509_crt_get_expiration_time(cert);
     795           0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_EXPIRED ? CheckValues::FAILED
     796             :                                                                          : CheckValues::PASSED,
     797           0 :                                      "");
     798             : }
     799             : 
     800             : /**
     801             :  * If the activation value is in the past
     802             :  *
     803             :  * @fixme Handle both "with ca" and "without ca" case
     804             :  */
     805             : TlsValidator::CheckResult
     806           0 : TlsValidator::activated()
     807             : {
     808           0 :     if (exist().first == CheckValues::FAILED)
     809           0 :         TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     810             : 
     811             :     // time_t activationTime = gnutls_x509_crt_get_activation_time(cert);
     812           0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_NOT_ACTIVATED
     813           0 :                                          ? CheckValues::FAILED
     814             :                                          : CheckValues::PASSED,
     815           0 :                                      "");
     816             : }
     817             : 
     818             : /**
     819             :  * If the algorithm used to sign the certificate is considered weak by modern
     820             :  * standard
     821             :  */
     822             : TlsValidator::CheckResult
     823           0 : TlsValidator::strongSigning()
     824             : {
     825           0 :     if (exist().first == CheckValues::FAILED)
     826           0 :         TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     827             : 
     828             :     // Doesn't seem to have the same value as
     829             :     // certtool  --infile /home/etudiant/Téléchargements/mynsauser.pem --key-inf
     830             :     // TODO figure out why
     831           0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_INSECURE_ALGORITHM
     832           0 :                                          ? CheckValues::FAILED
     833             :                                          : CheckValues::PASSED,
     834           0 :                                      "");
     835             : }
     836             : 
     837             : /**
     838             :  * The certificate is not self signed
     839             :  */
     840             : TlsValidator::CheckResult
     841           0 : TlsValidator::notSelfSigned()
     842             : {
     843           0 :     return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     844             : }
     845             : 
     846             : /**
     847             :  * The provided key can be used along with the certificate
     848             :  */
     849             : TlsValidator::CheckResult
     850           0 : TlsValidator::keyMatch()
     851             : {
     852           0 :     if (exist().first == CheckValues::FAILED)
     853           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     854             : 
     855           0 :     if (not privateKeyFound_)
     856           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     857           0 :     return TlsValidator::CheckResult(privateKeyMatch_ ? CheckValues::PASSED : CheckValues::FAILED,
     858           0 :                                      "");
     859             : }
     860             : 
     861             : TlsValidator::CheckResult
     862           0 : TlsValidator::privateKeyStoragePermissions()
     863             : {
     864             :     struct stat statbuf;
     865           0 :     int err = stat(privateKeyPath_.c_str(), &statbuf);
     866           0 :     if (err)
     867           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     868             : 
     869             : // clang-format off
     870             :     return TlsValidator::CheckResult(
     871           0 :         (statbuf.st_mode & S_IFREG) && /* Regular file only */
     872             :         /*                          READ                      WRITE                            EXECUTE          */
     873           0 :         /* Owner */    ( (statbuf.st_mode & S_IRUSR) /* write is not relevant */     && !(statbuf.st_mode & S_IXUSR))
     874           0 :         /* Group */ && (!(statbuf.st_mode & S_IRGRP) && !(statbuf.st_mode & S_IWGRP) && !(statbuf.st_mode & S_IXGRP))
     875           0 :         /* Other */ && (!(statbuf.st_mode & S_IROTH) && !(statbuf.st_mode & S_IWOTH) && !(statbuf.st_mode & S_IXOTH))
     876           0 :         ? CheckValues::PASSED:CheckValues::FAILED, "");
     877             : // clang-format on
     878             : }
     879             : 
     880             : TlsValidator::CheckResult
     881           0 : TlsValidator::publicKeyStoragePermissions()
     882             : {
     883             :     struct stat statbuf;
     884           0 :     int err = stat(certificatePath_.c_str(), &statbuf);
     885           0 :     if (err)
     886           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     887             : 
     888             : // clang-format off
     889             :     return TlsValidator::CheckResult(
     890           0 :         (statbuf.st_mode & S_IFREG) && /* Regular file only */
     891             :         /*                          READ                      WRITE                            EXECUTE          */
     892           0 :         /* Owner */    ( (statbuf.st_mode & S_IRUSR) /* write is not relevant */   && !(statbuf.st_mode & S_IXUSR))
     893           0 :         /* Group */ && ( /* read is not relevant */   !(statbuf.st_mode & S_IWGRP) && !(statbuf.st_mode & S_IXGRP))
     894           0 :         /* Other */ && ( /* read is not relevant */   !(statbuf.st_mode & S_IWOTH) && !(statbuf.st_mode & S_IXOTH))
     895           0 :         ? CheckValues::PASSED:CheckValues::FAILED, "");
     896             : // clang-format on
     897             : }
     898             : 
     899             : TlsValidator::CheckResult
     900           0 : TlsValidator::privateKeyDirectoryPermissions()
     901             : {
     902           0 :     if (privateKeyPath_.empty())
     903           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     904             : 
     905             : #ifndef _MSC_VER
     906           0 :     auto path = std::unique_ptr<char, decltype(free)&>(strdup(privateKeyPath_.c_str()), free);
     907           0 :     const char* dir = dirname(path.get());
     908             : #else
     909             :     char* dir;
     910             :     _splitpath(certificatePath_.c_str(), nullptr, dir, nullptr, nullptr);
     911             : #endif
     912             : 
     913             :     struct stat statbuf;
     914           0 :     int err = stat(dir, &statbuf);
     915           0 :     if (err)
     916           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     917             : 
     918             :     return TlsValidator::CheckResult(
     919             :         /*                          READ                      WRITE EXECUTE             */
     920             :         /* Owner */ (
     921           0 :             (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && (statbuf.st_mode & S_IXUSR))
     922             :                 /* Group */
     923           0 :                 && (!(statbuf.st_mode & S_IRGRP) && !(statbuf.st_mode & S_IWGRP)
     924           0 :                     && !(statbuf.st_mode & S_IXGRP))
     925             :                 /* Other */
     926           0 :                 && (!(statbuf.st_mode & S_IROTH) && !(statbuf.st_mode & S_IWOTH)
     927           0 :                     && !(statbuf.st_mode & S_IXOTH))
     928           0 :                 && S_ISDIR(statbuf.st_mode)
     929           0 :             ? CheckValues::PASSED
     930             :             : CheckValues::FAILED,
     931           0 :         "");
     932           0 : }
     933             : 
     934             : TlsValidator::CheckResult
     935           0 : TlsValidator::publicKeyDirectoryPermissions()
     936             : {
     937             : #ifndef _MSC_VER
     938           0 :     auto path = std::unique_ptr<char, decltype(free)&>(strdup(certificatePath_.c_str()), free);
     939           0 :     const char* dir = dirname(path.get());
     940             : #else
     941             :     char* dir;
     942             :     _splitpath(certificatePath_.c_str(), nullptr, dir, nullptr, nullptr);
     943             : #endif
     944             : 
     945             :     struct stat statbuf;
     946           0 :     int err = stat(dir, &statbuf);
     947           0 :     if (err)
     948           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     949             : 
     950             :     return TlsValidator::CheckResult(
     951             :         /*                          READ                      WRITE EXECUTE             */
     952             :         /* Owner */ (
     953           0 :             (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && (statbuf.st_mode & S_IXUSR))
     954             :                 /* Group */
     955           0 :                 && (!(statbuf.st_mode & S_IRGRP) && !(statbuf.st_mode & S_IWGRP)
     956           0 :                     && !(statbuf.st_mode & S_IXGRP))
     957             :                 /* Other */
     958           0 :                 && (!(statbuf.st_mode & S_IROTH) && !(statbuf.st_mode & S_IWOTH)
     959           0 :                     && !(statbuf.st_mode & S_IXOTH))
     960           0 :                 && S_ISDIR(statbuf.st_mode)
     961           0 :             ? CheckValues::PASSED
     962             :             : CheckValues::FAILED,
     963           0 :         "");
     964           0 : }
     965             : 
     966             : /**
     967             :  * Certificate should be located in specific path on some operating systems
     968             :  */
     969             : TlsValidator::CheckResult
     970           0 : TlsValidator::privateKeyStorageLocation()
     971             : {
     972             :     // TODO
     973           0 :     return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     974             : }
     975             : 
     976             : /**
     977             :  * Certificate should be located in specific path on some operating systems
     978             :  */
     979             : TlsValidator::CheckResult
     980           0 : TlsValidator::publicKeyStorageLocation()
     981             : {
     982             :     // TODO
     983           0 :     return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     984             : }
     985             : 
     986             : /**
     987             :  * SELinux provide additional key protection mechanism
     988             :  */
     989             : TlsValidator::CheckResult
     990           0 : TlsValidator::privateKeySelinuxAttributes()
     991             : {
     992             :     // TODO
     993           0 :     return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     994             : }
     995             : 
     996             : /**
     997             :  * SELinux provide additional key protection mechanism
     998             :  */
     999             : TlsValidator::CheckResult
    1000           0 : TlsValidator::publicKeySelinuxAttributes()
    1001             : {
    1002             :     // TODO
    1003           0 :     return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1004             : }
    1005             : 
    1006             : /**
    1007             :  * If the key need decryption
    1008             :  *
    1009             :  * Double factor authentication is recommended
    1010             :  */
    1011             : TlsValidator::CheckResult
    1012           0 : TlsValidator::requirePrivateKeyPassword()
    1013             : {
    1014           0 :     return TlsValidator::CheckResult(privateKeyPassword_ ? CheckValues::PASSED : CheckValues::FAILED,
    1015           0 :                                      "");
    1016             : }
    1017             : /**
    1018             :  * The CA and certificate provide conflicting ownership information
    1019             :  */
    1020             : TlsValidator::CheckResult
    1021           0 : TlsValidator::expectedOwner()
    1022             : {
    1023           0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_UNEXPECTED_OWNER
    1024           0 :                                          ? CheckValues::FAILED
    1025             :                                          : CheckValues::PASSED,
    1026           0 :                                      "");
    1027             : }
    1028             : 
    1029             : /**
    1030             :  * The file has been found
    1031             :  */
    1032             : TlsValidator::CheckResult
    1033           0 : TlsValidator::exist()
    1034             : {
    1035           0 :     return TlsValidator::CheckResult((certificateFound_ or certificateFileFound_)
    1036           0 :                                          ? CheckValues::PASSED
    1037             :                                          : CheckValues::FAILED,
    1038           0 :                                      "");
    1039             : }
    1040             : 
    1041             : /**
    1042             :  * The certificate is invalid compared to the authority
    1043             :  *
    1044             :  * @todo Handle case when there is facultative authority, such as DHT
    1045             :  */
    1046             : TlsValidator::CheckResult
    1047           0 : TlsValidator::valid()
    1048             : {
    1049           0 :     return TlsValidator::CheckResult(certificateFound_ ? CheckValues::PASSED : CheckValues::FAILED,
    1050           0 :                                      "");
    1051             : }
    1052             : 
    1053             : /**
    1054             :  * The provided authority is invalid
    1055             :  */
    1056             : TlsValidator::CheckResult
    1057           0 : TlsValidator::validAuthority()
    1058             : {
    1059             :     // TODO Merge with either above or bellow
    1060           0 :     return TlsValidator::CheckResult((compareToCa() & GNUTLS_CERT_SIGNER_NOT_FOUND)
    1061           0 :                                          ? CheckValues::FAILED
    1062             :                                          : CheckValues::PASSED,
    1063           0 :                                      "");
    1064             : }
    1065             : 
    1066             : /**
    1067             :  * Check if the authority match the certificate
    1068             :  */
    1069             : TlsValidator::CheckResult
    1070           0 : TlsValidator::authorityMatch()
    1071             : {
    1072           0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_SIGNER_NOT_CA
    1073           0 :                                          ? CheckValues::FAILED
    1074             :                                          : CheckValues::PASSED,
    1075           0 :                                      "");
    1076             : }
    1077             : 
    1078             : /**
    1079             :  * When an account require an authority known by the system (like /usr/share/ssl/certs)
    1080             :  * then the whole chain of trust need be to checked
    1081             :  *
    1082             :  * @fixme port crypto_cert_load_trusted
    1083             :  * @fixme add account settings
    1084             :  * @todo implement the check
    1085             :  */
    1086             : TlsValidator::CheckResult
    1087           0 : TlsValidator::knownAuthority()
    1088             : {
    1089             :     // TODO need a new boolean account setting "require trusted authority" or something defaulting
    1090             :     // to true using GNUTLS_CERT_SIGNER_NOT_FOUND is a temporary placeholder as it is close enough
    1091           0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_SIGNER_NOT_FOUND
    1092           0 :                                          ? CheckValues::FAILED
    1093             :                                          : CheckValues::PASSED,
    1094           0 :                                      "");
    1095             : }
    1096             : 
    1097             : /**
    1098             :  * Check if the certificate has been revoked
    1099             :  */
    1100             : TlsValidator::CheckResult
    1101           0 : TlsValidator::notRevoked()
    1102             : {
    1103           0 :     return TlsValidator::CheckResult((compareToCa() & GNUTLS_CERT_REVOKED)
    1104           0 :                                              || (compareToCa()
    1105           0 :                                                  & GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE)
    1106           0 :                                          ? CheckValues::FAILED
    1107             :                                          : CheckValues::PASSED,
    1108           0 :                                      "");
    1109             : }
    1110             : 
    1111             : /**
    1112             :  * A certificate authority has been provided
    1113             :  */
    1114             : bool
    1115           0 : TlsValidator::hasCa() const
    1116             : {
    1117           0 :     return (x509crt_ and x509crt_->issuer); /* or
    1118             :             (caCert_ != nullptr and caCert_->certificateFound_);*/
    1119             : }
    1120             : 
    1121             : //
    1122             : // Certificate details
    1123             : //
    1124             : 
    1125             : // TODO gnutls_x509_crl_get_this_update
    1126             : 
    1127             : /**
    1128             :  * An hexadecimal representation of the signature
    1129             :  */
    1130             : TlsValidator::CheckResult
    1131           0 : TlsValidator::getPublicSignature()
    1132             : {
    1133           0 :     size_t resultSize = sizeof(copy_buffer);
    1134           0 :     int err = gnutls_x509_crt_get_signature(x509crt_->cert, copy_buffer, &resultSize);
    1135           0 :     return checkBinaryError(err, copy_buffer, resultSize);
    1136             : }
    1137             : 
    1138             : /**
    1139             :  * Return the certificate version
    1140             :  */
    1141             : TlsValidator::CheckResult
    1142           0 : TlsValidator::getVersionNumber()
    1143             : {
    1144           0 :     int version = gnutls_x509_crt_get_version(x509crt_->cert);
    1145           0 :     if (version < 0)
    1146           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1147             : 
    1148           0 :     std::ostringstream convert;
    1149           0 :     convert << version;
    1150             : 
    1151           0 :     return TlsValidator::CheckResult(CheckValues::NUMBER, convert.str());
    1152           0 : }
    1153             : 
    1154             : /**
    1155             :  * Return the certificate serial number
    1156             :  */
    1157             : TlsValidator::CheckResult
    1158           0 : TlsValidator::getSerialNumber()
    1159             : {
    1160             :     // gnutls_x509_crl_iter_crt_serial
    1161             :     // gnutls_x509_crt_get_authority_key_gn_serial
    1162           0 :     size_t resultSize = sizeof(copy_buffer);
    1163           0 :     int err = gnutls_x509_crt_get_serial(x509crt_->cert, copy_buffer, &resultSize);
    1164           0 :     return checkBinaryError(err, copy_buffer, resultSize);
    1165             : }
    1166             : 
    1167             : /**
    1168             :  * If the certificate is not self signed, return the issuer
    1169             :  */
    1170             : TlsValidator::CheckResult
    1171           0 : TlsValidator::getIssuer()
    1172             : {
    1173           0 :     if (not x509crt_->issuer) {
    1174           0 :         auto icrt = certStore_.findIssuer(x509crt_);
    1175           0 :         if (icrt)
    1176           0 :             return TlsValidator::CheckResult(CheckValues::CUSTOM, icrt->getId().toString());
    1177           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1178           0 :     }
    1179           0 :     return TlsValidator::CheckResult(CheckValues::CUSTOM, x509crt_->issuer->getId().toString());
    1180             : }
    1181             : 
    1182             : /**
    1183             :  * The algorithm used to sign the certificate details (rather than the certificate itself)
    1184             :  */
    1185             : TlsValidator::CheckResult
    1186           0 : TlsValidator::getSubjectKeyAlgorithm()
    1187             : {
    1188             :     gnutls_pk_algorithm_t algo = (gnutls_pk_algorithm_t)
    1189           0 :         gnutls_x509_crt_get_pk_algorithm(x509crt_->cert, nullptr);
    1190             : 
    1191           0 :     if (algo < 0)
    1192           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1193             : 
    1194           0 :     const char* name = gnutls_pk_get_name(algo);
    1195             : 
    1196           0 :     if (!name)
    1197           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1198             : 
    1199           0 :     return TlsValidator::CheckResult(CheckValues::CUSTOM, name);
    1200             : }
    1201             : 
    1202             : /**
    1203             :  * The 'CN' section of a DN (RFC4514)
    1204             :  */
    1205             : TlsValidator::CheckResult
    1206           0 : TlsValidator::getCN()
    1207             : {
    1208             :     // TODO split, cache
    1209           0 :     size_t resultSize = sizeof(copy_buffer);
    1210           0 :     int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
    1211             :                                             GNUTLS_OID_X520_COMMON_NAME,
    1212             :                                             0,
    1213             :                                             0,
    1214           0 :                                             copy_buffer,
    1215             :                                             &resultSize);
    1216           0 :     return checkError(err, copy_buffer, resultSize);
    1217             : }
    1218             : 
    1219             : /**
    1220             :  * The 'N' section of a DN (RFC4514)
    1221             :  */
    1222             : TlsValidator::CheckResult
    1223           0 : TlsValidator::getN()
    1224             : {
    1225             :     // TODO split, cache
    1226           0 :     size_t resultSize = sizeof(copy_buffer);
    1227           0 :     int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
    1228             :                                             GNUTLS_OID_X520_NAME,
    1229             :                                             0,
    1230             :                                             0,
    1231           0 :                                             copy_buffer,
    1232             :                                             &resultSize);
    1233           0 :     return checkError(err, copy_buffer, resultSize);
    1234             : }
    1235             : 
    1236             : /**
    1237             :  * The 'O' section of a DN (RFC4514)
    1238             :  */
    1239             : TlsValidator::CheckResult
    1240           0 : TlsValidator::getO()
    1241             : {
    1242             :     // TODO split, cache
    1243           0 :     size_t resultSize = sizeof(copy_buffer);
    1244           0 :     int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
    1245             :                                             GNUTLS_OID_X520_ORGANIZATION_NAME,
    1246             :                                             0,
    1247             :                                             0,
    1248           0 :                                             copy_buffer,
    1249             :                                             &resultSize);
    1250           0 :     return checkError(err, copy_buffer, resultSize);
    1251             : }
    1252             : 
    1253             : /**
    1254             :  * Return the algorithm used to sign the Key
    1255             :  *
    1256             :  * For example: RSA
    1257             :  */
    1258             : TlsValidator::CheckResult
    1259           0 : TlsValidator::getSignatureAlgorithm()
    1260             : {
    1261           0 :     gnutls_sign_algorithm_t algo = (gnutls_sign_algorithm_t) gnutls_x509_crt_get_signature_algorithm(
    1262           0 :         x509crt_->cert);
    1263             : 
    1264           0 :     if (algo < 0)
    1265           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1266             : 
    1267           0 :     const char* algoName = gnutls_sign_get_name(algo);
    1268           0 :     return TlsValidator::CheckResult(CheckValues::CUSTOM, algoName);
    1269             : }
    1270             : 
    1271             : /**
    1272             :  *Compute the key fingerprint
    1273             :  *
    1274             :  * This need to be used along with getSha1Fingerprint() to avoid collisions
    1275             :  */
    1276             : TlsValidator::CheckResult
    1277           0 : TlsValidator::getMd5Fingerprint()
    1278             : {
    1279           0 :     size_t resultSize = sizeof(copy_buffer);
    1280           0 :     int err = gnutls_x509_crt_get_fingerprint(x509crt_->cert,
    1281             :                                               GNUTLS_DIG_MD5,
    1282           0 :                                               copy_buffer,
    1283             :                                               &resultSize);
    1284           0 :     return checkBinaryError(err, copy_buffer, resultSize);
    1285             : }
    1286             : 
    1287             : /**
    1288             :  * Compute the key fingerprint
    1289             :  *
    1290             :  * This need to be used along with getMd5Fingerprint() to avoid collisions
    1291             :  */
    1292             : TlsValidator::CheckResult
    1293           0 : TlsValidator::getSha1Fingerprint()
    1294             : {
    1295           0 :     size_t resultSize = sizeof(copy_buffer);
    1296           0 :     int err = gnutls_x509_crt_get_fingerprint(x509crt_->cert,
    1297             :                                               GNUTLS_DIG_SHA1,
    1298           0 :                                               copy_buffer,
    1299             :                                               &resultSize);
    1300           0 :     return checkBinaryError(err, copy_buffer, resultSize);
    1301             : }
    1302             : 
    1303             : /**
    1304             :  * Return an hexadecimal identifier
    1305             :  */
    1306             : TlsValidator::CheckResult
    1307           0 : TlsValidator::getPublicKeyId()
    1308             : {
    1309             :     static unsigned char unsigned_copy_buffer[4096];
    1310           0 :     size_t resultSize = sizeof(unsigned_copy_buffer);
    1311           0 :     int err = gnutls_x509_crt_get_key_id(x509crt_->cert, 0, unsigned_copy_buffer, &resultSize);
    1312             : 
    1313             :     // TODO check for GNUTLS_E_SHORT_MEMORY_BUFFER and increase the buffer size
    1314             :     // TODO get rid of the cast, display a HEX or something, need research
    1315             : 
    1316           0 :     return checkBinaryError(err, (char*) unsigned_copy_buffer, resultSize);
    1317             : }
    1318             : // gnutls_x509_crt_get_authority_key_id
    1319             : 
    1320             : /**
    1321             :  *  If the certificate is not self signed, return the issuer DN (RFC4514)
    1322             :  */
    1323             : TlsValidator::CheckResult
    1324           0 : TlsValidator::getIssuerDN()
    1325             : {
    1326           0 :     size_t resultSize = sizeof(copy_buffer);
    1327           0 :     int err = gnutls_x509_crt_get_issuer_dn(x509crt_->cert, copy_buffer, &resultSize);
    1328           0 :     return checkError(err, copy_buffer, resultSize);
    1329             : }
    1330             : 
    1331             : /**
    1332             :  * Get the expiration date
    1333             :  *
    1334             :  * @todo Move to "certificateDetails()" method once completed
    1335             :  */
    1336             : TlsValidator::CheckResult
    1337           0 : TlsValidator::getExpirationDate()
    1338             : {
    1339           0 :     if (not certificateFound_)
    1340           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1341             : 
    1342           0 :     time_t expiration = gnutls_x509_crt_get_expiration_time(x509crt_->cert);
    1343             : 
    1344           0 :     return formatDate(expiration);
    1345             : }
    1346             : 
    1347             : /**
    1348             :  * Get the activation date
    1349             :  *
    1350             :  * @todo Move to "certificateDetails()" method once completed
    1351             :  */
    1352             : TlsValidator::CheckResult
    1353           0 : TlsValidator::getActivationDate()
    1354             : {
    1355           0 :     if (not certificateFound_)
    1356           0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1357             : 
    1358           0 :     time_t expiration = gnutls_x509_crt_get_activation_time(x509crt_->cert);
    1359             : 
    1360           0 :     return formatDate(expiration);
    1361             : }
    1362             : 
    1363             : /**
    1364             :  * The expected outgoing server domain
    1365             :  *
    1366             :  * @todo Move to "certificateDetails()" method once completed
    1367             :  * @todo extract information for the certificate
    1368             :  */
    1369             : TlsValidator::CheckResult
    1370           0 : TlsValidator::outgoingServer()
    1371             : {
    1372             :     // TODO
    1373           0 :     return TlsValidator::CheckResult(CheckValues::CUSTOM, "");
    1374             : }
    1375             : 
    1376             : /**
    1377             :  * If the certificate is not self signed, return the issuer
    1378             :  */
    1379             : TlsValidator::CheckResult
    1380           0 : TlsValidator::isCA()
    1381             : {
    1382           0 :     return TlsValidator::CheckResult(CheckValues::CUSTOM, x509crt_->isCA() ? TRUE_STR : FALSE_STR);
    1383             : }
    1384             : 
    1385             : } // namespace tls
    1386             : } // namespace jami

Generated by: LCOV version 1.14