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
|