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