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