summaryrefslogtreecommitdiff
path: root/http/mutual_tls.hpp
blob: 9cd4cde01cbd0448966201cf47fe3c67e4594cf8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#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>
#include <span>

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 {}), waiting to reach final depth",
            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) || (usage->data == nullptr))
    {
        BMCWEB_LOG_DEBUG("TLS usage is null");
        return nullptr;
    }

    for (auto usageChar :
         std::span(usage->data, static_cast<size_t>(usage->length)))
    {
        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);
}