Refactoring of certificates managing and storing
This commit is about third stage code refactoring proposed by Zbigniew
Kurzynski (zbigniew.kurzynski@intel.com) on the mailing list
("phosphor-certificate-manager refactoring"): "Changing the way of
managing and storing TrustStore certificates".
Following changes are being implemented:
- each certificate has its own and unique ID,
- authority certificates are kept in files with random names under
/etc/ssl/certs/authority and symlinks (based on subject name hash) are
created to satisfy OpenSSL library,
- restarting bmcweb was moved from certificate class to certs_manager
class
- certificate uniqueness is based on certificate ID and checked while
installing and replacing operation in certs_manager class.
Tested by doing installing/replacing/removing operations on certificate
storage using RedFish API.
Signed-off-by: Zbigniew Lukwinski <zbigniew.lukwinski@linux.intel.com>
Change-Id: I0b02a10b940279c46ad9ee07925794262133b1b0
diff --git a/test/certs_manager_test.cpp b/test/certs_manager_test.cpp
index 4a3c6a4..30f0dd1 100644
--- a/test/certs_manager_test.cpp
+++ b/test/certs_manager_test.cpp
@@ -3,6 +3,13 @@
#include "certificate.hpp"
#include "certs_manager.hpp"
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/x509v3.h>
+
#include <algorithm>
#include <filesystem>
#include <fstream>
@@ -96,6 +103,35 @@
std::istreambuf_iterator<char>(f2.rdbuf()));
}
+ std::string getCertSubjectNameHash(const std::string& certFilePath)
+ {
+ std::unique_ptr<X509, decltype(&::X509_free)> cert(X509_new(),
+ ::X509_free);
+ if (!cert)
+ {
+ std::string();
+ }
+
+ std::unique_ptr<BIO, decltype(&::BIO_free)> bioCert(
+ BIO_new_file(certFilePath.c_str(), "rb"), ::BIO_free);
+ if (!bioCert)
+ {
+ std::string();
+ }
+
+ X509* x509 = cert.get();
+ if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
+ {
+ std::string();
+ }
+
+ unsigned long hash = X509_subject_name_hash(cert.get());
+ static constexpr auto AUTH_CERT_HASH_LENGTH = 9;
+ char hashBuf[AUTH_CERT_HASH_LENGTH];
+ sprintf(hashBuf, "%08lx", hash);
+ return std::string(hashBuf);
+ }
+
protected:
sdbusplus::bus::bus bus;
std::string certificateFile, CSRFile, privateKeyFile, rsaPrivateKeyFilePath;
@@ -213,7 +249,8 @@
EXPECT_FALSE(certs.empty());
- std::string verifyPath = verifyDir + "/" + certs[0]->getHash() + ".0";
+ std::string verifyPath =
+ verifyDir + "/" + getCertSubjectNameHash(certificateFile) + ".0";
// Check that certificate has been created at installation directory
EXPECT_FALSE(fs::is_empty(verifyDir));
@@ -223,14 +260,11 @@
EXPECT_TRUE(compareFiles(certificateFile, verifyPath));
}
-/** @brief Check if in athority mode user can install a certificate with certain
- * subject hash once, but cannot install another one with the same hash
+/** @brief Check if in authority mode user can't install the same
+ * certificate twice.
*/
-TEST_F(TestCertificates, InvokeInstallSameSubjectTwice)
+TEST_F(TestCertificates, InvokeInstallSameCertTwice)
{
- using NotAllowed =
- sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
-
std::string endpoint("ldap");
std::string unit("");
std::string type("authority");
@@ -250,23 +284,22 @@
EXPECT_FALSE(certs.empty());
- std::string verifyPath = verifyDir + "/" + certs[0]->getHash() + ".0";
-
// Check that certificate has been created at installation directory
+ std::string verifyPath =
+ verifyDir + "/" + getCertSubjectNameHash(certificateFile) + ".0";
EXPECT_FALSE(fs::is_empty(verifyDir));
EXPECT_TRUE(fs::exists(verifyPath));
// Check that installed cert is identical to input one
EXPECT_TRUE(compareFiles(certificateFile, verifyPath));
- // Try to install another one with the same subject
- createNewCertificate(false);
-
+ using NotAllowed =
+ sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
EXPECT_THROW(
{
try
{
- // install second certificate
+ // Try to install the same certificate second time
mainApp.install(certificateFile);
}
catch (const NotAllowed& e)
@@ -281,6 +314,141 @@
EXPECT_TRUE(fs::exists(verifyPath));
}
+/** @brief Check if in authority mode user cannot install a certificate with
+ * certain subject hash twice.
+ */
+TEST_F(TestCertificates, InvokeInstallSameSubjectTwice)
+{
+ std::string endpoint("ldap");
+ std::string unit("");
+ std::string type("authority");
+ std::string verifyDir(certDir);
+ UnitsToRestart verifyUnit(unit);
+ auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
+ auto event = sdeventplus::Event::get_default();
+ // Attach the bus to sd_event to service user requests
+ bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+ Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
+ std::move(certDir));
+ MainApp mainApp(&manager);
+ mainApp.install(certificateFile);
+
+ std::vector<std::unique_ptr<Certificate>>& certs =
+ manager.getCertificates();
+
+ EXPECT_FALSE(certs.empty());
+
+ // Check that certificate has been created at installation directory
+ std::string verifyPath0 =
+ verifyDir + "/" + getCertSubjectNameHash(certificateFile) + ".0";
+ EXPECT_FALSE(fs::is_empty(verifyDir));
+ EXPECT_TRUE(fs::exists(verifyPath0));
+
+ // Check that installed cert is identical to input one
+ EXPECT_TRUE(compareFiles(certificateFile, verifyPath0));
+
+ // Prepare second certificate with the same subject
+ createNewCertificate();
+
+ using NotAllowed =
+ sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
+ EXPECT_THROW(
+ {
+ try
+ {
+ // Install second certificate
+ mainApp.install(certificateFile);
+ }
+ catch (const NotAllowed& e)
+ {
+ throw;
+ }
+ },
+ NotAllowed);
+
+ // Expect there are exactly two certificates in the collection
+ EXPECT_EQ(certs.size(), 1);
+
+ // Check that the original/first certificate has been not removed
+ EXPECT_FALSE(fs::is_empty(verifyDir));
+ EXPECT_TRUE(fs::exists(verifyPath0));
+}
+
+/** @brief Check if in authority mode user can't install more than
+ * AUTHORITY_CERTIFICATES_LIMIT certificates.
+ */
+TEST_F(TestCertificates, InvokeInstallAuthCertLimit)
+{
+ std::string endpoint("ldap");
+ std::string unit("");
+ std::string type("authority");
+ std::string verifyDir(certDir);
+ UnitsToRestart verifyUnit(unit);
+ auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
+ auto event = sdeventplus::Event::get_default();
+ // Attach the bus to sd_event to service user requests
+ bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+ Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
+ std::move(certDir));
+ MainApp mainApp(&manager);
+
+ std::vector<std::unique_ptr<Certificate>>& certs =
+ manager.getCertificates();
+
+ std::vector<std::string> verifyPaths;
+
+ // Prepare maximum number of ceritificates
+ for (std::size_t i = 0; i < AUTHORITY_CERTIFICATES_LIMIT; ++i)
+ {
+ // Prepare new certificatate
+ createNewCertificate(true);
+
+ // Install ceritificate
+ mainApp.install(certificateFile);
+
+ // Check number of certificates in the collection
+ EXPECT_EQ(certs.size(), i + 1);
+
+ // Check that certificate has been created at installation directory
+ std::string verifyPath =
+ verifyDir + "/" + getCertSubjectNameHash(certificateFile) + ".0";
+ EXPECT_FALSE(fs::is_empty(verifyDir));
+ EXPECT_TRUE(fs::exists(verifyPath));
+
+ // Check that installed cert is identical to input one
+ EXPECT_TRUE(compareFiles(certificateFile, verifyPath));
+
+ // Save current certificate file for later check
+ verifyPaths.push_back(verifyPath);
+ }
+
+ // Prepare new certificatate
+ createNewCertificate(true);
+
+ using NotAllowed =
+ sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
+ EXPECT_THROW(
+ {
+ try
+ {
+ // Try to install one more certificate
+ mainApp.install(certificateFile);
+ }
+ catch (const NotAllowed& e)
+ {
+ throw;
+ }
+ },
+ NotAllowed);
+
+ // Check that the original certificate has been not removed
+ EXPECT_FALSE(fs::is_empty(verifyDir));
+ for (int i = 0; i < AUTHORITY_CERTIFICATES_LIMIT; ++i)
+ {
+ EXPECT_TRUE(fs::exists(verifyPaths[i]));
+ }
+}
+
/** @brief Compare the installed certificate with the copied certificate
*/
TEST_F(TestCertificates, CompareInstalledCertificate)
@@ -389,7 +557,8 @@
// Certificate successfully installed
EXPECT_FALSE(certs.empty());
- std::string verifyPath = verifyDir + "/" + certs[0]->getHash() + ".0";
+ std::string verifyPath =
+ verifyDir + "/" + getCertSubjectNameHash(certificateFile) + ".0";
// Check that certificate has been created at installation directory
EXPECT_FALSE(fs::is_empty(verifyDir));