blob: 9b6bb9698be903086d1f4a3a7f1b1d7027c5e927 [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
Adrien Béraudcb753622023-07-17 22:32:49 -04004 * This program is free software: you can redistribute it and/or modify
Adrien Béraud612b55b2023-05-29 10:42:04 -04005 * it under the terms of the GNU General Public License as published by
Adrien Béraudcb753622023-07-17 22:32:49 -04006 * the Free Software Foundation, either version 3 of the License, or
Adrien Béraud612b55b2023-05-29 10:42:04 -04007 * (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
Adrien Béraudcb753622023-07-17 22:32:49 -040011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Adrien Béraud612b55b2023-05-29 10:42:04 -040012 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
Adrien Béraudcb753622023-07-17 22:32:49 -040015 * along with this program. If not, see <https://www.gnu.org/licenses/>.
Adrien Béraud612b55b2023-05-29 10:42:04 -040016 */
Adrien Béraud612b55b2023-05-29 10:42:04 -040017#include "certstore.h"
18#include "security_const.h"
19
20#include "fileutils.h"
21
22#include <opendht/thread_pool.h>
23#include <opendht/logger.h>
24
25#include <gnutls/ocsp.h>
26
27#include <thread>
28#include <sstream>
29#include <fmt/format.h>
30
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040031namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040032namespace tls {
33
34CertificateStore::CertificateStore(const std::string& path, std::shared_ptr<Logger> logger)
35 : logger_(std::move(logger))
36 , certPath_(fmt::format("{}/certificates", path))
37 , crlPath_(fmt::format("{}/crls", path))
38 , ocspPath_(fmt::format("{}/oscp", path))
39{
40 fileutils::check_dir(certPath_.c_str());
41 fileutils::check_dir(crlPath_.c_str());
42 fileutils::check_dir(ocspPath_.c_str());
43 loadLocalCertificates();
44}
45
46unsigned
47CertificateStore::loadLocalCertificates()
48{
49 std::lock_guard<std::mutex> l(lock_);
50
51 auto dir_content = fileutils::readDirectory(certPath_);
52 unsigned n = 0;
53 for (const auto& f : dir_content) {
54 try {
55 auto crt = std::make_shared<crypto::Certificate>(
56 fileutils::loadFile(certPath_ + DIR_SEPARATOR_CH + f));
57 auto id = crt->getId().toString();
58 auto longId = crt->getLongId().toString();
59 if (id != f && longId != f)
60 throw std::logic_error("Certificate id mismatch");
61 while (crt) {
62 id = crt->getId().toString();
63 longId = crt->getLongId().toString();
64 certs_.emplace(std::move(id), crt);
65 certs_.emplace(std::move(longId), crt);
66 loadRevocations(*crt);
67 crt = crt->issuer;
68 ++n;
69 }
70 } catch (const std::exception& e) {
71 if (logger_)
72 logger_->warn("Remove cert. {}", e.what());
73 remove(fmt::format("{}/{}", certPath_, f).c_str());
74 }
75 }
76 if (logger_)
77 logger_->debug("CertificateStore: loaded {} local certificates.", n);
78 return n;
79}
80
81void
82CertificateStore::loadRevocations(crypto::Certificate& crt) const
83{
84 auto dir = fmt::format("{:s}/{:s}", crlPath_, crt.getId().toString());
85 for (const auto& crl : fileutils::readDirectory(dir)) {
86 try {
87 crt.addRevocationList(std::make_shared<crypto::RevocationList>(
88 fileutils::loadFile(fmt::format("{}/{}", dir, crl))));
89 } catch (const std::exception& e) {
90 if (logger_)
91 logger_->warn("Can't load revocation list: %s", e.what());
92 }
93 }
94 auto ocsp_dir = ocspPath_ + DIR_SEPARATOR_CH + crt.getId().toString();
95 for (const auto& ocsp : fileutils::readDirectory(ocsp_dir)) {
96 try {
97 auto ocsp_filepath = fmt::format("{}/{}", ocsp_dir, ocsp);
98 if (logger_) logger_->debug("Found {:s}", ocsp_filepath);
99 auto serial = crt.getSerialNumber();
100 if (dht::toHex(serial.data(), serial.size()) != ocsp)
101 continue;
102 // Save the response
103 auto ocspBlob = fileutils::loadFile(ocsp_filepath);
104 crt.ocspResponse = std::make_shared<dht::crypto::OcspResponse>(ocspBlob.data(),
105 ocspBlob.size());
106 unsigned int status = crt.ocspResponse->getCertificateStatus();
107 if (status == GNUTLS_OCSP_CERT_GOOD) {
108 if (logger_) logger_->debug("Certificate {:s} has good OCSP status", crt.getId());
109 } else if (status == GNUTLS_OCSP_CERT_REVOKED) {
110 if (logger_) logger_->error("Certificate {:s} has revoked OCSP status", crt.getId());
111 } else if (status == GNUTLS_OCSP_CERT_UNKNOWN) {
112 if (logger_) logger_->error("Certificate {:s} has unknown OCSP status", crt.getId());
113 } else {
114 if (logger_) logger_->error("Certificate {:s} has invalid OCSP status", crt.getId());
115 }
116 } catch (const std::exception& e) {
117 if (logger_)
118 logger_->warn("Can't load OCSP revocation status: {:s}", e.what());
119 }
120 }
121}
122
123std::vector<std::string>
124CertificateStore::getPinnedCertificates() const
125{
126 std::lock_guard<std::mutex> l(lock_);
127
128 std::vector<std::string> certIds;
129 certIds.reserve(certs_.size());
130 for (const auto& crt : certs_)
131 certIds.emplace_back(crt.first);
132 return certIds;
133}
134
135std::shared_ptr<crypto::Certificate>
136CertificateStore::getCertificate(const std::string& k)
137{
138 auto getCertificate_ = [this](const std::string& k) -> std::shared_ptr<crypto::Certificate> {
139 auto cit = certs_.find(k);
140 if (cit == certs_.cend())
141 return {};
142 return cit->second;
143 };
144 std::unique_lock<std::mutex> l(lock_);
145 auto crt = getCertificate_(k);
146 // Check if certificate is complete
147 // If the certificate has been splitted, reconstruct it
148 auto top_issuer = crt;
149 while (top_issuer && top_issuer->getUID() != top_issuer->getIssuerUID()) {
150 if (top_issuer->issuer) {
151 top_issuer = top_issuer->issuer;
152 } else if (auto cert = getCertificate_(top_issuer->getIssuerUID())) {
153 top_issuer->issuer = cert;
154 top_issuer = cert;
155 } else {
156 // In this case, a certificate was not found
157 if (logger_)
158 logger_->warn("Incomplete certificate detected {:s}", k);
159 break;
160 }
161 }
162 return crt;
163}
164
165std::shared_ptr<crypto::Certificate>
166CertificateStore::getCertificateLegacy(const std::string& dataDir, const std::string& k)
167{
Adrien Béraud8b831a82023-07-21 14:13:06 -0400168 try {
169 auto oldPath = fmt::format("{}/certificates/{}", dataDir, k);
170 if (fileutils::isFile(oldPath)) {
171 auto crt = std::make_shared<crypto::Certificate>(oldPath);
172 pinCertificate(crt, true);
173 return crt;
174 }
175 } catch (const std::exception& e) {
176 if (logger_)
177 logger_->warn("Can't load certificate: {:s}", e.what());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400178 }
179 return {};
180}
181
182std::shared_ptr<crypto::Certificate>
183CertificateStore::findCertificateByName(const std::string& name, crypto::NameType type) const
184{
185 std::unique_lock<std::mutex> l(lock_);
186 for (auto& i : certs_) {
187 if (i.second->getName() == name)
188 return i.second;
189 if (type != crypto::NameType::UNKNOWN) {
190 for (const auto& alt : i.second->getAltNames())
191 if (alt.first == type and alt.second == name)
192 return i.second;
193 }
194 }
195 return {};
196}
197
198std::shared_ptr<crypto::Certificate>
199CertificateStore::findCertificateByUID(const std::string& uid) const
200{
201 std::unique_lock<std::mutex> l(lock_);
202 for (auto& i : certs_) {
203 if (i.second->getUID() == uid)
204 return i.second;
205 }
206 return {};
207}
208
209std::shared_ptr<crypto::Certificate>
210CertificateStore::findIssuer(const std::shared_ptr<crypto::Certificate>& crt) const
211{
212 std::shared_ptr<crypto::Certificate> ret {};
213 auto n = crt->getIssuerUID();
214 if (not n.empty()) {
215 if (crt->issuer and crt->issuer->getUID() == n)
216 ret = crt->issuer;
217 else
218 ret = findCertificateByUID(n);
219 }
220 if (not ret) {
221 n = crt->getIssuerName();
222 if (not n.empty())
223 ret = findCertificateByName(n);
224 }
225 if (not ret)
226 return ret;
227 unsigned verify_out = 0;
228 int err = gnutls_x509_crt_verify(crt->cert, &ret->cert, 1, 0, &verify_out);
229 if (err != GNUTLS_E_SUCCESS) {
230 if (logger_)
231 logger_->warn("gnutls_x509_crt_verify failed: {:s}", gnutls_strerror(err));
232 return {};
233 }
234 if (verify_out & GNUTLS_CERT_INVALID)
235 return {};
236 return ret;
237}
238
239static std::vector<crypto::Certificate>
240readCertificates(const std::string& path, const std::string& crl_path)
241{
242 std::vector<crypto::Certificate> ret;
243 if (fileutils::isDirectory(path)) {
244 auto files = fileutils::readDirectory(path);
245 for (const auto& file : files) {
246 auto certs = readCertificates(fmt::format("{}/{}", path, file), crl_path);
247 ret.insert(std::end(ret),
248 std::make_move_iterator(std::begin(certs)),
249 std::make_move_iterator(std::end(certs)));
250 }
251 } else {
252 try {
253 auto data = fileutils::loadFile(path);
254 const gnutls_datum_t dt {data.data(), (unsigned) data.size()};
255 gnutls_x509_crt_t* certs {nullptr};
256 unsigned cert_num {0};
257 gnutls_x509_crt_list_import2(&certs, &cert_num, &dt, GNUTLS_X509_FMT_PEM, 0);
258 for (unsigned i = 0; i < cert_num; i++)
259 ret.emplace_back(certs[i]);
260 gnutls_free(certs);
261 } catch (const std::exception& e) {
262 };
263 }
264 return ret;
265}
266
267void
268CertificateStore::pinCertificatePath(const std::string& path,
269 std::function<void(const std::vector<std::string>&)> cb)
270{
271 dht::ThreadPool::computation().run([&, path, cb]() {
272 auto certs = readCertificates(path, crlPath_);
273 std::vector<std::string> ids;
274 std::vector<std::weak_ptr<crypto::Certificate>> scerts;
275 ids.reserve(certs.size());
276 scerts.reserve(certs.size());
277 {
278 std::lock_guard<std::mutex> l(lock_);
279
280 for (auto& cert : certs) {
Adrien Béraud8b831a82023-07-21 14:13:06 -0400281 try {
282 auto shared = std::make_shared<crypto::Certificate>(std::move(cert));
283 scerts.emplace_back(shared);
284 auto e = certs_.emplace(shared->getId().toString(), shared);
285 ids.emplace_back(e.first->first);
286 e = certs_.emplace(shared->getLongId().toString(), shared);
287 ids.emplace_back(e.first->first);
288 } catch (const std::exception& e) {
289 if (logger_)
290 logger_->warn("Can't load certificate: {:s}", e.what());
291 }
Adrien Béraud612b55b2023-05-29 10:42:04 -0400292 }
293 paths_.emplace(path, std::move(scerts));
294 }
295 if (logger_) logger_->d("CertificateStore: loaded %zu certificates from %s.", certs.size(), path.c_str());
296 if (cb)
297 cb(ids);
Adrien Béraud1ae60aa2023-07-07 09:55:09 -0400298 //emitSignal<libdhtnet::ConfigurationSignal::CertificatePathPinned>(path, ids);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400299 });
300}
301
302unsigned
303CertificateStore::unpinCertificatePath(const std::string& path)
304{
305 std::lock_guard<std::mutex> l(lock_);
306
307 auto certs = paths_.find(path);
308 if (certs == std::end(paths_))
309 return 0;
310 unsigned n = 0;
311 for (const auto& wcert : certs->second) {
312 if (auto cert = wcert.lock()) {
313 certs_.erase(cert->getId().toString());
314 ++n;
315 }
316 }
317 paths_.erase(certs);
318 return n;
319}
320
321std::vector<std::string>
322CertificateStore::pinCertificate(const std::vector<uint8_t>& cert, bool local) noexcept
323{
324 try {
325 return pinCertificate(crypto::Certificate(cert), local);
326 } catch (const std::exception& e) {
327 }
328 return {};
329}
330
331std::vector<std::string>
332CertificateStore::pinCertificate(crypto::Certificate&& cert, bool local)
333{
334 return pinCertificate(std::make_shared<crypto::Certificate>(std::move(cert)), local);
335}
336
337std::vector<std::string>
338CertificateStore::pinCertificate(const std::shared_ptr<crypto::Certificate>& cert, bool local)
339{
340 bool sig {false};
341 std::vector<std::string> ids {};
342 {
343 auto c = cert;
344 std::lock_guard<std::mutex> l(lock_);
345 while (c) {
346 bool inserted;
347 auto id = c->getId().toString();
348 auto longId = c->getLongId().toString();
349 decltype(certs_)::iterator it;
350 std::tie(it, inserted) = certs_.emplace(id, c);
351 if (not inserted)
352 it->second = c;
353 std::tie(it, inserted) = certs_.emplace(longId, c);
354 if (not inserted)
355 it->second = c;
356 if (local) {
357 for (const auto& crl : c->getRevocationLists())
358 pinRevocationList(id, *crl);
359 }
360 ids.emplace_back(longId);
361 ids.emplace_back(id);
362 c = c->issuer;
363 sig |= inserted;
364 }
365 if (local) {
366 if (sig)
367 fileutils::saveFile(certPath_ + DIR_SEPARATOR_CH + ids.front(), cert->getPacked());
368 }
369 }
370 //for (const auto& id : ids)
Adrien Béraud1ae60aa2023-07-07 09:55:09 -0400371 // emitSignal<libdhtnet::ConfigurationSignal::CertificatePinned>(id);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400372 return ids;
373}
374
375bool
376CertificateStore::unpinCertificate(const std::string& id)
377{
378 std::lock_guard<std::mutex> l(lock_);
379
380 certs_.erase(id);
381 return remove((certPath_ + DIR_SEPARATOR_CH + id).c_str()) == 0;
382}
383
384bool
385CertificateStore::setTrustedCertificate(const std::string& id, TrustStatus status)
386{
387 if (status == TrustStatus::TRUSTED) {
388 if (auto crt = getCertificate(id)) {
389 trustedCerts_.emplace_back(crt);
390 return true;
391 }
392 } else {
393 auto tc = std::find_if(trustedCerts_.begin(),
394 trustedCerts_.end(),
395 [&](const std::shared_ptr<crypto::Certificate>& crt) {
396 return crt->getId().toString() == id;
397 });
398 if (tc != trustedCerts_.end()) {
399 trustedCerts_.erase(tc);
400 return true;
401 }
402 }
403 return false;
404}
405
406std::vector<gnutls_x509_crt_t>
407CertificateStore::getTrustedCertificates() const
408{
409 std::vector<gnutls_x509_crt_t> crts;
410 crts.reserve(trustedCerts_.size());
411 for (auto& crt : trustedCerts_)
412 crts.emplace_back(crt->getCopy());
413 return crts;
414}
415
416void
417CertificateStore::pinRevocationList(const std::string& id,
418 const std::shared_ptr<dht::crypto::RevocationList>& crl)
419{
420 try {
421 if (auto c = getCertificate(id))
422 c->addRevocationList(crl);
423 pinRevocationList(id, *crl);
424 } catch (...) {
425 if (logger_)
426 logger_->warn("Can't add revocation list");
427 }
428}
429
430void
431CertificateStore::pinRevocationList(const std::string& id, const dht::crypto::RevocationList& crl)
432{
433 fileutils::check_dir((crlPath_ + DIR_SEPARATOR_CH + id).c_str());
434 fileutils::saveFile(crlPath_ + DIR_SEPARATOR_CH + id + DIR_SEPARATOR_CH
435 + dht::toHex(crl.getNumber()),
436 crl.getPacked());
437}
438
439void
440CertificateStore::pinOcspResponse(const dht::crypto::Certificate& cert)
441{
442 if (not cert.ocspResponse)
443 return;
444 try {
445 cert.ocspResponse->getCertificateStatus();
446 } catch (dht::crypto::CryptoException& e) {
447 if (logger_) logger_->error("Failed to read certificate status of OCSP response: {:s}", e.what());
448 return;
449 }
450 auto id = cert.getId().toString();
451 auto serial = cert.getSerialNumber();
452 auto serialhex = dht::toHex(serial);
453 auto dir = ocspPath_ + DIR_SEPARATOR_CH + id;
454
455 if (auto localCert = getCertificate(id)) {
456 // Update certificate in the local store if relevant
457 if (localCert.get() != &cert && serial == localCert->getSerialNumber()) {
458 if (logger_) logger_->d("Updating OCSP for certificate %s in the local store", id.c_str());
459 localCert->ocspResponse = cert.ocspResponse;
460 }
461 }
462
463 dht::ThreadPool::io().run([l=logger_,
464 path = dir + DIR_SEPARATOR_CH + serialhex,
465 dir = std::move(dir),
466 id = std::move(id),
467 serialhex = std::move(serialhex),
468 ocspResponse = cert.ocspResponse] {
469 if (l) l->d("Saving OCSP Response of device %s with serial %s", id.c_str(), serialhex.c_str());
470 std::lock_guard<std::mutex> lock(fileutils::getFileLock(path));
471 fileutils::check_dir(dir.c_str());
472 fileutils::saveFile(path, ocspResponse->pack());
473 });
474}
475
476TrustStore::PermissionStatus
477TrustStore::statusFromStr(const char* str)
478{
Adrien Béraud1ae60aa2023-07-07 09:55:09 -0400479 if (!std::strcmp(str, libdhtnet::Certificate::Status::ALLOWED))
Adrien Béraud612b55b2023-05-29 10:42:04 -0400480 return PermissionStatus::ALLOWED;
Adrien Béraud1ae60aa2023-07-07 09:55:09 -0400481 if (!std::strcmp(str, libdhtnet::Certificate::Status::BANNED))
Adrien Béraud612b55b2023-05-29 10:42:04 -0400482 return PermissionStatus::BANNED;
483 return PermissionStatus::UNDEFINED;
484}
485
486const char*
487TrustStore::statusToStr(TrustStore::PermissionStatus s)
488{
489 switch (s) {
490 case PermissionStatus::ALLOWED:
Adrien Béraud1ae60aa2023-07-07 09:55:09 -0400491 return libdhtnet::Certificate::Status::ALLOWED;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400492 case PermissionStatus::BANNED:
Adrien Béraud1ae60aa2023-07-07 09:55:09 -0400493 return libdhtnet::Certificate::Status::BANNED;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400494 case PermissionStatus::UNDEFINED:
495 default:
Adrien Béraud1ae60aa2023-07-07 09:55:09 -0400496 return libdhtnet::Certificate::Status::UNDEFINED;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400497 }
498}
499
500TrustStatus
501trustStatusFromStr(const char* str)
502{
Adrien Béraud1ae60aa2023-07-07 09:55:09 -0400503 if (!std::strcmp(str, libdhtnet::Certificate::TrustStatus::TRUSTED))
Adrien Béraud612b55b2023-05-29 10:42:04 -0400504 return TrustStatus::TRUSTED;
505 return TrustStatus::UNTRUSTED;
506}
507
508const char*
509statusToStr(TrustStatus s)
510{
511 switch (s) {
512 case TrustStatus::TRUSTED:
Adrien Béraud1ae60aa2023-07-07 09:55:09 -0400513 return libdhtnet::Certificate::TrustStatus::TRUSTED;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400514 case TrustStatus::UNTRUSTED:
515 default:
Adrien Béraud1ae60aa2023-07-07 09:55:09 -0400516 return libdhtnet::Certificate::TrustStatus::UNTRUSTED;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400517 }
518}
519
520bool
521TrustStore::addRevocationList(dht::crypto::RevocationList&& crl)
522{
523 allowed_.add(crl);
524 return true;
525}
526
527bool
528TrustStore::setCertificateStatus(const std::string& cert_id,
529 const TrustStore::PermissionStatus status)
530{
531 return setCertificateStatus(nullptr, cert_id, status, false);
532}
533
534bool
535TrustStore::setCertificateStatus(const std::shared_ptr<crypto::Certificate>& cert,
536 const TrustStore::PermissionStatus status,
537 bool local)
538{
539 return setCertificateStatus(cert, cert->getId().toString(), status, local);
540}
541
542bool
543TrustStore::setCertificateStatus(std::shared_ptr<crypto::Certificate> cert,
544 const std::string& cert_id,
545 const TrustStore::PermissionStatus status,
546 bool local)
547{
548 if (cert)
549 certStore_.pinCertificate(cert, local);
550 std::lock_guard<std::recursive_mutex> lk(mutex_);
551 updateKnownCerts();
552 bool dirty {false};
553 if (status == PermissionStatus::UNDEFINED) {
554 unknownCertStatus_.erase(cert_id);
555 dirty = certStatus_.erase(cert_id);
556 } else {
557 bool allowed = (status == PermissionStatus::ALLOWED);
558 auto s = certStatus_.find(cert_id);
559 if (s == std::end(certStatus_)) {
560 // Certificate state is currently undefined
561 if (not cert)
562 cert = certStore_.getCertificate(cert_id);
563 if (cert) {
564 unknownCertStatus_.erase(cert_id);
565 auto& crt_status = certStatus_[cert_id];
566 if (not crt_status.first)
567 crt_status.first = cert;
568 crt_status.second.allowed = allowed;
569 setStoreCertStatus(*cert, allowed);
570 } else {
571 // Can't find certificate
572 unknownCertStatus_[cert_id].allowed = allowed;
573 }
574 } else {
575 // Certificate is already allowed or banned
576 if (s->second.second.allowed != allowed) {
577 s->second.second.allowed = allowed;
578 if (allowed) // Certificate is re-added after ban, rebuld needed
579 dirty = true;
580 else
581 allowed_.remove(*s->second.first, false);
582 }
583 }
584 }
585 if (dirty)
586 rebuildTrust();
587 return true;
588}
589
590TrustStore::PermissionStatus
591TrustStore::getCertificateStatus(const std::string& cert_id) const
592{
593 std::lock_guard<std::recursive_mutex> lk(mutex_);
594 auto s = certStatus_.find(cert_id);
595 if (s == std::end(certStatus_)) {
596 auto us = unknownCertStatus_.find(cert_id);
597 if (us == std::end(unknownCertStatus_))
598 return PermissionStatus::UNDEFINED;
599 return us->second.allowed ? PermissionStatus::ALLOWED : PermissionStatus::BANNED;
600 }
601 return s->second.second.allowed ? PermissionStatus::ALLOWED : PermissionStatus::BANNED;
602}
603
604std::vector<std::string>
605TrustStore::getCertificatesByStatus(TrustStore::PermissionStatus status) const
606{
607 std::lock_guard<std::recursive_mutex> lk(mutex_);
608 std::vector<std::string> ret;
609 for (const auto& i : certStatus_)
610 if (i.second.second.allowed == (status == TrustStore::PermissionStatus::ALLOWED))
611 ret.emplace_back(i.first);
612 for (const auto& i : unknownCertStatus_)
613 if (i.second.allowed == (status == TrustStore::PermissionStatus::ALLOWED))
614 ret.emplace_back(i.first);
615 return ret;
616}
617
618bool
619TrustStore::isAllowed(const crypto::Certificate& crt, bool allowPublic)
620{
621 // Match by certificate pinning
622 std::lock_guard<std::recursive_mutex> lk(mutex_);
623 bool allowed {allowPublic};
624 for (auto c = &crt; c; c = c->issuer.get()) {
625 auto status = getCertificateStatus(c->getId().toString()); // lock mutex_
626 if (status == PermissionStatus::ALLOWED)
627 allowed = true;
628 else if (status == PermissionStatus::BANNED)
629 return false;
630 }
631
632 // Match by certificate chain
633 updateKnownCerts();
634 auto ret = allowed_.verify(crt);
635 // Unknown issuer (only that) are accepted if allowPublic is true
636 if (not ret
637 and !(allowPublic and ret.result == (GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND))) {
638 if (certStore_.logger())
639 certStore_.logger()->warn("%s", ret.toString().c_str());
640 return false;
641 }
642
643 return allowed;
644}
645
646void
647TrustStore::updateKnownCerts()
648{
649 auto i = std::begin(unknownCertStatus_);
650 while (i != std::end(unknownCertStatus_)) {
651 if (auto crt = certStore_.getCertificate(i->first)) {
652 certStatus_.emplace(i->first, std::make_pair(crt, i->second));
653 setStoreCertStatus(*crt, i->second.allowed);
654 i = unknownCertStatus_.erase(i);
655 } else
656 ++i;
657 }
658}
659
660void
661TrustStore::setStoreCertStatus(const crypto::Certificate& crt, bool status)
662{
663 if (status)
664 allowed_.add(crt);
665 else
666 allowed_.remove(crt, false);
667}
668
669void
670TrustStore::rebuildTrust()
671{
672 allowed_ = {};
673 for (const auto& c : certStatus_)
674 setStoreCertStatus(*c.second.first, c.second.second.allowed);
675}
676
677} // namespace tls
Sébastien Blin464bdff2023-07-19 08:02:53 -0400678} // namespace dhtnet