blob: f1a75870662565065078c6c468f94a2783a16616 [file] [log] [blame]
Marri Devender Rao947258d2018-09-25 10:52:24 -05001#include "certs_manager.hpp"
2
3#include <algorithm>
4#include <experimental/filesystem>
5#include <fstream>
6#include <iterator>
7#include <string>
8#include <xyz/openbmc_project/Certs/Install/error.hpp>
9#include <xyz/openbmc_project/Common/error.hpp>
10
11#include <gmock/gmock.h>
12#include <gtest/gtest.h>
13
14namespace fs = std::experimental::filesystem;
15static constexpr auto BUSNAME = "xyz.openbmc_project.Certs.Manager";
16static constexpr auto OBJPATH = "/xyz/openbmc_project/certs";
17using InternalFailure =
18 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
19
Marri Devender Raoe6597c52018-10-01 06:36:55 -050020using InvalidCertificate =
21 sdbusplus::xyz::openbmc_project::Certs::Install::Error::InvalidCertificate;
22
Marri Devender Raoddf64862018-10-03 07:11:02 -050023/**
24 * Class to generate certificate file and test verification of certificate file
25 */
Marri Devender Rao947258d2018-09-25 10:52:24 -050026class TestCertsManager : public ::testing::Test
27{
28 public:
29 TestCertsManager() : bus(sdbusplus::bus::new_default())
30 {
31 }
32 void SetUp() override
33 {
34 char dirTemplate[] = "/tmp/FakeCerts.XXXXXX";
35 auto dirPtr = mkdtemp(dirTemplate);
36 if (dirPtr == NULL)
37 {
38 throw std::bad_alloc();
39 }
40 certDir = dirPtr;
41 certificateFile = "cert.pem";
42 std::string cmd = "openssl req -x509 -sha256 -newkey rsa:2048 ";
43 cmd += "-keyout cert.pem -out cert.pem -days 3650 ";
44 cmd += "-subj "
45 "/O=openbmc-project.xyz/CN=localhost"
46 " -nodes";
47 auto val = std::system(cmd.c_str());
48 if (val)
49 {
50 std::cout << "COMMAND Error: " << val << std::endl;
51 }
52 }
53 void TearDown() override
54 {
55 fs::remove_all(certDir);
56 fs::remove(certificateFile);
57 }
58
59 bool compareFiles(const std::string& file1, const std::string& file2)
60 {
61 std::ifstream f1(file1, std::ifstream::binary | std::ifstream::ate);
62 std::ifstream f2(file2, std::ifstream::binary | std::ifstream::ate);
63
64 if (f1.fail() || f2.fail())
65 {
66 return false; // file problem
67 }
68
69 if (f1.tellg() != f2.tellg())
70 {
71 return false; // size mismatch
72 }
73
74 // seek back to beginning and use std::equal to compare contents
75 f1.seekg(0, std::ifstream::beg);
76 f2.seekg(0, std::ifstream::beg);
77 return std::equal(std::istreambuf_iterator<char>(f1.rdbuf()),
78 std::istreambuf_iterator<char>(),
79 std::istreambuf_iterator<char>(f2.rdbuf()));
80 }
81
82 protected:
83 sdbusplus::bus::bus bus;
84 std::string certificateFile;
85
86 std::string certDir;
87};
88
89class MainApp
90{
91 public:
92 MainApp(phosphor::certs::Manager* manager) : manager(manager)
93 {
94 }
95 void install(std::string& path)
96 {
97 manager->install(path);
98 }
Marri Devender Rao9abfae82018-10-03 08:10:35 -050099 void delete_()
100 {
101 manager->delete_();
102 }
Marri Devender Rao947258d2018-09-25 10:52:24 -0500103 phosphor::certs::Manager* manager;
104};
105
106class MockCertManager : public phosphor::certs::Manager
107{
108 public:
109 MockCertManager(sdbusplus::bus::bus& bus, const char* path,
110 std::string& type, std::string&& unit,
111 std::string&& certPath) :
112 Manager(bus, path, type, std::forward<std::string>(unit),
113 std::forward<std::string>(certPath))
114 {
115 }
116 virtual ~MockCertManager()
117 {
118 }
119
120 MOCK_METHOD0(clientInstall, void());
121 MOCK_METHOD0(serverInstall, void());
122};
123
124/** @brief Check if server install routine is invoked for server setup
125 */
126TEST_F(TestCertsManager, InvokeServerInstall)
127{
128 std::string endpoint("https");
129 std::string unit("nginx.service");
130 std::string type("server");
131 std::string path(certDir + "/" + certificateFile);
132 std::string verifyPath(path);
133 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
134 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
135 std::move(path));
136 EXPECT_CALL(manager, serverInstall()).Times(1);
137
138 MainApp mainApp(&manager);
139 EXPECT_NO_THROW({ mainApp.install(certificateFile); });
140 EXPECT_TRUE(fs::exists(verifyPath));
141}
142
143/** @brief Check if client install routine is invoked for client setup
144 */
145TEST_F(TestCertsManager, InvokeClientInstall)
146{
147 std::string endpoint("ldap");
148 std::string unit("nslcd.service");
149 std::string type("client");
150 std::string path(certDir + "/" + certificateFile);
151 std::string verifyPath(path);
152 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
153 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
154 std::move(path));
155 EXPECT_CALL(manager, clientInstall()).Times(1);
156 MainApp mainApp(&manager);
157 EXPECT_NO_THROW({ mainApp.install(certificateFile); });
158 EXPECT_TRUE(fs::exists(verifyPath));
159}
160
161/** @brief Compare the installed certificate with the copied certificate
162 */
163TEST_F(TestCertsManager, CompareInstalledCertificate)
164{
165 std::string endpoint("ldap");
166 std::string unit("nslcd.service");
167 std::string type("client");
168 std::string path(certDir + "/" + certificateFile);
169 std::string verifyPath(path);
170 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
171 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
172 std::move(path));
173 EXPECT_CALL(manager, clientInstall()).Times(1);
174 MainApp mainApp(&manager);
175 EXPECT_NO_THROW({ mainApp.install(certificateFile); });
176 EXPECT_TRUE(fs::exists(verifyPath));
177 EXPECT_TRUE(compareFiles(verifyPath, certificateFile));
178}
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500179
180/** @brief Check if install fails if certificate file is not found
181 */
182TEST_F(TestCertsManager, TestNoCertificateFile)
183{
184 std::string endpoint("ldap");
185 std::string unit("nslcd.service");
186 std::string type("client");
187 std::string path(certDir + "/" + certificateFile);
188 std::string verifyPath(path);
189 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
190 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
191 std::move(path));
192 EXPECT_CALL(manager, clientInstall()).Times(0);
193 MainApp mainApp(&manager);
194 std::string certpath = "nofile.pem";
195 EXPECT_THROW(
196 {
197 try
198 {
199 mainApp.install(certpath);
200 }
201 catch (const InternalFailure& e)
202 {
203 throw;
204 }
205 },
206 InternalFailure);
207 EXPECT_FALSE(fs::exists(verifyPath));
208}
209
210/** @brief Check if install fails if certificate file is empty
211 */
212TEST_F(TestCertsManager, TestEmptyCertificateFile)
213{
214 std::string endpoint("ldap");
215 std::string unit("nslcd.service");
216 std::string type("client");
217
Marri Devender Raoddf64862018-10-03 07:11:02 -0500218 std::string emptyFile("emptycert.pem");
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500219 std::ofstream ofs;
220 ofs.open(emptyFile, std::ofstream::out);
221 ofs.close();
222
223 std::string path(certDir + "/" + emptyFile);
224 std::string verifyPath(path);
225 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
226 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
227 std::move(path));
228 EXPECT_CALL(manager, clientInstall()).Times(0);
229 MainApp mainApp(&manager);
230 EXPECT_THROW(
231 {
232 try
233 {
234 mainApp.install(emptyFile);
235 }
236 catch (const InvalidCertificate& e)
237 {
238 throw;
239 }
240 },
241 InvalidCertificate);
242 EXPECT_FALSE(fs::exists(verifyPath));
243 fs::remove(emptyFile);
244}
245
Marri Devender Raoddf64862018-10-03 07:11:02 -0500246/** @brief Check if install fails if certificate file is corrupted
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500247 */
248TEST_F(TestCertsManager, TestInvalidCertificateFile)
249{
250 std::string endpoint("ldap");
251 std::string unit("nslcd.service");
252 std::string type("client");
253
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500254 std::ofstream ofs;
Marri Devender Raoddf64862018-10-03 07:11:02 -0500255 ofs.open(certificateFile, std::ofstream::out);
256 ofs << "-----BEGIN CERTIFICATE-----";
257 ofs << "ADD_SOME_INVALID_DATA_INTO_FILE";
258 ofs << "-----END CERTIFICATE-----";
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500259 ofs.close();
260
Marri Devender Raoddf64862018-10-03 07:11:02 -0500261 std::string path(certDir + "/" + certificateFile);
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500262 std::string verifyPath(path);
263 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
264 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
265 std::move(path));
266 EXPECT_CALL(manager, clientInstall()).Times(0);
267 MainApp mainApp(&manager);
268 EXPECT_THROW(
269 {
270 try
271 {
Marri Devender Raoddf64862018-10-03 07:11:02 -0500272 mainApp.install(certificateFile);
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500273 }
274 catch (const InvalidCertificate& e)
275 {
276 throw;
277 }
278 },
279 InvalidCertificate);
280 EXPECT_FALSE(fs::exists(verifyPath));
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500281}
Marri Devender Raoddf64862018-10-03 07:11:02 -0500282
Marri Devender Rao9abfae82018-10-03 08:10:35 -0500283/** @brief Test deletion of installed certificate file
284 */
285class MockReloadReset : public phosphor::certs::Manager
286{
287 public:
288 MockReloadReset(sdbusplus::bus::bus& bus, const char* path,
289 std::string& type, std::string&& unit,
290 std::string&& certPath) :
291 Manager(bus, path, type, std::forward<std::string>(unit),
292 std::forward<std::string>(certPath))
293 {
294 }
295 virtual ~MockReloadReset()
296 {
297 }
298
299 MOCK_METHOD1(reloadOrReset, void(const std::string& unit));
300};
301TEST_F(TestCertsManager, TestDeleteCertificate)
302{
303 std::string endpoint("ldap");
304 std::string unit("nslcd.service");
305 std::string type("client");
306 std::string path(certDir + "/" + certificateFile);
307 std::string verifyPath(path);
308 std::string verifyUnit(unit);
309 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
310 MockReloadReset manager(bus, objPath.c_str(), type, std::move(unit),
311 std::move(path));
312 EXPECT_CALL(manager, reloadOrReset(verifyUnit)).Times(2);
313 MainApp mainApp(&manager);
314 EXPECT_NO_THROW({ mainApp.install(certificateFile); });
315 EXPECT_TRUE(fs::exists(verifyPath));
316
317 // delete certificate file and verify file is deleted
318 mainApp.delete_();
319 EXPECT_FALSE(fs::exists(verifyPath));
320}
321
Marri Devender Raoddf64862018-10-03 07:11:02 -0500322/**
323 * Class to generate private and certificate only file and test verification
324 */
325class TestInvalidCertsManager : public ::testing::Test
326{
327 public:
328 TestInvalidCertsManager() : bus(sdbusplus::bus::new_default())
329 {
330 }
331 void SetUp() override
332 {
333 char dirTemplate[] = "/tmp/FakeCerts.XXXXXX";
334 auto dirPtr = mkdtemp(dirTemplate);
335 if (dirPtr == NULL)
336 {
337 throw std::bad_alloc();
338 }
339 certDir = dirPtr;
340 certificateFile = "cert.pem";
341 keyFile = "key.pem";
342 std::string cmd = "openssl req -x509 -sha256 -newkey rsa:2048 ";
343 cmd += "-keyout key.pem -out cert.pem -days 3650 ";
344 cmd += "-subj "
345 "/O=openbmc-project.xyz/CN=localhost"
346 " -nodes";
347
348 auto val = std::system(cmd.c_str());
349 if (val)
350 {
351 std::cout << "command Error: " << val << std::endl;
352 }
353 }
354 void TearDown() override
355 {
356 fs::remove_all(certDir);
357 fs::remove(certificateFile);
358 fs::remove(keyFile);
359 }
360
361 protected:
362 sdbusplus::bus::bus bus;
363 std::string certificateFile;
364 std::string keyFile;
365 std::string certDir;
366};
367
368/** @brief Check install fails if private key is missing in certificate file
369 */
370TEST_F(TestInvalidCertsManager, TestMissingPrivateKey)
371{
372 std::string endpoint("ldap");
373 std::string unit("nslcd.service");
374 std::string type("client");
375 std::string path(certDir + "/" + certificateFile);
376 std::string verifyPath(path);
377
378 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
379 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
380 std::move(path));
381 EXPECT_CALL(manager, clientInstall()).Times(0);
382 MainApp mainApp(&manager);
383 EXPECT_THROW(
384 {
385 try
386 {
387 mainApp.install(certificateFile);
388 }
389 catch (const InvalidCertificate& e)
390 {
391 throw;
392 }
393 },
394 InvalidCertificate);
395 EXPECT_FALSE(fs::exists(verifyPath));
396}
397
398/** @brief Check install fails if ceritificate is missing in certificate file
399 */
400TEST_F(TestInvalidCertsManager, TestMissingCeritificate)
401{
402 std::string endpoint("ldap");
403 std::string unit("nslcd.service");
404 std::string type("client");
405 std::string path(certDir + "/" + keyFile);
406 std::string verifyPath(path);
407
408 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
409 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
410 std::move(path));
411 EXPECT_CALL(manager, clientInstall()).Times(0);
412 MainApp mainApp(&manager);
413 EXPECT_THROW(
414 {
415 try
416 {
417 mainApp.install(keyFile);
418 }
419 catch (const InvalidCertificate& e)
420 {
421 throw;
422 }
423 },
424 InvalidCertificate);
425 EXPECT_FALSE(fs::exists(verifyPath));
Marri Devender Rao9abfae82018-10-03 08:10:35 -0500426}