diff options
author | Kai Michaelis <[email protected]> | 2016-08-10 17:51:42 +0200 |
---|---|---|
committer | seu <[email protected]> | 2016-10-02 18:10:32 +0200 |
commit | cc5bca63e7e7b4ae1650e9f468e3c1a00623579f (patch) | |
tree | 1777abf62fc7e3e4a2dce8d3c8c87449f9b46816 /src/lib | |
parent | 9ba36a7097f29ee54a88e34f38e3bc9842accea6 (diff) |
Certificate store using SQLite
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/cert/x509/certstor.cpp | 59 | ||||
-rw-r--r-- | src/lib/cert/x509/certstor.h | 21 | ||||
-rw-r--r-- | src/lib/cert/x509/certstor_sql/certstor_sql.cpp | 320 | ||||
-rw-r--r-- | src/lib/cert/x509/certstor_sql/certstor_sql.h | 93 | ||||
-rw-r--r-- | src/lib/cert/x509/certstor_sql/info.txt | 5 | ||||
-rw-r--r-- | src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.cpp | 18 | ||||
-rw-r--r-- | src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.h | 23 | ||||
-rw-r--r-- | src/lib/cert/x509/certstor_sqlite3/info.txt | 6 | ||||
-rw-r--r-- | src/lib/cert/x509/ocsp.cpp | 4 | ||||
-rw-r--r-- | src/lib/cert/x509/x509_crl.cpp | 9 | ||||
-rw-r--r-- | src/lib/cert/x509/x509_crl.h | 3 | ||||
-rw-r--r-- | src/lib/cert/x509/x509_ext.cpp | 8 | ||||
-rw-r--r-- | src/lib/cert/x509/x509_ext.h | 6 | ||||
-rw-r--r-- | src/lib/cert/x509/x509path.cpp | 76 | ||||
-rw-r--r-- | src/lib/cert/x509/x509path.h | 6 | ||||
-rw-r--r-- | src/lib/utils/database.h | 2 | ||||
-rw-r--r-- | src/lib/utils/sqlite3/sqlite3.cpp | 7 | ||||
-rw-r--r-- | src/lib/utils/sqlite3/sqlite3.h | 1 |
18 files changed, 585 insertions, 82 deletions
diff --git a/src/lib/cert/x509/certstor.cpp b/src/lib/cert/x509/certstor.cpp index 26c9ce117..4ef0e5240 100644 --- a/src/lib/cert/x509/certstor.cpp +++ b/src/lib/cert/x509/certstor.cpp @@ -5,62 +5,73 @@ * Botan is released under the Simplified BSD License (see license.txt) */ +#include <cassert> #include <botan/certstor.h> +#include <botan/ber_dec.h> +#include <botan/der_enc.h> #include <botan/internal/filesystem.h> +#include <botan/pkcs8.h> +#include <botan/data_src.h> +#include <botan/auto_rng.h> +#include <botan/hash.h> +#include <botan/hex.h> + +#include <iostream> namespace Botan { -const X509_CRL* Certificate_Store::find_crl_for(const X509_Certificate&) const +std::shared_ptr<const X509_CRL> Certificate_Store::find_crl_for(const X509_Certificate&) const { - return nullptr; + return std::shared_ptr<const X509_CRL>(); } void Certificate_Store_In_Memory::add_certificate(const X509_Certificate& cert) { for(size_t i = 0; i != m_certs.size(); ++i) { - if(m_certs[i] == cert) + if(*m_certs[i] == cert) return; } - m_certs.push_back(cert); + m_certs.push_back(std::make_shared<X509_Certificate>(cert)); } std::vector<X509_DN> Certificate_Store_In_Memory::all_subjects() const { std::vector<X509_DN> subjects; for(size_t i = 0; i != m_certs.size(); ++i) - subjects.push_back(m_certs[i].subject_dn()); + subjects.push_back(m_certs[i]->subject_dn()); return subjects; } namespace { -const X509_Certificate* +template<typename T> +std::shared_ptr<const X509_Certificate> cert_search(const X509_DN& subject_dn, const std::vector<byte>& key_id, - const std::vector<X509_Certificate>& certs) + const std::vector<std::shared_ptr<T>>& certs) { for(size_t i = 0; i != certs.size(); ++i) { // Only compare key ids if set in both call and in the cert if(key_id.size()) { - std::vector<byte> skid = certs[i].subject_key_id(); + std::vector<byte> skid = certs[i]->subject_key_id(); if(skid.size() && skid != key_id) // no match continue; } - if(certs[i].subject_dn() == subject_dn) - return &certs[i]; + if(certs[i]->subject_dn() == subject_dn) + return certs[i]; } - return nullptr; + return std::shared_ptr<const X509_Certificate>(); } } -const X509_Certificate* +std::shared_ptr<const X509_Certificate> Certificate_Store_In_Memory::find_cert(const X509_DN& subject_dn, const std::vector<byte>& key_id) const { @@ -74,19 +85,19 @@ void Certificate_Store_In_Memory::add_crl(const X509_CRL& crl) for(size_t i = 0; i != m_crls.size(); ++i) { // Found an update of a previously existing one; replace it - if(m_crls[i].issuer_dn() == crl_issuer) + if(m_crls[i]->issuer_dn() == crl_issuer) { - if(m_crls[i].this_update() <= crl.this_update()) - m_crls[i] = crl; + if(m_crls[i]->this_update() <= crl.this_update()) + m_crls[i] = std::make_shared<X509_CRL>(crl); return; } } // Totally new CRL, add to the list - m_crls.push_back(crl); + m_crls.push_back(std::make_shared<X509_CRL>(crl)); } -const X509_CRL* Certificate_Store_In_Memory::find_crl_for(const X509_Certificate& subject) const +std::shared_ptr<const X509_CRL> Certificate_Store_In_Memory::find_crl_for(const X509_Certificate& subject) const { const std::vector<byte>& key_id = subject.authority_key_id(); @@ -95,17 +106,17 @@ const X509_CRL* Certificate_Store_In_Memory::find_crl_for(const X509_Certificate // Only compare key ids if set in both call and in the CRL if(key_id.size()) { - std::vector<byte> akid = m_crls[i].authority_key_id(); + std::vector<byte> akid = m_crls[i]->authority_key_id(); if(akid.size() && akid != key_id) // no match continue; } - if(m_crls[i].issuer_dn() == subject.issuer_dn()) - return &m_crls[i]; + if(m_crls[i]->issuer_dn() == subject.issuer_dn()) + return m_crls[i]; } - return nullptr; + return std::shared_ptr<const X509_CRL>(); } Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert) @@ -123,7 +134,7 @@ Certificate_Store_In_Memory::Certificate_Store_In_Memory(const std::string& dir) { try { - m_certs.push_back(X509_Certificate(cert_file)); + m_certs.push_back(std::make_shared<X509_Certificate>(cert_file)); } catch(std::exception&) { @@ -131,7 +142,7 @@ Certificate_Store_In_Memory::Certificate_Store_In_Memory(const std::string& dir) } } -const X509_Certificate* +std::shared_ptr<const X509_Certificate> Certificate_Store_Overlay::find_cert(const X509_DN& subject_dn, const std::vector<byte>& key_id) const { @@ -142,7 +153,7 @@ std::vector<X509_DN> Certificate_Store_Overlay::all_subjects() const { std::vector<X509_DN> subjects; for(size_t i = 0; i != m_certs.size(); ++i) - subjects.push_back(m_certs[i].subject_dn()); + subjects.push_back(m_certs[i]->subject_dn()); return subjects; } diff --git a/src/lib/cert/x509/certstor.h b/src/lib/cert/x509/certstor.h index 29948c709..c769bfe5c 100644 --- a/src/lib/cert/x509/certstor.h +++ b/src/lib/cert/x509/certstor.h @@ -10,6 +10,8 @@ #include <botan/x509cert.h> #include <botan/x509_crl.h> +#include <botan/database.h> +#include <botan/pbkdf.h> namespace Botan { @@ -24,10 +26,10 @@ class BOTAN_DLL Certificate_Store /** * Subject DN and (optionally) key identifier */ - virtual const X509_Certificate* + virtual std::shared_ptr<const X509_Certificate> find_cert(const X509_DN& subject_dn, const std::vector<byte>& key_id) const = 0; - virtual const X509_CRL* find_crl_for(const X509_Certificate& subject) const; + virtual std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const; bool certificate_known(const X509_Certificate& cert) const { @@ -60,32 +62,31 @@ class BOTAN_DLL Certificate_Store_In_Memory : public Certificate_Store std::vector<X509_DN> all_subjects() const override; - const X509_Certificate* find_cert( + std::shared_ptr<const X509_Certificate> find_cert( const X509_DN& subject_dn, const std::vector<byte>& key_id) const override; - const X509_CRL* find_crl_for(const X509_Certificate& subject) const override; + std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override; private: // TODO: Add indexing on the DN and key id to avoid linear search - std::vector<X509_Certificate> m_certs; - std::vector<X509_CRL> m_crls; + std::vector<std::shared_ptr<X509_Certificate>> m_certs; + std::vector<std::shared_ptr<X509_CRL>> m_crls; }; class BOTAN_DLL Certificate_Store_Overlay : public Certificate_Store { public: - explicit Certificate_Store_Overlay(const std::vector<X509_Certificate>& certs) : + explicit Certificate_Store_Overlay(const std::vector<std::shared_ptr<const X509_Certificate>>& certs) : m_certs(certs) {} std::vector<X509_DN> all_subjects() const override; - const X509_Certificate* find_cert( + std::shared_ptr<const X509_Certificate> find_cert( const X509_DN& subject_dn, const std::vector<byte>& key_id) const override; private: - const std::vector<X509_Certificate>& m_certs; + const std::vector<std::shared_ptr<const X509_Certificate>>& m_certs; }; } - #endif diff --git a/src/lib/cert/x509/certstor_sql/certstor_sql.cpp b/src/lib/cert/x509/certstor_sql/certstor_sql.cpp new file mode 100644 index 000000000..d8325fb87 --- /dev/null +++ b/src/lib/cert/x509/certstor_sql/certstor_sql.cpp @@ -0,0 +1,320 @@ +/* +* Certificate Store in SQL +* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/certstor_sql.h> +#include <botan/ber_dec.h> +#include <botan/der_enc.h> +#include <botan/internal/filesystem.h> +#include <botan/pkcs8.h> +#include <botan/data_src.h> +#include <botan/auto_rng.h> +#include <botan/hash.h> +#include <botan/hex.h> + +namespace Botan { + +Certificate_Store_In_SQL::Certificate_Store_In_SQL(std::shared_ptr<SQL_Database> db, + const std::string& passwd, + const std::string& table_prefix) +: m_database(db), m_prefix(table_prefix), m_password(passwd) + { + m_database->create_table("CREATE TABLE IF NOT EXISTS certificates ( \ + fingerprint BLOB PRIMARY KEY, \ + subject_dn BLOB, \ + key_id BLOB, \ + priv_fingerprint BLOB, \ + certificate BLOB UNIQUE NOT NULL\ + )"); + m_database->create_table("CREATE TABLE IF NOT EXISTS keys ( \ + fingerprint BLOB PRIMARY KEY, \ + key BLOB UNIQUE NOT NULL\ + )"); + m_database->create_table("CREATE TABLE IF NOT EXISTS revoked (\ + fingerprint BLOB PRIMARY KEY, \ + reason BLOB NOT NULL, \ + time BLOB NOT NULL \ + )"); + } + +// Certificate handling +std::shared_ptr<const X509_Certificate> +Certificate_Store_In_SQL::find_cert(const X509_DN& subject_dn, const std::vector<byte>& key_id) const + { + DER_Encoder enc; + std::shared_ptr<SQL_Database::Statement> stmt; + + subject_dn.encode_into(enc); + + if(key_id.empty()) + { + stmt = m_database->new_statement("SELECT certificate FROM certificates WHERE subject_dn == ?1"); + stmt->bind(1,enc.get_contents_unlocked()); + } + else + { + stmt = m_database->new_statement("SELECT certificate FROM certificates WHERE\ + subject_dn == ?1 AND (key_id == NULL OR key_id == ?2)"); + stmt->bind(1,enc.get_contents_unlocked()); + stmt->bind(2,key_id); + } + + std::shared_ptr<const X509_Certificate> cert; + while(stmt->step()) + { + auto blob = stmt->get_blob(0); + cert = std::make_shared<X509_Certificate>( + std::vector<byte>(blob.first,blob.first + blob.second)); + + } + + return cert; + } + +std::shared_ptr<const X509_CRL> +Certificate_Store_In_SQL::find_crl_for(const X509_Certificate& subject) const + { + auto all_crls = generate_crls(); + + for(auto crl: all_crls) + { + if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn()) + return std::shared_ptr<X509_CRL>(new X509_CRL(crl)); + } + + return std::shared_ptr<X509_CRL>(); + } + +std::vector<X509_DN> Certificate_Store_In_SQL::all_subjects() const + { + std::vector<X509_DN> ret; + auto stmt = m_database->new_statement("SELECT subject_dn FROM certificates"); + + while(stmt->step()) + { + auto blob = stmt->get_blob(0); + BER_Decoder dec(blob.first,blob.second); + X509_DN dn; + + dn.decode_from(dec); + + ret.push_back(dn); + } + + return ret; + } + +bool Certificate_Store_In_SQL::insert_cert(const X509_Certificate& cert) + { + if(find_cert(cert.subject_dn(),cert.subject_key_id())) + return false; + + DER_Encoder enc; + auto stmt = m_database->new_statement("INSERT OR REPLACE INTO certificates (\ + fingerprint, \ + subject_dn, \ + key_id, \ + priv_fingerprint, \ + certificate \ + ) VALUES ( ?1, ?2, ?3, ?4, ?5 )"); + + stmt->bind(1,cert.fingerprint("SHA-256")); + cert.subject_dn().encode_into(enc); + stmt->bind(2,enc.get_contents_unlocked()); + stmt->bind(3,cert.subject_key_id()); + stmt->bind(4,std::vector<byte>()); + enc = DER_Encoder(); + cert.encode_into(enc); + stmt->bind(5,enc.get_contents_unlocked()); + stmt->spin(); + + return true; + } + + +bool Certificate_Store_In_SQL::remove_cert(const X509_Certificate& cert) + { + if(!find_cert(cert.subject_dn(),cert.subject_key_id())) + return false; + + auto stmt = m_database->new_statement("DELETE FROM certificates WHERE fingerprint == ?1"); + + stmt->bind(1,cert.fingerprint("SHA-256")); + stmt->spin(); + + return true; + } + +// Private key handling +std::shared_ptr<const Private_Key> Certificate_Store_In_SQL::find_key(const X509_Certificate& cert) const + { + auto stmt = m_database->new_statement("SELECT key FROM keys JOIN certificates ON keys.fingerprint == certificates.priv_fingerprint WHERE certificates.fingerprint == ?1"); + stmt->bind(1,cert.fingerprint("SHA-256")); + + std::shared_ptr<const Private_Key> key; + while(stmt->step()) + { + auto blob = stmt->get_blob(0); + AutoSeeded_RNG rng; + DataSource_Memory src(blob.first,blob.second); + key.reset(PKCS8::load_key(src,rng,m_password)); + } + + return key; + } + +std::vector<std::shared_ptr<const X509_Certificate>> +Certificate_Store_In_SQL::find_certs_for_key(const Private_Key& key) const + { + AutoSeeded_RNG rng; + auto fpr = fingerprint_key(key); + auto stmt = m_database->new_statement("SELECT certificate FROM certificates WHERE priv_fingerprint == ?1"); + + stmt->bind(1,fpr); + + std::shared_ptr<const X509_Certificate> cert; + while(stmt->step()) + { + auto blob = stmt->get_blob(0); + cert = std::make_shared<X509_Certificate>( + std::vector<byte>(blob.first,blob.first + blob.second)); + } + + return {cert}; + } + +bool Certificate_Store_In_SQL::insert_key(const X509_Certificate& cert, const Private_Key& key) { + insert_cert(cert); + + if(find_key(cert)) + return false; + + AutoSeeded_RNG rng; + auto pkcs8 = PKCS8::BER_encode(key,rng,m_password); + auto fpr = fingerprint_key(key); + + //m_database->new_statement("BEGIN TRANSACTION")->spin(); + + auto stmt1 = m_database->new_statement( + "INSERT OR REPLACE INTO keys ( fingerprint, key ) VALUES ( ?1, ?2 )"); + + stmt1->bind(1,fpr); + stmt1->bind(2,pkcs8.data(),pkcs8.size()); + stmt1->spin(); + + auto stmt2 = m_database->new_statement( + "UPDATE certificates SET priv_fingerprint = ?1 WHERE fingerprint == ?2"); + + stmt2->bind(1,fpr); + stmt2->bind(2,cert.fingerprint("SHA-256")); + stmt2->spin(); + + //m_database->new_statement("END TRANSACTION")->spin(); + + return true; + } + +std::string Certificate_Store_In_SQL::fingerprint_key(const Botan::Private_Key& key) const + { + secure_vector<byte> buf = key.pkcs8_private_key(); + std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-256")); + hash->update(buf); + const auto hex_print = hex_encode(hash->final()); + + std::string formatted_print; + + for(size_t i = 0; i != hex_print.size(); i += 2) + { + formatted_print.push_back(hex_print[i]); + formatted_print.push_back(hex_print[i+1]); + + if(i != hex_print.size() - 2) + formatted_print.push_back(':'); + } + + return formatted_print; + } + +void Certificate_Store_In_SQL::remove_key(const Private_Key& key) + { + AutoSeeded_RNG rng; + auto fpr = fingerprint_key(key); + auto stmt = m_database->new_statement("DELETE FROM keys WHERE fingerprint == ?1"); + + stmt->bind(1,fpr); + stmt->spin(); + } + +// Revocation +void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code, const X509_Time& time) + { + insert_cert(cert); + + auto stmt1 = m_database->new_statement( + "INSERT OR REPLACE INTO revoked ( fingerprint, reason, time ) VALUES ( ?1, ?2, ?3 )"); + + stmt1->bind(1,cert.fingerprint("SHA-256")); + stmt1->bind(2,code); + + if(time.time_is_set()) + { + DER_Encoder der; + time.encode_into(der); + stmt1->bind(3,der.get_contents_unlocked()); + } + else + { + stmt1->bind(3,-1); + } + + stmt1->spin(); + } + +void Certificate_Store_In_SQL::affirm_cert(const X509_Certificate& cert) + { + auto stmt = m_database->new_statement("DELETE FROM revoked WHERE fingerprint == ?1"); + + stmt->bind(1,cert.fingerprint("SHA-256")); + stmt->spin(); + } + +std::vector<X509_CRL> Certificate_Store_In_SQL::generate_crls() const + { + auto stmt = m_database->new_statement( + "SELECT certificate,reason,time FROM revoked JOIN certificates ON certificates.fingerprint == revoked.fingerprint"); + + std::map<X509_DN,std::vector<CRL_Entry>> crls; + while(stmt->step()) + { + auto blob = stmt->get_blob(0); + auto cert = X509_Certificate( + std::vector<byte>(blob.first,blob.first + blob.second)); + auto code = static_cast<CRL_Code>(stmt->get_size_t(1)); + auto ent = CRL_Entry(cert,code); + + auto i = crls.find(cert.issuer_dn()); + if(i == crls.end()) + { + crls.insert(std::make_pair(cert.issuer_dn(),std::vector<CRL_Entry>({ent}))); + } + else + { + i->second.push_back(ent); + } + } + + std::vector<X509_CRL> ret; + X509_Time t(std::chrono::system_clock::now()); + + for(auto p: crls) + { + ret.push_back(X509_CRL(p.first,t,t,p.second)); + } + + return ret; + } + +} diff --git a/src/lib/cert/x509/certstor_sql/certstor_sql.h b/src/lib/cert/x509/certstor_sql/certstor_sql.h new file mode 100644 index 000000000..24db88cdd --- /dev/null +++ b/src/lib/cert/x509/certstor_sql/certstor_sql.h @@ -0,0 +1,93 @@ +/* +* Certificate Store in SQL +* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CERT_STORE_SQL_H__ +#define BOTAN_CERT_STORE_SQL_H__ + +#include <botan/certstor.h> +#include <botan/x509cert.h> +#include <botan/x509_crl.h> +#include <botan/database.h> + +namespace Botan { + +/** + * Certificate and private key store backed an SQL database. + */ +class BOTAN_DLL Certificate_Store_In_SQL : public Certificate_Store + { + public: + /** + * Create/open a certificate store backed by "db". + * Inserted private keys are encrypted using "passwd". + */ + explicit Certificate_Store_In_SQL(const std::shared_ptr<SQL_Database> db, + const std::string& passwd, + const std::string& table_prefix = ""); + + /// Returns the first certificate with matching subject DN and optional key ID. + virtual std::shared_ptr<const X509_Certificate> + find_cert(const X509_DN& subject_dn, const std::vector<byte>& key_id) const override; + + /// Generates a CRL for all certificates issued by "subject"s issuer. + virtual std::shared_ptr<const X509_CRL> + find_crl_for(const X509_Certificate& subject) const override; + + /// Returns all subject DNs known to the store instance, + virtual std::vector<X509_DN> all_subjects() const override; + + /** + * Inserts "cert" into the store, returns false if the certificate is + * already known and true if insertion was successful. + */ + bool insert_cert(const X509_Certificate& cert); + + /** + * Removes "cert" from the store. Returns false if the certificate could not + * be found and true if removal was successful. + */ + bool remove_cert(const X509_Certificate& cert); + + /// Returns the private key for "cert" or an empty shared_ptr if none was found. + std::shared_ptr<const Private_Key> find_key(const X509_Certificate&) const; + + /// Returns all certificates for private key "key". + std::vector<std::shared_ptr<const X509_Certificate>> + find_certs_for_key(const Private_Key& key) const; + + /** + * Inserts "key" for "cert" into the store, returns false if the key is + * already known and true if insertion was successful. + */ + bool insert_key(const X509_Certificate& cert, const Private_Key& key); + + /// Removes "key" from the store. + void remove_key(const Private_Key& key); + + /// Marks "cert" as revoked starting from "time". + void revoke_cert(const X509_Certificate&,CRL_Code,const X509_Time& time = X509_Time()); + + /// Reverses the revokation for "cert". + void affirm_cert(const X509_Certificate&); + + /** + * Generates Certificate Revocation Lists for all certificates marked as revoked. + * A CRL is returned for each unique issuer DN. + */ + std::vector<X509_CRL> generate_crls() const; + + private: + std::string fingerprint_key(const Private_Key&) const; + + std::shared_ptr<SQL_Database> m_database; + std::string m_prefix; + std::string m_password; + std::mutex m_mutex; + }; + +} +#endif diff --git a/src/lib/cert/x509/certstor_sql/info.txt b/src/lib/cert/x509/certstor_sql/info.txt new file mode 100644 index 000000000..cfdd521a2 --- /dev/null +++ b/src/lib/cert/x509/certstor_sql/info.txt @@ -0,0 +1,5 @@ +define CERTSTOR_SQL 20160818 + +<requires> +datastor +</requires> diff --git a/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.cpp b/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.cpp new file mode 100644 index 000000000..89dfb3575 --- /dev/null +++ b/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.cpp @@ -0,0 +1,18 @@ +/* +* Certificate Store in SQL +* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/certstor_sqlite.h> +#include <botan/sqlite3.h> + +namespace Botan { + +Certificate_Store_In_SQLite::Certificate_Store_In_SQLite(const std::string& db_path, + const std::string& passwd, + const std::string& table_prefix) +: Certificate_Store_In_SQL(std::make_shared<Sqlite3_Database>(db_path), passwd, table_prefix) + {} +} diff --git a/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.h b/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.h new file mode 100644 index 000000000..c7d686d89 --- /dev/null +++ b/src/lib/cert/x509/certstor_sqlite3/certstor_sqlite.h @@ -0,0 +1,23 @@ +/* +* Certificate Store in SQL +* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CERT_STORE_SQLITE_H__ +#define BOTAN_CERT_STORE_SQLITE_H__ + +#include <botan/certstor_sql.h> + +namespace Botan { + +class BOTAN_DLL Certificate_Store_In_SQLite : public Certificate_Store_In_SQL + { + public: + Certificate_Store_In_SQLite(const std::string& db_path, + const std::string& passwd, + const std::string& table_prefix = ""); + }; +} +#endif diff --git a/src/lib/cert/x509/certstor_sqlite3/info.txt b/src/lib/cert/x509/certstor_sqlite3/info.txt new file mode 100644 index 000000000..d5e50fc95 --- /dev/null +++ b/src/lib/cert/x509/certstor_sqlite3/info.txt @@ -0,0 +1,6 @@ +define CERTSTOR_SQLITE3 20160818 + +<requires> +certstor_sql +sqlite3 +</requires> diff --git a/src/lib/cert/x509/ocsp.cpp b/src/lib/cert/x509/ocsp.cpp index df8df3b39..761c5b436 100644 --- a/src/lib/cert/x509/ocsp.cpp +++ b/src/lib/cert/x509/ocsp.cpp @@ -92,9 +92,9 @@ void check_signature(const std::vector<byte>& tbs_response, if(!trusted_roots.certificate_known(result.trust_root())) // not needed anymore? throw Exception("Certificate chain roots in unknown/untrusted CA"); - const std::vector<X509_Certificate>& cert_path = result.cert_path(); + const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path = result.cert_path(); - check_signature(tbs_response, sig_algo, signature, cert_path[0]); + check_signature(tbs_response, sig_algo, signature, *cert_path[0]); } } diff --git a/src/lib/cert/x509/x509_crl.cpp b/src/lib/cert/x509/x509_crl.cpp index 64cb1b308..3c75825c1 100644 --- a/src/lib/cert/x509/x509_crl.cpp +++ b/src/lib/cert/x509/x509_crl.cpp @@ -39,6 +39,15 @@ X509_CRL::X509_CRL(const std::vector<byte>& in, bool touc) : do_decode(); } +X509_CRL::X509_CRL(const X509_DN& issuer, const X509_Time& thisUpdate, + const X509_Time& nextUpdate, const std::vector<CRL_Entry>& revoked) : + X509_Object(), m_throw_on_unknown_critical(false), m_revoked(revoked) + { + m_info.add(issuer.contents()); + m_info.add("X509.CRL.start", thisUpdate.to_string()); + m_info.add("X509.CRL.end", nextUpdate.to_string()); + } + /** * Check if this particular certificate is listed in the CRL */ diff --git a/src/lib/cert/x509/x509_crl.h b/src/lib/cert/x509/x509_crl.h index dab4d5153..2e05f98fb 100644 --- a/src/lib/cert/x509/x509_crl.h +++ b/src/lib/cert/x509/x509_crl.h @@ -100,6 +100,9 @@ class BOTAN_DLL X509_CRL final : public X509_Object X509_CRL(const std::vector<byte>& vec, bool throw_on_unknown_critical = false); + X509_CRL(const X509_DN& issuer, const X509_Time& thisUpdate, + const X509_Time& nextUpdate, const std::vector<CRL_Entry>& revoked); + private: void force_decode() override; diff --git a/src/lib/cert/x509/x509_ext.cpp b/src/lib/cert/x509/x509_ext.cpp index 650c20d53..23340f784 100644 --- a/src/lib/cert/x509/x509_ext.cpp +++ b/src/lib/cert/x509/x509_ext.cpp @@ -83,7 +83,7 @@ OID Certificate_Extension::oid_of() const * Validate the extension (the default implementation is a NOP) */ void Certificate_Extension::validate(const X509_Certificate&, const X509_Certificate&, - const std::vector<X509_Certificate>&, + const std::vector<std::shared_ptr<const X509_Certificate>>&, std::vector<std::set<Certificate_Status_Code>>&, size_t) { @@ -525,7 +525,7 @@ void Name_Constraints::contents_to(Data_Store& subject, Data_Store&) const } void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& issuer, - const std::vector<X509_Certificate>& cert_path, + const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, std::vector<std::set<Certificate_Status_Code>>& cert_status, size_t pos) { @@ -547,7 +547,7 @@ void Name_Constraints::validate(const X509_Certificate& subject, const X509_Cert for(auto c: m_name_constraints.permitted()) { - switch(c.base().matches(cert_path.at(j))) + switch(c.base().matches(*cert_path.at(j))) { case GeneralName::MatchResult::NotFound: case GeneralName::MatchResult::All: @@ -564,7 +564,7 @@ void Name_Constraints::validate(const X509_Certificate& subject, const X509_Cert for(auto c: m_name_constraints.excluded()) { - switch(c.base().matches(cert_path.at(j))) + switch(c.base().matches(*cert_path.at(j))) { case GeneralName::MatchResult::All: case GeneralName::MatchResult::Some: diff --git a/src/lib/cert/x509/x509_ext.h b/src/lib/cert/x509/x509_ext.h index 8ea2f2da6..b1984fa94 100644 --- a/src/lib/cert/x509/x509_ext.h +++ b/src/lib/cert/x509/x509_ext.h @@ -68,7 +68,7 @@ class BOTAN_DLL Certificate_Extension * @param pos Position of subject certificate in cert_path */ virtual void validate(const X509_Certificate& subject, const X509_Certificate& issuer, - const std::vector<X509_Certificate>& cert_path, + const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, std::vector<std::set<Certificate_Status_Code>>& cert_status, size_t pos); @@ -305,7 +305,7 @@ class BOTAN_DLL Name_Constraints : public Certificate_Extension Name_Constraints(const NameConstraints &nc) : m_name_constraints(nc) {} void validate(const X509_Certificate& subject, const X509_Certificate& issuer, - const std::vector<X509_Certificate>& cert_path, + const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, std::vector<std::set<Certificate_Status_Code>>& cert_status, size_t pos) override; @@ -479,7 +479,7 @@ class BOTAN_DLL Unknown_Critical_Extension final : public Certificate_Extension { return m_oid; }; void validate(const X509_Certificate&, const X509_Certificate&, - const std::vector<X509_Certificate>&, + const std::vector<std::shared_ptr<const X509_Certificate>>&, std::vector<std::set<Certificate_Status_Code>>& cert_status, size_t pos) override { diff --git a/src/lib/cert/x509/x509path.cpp b/src/lib/cert/x509/x509path.cpp index c08b11d42..65ab3eac1 100644 --- a/src/lib/cert/x509/x509path.cpp +++ b/src/lib/cert/x509/x509path.cpp @@ -20,7 +20,7 @@ namespace Botan { namespace { -const X509_Certificate* +std::shared_ptr<const X509_Certificate> find_issuing_cert(const X509_Certificate& cert, Certificate_Store& end_certs, const std::vector<Certificate_Store*>& certstores) @@ -28,25 +28,25 @@ find_issuing_cert(const X509_Certificate& cert, const X509_DN issuer_dn = cert.issuer_dn(); const std::vector<byte> auth_key_id = cert.authority_key_id(); - const X509_Certificate* c = end_certs.find_cert(issuer_dn, auth_key_id); + std::shared_ptr<const X509_Certificate> c = end_certs.find_cert(issuer_dn, auth_key_id); if(c && *c != cert) return c; for(size_t i = 0; i != certstores.size(); ++i) { - if(const X509_Certificate* c = certstores[i]->find_cert(issuer_dn, auth_key_id)) + if(std::shared_ptr<const X509_Certificate> c = certstores[i]->find_cert(issuer_dn, auth_key_id)) return c; } return nullptr; } -const X509_CRL* find_crls_for(const X509_Certificate& cert, +std::shared_ptr<const X509_CRL> find_crls_for(const X509_Certificate& cert, const std::vector<Certificate_Store*>& certstores) { for(size_t i = 0; i != certstores.size(); ++i) { - if(const X509_CRL* crl = certstores[i]->find_crl_for(cert)) + if(std::shared_ptr<const X509_CRL> crl = certstores[i]->find_crl_for(cert)) return crl; } @@ -72,7 +72,7 @@ const X509_CRL* find_crls_for(const X509_Certificate& cert, } std::vector<std::set<Certificate_Status_Code>> -check_chain(const std::vector<X509_Certificate>& cert_path, +check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, const Path_Validation_Restrictions& restrictions, const std::vector<Certificate_Store*>& certstores) { @@ -92,9 +92,9 @@ check_chain(const std::vector<X509_Certificate>& cert_path, const bool at_self_signed_root = (i == cert_path.size() - 1); - const X509_Certificate& subject = cert_path[i]; + std::shared_ptr<const X509_Certificate> subject = cert_path[i]; - const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)]; + std::shared_ptr<const X509_Certificate> issuer = cert_path[at_self_signed_root ? (i) : (i + 1)]; if(i == 0 || restrictions.ocsp_all_intermediates()) { @@ -102,25 +102,25 @@ check_chain(const std::vector<X509_Certificate>& cert_path, if(certstores.size() > 1) ocsp_responses.push_back( std::async(std::launch::async, - OCSP::online_check, issuer, subject, certstores[0])); + OCSP::online_check, *issuer, *subject, certstores[0])); } // Check all certs for valid time range - if(current_time < X509_Time(subject.start_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME)) + if(current_time < X509_Time(subject->start_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME)) status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID); - if(current_time > X509_Time(subject.end_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME)) + if(current_time > X509_Time(subject->end_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME)) status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED); // Check issuer constraints - if(!issuer.is_CA_cert() && !self_signed_ee_cert) + if(!issuer->is_CA_cert() && !self_signed_ee_cert) status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER); - if(issuer.path_limit() < i) + if(issuer->path_limit() < i) status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG); - std::unique_ptr<Public_Key> issuer_key(issuer.subject_public_key()); + std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key()); if(!issuer_key) { @@ -128,7 +128,7 @@ check_chain(const std::vector<X509_Certificate>& cert_path, } else { - if(subject.check_signature(*issuer_key) == false) + if(subject->check_signature(*issuer_key) == false) status.insert(Certificate_Status_Code::SIGNATURE_ERROR); if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) @@ -138,15 +138,15 @@ check_chain(const std::vector<X509_Certificate>& cert_path, // Allow untrusted hashes on self-signed roots if(!trusted_hashes.empty() && !at_self_signed_root) { - if(!trusted_hashes.count(subject.hash_used_for_signature())) + if(!trusted_hashes.count(subject->hash_used_for_signature())) status.insert(Certificate_Status_Code::UNTRUSTED_HASH); } // Check cert extensions - Extensions extensions = subject.v3_extensions(); + Extensions extensions = subject->v3_extensions(); for(auto& extension : extensions.extensions()) { - extension.first->validate(subject, issuer, cert_path, cert_status, i); + extension.first->validate(*subject, *issuer, cert_path, cert_status, i); } } @@ -154,8 +154,8 @@ check_chain(const std::vector<X509_Certificate>& cert_path, { std::set<Certificate_Status_Code>& status = cert_status.at(i); - const X509_Certificate& subject = cert_path.at(i); - const X509_Certificate& ca = cert_path.at(i+1); + std::shared_ptr<const X509_Certificate> subject = cert_path.at(i); + std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1); if(i < ocsp_responses.size()) { @@ -163,7 +163,7 @@ check_chain(const std::vector<X509_Certificate>& cert_path, { OCSP::Response ocsp = ocsp_responses[i].get(); - auto ocsp_status = ocsp.status_for(ca, subject); + auto ocsp_status = ocsp.status_for(*ca, *subject); status.insert(ocsp_status); @@ -181,7 +181,7 @@ check_chain(const std::vector<X509_Certificate>& cert_path, } } - const X509_CRL* crl_p = find_crls_for(subject, certstores); + std::shared_ptr<const X509_CRL> crl_p = find_crls_for(*subject, certstores); if(!crl_p) { @@ -192,7 +192,7 @@ check_chain(const std::vector<X509_Certificate>& cert_path, const X509_CRL& crl = *crl_p; - if(!ca.allowed_usage(CRL_SIGN)) + if(!ca->allowed_usage(CRL_SIGN)) status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER); if(current_time < X509_Time(crl.this_update())) @@ -201,10 +201,10 @@ check_chain(const std::vector<X509_Certificate>& cert_path, if(current_time > X509_Time(crl.next_update())) status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED); - if(crl.check_signature(ca.subject_public_key()) == false) + if(crl.check_signature(ca->subject_public_key()) == false) status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE); - if(crl.is_revoked(subject)) + if(crl.is_revoked(*subject)) status.insert(Certificate_Status_Code::CERT_IS_REVOKED); } @@ -226,8 +226,12 @@ Path_Validation_Result x509_path_validate( if(end_certs.empty()) throw Invalid_Argument("x509_path_validate called with no subjects"); - std::vector<X509_Certificate> cert_path; - cert_path.push_back(end_certs[0]); + std::vector<std::shared_ptr<const X509_Certificate>> cert_path; + std::vector<std::shared_ptr<const X509_Certificate>> end_certs_sharedptr; + cert_path.push_back(std::make_shared<X509_Certificate>(end_certs[0])); + + for(auto c: end_certs) + end_certs_sharedptr.push_back(std::make_shared<const X509_Certificate>(c)); /* * This is an inelegant but functional way of preventing path loops @@ -236,12 +240,12 @@ Path_Validation_Result x509_path_validate( */ std::set<std::string> certs_seen; - Certificate_Store_Overlay extra(end_certs); + Certificate_Store_Overlay extra(end_certs_sharedptr); // iterate until we reach a root or cannot find the issuer - while(!cert_path.back().is_self_signed()) + while(!cert_path.back()->is_self_signed()) { - const X509_Certificate* cert = find_issuing_cert(cert_path.back(), extra, certstores); + std::shared_ptr<const X509_Certificate> cert = find_issuing_cert(*cert_path.back(), extra, certstores); if(!cert) return Path_Validation_Result(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND); @@ -249,15 +253,15 @@ Path_Validation_Result x509_path_validate( if(certs_seen.count(fprint) > 0) return Path_Validation_Result(Certificate_Status_Code::CERT_CHAIN_LOOP); certs_seen.insert(fprint); - cert_path.push_back(*cert); + cert_path.push_back(cert); } std::vector<std::set<Certificate_Status_Code>> res = check_chain(cert_path, restrictions, certstores); - if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname)) + if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname)) res[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH); - if(!cert_path[0].allowed_usage(usage)) + if(!cert_path[0]->allowed_usage(usage)) res[0].insert(Certificate_Status_Code::INVALID_USAGE); return Path_Validation_Result(res, std::move(cert_path)); @@ -321,7 +325,7 @@ Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev, } Path_Validation_Result::Path_Validation_Result(std::vector<std::set<Certificate_Status_Code>> status, - std::vector<X509_Certificate>&& cert_chain) : + std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain) : m_overall(Certificate_Status_Code::VERIFIED), m_all_status(status), m_cert_path(cert_chain) @@ -346,14 +350,14 @@ const X509_Certificate& Path_Validation_Result::trust_root() const if(result() != Certificate_Status_Code::VERIFIED) throw Exception("Path_Validation_Result::trust_root meaningless with invalid status"); - return m_cert_path[m_cert_path.size()-1]; + return *m_cert_path[m_cert_path.size()-1]; } std::set<std::string> Path_Validation_Result::trusted_hashes() const { std::set<std::string> hashes; for(size_t i = 0; i != m_cert_path.size(); ++i) - hashes.insert(m_cert_path[i].hash_used_for_signature()); + hashes.insert(m_cert_path[i]->hash_used_for_signature()); return hashes; } diff --git a/src/lib/cert/x509/x509path.h b/src/lib/cert/x509/x509path.h index b7061685a..60b7fa1a2 100644 --- a/src/lib/cert/x509/x509path.h +++ b/src/lib/cert/x509/x509path.h @@ -92,7 +92,7 @@ class BOTAN_DLL Path_Validation_Result /** * @return the full path from subject to trust root */ - const std::vector<X509_Certificate>& cert_path() const { return m_cert_path; } + const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path() const { return m_cert_path; } /** * @return true iff the validation was successful @@ -118,7 +118,7 @@ class BOTAN_DLL Path_Validation_Result static const char* status_string(Certificate_Status_Code code); Path_Validation_Result(std::vector<std::set<Certificate_Status_Code>> status, - std::vector<X509_Certificate>&& cert_chain); + std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain); explicit Path_Validation_Result(Certificate_Status_Code status) : m_overall(status) {} @@ -130,7 +130,7 @@ class BOTAN_DLL Path_Validation_Result Certificate_Status_Code m_overall; std::vector<std::set<Certificate_Status_Code>> m_all_status; - std::vector<X509_Certificate> m_cert_path; + std::vector<std::shared_ptr<const X509_Certificate>> m_cert_path; }; diff --git a/src/lib/utils/database.h b/src/lib/utils/database.h index 4cc0989b1..0cd45dac0 100644 --- a/src/lib/utils/database.h +++ b/src/lib/utils/database.h @@ -38,6 +38,8 @@ class BOTAN_DLL SQL_Database virtual void bind(int column, const std::vector<byte>& blob) = 0; + virtual void bind(int column, const byte* data, size_t len) = 0; + /* Get output */ virtual std::pair<const byte*, size_t> get_blob(int column) = 0; diff --git a/src/lib/utils/sqlite3/sqlite3.cpp b/src/lib/utils/sqlite3/sqlite3.cpp index 77b4c0d10..251cbcdf5 100644 --- a/src/lib/utils/sqlite3/sqlite3.cpp +++ b/src/lib/utils/sqlite3/sqlite3.cpp @@ -99,6 +99,13 @@ void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::vector<byt throw SQL_DB_Error("sqlite3_bind_text failed, code " + std::to_string(rc)); } +void Sqlite3_Database::Sqlite3_Statement::bind(int column, const byte* p, size_t len) + { + int rc = ::sqlite3_bind_blob(m_stmt, column, p, len, SQLITE_TRANSIENT); + if(rc != SQLITE_OK) + throw SQL_DB_Error("sqlite3_bind_text failed, code " + std::to_string(rc)); + } + std::pair<const byte*, size_t> Sqlite3_Database::Sqlite3_Statement::get_blob(int column) { BOTAN_ASSERT(::sqlite3_column_type(m_stmt, 0) == SQLITE_BLOB, diff --git a/src/lib/utils/sqlite3/sqlite3.h b/src/lib/utils/sqlite3/sqlite3.h index 067b94e85..659e1c487 100644 --- a/src/lib/utils/sqlite3/sqlite3.h +++ b/src/lib/utils/sqlite3/sqlite3.h @@ -35,6 +35,7 @@ class BOTAN_DLL Sqlite3_Database : public SQL_Database void bind(int column, size_t val) override; void bind(int column, std::chrono::system_clock::time_point time) override; void bind(int column, const std::vector<byte>& val) override; + void bind(int column, const byte* data, size_t len) override; std::pair<const byte*, size_t> get_blob(int column) override; size_t get_size_t(int column) override; |