mutual-tls: Add support for Meta certificates

Meta Inc's client certificates use an internal Subject CN format
which AFAIK is specific to Meta and don't adhere to a known standard:

  Subject: CN = <type>:<entity>/<hostname>

Commit adds the `mutual-tls-common-name-parsing=meta` option to, on
Meta builds, parse the Subject CN field and map either the <entity>
to a local user.

The <type> field determines what kind of client identity the cert
represents. Only type="user" is supported for now with <entity> being
the unixname of a Meta employee. For example, the Subject CN string
below maps to a local BMC user named "kawmarco":

   Subject CN =  "user:kawmarco/dev123.facebook.com"

Tested: Unit tests, built and tested on romulus using the script below:
https://gist.github.com/kawmarco/87170a8250020023d913ed5f7ed5c01f

Flags used in meta-ibm/meta-romulus/conf/layer.conf :
```
-Dbmcweb-logging='enabled'
-Dmutual-tls-common-name-parsing='meta'
```

Change-Id: I35ee9b92d163ce56815a5bd9cce5296ba1a44eef
Signed-off-by: Marco Kawajiri <kawajiri@meta.com>
diff --git a/http/mutual_tls_meta.hpp b/http/mutual_tls_meta.hpp
new file mode 100644
index 0000000..5e55db8
--- /dev/null
+++ b/http/mutual_tls_meta.hpp
@@ -0,0 +1,73 @@
+#pragma once
+
+#include "logging.hpp"
+
+#include <format>
+#include <optional>
+#include <string>
+#include <string_view>
+
+inline std::optional<std::string_view>
+    mtlsMetaParseSslUser(std::string_view sslUser)
+{
+    // Parses a Meta internal TLS client certificate Subject CN in
+    // '<entityType>:<entity>[/<hostname>]' format and returns the resulting
+    // POSIX-compatible local user name on success, null otherwise.
+    //
+    // Only entityType = "user" is supported for now.
+    //
+    // Example client subject CN -> local user name:
+    // "user:a_username/hostname" -> "a_username"
+
+    // Parse entityType
+    size_t colonIndex = sslUser.find(':');
+    if (colonIndex == std::string_view::npos)
+    {
+        BMCWEB_LOG_WARNING("Invalid Meta TLS client cert Subject CN: '{}'",
+                           sslUser);
+        return std::nullopt;
+    }
+
+    std::string_view entityType = sslUser.substr(0, colonIndex);
+    sslUser.remove_prefix(colonIndex + 1);
+    if (entityType != "user")
+    {
+        BMCWEB_LOG_WARNING(
+            "Invalid/unsupported entityType='{}' in Meta TLS client cert Subject CN: '{}'",
+            entityType, sslUser);
+        return std::nullopt;
+    }
+
+    // Parse entity
+    size_t slashIndex = sslUser.find('/');
+    std::string_view entity;
+    if (slashIndex == std::string_view::npos)
+    {
+        // No '/' character, Subject CN is just '<entityType>:<entity>'
+        entity = sslUser;
+    }
+    else
+    {
+        // Subject CN ends with /<hostname>
+        entity = sslUser.substr(0, slashIndex);
+        sslUser.remove_prefix(slashIndex + 1);
+
+        if (entity.find_first_not_of(
+                "abcdefghijklmnopqrstuvwxyz0123456789_-.") != std::string::npos)
+        {
+            BMCWEB_LOG_WARNING(
+                "Invalid entity='{}' in Meta TLS client cert Subject CN: '{}'",
+                entity, sslUser);
+            return std::nullopt;
+        }
+    }
+
+    if (entity.empty())
+    {
+        BMCWEB_LOG_DEBUG("Invalid Meta TLS client cert Subject CN: '{}'",
+                         sslUser);
+        return std::nullopt;
+    }
+
+    return entity;
+}