xref: /openbmc/bmcweb/include/hostname_monitor.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #pragma once
4 #include "dbus_singleton.hpp"
5 #include "dbus_utility.hpp"
6 #include "include/dbus_utility.hpp"
7 #include "logging.hpp"
8 #include "ssl_key_handler.hpp"
9 
10 #include <sdbusplus/bus/match.hpp>
11 #include <sdbusplus/message/types.hpp>
12 
13 namespace crow
14 {
15 namespace hostname_monitor
16 {
17 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
18 static std::unique_ptr<sdbusplus::bus::match_t> hostnameSignalMonitor;
19 
20 inline void installCertificate(const std::filesystem::path& certPath)
21 {
22     crow::connections::systemBus->async_method_call(
23         [certPath](const boost::system::error_code& ec) {
24             if (ec)
25             {
26                 BMCWEB_LOG_ERROR("Replace Certificate Fail..");
27                 return;
28             }
29 
30             BMCWEB_LOG_INFO("Replace HTTPs Certificate Success, "
31                             "remove temporary certificate file..");
32             std::error_code ec2;
33             std::filesystem::remove(certPath.c_str(), ec2);
34             if (ec2)
35             {
36                 BMCWEB_LOG_ERROR("Failed to remove certificate");
37             }
38         },
39         "xyz.openbmc_project.Certs.Manager.Server.Https",
40         "/xyz/openbmc_project/certs/server/https/1",
41         "xyz.openbmc_project.Certs.Replace", "Replace", certPath.string());
42 }
43 
44 inline int onPropertyUpdate(sd_bus_message* m, void* /* userdata */,
45                             sd_bus_error* retError)
46 {
47     if (retError == nullptr || (sd_bus_error_is_set(retError) != 0))
48     {
49         BMCWEB_LOG_ERROR("Got sdbus error on match");
50         return 0;
51     }
52 
53     sdbusplus::message_t message(m);
54     std::string iface;
55     dbus::utility::DBusPropertiesMap changedProperties;
56 
57     message.read(iface, changedProperties);
58     const std::string* hostname = nullptr;
59     for (const auto& propertyPair : changedProperties)
60     {
61         if (propertyPair.first == "HostName")
62         {
63             hostname = std::get_if<std::string>(&propertyPair.second);
64         }
65     }
66     if (hostname == nullptr)
67     {
68         return 0;
69     }
70 
71     BMCWEB_LOG_DEBUG("Read hostname from signal: {}", *hostname);
72     const std::string certFile = "/etc/ssl/certs/https/server.pem";
73 
74     X509* cert = ensuressl::loadCert(certFile);
75     if (cert == nullptr)
76     {
77         BMCWEB_LOG_ERROR("Failed to read cert");
78         return 0;
79     }
80 
81     const int maxKeySize = 256;
82     std::array<char, maxKeySize> cnBuffer{};
83 
84     int cnLength =
85         X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName,
86                                   cnBuffer.data(), cnBuffer.size());
87     if (cnLength == -1)
88     {
89         BMCWEB_LOG_ERROR("Failed to read NID_commonName");
90         X509_free(cert);
91         return 0;
92     }
93     std::string_view cnValue(std::begin(cnBuffer),
94                              static_cast<size_t>(cnLength));
95 
96     EVP_PKEY* pPubKey = X509_get_pubkey(cert);
97     if (pPubKey == nullptr)
98     {
99         BMCWEB_LOG_ERROR("Failed to get public key");
100         X509_free(cert);
101         return 0;
102     }
103     int isSelfSigned = X509_verify(cert, pPubKey);
104     EVP_PKEY_free(pPubKey);
105 
106     BMCWEB_LOG_DEBUG(
107         "Current HTTPs Certificate Subject CN: {}, New HostName: {}, isSelfSigned: {}",
108         cnValue, *hostname, isSelfSigned);
109 
110     ASN1_IA5STRING* asn1 = static_cast<ASN1_IA5STRING*>(
111         X509_get_ext_d2i(cert, NID_netscape_comment, nullptr, nullptr));
112     if (asn1 != nullptr)
113     {
114         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
115         std::string_view comment(reinterpret_cast<const char*>(asn1->data),
116                                  static_cast<size_t>(asn1->length));
117         BMCWEB_LOG_DEBUG("x509Comment: {}", comment);
118 
119         if (ensuressl::x509Comment == comment && isSelfSigned == 1 &&
120             cnValue != *hostname)
121         {
122             BMCWEB_LOG_INFO(
123                 "Ready to generate new HTTPs certificate with subject cn: {}",
124                 *hostname);
125 
126             std::string certData = ensuressl::generateSslCertificate(*hostname);
127             if (certData.empty())
128             {
129                 BMCWEB_LOG_ERROR("Failed to generate cert");
130                 return 0;
131             }
132             ensuressl::writeCertificateToFile("/tmp/hostname_cert.tmp",
133                                               certData);
134 
135             installCertificate("/tmp/hostname_cert.tmp");
136         }
137         ASN1_STRING_free(asn1);
138     }
139     X509_free(cert);
140     return 0;
141 }
142 
143 inline void registerHostnameSignal()
144 {
145     BMCWEB_LOG_INFO("Register HostName PropertiesChanged Signal");
146     std::string propertiesMatchString =
147         ("type='signal',"
148          "interface='org.freedesktop.DBus.Properties',"
149          "path='/xyz/openbmc_project/network/config',"
150          "arg0='xyz.openbmc_project.Network.SystemConfiguration',"
151          "member='PropertiesChanged'");
152 
153     hostnameSignalMonitor = std::make_unique<sdbusplus::bus::match_t>(
154         *crow::connections::systemBus, propertiesMatchString, onPropertyUpdate,
155         nullptr);
156 }
157 } // namespace hostname_monitor
158 } // namespace crow
159