diff options
author | Alan Kuo <Alan_Kuo@quantatw.com> | 2020-11-26 06:15:29 +0300 |
---|---|---|
committer | Ed Tanous <ed@tanous.net> | 2020-12-12 04:46:28 +0300 |
commit | a82207087d79c4dd85447bdacfd4de91be4e7166 (patch) | |
tree | e452ce36b3df5889bafeae2f163a7f599c02d398 /include/hostname_monitor.hpp | |
parent | 9e319cf0c5ee714cdd879a1d2a6e5b5ac96c7f1d (diff) | |
download | bmcweb-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.hpp | 141 |
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 |