1 #pragma once 2 3 #include "logging.hpp" 4 #include "persistent_data.hpp" 5 6 #include <openssl/crypto.h> 7 #include <openssl/ssl.h> 8 9 #include <boost/asio/ip/address.hpp> 10 #include <boost/asio/ssl/verify_context.hpp> 11 12 #include <memory> 13 #include <span> 14 15 inline std::shared_ptr<persistent_data::UserSession> 16 verifyMtlsUser(const boost::asio::ip::address& clientIp, 17 boost::asio::ssl::verify_context& ctx) 18 { 19 // do nothing if TLS is disabled 20 if (!persistent_data::SessionStore::getInstance() 21 .getAuthMethodsConfig() 22 .tls) 23 { 24 BMCWEB_LOG_DEBUG("TLS auth_config is disabled"); 25 return nullptr; 26 } 27 28 X509_STORE_CTX* cts = ctx.native_handle(); 29 if (cts == nullptr) 30 { 31 BMCWEB_LOG_DEBUG("Cannot get native TLS handle."); 32 return nullptr; 33 } 34 35 // Get certificate 36 X509* peerCert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); 37 if (peerCert == nullptr) 38 { 39 BMCWEB_LOG_DEBUG("Cannot get current TLS certificate."); 40 return nullptr; 41 } 42 43 // Check if certificate is OK 44 int ctxError = X509_STORE_CTX_get_error(cts); 45 if (ctxError != X509_V_OK) 46 { 47 BMCWEB_LOG_INFO("Last TLS error is: {}", ctxError); 48 return nullptr; 49 } 50 // Check that we have reached final certificate in chain 51 int32_t depth = X509_STORE_CTX_get_error_depth(cts); 52 if (depth != 0) 53 54 { 55 BMCWEB_LOG_DEBUG( 56 "Certificate verification in progress (depth {}), waiting to reach final depth", 57 depth); 58 return nullptr; 59 } 60 61 BMCWEB_LOG_DEBUG("Certificate verification of final depth"); 62 63 // Verify KeyUsage 64 bool isKeyUsageDigitalSignature = false; 65 bool isKeyUsageKeyAgreement = false; 66 67 ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>( 68 X509_get_ext_d2i(peerCert, NID_key_usage, nullptr, nullptr)); 69 70 if ((usage == nullptr) || (usage->data == nullptr)) 71 { 72 BMCWEB_LOG_DEBUG("TLS usage is null"); 73 return nullptr; 74 } 75 76 for (auto usageChar : 77 std::span(usage->data, static_cast<size_t>(usage->length))) 78 { 79 if (KU_DIGITAL_SIGNATURE & usageChar) 80 { 81 isKeyUsageDigitalSignature = true; 82 } 83 if (KU_KEY_AGREEMENT & usageChar) 84 { 85 isKeyUsageKeyAgreement = true; 86 } 87 } 88 ASN1_BIT_STRING_free(usage); 89 90 if (!isKeyUsageDigitalSignature || !isKeyUsageKeyAgreement) 91 { 92 BMCWEB_LOG_DEBUG("Certificate ExtendedKeyUsage does " 93 "not allow provided certificate to " 94 "be used for user authentication"); 95 return nullptr; 96 } 97 98 // Determine that ExtendedKeyUsage includes Client Auth 99 100 stack_st_ASN1_OBJECT* extUsage = static_cast<stack_st_ASN1_OBJECT*>( 101 X509_get_ext_d2i(peerCert, NID_ext_key_usage, nullptr, nullptr)); 102 103 if (extUsage == nullptr) 104 { 105 BMCWEB_LOG_DEBUG("TLS extUsage is null"); 106 return nullptr; 107 } 108 109 bool isExKeyUsageClientAuth = false; 110 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++) 111 { 112 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) 113 int nid = OBJ_obj2nid(sk_ASN1_OBJECT_value(extUsage, i)); 114 if (NID_client_auth == nid) 115 { 116 isExKeyUsageClientAuth = true; 117 break; 118 } 119 } 120 sk_ASN1_OBJECT_free(extUsage); 121 122 // Certificate has to have proper key usages set 123 if (!isExKeyUsageClientAuth) 124 { 125 BMCWEB_LOG_DEBUG("Certificate ExtendedKeyUsage does " 126 "not allow provided certificate to " 127 "be used for user authentication"); 128 return nullptr; 129 } 130 std::string sslUser; 131 // Extract username contained in CommonName 132 sslUser.resize(256, '\0'); 133 134 int status = X509_NAME_get_text_by_NID(X509_get_subject_name(peerCert), 135 NID_commonName, sslUser.data(), 136 static_cast<int>(sslUser.size())); 137 138 if (status == -1) 139 { 140 BMCWEB_LOG_DEBUG("TLS cannot get username to create session"); 141 return nullptr; 142 } 143 144 size_t lastChar = sslUser.find('\0'); 145 if (lastChar == std::string::npos || lastChar == 0) 146 { 147 BMCWEB_LOG_DEBUG("Invalid TLS user name"); 148 return nullptr; 149 } 150 sslUser.resize(lastChar); 151 std::string unsupportedClientId; 152 return persistent_data::SessionStore::getInstance().generateUserSession( 153 sslUser, clientIp, unsupportedClientId, 154 persistent_data::PersistenceType::TIMEOUT); 155 } 156