xref: /openbmc/bmcweb/http/mutual_tls.hpp (revision 25b54dba)
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::shared_ptr<persistent_data::UserSession>
verifyMtlsUser(const boost::asio::ip::address & clientIp,boost::asio::ssl::verify_context & ctx)20     verifyMtlsUser(const boost::asio::ip::address& clientIp,
21                    boost::asio::ssl::verify_context& ctx)
22 {
23     // do nothing if TLS is disabled
24     if (!persistent_data::SessionStore::getInstance()
25              .getAuthMethodsConfig()
26              .tls)
27     {
28         BMCWEB_LOG_DEBUG("TLS auth_config is disabled");
29         return nullptr;
30     }
31 
32     X509_STORE_CTX* cts = ctx.native_handle();
33     if (cts == nullptr)
34     {
35         BMCWEB_LOG_DEBUG("Cannot get native TLS handle.");
36         return nullptr;
37     }
38 
39     // Get certificate
40     X509* peerCert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
41     if (peerCert == nullptr)
42     {
43         BMCWEB_LOG_DEBUG("Cannot get current TLS certificate.");
44         return nullptr;
45     }
46 
47     // Check if certificate is OK
48     int ctxError = X509_STORE_CTX_get_error(cts);
49     if (ctxError != X509_V_OK)
50     {
51         BMCWEB_LOG_INFO("Last TLS error is: {}", ctxError);
52         return nullptr;
53     }
54 
55     // Check that we have reached final certificate in chain
56     int32_t depth = X509_STORE_CTX_get_error_depth(cts);
57     if (depth != 0)
58     {
59         BMCWEB_LOG_DEBUG(
60             "Certificate verification in progress (depth {}), waiting to reach final depth",
61             depth);
62         return nullptr;
63     }
64 
65     BMCWEB_LOG_DEBUG("Certificate verification of final depth");
66 
67     if (X509_check_purpose(peerCert, X509_PURPOSE_SSL_CLIENT, 0) != 1)
68     {
69         BMCWEB_LOG_DEBUG(
70             "Chain does not allow certificate to be used for SSL client authentication");
71         return nullptr;
72     }
73 
74     std::string sslUser;
75     // Extract username contained in CommonName
76     sslUser.resize(256, '\0');
77 
78     int status = X509_NAME_get_text_by_NID(X509_get_subject_name(peerCert),
79                                            NID_commonName, sslUser.data(),
80                                            static_cast<int>(sslUser.size()));
81 
82     if (status == -1)
83     {
84         BMCWEB_LOG_DEBUG("TLS cannot get username to create session");
85         return nullptr;
86     }
87 
88     size_t lastChar = sslUser.find('\0');
89     if (lastChar == std::string::npos || lastChar == 0)
90     {
91         BMCWEB_LOG_DEBUG("Invalid TLS user name");
92         return nullptr;
93     }
94     sslUser.resize(lastChar);
95 
96     // Meta Inc. CommonName parsing
97     if constexpr (BMCWEB_MUTUAL_TLS_COMMON_NAME_PARSING == "meta")
98     {
99         std::optional<std::string_view> sslUserMeta =
100             mtlsMetaParseSslUser(sslUser);
101         if (!sslUserMeta)
102         {
103             return nullptr;
104         }
105         sslUser = *sslUserMeta;
106     }
107 
108     std::string unsupportedClientId;
109     return persistent_data::SessionStore::getInstance().generateUserSession(
110         sslUser, clientIp, unsupportedClientId,
111         persistent_data::PersistenceType::TIMEOUT);
112 }
113