blob: e77315a057a61bf34ca822bd5e3b6314f93d12d1 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanouse7923992023-12-03 14:14:02 -08003#include "mutual_tls.hpp"
4
Ed Tanousf0b59af2024-03-20 13:38:04 -07005#include "sessions.hpp"
6
Ed Tanous6dbe9be2024-04-14 10:24:20 -07007extern "C"
8{
Ed Tanousf0b59af2024-03-20 13:38:04 -07009#include <openssl/asn1.h>
10#include <openssl/ec.h>
11#include <openssl/evp.h>
12#include <openssl/obj_mac.h>
13#include <openssl/types.h>
14#include <openssl/x509.h>
15#include <openssl/x509_vfy.h>
16#include <openssl/x509v3.h>
Ed Tanous6dbe9be2024-04-14 10:24:20 -070017}
Ed Tanousf0b59af2024-03-20 13:38:04 -070018
Ed Tanouse7923992023-12-03 14:14:02 -080019#include <boost/asio/ip/address.hpp>
20#include <boost/asio/ssl/verify_context.hpp>
21
Ed Tanousf0b59af2024-03-20 13:38:04 -070022#include <array>
Ed Tanouse7923992023-12-03 14:14:02 -080023#include <memory>
24
25#include <gmock/gmock.h>
Ed Tanous478b7ad2024-07-15 19:11:54 -070026#include <gtest/gtest.h>
Ed Tanouse7923992023-12-03 14:14:02 -080027
28using ::testing::IsNull;
29using ::testing::NotNull;
30
31namespace
32{
33class OSSLX509
34{
35 X509* ptr = X509_new();
36
37 public:
38 OSSLX509& operator=(const OSSLX509&) = delete;
39 OSSLX509& operator=(OSSLX509&&) = delete;
40
41 OSSLX509(const OSSLX509&) = delete;
42 OSSLX509(OSSLX509&&) = delete;
43
44 OSSLX509() = default;
Ed Tanous23f1c962023-12-05 15:57:46 -080045
46 void setSubjectName()
47 {
48 X509_NAME* name = X509_get_subject_name(ptr);
49 std::array<unsigned char, 5> user = {'u', 's', 'e', 'r', '\0'};
50 X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, user.data(), -1,
51 -1, 0);
52 }
53 void sign()
54 {
55 // Generate test key
56 EVP_PKEY* pkey = nullptr;
57 EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr);
58 ASSERT_EQ(EVP_PKEY_keygen_init(pctx), 1);
59 ASSERT_EQ(
60 EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1),
61 1);
62 ASSERT_EQ(EVP_PKEY_keygen(pctx, &pkey), 1);
63 EVP_PKEY_CTX_free(pctx);
64
65 // Sign cert with key
66 ASSERT_EQ(X509_set_pubkey(ptr, pkey), 1);
67 ASSERT_GT(X509_sign(ptr, pkey, EVP_sha256()), 0);
68 EVP_PKEY_free(pkey);
69 }
70
Ed Tanouse7923992023-12-03 14:14:02 -080071 X509* get()
72 {
73 return ptr;
74 }
75 ~OSSLX509()
76 {
77 X509_free(ptr);
78 }
79};
80
81class OSSLX509StoreCTX
82{
83 X509_STORE_CTX* ptr = X509_STORE_CTX_new();
84
85 public:
86 OSSLX509StoreCTX& operator=(const OSSLX509StoreCTX&) = delete;
87 OSSLX509StoreCTX& operator=(OSSLX509StoreCTX&&) = delete;
88
89 OSSLX509StoreCTX(const OSSLX509StoreCTX&) = delete;
90 OSSLX509StoreCTX(OSSLX509StoreCTX&&) = delete;
91
92 OSSLX509StoreCTX() = default;
93 X509_STORE_CTX* get()
94 {
95 return ptr;
96 }
97 ~OSSLX509StoreCTX()
98 {
99 X509_STORE_CTX_free(ptr);
100 }
101};
102
103TEST(MutualTLS, GoodCert)
104{
105 OSSLX509 x509;
106
Ed Tanous23f1c962023-12-05 15:57:46 -0800107 x509.setSubjectName();
Ed Tanouse7923992023-12-03 14:14:02 -0800108 X509_EXTENSION* ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_key_usage,
109 "digitalSignature, keyAgreement");
110 ASSERT_THAT(ex, NotNull());
111 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
112 X509_EXTENSION_free(ex);
113 ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_ext_key_usage, "clientAuth");
114 ASSERT_THAT(ex, NotNull());
115 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
116 X509_EXTENSION_free(ex);
117
Ed Tanous23f1c962023-12-05 15:57:46 -0800118 x509.sign();
119
Ed Tanouse7923992023-12-03 14:14:02 -0800120 OSSLX509StoreCTX x509Store;
121 X509_STORE_CTX_set_current_cert(x509Store.get(), x509.get());
122
123 boost::asio::ip::address ip;
124 boost::asio::ssl::verify_context ctx(x509Store.get());
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400125 std::shared_ptr<persistent_data::UserSession> session =
126 verifyMtlsUser(ip, ctx);
Ed Tanouse7923992023-12-03 14:14:02 -0800127 ASSERT_THAT(session, NotNull());
128 EXPECT_THAT(session->username, "user");
129}
130
Ed Tanouse7923992023-12-03 14:14:02 -0800131TEST(MutualTLS, MissingKeyUsage)
132{
Ed Tanous23f1c962023-12-05 15:57:46 -0800133 for (const char* usageString :
134 {"digitalSignature", "keyAgreement", "digitalSignature, keyAgreement"})
Ed Tanouse7923992023-12-03 14:14:02 -0800135 {
136 OSSLX509 x509;
Ed Tanous23f1c962023-12-05 15:57:46 -0800137 x509.setSubjectName();
Ed Tanouse7923992023-12-03 14:14:02 -0800138
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400139 X509_EXTENSION* ex =
140 X509V3_EXT_conf_nid(nullptr, nullptr, NID_key_usage, usageString);
Ed Tanouse7923992023-12-03 14:14:02 -0800141
142 ASSERT_THAT(ex, NotNull());
143 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
144 X509_EXTENSION_free(ex);
145 ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_ext_key_usage,
146 "clientAuth");
147 ASSERT_THAT(ex, NotNull());
148 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
149 X509_EXTENSION_free(ex);
Ed Tanous23f1c962023-12-05 15:57:46 -0800150 x509.sign();
Ed Tanouse7923992023-12-03 14:14:02 -0800151
152 OSSLX509StoreCTX x509Store;
153 X509_STORE_CTX_set_current_cert(x509Store.get(), x509.get());
154
155 boost::asio::ip::address ip;
156 boost::asio::ssl::verify_context ctx(x509Store.get());
157 std::shared_ptr<persistent_data::UserSession> session =
158 verifyMtlsUser(ip, ctx);
Ed Tanous23f1c962023-12-05 15:57:46 -0800159 ASSERT_THAT(session, NotNull());
Ed Tanouse7923992023-12-03 14:14:02 -0800160 }
161}
162
Ed Tanouse7923992023-12-03 14:14:02 -0800163TEST(MutualTLS, MissingCert)
164{
165 OSSLX509StoreCTX x509Store;
166
167 boost::asio::ip::address ip;
168 boost::asio::ssl::verify_context ctx(x509Store.get());
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400169 std::shared_ptr<persistent_data::UserSession> session =
170 verifyMtlsUser(ip, ctx);
Ed Tanouse7923992023-12-03 14:14:02 -0800171 ASSERT_THAT(session, IsNull());
172}
173} // namespace