xref: /openbmc/bmcweb/http/mutual_tls.hpp (revision a529a6aa)
1 #pragma once
2 
3 #include "logging.hpp"
4 #include "mutual_tls_meta.hpp"
5 #include "persistent_data.hpp"
6 
7 extern "C"
8 {
9 #include <openssl/crypto.h>
10 #include <openssl/ssl.h>
11 }
12 
13 #include <boost/asio/ip/address.hpp>
14 #include <boost/asio/ssl/verify_context.hpp>
15 
16 #include <memory>
17 #include <span>
18 
19 inline std::string getUsernameFromCommonName(std::string_view commonName)
20 {
21     const persistent_data::AuthConfigMethods& authMethodsConfig =
22         persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
23     switch (authMethodsConfig.mTLSCommonNameParsingMode)
24     {
25         case persistent_data::MTLSCommonNameParseMode::Invalid:
26         case persistent_data::MTLSCommonNameParseMode::Whole:
27         case persistent_data::MTLSCommonNameParseMode::UserPrincipalName:
28         {
29             // Not yet supported
30             return "";
31         }
32         case persistent_data::MTLSCommonNameParseMode::CommonName:
33         {
34             return std::string{commonName};
35         }
36         case persistent_data::MTLSCommonNameParseMode::Meta:
37         {
38             // Meta Inc. CommonName parsing
39             std::optional<std::string_view> sslUserMeta =
40                 mtlsMetaParseSslUser(commonName);
41             if (!sslUserMeta)
42             {
43                 return "";
44             }
45             return std::string{*sslUserMeta};
46         }
47     }
48     return "";
49 }
50 
51 inline std::shared_ptr<persistent_data::UserSession>
52     verifyMtlsUser(const boost::asio::ip::address& clientIp,
53                    boost::asio::ssl::verify_context& ctx)
54 {
55     // do nothing if TLS is disabled
56     if (!persistent_data::SessionStore::getInstance()
57              .getAuthMethodsConfig()
58              .tls)
59     {
60         BMCWEB_LOG_DEBUG("TLS auth_config is disabled");
61         return nullptr;
62     }
63 
64     X509_STORE_CTX* cts = ctx.native_handle();
65     if (cts == nullptr)
66     {
67         BMCWEB_LOG_DEBUG("Cannot get native TLS handle.");
68         return nullptr;
69     }
70 
71     // Get certificate
72     X509* peerCert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
73     if (peerCert == nullptr)
74     {
75         BMCWEB_LOG_DEBUG("Cannot get current TLS certificate.");
76         return nullptr;
77     }
78 
79     // Check if certificate is OK
80     int ctxError = X509_STORE_CTX_get_error(cts);
81     if (ctxError != X509_V_OK)
82     {
83         BMCWEB_LOG_INFO("Last TLS error is: {}", ctxError);
84         return nullptr;
85     }
86 
87     // Check that we have reached final certificate in chain
88     int32_t depth = X509_STORE_CTX_get_error_depth(cts);
89     if (depth != 0)
90     {
91         BMCWEB_LOG_DEBUG(
92             "Certificate verification in progress (depth {}), waiting to reach final depth",
93             depth);
94         return nullptr;
95     }
96 
97     BMCWEB_LOG_DEBUG("Certificate verification of final depth");
98 
99     if (X509_check_purpose(peerCert, X509_PURPOSE_SSL_CLIENT, 0) != 1)
100     {
101         BMCWEB_LOG_DEBUG(
102             "Chain does not allow certificate to be used for SSL client authentication");
103         return nullptr;
104     }
105 
106     std::string commonName;
107     // Extract username contained in CommonName
108     commonName.resize(256, '\0');
109 
110     int length = X509_NAME_get_text_by_NID(X509_get_subject_name(peerCert),
111                                            NID_commonName, commonName.data(),
112                                            static_cast<int>(commonName.size()));
113     if (length <= 0)
114     {
115         BMCWEB_LOG_DEBUG("TLS cannot get common name to create session");
116         return nullptr;
117     }
118 
119     commonName.resize(static_cast<size_t>(length));
120     std::string sslUser = getUsernameFromCommonName(commonName);
121     if (sslUser.empty())
122     {
123         BMCWEB_LOG_WARNING("Failed to get user from common name {}",
124                            commonName);
125         return nullptr;
126     }
127 
128     std::string unsupportedClientId;
129     return persistent_data::SessionStore::getInstance().generateUserSession(
130         sslUser, clientIp, unsupportedClientId,
131         persistent_data::SessionType::MutualTLS);
132 }
133