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 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> 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