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