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