From d0905f8facef2aa3dbaf40715d4375d5a99c9fc4 Mon Sep 17 00:00:00 2001
From: Nicolas Werner <nicolas.werner@hotmail.de>
Date: Sat, 6 Mar 2021 19:35:56 +0100
Subject: [PATCH] Strict hostname checking and require tls1.2+

---
 include/mtxclient/http/client.hpp |   2 +
 lib/http/client.cpp               |  18 ++++-
 tests/client_api.cpp              | 106 +++++++++++++++---------------
 tests/connection.cpp              |  14 ++--
 tests/e2ee.cpp                    |  38 +++++------
 tests/media_api.cpp               |  10 +--
 tests/pushrules.cpp               |  14 ++--
 tests/test_helpers.hpp            |   9 +++
 8 files changed, 119 insertions(+), 92 deletions(-)

diff --git a/include/mtxclient/http/client.hpp b/include/mtxclient/http/client.hpp
index 1593f873e..745f33e9c 100644
--- a/include/mtxclient/http/client.hpp
+++ b/include/mtxclient/http/client.hpp
@@ -172,6 +172,8 @@ public:
 
         //! Wait for the client to close.
         void close(bool force = false);
+        //! Enable or disable certificate verification. On by default
+        void verify_certificates(bool enabled = true);
         //! Set the homeserver domain name.
         void set_user(const mtx::identifiers::User &user) { user_id_ = user; }
         //! Set the device ID.
diff --git a/lib/http/client.cpp b/lib/http/client.cpp
index d477526b9..8595e53e0 100644
--- a/lib/http/client.cpp
+++ b/lib/http/client.cpp
@@ -34,7 +34,7 @@ struct ClientPrivate
         //! Worker threads for the requests.
         boost::thread_group thread_group_;
         //! SSL context for requests.
-        boost::asio::ssl::context ssl_ctx_{boost::asio::ssl::context::sslv23_client};
+        boost::asio::ssl::context ssl_ctx_{boost::asio::ssl::context::tls_client};
         //! All the active sessions will shutdown the connection.
         boost::signals2::signal<void()> shutdown_signal;
 };
@@ -48,6 +48,13 @@ Client::Client(const std::string &server, uint16_t port)
         using namespace boost::asio;
         const auto threads_num = std::min(8U, std::max(1U, std::thread::hardware_concurrency()));
 
+        using boost::asio::ssl::context;
+        p->ssl_ctx_.set_options(context::default_workarounds | context::no_sslv2 |
+                                context::no_sslv3 | context::no_tlsv1 | context::no_tlsv1_1);
+        p->ssl_ctx_.set_default_verify_paths();
+        verify_certificates(true);
+        p->ssl_ctx_.set_verify_callback(ssl::rfc2818_verification(server));
+
         for (unsigned int i = 0; i < threads_num; ++i)
                 p->thread_group_.add_thread(new boost::thread([this]() { p->ios_.run(); }));
 }
@@ -195,6 +202,13 @@ mtx::http::Client::get(const std::string &endpoint,
         session->run();
 }
 
+void
+Client::verify_certificates(bool enabled)
+{
+        using namespace boost::asio;
+        p->ssl_ctx_.set_verify_mode(enabled ? ssl::verify_peer : ssl::verify_none);
+}
+
 void
 Client::set_server(const std::string &server)
 {
@@ -223,6 +237,8 @@ Client::set_server(const std::string &server)
                 server_ = server_name;
                 port_   = port;
         }
+        p->ssl_ctx_.set_verify_callback(
+          boost::asio::ssl::rfc2818_verification(std::string(server_)));
 }
 
 void
diff --git a/tests/client_api.cpp b/tests/client_api.cpp
index 4ef53dd47..9332f5d82 100644
--- a/tests/client_api.cpp
+++ b/tests/client_api.cpp
@@ -25,7 +25,7 @@ using namespace std;
 
 TEST(ClientAPI, Register)
 {
-        auto user = std::make_shared<Client>("localhost");
+        auto user = make_test_client();
 
         user->registration("alice", "secret", [](const mtx::responses::Register &, RequestErr err) {
                 ASSERT_TRUE(err);
@@ -62,7 +62,7 @@ TEST(ClientAPI, Register)
 
 TEST(ClientAPI, LoginSuccess)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
 
         mtx_client->login("alice", "secret", [](const mtx::responses::Login &res, RequestErr err) {
                 check_error(err);
@@ -84,7 +84,7 @@ TEST(ClientAPI, LoginSuccess)
 
 TEST(ClientAPI, LoginWrongPassword)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
 
         mtx_client->login(
           "alice", "wrong_password", [](const mtx::responses::Login &res, RequestErr err) {
@@ -102,7 +102,7 @@ TEST(ClientAPI, LoginWrongPassword)
 
 TEST(ClientAPI, LoginWrongUsername)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
 
         mtx_client->login("john", "secret", [](const mtx::responses::Login &res, RequestErr err) {
                 ASSERT_TRUE(err);
@@ -119,7 +119,7 @@ TEST(ClientAPI, LoginWrongUsername)
 
 TEST(ClientAPI, LoginFlows)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
 
         mtx_client->get_login([](const mtx::responses::LoginFlows &res, RequestErr err) {
                 ASSERT_FALSE(err);
@@ -132,7 +132,7 @@ TEST(ClientAPI, LoginFlows)
 
 TEST(ClientAPI, SSORedirect)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost", 443);
         EXPECT_EQ(mtx_client->login_sso_redirect("http://aaa:555/sso"),
                   "https://localhost:443/_matrix/client/r0/login/sso/"
                   "redirect?redirectUrl=http%3A%2F%2Faaa%3A555%2Fsso");
@@ -141,7 +141,7 @@ TEST(ClientAPI, SSORedirect)
 
 TEST(ClientAPI, EmptyUserAvatar)
 {
-        auto alice = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &res, RequestErr err) {
                 ASSERT_FALSE(err);
@@ -179,7 +179,7 @@ TEST(ClientAPI, EmptyUserAvatar)
 
 TEST(ClientAPI, RealUserAvatar)
 {
-        auto alice = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &res, RequestErr err) {
                 ASSERT_FALSE(err);
@@ -219,7 +219,7 @@ TEST(ClientAPI, RealUserAvatar)
 
 TEST(ClientAPI, ChangeDisplayName)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
 
         mtx_client->login(
           "alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
@@ -236,7 +236,7 @@ TEST(ClientAPI, ChangeDisplayName)
 
 TEST(ClientAPI, EmptyDisplayName)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
 
         mtx_client->login(
           "alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
@@ -252,7 +252,7 @@ TEST(ClientAPI, EmptyDisplayName)
 
 TEST(ClientAPI, CreateRoom)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
 
         mtx_client->login(
           "alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
@@ -276,7 +276,7 @@ TEST(ClientAPI, CreateRoom)
 
 TEST(ClientAPI, TagRoom)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
 
         mtx_client->login(
           "alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
@@ -330,7 +330,7 @@ TEST(ClientAPI, TagRoom)
 
 TEST(ClientAPI, LogoutSuccess)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
         std::string token;
 
         // Login and prove that login was successful by creating a room
@@ -373,7 +373,7 @@ TEST(ClientAPI, LogoutSuccess)
 
 TEST(ClientAPI, LogoutInvalidatesTokenOnServer)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
         std::string token;
 
         // Login and prove that login was successful by creating a room
@@ -418,9 +418,9 @@ TEST(ClientAPI, LogoutInvalidatesTokenOnServer)
 
 TEST(ClientAPI, CreateRoomInvites)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
-        auto carl  = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
+        auto carl  = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -462,8 +462,8 @@ TEST(ClientAPI, CreateRoomInvites)
 
 TEST(ClientAPI, JoinRoom)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -515,8 +515,8 @@ TEST(ClientAPI, JoinRoom)
 
 TEST(ClientAPI, LeaveRoom)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -561,8 +561,8 @@ TEST(ClientAPI, LeaveRoom)
 
 TEST(ClientAPI, InviteRoom)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -603,8 +603,8 @@ TEST(ClientAPI, InviteRoom)
 
 TEST(ClientAPI, KickRoom)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -651,8 +651,8 @@ TEST(ClientAPI, KickRoom)
 
 TEST(ClientAPI, BanRoom)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -709,8 +709,8 @@ TEST(ClientAPI, BanRoom)
 
 TEST(ClientAPI, InvalidInvite)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -748,7 +748,7 @@ TEST(ClientAPI, InvalidInvite)
 
 TEST(ClientAPI, Sync)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
 
         mtx_client->login(
           "alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
@@ -779,7 +779,7 @@ TEST(ClientAPI, Sync)
 
 TEST(ClientAPI, Versions)
 {
-        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> mtx_client = make_test_client();
 
         mtx_client->versions([](const mtx::responses::Versions &res, RequestErr err) {
                 check_error(err);
@@ -799,7 +799,7 @@ TEST(ClientAPI, Versions)
 
 TEST(ClientAPI, Typing)
 {
-        auto alice = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
 
         alice->login("alice", "secret", [](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -869,7 +869,7 @@ TEST(ClientAPI, Typing)
 
 TEST(ClientAPI, Presence)
 {
-        auto alice = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
 
         alice->login("alice", "secret", [](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -913,8 +913,8 @@ TEST(ClientAPI, Presence)
 
 TEST(ClientAPI, PresenceOverSync)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
 
         alice->login("alice", "secret", [](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -983,8 +983,8 @@ TEST(ClientAPI, PresenceOverSync)
 
 TEST(ClientAPI, SendMessages)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -1064,7 +1064,7 @@ TEST(ClientAPI, SendMessages)
 
 TEST(ClientAPI, RedactEvent)
 {
-        auto alice = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
         alice->login("alice", "secret", check_login);
 
         while (alice->access_token().empty())
@@ -1099,8 +1099,8 @@ TEST(ClientAPI, RedactEvent)
 
 TEST(ClientAPI, SendStateEvents)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -1181,7 +1181,7 @@ TEST(ClientAPI, SendStateEvents)
 
 TEST(ClientAPI, Pagination)
 {
-        auto alice = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -1224,7 +1224,7 @@ TEST(ClientAPI, Pagination)
 
 TEST(ClientAPI, UploadFilter)
 {
-        auto alice = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -1248,7 +1248,7 @@ TEST(ClientAPI, UploadFilter)
 
 TEST(ClientAPI, ReadMarkers)
 {
-        auto alice = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -1308,8 +1308,8 @@ TEST(ClientAPI, ReadMarkers)
 
 TEST(ClientAPI, SendToDevice)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
 
         alice->login("alice", "secret", &check_login);
         bob->login("bob", "secret", &check_login);
@@ -1361,9 +1361,9 @@ TEST(ClientAPI, SendToDevice)
 
 TEST(ClientAPI, NewSendToDevice)
 {
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
-        auto carl  = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
+        auto carl  = make_test_client();
 
         alice->login("alice", "secret", &check_login);
         bob->login("bob", "secret", &check_login);
@@ -1420,7 +1420,7 @@ TEST(ClientAPI, NewSendToDevice)
 
 TEST(ClientAPI, RetrieveSingleEvent)
 {
-        auto bob = std::make_shared<Client>("localhost");
+        auto bob = make_test_client();
         bob->login("bob", "secret", check_login);
 
         while (bob->access_token().empty())
@@ -1472,7 +1472,7 @@ TEST(ClientAPI, RetrieveSingleEvent)
 
 TEST(Groups, Rooms)
 {
-        auto alice = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
         alice->login("alice", "secret", check_login);
 
         WAIT_UNTIL(!alice->access_token().empty())
@@ -1542,7 +1542,7 @@ TEST(Groups, Rooms)
 
 TEST(Groups, Profiles)
 {
-        auto alice = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
         alice->login("alice", "secret", check_login);
 
         WAIT_UNTIL(!alice->access_token().empty())
@@ -1579,8 +1579,8 @@ TEST(Groups, Profiles)
 TEST(ClientAPI, PublicRooms)
 {
         // Setup : Create a new (public) room with some settings
-        auto alice = std::make_shared<Client>("localhost");
-        auto bob   = std::make_shared<Client>("localhost");
+        auto alice = make_test_client();
+        auto bob   = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
diff --git a/tests/connection.cpp b/tests/connection.cpp
index 0445dfd3c..6a78e0e83 100644
--- a/tests/connection.cpp
+++ b/tests/connection.cpp
@@ -14,20 +14,17 @@ using namespace std;
 
 TEST(Basic, Connection)
 {
-        auto alice = std::make_shared<Client>("localhost", 8448);
-        auto bob   = std::make_shared<Client>("localhost", 443);
+        auto client = make_test_client();
 
-        alice->versions(
+        client->versions(
           [](const mtx::responses::Versions &, RequestErr err) { ASSERT_FALSE(err); });
-        bob->versions([](const mtx::responses::Versions &, RequestErr err) { ASSERT_FALSE(err); });
-
-        bob->close();
-        alice->close();
+        client->close();
 }
 
 TEST(Basic, ServerWithPort)
 {
         auto alice = std::make_shared<Client>("matrix.org");
+        alice->verify_certificates(false);
         alice->set_server("localhost:8448");
 
         EXPECT_EQ(alice->server(), "localhost");
@@ -41,13 +38,14 @@ TEST(Basic, ServerWithPort)
 TEST(Basic, Failure)
 {
         auto alice = std::make_shared<Client>("not-resolvable-example-domain.wrong");
+        alice->verify_certificates(false);
         alice->versions([](const mtx::responses::Versions &, RequestErr err) { ASSERT_TRUE(err); });
         alice->close();
 }
 
 TEST(Basic, Shutdown)
 {
-        std::shared_ptr<Client> client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> client = make_test_client();
 
         client->login("carl", "secret", [client](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
diff --git a/tests/e2ee.cpp b/tests/e2ee.cpp
index 6f2cf68f0..d828ea9c7 100644
--- a/tests/e2ee.cpp
+++ b/tests/e2ee.cpp
@@ -68,7 +68,7 @@ generate_keys(std::shared_ptr<mtx::crypto::OlmClient> account)
 
 TEST(Encryption, UploadIdentityKeys)
 {
-        auto alice       = std::make_shared<Client>("localhost");
+        auto alice       = make_test_client();
         auto olm_account = std::make_shared<mtx::crypto::OlmClient>();
         olm_account->create_new_account();
 
@@ -101,7 +101,7 @@ TEST(Encryption, UploadIdentityKeys)
 
 TEST(Encryption, UploadOneTimeKeys)
 {
-        auto alice       = std::make_shared<Client>("localhost");
+        auto alice       = make_test_client();
         auto olm_account = std::make_shared<mtx::crypto::OlmClient>();
         olm_account->create_new_account();
 
@@ -136,7 +136,7 @@ TEST(Encryption, UploadOneTimeKeys)
 
 TEST(Encryption, UploadSignedOneTimeKeys)
 {
-        auto alice       = std::make_shared<Client>("localhost");
+        auto alice       = make_test_client();
         auto olm_account = std::make_shared<mtx::crypto::OlmClient>();
         olm_account->create_new_account();
 
@@ -170,7 +170,7 @@ TEST(Encryption, UploadSignedOneTimeKeys)
 
 TEST(Encryption, UploadKeys)
 {
-        auto alice       = std::make_shared<Client>("localhost");
+        auto alice       = make_test_client();
         auto olm_account = std::make_shared<mtx::crypto::OlmClient>();
         olm_account->create_new_account();
 
@@ -197,10 +197,10 @@ TEST(Encryption, UploadKeys)
 
 TEST(Encryption, QueryKeys)
 {
-        auto alice     = std::make_shared<Client>("localhost");
+        auto alice     = make_test_client();
         auto alice_olm = std::make_shared<mtx::crypto::OlmClient>();
 
-        auto bob     = std::make_shared<Client>("localhost");
+        auto bob     = make_test_client();
         auto bob_olm = std::make_shared<mtx::crypto::OlmClient>();
 
         alice_olm->create_new_account();
@@ -306,13 +306,13 @@ TEST(Encryption, ClaimKeys)
 {
         using namespace mtx::crypto;
 
-        auto alice     = std::make_shared<Client>("localhost");
+        auto alice     = make_test_client();
         auto alice_olm = std::make_shared<OlmClient>();
 
         alice_olm->create_new_account();
         alice->login("alice", "secret", check_login);
 
-        auto bob     = std::make_shared<Client>("localhost");
+        auto bob     = make_test_client();
         auto bob_olm = std::make_shared<OlmClient>();
 
         bob_olm->create_new_account();
@@ -401,9 +401,9 @@ TEST(Encryption, ClaimMultipleDeviceKeys)
         using namespace mtx::crypto;
 
         // Login with alice multiple times
-        auto alice1 = std::make_shared<Client>("localhost");
-        auto alice2 = std::make_shared<Client>("localhost");
-        auto alice3 = std::make_shared<Client>("localhost");
+        auto alice1 = make_test_client();
+        auto alice2 = make_test_client();
+        auto alice3 = make_test_client();
         alice1->login("alice", "secret", check_login);
         alice2->login("alice", "secret", check_login);
         alice3->login("alice", "secret", check_login);
@@ -444,7 +444,7 @@ TEST(Encryption, ClaimMultipleDeviceKeys)
         WAIT_UNTIL(uploads == 3);
 
         // Bob will claim all keys from alice
-        auto bob = std::make_shared<Client>("localhost");
+        auto bob = make_test_client();
         bob->login("bob", "secret", check_login);
 
         WAIT_UNTIL(!bob->access_token().empty())
@@ -475,7 +475,7 @@ TEST(Encryption, ClaimMultipleDeviceKeys)
 
 TEST(Encryption, KeyChanges)
 {
-        auto carl     = std::make_shared<Client>("localhost");
+        auto carl     = make_test_client();
         auto carl_olm = std::make_shared<mtx::crypto::OlmClient>();
         carl_olm->create_new_account();
 
@@ -538,8 +538,8 @@ TEST(Encryption, KeyChanges)
 
 TEST(Encryption, EnableEncryption)
 {
-        auto bob  = make_shared<Client>("localhost");
-        auto carl = make_shared<Client>("localhost");
+        auto bob  = make_test_client();
+        auto carl = make_test_client();
 
         bob->login("bob", "secret", [](const Login &, RequestErr err) { check_error(err); });
         carl->login("carl", "secret", [](const Login &, RequestErr err) { check_error(err); });
@@ -750,12 +750,12 @@ TEST(Encryption, OlmRoomKeyEncryption)
 {
         // Alice wants to use olm to send data to Bob.
         auto alice_olm  = std::make_shared<OlmClient>();
-        auto alice_http = std::make_shared<Client>("localhost");
+        auto alice_http = make_test_client();
         alice_olm->create_new_account();
         alice_olm->generate_one_time_keys(10);
 
         auto bob_olm  = std::make_shared<OlmClient>();
-        auto bob_http = std::make_shared<Client>("localhost");
+        auto bob_http = make_test_client();
         bob_olm->create_new_account();
         bob_olm->generate_one_time_keys(10);
 
@@ -907,12 +907,12 @@ TEST(Encryption, ShareSecret)
 {
         // Alice wants to use olm to send data to Bob.
         auto alice_olm  = std::make_shared<OlmClient>();
-        auto alice_http = std::make_shared<Client>("localhost");
+        auto alice_http = make_test_client();
         alice_olm->create_new_account();
         alice_olm->generate_one_time_keys(10);
 
         auto bob_olm  = std::make_shared<OlmClient>();
-        auto bob_http = std::make_shared<Client>("localhost");
+        auto bob_http = make_test_client();
         bob_olm->create_new_account();
         bob_olm->generate_one_time_keys(10);
 
diff --git a/tests/media_api.cpp b/tests/media_api.cpp
index defd83523..61b00827c 100644
--- a/tests/media_api.cpp
+++ b/tests/media_api.cpp
@@ -11,6 +11,8 @@
 #include <mtx/responses.hpp>
 #include <mtxclient/http/client.hpp>
 
+#include "test_helpers.hpp"
+
 using namespace mtx::http;
 using namespace mtx::identifiers;
 
@@ -43,7 +45,7 @@ validate_upload(const mtx::responses::ContentURI &res, RequestErr err)
 
 TEST(MediaAPI, UploadTextFile)
 {
-        std::shared_ptr<Client> alice = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> alice = make_test_client();
 
         alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
                 ASSERT_FALSE(err);
@@ -77,7 +79,7 @@ TEST(MediaAPI, UploadTextFile)
 
 TEST(MediaAPI, UploadAudio)
 {
-        std::shared_ptr<Client> bob = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> bob = make_test_client();
 
         bob->login("bob", "secret", [bob](const mtx::responses::Login &, RequestErr err) {
                 ASSERT_FALSE(err);
@@ -108,7 +110,7 @@ TEST(MediaAPI, UploadAudio)
 
 TEST(MediaAPI, UploadImage)
 {
-        std::shared_ptr<Client> carl = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> carl = make_test_client();
 
         carl->login("carl", "secret", [carl](const mtx::responses::Login &, RequestErr err) {
                 ASSERT_FALSE(err);
@@ -154,7 +156,7 @@ TEST(MediaAPI, UploadImage)
 
 TEST(MediaAPI, UploadSVG)
 {
-        std::shared_ptr<Client> carl = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> carl = make_test_client();
 
         carl->login("carl", "secret", [carl](const mtx::responses::Login &, RequestErr err) {
                 ASSERT_FALSE(err);
diff --git a/tests/pushrules.cpp b/tests/pushrules.cpp
index 9cb1171d1..1c8f9c0a7 100644
--- a/tests/pushrules.cpp
+++ b/tests/pushrules.cpp
@@ -231,7 +231,7 @@ TEST(Pushrules, GlobalRuleset)
 
 TEST(Pushrules, GetGlobalRuleset)
 {
-        std::shared_ptr<Client> client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> client = make_test_client();
 
         client->login(
           "alice", "secret", [client](const mtx::responses::Login &res, RequestErr err) {
@@ -247,7 +247,7 @@ TEST(Pushrules, GetGlobalRuleset)
 
 TEST(Pushrules, GetRuleset)
 {
-        std::shared_ptr<Client> client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> client = make_test_client();
 
         client->login(
           "alice", "secret", [client](const mtx::responses::Login &res, RequestErr err) {
@@ -267,7 +267,7 @@ TEST(Pushrules, GetRuleset)
 
 TEST(Pushrules, PutAndDeleteRuleset)
 {
-        std::shared_ptr<Client> client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> client = make_test_client();
 
         client->login(
           "alice", "secret", [client](const mtx::responses::Login &res, RequestErr err) {
@@ -297,7 +297,7 @@ TEST(Pushrules, PutAndDeleteRuleset)
 
 TEST(Pushrules, RulesetEnabled)
 {
-        std::shared_ptr<Client> client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> client = make_test_client();
 
         client->login(
           "alice", "secret", [client](const mtx::responses::Login &res, RequestErr err) {
@@ -344,7 +344,7 @@ TEST(Pushrules, RulesetEnabled)
 
 TEST(Pushrules, Actions)
 {
-        std::shared_ptr<Client> client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> client = make_test_client();
 
         client->login(
           "alice", "secret", [client](const mtx::responses::Login &res, RequestErr err) {
@@ -378,7 +378,7 @@ TEST(Pushrules, Actions)
 
 TEST(Pushrules, RoomRuleMute)
 {
-        std::shared_ptr<Client> client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> client = make_test_client();
 
         client->login(
           "alice", "secret", [client](const mtx::responses::Login &res, RequestErr err) {
@@ -418,7 +418,7 @@ TEST(Pushrules, RoomRuleMute)
 
 TEST(Pushrules, RoomRuleMentions)
 {
-        std::shared_ptr<Client> client = std::make_shared<Client>("localhost");
+        std::shared_ptr<Client> client = make_test_client();
 
         client->login(
           "alice", "secret", [client](const mtx::responses::Login &res, RequestErr err) {
diff --git a/tests/test_helpers.hpp b/tests/test_helpers.hpp
index 433b052a4..7ff864aeb 100644
--- a/tests/test_helpers.hpp
+++ b/tests/test_helpers.hpp
@@ -50,6 +50,15 @@ check_error(mtx::http::RequestErr err)
         ASSERT_FALSE(err);
 }
 
+inline auto
+make_test_client()
+{
+        const char *server = std::getenv("MTXCLIENT_SERVER");
+        auto client        = std::make_shared<mtx::http::Client>(server ? server : "localhost");
+        client->verify_certificates(false);
+        return client;
+}
+
 inline void
 check_login(const mtx::responses::Login &, mtx::http::RequestErr err)
 {
-- 
GitLab