xref: /openbmc/bmcweb/include/hostname_monitor.hpp (revision d78572018fc2022091ff8b8eb5a7fef2172ba3d6)
140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3a8220708SAlan Kuo #pragma once
43ccb3adbSEd Tanous #include "dbus_singleton.hpp"
53ccb3adbSEd Tanous #include "dbus_utility.hpp"
63ccb3adbSEd Tanous #include "include/dbus_utility.hpp"
73ccb3adbSEd Tanous #include "logging.hpp"
83ccb3adbSEd Tanous #include "ssl_key_handler.hpp"
93ccb3adbSEd Tanous 
10*d7857201SEd Tanous #include <openssl/asn1.h>
11*d7857201SEd Tanous #include <openssl/crypto.h>
12*d7857201SEd Tanous #include <openssl/evp.h>
13*d7857201SEd Tanous #include <openssl/obj_mac.h>
14*d7857201SEd Tanous #include <openssl/x509.h>
15*d7857201SEd Tanous #include <systemd/sd-bus.h>
16*d7857201SEd Tanous 
17a8220708SAlan Kuo #include <sdbusplus/bus/match.hpp>
18*d7857201SEd Tanous #include <sdbusplus/message.hpp>
19*d7857201SEd Tanous 
20*d7857201SEd Tanous #include <array>
21*d7857201SEd Tanous #include <cstddef>
22*d7857201SEd Tanous #include <filesystem>
23*d7857201SEd Tanous #include <iterator>
24*d7857201SEd Tanous #include <memory>
25*d7857201SEd Tanous #include <string_view>
26*d7857201SEd Tanous #include <system_error>
27*d7857201SEd Tanous #include <variant>
28a8220708SAlan Kuo 
29a8220708SAlan Kuo namespace crow
30a8220708SAlan Kuo {
31a8220708SAlan Kuo namespace hostname_monitor
32a8220708SAlan Kuo {
33cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
3459d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> hostnameSignalMonitor;
35a8220708SAlan Kuo 
installCertificate(const std::filesystem::path & certPath)36a8220708SAlan Kuo inline void installCertificate(const std::filesystem::path& certPath)
37a8220708SAlan Kuo {
38a8220708SAlan Kuo     crow::connections::systemBus->async_method_call(
395e7e2dc5SEd Tanous         [certPath](const boost::system::error_code& ec) {
40a8220708SAlan Kuo             if (ec)
41a8220708SAlan Kuo             {
4262598e31SEd Tanous                 BMCWEB_LOG_ERROR("Replace Certificate Fail..");
43a8220708SAlan Kuo                 return;
44a8220708SAlan Kuo             }
45a8220708SAlan Kuo 
4662598e31SEd Tanous             BMCWEB_LOG_INFO("Replace HTTPs Certificate Success, "
4762598e31SEd Tanous                             "remove temporary certificate file..");
484c521c3cSEd Tanous             std::error_code ec2;
494c521c3cSEd Tanous             std::filesystem::remove(certPath.c_str(), ec2);
504c521c3cSEd Tanous             if (ec2)
514c521c3cSEd Tanous             {
524c521c3cSEd Tanous                 BMCWEB_LOG_ERROR("Failed to remove certificate");
534c521c3cSEd Tanous             }
54a8220708SAlan Kuo         },
55a8220708SAlan Kuo         "xyz.openbmc_project.Certs.Manager.Server.Https",
56a8220708SAlan Kuo         "/xyz/openbmc_project/certs/server/https/1",
57a8220708SAlan Kuo         "xyz.openbmc_project.Certs.Replace", "Replace", certPath.string());
58a8220708SAlan Kuo }
59a8220708SAlan Kuo 
onPropertyUpdate(sd_bus_message * m,void *,sd_bus_error * retError)60a8220708SAlan Kuo inline int onPropertyUpdate(sd_bus_message* m, void* /* userdata */,
6181ce609eSEd Tanous                             sd_bus_error* retError)
62a8220708SAlan Kuo {
63e662eae8SEd Tanous     if (retError == nullptr || (sd_bus_error_is_set(retError) != 0))
64a8220708SAlan Kuo     {
6562598e31SEd Tanous         BMCWEB_LOG_ERROR("Got sdbus error on match");
66a8220708SAlan Kuo         return 0;
67a8220708SAlan Kuo     }
68a8220708SAlan Kuo 
6959d494eeSPatrick Williams     sdbusplus::message_t message(m);
70a8220708SAlan Kuo     std::string iface;
71b9d36b47SEd Tanous     dbus::utility::DBusPropertiesMap changedProperties;
72a8220708SAlan Kuo 
73a8220708SAlan Kuo     message.read(iface, changedProperties);
74b9d36b47SEd Tanous     const std::string* hostname = nullptr;
75b9d36b47SEd Tanous     for (const auto& propertyPair : changedProperties)
76a8220708SAlan Kuo     {
77b9d36b47SEd Tanous         if (propertyPair.first == "HostName")
78b9d36b47SEd Tanous         {
79b9d36b47SEd Tanous             hostname = std::get_if<std::string>(&propertyPair.second);
80a8220708SAlan Kuo         }
81b9d36b47SEd Tanous     }
82a8220708SAlan Kuo     if (hostname == nullptr)
83a8220708SAlan Kuo     {
84a8220708SAlan Kuo         return 0;
85a8220708SAlan Kuo     }
86a8220708SAlan Kuo 
8762598e31SEd Tanous     BMCWEB_LOG_DEBUG("Read hostname from signal: {}", *hostname);
88a8220708SAlan Kuo     const std::string certFile = "/etc/ssl/certs/https/server.pem";
89a8220708SAlan Kuo 
90a8220708SAlan Kuo     X509* cert = ensuressl::loadCert(certFile);
91a8220708SAlan Kuo     if (cert == nullptr)
92a8220708SAlan Kuo     {
9362598e31SEd Tanous         BMCWEB_LOG_ERROR("Failed to read cert");
94a8220708SAlan Kuo         return 0;
95a8220708SAlan Kuo     }
96a8220708SAlan Kuo 
97a8220708SAlan Kuo     const int maxKeySize = 256;
98a8220708SAlan Kuo     std::array<char, maxKeySize> cnBuffer{};
99a8220708SAlan Kuo 
100bd79bce8SPatrick Williams     int cnLength =
101bd79bce8SPatrick Williams         X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName,
102bd79bce8SPatrick Williams                                   cnBuffer.data(), cnBuffer.size());
103a8220708SAlan Kuo     if (cnLength == -1)
104a8220708SAlan Kuo     {
10562598e31SEd Tanous         BMCWEB_LOG_ERROR("Failed to read NID_commonName");
106a8220708SAlan Kuo         X509_free(cert);
107a8220708SAlan Kuo         return 0;
108a8220708SAlan Kuo     }
109a8220708SAlan Kuo     std::string_view cnValue(std::begin(cnBuffer),
110a8220708SAlan Kuo                              static_cast<size_t>(cnLength));
111a8220708SAlan Kuo 
112a8220708SAlan Kuo     EVP_PKEY* pPubKey = X509_get_pubkey(cert);
113a8220708SAlan Kuo     if (pPubKey == nullptr)
114a8220708SAlan Kuo     {
11562598e31SEd Tanous         BMCWEB_LOG_ERROR("Failed to get public key");
116a8220708SAlan Kuo         X509_free(cert);
117a8220708SAlan Kuo         return 0;
118a8220708SAlan Kuo     }
119a8220708SAlan Kuo     int isSelfSigned = X509_verify(cert, pPubKey);
120a8220708SAlan Kuo     EVP_PKEY_free(pPubKey);
121a8220708SAlan Kuo 
12262598e31SEd Tanous     BMCWEB_LOG_DEBUG(
12362598e31SEd Tanous         "Current HTTPs Certificate Subject CN: {}, New HostName: {}, isSelfSigned: {}",
12462598e31SEd Tanous         cnValue, *hostname, isSelfSigned);
125a8220708SAlan Kuo 
126a8220708SAlan Kuo     ASN1_IA5STRING* asn1 = static_cast<ASN1_IA5STRING*>(
127a8220708SAlan Kuo         X509_get_ext_d2i(cert, NID_netscape_comment, nullptr, nullptr));
128e662eae8SEd Tanous     if (asn1 != nullptr)
129a8220708SAlan Kuo     {
13046ff87baSEd Tanous         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
131a8220708SAlan Kuo         std::string_view comment(reinterpret_cast<const char*>(asn1->data),
132a8220708SAlan Kuo                                  static_cast<size_t>(asn1->length));
13362598e31SEd Tanous         BMCWEB_LOG_DEBUG("x509Comment: {}", comment);
134a8220708SAlan Kuo 
135a8220708SAlan Kuo         if (ensuressl::x509Comment == comment && isSelfSigned == 1 &&
136a8220708SAlan Kuo             cnValue != *hostname)
137a8220708SAlan Kuo         {
13862598e31SEd Tanous             BMCWEB_LOG_INFO(
13962598e31SEd Tanous                 "Ready to generate new HTTPs certificate with subject cn: {}",
14062598e31SEd Tanous                 *hostname);
141a8220708SAlan Kuo 
142099225ccSEd Tanous             std::string certData = ensuressl::generateSslCertificate(*hostname);
143099225ccSEd Tanous             if (certData.empty())
144099225ccSEd Tanous             {
145099225ccSEd Tanous                 BMCWEB_LOG_ERROR("Failed to generate cert");
146099225ccSEd Tanous                 return 0;
147099225ccSEd Tanous             }
148099225ccSEd Tanous             ensuressl::writeCertificateToFile("/tmp/hostname_cert.tmp",
149099225ccSEd Tanous                                               certData);
150099225ccSEd Tanous 
151a8220708SAlan Kuo             installCertificate("/tmp/hostname_cert.tmp");
152a8220708SAlan Kuo         }
153a8220708SAlan Kuo         ASN1_STRING_free(asn1);
154a8220708SAlan Kuo     }
155a8220708SAlan Kuo     X509_free(cert);
156a8220708SAlan Kuo     return 0;
157a8220708SAlan Kuo }
158a8220708SAlan Kuo 
registerHostnameSignal()159a8220708SAlan Kuo inline void registerHostnameSignal()
160a8220708SAlan Kuo {
16162598e31SEd Tanous     BMCWEB_LOG_INFO("Register HostName PropertiesChanged Signal");
162a8220708SAlan Kuo     std::string propertiesMatchString =
163a8220708SAlan Kuo         ("type='signal',"
164a8220708SAlan Kuo          "interface='org.freedesktop.DBus.Properties',"
165a8220708SAlan Kuo          "path='/xyz/openbmc_project/network/config',"
166a8220708SAlan Kuo          "arg0='xyz.openbmc_project.Network.SystemConfiguration',"
167a8220708SAlan Kuo          "member='PropertiesChanged'");
168a8220708SAlan Kuo 
16959d494eeSPatrick Williams     hostnameSignalMonitor = std::make_unique<sdbusplus::bus::match_t>(
170a8220708SAlan Kuo         *crow::connections::systemBus, propertiesMatchString, onPropertyUpdate,
171a8220708SAlan Kuo         nullptr);
172a8220708SAlan Kuo }
173a8220708SAlan Kuo } // namespace hostname_monitor
174a8220708SAlan Kuo } // namespace crow
175