blob: 3c600dc5a533c7c5a26dc8a71b62745187a68099 [file] [log] [blame]
#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_