blob: aee85c265edb98842682a81bdf322b7d01c5c46a [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>
13
14inline std::shared_ptr<persistent_data::UserSession>
15 verifyMtlsUser(const boost::asio::ip::address& clientIp,
16 boost::asio::ssl::verify_context& ctx)
17{
18 // do nothing if TLS is disabled
19 if (!persistent_data::SessionStore::getInstance()
20 .getAuthMethodsConfig()
21 .tls)
22 {
23 BMCWEB_LOG_DEBUG << "TLS auth_config is disabled";
24 return nullptr;
25 }
26
27 X509_STORE_CTX* cts = ctx.native_handle();
28 if (cts == nullptr)
29 {
30 BMCWEB_LOG_DEBUG << "Cannot get native TLS handle.";
31 return nullptr;
32 }
33
34 // Get certificate
35 X509* peerCert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
36 if (peerCert == nullptr)
37 {
38 BMCWEB_LOG_DEBUG << "Cannot get current TLS certificate.";
39 return nullptr;
40 }
41
42 // Check if certificate is OK
43 int ctxError = X509_STORE_CTX_get_error(cts);
44 if (ctxError != X509_V_OK)
45 {
46 BMCWEB_LOG_INFO << "Last TLS error is: " << ctxError;
47 return nullptr;
48 }
49 // Check that we have reached final certificate in chain
50 int32_t depth = X509_STORE_CTX_get_error_depth(cts);
51 if (depth != 0)
52
53 {
54 BMCWEB_LOG_DEBUG << "Certificate verification in progress (depth "
55 << depth << "), waiting to reach final depth";
56 return nullptr;
57 }
58
59 BMCWEB_LOG_DEBUG << "Certificate verification of final depth";
60
61 // Verify KeyUsage
62 bool isKeyUsageDigitalSignature = false;
63 bool isKeyUsageKeyAgreement = false;
64
65 ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>(
66 X509_get_ext_d2i(peerCert, NID_key_usage, nullptr, nullptr));
67
68 if (usage == nullptr)
69 {
70 BMCWEB_LOG_DEBUG << "TLS usage is null";
71 return nullptr;
72 }
73
74 for (int i = 0; i < usage->length; i++)
75 {
76 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
77 unsigned char usageChar = usage->data[i];
78 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}