Authorities list: implement InstallAll & ReplaceAll
This change implements the design in
https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/49317.
InstallAll: enumerate all certs in the input file and install all of
them;
ReplaceAll: replace all certs with the new authorities list
Atomic: implemented via creating temporary folder and issuing swap.
Added ability to unit test service reload as well.
Tested:
1. Unit tests
2. Tested loading/deleting authorities list in QEMU.
```
root@xxx:~# busctl call xyz.openbmc_project.Certs.Manager.Authority.Ldap \
> /xyz/openbmc_project/certs/authority/ldap \
> xyz.openbmc_project.Certs.InstallAll \
> InstallAll s /tmp/trust_bundle.pem
as 3 "/xyz/openbmc_project/certs/authority/ldap/1"
"/xyz/openbmc_project/certs/authority/ldap/2"
"/xyz/openbmc_project/certs/authority/ldap/3"
root@xxx:~# ls /etc/ssl/certs/authority/
10a5d8b0.0 5b49ceaa.0 f3ddaa86.0 file0qmgPV fileDbjTzW fileR4TtjO
trust_bundle
root@xxx:~# busctl call
xyz.openbmc_project.Certs.Manager.Authority.Ldap
/xyz/openbmc_project/certs/authority/ldap
xyz.openbmc_project.Certs.ReplaceAll ReplaceAll s /tmp/trust_bundle.pem
root@xxx:~# ls /etc/ssl/certs/authority/
10a5d8b0.0 5b49ceaa.0 f3ddaa86.0 file1obsEZ fileOqVoaC filerUBZCj
trust_bundle
root@xxx:~# wget -qO- http://localhost/redfish/v1/Managers/bmc/Truststore/Certificates/
{
"@odata.id": "/redfish/v1/Managers/bmc/Truststore/Certificates/",
"@odata.type": "#CertificateCollection.CertificateCollection",
"Description": "A Collection of TrustStore certificate instances",
"Members": [
{
"@odata.id": "/redfish/v1/Managers/bmc/Truststore/Certificates/1"
},
{
"@odata.id": "/redfish/v1/Managers/bmc/Truststore/Certificates/2"
},
{
"@odata.id": "/redfish/v1/Managers/bmc/Truststore/Certificates/3"
}
],
"Members@odata.count": 3,
"Name": "TrustStore Certificates Collection"
}
root@xxx:~# wget -qO- http://localhost/redfish/v1/Managers/bmc/Truststore/Certificates/1
{
"@odata.id": "/redfish/v1/Managers/bmc/Truststore/Certificates/1",
"@odata.type": "#Certificate.v1_0_0.Certificate",
"CertificateString": "-----BEGIN CERTIFICATE-----\nMIICZTCCAgugAwIBAgIUANIf0jvaRNq1MdwxrXPnk25VrmYwCgYIKoZIzj0EAwIw\nVTETMBEGA1UEChMKY2FtcHVzLWFzaDENMAsGA1UECxMEcm9vdDEvMC0GA1UEAwwm\ne2QyZWQ1MGJkLTczMTQtNDgxZC04OWE0LTVkMjkxMmYyMGQ5NH0wIBcNNzAwMTAx\nMDAwMDAwWhgPOTk5OTEyMzEyMzU5NTlaMFUxEzARBgNVBAoTCmNhbXB1cy1hc2gx\nDTALBgNVBAsTBHJvb3QxLzAtBgNVBAMMJntkMmVkNTBiZC03MzE0LTQ4MWQtODlh\nNC01ZDI5MTJmMjBkOTR9MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7lp/J3Gj\nc4TKubuYtzpxu2D3STlwTwEjgFbTaLZnQ0KXt7pBrcYc3yY1t74WBluvzM9iok6Q\nDcEFX5aIYcoaAKOBtjCBszAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0lBCIwIAYIKwYB\nBQUHAwEGCCsGAQUFBwMCBgorBgEEAdZ5AgcBMA8GA1UdEwEB/wQFMAMBAf8wHQYD\nVR0OBBYEFIPrX7lbeJhvHHcQ7iYOry50aYKYMBcGA1UdIAQQMA4wDAYKKwYBBAHW\neQIFBDAtBgNVHR4BAf8EIzAhoB8wHYYbLmNhbXB1cy1hc2gucHJvZC5nb29nbGUu\nY29tMAoGCCqGSM49BAMCA0gAMEUCIAS/ZrMPBj992vVVplwzH9DWDCSMu1rCgvqw\nam3byOT1AiEAyrr3FAP+7js7z+h8d94hTyy1kTn+4NOvUWrVzHUmJI8=\n-----END CERTIFICATE-----\n",
"Description": "TrustStore Certificate",
"Id": "1",
"Issuer": {
"CommonName": "{d2ed50bd-7314-481d-89a4-5d2912f20d94}",
"Organization": "campus-ash",
"OrganizationalUnit": "root"
},
"KeyUsage": [
"CRLSigning",
"ServerAuthentication",
"ClientAuthentication",
""
],
"Name": "TrustStore Certificate",
"Subject": {
"CommonName": "{d2ed50bd-7314-481d-89a4-5d2912f20d94}",
"Organization": "campus-ash",
"OrganizationalUnit": "root"
},
"ValidNotAfter": "9999-12-31T23:59:59+00:00",
"ValidNotBefore": "1970-01-01T00:00:00+00:00"
}
```
Signed-off-by: Nan Zhou <nanzhoumails@gmail.com>
Change-Id: I495f5c1c1c4a2ac880dd3233be31b84a78d79a43
diff --git a/test/certs_manager_test.cpp b/test/certs_manager_test.cpp
index 6e4c37f..b3a1563 100644
--- a/test/certs_manager_test.cpp
+++ b/test/certs_manager_test.cpp
@@ -23,11 +23,13 @@
#include <sdbusplus/bus.hpp>
#include <sdeventplus/event.hpp>
#include <string>
+#include <unordered_set>
#include <utility>
#include <vector>
#include <xyz/openbmc_project/Certs/error.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace phosphor::certs
@@ -37,6 +39,31 @@
namespace fs = std::filesystem;
using ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using ::testing::Eq;
+using ::testing::Return;
+// Compares two files; returns true only if the two are the same
+bool compareFiles(const std::string& file1, const std::string& file2)
+{
+ std::ifstream f1(file1, std::ifstream::binary | std::ifstream::ate);
+ std::ifstream f2(file2, std::ifstream::binary | std::ifstream::ate);
+
+ if (f1.fail() || f2.fail())
+ {
+ return false; // file problem
+ }
+
+ if (f1.tellg() != f2.tellg())
+ {
+ return false; // size mismatch
+ }
+
+ // seek back to beginning and use std::equal to compare contents
+ f1.seekg(0, std::ifstream::beg);
+ f2.seekg(0, std::ifstream::beg);
+ return std::equal(std::istreambuf_iterator<char>(f1.rdbuf()),
+ std::istreambuf_iterator<char>(),
+ std::istreambuf_iterator<char>(f2.rdbuf()));
+}
/**
* Class to generate certificate file and test verification of certificate file
@@ -228,23 +255,39 @@
phosphor::certs::CSR* csr_;
};
+class ManagerInTest : public phosphor::certs::Manager
+{
+ public:
+ static constexpr std::string_view unitToRestartInTest =
+ "xyz.openbmc_project.awesome-service";
+ ManagerInTest(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
+ const char* path, CertificateType type,
+ const std::string& unit, const std::string& installPath) :
+ Manager(bus, event, path, type, unit, installPath)
+ {
+ }
+
+ MOCK_METHOD(void, reloadOrReset, (const std::string&), (override));
+};
+
/** @brief Check if server install routine is invoked for server setup
*/
TEST_F(TestCertificates, InvokeServerInstall)
{
std::string endpoint("https");
- std::string unit;
CertificateType type = CertificateType::Server;
std::string installPath(certDir + "/" + certificateFile);
std::string verifyPath(installPath);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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(installPath));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ installPath);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return());
MainApp mainApp(&manager);
mainApp.install(certificateFile);
EXPECT_TRUE(fs::exists(verifyPath));
@@ -255,18 +298,19 @@
TEST_F(TestCertificates, InvokeClientInstall)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Server;
std::string installPath(certDir + "/" + certificateFile);
std::string verifyPath(installPath);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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(installPath));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ installPath);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return());
MainApp mainApp(&manager);
mainApp.install(certificateFile);
EXPECT_TRUE(fs::exists(verifyPath));
@@ -277,17 +321,18 @@
TEST_F(TestCertificates, InvokeAuthorityInstall)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Authority;
std::string verifyDir(certDir);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ verifyDir);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return());
MainApp mainApp(&manager);
// install the default certificate that's valid from today to 100 years
// later
@@ -319,17 +364,18 @@
TEST_F(TestCertificates, InvokeAuthorityInstallNeverExpiredRootCert)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Authority;
std::string verifyDir(certDir);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ certDir);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return());
MainApp mainApp(&manager);
// install the certificate that's valid from the Unix Epoch to Dec 31, 9999
@@ -359,17 +405,18 @@
TEST_F(TestCertificates, InvokeInstallSameCertTwice)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Authority;
std::string verifyDir(certDir);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ std::move(certDir));
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return());
MainApp mainApp(&manager);
mainApp.install(certificateFile);
@@ -414,17 +461,19 @@
TEST_F(TestCertificates, InvokeInstallSameSubjectTwice)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Authority;
std::string verifyDir(certDir);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ certDir);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return())
+ .WillOnce(Return());
MainApp mainApp(&manager);
mainApp.install(certificateFile);
@@ -470,17 +519,18 @@
TEST_F(TestCertificates, InvokeInstallAuthCertLimit)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Authority;
std::string verifyDir(certDir);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ certDir);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillRepeatedly(Return());
MainApp mainApp(&manager);
std::vector<std::unique_ptr<Certificate>>& certs =
@@ -545,19 +595,19 @@
TEST_F(TestCertificates, CompareInstalledCertificate)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Client;
- ;
std::string installPath(certDir + "/" + certificateFile);
std::string verifyPath(installPath);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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(installPath));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ installPath);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return());
MainApp mainApp(&manager);
mainApp.install(certificateFile);
EXPECT_TRUE(fs::exists(verifyPath));
@@ -569,12 +619,10 @@
TEST_F(TestCertificates, TestNoCertificateFile)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Client;
- ;
std::string installPath(certDir + "/" + certificateFile);
std::string verifyPath(installPath);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(type) + '/' + endpoint;
std::string uploadFile = "nofile.pem";
@@ -585,8 +633,8 @@
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(installPath));
+ ManagerInTest manager(bus, event, objPath.c_str(), type,
+ verifyUnit, installPath);
MainApp mainApp(&manager);
mainApp.install(uploadFile);
}
@@ -604,17 +652,20 @@
TEST_F(TestCertificates, TestReplaceCertificate)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Server;
std::string installPath(certDir + "/" + certificateFile);
std::string verifyPath(installPath);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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(installPath));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ std::move(installPath));
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return())
+ .WillOnce(Return());
MainApp mainApp(&manager);
mainApp.install(certificateFile);
EXPECT_TRUE(fs::exists(verifyPath));
@@ -631,23 +682,25 @@
TEST_F(TestCertificates, TestAuthorityReplaceCertificate)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Authority;
std::string verifyDir(certDir);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ certDir);
+ constexpr const unsigned int REPLACE_ITERATIONS = 10;
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .Times(REPLACE_ITERATIONS + 1)
+ .WillRepeatedly(Return());
MainApp mainApp(&manager);
mainApp.install(certificateFile);
std::vector<std::unique_ptr<Certificate>>& certs =
manager.getCertificates();
- constexpr const unsigned int REPLACE_ITERATIONS = 10;
for (unsigned int i = 0; i < REPLACE_ITERATIONS; i++)
{
@@ -679,17 +732,18 @@
TEST_F(TestCertificates, TestStorageDeleteCertificate)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Authority;
std::string verifyDir(certDir);
- std::string verifyUnit(unit);
+ std::string verifyUnit((ManagerInTest::unitToRestartInTest));
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ certDir);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillRepeatedly(Return());
MainApp mainApp(&manager);
// Check if certificate placeholder dir is empty
@@ -731,12 +785,10 @@
TEST_F(TestCertificates, TestEmptyCertificateFile)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Client;
- ;
std::string installPath(certDir + "/" + certificateFile);
std::string verifyPath(installPath);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(type) + '/' + endpoint;
std::string emptyFile("emptycert.pem");
@@ -750,8 +802,8 @@
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(installPath));
+ ManagerInTest manager(bus, event, objPath.c_str(), type,
+ verifyUnit, installPath);
MainApp mainApp(&manager);
mainApp.install(emptyFile);
}
@@ -770,9 +822,7 @@
TEST_F(TestCertificates, TestInvalidCertificateFile)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Client;
- ;
std::ofstream ofs;
ofs.open(certificateFile, std::ofstream::out);
@@ -783,7 +833,7 @@
std::string installPath(certDir + "/" + certificateFile);
std::string verifyPath(installPath);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(type) + '/' + endpoint;
EXPECT_THROW(
@@ -793,8 +843,8 @@
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(installPath));
+ ManagerInTest manager(bus, event, objPath.c_str(), type,
+ verifyUnit, installPath);
MainApp mainApp(&manager);
mainApp.install(certificateFile);
}
@@ -859,12 +909,10 @@
TEST_F(TestInvalidCertificate, TestMissingPrivateKey)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Client;
- ;
std::string installPath(certDir + "/" + certificateFile);
std::string verifyPath(installPath);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(type) + '/' + endpoint;
EXPECT_THROW(
@@ -874,8 +922,8 @@
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(installPath));
+ ManagerInTest manager(bus, event, objPath.c_str(), type,
+ verifyUnit, installPath);
MainApp mainApp(&manager);
mainApp.install(certificateFile);
}
@@ -893,12 +941,10 @@
TEST_F(TestInvalidCertificate, TestMissingCeritificate)
{
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Client;
- ;
std::string installPath(certDir + "/" + keyFile);
std::string verifyPath(installPath);
- std::string verifyUnit(unit);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(type) + '/' + endpoint;
EXPECT_THROW(
@@ -908,8 +954,8 @@
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(installPath));
+ ManagerInTest manager(bus, event, objPath.c_str(), type,
+ verifyUnit, installPath);
MainApp mainApp(&manager);
mainApp.install(keyFile);
}
@@ -930,18 +976,17 @@
using NotAllowed =
sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
std::string endpoint("ldap");
- std::string unit;
CertificateType type = CertificateType::Client;
- ;
std::string installPath(certDir + "/" + certificateFile);
std::string verifyPath(installPath);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(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(installPath));
+ ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+ installPath);
MainApp mainApp(&manager);
mainApp.install(certificateFile);
EXPECT_TRUE(fs::exists(verifyPath));
@@ -1413,17 +1458,407 @@
TEST_F(TestCertificates, TestGenerateRSAPrivateKeyFile)
{
std::string endpoint("https");
- std::string unit;
CertificateType type = CertificateType::Server;
std::string installPath(certDir + "/" + certificateFile);
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
auto objPath = std::string(objectNamePrefix) + '/' +
certificateTypeToString(type) + '/' + endpoint;
auto event = sdeventplus::Event::get_default();
EXPECT_FALSE(fs::exists(rsaPrivateKeyFilePath));
- Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
- std::move(installPath));
+ Manager manager(bus, event, objPath.c_str(), type, verifyUnit, installPath);
EXPECT_TRUE(fs::exists(rsaPrivateKeyFilePath));
}
+
+/**
+ * Class to test Authorities List installation and replacement
+ */
+class AuthoritiesListTest : public testing::Test
+{
+ public:
+ AuthoritiesListTest() :
+ bus(sdbusplus::bus::new_default()),
+ authoritiesListFolder(
+ Certificate::generateUniqueFilePath(fs::temp_directory_path()))
+ {
+ fs::create_directory(authoritiesListFolder);
+ createAuthoritiesList(maxNumAuthorityCertificates);
+ }
+ ~AuthoritiesListTest() override
+ {
+ fs::remove_all(authoritiesListFolder);
+ }
+
+ protected:
+ // Creates a testing authorities list which consists of |count| root
+ // certificates
+ void createAuthoritiesList(int count)
+ {
+ fs::path srcFolder = fs::temp_directory_path();
+ srcFolder = Certificate::generateUniqueFilePath(srcFolder);
+ fs::create_directory(srcFolder);
+ createSingleAuthority(srcFolder, "root_0");
+ sourceAuthoritiesListFile = srcFolder / "root_0_cert";
+ for (int i = 1; i < count; ++i)
+ {
+ std::string name = "root_" + std::to_string(i);
+ createSingleAuthority(srcFolder, name);
+ appendContentFromFile(sourceAuthoritiesListFile,
+ srcFolder / (name + "_cert"));
+ }
+ }
+
+ // Creates a single self-signed root certificate in given |path|; the key
+ // will be |path|/|cn|_key, the cert will be |path|/|cn|_cert, and the cn
+ // will be "/O=openbmc-project.xyz/C=US/ST=CA/CN=|cn|"
+ static void createSingleAuthority(const std::string& path,
+ const std::string& cn)
+ {
+ std::string key = fs::path(path) / (cn + "_key");
+ std::string cert = fs::path(path) / (cn + "_cert");
+ std::string cmd = "openssl req -x509 -sha256 -newkey rsa:2048 -keyout ";
+ cmd += key + " -out " + cert + " -nodes --days 365000 ";
+ cmd += "-subj /O=openbmc-project.xyz/CN=" + cn;
+ ASSERT_EQ(std::system(cmd.c_str()), 0);
+ }
+
+ // Appends the content of the |from| file to the |to| file.
+ static void appendContentFromFile(const std::string& to,
+ const std::string& from)
+ {
+ ASSERT_NO_THROW({
+ std::ifstream inputCertFileStream;
+ std::ofstream outputCertFileStream;
+ inputCertFileStream.exceptions(std::ifstream::failbit |
+ std::ifstream::badbit |
+ std::ifstream::eofbit);
+ outputCertFileStream.exceptions(std::ofstream::failbit |
+ std::ofstream::badbit |
+ std::ofstream::eofbit);
+ inputCertFileStream.open(from);
+ outputCertFileStream.open(to, std::ios::app);
+ outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
+ inputCertFileStream.close();
+ outputCertFileStream.close();
+ });
+ }
+
+ // Appends the content of the |from| buffer to the |to| file.
+ static void setContentFromString(const std::string& to,
+ const std::string& from)
+ {
+ ASSERT_NO_THROW({
+ std::ofstream outputCertFileStream;
+ outputCertFileStream.exceptions(std::ofstream::failbit |
+ std::ofstream::badbit |
+ std::ofstream::eofbit);
+ outputCertFileStream.open(to, std::ios::out);
+ outputCertFileStream << from << std::flush;
+ outputCertFileStream.close();
+ });
+ }
+
+ // Verifies the effect of InstallAll or ReplaceAll
+ void verifyCertificates(std::vector<std::unique_ptr<Certificate>>& certs)
+ {
+ // The trust bundle file has been copied over
+ EXPECT_FALSE(fs::is_empty(authoritiesListFolder));
+ EXPECT_TRUE(
+ compareFiles(authoritiesListFolder / defaultAuthoritiesListFileName,
+ sourceAuthoritiesListFile));
+
+ ASSERT_EQ(certs.size(), maxNumAuthorityCertificates);
+ // Check attributes and alias
+ for (size_t i = 0; i < certs.size(); ++i)
+ {
+ std::string name = "root_" + std::to_string(i);
+ EXPECT_EQ(certs[i]->subject(), "O=openbmc-project.xyz,CN=" + name);
+ EXPECT_EQ(certs[i]->issuer(), "O=openbmc-project.xyz,CN=" + name);
+ std::string symbolLink =
+ authoritiesListFolder /
+ (certs[i]->getCertId().substr(0, 8) + ".0");
+ ASSERT_TRUE(fs::exists(symbolLink));
+ compareFileAgainstString(symbolLink, certs[i]->certificateString());
+ }
+ }
+
+ // Expects that the content of |path| file is |buffer|.
+ static void compareFileAgainstString(const std::string& path,
+ const std::string& buffer)
+ {
+ ASSERT_NO_THROW({
+ std::ifstream inputCertFileStream;
+ inputCertFileStream.exceptions(std::ifstream::failbit |
+ std::ifstream::badbit |
+ std::ifstream::eofbit);
+ inputCertFileStream.open(path);
+ std::stringstream read;
+ read << inputCertFileStream.rdbuf();
+ inputCertFileStream.close();
+ EXPECT_EQ(read.str(), buffer);
+ });
+ };
+
+ sdbusplus::bus::bus bus;
+ fs::path authoritiesListFolder;
+ fs::path sourceAuthoritiesListFile;
+};
+
+// Tests that the Authority Manager installs all the certificates in an
+// authorities list
+TEST_F(AuthoritiesListTest, InstallAll)
+{
+ std::string endpoint("ldap");
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+ CertificateType type = CertificateType::Authority;
+
+ std::string object = std::string(objectNamePrefix) + '/' +
+ certificateTypeToString(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);
+ ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+ authoritiesListFolder);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return());
+ ASSERT_TRUE(manager.getCertificates().empty());
+
+ std::vector<sdbusplus::message::object_path> objects =
+ manager.installAll(sourceAuthoritiesListFile);
+ for (size_t i = 0; i < manager.getCertificates().size(); ++i)
+ {
+ EXPECT_EQ(manager.getCertificates()[i]->getObjectPath(), objects[i]);
+ }
+ verifyCertificates(manager.getCertificates());
+}
+
+// Tests that the Authority Manager recovers from the authorities list persisted
+// in the installation path at boot up
+TEST_F(AuthoritiesListTest, RecoverAtBootUp)
+{
+ std::string endpoint("ldap");
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+ CertificateType type = CertificateType::Authority;
+
+ std::string object = std::string(objectNamePrefix) + '/' +
+ certificateTypeToString(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);
+
+ // Copy the trust bundle into the installation path before creating an
+ // Authority Manager
+ fs::copy_file(/*from=*/sourceAuthoritiesListFile,
+ authoritiesListFolder / defaultAuthoritiesListFileName);
+ // Create some noise as well
+ fs::copy_file(/*from=*/sourceAuthoritiesListFile,
+ authoritiesListFolder / "should_be_deleted");
+
+ ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+ authoritiesListFolder);
+
+ ASSERT_EQ(manager.getCertificates().size(), maxNumAuthorityCertificates);
+
+ // Check attributes and alias
+ std::unordered_set<std::string> expectedFiles = {authoritiesListFolder /
+ "trust_bundle"};
+ std::vector<std::unique_ptr<Certificate>>& certs =
+ manager.getCertificates();
+ for (size_t i = 0; i < certs.size(); ++i)
+ {
+ std::string name = "root_" + std::to_string(i);
+ EXPECT_EQ(certs[i]->subject(), "O=openbmc-project.xyz,CN=" + name);
+ EXPECT_EQ(certs[i]->issuer(), "O=openbmc-project.xyz,CN=" + name);
+ std::string symbolLink =
+ authoritiesListFolder / (certs[i]->getCertId().substr(0, 8) + ".0");
+ expectedFiles.insert(symbolLink);
+ expectedFiles.insert(certs[i]->getCertFilePath());
+ ASSERT_TRUE(fs::exists(symbolLink));
+ compareFileAgainstString(symbolLink, certs[i]->certificateString());
+ }
+
+ // Check folder content
+ for (auto& path : fs::directory_iterator(authoritiesListFolder))
+ {
+ EXPECT_NE(path, authoritiesListFolder / "should_be_deleted");
+ expectedFiles.erase(path.path());
+ }
+ EXPECT_TRUE(expectedFiles.empty());
+}
+
+TEST_F(AuthoritiesListTest, InstallAndDelete)
+{
+ std::string endpoint("ldap");
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+ CertificateType type = CertificateType::Authority;
+
+ std::string object = std::string(objectNamePrefix) + '/' +
+ certificateTypeToString(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);
+ ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+ authoritiesListFolder);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return())
+ .WillOnce(Return());
+ ASSERT_TRUE(manager.getCertificates().empty());
+ ASSERT_EQ(manager.installAll(sourceAuthoritiesListFile).size(),
+ maxNumAuthorityCertificates);
+ manager.deleteAll();
+ EXPECT_TRUE(manager.getCertificates().empty());
+ // Check folder content
+ for (const fs::path& f : fs::directory_iterator(authoritiesListFolder))
+ {
+ EXPECT_THAT(f.filename(), testing::AnyOf(".", ".."));
+ }
+}
+
+TEST_F(AuthoritiesListTest, InstallAllWrongManagerType)
+{
+ std::string endpoint("ldap");
+ CertificateType type = CertificateType::Server;
+
+ std::string object = std::string(objectNamePrefix) + '/' +
+ certificateTypeToString(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);
+ ManagerInTest serverManager(bus, event, object.c_str(), type, "",
+ authoritiesListFolder);
+ EXPECT_THROW(serverManager.installAll(sourceAuthoritiesListFile),
+ sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed);
+
+ type = CertificateType::Client;
+ object = std::string(objectNamePrefix) + '/' +
+ certificateTypeToString(type) + '/' + endpoint;
+ ManagerInTest clientManager(bus, event, object.c_str(), type, "",
+ authoritiesListFolder);
+ EXPECT_THROW(clientManager.installAll(sourceAuthoritiesListFile),
+ sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed);
+}
+
+TEST_F(AuthoritiesListTest, InstallAllTwice)
+{
+ std::string endpoint("ldap");
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+ CertificateType type = CertificateType::Authority;
+
+ std::string object = std::string(objectNamePrefix) + '/' +
+ certificateTypeToString(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);
+ ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+ authoritiesListFolder);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return());
+ ASSERT_TRUE(manager.getCertificates().empty());
+
+ ASSERT_EQ(manager.installAll(sourceAuthoritiesListFile).size(),
+ maxNumAuthorityCertificates);
+ EXPECT_THROW(manager.installAll(sourceAuthoritiesListFile).size(),
+ sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed);
+}
+
+TEST_F(AuthoritiesListTest, InstallAllMissSourceFile)
+{
+ std::string endpoint("ldap");
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+ CertificateType type = CertificateType::Authority;
+
+ std::string object = std::string(objectNamePrefix) + '/' +
+ certificateTypeToString(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);
+ ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+ authoritiesListFolder);
+
+ EXPECT_THROW(manager.installAll(authoritiesListFolder / "trust_bundle"),
+ InternalFailure);
+}
+
+TEST_F(AuthoritiesListTest, TooManyRootCertificates)
+{
+ std::string endpoint("ldap");
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+ CertificateType type = CertificateType::Authority;
+
+ std::string object = std::string(objectNamePrefix) + '/' +
+ certificateTypeToString(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);
+ ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+ authoritiesListFolder);
+ createAuthoritiesList(maxNumAuthorityCertificates + 1);
+ EXPECT_THROW(manager.installAll(sourceAuthoritiesListFile),
+ sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed);
+}
+
+TEST_F(AuthoritiesListTest, CertInWrongFormat)
+{
+ std::string endpoint("ldap");
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+ CertificateType type = CertificateType::Authority;
+
+ std::string object = std::string(objectNamePrefix) + '/' +
+ certificateTypeToString(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);
+
+ ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+ authoritiesListFolder);
+
+ // Replace the authorities list with non-valid PEM encoded x509 certificate
+ setContentFromString(sourceAuthoritiesListFile, "blah-blah");
+ EXPECT_THROW(manager.installAll(sourceAuthoritiesListFile),
+ InvalidCertificate);
+ setContentFromString(sourceAuthoritiesListFile,
+ "-----BEGIN CERTIFICATE-----");
+ EXPECT_THROW(manager.installAll(sourceAuthoritiesListFile),
+ InvalidCertificate);
+}
+
+TEST_F(AuthoritiesListTest, ReplaceAll)
+{
+ std::string endpoint("ldap");
+ std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+ CertificateType type = CertificateType::Authority;
+
+ std::string object = std::string(objectNamePrefix) + '/' +
+ certificateTypeToString(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);
+ ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+ authoritiesListFolder);
+ EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+ .WillOnce(Return())
+ .WillOnce(Return());
+ manager.installAll(sourceAuthoritiesListFile);
+
+ // Replace the current list with a different list
+ fs::remove_all(sourceAuthoritiesListFile.parent_path());
+ createAuthoritiesList(maxNumAuthorityCertificates);
+ std::vector<sdbusplus::message::object_path> objects =
+ manager.replaceAll(sourceAuthoritiesListFile);
+
+ for (size_t i = 0; i < manager.getCertificates().size(); ++i)
+ {
+ EXPECT_EQ(manager.getCertificates()[i]->getObjectPath(), objects[i]);
+ }
+ verifyCertificates(manager.getCertificates());
+}
+
} // namespace
} // namespace phosphor::certs