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