| /* |
| * Copyright (C) 2004-2023 Savoir-faire Linux Inc. |
| * |
| * This program is free software: you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation, either version 3 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program. If not, see <https://www.gnu.org/licenses/>. |
| */ |
| #include "certstore.h" |
| #include "security_const.h" |
| |
| #include "fileutils.h" |
| |
| #include <opendht/thread_pool.h> |
| #include <opendht/logger.h> |
| |
| #include <gnutls/ocsp.h> |
| |
| #if __has_include(<fmt/std.h>) |
| #include <fmt/std.h> |
| #else |
| #include <fmt/ostream.h> |
| #endif |
| #include <thread> |
| #include <sstream> |
| #include <fmt/format.h> |
| |
| namespace dhtnet { |
| namespace tls { |
| |
| CertificateStore::CertificateStore(const std::filesystem::path& path, std::shared_ptr<Logger> logger) |
| : logger_(std::move(logger)) |
| , certPath_(path / "certificates") |
| , crlPath_(path /"crls") |
| , ocspPath_(path /"oscp") |
| { |
| fileutils::check_dir(certPath_); |
| fileutils::check_dir(crlPath_); |
| fileutils::check_dir(ocspPath_); |
| loadLocalCertificates(); |
| } |
| |
| unsigned |
| CertificateStore::loadLocalCertificates() |
| { |
| std::lock_guard l(lock_); |
| if (logger_) |
| logger_->debug("CertificateStore: loading certificates from {}", certPath_); |
| |
| unsigned n = 0; |
| std::error_code ec; |
| for (const auto& crtPath : std::filesystem::directory_iterator(certPath_, ec)) { |
| const auto& path = crtPath.path(); |
| auto fileName = path.filename().string(); |
| try { |
| auto crt = std::make_shared<crypto::Certificate>( |
| fileutils::loadFile(crtPath)); |
| auto id = crt->getId().toString(); |
| auto longId = crt->getLongId().toString(); |
| if (id != fileName && longId != fileName) |
| throw std::logic_error("Certificate id mismatch"); |
| while (crt) { |
| id = crt->getId().toString(); |
| longId = crt->getLongId().toString(); |
| certs_.emplace(std::move(id), crt); |
| certs_.emplace(std::move(longId), crt); |
| loadRevocations(*crt); |
| crt = crt->issuer; |
| ++n; |
| } |
| } catch (const std::exception& e) { |
| if (logger_) |
| logger_->warn("loadLocalCertificates: error loading {}: {}", path, e.what()); |
| remove(path); |
| } |
| } |
| if (logger_) |
| logger_->debug("CertificateStore: loaded {} local certificates.", n); |
| return n; |
| } |
| |
| void |
| CertificateStore::loadRevocations(crypto::Certificate& crt) const |
| { |
| std::error_code ec; |
| auto dir = crlPath_ / crt.getId().toString(); |
| for (const auto& crl : std::filesystem::directory_iterator(dir, ec)) { |
| try { |
| crt.addRevocationList(std::make_shared<crypto::RevocationList>( |
| fileutils::loadFile(crl))); |
| } catch (const std::exception& e) { |
| if (logger_) |
| logger_->warn("Unable to load revocation list: %s", e.what()); |
| } |
| } |
| |
| auto ocsp_dir = ocspPath_ / crt.getId().toString(); |
| for (const auto& ocsp_filepath : std::filesystem::directory_iterator(ocsp_dir, ec)) { |
| try { |
| auto ocsp = ocsp_filepath.path().filename().string(); |
| if (logger_) logger_->debug("Found {}", ocsp_filepath.path()); |
| auto serial = crt.getSerialNumber(); |
| if (dht::toHex(serial.data(), serial.size()) != ocsp) |
| continue; |
| // Save the response |
| auto ocspBlob = fileutils::loadFile(ocsp_filepath); |
| crt.ocspResponse = std::make_shared<dht::crypto::OcspResponse>(ocspBlob.data(), |
| ocspBlob.size()); |
| unsigned int status = crt.ocspResponse->getCertificateStatus(); |
| if (status == GNUTLS_OCSP_CERT_GOOD) { |
| if (logger_) logger_->debug("Certificate {:s} has good OCSP status", crt.getId()); |
| } else if (status == GNUTLS_OCSP_CERT_REVOKED) { |
| if (logger_) logger_->error("Certificate {:s} has revoked OCSP status", crt.getId()); |
| } else if (status == GNUTLS_OCSP_CERT_UNKNOWN) { |
| if (logger_) logger_->error("Certificate {:s} has unknown OCSP status", crt.getId()); |
| } else { |
| if (logger_) logger_->error("Certificate {:s} has invalid OCSP status", crt.getId()); |
| } |
| } catch (const std::exception& e) { |
| if (logger_) |
| logger_->warn("Unable to load OCSP revocation status: {:s}", e.what()); |
| } |
| } |
| } |
| |
| std::vector<std::string> |
| CertificateStore::getPinnedCertificates() const |
| { |
| std::lock_guard l(lock_); |
| |
| std::vector<std::string> certIds; |
| certIds.reserve(certs_.size()); |
| for (const auto& crt : certs_) |
| certIds.emplace_back(crt.first); |
| return certIds; |
| } |
| |
| std::shared_ptr<crypto::Certificate> |
| CertificateStore::getCertificate(const std::string& k) |
| { |
| auto getCertificate_ = [this](const std::string& k) -> std::shared_ptr<crypto::Certificate> { |
| auto cit = certs_.find(k); |
| return cit != certs_.cend() ? cit->second : std::shared_ptr<crypto::Certificate>{}; |
| }; |
| std::unique_lock l(lock_); |
| auto crt = getCertificate_(k); |
| // Check if certificate is complete |
| // If the certificate has been splitted, reconstruct it |
| auto top_issuer = crt; |
| while (top_issuer && top_issuer->getUID() != top_issuer->getIssuerUID()) { |
| if (top_issuer->issuer) { |
| top_issuer = top_issuer->issuer; |
| } else if (auto cert = getCertificate_(top_issuer->getIssuerUID())) { |
| top_issuer->issuer = cert; |
| top_issuer = cert; |
| } else { |
| // In this case, a certificate was not found |
| if (logger_) |
| logger_->warn("Incomplete certificate detected {:s}", k); |
| break; |
| } |
| } |
| return crt; |
| } |
| |
| std::shared_ptr<crypto::Certificate> |
| CertificateStore::getCertificateLegacy(const std::string& dataDir, const std::string& k) |
| { |
| try { |
| auto oldPath = fmt::format("{}/certificates/{}", dataDir, k); |
| if (fileutils::isFile(oldPath)) { |
| auto crt = std::make_shared<crypto::Certificate>(oldPath); |
| pinCertificate(crt, true); |
| return crt; |
| } |
| } catch (const std::exception& e) { |
| if (logger_) |
| logger_->warn("Unable to load certificate: {:s}", e.what()); |
| } |
| return {}; |
| } |
| |
| std::shared_ptr<crypto::Certificate> |
| CertificateStore::findCertificateByName(const std::string& name, crypto::NameType type) const |
| { |
| std::unique_lock l(lock_); |
| for (auto& i : certs_) { |
| if (i.second->getName() == name) |
| return i.second; |
| if (type != crypto::NameType::UNKNOWN) { |
| for (const auto& alt : i.second->getAltNames()) |
| if (alt.first == type and alt.second == name) |
| return i.second; |
| } |
| } |
| return {}; |
| } |
| |
| std::shared_ptr<crypto::Certificate> |
| CertificateStore::findCertificateByUID(const std::string& uid) const |
| { |
| std::unique_lock l(lock_); |
| for (auto& i : certs_) { |
| if (i.second->getUID() == uid) |
| return i.second; |
| } |
| return {}; |
| } |
| |
| std::shared_ptr<crypto::Certificate> |
| CertificateStore::findIssuer(const std::shared_ptr<crypto::Certificate>& crt) const |
| { |
| std::shared_ptr<crypto::Certificate> ret {}; |
| auto n = crt->getIssuerUID(); |
| if (not n.empty()) { |
| if (crt->issuer and crt->issuer->getUID() == n) |
| ret = crt->issuer; |
| else |
| ret = findCertificateByUID(n); |
| } |
| if (not ret) { |
| n = crt->getIssuerName(); |
| if (not n.empty()) |
| ret = findCertificateByName(n); |
| } |
| if (not ret) |
| return ret; |
| unsigned verify_out = 0; |
| int err = gnutls_x509_crt_verify(crt->cert, &ret->cert, 1, 0, &verify_out); |
| if (err != GNUTLS_E_SUCCESS) { |
| if (logger_) |
| logger_->warn("gnutls_x509_crt_verify failed: {:s}", gnutls_strerror(err)); |
| return {}; |
| } |
| if (verify_out & GNUTLS_CERT_INVALID) |
| return {}; |
| return ret; |
| } |
| |
| static std::vector<crypto::Certificate> |
| readCertificates(const std::filesystem::path& path, const std::string& crl_path) |
| { |
| std::vector<crypto::Certificate> ret; |
| if (std::filesystem::is_directory(path)) { |
| std::error_code ec; |
| for (const auto& file : std::filesystem::directory_iterator(path, ec)) { |
| auto certs = readCertificates(file, crl_path); |
| ret.insert(std::end(ret), |
| std::make_move_iterator(std::begin(certs)), |
| std::make_move_iterator(std::end(certs))); |
| } |
| } else { |
| try { |
| auto data = fileutils::loadFile(path); |
| const gnutls_datum_t dt {data.data(), (unsigned) data.size()}; |
| gnutls_x509_crt_t* certs {nullptr}; |
| unsigned cert_num {0}; |
| gnutls_x509_crt_list_import2(&certs, &cert_num, &dt, GNUTLS_X509_FMT_PEM, 0); |
| for (unsigned i = 0; i < cert_num; i++) |
| ret.emplace_back(certs[i]); |
| gnutls_free(certs); |
| } catch (const std::exception& e) { |
| }; |
| } |
| return ret; |
| } |
| |
| void |
| CertificateStore::pinCertificatePath(const std::string& path, |
| std::function<void(const std::vector<std::string>&)> cb) |
| { |
| dht::ThreadPool::computation().run([&, path, cb]() { |
| auto certs = readCertificates(path, crlPath_.string()); |
| std::vector<std::string> ids; |
| std::vector<std::weak_ptr<crypto::Certificate>> scerts; |
| ids.reserve(certs.size()); |
| scerts.reserve(certs.size()); |
| { |
| std::lock_guard l(lock_); |
| |
| for (auto& cert : certs) { |
| try { |
| auto shared = std::make_shared<crypto::Certificate>(std::move(cert)); |
| scerts.emplace_back(shared); |
| auto e = certs_.emplace(shared->getId().toString(), shared); |
| ids.emplace_back(e.first->first); |
| e = certs_.emplace(shared->getLongId().toString(), shared); |
| ids.emplace_back(e.first->first); |
| } catch (const std::exception& e) { |
| if (logger_) |
| logger_->warn("Unable to load certificate: {:s}", e.what()); |
| } |
| } |
| paths_.emplace(path, std::move(scerts)); |
| } |
| if (logger_) logger_->d("CertificateStore: loaded %zu certificates from %s.", certs.size(), path.c_str()); |
| if (cb) |
| cb(ids); |
| //emitSignal<libdhtnet::ConfigurationSignal::CertificatePathPinned>(path, ids); |
| }); |
| } |
| |
| unsigned |
| CertificateStore::unpinCertificatePath(const std::string& path) |
| { |
| std::lock_guard l(lock_); |
| |
| auto certs = paths_.find(path); |
| if (certs == std::end(paths_)) |
| return 0; |
| unsigned n = 0; |
| for (const auto& wcert : certs->second) { |
| if (auto cert = wcert.lock()) { |
| certs_.erase(cert->getId().toString()); |
| ++n; |
| } |
| } |
| paths_.erase(certs); |
| return n; |
| } |
| |
| std::vector<std::string> |
| CertificateStore::pinCertificate(const std::vector<uint8_t>& cert, bool local) noexcept |
| { |
| try { |
| return pinCertificate(crypto::Certificate(cert), local); |
| } catch (const std::exception& e) { |
| } |
| return {}; |
| } |
| |
| std::vector<std::string> |
| CertificateStore::pinCertificate(crypto::Certificate&& cert, bool local) |
| { |
| return pinCertificate(std::make_shared<crypto::Certificate>(std::move(cert)), local); |
| } |
| |
| std::vector<std::string> |
| CertificateStore::pinCertificate(const std::shared_ptr<crypto::Certificate>& cert, bool local) |
| { |
| bool sig {false}; |
| std::vector<std::string> ids {}; |
| { |
| auto c = cert; |
| std::lock_guard l(lock_); |
| while (c) { |
| bool inserted; |
| auto id = c->getId().toString(); |
| auto longId = c->getLongId().toString(); |
| decltype(certs_)::iterator it; |
| std::tie(it, inserted) = certs_.emplace(id, c); |
| if (not inserted) |
| it->second = c; |
| std::tie(it, inserted) = certs_.emplace(longId, c); |
| if (not inserted) |
| it->second = c; |
| if (local) { |
| for (const auto& crl : c->getRevocationLists()) |
| pinRevocationList(id, *crl); |
| } |
| ids.emplace_back(longId); |
| ids.emplace_back(id); |
| c = c->issuer; |
| sig |= inserted; |
| } |
| if (local) { |
| if (sig) |
| fileutils::saveFile(certPath_ / ids.front(), cert->getPacked()); |
| } |
| } |
| //for (const auto& id : ids) |
| // emitSignal<libdhtnet::ConfigurationSignal::CertificatePinned>(id); |
| return ids; |
| } |
| |
| bool |
| CertificateStore::unpinCertificate(const std::string& id) |
| { |
| std::lock_guard l(lock_); |
| |
| certs_.erase(id); |
| return remove(certPath_ / id); |
| } |
| |
| bool |
| CertificateStore::setTrustedCertificate(const std::string& id, TrustStatus status) |
| { |
| if (status == TrustStatus::TRUSTED) { |
| if (auto crt = getCertificate(id)) { |
| trustedCerts_.emplace_back(crt); |
| return true; |
| } |
| } else { |
| auto tc = std::find_if(trustedCerts_.begin(), |
| trustedCerts_.end(), |
| [&](const std::shared_ptr<crypto::Certificate>& crt) { |
| return crt->getId().toString() == id; |
| }); |
| if (tc != trustedCerts_.end()) { |
| trustedCerts_.erase(tc); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| std::vector<gnutls_x509_crt_t> |
| CertificateStore::getTrustedCertificates() const |
| { |
| std::vector<gnutls_x509_crt_t> crts; |
| crts.reserve(trustedCerts_.size()); |
| for (auto& crt : trustedCerts_) |
| crts.emplace_back(crt->getCopy()); |
| return crts; |
| } |
| |
| void |
| CertificateStore::pinRevocationList(const std::string& id, |
| const std::shared_ptr<dht::crypto::RevocationList>& crl) |
| { |
| try { |
| if (auto c = getCertificate(id)) |
| c->addRevocationList(crl); |
| pinRevocationList(id, *crl); |
| } catch (...) { |
| if (logger_) |
| logger_->warn("Unable to add revocation list"); |
| } |
| } |
| |
| void |
| CertificateStore::pinRevocationList(const std::string& id, const dht::crypto::RevocationList& crl) |
| { |
| fileutils::check_dir(crlPath_ / id); |
| fileutils::saveFile(crlPath_ / id / dht::toHex(crl.getNumber()), |
| crl.getPacked()); |
| } |
| |
| void |
| CertificateStore::pinOcspResponse(const dht::crypto::Certificate& cert) |
| { |
| if (not cert.ocspResponse) |
| return; |
| try { |
| cert.ocspResponse->getCertificateStatus(); |
| } catch (dht::crypto::CryptoException& e) { |
| if (logger_) logger_->error("Failed to read certificate status of OCSP response: {:s}", e.what()); |
| return; |
| } |
| auto id = cert.getId().toString(); |
| auto serial = cert.getSerialNumber(); |
| auto serialhex = dht::toHex(serial); |
| auto dir = ocspPath_ / id; |
| |
| if (auto localCert = getCertificate(id)) { |
| // Update certificate in the local store if relevant |
| if (localCert.get() != &cert && serial == localCert->getSerialNumber()) { |
| if (logger_) logger_->d("Updating OCSP for certificate %s in the local store", id.c_str()); |
| localCert->ocspResponse = cert.ocspResponse; |
| } |
| } |
| |
| dht::ThreadPool::io().run([l=logger_, |
| path = dir / serialhex, |
| dir = std::move(dir), |
| id = std::move(id), |
| serialhex = std::move(serialhex), |
| ocspResponse = cert.ocspResponse] { |
| if (l) l->d("Saving OCSP Response of device %s with serial %s", id.c_str(), serialhex.c_str()); |
| std::lock_guard lock(fileutils::getFileLock(path)); |
| fileutils::check_dir(dir.c_str()); |
| fileutils::saveFile(path, ocspResponse->pack()); |
| }); |
| } |
| |
| TrustStore::PermissionStatus |
| TrustStore::statusFromStr(const char* str) |
| { |
| if (!std::strcmp(str, libdhtnet::Certificate::Status::ALLOWED)) |
| return PermissionStatus::ALLOWED; |
| if (!std::strcmp(str, libdhtnet::Certificate::Status::BANNED)) |
| return PermissionStatus::BANNED; |
| return PermissionStatus::UNDEFINED; |
| } |
| |
| const char* |
| TrustStore::statusToStr(TrustStore::PermissionStatus s) |
| { |
| switch (s) { |
| case PermissionStatus::ALLOWED: |
| return libdhtnet::Certificate::Status::ALLOWED; |
| case PermissionStatus::BANNED: |
| return libdhtnet::Certificate::Status::BANNED; |
| case PermissionStatus::UNDEFINED: |
| default: |
| return libdhtnet::Certificate::Status::UNDEFINED; |
| } |
| } |
| |
| TrustStatus |
| trustStatusFromStr(const char* str) |
| { |
| if (!std::strcmp(str, libdhtnet::Certificate::TrustStatus::TRUSTED)) |
| return TrustStatus::TRUSTED; |
| return TrustStatus::UNTRUSTED; |
| } |
| |
| const char* |
| statusToStr(TrustStatus s) |
| { |
| switch (s) { |
| case TrustStatus::TRUSTED: |
| return libdhtnet::Certificate::TrustStatus::TRUSTED; |
| case TrustStatus::UNTRUSTED: |
| default: |
| return libdhtnet::Certificate::TrustStatus::UNTRUSTED; |
| } |
| } |
| |
| bool |
| TrustStore::addRevocationList(dht::crypto::RevocationList&& crl) |
| { |
| allowed_.add(crl); |
| return true; |
| } |
| |
| bool |
| TrustStore::setCertificateStatus(const std::string& cert_id, |
| const TrustStore::PermissionStatus status) |
| { |
| return setCertificateStatus(nullptr, cert_id, status, false); |
| } |
| |
| bool |
| TrustStore::setCertificateStatus(const std::shared_ptr<crypto::Certificate>& cert, |
| const TrustStore::PermissionStatus status, |
| bool local) |
| { |
| return setCertificateStatus(cert, cert->getId().toString(), status, local); |
| } |
| |
| bool |
| TrustStore::setCertificateStatus(std::shared_ptr<crypto::Certificate> cert, |
| const std::string& cert_id, |
| const TrustStore::PermissionStatus status, |
| bool local) |
| { |
| if (cert) |
| certStore_.pinCertificate(cert, local); |
| std::lock_guard lk(mutex_); |
| updateKnownCerts(); |
| bool dirty {false}; |
| if (status == PermissionStatus::UNDEFINED) { |
| unknownCertStatus_.erase(cert_id); |
| dirty = certStatus_.erase(cert_id); |
| } else { |
| bool allowed = (status == PermissionStatus::ALLOWED); |
| auto s = certStatus_.find(cert_id); |
| if (s == std::end(certStatus_)) { |
| // Certificate state is currently undefined |
| if (not cert) |
| cert = certStore_.getCertificate(cert_id); |
| if (cert) { |
| unknownCertStatus_.erase(cert_id); |
| auto& crt_status = certStatus_[cert_id]; |
| if (not crt_status.first) |
| crt_status.first = cert; |
| crt_status.second.allowed = allowed; |
| setStoreCertStatus(*cert, allowed); |
| } else { |
| // Unable to find certificate |
| unknownCertStatus_[cert_id].allowed = allowed; |
| } |
| } else { |
| // Certificate is already allowed or banned |
| if (s->second.second.allowed != allowed) { |
| s->second.second.allowed = allowed; |
| if (allowed) // Certificate is re-added after ban, rebuld needed |
| dirty = true; |
| else |
| allowed_.remove(*s->second.first, false); |
| } |
| } |
| } |
| if (dirty) |
| rebuildTrust(); |
| return true; |
| } |
| |
| TrustStore::PermissionStatus |
| TrustStore::getCertificateStatus(const std::string& cert_id) const |
| { |
| std::lock_guard lk(mutex_); |
| auto cert = certStore_.getCertificate(cert_id); |
| if (!cert) |
| return PermissionStatus::UNDEFINED; |
| auto allowed = false; |
| auto found = false; |
| while (cert) { |
| auto s = certStatus_.find(cert->getId().toString()); |
| if (s != std::end(certStatus_)) { |
| if (!found) { |
| found = true; |
| allowed = true; // we need to find at least a certificate |
| } |
| allowed &= s->second.second.allowed; |
| if (!allowed) |
| return PermissionStatus::BANNED; |
| } else { |
| auto us = unknownCertStatus_.find(cert->getId().toString()); |
| if (us != std::end(unknownCertStatus_)) { |
| if (!found) { |
| found = true; |
| allowed = true; // we need to find at least a certificate |
| } |
| allowed &= us->second.allowed; |
| if (!allowed) |
| return PermissionStatus::BANNED; |
| } |
| } |
| if (cert->getUID() == cert->getIssuerUID()) |
| break; |
| cert = cert->issuer? cert->issuer : certStore_.getCertificate(cert->getIssuerUID()); |
| } |
| |
| return allowed ? PermissionStatus::ALLOWED : PermissionStatus::UNDEFINED; |
| } |
| |
| std::vector<std::string> |
| TrustStore::getCertificatesByStatus(TrustStore::PermissionStatus status) const |
| { |
| std::lock_guard lk(mutex_); |
| std::vector<std::string> ret; |
| for (const auto& i : certStatus_) |
| if (i.second.second.allowed == (status == TrustStore::PermissionStatus::ALLOWED)) |
| ret.emplace_back(i.first); |
| for (const auto& i : unknownCertStatus_) |
| if (i.second.allowed == (status == TrustStore::PermissionStatus::ALLOWED)) |
| ret.emplace_back(i.first); |
| return ret; |
| } |
| |
| bool |
| TrustStore::isAllowed(const crypto::Certificate& crt, bool allowPublic) |
| { |
| // Match by certificate pinning |
| std::lock_guard lk(mutex_); |
| bool allowed {allowPublic}; |
| for (auto c = &crt; c; c = c->issuer.get()) { |
| auto status = getCertificateStatus(c->getId().toString()); // lock mutex_ |
| if (status == PermissionStatus::ALLOWED) |
| allowed = true; |
| else if (status == PermissionStatus::BANNED) |
| return false; |
| } |
| |
| // Match by certificate chain |
| updateKnownCerts(); |
| auto ret = allowed_.verify(crt); |
| // Unknown issuer (only that) are accepted if allowPublic is true |
| if (not ret |
| and !(allowPublic and ret.result == (GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND))) { |
| if (certStore_.logger()) |
| certStore_.logger()->warn("%s", ret.toString().c_str()); |
| return false; |
| } |
| |
| return allowed; |
| } |
| |
| void |
| TrustStore::updateKnownCerts() |
| { |
| auto i = std::begin(unknownCertStatus_); |
| while (i != std::end(unknownCertStatus_)) { |
| if (auto crt = certStore_.getCertificate(i->first)) { |
| certStatus_.emplace(i->first, std::make_pair(crt, i->second)); |
| setStoreCertStatus(*crt, i->second.allowed); |
| i = unknownCertStatus_.erase(i); |
| } else |
| ++i; |
| } |
| } |
| |
| void |
| TrustStore::setStoreCertStatus(const crypto::Certificate& crt, bool status) |
| { |
| if (status) |
| allowed_.add(crt); |
| else |
| allowed_.remove(crt, false); |
| } |
| |
| void |
| TrustStore::rebuildTrust() |
| { |
| allowed_ = {}; |
| for (const auto& c : certStatus_) |
| setStoreCertStatus(*c.second.first, c.second.second.allowed); |
| } |
| |
| } // namespace tls |
| } // namespace dhtnet |