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
installCertificate(const std::filesystem::path & certPath)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
onPropertyUpdate(sd_bus_message * m,void *,sd_bus_error * retError)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
registerHostnameSignal()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