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