| #ifndef THIRD_PARTY_GBMCWEB_HTTP_MUTUAL_TLS_H_ |
| #define THIRD_PARTY_GBMCWEB_HTTP_MUTUAL_TLS_H_ |
| |
| #ifdef GOOGLE3_GBMCWEB_BUILD |
| #include <openssl/asn1.h> |
| #include <openssl/nid.h> |
| #include <openssl/obj.h> |
| #include <openssl/x509.h> |
| #include <openssl/x509v3.h> |
| #endif |
| |
| #include <openssl/crypto.h> |
| #include <openssl/ssl.h> |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| |
| #include "boost/asio/ip/address.hpp" // NOLINT |
| #include "boost/asio/ssl/verify_context.hpp" // NOLINT |
| #include "logging.hpp" |
| #include "sessions.hpp" |
| |
| inline std::shared_ptr<persistent_data::UserSession> verifyMtlsUser( |
| const boost::asio::ip::address& clientIp, |
| boost::asio::ssl::verify_context& ctx) { |
| // do nothing if TLS is disabled |
| if (!persistent_data::SessionStore::getInstance() |
| .getAuthMethodsConfig() |
| .tls) { |
| BMCWEB_LOG_DEBUG << "TLS auth_config is disabled"; |
| return nullptr; |
| } |
| |
| X509_STORE_CTX* cts = ctx.native_handle(); |
| if (cts == nullptr) { |
| BMCWEB_LOG_DEBUG << "Cannot get native TLS handle."; |
| return nullptr; |
| } |
| |
| // Get certificate |
| X509* peer_cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); |
| if (peer_cert == nullptr) { |
| BMCWEB_LOG_DEBUG << "Cannot get current TLS certificate."; |
| return nullptr; |
| } |
| |
| // Check if certificate is OK |
| int ctx_error = X509_STORE_CTX_get_error(cts); |
| if (ctx_error != X509_V_OK) { |
| BMCWEB_LOG_INFO << "Last TLS error is: " << ctx_error; |
| return nullptr; |
| } |
| // Check that we have reached final certificate in chain |
| int32_t depth = X509_STORE_CTX_get_error_depth(cts); |
| if (depth != 0) { |
| BMCWEB_LOG_DEBUG << "Certificate verification in progress (depth " << depth |
| << "), waiting to reach final depth"; |
| return nullptr; |
| } |
| |
| BMCWEB_LOG_DEBUG << "Certificate verification of final depth"; |
| |
| // Verify KeyUsage |
| bool is_key_usage_digital_signature = false; |
| bool is_key_usage_key_agreement = false; |
| |
| ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>( |
| X509_get_ext_d2i(peer_cert, NID_key_usage, nullptr, nullptr)); |
| |
| if (usage == nullptr) { |
| BMCWEB_LOG_DEBUG << "TLS usage is null"; |
| return nullptr; |
| } |
| |
| for (int i = 0; i < usage->length; i++) { |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
| unsigned char usage_char = usage->data[i]; |
| if (KU_DIGITAL_SIGNATURE & usage_char) { |
| is_key_usage_digital_signature = true; |
| } |
| if (KU_KEY_AGREEMENT & usage_char) { |
| is_key_usage_key_agreement = true; |
| } |
| } |
| ASN1_BIT_STRING_free(usage); |
| |
| if (!is_key_usage_digital_signature || !is_key_usage_key_agreement) { |
| BMCWEB_LOG_DEBUG << "Certificate ExtendedKeyUsage does " |
| "not allow provided certificate to " |
| "be used for user authentication"; |
| return nullptr; |
| } |
| |
| // Determine that ExtendedKeyUsage includes Client Auth |
| |
| stack_st_ASN1_OBJECT* ext_usage = static_cast<stack_st_ASN1_OBJECT*>( |
| X509_get_ext_d2i(peer_cert, NID_ext_key_usage, nullptr, nullptr)); |
| |
| if (ext_usage == nullptr) { |
| BMCWEB_LOG_DEBUG << "TLS ext_usage is null"; |
| return nullptr; |
| } |
| |
| bool is_ex_key_usage_client_auth = false; |
| for (int i = 0; i < sk_ASN1_OBJECT_num(ext_usage); i++) { |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) |
| int nid = OBJ_obj2nid(sk_ASN1_OBJECT_value(ext_usage, i)); |
| if (NID_client_auth == nid) { |
| is_ex_key_usage_client_auth = true; |
| break; |
| } |
| } |
| sk_ASN1_OBJECT_free(ext_usage); |
| |
| // Certificate has to have proper key usages set |
| if (!is_ex_key_usage_client_auth) { |
| BMCWEB_LOG_DEBUG << "Certificate ExtendedKeyUsage does " |
| "not allow provided certificate to " |
| "be used for user authentication"; |
| return nullptr; |
| } |
| std::string ssl_user; |
| // Extract username contained in CommonName |
| ssl_user.resize(256, '\0'); |
| |
| int status = X509_NAME_get_text_by_NID(X509_get_subject_name(peer_cert), |
| NID_commonName, ssl_user.data(), |
| static_cast<int>(ssl_user.size())); |
| |
| if (status == -1) { |
| BMCWEB_LOG_DEBUG << "TLS cannot get username to create session"; |
| return nullptr; |
| } |
| |
| size_t last_char = ssl_user.find('\0'); |
| if (last_char == std::string::npos || last_char == 0) { |
| BMCWEB_LOG_DEBUG << "Invalid TLS user name"; |
| return nullptr; |
| } |
| ssl_user.resize(last_char); |
| std::string unsupported_client_id; |
| return persistent_data::SessionStore::getInstance().generateUserSession( |
| ssl_user, clientIp, unsupported_client_id, |
| persistent_data::PersistenceType::TIMEOUT); |
| } |
| |
| #endif // THIRD_PARTY_GBMCWEB_HTTP_MUTUAL_TLS_H_ |