summaryrefslogtreecommitdiff
path: root/include/hostname_monitor.hpp
diff options
context:
space:
mode:
authorAlan Kuo <Alan_Kuo@quantatw.com>2020-11-26 06:15:29 +0300
committerEd Tanous <ed@tanous.net>2020-12-12 04:46:28 +0300
commita82207087d79c4dd85447bdacfd4de91be4e7166 (patch)
treee452ce36b3df5889bafeae2f163a7f599c02d398 /include/hostname_monitor.hpp
parent9e319cf0c5ee714cdd879a1d2a6e5b5ac96c7f1d (diff)
downloadbmcweb-a82207087d79c4dd85447bdacfd4de91be4e7166.tar.xz
Add hostname listener for generating self-signed HTTPS certificate
- Add a hostname listener that will create a self-signed HTTPS certificate with the appropriate subject when the BMC gets its hostname assigned via IPMI. The "insecure-disable-ssl" must be disabled for this feature to take effect. Note: - New self-signed certificate subject: C=US, O=OpenBMC, CN=${hostname} - If the same hostname is assigned, it will not be triggered - Only the self-signed certificate with Netscape Comment of "Generated from OpenBMC service" will be replaced Details about certificate key usage: - NID_basic_constraints The CA boolean indicates whether the certified public key may be used to verify certificate signatures. Refer to: https://tools.ietf.org/html/rfc5280#section-4.2.1.9 - NID_subject_alt_name Although the use of the Common Name is existing practice, it is deprecated and Certification Authorities are encouraged to use the dNSName instead. Refer to: https://tools.ietf.org/html/rfc2818#section-3.1 - NID_subject_key_identifier The subject key identifier extension provides a means of identifying certificates that contain a particular public key. Refer to: https://tools.ietf.org/html/rfc5280#section-4.2.1.2 - NID_authority_key_identifier The authority key identifier extension provides a means of identifying the public key corresponding to the private key used to sign a certificate. Refer to: https://tools.ietf.org/html/rfc5280#section-4.2.1.1 - NID_key_usage - NID_ext_key_usage id-kp-serverAuth -- TLS WWW server authentication -- Key usage bits that may be consistent: digitalSignature, -- keyEncipherment or keyAgreement Refer to: https://tools.ietf.org/html/rfc5280#section-4.2.1.3 Refer to: https://tools.ietf.org/html/rfc5280#section-4.2.1.12 Tested: - To test and verify the service is functionally working correctly, we can use `openssl` and `ipmitool` to execute the following commands: - Assign BMC hostname ipmitool -H $IP -I lanplus -U root -P 0penBmc -C 17 dcmi set_mc_id_string $hostname - Get BMC server certificate infomation echo quit | openssl s_client -showcerts -servername $IP -connect $IP:443 Signed-off-by: Alan Kuo <Alan_Kuo@quantatw.com> Change-Id: I24aeb4d2fb46ff5f0cc1c6aa65984f46b0e1d3e2
Diffstat (limited to 'include/hostname_monitor.hpp')
-rw-r--r--include/hostname_monitor.hpp141
1 files changed, 141 insertions, 0 deletions
diff --git a/include/hostname_monitor.hpp b/include/hostname_monitor.hpp
new file mode 100644
index 0000000000..7b8283e06d
--- /dev/null
+++ b/include/hostname_monitor.hpp
@@ -0,0 +1,141 @@
+#pragma once
+#ifdef BMCWEB_ENABLE_SSL
+#include <boost/container/flat_map.hpp>
+#include <dbus_singleton.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/message/types.hpp>
+#include <ssl_key_handler.hpp>
+
+namespace crow
+{
+namespace hostname_monitor
+{
+static std::unique_ptr<sdbusplus::bus::match::match> hostnameSignalMonitor;
+
+inline void installCertificate(const std::filesystem::path& certPath)
+{
+ crow::connections::systemBus->async_method_call(
+ [certPath](boost::system::error_code ec) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "Replace Certificate Fail..";
+ return;
+ }
+
+ BMCWEB_LOG_INFO << "Replace HTTPs Certificate Success, "
+ "remove temporary certificate file..";
+ remove(certPath.c_str());
+ },
+ "xyz.openbmc_project.Certs.Manager.Server.Https",
+ "/xyz/openbmc_project/certs/server/https/1",
+ "xyz.openbmc_project.Certs.Replace", "Replace", certPath.string());
+}
+
+inline int onPropertyUpdate(sd_bus_message* m, void* /* userdata */,
+ sd_bus_error* ret_error)
+{
+ if (ret_error == nullptr || sd_bus_error_is_set(ret_error))
+ {
+ BMCWEB_LOG_ERROR << "Got sdbus error on match";
+ return 0;
+ }
+
+ sdbusplus::message::message message(m);
+ std::string iface;
+ boost::container::flat_map<std::string, std::variant<std::string>>
+ changedProperties;
+
+ message.read(iface, changedProperties);
+ auto it = changedProperties.find("HostName");
+ if (it == changedProperties.end())
+ {
+ return 0;
+ }
+
+ std::string* hostname = std::get_if<std::string>(&it->second);
+ if (hostname == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "Unable to read hostname";
+ return 0;
+ }
+
+ BMCWEB_LOG_DEBUG << "Read hostname from signal: " << *hostname;
+ const std::string certFile = "/etc/ssl/certs/https/server.pem";
+
+ X509* cert = ensuressl::loadCert(certFile);
+ if (cert == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "Failed to read cert";
+ return 0;
+ }
+
+ const int maxKeySize = 256;
+ std::array<char, maxKeySize> cnBuffer{};
+
+ int cnLength =
+ X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName,
+ cnBuffer.data(), cnBuffer.size());
+ if (cnLength == -1)
+ {
+ BMCWEB_LOG_ERROR << "Failed to read NID_commonName";
+ X509_free(cert);
+ return 0;
+ }
+ std::string_view cnValue(std::begin(cnBuffer),
+ static_cast<size_t>(cnLength));
+
+ EVP_PKEY* pPubKey = X509_get_pubkey(cert);
+ if (pPubKey == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "Failed to get public key";
+ X509_free(cert);
+ return 0;
+ }
+ int isSelfSigned = X509_verify(cert, pPubKey);
+ EVP_PKEY_free(pPubKey);
+
+ BMCWEB_LOG_DEBUG << "Current HTTPs Certificate Subject CN: " << cnValue
+ << ", New HostName: " << *hostname
+ << ", isSelfSigned: " << isSelfSigned;
+
+ ASN1_IA5STRING* asn1 = static_cast<ASN1_IA5STRING*>(
+ X509_get_ext_d2i(cert, NID_netscape_comment, nullptr, nullptr));
+ if (asn1)
+ {
+ std::string_view comment(reinterpret_cast<const char*>(asn1->data),
+ static_cast<size_t>(asn1->length));
+ BMCWEB_LOG_DEBUG << "x509Comment: " << comment;
+
+ if (ensuressl::x509Comment == comment && isSelfSigned == 1 &&
+ cnValue != *hostname)
+ {
+ BMCWEB_LOG_INFO << "Ready to generate new HTTPs "
+ << "certificate with subject cn: " << *hostname;
+
+ ensuressl::generateSslCertificate("/tmp/hostname_cert.tmp",
+ *hostname);
+ installCertificate("/tmp/hostname_cert.tmp");
+ }
+ ASN1_STRING_free(asn1);
+ }
+ X509_free(cert);
+ return 0;
+}
+
+inline void registerHostnameSignal()
+{
+ BMCWEB_LOG_INFO << "Register HostName PropertiesChanged Signal";
+ std::string propertiesMatchString =
+ ("type='signal',"
+ "interface='org.freedesktop.DBus.Properties',"
+ "path='/xyz/openbmc_project/network/config',"
+ "arg0='xyz.openbmc_project.Network.SystemConfiguration',"
+ "member='PropertiesChanged'");
+
+ hostnameSignalMonitor = std::make_unique<sdbusplus::bus::match::match>(
+ *crow::connections::systemBus, propertiesMatchString, onPropertyUpdate,
+ nullptr);
+}
+} // namespace hostname_monitor
+} // namespace crow
+#endif