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