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