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