Commit 50fa02c0 authored by Adrien Béraud's avatar Adrien Béraud Committed by gerrit2

certificate store: add CRL support

Add certificate revocation list support to the
CertificateStore.

* Uses new OpenDHT CRL crypto API (bump to new version).
* Stores CRLs in .local/share/ring/crls/{cert_id}/{crl_number}
* CRLs are associated to the corresponding certificate authority.
* CRLs are used in certificate checks.

Change-Id: I1aa86d9bbd5631f5c38bccc69e1e9125b8f8b2cc
parent 73f208d1
......@@ -40,41 +40,56 @@ CertificateStore::instance()
}
CertificateStore::CertificateStore()
: certPath_(fileutils::get_data_dir()+DIR_SEPARATOR_CH+"certificates")
: certPath_(fileutils::get_data_dir()+DIR_SEPARATOR_CH+"certificates"),
crlPath_(fileutils::get_data_dir()+DIR_SEPARATOR_CH+"crls")
{
fileutils::check_dir(certPath_.c_str());
loadLocalCertificates(certPath_);
fileutils::check_dir(crlPath_.c_str());
loadLocalCertificates();
}
unsigned
CertificateStore::loadLocalCertificates(const std::string& path)
CertificateStore::loadLocalCertificates()
{
std::lock_guard<std::mutex> l(lock_);
auto dir_content = fileutils::readDirectory(path);
auto dir_content = fileutils::readDirectory(certPath_);
unsigned n = 0;
for (const auto& f : dir_content) {
try {
auto crt = std::make_shared<crypto::Certificate>(fileutils::loadFile(path+DIR_SEPARATOR_CH+f));
auto crt = std::make_shared<crypto::Certificate>(fileutils::loadFile(certPath_+DIR_SEPARATOR_CH+f));
auto id = crt->getId().toString();
if (id != f)
throw std::logic_error({});
certs_.emplace(id, crt);
++n;
crt = crt->issuer;
while (crt) {
certs_.emplace(crt->getId().toString(), crt);
auto id_str = crt->getId().toString();
certs_.emplace(id_str, crt);
loadRevocations(*crt);
crt = crt->issuer;
++n;
}
} catch (const std::exception& e) {
remove((path+DIR_SEPARATOR_CH+f).c_str());
remove((certPath_+DIR_SEPARATOR_CH+f).c_str());
}
}
RING_DBG("CertificateStore: loaded %u local certificates.", n);
return n;
}
void
CertificateStore::loadRevocations(crypto::Certificate& crt)
{
auto dir = crlPath_+DIR_SEPARATOR_CH+crt.getId().toString();
auto crl_dir_content = fileutils::readDirectory(dir);
for (const auto& crl : crl_dir_content) {
try {
crt.addRevocationList(std::make_shared<crypto::RevocationList>(fileutils::loadFile(dir+DIR_SEPARATOR_CH+crl)));
} catch (const std::exception& e) {
RING_WARN("Can't load revocation list: %s", e.what());
}
}
}
std::vector<std::string>
CertificateStore::getPinnedCertificates() const
{
......@@ -156,13 +171,13 @@ CertificateStore::findIssuer(std::shared_ptr<crypto::Certificate> crt) const
}
static std::vector<crypto::Certificate>
readCertificates(const std::string& path)
readCertificates(const std::string& path, const std::string& crl_path)
{
std::vector<crypto::Certificate> ret;
if (fileutils::isDirectory(path)) {
auto files = fileutils::readDirectory(path);
for (const auto& file : files) {
auto certs = readCertificates(path+"/"+file);
auto certs = readCertificates(path+DIR_SEPARATOR_CH+file, crl_path);
ret.insert(std::end(ret),
std::make_move_iterator(std::begin(certs)),
std::make_move_iterator(std::end(certs)));
......@@ -185,7 +200,7 @@ void
CertificateStore::pinCertificatePath(const std::string& path, std::function<void(const std::vector<std::string>&)> cb)
{
ThreadPool::instance().run([&, path, cb]() {
auto certs = readCertificates(path);
auto certs = readCertificates(path, crlPath_);
std::vector<std::string> ids;
std::vector<std::weak_ptr<crypto::Certificate>> scerts;
ids.reserve(certs.size());
......@@ -245,8 +260,7 @@ CertificateStore::pinCertificate(crypto::Certificate&& cert, bool local)
}
std::vector<std::string>
CertificateStore::pinCertificate(std::shared_ptr<crypto::Certificate> cert,
bool local)
CertificateStore::pinCertificate(std::shared_ptr<crypto::Certificate> cert, bool local)
{
bool sig {false};
std::vector<std::string> ids {};
......@@ -258,12 +272,20 @@ CertificateStore::pinCertificate(std::shared_ptr<crypto::Certificate> cert,
auto id = c->getId().toString();
decltype(certs_)::iterator it;
std::tie(it, inserted) = certs_.emplace(id, c);
if (not inserted)
it->second = c;
if (local) {
for (const auto& crl : c->getRevocationLists())
pinRevocationList(id, *crl);
}
ids.emplace_back(id);
c = c->issuer;
sig |= inserted;
}
if (sig and local)
fileutils::saveFile(certPath_+DIR_SEPARATOR_CH+ids.front(), cert->getPacked());
if (local) {
if (sig)
fileutils::saveFile(certPath_+DIR_SEPARATOR_CH+ids.front(), cert->getPacked());
}
}
for (const auto& id : ids)
emitSignal<DRing::ConfigurationSignal::CertificatePinned>(id);
......@@ -311,6 +333,31 @@ CertificateStore::getTrustedCertificates() const
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 (...) {
RING_WARN("Can't add revocation list");
}
}
void
CertificateStore::pinRevocationList(const std::string& id, const dht::crypto::RevocationList& crl)
{
auto v = crl.getNumber();
std::stringstream ss;
ss << std::hex;
for (const auto& b : v)
ss << (unsigned)b;
fileutils::check_dir((crlPath_+DIR_SEPARATOR_CH+id).c_str());
fileutils::saveFile(crlPath_+DIR_SEPARATOR_CH+id+DIR_SEPARATOR_CH+ss.str(), crl.getPacked());
}
TrustStore::PermissionStatus
TrustStore::statusFromStr(const char* str)
{
......@@ -367,6 +414,15 @@ TrustStore::~TrustStore()
gnutls_x509_trust_list_deinit(allowed_, false);
}
bool
TrustStore::addRevocationList(dht::crypto::RevocationList&& crl)
{
auto packed = crl.getPacked();
revokedList_.emplace_back(std::forward<dht::crypto::RevocationList>(crl));
auto crlp = crl.get();
return gnutls_x509_trust_list_add_crls(allowed_, &crlp, 1, GNUTLS_TL_VERIFY_CRL, 0) > 0;
}
bool
TrustStore::setCertificateStatus(const std::string& cert_id,
const TrustStore::PermissionStatus status)
......@@ -510,6 +566,7 @@ TrustStore::getTrustedCertificates() const
bool
TrustStore::matchTrustStore(std::vector<gnutls_x509_crt_t>&& crts, gnutls_x509_trust_list_st* store)
{
RING_ERR("TrustStore::matchTrustStore: %lu", crts.size());
unsigned result = 0;
#if GNUTLS_VERSION_NUMBER > 0x030308
......@@ -547,6 +604,17 @@ getChain(const crypto::Certificate& crt)
return crts;
}
std::vector<gnutls_x509_crl_t>
getRevocationList(const crypto::Certificate& crt)
{
std::vector<gnutls_x509_crl_t> crls_ret;
const auto& crls = crt.getRevocationLists();
crls_ret.reserve(crls.size());
for (const auto& crl : crls)
crls_ret.emplace_back(crl->get());
return crls_ret;
}
void
TrustStore::updateKnownCerts()
{
......@@ -567,8 +635,13 @@ TrustStore::setStoreCertStatus(const crypto::Certificate& crt, TrustStore::Permi
if (not crt.isCA())
return;
if (status == PermissionStatus::ALLOWED)
if (status == PermissionStatus::ALLOWED) {
gnutls_x509_trust_list_add_cas(allowed_, &crt.cert, 1, 0);
auto crls = getRevocationList(crt);
if (not crls.empty())
if (gnutls_x509_trust_list_add_crls(allowed_, crls.data(), crls.size(), GNUTLS_TL_VERIFY_CRL, 0) == 0)
RING_WARN("No CRLs where added");
}
else
gnutls_x509_trust_list_remove_cas(allowed_, &crt.cert, 1);
......
......@@ -74,12 +74,21 @@ public:
bool setTrustedCertificate(const std::string& id, TrustStatus status);
std::vector<gnutls_x509_crt_t> getTrustedCertificates() const;
void pinRevocationList(const std::string& id, const std::shared_ptr<dht::crypto::RevocationList>& crl);
void pinRevocationList(const std::string& id, dht::crypto::RevocationList&& crl) {
pinRevocationList(id, std::make_shared<dht::crypto::RevocationList>(std::forward<dht::crypto::RevocationList>(crl)));
}
void loadRevocations(crypto::Certificate& crt);
private:
NON_COPYABLE(CertificateStore);
unsigned loadLocalCertificates(const std::string& path);
unsigned loadLocalCertificates();
void pinRevocationList(const std::string& id, const dht::crypto::RevocationList& crl);
const std::string certPath_;
const std::string crlPath_;
mutable std::mutex lock_;
std::map<std::string, std::shared_ptr<crypto::Certificate>> certs_;
......@@ -111,6 +120,8 @@ public:
static PermissionStatus statusFromStr(const char* str);
static const char* statusToStr(PermissionStatus s);
bool addRevocationList(dht::crypto::RevocationList&& crl);
bool setCertificateStatus(const std::string& cert_id, const PermissionStatus status);
bool setCertificateStatus(const std::shared_ptr<crypto::Certificate>& cert, PermissionStatus status, bool local = true);
......@@ -142,9 +153,11 @@ private:
// unknown certificates with known status
std::map<std::string, Status> unknownCertStatus_;
std::map<std::string, std::pair<std::shared_ptr<crypto::Certificate>, Status>> certStatus_;
std::vector<dht::crypto::RevocationList> revokedList_;
gnutls_x509_trust_list_st* allowed_;
};
std::vector<gnutls_x509_crt_t> getChain(const crypto::Certificate& crt);
std::vector<gnutls_x509_crl_t> getRevocationList(const crypto::Certificate& crt);
}} // namespace ring::tls
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment