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