diff --git a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc index dc3be6abf9..eee6a1c561 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc @@ -1658,7 +1658,12 @@ TEST_F(HAConfigTest, badTrustAnchor) { #ifdef WITH_OPENSSL expected += "No such file or directory"; #else - expected += "I/O error: DataSource: Failure opening file /this-file-does-not-exist"; + expected += "I/O error: DataSource: Failure opening file "; +#if BOTAN_VERSION_MAJOR > 2 + expected += "'/this-file-does-not-exist'"; +#else + expected += "/this-file-does-not-exist"; +#endif #endif testInvalidConfig(patched, expected); } @@ -1698,7 +1703,12 @@ TEST_F(HAConfigTest, badCertFile) { #ifdef WITH_OPENSSL expected += "No such file or directory"; #else - expected += "I/O error: DataSource: Failure opening file /this-file-does-not-exist"; + expected += "I/O error: DataSource: Failure opening file "; +#if BOTAN_VERSION_MAJOR > 2 + expected += "'/this-file-does-not-exist'"; +#else + expected += "/this-file-does-not-exist"; +#endif #endif testInvalidConfig(patched, expected); } @@ -1738,7 +1748,12 @@ TEST_F(HAConfigTest, badKeyFile) { #ifdef WITH_OPENSSL expected += "No such file or directory"; #else - expected += "I/O error: DataSource: Failure opening file /this-file-does-not-exist"; + expected += "I/O error: DataSource: Failure opening file "; +#if BOTAN_VERSION_MAJOR > 2 + expected += "'/this-file-does-not-exist'"; +#else + expected += "/this-file-does-not-exist"; +#endif #endif testInvalidConfig(patched, expected); } diff --git a/src/lib/asiolink/botan_tls.cc b/src/lib/asiolink/botan_tls.cc index 0ec0ed5fdb..18acde5ea8 100644 --- a/src/lib/asiolink/botan_tls.cc +++ b/src/lib/asiolink/botan_tls.cc @@ -18,6 +18,9 @@ #include #include #include +#if BOTAN_VERSION_MAJOR > 2 +#include +#endif using namespace isc::cryptolink; @@ -36,8 +39,7 @@ public: } // Destructor. - virtual ~KeaCredentialsManager() { - } + virtual ~KeaCredentialsManager() = default; // CA certificate stores. // nullptr means do not require or check peer certificate. @@ -54,17 +56,28 @@ public: // Certificate chain. std::vector cert_chain(const std::vector&, +#if BOTAN_VERSION_MAJOR > 2 + const std::vector&, +#endif const std::string&, const std::string&) override { return (certs_); } // Private key. +#if BOTAN_VERSION_MAJOR > 2 + std::shared_ptr +#else Botan::Private_Key* +#endif private_key_for(const Botan::X509_Certificate&, const std::string&, const std::string&) override { +#if BOTAN_VERSION_MAJOR > 2 + return (key_); +#else return (key_.get()); +#endif } // Set the store from a path. @@ -118,13 +131,27 @@ public: // Set the private key. void setPrivateKey(const std::string& file, +#if BOTAN_VERSION_MAJOR > 2 + Botan::RandomNumberGenerator&, +#else Botan::RandomNumberGenerator& rng, +#endif bool& is_rsa) { - key_.reset(Botan::PKCS8::load_key(file, rng)); - if (!key_) { +#if BOTAN_VERSION_MAJOR > 2 + Botan::DataSource_Stream source(file); + auto priv_key = Botan::PKCS8::load_key(source); +#else + auto priv_key = Botan::PKCS8::load_key(file, rng); +#endif + if (!priv_key) { isc_throw(Unexpected, "Botan::PKCS8::load_key failed but not threw?"); } +#if BOTAN_VERSION_MAJOR > 2 + key_ = std::move(priv_key); +#else + key_.reset(priv_key); +#endif is_rsa = (key_->algo_name() == "RSA"); } @@ -138,7 +165,7 @@ public: std::vector certs_; // Pointer to the private key. - std::unique_ptr key_; + std::shared_ptr key_; }; // Class of Kea policy. @@ -197,29 +224,32 @@ KeaPolicy::AllowedSignatureMethodsECDSA = { "ECDSA", "RSA", "DSA" }; class TlsContextImpl { public: // Constructor. - TlsContextImpl() : cred_mgr_(), rng_(), sess_mgr_(), policy_() { + TlsContextImpl() : + cred_mgr_(new KeaCredentialsManager()), + rng_(new Botan::AutoSeeded_RNG()), + sess_mgr_(new KeaSessionManager()), + policy_(new KeaPolicy()) { } // Destructor. - virtual ~TlsContextImpl() { - } + virtual ~TlsContextImpl() = default; // Get the peer certificate requirement mode. virtual bool getCertRequired() const { - return (cred_mgr_.getUseStores()); + return (cred_mgr_->getUseStores()); } // Set the peer certificate requirement mode. // // With Botan this means to provide or not the CA certificate stores. virtual void setCertRequired(bool cert_required) { - cred_mgr_.setUseStores(cert_required); + cred_mgr_->setUseStores(cert_required); } // Load the trust anchor aka certificate authority (path). virtual void loadCaPath(const std::string& ca_path) { try { - cred_mgr_.setStorePath(ca_path); + cred_mgr_->setStorePath(ca_path); } catch (const std::exception& ex) { isc_throw(LibraryError, ex.what()); } @@ -228,7 +258,7 @@ public: // Load the trust anchor aka certificate authority (file). virtual void loadCaFile(const std::string& ca_file) { try { - cred_mgr_.setStoreFile(ca_file); + cred_mgr_->setStoreFile(ca_file); } catch (const std::exception& ex) { isc_throw(LibraryError, ex.what()); } @@ -237,7 +267,7 @@ public: /// @brief Load the certificate file. virtual void loadCertFile(const std::string& cert_file) { try { - cred_mgr_.setCertChain(cert_file); + cred_mgr_->setCertChain(cert_file); } catch (const std::exception& ex) { isc_throw(LibraryError, ex.what()); } @@ -249,8 +279,8 @@ public: virtual void loadKeyFile(const std::string& key_file) { try { bool is_rsa = true; - cred_mgr_.setPrivateKey(key_file, rng_, is_rsa); - policy_.setPrefRSA(is_rsa); + cred_mgr_->setPrivateKey(key_file, *rng_, is_rsa); + policy_->setPrefRSA(is_rsa); } catch (const std::exception& ex) { isc_throw(LibraryError, ex.what()); } @@ -261,28 +291,41 @@ public: if (context_) { return; } +#if BOTAN_VERSION_MAJOR > 2 context_.reset(new Botan::TLS::Context(cred_mgr_, rng_, sess_mgr_, policy_)); +#else + context_.reset(new Botan::TLS::Context(*cred_mgr_, + *rng_, + *sess_mgr_, + *policy_)); +#endif } +#if BOTAN_VERSION_MAJOR > 2 + virtual std::shared_ptr get() { + return (context_); + } +#else virtual Botan::TLS::Context& get() { return (*context_); } +#endif // Credentials Manager. - KeaCredentialsManager cred_mgr_; + std::shared_ptr cred_mgr_; // Random Number Generator. - Botan::AutoSeeded_RNG rng_; + std::shared_ptr rng_; // Session Manager. - KeaSessionManager sess_mgr_; + std::shared_ptr sess_mgr_; - KeaPolicy policy_; + std::shared_ptr policy_; - std::unique_ptr context_; + std::shared_ptr context_; }; TlsContext::~TlsContext() { @@ -292,11 +335,19 @@ TlsContext::TlsContext(TlsRole role) : TlsContextBase(role), impl_(new TlsContextImpl()) { } +#if BOTAN_VERSION_MAJOR > 2 +std::shared_ptr +TlsContext::getContext() { + impl_->build(); + return (impl_->get()); +} +#else Botan::TLS::Context& TlsContext::getContext() { impl_->build(); return (impl_->get()); } +#endif void TlsContext::setCertRequired(bool cert_required) { diff --git a/src/lib/asiolink/botan_tls.h b/src/lib/asiolink/botan_tls.h index 8dc9e40b37..13afc98d9c 100644 --- a/src/lib/asiolink/botan_tls.h +++ b/src/lib/asiolink/botan_tls.h @@ -28,9 +28,17 @@ namespace asiolink { /// @brief Translate TLS role into implementation. inline Botan::TLS::Connection_Side roleToImpl(TlsRole role) { if (role == TlsRole::SERVER) { +#if BOTAN_VERSION_MAJOR > 2 + return (Botan::TLS::Connection_Side::Server); +#else return (Botan::TLS::Connection_Side::SERVER); +#endif } else { +#if BOTAN_VERSION_MAJOR > 2 + return (Botan::TLS::Connection_Side::Client); +#else return (Botan::TLS::Connection_Side::CLIENT); +#endif } } @@ -53,7 +61,11 @@ public: explicit TlsContext(TlsRole role); /// @brief Return the underlying context. +#if BOTAN_VERSION_MAJOR > 2 + std::shared_ptr getContext(); +#else Botan::TLS::Context& getContext(); +#endif /// @brief Get the peer certificate requirement mode. /// diff --git a/src/lib/asiolink/tests/tls_unittest.cc b/src/lib/asiolink/tests/tls_unittest.cc index 39aa8ade50..c6150d60fd 100644 --- a/src/lib/asiolink/tests/tls_unittest.cc +++ b/src/lib/asiolink/tests/tls_unittest.cc @@ -509,6 +509,7 @@ TEST_F(TLSTest, loadNoCAFile) { Expecteds exps; // Botan error. exps.addThrow("I/O error: DataSource: Failure opening file /no-such-file"); + exps.addThrow("I/O error: DataSource: Failure opening file '/no-such-file'"); // OpenSSL errors. exps.addThrow("No such file or directory"); exps.addThrow("No such file or directory (system library)"); @@ -586,6 +587,7 @@ TEST_F(TLSTest, loadNoCertFile) { Expecteds exps; // Botan error. exps.addThrow("I/O error: DataSource: Failure opening file /no-such-file"); + exps.addThrow("I/O error: DataSource: Failure opening file '/no-such-file'"); // OpenSSL errors. exps.addThrow("No such file or directory"); exps.addThrow("No such file or directory (system library)"); @@ -632,6 +634,7 @@ TEST_F(TLSTest, loadNoKeyFile) { Expecteds exps; // Botan error. exps.addThrow("I/O error: DataSource: Failure opening file /no-such-file"); + exps.addThrow("I/O error: DataSource: Failure opening file '/no-such-file'"); // OpenSSL errors. exps.addThrow("No such file or directory"); exps.addThrow("No such file or directory (system library)"); @@ -655,6 +658,9 @@ TEST_F(TLSTest, loadCertKeyFile) { string botan_error = "PKCS #8 private key decoding failed with PKCS #8: "; botan_error += "Unknown PEM label CERTIFICATE"; exps.addThrow(botan_error); + botan_error = "PKCS #8 private key decoding failed with PKCS #8: "; + botan_error += "Unknown PEM label 'CERTIFICATE'"; + exps.addThrow(botan_error); // OpenSSL errors. exps.addThrow("no start line"); exps.addThrow("no start line (PEM routines)"); @@ -733,6 +739,8 @@ TEST_F(TLSTest, configureError) { // Botan error. string botan_error = "I/O error: DataSource: Failure opening file /no-such-file"; exps.addThrow(common_error + botan_error); + botan_error = "I/O error: DataSource: Failure opening file '/no-such-file'"; + exps.addThrow(common_error + botan_error); // OpenSSL errors. string openssl_error = "No such file or directory"; exps.addThrow(common_error + openssl_error); @@ -946,6 +954,7 @@ TEST_F(TLSTest, serverNotConfigured) { exps.clear(); // On Botan and some OpenSSL the client hangs. exps.addTimeout(); + exps.addError("handshake_failure"); // OpenSSL errors. exps.addError("sslv3 alert handshake failure"); exps.addError("sslv3 alert handshake failure (SSL routines)"); @@ -1027,6 +1036,7 @@ TEST_F(TLSTest, clientNotConfigured) { Expecteds exps; // On Botan and some OpenSSL the server hangs. exps.addTimeout(); + exps.addError("bad_certificate"); // OpenSSL errors. exps.addError("tlsv1 alert unknown ca"); exps.addError("tlsv1 alert unknown ca (SSL routines)"); @@ -1128,6 +1138,7 @@ TEST_F(TLSTest, clientHTTPnoS) { exps.addTimeout(); // Botan error. exps.addError("protocol_version"); + exps.addError("unexpected_message"); // Old LibreSSL error. exps.addError("tlsv1 alert protocol version"); // OpenSSL errors (OpenSSL recognizes HTTP). @@ -1220,6 +1231,7 @@ TEST_F(TLSTest, unknownClient) { // Botan errors. exps.addError("record_overflow"); exps.addError("protocol_version"); + exps.addError("unexpected_message"); // Old LibreSSL error. exps.addError("tlsv1 alert protocol version"); // Old OpenSSL error. @@ -1630,6 +1642,7 @@ TEST_F(TLSTest, serverNotConfiguredCloseonError) { exps.clear(); // Botan and some OpenSSL. exps.addError("stream truncated"); + exps.addError("handshake_failure"); // Alias on old OpenSSL. exps.addError("short read"); // OpenSSL errors. @@ -1710,6 +1723,7 @@ TEST_F(TLSTest, clientNotConfiguredCloseonError) { Expecteds exps; // Botan and some OpenSSL. exps.addError("stream truncated"); + exps.addError("bad_certificate"); // Alias on old OpenSSL. exps.addError("short read"); // OpenSSL errors. @@ -1810,6 +1824,7 @@ TEST_F(TLSTest, clientHTTPnoSCloseonError) { exps.addTimeout(); // Botan behavior was reported and fixed. exps.addError("protocol_version"); + exps.addError("unexpected_message"); // Old LibreSSL error. exps.addError("tlsv1 alert protocol version"); // OpenSSL errors when OpenSSL recognizes HTTP. diff --git a/src/lib/asiolink/testutils/botan_sample_client.cc b/src/lib/asiolink/testutils/botan_sample_client.cc index 5930186cb7..20527355e8 100644 --- a/src/lib/asiolink/testutils/botan_sample_client.cc +++ b/src/lib/asiolink/testutils/botan_sample_client.cc @@ -21,6 +21,9 @@ #include #include #include +#if BOTAN_VERSION_MAJOR > 2 +#include +#endif inline std::string CA_(const std::string& filename) { return (std::string(TEST_CA_DIR) + "/" + filename); @@ -35,19 +38,29 @@ using Client_Certificate_Store = Botan::Flatfile_Certificate_Store; class Client_Credentials_Manager : public Botan::Credentials_Manager { public: +#if BOTAN_VERSION_MAJOR > 2 + explicit Client_Credentials_Manager() +#else explicit Client_Credentials_Manager(Botan::RandomNumberGenerator& rng) +#endif : stores_(), certs_(), store_(new Client_Certificate_Store(CA_("kea-ca.crt"))), cert_(Botan::X509_Certificate(CA_("kea-client.crt"))), - key_(Botan::PKCS8::load_key(CA_("kea-client.key"), rng)) + key_() { +#if BOTAN_VERSION_MAJOR > 2 + Botan::DataSource_Stream source(CA_("kea-client.key")); + auto priv_key = Botan::PKCS8::load_key(source); + key_ = std::move(priv_key); +#else + auto priv_key = Botan::PKCS8::load_key(CA_("kea-client.key"), rng); + key_.reset(priv_key); +#endif stores_.push_back(store_.get()); certs_.push_back(cert_); } - virtual ~Client_Credentials_Manager() - { - } + virtual ~Client_Credentials_Manager() = default; std::vector trusted_certificate_authorities(const std::string&, @@ -58,25 +71,36 @@ public: std::vector cert_chain(const std::vector&, +#if BOTAN_VERSION_MAJOR > 2 + const std::vector&, +#endif const std::string&, const std::string&) override { return certs_; } - Botan::Private_Key* +#if BOTAN_VERSION_MAJOR > 2 + std::shared_ptr +#else + Botan::Private_Key* +#endif private_key_for(const Botan::X509_Certificate&, const std::string&, const std::string&) override { - return key_.get(); +#if BOTAN_VERSION_MAJOR > 2 + return (key_); +#else + return (key_.get()); +#endif } std::vector stores_; std::vector certs_; std::shared_ptr store_; Botan::X509_Certificate cert_; - std::unique_ptr key_; + std::shared_ptr key_; }; using Client_Session_Manager = Botan::TLS::Session_Manager_Noop; @@ -101,8 +125,12 @@ public: class client { public: - client(boost::asio::io_context& io_context, + client(boost::asio::io_service& io_context, +#if BOTAN_VERSION_MAJOR > 2 + std::shared_ptr context, +#else Botan::TLS::Context& context, +#endif const tcp::endpoint& endpoint) : socket_(io_context, context) { @@ -128,7 +156,11 @@ private: void handshake() { +#if BOTAN_VERSION_MAJOR > 2 + socket_.async_handshake(Botan::TLS::Connection_Side::Client, +#else socket_.async_handshake(Botan::TLS::Connection_Side::CLIENT, +#endif [this](const boost::system::error_code& error) { if (!error) @@ -210,11 +242,24 @@ int main(int argc, char* argv[]) using namespace std; // For atoi. tcp::endpoint endpoint( boost::asio::ip::make_address(argv[1]), atoi(argv[2])); +#if BOTAN_VERSION_MAJOR > 2 + std::shared_ptr + rng(new Botan::AutoSeeded_RNG()); + std::shared_ptr + creds_mgr(new Client_Credentials_Manager()); + std::shared_ptr + sess_mgr(new Client_Session_Manager()); + std::shared_ptr + policy(new Client_Policy()); + std::shared_ptr + ctx(new Botan::TLS::Context(creds_mgr, rng, sess_mgr, policy)); +#else Botan::AutoSeeded_RNG rng; Client_Credentials_Manager creds_mgr(rng); Client_Session_Manager sess_mgr; Client_Policy policy; Botan::TLS::Context ctx(creds_mgr, rng, sess_mgr, policy); +#endif client c(io_context, ctx, endpoint); diff --git a/src/lib/asiolink/testutils/botan_sample_server.cc b/src/lib/asiolink/testutils/botan_sample_server.cc index b1373b34bd..78860c08b7 100644 --- a/src/lib/asiolink/testutils/botan_sample_server.cc +++ b/src/lib/asiolink/testutils/botan_sample_server.cc @@ -20,6 +20,9 @@ #include #include #include +#if BOTAN_VERSION_MAJOR > 2 +#include +#endif inline std::string CA_(const std::string& filename) { return (std::string(TEST_CA_DIR) + "/" + filename); @@ -32,19 +35,29 @@ using Server_Certificate_Store = Botan::Flatfile_Certificate_Store; class Server_Credentials_Manager : public Botan::Credentials_Manager { public: +#if BOTAN_VERSION_MAJOR > 2 + explicit Server_Credentials_Manager() +#else explicit Server_Credentials_Manager(Botan::RandomNumberGenerator& rng) +#endif : stores_(), certs_(), store_(new Server_Certificate_Store(CA_("kea-ca.crt"))), cert_(Botan::X509_Certificate(CA_("kea-server.crt"))), - key_(Botan::PKCS8::load_key(CA_("kea-server.key"), rng)) + key_() { +#if BOTAN_VERSION_MAJOR > 2 + Botan::DataSource_Stream source(CA_("kea-server.key")); + auto priv_key = Botan::PKCS8::load_key(source); + key_ = std::move(priv_key); +#else + auto priv_key = Botan::PKCS8::load_key(CA_("kea-server.key"), rng); + key_.reset(priv_key); +#endif stores_.push_back(store_.get()); certs_.push_back(cert_); } - virtual ~Server_Credentials_Manager() - { - } + virtual ~Server_Credentials_Manager() = default; std::vector trusted_certificate_authorities(const std::string&, @@ -55,25 +68,36 @@ public: std::vector cert_chain(const std::vector&, +#if BOTAN_VERSION_MAJOR > 2 + const std::vector&, +#endif const std::string&, const std::string&) override { return certs_; } - Botan::Private_Key* +#if BOTAN_VERSION_MAJOR > 2 + std::shared_ptr +#else + Botan::Private_Key* +#endif private_key_for(const Botan::X509_Certificate&, const std::string&, const std::string&) override { - return key_.get(); +#if BOTAN_VERSION_MAJOR > 2 + return (key_); +#else + return (key_.get()); +#endif } std::vector stores_; std::vector certs_; std::shared_ptr store_; Botan::X509_Certificate cert_; - std::unique_ptr key_; + std::shared_ptr key_; }; using Server_Session_Manager = Botan::TLS::Session_Manager_Noop; @@ -98,7 +122,11 @@ public: class session : public std::enable_shared_from_this { public: +#if BOTAN_VERSION_MAJOR > 2 + session(tcp::socket socket, std::shared_ptr ctx) +#else session(tcp::socket socket, Botan::TLS::Context& ctx) +#endif : socket_(std::move(socket), ctx) { } @@ -112,7 +140,11 @@ private: void do_handshake() { auto self(shared_from_this()); +#if BOTAN_VERSION_MAJOR > 2 + socket_.async_handshake(Botan::TLS::Connection_Side::Server, +#else socket_.async_handshake(Botan::TLS::Connection_Side::SERVER, +#endif [this, self](const boost::system::error_code& error) { if (!error) @@ -162,12 +194,24 @@ class server public: server(boost::asio::io_context& io_context, unsigned short port, +#if BOTAN_VERSION_MAJOR > 2 + std::shared_ptr creds_mgr, + std::shared_ptr rng, + std::shared_ptr sess_mgr, + std::shared_ptr policy +#else Botan::Credentials_Manager& creds_mgr, Botan::RandomNumberGenerator& rng, Botan::TLS::Session_Manager& sess_mgr, - Botan::TLS::Policy& policy) + Botan::TLS::Policy& policy +#endif + ) : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), +#if BOTAN_VERSION_MAJOR > 2 + context_(new Botan::TLS::Context(creds_mgr, rng, sess_mgr, policy)) +#else context_(creds_mgr, rng, sess_mgr, policy) +#endif { do_accept(); } @@ -188,7 +232,11 @@ private: } tcp::acceptor acceptor_; +#if BOTAN_VERSION_MAJOR > 2 + std::shared_ptr context_; +#else Botan::TLS::Context context_; +#endif }; int main(int argc, char* argv[]) @@ -203,10 +251,21 @@ int main(int argc, char* argv[]) boost::asio::io_context io_context; +#if BOTAN_VERSION_MAJOR > 2 + std::shared_ptr + rng(new Botan::AutoSeeded_RNG()); + std::shared_ptr + creds_mgr(new Server_Credentials_Manager()); + std::shared_ptr + sess_mgr(new Server_Session_Manager()); + std::shared_ptr + policy(new Server_Policy()); +#else Botan::AutoSeeded_RNG rng; Server_Credentials_Manager creds_mgr(rng); Server_Session_Manager sess_mgr; Server_Policy policy; +#endif server s(io_context, std::atoi(argv[1]), creds_mgr, rng, sess_mgr, policy); io_context.run();