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