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