xref: /openbmc/bmcweb/test/http/mutual_tls.cpp (revision 2ae81db9)
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 
29     void setSubjectName()
30     {
31         X509_NAME* name = X509_get_subject_name(ptr);
32         std::array<unsigned char, 5> user = {'u', 's', 'e', 'r', '\0'};
33         X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, user.data(), -1,
34                                    -1, 0);
35     }
36     void sign()
37     {
38         // Generate test key
39         EVP_PKEY* pkey = nullptr;
40         EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr);
41         ASSERT_EQ(EVP_PKEY_keygen_init(pctx), 1);
42         ASSERT_EQ(
43             EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1),
44             1);
45         ASSERT_EQ(EVP_PKEY_keygen(pctx, &pkey), 1);
46         EVP_PKEY_CTX_free(pctx);
47 
48         // Sign cert with key
49         ASSERT_EQ(X509_set_pubkey(ptr, pkey), 1);
50         ASSERT_GT(X509_sign(ptr, pkey, EVP_sha256()), 0);
51         EVP_PKEY_free(pkey);
52     }
53 
54     X509* get()
55     {
56         return ptr;
57     }
58     ~OSSLX509()
59     {
60         X509_free(ptr);
61     }
62 };
63 
64 class OSSLX509StoreCTX
65 {
66     X509_STORE_CTX* ptr = X509_STORE_CTX_new();
67 
68   public:
69     OSSLX509StoreCTX& operator=(const OSSLX509StoreCTX&) = delete;
70     OSSLX509StoreCTX& operator=(OSSLX509StoreCTX&&) = delete;
71 
72     OSSLX509StoreCTX(const OSSLX509StoreCTX&) = delete;
73     OSSLX509StoreCTX(OSSLX509StoreCTX&&) = delete;
74 
75     OSSLX509StoreCTX() = default;
76     X509_STORE_CTX* get()
77     {
78         return ptr;
79     }
80     ~OSSLX509StoreCTX()
81     {
82         X509_STORE_CTX_free(ptr);
83     }
84 };
85 
86 TEST(MutualTLS, GoodCert)
87 {
88     OSSLX509 x509;
89 
90     x509.setSubjectName();
91     X509_EXTENSION* ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_key_usage,
92                                              "digitalSignature, keyAgreement");
93     ASSERT_THAT(ex, NotNull());
94     ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
95     X509_EXTENSION_free(ex);
96     ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_ext_key_usage, "clientAuth");
97     ASSERT_THAT(ex, NotNull());
98     ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
99     X509_EXTENSION_free(ex);
100 
101     x509.sign();
102 
103     OSSLX509StoreCTX x509Store;
104     X509_STORE_CTX_set_current_cert(x509Store.get(), x509.get());
105 
106     boost::asio::ip::address ip;
107     boost::asio::ssl::verify_context ctx(x509Store.get());
108     std::shared_ptr<persistent_data::UserSession> session = verifyMtlsUser(ip,
109                                                                            ctx);
110     ASSERT_THAT(session, NotNull());
111     EXPECT_THAT(session->username, "user");
112 }
113 
114 TEST(MutualTLS, MissingKeyUsage)
115 {
116     for (const char* usageString :
117          {"digitalSignature", "keyAgreement", "digitalSignature, keyAgreement"})
118     {
119         OSSLX509 x509;
120         x509.setSubjectName();
121 
122         X509_EXTENSION* ex = X509V3_EXT_conf_nid(nullptr, nullptr,
123                                                  NID_key_usage, usageString);
124 
125         ASSERT_THAT(ex, NotNull());
126         ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
127         X509_EXTENSION_free(ex);
128         ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_ext_key_usage,
129                                  "clientAuth");
130         ASSERT_THAT(ex, NotNull());
131         ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
132         X509_EXTENSION_free(ex);
133         x509.sign();
134 
135         OSSLX509StoreCTX x509Store;
136         X509_STORE_CTX_set_current_cert(x509Store.get(), x509.get());
137 
138         boost::asio::ip::address ip;
139         boost::asio::ssl::verify_context ctx(x509Store.get());
140         std::shared_ptr<persistent_data::UserSession> session =
141             verifyMtlsUser(ip, ctx);
142         ASSERT_THAT(session, NotNull());
143     }
144 }
145 
146 TEST(MutualTLS, MissingCert)
147 {
148     OSSLX509StoreCTX x509Store;
149 
150     boost::asio::ip::address ip;
151     boost::asio::ssl::verify_context ctx(x509Store.get());
152     std::shared_ptr<persistent_data::UserSession> session = verifyMtlsUser(ip,
153                                                                            ctx);
154     ASSERT_THAT(session, IsNull());
155 }
156 } // namespace
157