1 #include "mutual_tls.hpp" 2 3 #include <boost/asio/ip/address.hpp> 4 #include <boost/asio/ssl/verify_context.hpp> 5 6 #include <memory> 7 8 #include <gmock/gmock.h> 9 #include <gtest/gtest.h> // IWYU pragma: keep 10 11 using ::testing::IsNull; 12 using ::testing::NotNull; 13 14 namespace 15 { 16 class OSSLX509 17 { 18 X509* ptr = X509_new(); 19 20 public: 21 OSSLX509& operator=(const OSSLX509&) = delete; 22 OSSLX509& operator=(OSSLX509&&) = delete; 23 24 OSSLX509(const OSSLX509&) = delete; 25 OSSLX509(OSSLX509&&) = delete; 26 27 OSSLX509() = default; 28 X509* get() 29 { 30 return ptr; 31 } 32 ~OSSLX509() 33 { 34 X509_free(ptr); 35 } 36 }; 37 38 class OSSLX509StoreCTX 39 { 40 X509_STORE_CTX* ptr = X509_STORE_CTX_new(); 41 42 public: 43 OSSLX509StoreCTX& operator=(const OSSLX509StoreCTX&) = delete; 44 OSSLX509StoreCTX& operator=(OSSLX509StoreCTX&&) = delete; 45 46 OSSLX509StoreCTX(const OSSLX509StoreCTX&) = delete; 47 OSSLX509StoreCTX(OSSLX509StoreCTX&&) = delete; 48 49 OSSLX509StoreCTX() = default; 50 X509_STORE_CTX* get() 51 { 52 return ptr; 53 } 54 ~OSSLX509StoreCTX() 55 { 56 X509_STORE_CTX_free(ptr); 57 } 58 }; 59 60 TEST(MutualTLS, GoodCert) 61 { 62 OSSLX509 x509; 63 64 X509_NAME* name = X509_get_subject_name(x509.get()); 65 std::array<unsigned char, 5> user = {'u', 's', 'e', 'r', '\0'}; 66 X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, user.data(), -1, -1, 67 0); 68 69 X509_EXTENSION* ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_key_usage, 70 "digitalSignature, keyAgreement"); 71 ASSERT_THAT(ex, NotNull()); 72 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1); 73 X509_EXTENSION_free(ex); 74 ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_ext_key_usage, "clientAuth"); 75 ASSERT_THAT(ex, NotNull()); 76 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1); 77 X509_EXTENSION_free(ex); 78 79 OSSLX509StoreCTX x509Store; 80 X509_STORE_CTX_set_current_cert(x509Store.get(), x509.get()); 81 82 boost::asio::ip::address ip; 83 boost::asio::ssl::verify_context ctx(x509Store.get()); 84 std::shared_ptr<persistent_data::UserSession> session = verifyMtlsUser(ip, 85 ctx); 86 ASSERT_THAT(session, NotNull()); 87 EXPECT_THAT(session->username, "user"); 88 } 89 90 TEST(MutualTLS, MissingSubject) 91 { 92 OSSLX509 x509; 93 94 X509_EXTENSION* ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_key_usage, 95 "digitalSignature, keyAgreement"); 96 ASSERT_THAT(ex, NotNull()); 97 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1); 98 X509_EXTENSION_free(ex); 99 ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_ext_key_usage, "clientAuth"); 100 ASSERT_THAT(ex, NotNull()); 101 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1); 102 X509_EXTENSION_free(ex); 103 104 OSSLX509StoreCTX x509Store; 105 X509_STORE_CTX_set_current_cert(x509Store.get(), x509.get()); 106 107 boost::asio::ip::address ip; 108 boost::asio::ssl::verify_context ctx(x509Store.get()); 109 std::shared_ptr<persistent_data::UserSession> session = verifyMtlsUser(ip, 110 ctx); 111 ASSERT_THAT(session, IsNull()); 112 } 113 114 TEST(MutualTLS, MissingKeyUsage) 115 { 116 for (const char* usageString : {"digitalSignature", "keyAgreement"}) 117 { 118 OSSLX509 x509; 119 120 X509_EXTENSION* ex = X509V3_EXT_conf_nid(nullptr, nullptr, 121 NID_key_usage, usageString); 122 123 ASSERT_THAT(ex, NotNull()); 124 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1); 125 X509_EXTENSION_free(ex); 126 ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_ext_key_usage, 127 "clientAuth"); 128 ASSERT_THAT(ex, NotNull()); 129 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1); 130 X509_EXTENSION_free(ex); 131 132 OSSLX509StoreCTX x509Store; 133 X509_STORE_CTX_set_current_cert(x509Store.get(), x509.get()); 134 135 boost::asio::ip::address ip; 136 boost::asio::ssl::verify_context ctx(x509Store.get()); 137 std::shared_ptr<persistent_data::UserSession> session = 138 verifyMtlsUser(ip, ctx); 139 ASSERT_THAT(session, IsNull()); 140 } 141 } 142 143 TEST(MutualTLS, MissingExtKeyUsage) 144 { 145 OSSLX509 x509; 146 147 X509_EXTENSION* ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_key_usage, 148 "digitalSignature, keyAgreement"); 149 150 ASSERT_THAT(ex, NotNull()); 151 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1); 152 X509_EXTENSION_free(ex); 153 154 OSSLX509StoreCTX x509Store; 155 X509_STORE_CTX_set_current_cert(x509Store.get(), x509.get()); 156 157 boost::asio::ip::address ip; 158 boost::asio::ssl::verify_context ctx(x509Store.get()); 159 std::shared_ptr<persistent_data::UserSession> session = verifyMtlsUser(ip, 160 ctx); 161 ASSERT_THAT(session, IsNull()); 162 } 163 164 TEST(MutualTLS, MissingCert) 165 { 166 OSSLX509StoreCTX x509Store; 167 168 boost::asio::ip::address ip; 169 boost::asio::ssl::verify_context ctx(x509Store.get()); 170 std::shared_ptr<persistent_data::UserSession> session = verifyMtlsUser(ip, 171 ctx); 172 ASSERT_THAT(session, IsNull()); 173 } 174 } // namespace 175