blob: f8af0f6f5785224eb0142c62bab4a5bae9a32c96 [file] [log] [blame]
Ed Tanous7c8e0642022-02-21 12:11:14 -08001#pragma once
2
3#include "logging.hpp"
4#include "persistent_data.hpp"
5
6#include <openssl/crypto.h>
7#include <openssl/ssl.h>
8
9#include <boost/asio/ip/address.hpp>
10#include <boost/asio/ssl/verify_context.hpp>
11
12#include <memory>
Patrick Williams3d7fc712023-05-10 20:03:19 -050013#include <span>
Ed Tanous7c8e0642022-02-21 12:11:14 -080014
15inline std::shared_ptr<persistent_data::UserSession>
16 verifyMtlsUser(const boost::asio::ip::address& clientIp,
17 boost::asio::ssl::verify_context& ctx)
18{
19 // do nothing if TLS is disabled
20 if (!persistent_data::SessionStore::getInstance()
21 .getAuthMethodsConfig()
22 .tls)
23 {
24 BMCWEB_LOG_DEBUG << "TLS auth_config is disabled";
25 return nullptr;
26 }
27
28 X509_STORE_CTX* cts = ctx.native_handle();
29 if (cts == nullptr)
30 {
31 BMCWEB_LOG_DEBUG << "Cannot get native TLS handle.";
32 return nullptr;
33 }
34
35 // Get certificate
36 X509* peerCert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
37 if (peerCert == nullptr)
38 {
39 BMCWEB_LOG_DEBUG << "Cannot get current TLS certificate.";
40 return nullptr;
41 }
42
43 // Check if certificate is OK
44 int ctxError = X509_STORE_CTX_get_error(cts);
45 if (ctxError != X509_V_OK)
46 {
47 BMCWEB_LOG_INFO << "Last TLS error is: " << ctxError;
48 return nullptr;
49 }
50 // Check that we have reached final certificate in chain
51 int32_t depth = X509_STORE_CTX_get_error_depth(cts);
52 if (depth != 0)
53
54 {
55 BMCWEB_LOG_DEBUG << "Certificate verification in progress (depth "
56 << depth << "), waiting to reach final depth";
57 return nullptr;
58 }
59
60 BMCWEB_LOG_DEBUG << "Certificate verification of final depth";
61
62 // Verify KeyUsage
63 bool isKeyUsageDigitalSignature = false;
64 bool isKeyUsageKeyAgreement = false;
65
66 ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>(
67 X509_get_ext_d2i(peerCert, NID_key_usage, nullptr, nullptr));
68
Patrick Williams3d7fc712023-05-10 20:03:19 -050069 if ((usage == nullptr) || (usage->data == nullptr))
Ed Tanous7c8e0642022-02-21 12:11:14 -080070 {
71 BMCWEB_LOG_DEBUG << "TLS usage is null";
72 return nullptr;
73 }
74
Patrick Williams3d7fc712023-05-10 20:03:19 -050075 for (auto usageChar :
76 std::span(usage->data, static_cast<size_t>(usage->length)))
Ed Tanous7c8e0642022-02-21 12:11:14 -080077 {
Ed Tanous7c8e0642022-02-21 12:11:14 -080078 if (KU_DIGITAL_SIGNATURE & usageChar)
79 {
80 isKeyUsageDigitalSignature = true;
81 }
82 if (KU_KEY_AGREEMENT & usageChar)
83 {
84 isKeyUsageKeyAgreement = true;
85 }
86 }
87 ASN1_BIT_STRING_free(usage);
88
89 if (!isKeyUsageDigitalSignature || !isKeyUsageKeyAgreement)
90 {
91 BMCWEB_LOG_DEBUG << "Certificate ExtendedKeyUsage does "
92 "not allow provided certificate to "
93 "be used for user authentication";
94 return nullptr;
95 }
96
97 // Determine that ExtendedKeyUsage includes Client Auth
98
99 stack_st_ASN1_OBJECT* extUsage = static_cast<stack_st_ASN1_OBJECT*>(
100 X509_get_ext_d2i(peerCert, NID_ext_key_usage, nullptr, nullptr));
101
102 if (extUsage == nullptr)
103 {
104 BMCWEB_LOG_DEBUG << "TLS extUsage is null";
105 return nullptr;
106 }
107
108 bool isExKeyUsageClientAuth = false;
109 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
110 {
111 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
112 int nid = OBJ_obj2nid(sk_ASN1_OBJECT_value(extUsage, i));
113 if (NID_client_auth == nid)
114 {
115 isExKeyUsageClientAuth = true;
116 break;
117 }
118 }
119 sk_ASN1_OBJECT_free(extUsage);
120
121 // Certificate has to have proper key usages set
122 if (!isExKeyUsageClientAuth)
123 {
124 BMCWEB_LOG_DEBUG << "Certificate ExtendedKeyUsage does "
125 "not allow provided certificate to "
126 "be used for user authentication";
127 return nullptr;
128 }
129 std::string sslUser;
130 // Extract username contained in CommonName
131 sslUser.resize(256, '\0');
132
133 int status = X509_NAME_get_text_by_NID(X509_get_subject_name(peerCert),
134 NID_commonName, sslUser.data(),
135 static_cast<int>(sslUser.size()));
136
137 if (status == -1)
138 {
139 BMCWEB_LOG_DEBUG << "TLS cannot get username to create session";
140 return nullptr;
141 }
142
143 size_t lastChar = sslUser.find('\0');
144 if (lastChar == std::string::npos || lastChar == 0)
145 {
146 BMCWEB_LOG_DEBUG << "Invalid TLS user name";
147 return nullptr;
148 }
149 sslUser.resize(lastChar);
150 std::string unsupportedClientId;
151 return persistent_data::SessionStore::getInstance().generateUserSession(
152 sslUser, clientIp, unsupportedClientId,
153 persistent_data::PersistenceType::TIMEOUT);
154}