xref: /openbmc/bmcweb/test/http/mutual_tls.cpp (revision e7923997)
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