1947258dcSMarri Devender Rao #include "certs_manager.hpp" 2947258dcSMarri Devender Rao 3947258dcSMarri Devender Rao #include <algorithm> 4947258dcSMarri Devender Rao #include <experimental/filesystem> 5947258dcSMarri Devender Rao #include <fstream> 6947258dcSMarri Devender Rao #include <iterator> 7947258dcSMarri Devender Rao #include <string> 8947258dcSMarri Devender Rao #include <xyz/openbmc_project/Certs/Install/error.hpp> 9947258dcSMarri Devender Rao #include <xyz/openbmc_project/Common/error.hpp> 10947258dcSMarri Devender Rao 11947258dcSMarri Devender Rao #include <gmock/gmock.h> 12947258dcSMarri Devender Rao #include <gtest/gtest.h> 13947258dcSMarri Devender Rao 14947258dcSMarri Devender Rao namespace fs = std::experimental::filesystem; 15947258dcSMarri Devender Rao static constexpr auto BUSNAME = "xyz.openbmc_project.Certs.Manager"; 16947258dcSMarri Devender Rao static constexpr auto OBJPATH = "/xyz/openbmc_project/certs"; 17947258dcSMarri Devender Rao using InternalFailure = 18947258dcSMarri Devender Rao sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 19947258dcSMarri Devender Rao 20*e6597c5bSMarri Devender Rao using InvalidCertificate = 21*e6597c5bSMarri Devender Rao sdbusplus::xyz::openbmc_project::Certs::Install::Error::InvalidCertificate; 22*e6597c5bSMarri Devender Rao 23947258dcSMarri Devender Rao class TestCertsManager : public ::testing::Test 24947258dcSMarri Devender Rao { 25947258dcSMarri Devender Rao public: 26947258dcSMarri Devender Rao TestCertsManager() : bus(sdbusplus::bus::new_default()) 27947258dcSMarri Devender Rao { 28947258dcSMarri Devender Rao } 29947258dcSMarri Devender Rao void SetUp() override 30947258dcSMarri Devender Rao { 31947258dcSMarri Devender Rao char dirTemplate[] = "/tmp/FakeCerts.XXXXXX"; 32947258dcSMarri Devender Rao auto dirPtr = mkdtemp(dirTemplate); 33947258dcSMarri Devender Rao if (dirPtr == NULL) 34947258dcSMarri Devender Rao { 35947258dcSMarri Devender Rao throw std::bad_alloc(); 36947258dcSMarri Devender Rao } 37947258dcSMarri Devender Rao certDir = dirPtr; 38947258dcSMarri Devender Rao certificateFile = "cert.pem"; 39947258dcSMarri Devender Rao std::string cmd = "openssl req -x509 -sha256 -newkey rsa:2048 "; 40947258dcSMarri Devender Rao cmd += "-keyout cert.pem -out cert.pem -days 3650 "; 41947258dcSMarri Devender Rao cmd += "-subj " 42947258dcSMarri Devender Rao "/O=openbmc-project.xyz/CN=localhost" 43947258dcSMarri Devender Rao " -nodes"; 44947258dcSMarri Devender Rao auto val = std::system(cmd.c_str()); 45947258dcSMarri Devender Rao if (val) 46947258dcSMarri Devender Rao { 47947258dcSMarri Devender Rao std::cout << "COMMAND Error: " << val << std::endl; 48947258dcSMarri Devender Rao } 49947258dcSMarri Devender Rao } 50947258dcSMarri Devender Rao void TearDown() override 51947258dcSMarri Devender Rao { 52947258dcSMarri Devender Rao fs::remove_all(certDir); 53947258dcSMarri Devender Rao fs::remove(certificateFile); 54947258dcSMarri Devender Rao } 55947258dcSMarri Devender Rao 56947258dcSMarri Devender Rao bool compareFiles(const std::string& file1, const std::string& file2) 57947258dcSMarri Devender Rao { 58947258dcSMarri Devender Rao std::ifstream f1(file1, std::ifstream::binary | std::ifstream::ate); 59947258dcSMarri Devender Rao std::ifstream f2(file2, std::ifstream::binary | std::ifstream::ate); 60947258dcSMarri Devender Rao 61947258dcSMarri Devender Rao if (f1.fail() || f2.fail()) 62947258dcSMarri Devender Rao { 63947258dcSMarri Devender Rao return false; // file problem 64947258dcSMarri Devender Rao } 65947258dcSMarri Devender Rao 66947258dcSMarri Devender Rao if (f1.tellg() != f2.tellg()) 67947258dcSMarri Devender Rao { 68947258dcSMarri Devender Rao return false; // size mismatch 69947258dcSMarri Devender Rao } 70947258dcSMarri Devender Rao 71947258dcSMarri Devender Rao // seek back to beginning and use std::equal to compare contents 72947258dcSMarri Devender Rao f1.seekg(0, std::ifstream::beg); 73947258dcSMarri Devender Rao f2.seekg(0, std::ifstream::beg); 74947258dcSMarri Devender Rao return std::equal(std::istreambuf_iterator<char>(f1.rdbuf()), 75947258dcSMarri Devender Rao std::istreambuf_iterator<char>(), 76947258dcSMarri Devender Rao std::istreambuf_iterator<char>(f2.rdbuf())); 77947258dcSMarri Devender Rao } 78947258dcSMarri Devender Rao 79947258dcSMarri Devender Rao protected: 80947258dcSMarri Devender Rao sdbusplus::bus::bus bus; 81947258dcSMarri Devender Rao std::string certificateFile; 82947258dcSMarri Devender Rao 83947258dcSMarri Devender Rao std::string certDir; 84947258dcSMarri Devender Rao }; 85947258dcSMarri Devender Rao 86947258dcSMarri Devender Rao class MainApp 87947258dcSMarri Devender Rao { 88947258dcSMarri Devender Rao public: 89947258dcSMarri Devender Rao MainApp(phosphor::certs::Manager* manager) : manager(manager) 90947258dcSMarri Devender Rao { 91947258dcSMarri Devender Rao } 92947258dcSMarri Devender Rao void install(std::string& path) 93947258dcSMarri Devender Rao { 94947258dcSMarri Devender Rao manager->install(path); 95947258dcSMarri Devender Rao } 96947258dcSMarri Devender Rao phosphor::certs::Manager* manager; 97947258dcSMarri Devender Rao }; 98947258dcSMarri Devender Rao 99947258dcSMarri Devender Rao class MockCertManager : public phosphor::certs::Manager 100947258dcSMarri Devender Rao { 101947258dcSMarri Devender Rao public: 102947258dcSMarri Devender Rao MockCertManager(sdbusplus::bus::bus& bus, const char* path, 103947258dcSMarri Devender Rao std::string& type, std::string&& unit, 104947258dcSMarri Devender Rao std::string&& certPath) : 105947258dcSMarri Devender Rao Manager(bus, path, type, std::forward<std::string>(unit), 106947258dcSMarri Devender Rao std::forward<std::string>(certPath)) 107947258dcSMarri Devender Rao { 108947258dcSMarri Devender Rao } 109947258dcSMarri Devender Rao virtual ~MockCertManager() 110947258dcSMarri Devender Rao { 111947258dcSMarri Devender Rao } 112947258dcSMarri Devender Rao 113947258dcSMarri Devender Rao MOCK_METHOD0(clientInstall, void()); 114947258dcSMarri Devender Rao MOCK_METHOD0(serverInstall, void()); 115947258dcSMarri Devender Rao }; 116947258dcSMarri Devender Rao 117947258dcSMarri Devender Rao /** @brief Check if server install routine is invoked for server setup 118947258dcSMarri Devender Rao */ 119947258dcSMarri Devender Rao TEST_F(TestCertsManager, InvokeServerInstall) 120947258dcSMarri Devender Rao { 121947258dcSMarri Devender Rao std::string endpoint("https"); 122947258dcSMarri Devender Rao std::string unit("nginx.service"); 123947258dcSMarri Devender Rao std::string type("server"); 124947258dcSMarri Devender Rao std::string path(certDir + "/" + certificateFile); 125947258dcSMarri Devender Rao std::string verifyPath(path); 126947258dcSMarri Devender Rao auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint; 127947258dcSMarri Devender Rao MockCertManager manager(bus, objPath.c_str(), type, std::move(unit), 128947258dcSMarri Devender Rao std::move(path)); 129947258dcSMarri Devender Rao EXPECT_CALL(manager, serverInstall()).Times(1); 130947258dcSMarri Devender Rao 131947258dcSMarri Devender Rao MainApp mainApp(&manager); 132947258dcSMarri Devender Rao EXPECT_NO_THROW({ mainApp.install(certificateFile); }); 133947258dcSMarri Devender Rao EXPECT_TRUE(fs::exists(verifyPath)); 134947258dcSMarri Devender Rao } 135947258dcSMarri Devender Rao 136947258dcSMarri Devender Rao /** @brief Check if client install routine is invoked for client setup 137947258dcSMarri Devender Rao */ 138947258dcSMarri Devender Rao TEST_F(TestCertsManager, InvokeClientInstall) 139947258dcSMarri Devender Rao { 140947258dcSMarri Devender Rao std::string endpoint("ldap"); 141947258dcSMarri Devender Rao std::string unit("nslcd.service"); 142947258dcSMarri Devender Rao std::string type("client"); 143947258dcSMarri Devender Rao std::string path(certDir + "/" + certificateFile); 144947258dcSMarri Devender Rao std::string verifyPath(path); 145947258dcSMarri Devender Rao auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint; 146947258dcSMarri Devender Rao MockCertManager manager(bus, objPath.c_str(), type, std::move(unit), 147947258dcSMarri Devender Rao std::move(path)); 148947258dcSMarri Devender Rao EXPECT_CALL(manager, clientInstall()).Times(1); 149947258dcSMarri Devender Rao MainApp mainApp(&manager); 150947258dcSMarri Devender Rao EXPECT_NO_THROW({ mainApp.install(certificateFile); }); 151947258dcSMarri Devender Rao EXPECT_TRUE(fs::exists(verifyPath)); 152947258dcSMarri Devender Rao } 153947258dcSMarri Devender Rao 154947258dcSMarri Devender Rao /** @brief Compare the installed certificate with the copied certificate 155947258dcSMarri Devender Rao */ 156947258dcSMarri Devender Rao TEST_F(TestCertsManager, CompareInstalledCertificate) 157947258dcSMarri Devender Rao { 158947258dcSMarri Devender Rao std::string endpoint("ldap"); 159947258dcSMarri Devender Rao std::string unit("nslcd.service"); 160947258dcSMarri Devender Rao std::string type("client"); 161947258dcSMarri Devender Rao std::string path(certDir + "/" + certificateFile); 162947258dcSMarri Devender Rao std::string verifyPath(path); 163947258dcSMarri Devender Rao auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint; 164947258dcSMarri Devender Rao MockCertManager manager(bus, objPath.c_str(), type, std::move(unit), 165947258dcSMarri Devender Rao std::move(path)); 166947258dcSMarri Devender Rao EXPECT_CALL(manager, clientInstall()).Times(1); 167947258dcSMarri Devender Rao MainApp mainApp(&manager); 168947258dcSMarri Devender Rao EXPECT_NO_THROW({ mainApp.install(certificateFile); }); 169947258dcSMarri Devender Rao EXPECT_TRUE(fs::exists(verifyPath)); 170947258dcSMarri Devender Rao EXPECT_TRUE(compareFiles(verifyPath, certificateFile)); 171947258dcSMarri Devender Rao } 172*e6597c5bSMarri Devender Rao 173*e6597c5bSMarri Devender Rao /** @brief Check if install fails if certificate file is not found 174*e6597c5bSMarri Devender Rao */ 175*e6597c5bSMarri Devender Rao TEST_F(TestCertsManager, TestNoCertificateFile) 176*e6597c5bSMarri Devender Rao { 177*e6597c5bSMarri Devender Rao std::string endpoint("ldap"); 178*e6597c5bSMarri Devender Rao std::string unit("nslcd.service"); 179*e6597c5bSMarri Devender Rao std::string type("client"); 180*e6597c5bSMarri Devender Rao std::string path(certDir + "/" + certificateFile); 181*e6597c5bSMarri Devender Rao std::string verifyPath(path); 182*e6597c5bSMarri Devender Rao auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint; 183*e6597c5bSMarri Devender Rao MockCertManager manager(bus, objPath.c_str(), type, std::move(unit), 184*e6597c5bSMarri Devender Rao std::move(path)); 185*e6597c5bSMarri Devender Rao EXPECT_CALL(manager, clientInstall()).Times(0); 186*e6597c5bSMarri Devender Rao MainApp mainApp(&manager); 187*e6597c5bSMarri Devender Rao std::string certpath = "nofile.pem"; 188*e6597c5bSMarri Devender Rao EXPECT_THROW( 189*e6597c5bSMarri Devender Rao { 190*e6597c5bSMarri Devender Rao try 191*e6597c5bSMarri Devender Rao { 192*e6597c5bSMarri Devender Rao mainApp.install(certpath); 193*e6597c5bSMarri Devender Rao } 194*e6597c5bSMarri Devender Rao catch (const InternalFailure& e) 195*e6597c5bSMarri Devender Rao { 196*e6597c5bSMarri Devender Rao throw; 197*e6597c5bSMarri Devender Rao } 198*e6597c5bSMarri Devender Rao }, 199*e6597c5bSMarri Devender Rao InternalFailure); 200*e6597c5bSMarri Devender Rao EXPECT_FALSE(fs::exists(verifyPath)); 201*e6597c5bSMarri Devender Rao } 202*e6597c5bSMarri Devender Rao 203*e6597c5bSMarri Devender Rao /** @brief Check if install fails if certificate file is empty 204*e6597c5bSMarri Devender Rao */ 205*e6597c5bSMarri Devender Rao TEST_F(TestCertsManager, TestEmptyCertificateFile) 206*e6597c5bSMarri Devender Rao { 207*e6597c5bSMarri Devender Rao std::string endpoint("ldap"); 208*e6597c5bSMarri Devender Rao std::string unit("nslcd.service"); 209*e6597c5bSMarri Devender Rao std::string type("client"); 210*e6597c5bSMarri Devender Rao 211*e6597c5bSMarri Devender Rao std::string emptyFile("certcorrupted.pem"); 212*e6597c5bSMarri Devender Rao std::ofstream ofs; 213*e6597c5bSMarri Devender Rao ofs.open(emptyFile, std::ofstream::out); 214*e6597c5bSMarri Devender Rao ofs.close(); 215*e6597c5bSMarri Devender Rao 216*e6597c5bSMarri Devender Rao std::string path(certDir + "/" + emptyFile); 217*e6597c5bSMarri Devender Rao std::string verifyPath(path); 218*e6597c5bSMarri Devender Rao auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint; 219*e6597c5bSMarri Devender Rao MockCertManager manager(bus, objPath.c_str(), type, std::move(unit), 220*e6597c5bSMarri Devender Rao std::move(path)); 221*e6597c5bSMarri Devender Rao EXPECT_CALL(manager, clientInstall()).Times(0); 222*e6597c5bSMarri Devender Rao MainApp mainApp(&manager); 223*e6597c5bSMarri Devender Rao EXPECT_THROW( 224*e6597c5bSMarri Devender Rao { 225*e6597c5bSMarri Devender Rao try 226*e6597c5bSMarri Devender Rao { 227*e6597c5bSMarri Devender Rao mainApp.install(emptyFile); 228*e6597c5bSMarri Devender Rao } 229*e6597c5bSMarri Devender Rao catch (const InvalidCertificate& e) 230*e6597c5bSMarri Devender Rao { 231*e6597c5bSMarri Devender Rao throw; 232*e6597c5bSMarri Devender Rao } 233*e6597c5bSMarri Devender Rao }, 234*e6597c5bSMarri Devender Rao InvalidCertificate); 235*e6597c5bSMarri Devender Rao EXPECT_FALSE(fs::exists(verifyPath)); 236*e6597c5bSMarri Devender Rao fs::remove(emptyFile); 237*e6597c5bSMarri Devender Rao } 238*e6597c5bSMarri Devender Rao 239*e6597c5bSMarri Devender Rao /** @brief Check if install fails if corrupted certificate file is not found 240*e6597c5bSMarri Devender Rao */ 241*e6597c5bSMarri Devender Rao TEST_F(TestCertsManager, TestInvalidCertificateFile) 242*e6597c5bSMarri Devender Rao { 243*e6597c5bSMarri Devender Rao std::string endpoint("ldap"); 244*e6597c5bSMarri Devender Rao std::string unit("nslcd.service"); 245*e6597c5bSMarri Devender Rao std::string type("client"); 246*e6597c5bSMarri Devender Rao 247*e6597c5bSMarri Devender Rao std::string corrputedFile("certcorrupted.pem"); 248*e6597c5bSMarri Devender Rao std::ofstream ofs; 249*e6597c5bSMarri Devender Rao ofs.open(corrputedFile, std::ofstream::out); 250*e6597c5bSMarri Devender Rao ofs << " PUBLIC KEY PRIVATE KEY XXXX YYYY ZZZZ"; 251*e6597c5bSMarri Devender Rao ofs.close(); 252*e6597c5bSMarri Devender Rao 253*e6597c5bSMarri Devender Rao std::string path(certDir + "/" + corrputedFile); 254*e6597c5bSMarri Devender Rao std::string verifyPath(path); 255*e6597c5bSMarri Devender Rao auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint; 256*e6597c5bSMarri Devender Rao MockCertManager manager(bus, objPath.c_str(), type, std::move(unit), 257*e6597c5bSMarri Devender Rao std::move(path)); 258*e6597c5bSMarri Devender Rao EXPECT_CALL(manager, clientInstall()).Times(0); 259*e6597c5bSMarri Devender Rao MainApp mainApp(&manager); 260*e6597c5bSMarri Devender Rao EXPECT_THROW( 261*e6597c5bSMarri Devender Rao { 262*e6597c5bSMarri Devender Rao try 263*e6597c5bSMarri Devender Rao { 264*e6597c5bSMarri Devender Rao mainApp.install(corrputedFile); 265*e6597c5bSMarri Devender Rao } 266*e6597c5bSMarri Devender Rao catch (const InvalidCertificate& e) 267*e6597c5bSMarri Devender Rao { 268*e6597c5bSMarri Devender Rao throw; 269*e6597c5bSMarri Devender Rao } 270*e6597c5bSMarri Devender Rao }, 271*e6597c5bSMarri Devender Rao InvalidCertificate); 272*e6597c5bSMarri Devender Rao EXPECT_FALSE(fs::exists(verifyPath)); 273*e6597c5bSMarri Devender Rao fs::remove(corrputedFile); 274*e6597c5bSMarri Devender Rao } 275