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