Refactor mtls callbacks into their own file

Mutual TLS is non-trivial enough that it definitely shouldn't be done in
an inline lambda method.  This commit moves the code.

Tested: WIP.  This is a pretty negligible code move;  Minimal touch
testing should be good.

Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I7a15a6bc66f4d8fb090411509549628f6d1045a5
diff --git a/http/http_connection.hpp b/http/http_connection.hpp
index efd4313..dc95b8e 100644
--- a/http/http_connection.hpp
+++ b/http/http_connection.hpp
@@ -21,6 +21,7 @@
 #include <boost/beast/websocket.hpp>
 #include <boost/url/url_view.hpp>
 #include <json_html_serializer.hpp>
+#include <mutual_tls.hpp>
 #include <security_headers.hpp>
 #include <ssl_key_handler.hpp>
 
@@ -53,6 +54,8 @@
 class Connection :
     public std::enable_shared_from_this<Connection<Adaptor, Handler>>
 {
+    using self_type = Connection<Adaptor, Handler>;
+
   public:
     Connection(Handler* handlerIn, boost::asio::steady_timer&& timerIn,
                std::function<std::string()>& getCachedDateStrF,
@@ -90,6 +93,22 @@
     Connection& operator=(const Connection&) = delete;
     Connection& operator=(Connection&&) = delete;
 
+    bool tlsVerifyCallback(bool preverified,
+                           boost::asio::ssl::verify_context& ctx)
+    {
+        // We always return true to allow full auth flow for resources that
+        // don't require auth
+        if (preverified)
+        {
+            userSession = verifyMtlsUser(req->ipAddress, ctx);
+            if (userSession)
+            {
+                sessionIsFromTransport = true;
+            }
+        }
+        return true;
+    }
+
     void prepareMutualTls()
     {
         std::error_code error;
@@ -116,167 +135,7 @@
         }
 
         adaptor.set_verify_callback(
-            [this](bool preverified, boost::asio::ssl::verify_context& ctx) {
-            // do nothing if TLS is disabled
-            if (!persistent_data::SessionStore::getInstance()
-                     .getAuthMethodsConfig()
-                     .tls)
-            {
-                BMCWEB_LOG_DEBUG << this << " TLS auth_config is disabled";
-                return true;
-            }
-
-            // We always return true to allow full auth flow
-            if (!preverified)
-            {
-                BMCWEB_LOG_DEBUG << this << " TLS preverification failed.";
-                return true;
-            }
-
-            X509_STORE_CTX* cts = ctx.native_handle();
-            if (cts == nullptr)
-            {
-                BMCWEB_LOG_DEBUG << this << " Cannot get native TLS handle.";
-                return true;
-            }
-
-            // Get certificate
-            X509* peerCert =
-                X509_STORE_CTX_get_current_cert(ctx.native_handle());
-            if (peerCert == nullptr)
-            {
-                BMCWEB_LOG_DEBUG << this
-                                 << " Cannot get current TLS certificate.";
-                return true;
-            }
-
-            // Check if certificate is OK
-            int ctxError = X509_STORE_CTX_get_error(cts);
-            if (ctxError != X509_V_OK)
-            {
-                BMCWEB_LOG_INFO << this << " Last TLS error is: " << ctxError;
-                return true;
-            }
-            // 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
-                    << this << " Certificate verification in progress (depth "
-                    << depth << "), waiting to reach final depth";
-                return true;
-            }
-
-            BMCWEB_LOG_DEBUG << this
-                             << " Certificate verification of final depth";
-
-            // Verify KeyUsage
-            bool isKeyUsageDigitalSignature = false;
-            bool isKeyUsageKeyAgreement = false;
-
-            ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>(
-                X509_get_ext_d2i(peerCert, NID_key_usage, nullptr, nullptr));
-
-            if (usage == nullptr)
-            {
-                BMCWEB_LOG_DEBUG << this << " TLS usage is null";
-                return true;
-            }
-
-            for (int i = 0; i < usage->length; i++)
-            {
-                // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
-                unsigned char usageChar = usage->data[i];
-                if (KU_DIGITAL_SIGNATURE & usageChar)
-                {
-                    isKeyUsageDigitalSignature = true;
-                }
-                if (KU_KEY_AGREEMENT & usageChar)
-                {
-                    isKeyUsageKeyAgreement = true;
-                }
-            }
-            ASN1_BIT_STRING_free(usage);
-
-            if (!isKeyUsageDigitalSignature || !isKeyUsageKeyAgreement)
-            {
-                BMCWEB_LOG_DEBUG << this
-                                 << " Certificate ExtendedKeyUsage does "
-                                    "not allow provided certificate to "
-                                    "be used for user authentication";
-                return true;
-            }
-
-            // Determine that ExtendedKeyUsage includes Client Auth
-
-            stack_st_ASN1_OBJECT* extUsage =
-                static_cast<stack_st_ASN1_OBJECT*>(X509_get_ext_d2i(
-                    peerCert, NID_ext_key_usage, nullptr, nullptr));
-
-            if (extUsage == nullptr)
-            {
-                BMCWEB_LOG_DEBUG << this << " TLS extUsage is null";
-                return true;
-            }
-
-            bool isExKeyUsageClientAuth = false;
-            for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
-            {
-                // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
-                int nid = OBJ_obj2nid(sk_ASN1_OBJECT_value(extUsage, i));
-                if (NID_client_auth == nid)
-                {
-                    isExKeyUsageClientAuth = true;
-                    break;
-                }
-            }
-            sk_ASN1_OBJECT_free(extUsage);
-
-            // Certificate has to have proper key usages set
-            if (!isExKeyUsageClientAuth)
-            {
-                BMCWEB_LOG_DEBUG << this
-                                 << " Certificate ExtendedKeyUsage does "
-                                    "not allow provided certificate to "
-                                    "be used for user authentication";
-                return true;
-            }
-            std::string sslUser;
-            // Extract username contained in CommonName
-            sslUser.resize(256, '\0');
-
-            int status = X509_NAME_get_text_by_NID(
-                X509_get_subject_name(peerCert), NID_commonName, sslUser.data(),
-                static_cast<int>(sslUser.size()));
-
-            if (status == -1)
-            {
-                BMCWEB_LOG_DEBUG
-                    << this << " TLS cannot get username to create session";
-                return true;
-            }
-
-            size_t lastChar = sslUser.find('\0');
-            if (lastChar == std::string::npos || lastChar == 0)
-            {
-                BMCWEB_LOG_DEBUG << this << " Invalid TLS user name";
-                return true;
-            }
-            sslUser.resize(lastChar);
-            sessionIsFromTransport = true;
-            userSession = persistent_data::SessionStore::getInstance()
-                              .generateUserSession(
-                                  sslUser, req->ipAddress, std::nullopt,
-                                  persistent_data::PersistenceType::TIMEOUT);
-            if (userSession != nullptr)
-            {
-                BMCWEB_LOG_DEBUG
-                    << this
-                    << " Generating TLS session: " << userSession->uniqueId;
-            }
-            return true;
-        });
+            std::bind_front(&self_type::tlsVerifyCallback, this));
     }
 
     Adaptor& socket()
diff --git a/http/mutual_tls.hpp b/http/mutual_tls.hpp
new file mode 100644
index 0000000..aee85c2
--- /dev/null
+++ b/http/mutual_tls.hpp
@@ -0,0 +1,154 @@
+#pragma once
+
+#include "logging.hpp"
+#include "persistent_data.hpp"
+
+#include <openssl/crypto.h>
+#include <openssl/ssl.h>
+
+#include <boost/asio/ip/address.hpp>
+#include <boost/asio/ssl/verify_context.hpp>
+
+#include <memory>
+
+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* peerCert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
+    if (peerCert == nullptr)
+    {
+        BMCWEB_LOG_DEBUG << "Cannot get current TLS certificate.";
+        return nullptr;
+    }
+
+    // Check if certificate is OK
+    int ctxError = X509_STORE_CTX_get_error(cts);
+    if (ctxError != X509_V_OK)
+    {
+        BMCWEB_LOG_INFO << "Last TLS error is: " << ctxError;
+        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 isKeyUsageDigitalSignature = false;
+    bool isKeyUsageKeyAgreement = false;
+
+    ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>(
+        X509_get_ext_d2i(peerCert, 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 usageChar = usage->data[i];
+        if (KU_DIGITAL_SIGNATURE & usageChar)
+        {
+            isKeyUsageDigitalSignature = true;
+        }
+        if (KU_KEY_AGREEMENT & usageChar)
+        {
+            isKeyUsageKeyAgreement = true;
+        }
+    }
+    ASN1_BIT_STRING_free(usage);
+
+    if (!isKeyUsageDigitalSignature || !isKeyUsageKeyAgreement)
+    {
+        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* extUsage = static_cast<stack_st_ASN1_OBJECT*>(
+        X509_get_ext_d2i(peerCert, NID_ext_key_usage, nullptr, nullptr));
+
+    if (extUsage == nullptr)
+    {
+        BMCWEB_LOG_DEBUG << "TLS extUsage is null";
+        return nullptr;
+    }
+
+    bool isExKeyUsageClientAuth = false;
+    for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
+    {
+        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
+        int nid = OBJ_obj2nid(sk_ASN1_OBJECT_value(extUsage, i));
+        if (NID_client_auth == nid)
+        {
+            isExKeyUsageClientAuth = true;
+            break;
+        }
+    }
+    sk_ASN1_OBJECT_free(extUsage);
+
+    // Certificate has to have proper key usages set
+    if (!isExKeyUsageClientAuth)
+    {
+        BMCWEB_LOG_DEBUG << "Certificate ExtendedKeyUsage does "
+                            "not allow provided certificate to "
+                            "be used for user authentication";
+        return nullptr;
+    }
+    std::string sslUser;
+    // Extract username contained in CommonName
+    sslUser.resize(256, '\0');
+
+    int status = X509_NAME_get_text_by_NID(X509_get_subject_name(peerCert),
+                                           NID_commonName, sslUser.data(),
+                                           static_cast<int>(sslUser.size()));
+
+    if (status == -1)
+    {
+        BMCWEB_LOG_DEBUG << "TLS cannot get username to create session";
+        return nullptr;
+    }
+
+    size_t lastChar = sslUser.find('\0');
+    if (lastChar == std::string::npos || lastChar == 0)
+    {
+        BMCWEB_LOG_DEBUG << "Invalid TLS user name";
+        return nullptr;
+    }
+    sslUser.resize(lastChar);
+    std::string unsupportedClientId;
+    return persistent_data::SessionStore::getInstance().generateUserSession(
+        sslUser, clientIp, unsupportedClientId,
+        persistent_data::PersistenceType::TIMEOUT);
+}