Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 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 :
63 : namespace jami {
64 : namespace tls {
65 :
66 : // Map the internal ring Enum class of the exported names
67 :
68 : const EnumClassNames<TlsValidator::CheckValues> TlsValidator::CheckValuesNames = {{
69 : /* CheckValues Name */
70 : /* PASSED */ libjami::Certificate::CheckValuesNames::PASSED,
71 : /* FAILED */ libjami::Certificate::CheckValuesNames::FAILED,
72 : /* UNSUPPORTED */ libjami::Certificate::CheckValuesNames::UNSUPPORTED,
73 : /* ISO_DATE */ libjami::Certificate::CheckValuesNames::ISO_DATE,
74 : /* CUSTOM */ libjami::Certificate::CheckValuesNames::CUSTOM,
75 : /* CUSTOM */ libjami::Certificate::CheckValuesNames::DATE,
76 : }};
77 :
78 : const CallbackMatrix1D<TlsValidator::CertificateCheck, TlsValidator, TlsValidator::CheckResult>
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 :
104 : const CallbackMatrix1D<TlsValidator::CertificateDetails, TlsValidator, TlsValidator::CheckResult>
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 :
133 : const Matrix1D<TlsValidator::CertificateCheck, TlsValidator::CheckValuesType>
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 :
159 : const EnumClassNames<TlsValidator::CertificateCheck> TlsValidator::CertificateCheckNames = {{
160 : /* CertificateCheck Name */
161 : /*HAS_PRIVATE_KEY */ libjami::Certificate::ChecksNames::HAS_PRIVATE_KEY,
162 : /*EXPIRED */ libjami::Certificate::ChecksNames::EXPIRED,
163 : /*STRONG_SIGNING */ libjami::Certificate::ChecksNames::STRONG_SIGNING,
164 : /*NOT_SELF_SIGNED */ libjami::Certificate::ChecksNames::NOT_SELF_SIGNED,
165 : /*KEY_MATCH */ libjami::Certificate::ChecksNames::KEY_MATCH,
166 : /*PRIVATE_KEY_STORAGE_PERMISSION */ libjami::Certificate::ChecksNames::PRIVATE_KEY_STORAGE_PERMISSION,
167 : /*PUBLIC_KEY_STORAGE_PERMISSION */ libjami::Certificate::ChecksNames::PUBLIC_KEY_STORAGE_PERMISSION,
168 : /*PRIVATEKEY_DIRECTORY_PERMISSIONS */ libjami::Certificate::ChecksNames::PRIVATE_KEY_DIRECTORY_PERMISSIONS,
169 : /*PUBLICKEY_DIRECTORY_PERMISSIONS */ libjami::Certificate::ChecksNames::PUBLIC_KEY_DIRECTORY_PERMISSIONS,
170 : /*PRIVATE_KEY_STORAGE_LOCATION */ libjami::Certificate::ChecksNames::PRIVATE_KEY_STORAGE_LOCATION,
171 : /*PUBLIC_KEY_STORAGE_LOCATION */ libjami::Certificate::ChecksNames::PUBLIC_KEY_STORAGE_LOCATION,
172 : /*PRIVATE_KEY_SELINUX_ATTRIBUTES */ libjami::Certificate::ChecksNames::PRIVATE_KEY_SELINUX_ATTRIBUTES,
173 : /*PUBLIC_KEY_SELINUX_ATTRIBUTES */ libjami::Certificate::ChecksNames::PUBLIC_KEY_SELINUX_ATTRIBUTES,
174 : /*EXIST */ libjami::Certificate::ChecksNames::EXIST,
175 : /*VALID */ libjami::Certificate::ChecksNames::VALID,
176 : /*VALID_AUTHORITY */ libjami::Certificate::ChecksNames::VALID_AUTHORITY,
177 : /*KNOWN_AUTHORITY */ libjami::Certificate::ChecksNames::KNOWN_AUTHORITY,
178 : /*NOT_REVOKED */ libjami::Certificate::ChecksNames::NOT_REVOKED,
179 : /*AUTHORITY_MISMATCH */ libjami::Certificate::ChecksNames::AUTHORITY_MISMATCH,
180 : /*UNEXPECTED_OWNER */ libjami::Certificate::ChecksNames::UNEXPECTED_OWNER,
181 : /*NOT_ACTIVATED */ libjami::Certificate::ChecksNames::NOT_ACTIVATED,
182 : }};
183 :
184 : const EnumClassNames<TlsValidator::CertificateDetails> TlsValidator::CertificateDetailsNames = {{
185 : /* EXPIRATION_DATE */ libjami::Certificate::DetailsNames::EXPIRATION_DATE,
186 : /* ACTIVATION_DATE */ libjami::Certificate::DetailsNames::ACTIVATION_DATE,
187 : /* REQUIRE_PRIVATE_KEY_PASSWORD */ libjami::Certificate::DetailsNames::REQUIRE_PRIVATE_KEY_PASSWORD,
188 : /* PUBLIC_SIGNATURE */ libjami::Certificate::DetailsNames::PUBLIC_SIGNATURE,
189 : /* VERSION_NUMBER */ libjami::Certificate::DetailsNames::VERSION_NUMBER,
190 : /* SERIAL_NUMBER */ libjami::Certificate::DetailsNames::SERIAL_NUMBER,
191 : /* ISSUER */ libjami::Certificate::DetailsNames::ISSUER,
192 : /* SUBJECT_KEY_ALGORITHM */ libjami::Certificate::DetailsNames::SUBJECT_KEY_ALGORITHM,
193 : /* SUBJECT_KEY */ libjami::Certificate::DetailsNames::SUBJECT_KEY,
194 : /* CN */ libjami::Certificate::DetailsNames::CN,
195 : /* UID */ libjami::Certificate::DetailsNames::UID,
196 : /* N */ libjami::Certificate::DetailsNames::N,
197 : /* O */ libjami::Certificate::DetailsNames::O,
198 : /* SIGNATURE_ALGORITHM */ libjami::Certificate::DetailsNames::SIGNATURE_ALGORITHM,
199 : /* MD5_FINGERPRINT */ libjami::Certificate::DetailsNames::MD5_FINGERPRINT,
200 : /* SHA1_FINGERPRINT */ libjami::Certificate::DetailsNames::SHA1_FINGERPRINT,
201 : /* PUBLIC_KEY_ID */ libjami::Certificate::DetailsNames::PUBLIC_KEY_ID,
202 : /* ISSUER_DN */ libjami::Certificate::DetailsNames::ISSUER_DN,
203 : /* ISSUER_CN */ libjami::Certificate::DetailsNames::ISSUER_CN,
204 : /* ISSUER_UID */ libjami::Certificate::DetailsNames::ISSUER_UID,
205 : /* ISSUER_N */ libjami::Certificate::DetailsNames::ISSUER_N,
206 : /* ISSUER_O */ libjami::Certificate::DetailsNames::ISSUER_O,
207 : /* NEXT_EXPECTED_UPDATE_DATE */ libjami::Certificate::DetailsNames::NEXT_EXPECTED_UPDATE_DATE,
208 : /* OUTGOING_SERVER */ libjami::Certificate::DetailsNames::OUTGOING_SERVER,
209 : /* IS_CA */ libjami::Certificate::DetailsNames::IS_CA,
210 :
211 : }};
212 :
213 : const EnumClassNames<const TlsValidator::CheckValuesType> TlsValidator::CheckValuesTypeNames = {{
214 : /* Type Name */
215 : /* BOOLEAN */ libjami::Certificate::ChecksValuesTypesNames::BOOLEAN,
216 : /* ISO_DATE */ libjami::Certificate::ChecksValuesTypesNames::ISO_DATE,
217 : /* CUSTOM */ libjami::Certificate::ChecksValuesTypesNames::CUSTOM,
218 : /* NUMBER */ libjami::Certificate::ChecksValuesTypesNames::NUMBER,
219 : }};
220 :
221 : const Matrix2D<TlsValidator::CheckValuesType, TlsValidator::CheckValues, bool>
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 :
230 0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::vector<std::vector<uint8_t>>& crtChain)
231 0 : : TlsValidator(certStore, std::make_shared<dht::crypto::Certificate>(crtChain.begin(), crtChain.end()))
232 0 : {}
233 :
234 0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore,
235 : const std::string& certificate,
236 : const std::string& privatekey,
237 : const std::string& privatekeyPasswd,
238 0 : const std::string& caList)
239 0 : : certStore_(certStore)
240 0 : , certificatePath_(certificate)
241 0 : , privateKeyPath_(privatekey)
242 0 : , caListPath_(caList)
243 0 : , certificateFound_(false)
244 : {
245 0 : std::vector<uint8_t> certificate_raw;
246 : try {
247 0 : certificate_raw = fileutils::loadFile(certificatePath_);
248 0 : certificateFileFound_ = true;
249 0 : } catch (const std::exception& e) {
250 0 : }
251 :
252 0 : if (not certificate_raw.empty()) {
253 : try {
254 0 : x509crt_ = std::make_shared<dht::crypto::Certificate>(certificate_raw);
255 0 : certificateContent_ = x509crt_->getPacked();
256 0 : certificateFound_ = true;
257 0 : } catch (const std::exception& e) {
258 0 : }
259 : }
260 :
261 : try {
262 0 : auto privateKeyContent = fileutils::loadFile(privateKeyPath_);
263 0 : dht::crypto::PrivateKey key_tmp(privateKeyContent, privatekeyPasswd);
264 0 : privateKeyFound_ = true;
265 0 : privateKeyPassword_ = not privatekeyPasswd.empty();
266 0 : privateKeyMatch_ = key_tmp.getPublicKey().getId() == x509crt_->getId();
267 0 : } 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 0 : JAMI_WARN("decryption error: %s", d.what());
271 0 : privateKeyFound_ = true;
272 0 : privateKeyPassword_ = true;
273 0 : } catch (const std::exception& e) {
274 0 : JAMI_WARN("creation failed: %s", e.what());
275 0 : }
276 0 : }
277 :
278 0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::vector<uint8_t>& certificate_raw)
279 0 : : certStore_(certStore)
280 : {
281 : try {
282 0 : x509crt_ = std::make_shared<dht::crypto::Certificate>(certificate_raw);
283 0 : certificateContent_ = x509crt_->getPacked();
284 0 : certificateFound_ = true;
285 0 : } catch (const std::exception& e) {
286 0 : throw TlsValidatorException("Unable to load certificate");
287 0 : }
288 0 : }
289 :
290 0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::shared_ptr<dht::crypto::Certificate>& crt)
291 0 : : certStore_(certStore)
292 0 : , certificateFound_(true)
293 : {
294 : try {
295 0 : if (not crt)
296 0 : throw std::invalid_argument("Certificate must be set");
297 0 : x509crt_ = crt;
298 0 : certificateContent_ = x509crt_->getPacked();
299 0 : } catch (const std::exception& e) {
300 0 : throw TlsValidatorException("Unable to load certificate");
301 0 : }
302 0 : }
303 :
304 0 : TlsValidator::~TlsValidator() {}
305 :
306 : /**
307 : * This method convert results into validated strings
308 : *
309 : * @todo The date should be validated, this is currently not an issue
310 : */
311 : std::string
312 0 : TlsValidator::getStringValue(const TlsValidator::CertificateCheck check,
313 : const TlsValidator::CheckResult result)
314 : {
315 0 : assert(acceptedCheckValuesResult[enforcedCheckType[check]][result.first]);
316 :
317 0 : switch (result.first) {
318 0 : case CheckValues::PASSED:
319 : case CheckValues::FAILED:
320 : case CheckValues::UNSUPPORTED:
321 0 : return std::string(CheckValuesNames[result.first]);
322 0 : case CheckValues::ISO_DATE:
323 : // TODO validate date
324 : // return CheckValues::FAILED;
325 0 : return result.second;
326 0 : case CheckValues::NUMBER:
327 : // TODO Validate numbers
328 : case CheckValues::CUSTOM:
329 0 : return result.second;
330 0 : default:
331 : // Consider any other case (such as forced int->CheckValues casting) as failed
332 0 : return std::string(CheckValuesNames[CheckValues::FAILED]);
333 : };
334 : }
335 :
336 : /**
337 : * Check if all boolean check passed
338 : * return true if there was no ::FAILED checks
339 : *
340 : * Checks functions are not "const", so this function isn't
341 : */
342 : bool
343 0 : TlsValidator::isValid(bool verbose)
344 : {
345 0 : for (const CertificateCheck check : Matrix0D<CertificateCheck>()) {
346 0 : if (enforcedCheckType[check] == CheckValuesType::BOOLEAN) {
347 0 : if (((this->*(checkCallback[check]))()).first == CheckValues::FAILED) {
348 0 : if (verbose)
349 0 : JAMI_WARNING("Check failed: {}", CertificateCheckNames[check]);
350 0 : return false;
351 : }
352 : }
353 : }
354 0 : return true;
355 : }
356 :
357 : /**
358 : * Convert all checks results into a string map
359 : */
360 : std::map<std::string, std::string>
361 0 : TlsValidator::getSerializedChecks()
362 : {
363 0 : std::map<std::string, std::string> ret;
364 0 : if (not certificateFound_) {
365 : // Instead of checking `certificateFound` everywhere, handle it once
366 0 : ret[std::string(CertificateCheckNames[CertificateCheck::EXIST])] = getStringValue(CertificateCheck::EXIST,
367 0 : exist());
368 : } else {
369 0 : for (const CertificateCheck check : Matrix0D<CertificateCheck>())
370 0 : ret[std::string(CertificateCheckNames[check])] = getStringValue(check,
371 0 : (this->*(checkCallback[check]))());
372 : }
373 :
374 0 : return ret;
375 0 : }
376 :
377 : /**
378 : * Get a map with all common certificate details
379 : */
380 : std::map<std::string, std::string>
381 0 : TlsValidator::getSerializedDetails()
382 : {
383 0 : std::map<std::string, std::string> ret;
384 0 : if (certificateFound_) {
385 0 : for (const CertificateDetails& det : Matrix0D<CertificateDetails>()) {
386 0 : const CheckResult r = (this->*(getterCallback[det]))();
387 0 : std::string val;
388 : // TODO move this to a fuction
389 0 : switch (r.first) {
390 0 : case CheckValues::PASSED:
391 : case CheckValues::FAILED:
392 : case CheckValues::UNSUPPORTED:
393 0 : val = CheckValuesNames[r.first];
394 0 : break;
395 0 : case CheckValues::ISO_DATE:
396 : // TODO validate date
397 : case CheckValues::NUMBER:
398 : // TODO Validate numbers
399 : case CheckValues::CUSTOM:
400 : default:
401 0 : val = r.second;
402 0 : break;
403 : }
404 0 : ret[std::string(CertificateDetailsNames[det])] = val;
405 0 : }
406 : }
407 0 : return ret;
408 0 : }
409 :
410 : /**
411 : * Helper method to return UNSUPPORTED when an error is detected
412 : */
413 : static TlsValidator::CheckResult
414 0 : checkError(int err, char* copy_buffer, size_t size)
415 : {
416 : return TlsValidator::TlsValidator::CheckResult(err == GNUTLS_E_SUCCESS
417 0 : ? TlsValidator::CheckValues::CUSTOM
418 : : TlsValidator::CheckValues::UNSUPPORTED,
419 : err == GNUTLS_E_SUCCESS
420 0 : ? std::string(copy_buffer, size)
421 0 : : "");
422 : }
423 :
424 : /**
425 : * Convert a time_t to an ISO date string
426 : */
427 : static TlsValidator::CheckResult
428 0 : formatDate(const time_t time)
429 : {
430 : char buffer[12];
431 0 : struct tm* timeinfo = localtime(&time);
432 0 : strftime(buffer, sizeof(buffer), "%F", timeinfo);
433 0 : return TlsValidator::CheckResult(TlsValidator::CheckValues::ISO_DATE, buffer);
434 : }
435 :
436 : /**
437 : * Helper method to return UNSUPPORTED when an error is detected
438 : *
439 : * This method also convert the output to binary
440 : */
441 : static TlsValidator::CheckResult
442 0 : checkBinaryError(int err, char* copy_buffer, size_t resultSize)
443 : {
444 0 : if (err == GNUTLS_E_SUCCESS)
445 0 : return TlsValidator::CheckResult(TlsValidator::CheckValues::CUSTOM,
446 0 : dht::toHex(reinterpret_cast<uint8_t*>(copy_buffer),
447 0 : resultSize));
448 : else
449 0 : return TlsValidator::CheckResult(TlsValidator::CheckValues::UNSUPPORTED, "");
450 : }
451 :
452 : /**
453 : * Check if a certificate has been signed with the authority
454 : */
455 : unsigned int
456 0 : TlsValidator::compareToCa()
457 : {
458 : // Don't check unless the certificate changed
459 0 : if (caChecked_)
460 0 : return caValidationOutput_;
461 :
462 : // build the CA trusted list
463 : gnutls_x509_trust_list_t trust;
464 0 : gnutls_x509_trust_list_init(&trust, 0);
465 :
466 0 : auto root_cas = certStore_.getTrustedCertificates();
467 0 : auto err = gnutls_x509_trust_list_add_cas(trust, root_cas.data(), root_cas.size(), 0);
468 0 : if (err)
469 0 : JAMI_WARN("gnutls_x509_trust_list_add_cas failed: %s", gnutls_strerror(err));
470 :
471 0 : if (not caListPath_.empty()) {
472 0 : if (std::filesystem::is_directory(caListPath_))
473 0 : gnutls_x509_trust_list_add_trust_dir(trust,
474 : caListPath_.c_str(),
475 : nullptr,
476 : GNUTLS_X509_FMT_PEM,
477 : 0,
478 : 0);
479 : else
480 0 : gnutls_x509_trust_list_add_trust_file(trust,
481 : caListPath_.c_str(),
482 : nullptr,
483 : GNUTLS_X509_FMT_PEM,
484 : 0,
485 : 0);
486 : }
487 :
488 : // build the certificate chain
489 0 : auto crts = x509crt_->getChain();
490 0 : err = gnutls_x509_trust_list_verify_crt2(trust,
491 : crts.data(),
492 0 : crts.size(),
493 : nullptr,
494 : 0,
495 : GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_MEDIUM),
496 : &caValidationOutput_,
497 : nullptr);
498 :
499 0 : gnutls_x509_trust_list_deinit(trust, true);
500 :
501 0 : if (err) {
502 0 : JAMI_WARN("gnutls_x509_trust_list_verify_crt2 failed: %s", gnutls_strerror(err));
503 0 : return GNUTLS_CERT_SIGNER_NOT_FOUND;
504 : }
505 :
506 0 : caChecked_ = true;
507 0 : return caValidationOutput_;
508 0 : }
509 :
510 : #if 0 // disabled, see .h for reason
511 : /**
512 : * Verify if a hostname is valid
513 : *
514 : * @warning This function is blocking
515 : *
516 : * Mainly based on Fedora Defensive Coding tutorial
517 : * https://docs.fedoraproject.org/en-US/Fedora_Security_Team/1/html/Defensive_Coding/sect-Defensive_Coding-TLS-Client-GNUTLS.html
518 : */
519 : int 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;
525 : gnutls_certificate_credentials_t cred = 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;
535 : fd_set fdset;
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);
574 : FD_SET(sockfd, &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);
586 : getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len);
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. */
612 : err = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
613 : if (err < 0) {
614 : JAMI_ERR("Unable to set TCP_NODELAY.");
615 : goto out;
616 : }
617 :
618 :
619 : /* Load the trusted CA certificates. */
620 : err = gnutls_certificate_allocate_credentials(&cred);
621 : if (err != GNUTLS_E_SUCCESS) {
622 : JAMI_ERR("Unable to allocate credentials - %s", gnutls_strerror(err));
623 : goto out;
624 : }
625 : err = gnutls_certificate_set_x509_system_trust(cred);
626 : if (err != GNUTLS_E_SUCCESS) {
627 : JAMI_ERR("Unable to load credentials.");
628 : goto out;
629 : }
630 :
631 : /* Create the session object. */
632 : err = gnutls_init(&session, GNUTLS_CLIENT);
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. */
639 : err = gnutls_priority_set_direct(session, "NORMAL", &errptr);
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. */
646 : err = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred);
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. */
653 : gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) (uintptr_t) sockfd);
654 : err = gnutls_server_name_set(session, GNUTLS_NAME_DNS, host.c_str(), host.size());
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. */
661 : err = gnutls_handshake(session);
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. */
668 : certs = gnutls_certificate_get_peers(session, &certslen);
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. */
675 : err = gnutls_certificate_verify_peers2(session, &status);
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) {
681 : gnutls_datum_t msg;
682 : #if GNUTLS_VERSION_AT_LEAST_3_1_4
683 : int type = gnutls_certificate_type_get(session);
684 : err = gnutls_certificate_verification_status_print(status, type, &out, 0);
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 :
703 : err = gnutls_x509_crt_init(&cert);
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. */
710 : err = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_PEM);
711 : if (err != GNUTLS_E_SUCCESS)
712 : err = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER);
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. */
718 : err = gnutls_x509_crt_check_hostname(cert, host.c_str());
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());
726 : err = gnutls_record_send(session, buf, strlen(buf));
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;
739 : out:
740 : if (session) {
741 : gnutls_bye(session, GNUTLS_SHUT_RDWR);
742 : gnutls_deinit(session);
743 : }
744 : if (cert)
745 : gnutls_x509_crt_deinit(cert);
746 : if (cred)
747 : gnutls_certificate_free_credentials(cred);
748 : close(sockfd);
749 : return res;
750 : }
751 : #endif
752 :
753 : /**
754 : * Check if the Validator have access to a private key
755 : */
756 : TlsValidator::CheckResult
757 0 : TlsValidator::hasPrivateKey()
758 : {
759 0 : if (privateKeyFound_)
760 0 : return TlsValidator::CheckResult(CheckValues::PASSED, "");
761 :
762 : try {
763 0 : dht::crypto::PrivateKey key_tmp(certificateContent_);
764 0 : } catch (const std::exception& e) {
765 0 : return CheckResult(CheckValues::FAILED, e.what());
766 0 : }
767 :
768 0 : JAMI_DBG("Key from %s seems valid.", certificatePath_.c_str());
769 0 : return CheckResult(CheckValues::PASSED, "");
770 : }
771 :
772 : /**
773 : * Check if the certificate is not expired
774 : *
775 : * The double negative is used because all boolean checks need to have
776 : * a consistent return value semantic
777 : *
778 : * @fixme Handle both "with ca" and "without ca" case
779 : */
780 : TlsValidator::CheckResult
781 0 : TlsValidator::notExpired()
782 : {
783 0 : if (exist().first == CheckValues::FAILED)
784 0 : TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
785 :
786 : // time_t expirationTime = gnutls_x509_crt_get_expiration_time(cert);
787 0 : return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_EXPIRED ? CheckValues::FAILED
788 : : CheckValues::PASSED,
789 0 : "");
790 : }
791 :
792 : /**
793 : * If the activation value is in the past
794 : *
795 : * @fixme Handle both "with ca" and "without ca" case
796 : */
797 : TlsValidator::CheckResult
798 0 : TlsValidator::activated()
799 : {
800 0 : if (exist().first == CheckValues::FAILED)
801 0 : TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
802 :
803 : // time_t activationTime = gnutls_x509_crt_get_activation_time(cert);
804 0 : return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_NOT_ACTIVATED
805 0 : ? CheckValues::FAILED
806 : : CheckValues::PASSED,
807 0 : "");
808 : }
809 :
810 : /**
811 : * If the algorithm used to sign the certificate is considered weak by modern
812 : * standard
813 : */
814 : TlsValidator::CheckResult
815 0 : TlsValidator::strongSigning()
816 : {
817 0 : if (exist().first == CheckValues::FAILED)
818 0 : TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
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
823 0 : return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_INSECURE_ALGORITHM
824 0 : ? CheckValues::FAILED
825 : : CheckValues::PASSED,
826 0 : "");
827 : }
828 :
829 : /**
830 : * The certificate is not self signed
831 : */
832 : TlsValidator::CheckResult
833 0 : TlsValidator::notSelfSigned()
834 : {
835 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
836 : }
837 :
838 : /**
839 : * The provided key can be used along with the certificate
840 : */
841 : TlsValidator::CheckResult
842 0 : TlsValidator::keyMatch()
843 : {
844 0 : if (exist().first == CheckValues::FAILED)
845 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
846 :
847 0 : if (not privateKeyFound_)
848 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
849 0 : return TlsValidator::CheckResult(privateKeyMatch_ ? CheckValues::PASSED : CheckValues::FAILED,
850 0 : "");
851 : }
852 :
853 : TlsValidator::CheckResult
854 0 : TlsValidator::privateKeyStoragePermissions()
855 : {
856 : struct stat statbuf;
857 0 : int err = stat(privateKeyPath_.c_str(), &statbuf);
858 0 : if (err)
859 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
860 :
861 : // clang-format off
862 : return TlsValidator::CheckResult(
863 0 : (statbuf.st_mode & S_IFREG) && /* Regular file only */
864 : /* READ WRITE EXECUTE */
865 0 : /* Owner */ ( (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && !(statbuf.st_mode & S_IXUSR))
866 0 : /* Group */ && (!(statbuf.st_mode & S_IRGRP) && !(statbuf.st_mode & S_IWGRP) && !(statbuf.st_mode & S_IXGRP))
867 0 : /* Other */ && (!(statbuf.st_mode & S_IROTH) && !(statbuf.st_mode & S_IWOTH) && !(statbuf.st_mode & S_IXOTH))
868 0 : ? CheckValues::PASSED:CheckValues::FAILED, "");
869 : // clang-format on
870 : }
871 :
872 : TlsValidator::CheckResult
873 0 : TlsValidator::publicKeyStoragePermissions()
874 : {
875 : struct stat statbuf;
876 0 : int err = stat(certificatePath_.c_str(), &statbuf);
877 0 : if (err)
878 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
879 :
880 : // clang-format off
881 : return TlsValidator::CheckResult(
882 0 : (statbuf.st_mode & S_IFREG) && /* Regular file only */
883 : /* READ WRITE EXECUTE */
884 0 : /* Owner */ ( (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && !(statbuf.st_mode & S_IXUSR))
885 0 : /* Group */ && ( /* read is not relevant */ !(statbuf.st_mode & S_IWGRP) && !(statbuf.st_mode & S_IXGRP))
886 0 : /* Other */ && ( /* read is not relevant */ !(statbuf.st_mode & S_IWOTH) && !(statbuf.st_mode & S_IXOTH))
887 0 : ? CheckValues::PASSED:CheckValues::FAILED, "");
888 : // clang-format on
889 : }
890 :
891 : TlsValidator::CheckResult
892 0 : TlsValidator::privateKeyDirectoryPermissions()
893 : {
894 0 : if (privateKeyPath_.empty())
895 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
896 :
897 : #ifndef _MSC_VER
898 0 : auto path = std::unique_ptr<char, decltype(free)&>(strdup(privateKeyPath_.c_str()), free);
899 0 : 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 0 : int err = stat(dir, &statbuf);
907 0 : if (err)
908 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
909 :
910 : return TlsValidator::CheckResult(
911 : /* READ WRITE EXECUTE */
912 : /* Owner */ (
913 0 : (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && (statbuf.st_mode & S_IXUSR))
914 : /* Group */
915 0 : && (!(statbuf.st_mode & S_IRGRP) && !(statbuf.st_mode & S_IWGRP)
916 0 : && !(statbuf.st_mode & S_IXGRP))
917 : /* Other */
918 0 : && (!(statbuf.st_mode & S_IROTH) && !(statbuf.st_mode & S_IWOTH)
919 0 : && !(statbuf.st_mode & S_IXOTH))
920 0 : && S_ISDIR(statbuf.st_mode)
921 0 : ? CheckValues::PASSED
922 : : CheckValues::FAILED,
923 0 : "");
924 0 : }
925 :
926 : TlsValidator::CheckResult
927 0 : TlsValidator::publicKeyDirectoryPermissions()
928 : {
929 : #ifndef _MSC_VER
930 0 : auto path = std::unique_ptr<char, decltype(free)&>(strdup(certificatePath_.c_str()), free);
931 0 : 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 0 : int err = stat(dir, &statbuf);
939 0 : if (err)
940 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
941 :
942 : return TlsValidator::CheckResult(
943 : /* READ WRITE EXECUTE */
944 : /* Owner */ (
945 0 : (statbuf.st_mode & S_IRUSR) /* write is not relevant */ && (statbuf.st_mode & S_IXUSR))
946 : /* Group */
947 0 : && (!(statbuf.st_mode & S_IRGRP) && !(statbuf.st_mode & S_IWGRP)
948 0 : && !(statbuf.st_mode & S_IXGRP))
949 : /* Other */
950 0 : && (!(statbuf.st_mode & S_IROTH) && !(statbuf.st_mode & S_IWOTH)
951 0 : && !(statbuf.st_mode & S_IXOTH))
952 0 : && S_ISDIR(statbuf.st_mode)
953 0 : ? CheckValues::PASSED
954 : : CheckValues::FAILED,
955 0 : "");
956 0 : }
957 :
958 : /**
959 : * Certificate should be located in specific path on some operating systems
960 : */
961 : TlsValidator::CheckResult
962 0 : TlsValidator::privateKeyStorageLocation()
963 : {
964 : // TODO
965 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
966 : }
967 :
968 : /**
969 : * Certificate should be located in specific path on some operating systems
970 : */
971 : TlsValidator::CheckResult
972 0 : TlsValidator::publicKeyStorageLocation()
973 : {
974 : // TODO
975 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
976 : }
977 :
978 : /**
979 : * SELinux provide additional key protection mechanism
980 : */
981 : TlsValidator::CheckResult
982 0 : TlsValidator::privateKeySelinuxAttributes()
983 : {
984 : // TODO
985 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
986 : }
987 :
988 : /**
989 : * SELinux provide additional key protection mechanism
990 : */
991 : TlsValidator::CheckResult
992 0 : TlsValidator::publicKeySelinuxAttributes()
993 : {
994 : // TODO
995 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
996 : }
997 :
998 : /**
999 : * If the key need decryption
1000 : *
1001 : * Double factor authentication is recommended
1002 : */
1003 : TlsValidator::CheckResult
1004 0 : TlsValidator::requirePrivateKeyPassword()
1005 : {
1006 0 : return TlsValidator::CheckResult(privateKeyPassword_ ? CheckValues::PASSED : CheckValues::FAILED,
1007 0 : "");
1008 : }
1009 : /**
1010 : * The CA and certificate provide conflicting ownership information
1011 : */
1012 : TlsValidator::CheckResult
1013 0 : TlsValidator::expectedOwner()
1014 : {
1015 0 : return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_UNEXPECTED_OWNER
1016 0 : ? CheckValues::FAILED
1017 : : CheckValues::PASSED,
1018 0 : "");
1019 : }
1020 :
1021 : /**
1022 : * The file has been found
1023 : */
1024 : TlsValidator::CheckResult
1025 0 : TlsValidator::exist()
1026 : {
1027 0 : return TlsValidator::CheckResult((certificateFound_ or certificateFileFound_)
1028 0 : ? CheckValues::PASSED
1029 : : CheckValues::FAILED,
1030 0 : "");
1031 : }
1032 :
1033 : /**
1034 : * The certificate is invalid compared to the authority
1035 : *
1036 : * @todo Handle case when there is facultative authority, such as DHT
1037 : */
1038 : TlsValidator::CheckResult
1039 0 : TlsValidator::valid()
1040 : {
1041 0 : return TlsValidator::CheckResult(certificateFound_ ? CheckValues::PASSED : CheckValues::FAILED,
1042 0 : "");
1043 : }
1044 :
1045 : /**
1046 : * The provided authority is invalid
1047 : */
1048 : TlsValidator::CheckResult
1049 0 : TlsValidator::validAuthority()
1050 : {
1051 : // TODO Merge with either above or below
1052 0 : return TlsValidator::CheckResult((compareToCa() & GNUTLS_CERT_SIGNER_NOT_FOUND)
1053 0 : ? CheckValues::FAILED
1054 : : CheckValues::PASSED,
1055 0 : "");
1056 : }
1057 :
1058 : /**
1059 : * Check if the authority match the certificate
1060 : */
1061 : TlsValidator::CheckResult
1062 0 : TlsValidator::authorityMatch()
1063 : {
1064 0 : return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_SIGNER_NOT_CA
1065 0 : ? CheckValues::FAILED
1066 : : CheckValues::PASSED,
1067 0 : "");
1068 : }
1069 :
1070 : /**
1071 : * When an account require an authority known by the system (like /usr/share/ssl/certs)
1072 : * then the whole chain of trust need be to checked
1073 : *
1074 : * @fixme port crypto_cert_load_trusted
1075 : * @fixme add account settings
1076 : * @todo implement the check
1077 : */
1078 : TlsValidator::CheckResult
1079 0 : TlsValidator::knownAuthority()
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
1083 0 : return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_SIGNER_NOT_FOUND
1084 0 : ? CheckValues::FAILED
1085 : : CheckValues::PASSED,
1086 0 : "");
1087 : }
1088 :
1089 : /**
1090 : * Check if the certificate has been revoked
1091 : */
1092 : TlsValidator::CheckResult
1093 0 : TlsValidator::notRevoked()
1094 : {
1095 0 : return TlsValidator::CheckResult((compareToCa() & GNUTLS_CERT_REVOKED)
1096 0 : || (compareToCa()
1097 0 : & GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE)
1098 0 : ? CheckValues::FAILED
1099 : : CheckValues::PASSED,
1100 0 : "");
1101 : }
1102 :
1103 : /**
1104 : * A certificate authority has been provided
1105 : */
1106 : bool
1107 0 : TlsValidator::hasCa() const
1108 : {
1109 0 : 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 :
1119 : /**
1120 : * An hexadecimal representation of the signature
1121 : */
1122 : TlsValidator::CheckResult
1123 0 : TlsValidator::getPublicSignature()
1124 : {
1125 0 : size_t resultSize = sizeof(copy_buffer);
1126 0 : int err = gnutls_x509_crt_get_signature(x509crt_->cert, copy_buffer, &resultSize);
1127 0 : return checkBinaryError(err, copy_buffer, resultSize);
1128 : }
1129 :
1130 : /**
1131 : * Return the certificate version
1132 : */
1133 : TlsValidator::CheckResult
1134 0 : TlsValidator::getVersionNumber()
1135 : {
1136 0 : int version = gnutls_x509_crt_get_version(x509crt_->cert);
1137 0 : if (version < 0)
1138 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
1139 :
1140 0 : std::ostringstream convert;
1141 0 : convert << version;
1142 :
1143 0 : return TlsValidator::CheckResult(CheckValues::NUMBER, convert.str());
1144 0 : }
1145 :
1146 : /**
1147 : * Return the certificate serial number
1148 : */
1149 : TlsValidator::CheckResult
1150 0 : TlsValidator::getSerialNumber()
1151 : {
1152 : // gnutls_x509_crl_iter_crt_serial
1153 : // gnutls_x509_crt_get_authority_key_gn_serial
1154 0 : size_t resultSize = sizeof(copy_buffer);
1155 0 : int err = gnutls_x509_crt_get_serial(x509crt_->cert, copy_buffer, &resultSize);
1156 0 : return checkBinaryError(err, copy_buffer, resultSize);
1157 : }
1158 :
1159 : /**
1160 : * If the certificate is not self signed, return the issuer
1161 : */
1162 : TlsValidator::CheckResult
1163 0 : TlsValidator::getIssuer()
1164 : {
1165 0 : if (not x509crt_->issuer) {
1166 0 : auto icrt = certStore_.findIssuer(x509crt_);
1167 0 : if (icrt)
1168 0 : return TlsValidator::CheckResult(CheckValues::CUSTOM, icrt->getId().toString());
1169 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
1170 0 : }
1171 0 : return TlsValidator::CheckResult(CheckValues::CUSTOM, x509crt_->issuer->getId().toString());
1172 : }
1173 :
1174 : /**
1175 : * The algorithm used to sign the certificate details (rather than the certificate itself)
1176 : */
1177 : TlsValidator::CheckResult
1178 0 : TlsValidator::getSubjectKeyAlgorithm()
1179 : {
1180 0 : unsigned key_length = 0;
1181 : gnutls_pk_algorithm_t algo = (gnutls_pk_algorithm_t)
1182 0 : gnutls_x509_crt_get_pk_algorithm(x509crt_->cert, &key_length);
1183 :
1184 0 : if (algo < 0)
1185 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
1186 :
1187 0 : const char* name = gnutls_pk_get_name(algo);
1188 :
1189 0 : if (!name)
1190 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
1191 :
1192 0 : if (key_length)
1193 0 : return TlsValidator::CheckResult(CheckValues::CUSTOM, fmt::format("{} ({} bits)", name, key_length));
1194 : else
1195 0 : return TlsValidator::CheckResult(CheckValues::CUSTOM, name);
1196 : }
1197 :
1198 : /**
1199 : * The subject public key
1200 : */
1201 : TlsValidator::CheckResult
1202 0 : TlsValidator::getSubjectKey()
1203 : {
1204 : try {
1205 0 : std::vector<uint8_t> data;
1206 0 : x509crt_->getPublicKey().pack(data);
1207 0 : return TlsValidator::CheckResult(CheckValues::CUSTOM, dht::toHex(data));
1208 0 : } catch (const dht::crypto::CryptoException& e) {
1209 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, e.what());
1210 0 : }
1211 : }
1212 :
1213 : /**
1214 : * The 'CN' section of a DN (RFC4514)
1215 : */
1216 : TlsValidator::CheckResult
1217 0 : TlsValidator::getCN()
1218 : {
1219 : // TODO split, cache
1220 0 : size_t resultSize = sizeof(copy_buffer);
1221 0 : int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
1222 : GNUTLS_OID_X520_COMMON_NAME,
1223 : 0,
1224 : 0,
1225 0 : copy_buffer,
1226 : &resultSize);
1227 0 : return checkError(err, copy_buffer, resultSize);
1228 : }
1229 :
1230 : /**
1231 : * The 'UID' section of a DN (RFC4514)
1232 : */
1233 : TlsValidator::CheckResult
1234 0 : TlsValidator::getUID()
1235 : {
1236 0 : size_t resultSize = sizeof(copy_buffer);
1237 0 : int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
1238 : GNUTLS_OID_LDAP_UID,
1239 : 0,
1240 : 0,
1241 0 : copy_buffer,
1242 : &resultSize);
1243 0 : return checkError(err, copy_buffer, resultSize);
1244 : }
1245 :
1246 : /**
1247 : * The 'N' section of a DN (RFC4514)
1248 : */
1249 : TlsValidator::CheckResult
1250 0 : TlsValidator::getN()
1251 : {
1252 : // TODO split, cache
1253 0 : size_t resultSize = sizeof(copy_buffer);
1254 0 : int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
1255 : GNUTLS_OID_X520_NAME,
1256 : 0,
1257 : 0,
1258 0 : copy_buffer,
1259 : &resultSize);
1260 0 : return checkError(err, copy_buffer, resultSize);
1261 : }
1262 :
1263 : /**
1264 : * The 'O' section of a DN (RFC4514)
1265 : */
1266 : TlsValidator::CheckResult
1267 0 : TlsValidator::getO()
1268 : {
1269 : // TODO split, cache
1270 0 : size_t resultSize = sizeof(copy_buffer);
1271 0 : int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
1272 : GNUTLS_OID_X520_ORGANIZATION_NAME,
1273 : 0,
1274 : 0,
1275 0 : copy_buffer,
1276 : &resultSize);
1277 0 : return checkError(err, copy_buffer, resultSize);
1278 : }
1279 :
1280 : /**
1281 : * Return the algorithm used to sign the Key
1282 : *
1283 : * For example: RSA
1284 : */
1285 : TlsValidator::CheckResult
1286 0 : TlsValidator::getSignatureAlgorithm()
1287 : {
1288 0 : gnutls_sign_algorithm_t algo = (gnutls_sign_algorithm_t) gnutls_x509_crt_get_signature_algorithm(
1289 0 : x509crt_->cert);
1290 :
1291 0 : if (algo < 0)
1292 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
1293 :
1294 0 : const char* algoName = gnutls_sign_get_name(algo);
1295 0 : return TlsValidator::CheckResult(CheckValues::CUSTOM, algoName);
1296 : }
1297 :
1298 : /**
1299 : *Compute the key fingerprint
1300 : *
1301 : * This need to be used along with getSha1Fingerprint() to avoid collisions
1302 : */
1303 : TlsValidator::CheckResult
1304 0 : TlsValidator::getMd5Fingerprint()
1305 : {
1306 0 : size_t resultSize = sizeof(copy_buffer);
1307 0 : int err = gnutls_x509_crt_get_fingerprint(x509crt_->cert,
1308 : GNUTLS_DIG_MD5,
1309 0 : copy_buffer,
1310 : &resultSize);
1311 0 : return checkBinaryError(err, copy_buffer, resultSize);
1312 : }
1313 :
1314 : /**
1315 : * Compute the key fingerprint
1316 : *
1317 : * This need to be used along with getMd5Fingerprint() to avoid collisions
1318 : */
1319 : TlsValidator::CheckResult
1320 0 : TlsValidator::getSha1Fingerprint()
1321 : {
1322 0 : size_t resultSize = sizeof(copy_buffer);
1323 0 : int err = gnutls_x509_crt_get_fingerprint(x509crt_->cert,
1324 : GNUTLS_DIG_SHA1,
1325 0 : copy_buffer,
1326 : &resultSize);
1327 0 : return checkBinaryError(err, copy_buffer, resultSize);
1328 : }
1329 :
1330 : /**
1331 : * Return an hexadecimal identifier
1332 : */
1333 : TlsValidator::CheckResult
1334 0 : TlsValidator::getPublicKeyId()
1335 : {
1336 : static unsigned char unsigned_copy_buffer[4096];
1337 0 : size_t resultSize = sizeof(unsigned_copy_buffer);
1338 0 : int err = gnutls_x509_crt_get_key_id(x509crt_->cert, 0, unsigned_copy_buffer, &resultSize);
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 :
1343 0 : return checkBinaryError(err, (char*) unsigned_copy_buffer, resultSize);
1344 : }
1345 : // gnutls_x509_crt_get_authority_key_id
1346 :
1347 : /**
1348 : * If the certificate is not self signed, return the issuer DN (RFC4514)
1349 : */
1350 : TlsValidator::CheckResult
1351 0 : TlsValidator::getIssuerDN()
1352 : {
1353 0 : size_t resultSize = sizeof(copy_buffer);
1354 0 : int err = gnutls_x509_crt_get_issuer_dn(x509crt_->cert, copy_buffer, &resultSize);
1355 0 : return checkError(err, copy_buffer, resultSize);
1356 : }
1357 :
1358 : /**
1359 : * If the certificate is not self signed, return the issuer CN
1360 : */
1361 : TlsValidator::CheckResult
1362 0 : TlsValidator::getIssuerCN()
1363 : {
1364 0 : size_t resultSize = sizeof(copy_buffer);
1365 0 : int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
1366 : GNUTLS_OID_X520_COMMON_NAME,
1367 : 0,
1368 : 0,
1369 0 : copy_buffer,
1370 : &resultSize);
1371 0 : return checkError(err, copy_buffer, resultSize);
1372 : }
1373 :
1374 : /**
1375 : * If the certificate is not self signed, return the issuer UID
1376 : */
1377 : TlsValidator::CheckResult
1378 0 : TlsValidator::getIssuerUID()
1379 : {
1380 0 : size_t resultSize = sizeof(copy_buffer);
1381 0 : int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
1382 : GNUTLS_OID_LDAP_UID,
1383 : 0,
1384 : 0,
1385 0 : copy_buffer,
1386 : &resultSize);
1387 0 : return checkError(err, copy_buffer, resultSize);
1388 : }
1389 :
1390 : /**
1391 : * If the certificate is not self signed, return the issuer N
1392 : */
1393 : TlsValidator::CheckResult
1394 0 : TlsValidator::getIssuerN()
1395 : {
1396 0 : size_t resultSize = sizeof(copy_buffer);
1397 0 : int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
1398 : GNUTLS_OID_X520_NAME,
1399 : 0,
1400 : 0,
1401 0 : copy_buffer,
1402 : &resultSize);
1403 0 : return checkError(err, copy_buffer, resultSize);
1404 : }
1405 :
1406 : /**
1407 : * If the certificate is not self signed, return the issuer O
1408 : */
1409 : TlsValidator::CheckResult
1410 0 : TlsValidator::getIssuerO()
1411 : {
1412 0 : size_t resultSize = sizeof(copy_buffer);
1413 0 : int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
1414 : GNUTLS_OID_X520_ORGANIZATION_NAME,
1415 : 0,
1416 : 0,
1417 0 : copy_buffer,
1418 : &resultSize);
1419 0 : return checkError(err, copy_buffer, resultSize);
1420 : }
1421 :
1422 : /**
1423 : * Get the expiration date
1424 : *
1425 : * @todo Move to "certificateDetails()" method once completed
1426 : */
1427 : TlsValidator::CheckResult
1428 0 : TlsValidator::getExpirationDate()
1429 : {
1430 0 : if (not certificateFound_)
1431 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
1432 :
1433 0 : time_t expiration = gnutls_x509_crt_get_expiration_time(x509crt_->cert);
1434 :
1435 0 : return formatDate(expiration);
1436 : }
1437 :
1438 : /**
1439 : * Get the activation date
1440 : *
1441 : * @todo Move to "certificateDetails()" method once completed
1442 : */
1443 : TlsValidator::CheckResult
1444 0 : TlsValidator::getActivationDate()
1445 : {
1446 0 : if (not certificateFound_)
1447 0 : return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
1448 :
1449 0 : time_t expiration = gnutls_x509_crt_get_activation_time(x509crt_->cert);
1450 :
1451 0 : return formatDate(expiration);
1452 : }
1453 :
1454 : /**
1455 : * The expected outgoing server domain
1456 : *
1457 : * @todo Move to "certificateDetails()" method once completed
1458 : * @todo extract information for the certificate
1459 : */
1460 : TlsValidator::CheckResult
1461 0 : TlsValidator::outgoingServer()
1462 : {
1463 : // TODO
1464 0 : return TlsValidator::CheckResult(CheckValues::CUSTOM, "");
1465 : }
1466 :
1467 : /**
1468 : * If the certificate is not self signed, return the issuer
1469 : */
1470 : TlsValidator::CheckResult
1471 0 : TlsValidator::isCA()
1472 : {
1473 0 : return TlsValidator::CheckResult(CheckValues::CUSTOM, x509crt_->isCA() ? TRUE_STR : FALSE_STR);
1474 : }
1475 :
1476 : } // namespace tls
1477 : } // namespace jami
|