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 36 inline void installCertificate(const std::filesystem::path& certPath) 37 { 38 dbus::utility::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 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 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