blob: 82793a254744edebeea387b0433f6939e0d6d4d2 [file] [log] [blame]
Marri Devender Rao8841dbd2019-03-04 05:43:55 -06001#include "certificate.hpp"
Marri Devender Rao947258d2018-09-25 10:52:24 -05002#include "certs_manager.hpp"
3
4#include <algorithm>
Marri Devender Rao8841dbd2019-03-04 05:43:55 -06005#include <filesystem>
Marri Devender Rao947258d2018-09-25 10:52:24 -05006#include <fstream>
7#include <iterator>
8#include <string>
9#include <xyz/openbmc_project/Certs/Install/error.hpp>
10#include <xyz/openbmc_project/Common/error.hpp>
11
Marri Devender Rao947258d2018-09-25 10:52:24 -050012#include <gtest/gtest.h>
Marri Devender Rao8841dbd2019-03-04 05:43:55 -060013namespace fs = std::filesystem;
Marri Devender Rao947258d2018-09-25 10:52:24 -050014static constexpr auto BUSNAME = "xyz.openbmc_project.Certs.Manager";
15static constexpr auto OBJPATH = "/xyz/openbmc_project/certs";
16using InternalFailure =
17 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
18
Marri Devender Raoe6597c52018-10-01 06:36:55 -050019using InvalidCertificate =
20 sdbusplus::xyz::openbmc_project::Certs::Install::Error::InvalidCertificate;
Marri Devender Rao8841dbd2019-03-04 05:43:55 -060021using namespace phosphor::certs;
Marri Devender Raoe6597c52018-10-01 06:36:55 -050022
Marri Devender Raoddf64862018-10-03 07:11:02 -050023/**
24 * Class to generate certificate file and test verification of certificate file
25 */
Marri Devender Rao8841dbd2019-03-04 05:43:55 -060026class TestCertificates : public ::testing::Test
Marri Devender Rao947258d2018-09-25 10:52:24 -050027{
28 public:
Marri Devender Rao8841dbd2019-03-04 05:43:55 -060029 TestCertificates() : bus(sdbusplus::bus::new_default())
Marri Devender Rao947258d2018-09-25 10:52:24 -050030 {
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
Marri Devender Rao947258d2018-09-25 10:52:24 -0500106/** @brief Check if server install routine is invoked for server setup
107 */
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600108TEST_F(TestCertificates, InvokeServerInstall)
Marri Devender Rao947258d2018-09-25 10:52:24 -0500109{
110 std::string endpoint("https");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600111 std::string unit("");
Marri Devender Rao947258d2018-09-25 10:52:24 -0500112 std::string type("server");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600113 std::string installPath(certDir + "/" + certificateFile);
114 std::string verifyPath(installPath);
115 UnitsToRestart verifyUnit(unit);
Marri Devender Rao947258d2018-09-25 10:52:24 -0500116 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600117 Certificate certificate(bus, objPath, type, unit, installPath,
118 certificateFile);
Marri Devender Rao947258d2018-09-25 10:52:24 -0500119 EXPECT_TRUE(fs::exists(verifyPath));
120}
121
122/** @brief Check if client install routine is invoked for client setup
123 */
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600124TEST_F(TestCertificates, InvokeClientInstall)
Marri Devender Rao947258d2018-09-25 10:52:24 -0500125{
126 std::string endpoint("ldap");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600127 std::string unit("");
128 std::string type("server");
129 std::string installPath(certDir + "/" + certificateFile);
130 std::string verifyPath(installPath);
131 UnitsToRestart verifyUnit(unit);
Marri Devender Rao947258d2018-09-25 10:52:24 -0500132 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600133 Certificate certificate(bus, objPath, type, unit, installPath,
134 certificateFile);
Jayanth Othayothb50789c2018-10-09 07:13:54 -0500135 EXPECT_TRUE(fs::exists(verifyPath));
136}
137
138/** @brief Check if authority install routine is invoked for authority setup
139 */
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600140TEST_F(TestCertificates, InvokeAuthorityInstall)
Jayanth Othayothb50789c2018-10-09 07:13:54 -0500141{
142 std::string endpoint("ldap");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600143 std::string unit("");
Jayanth Othayothb50789c2018-10-09 07:13:54 -0500144 std::string type("authority");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600145 std::string installPath(certDir + "/" + certificateFile);
146 std::string verifyPath(installPath);
147 UnitsToRestart verifyUnit(unit);
Jayanth Othayothb50789c2018-10-09 07:13:54 -0500148 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600149 Certificate certificate(bus, objPath, type, unit, installPath,
150 certificateFile);
Marri Devender Rao947258d2018-09-25 10:52:24 -0500151 EXPECT_TRUE(fs::exists(verifyPath));
152}
153
154/** @brief Compare the installed certificate with the copied certificate
155 */
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600156TEST_F(TestCertificates, CompareInstalledCertificate)
Marri Devender Rao947258d2018-09-25 10:52:24 -0500157{
158 std::string endpoint("ldap");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600159 std::string unit("");
Marri Devender Rao947258d2018-09-25 10:52:24 -0500160 std::string type("client");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600161 std::string installPath(certDir + "/" + certificateFile);
162 std::string verifyPath(installPath);
163 UnitsToRestart verifyUnit(unit);
Marri Devender Rao947258d2018-09-25 10:52:24 -0500164 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600165 Certificate certificate(bus, objPath, type, unit, installPath,
166 certificateFile);
Marri Devender Rao947258d2018-09-25 10:52:24 -0500167 EXPECT_TRUE(fs::exists(verifyPath));
168 EXPECT_TRUE(compareFiles(verifyPath, certificateFile));
169}
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500170
171/** @brief Check if install fails if certificate file is not found
172 */
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600173TEST_F(TestCertificates, TestNoCertificateFile)
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500174{
175 std::string endpoint("ldap");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600176 std::string unit("");
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500177 std::string type("client");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600178 std::string installPath(certDir + "/" + certificateFile);
179 std::string verifyPath(installPath);
Jayanth Othayothb50789c2018-10-09 07:13:54 -0500180 std::string verifyUnit(unit);
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500181 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600182 std::string uploadFile = "nofile.pem";
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500183 EXPECT_THROW(
184 {
185 try
186 {
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600187 Certificate certificate(bus, objPath, type, unit, installPath,
188 uploadFile);
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500189 }
190 catch (const InternalFailure& e)
191 {
192 throw;
193 }
194 },
195 InternalFailure);
196 EXPECT_FALSE(fs::exists(verifyPath));
197}
198
199/** @brief Check if install fails if certificate file is empty
200 */
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600201TEST_F(TestCertificates, TestEmptyCertificateFile)
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500202{
203 std::string endpoint("ldap");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600204 std::string unit("");
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500205 std::string type("client");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600206 std::string installPath(certDir + "/" + certificateFile);
207 std::string verifyPath(installPath);
208 std::string verifyUnit(unit);
209 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
Marri Devender Raoddf64862018-10-03 07:11:02 -0500210 std::string emptyFile("emptycert.pem");
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500211 std::ofstream ofs;
212 ofs.open(emptyFile, std::ofstream::out);
213 ofs.close();
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500214 EXPECT_THROW(
215 {
216 try
217 {
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600218 Certificate certificate(bus, objPath, type, unit, installPath,
219 emptyFile);
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500220 }
221 catch (const InvalidCertificate& e)
222 {
223 throw;
224 }
225 },
226 InvalidCertificate);
227 EXPECT_FALSE(fs::exists(verifyPath));
228 fs::remove(emptyFile);
229}
230
Marri Devender Raoddf64862018-10-03 07:11:02 -0500231/** @brief Check if install fails if certificate file is corrupted
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500232 */
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600233TEST_F(TestCertificates, TestInvalidCertificateFile)
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500234{
235 std::string endpoint("ldap");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600236 std::string unit("");
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500237 std::string type("client");
238
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500239 std::ofstream ofs;
Marri Devender Raoddf64862018-10-03 07:11:02 -0500240 ofs.open(certificateFile, std::ofstream::out);
241 ofs << "-----BEGIN CERTIFICATE-----";
242 ofs << "ADD_SOME_INVALID_DATA_INTO_FILE";
243 ofs << "-----END CERTIFICATE-----";
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500244 ofs.close();
245
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600246 std::string installPath(certDir + "/" + certificateFile);
247 std::string verifyPath(installPath);
Jayanth Othayothb50789c2018-10-09 07:13:54 -0500248 std::string verifyUnit(unit);
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500249 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500250 EXPECT_THROW(
251 {
252 try
253 {
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600254 Certificate certificate(bus, objPath, type, unit, installPath,
255 certificateFile);
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500256 }
257 catch (const InvalidCertificate& e)
258 {
259 throw;
260 }
261 },
262 InvalidCertificate);
263 EXPECT_FALSE(fs::exists(verifyPath));
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500264}
Marri Devender Raoddf64862018-10-03 07:11:02 -0500265
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600266/** @brief check certificate delete at manager level
267 */
268TEST_F(TestCertificates, TestCertManagerDelete)
Marri Devender Rao9abfae82018-10-03 08:10:35 -0500269{
270 std::string endpoint("ldap");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600271 std::string unit("");
Marri Devender Rao9abfae82018-10-03 08:10:35 -0500272 std::string type("client");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600273 std::string installPath(certDir + "/" + certificateFile);
274 std::string verifyPath(installPath);
Marri Devender Rao9abfae82018-10-03 08:10:35 -0500275 std::string verifyUnit(unit);
276 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600277 Manager manager(bus, objPath.c_str(), type, std::move(unit),
278 std::move(installPath));
Marri Devender Rao9abfae82018-10-03 08:10:35 -0500279 MainApp mainApp(&manager);
Marri Devender Rao9abfae82018-10-03 08:10:35 -0500280 // delete certificate file and verify file is deleted
281 mainApp.delete_();
282 EXPECT_FALSE(fs::exists(verifyPath));
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600283}
284
285/** @brief check certificate install at manager level
286 */
287TEST_F(TestCertificates, TestCertManagerInstall)
288{
289 std::string endpoint("ldap");
290 std::string unit("");
291 std::string type("client");
292 std::string installPath(certDir + "/" + certificateFile);
293 std::string verifyPath(installPath);
294 std::string verifyUnit(unit);
295 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
296 Manager manager(bus, objPath.c_str(), type, std::move(unit),
297 std::move(installPath));
298 MainApp mainApp(&manager);
299 mainApp.install(certificateFile);
300 EXPECT_TRUE(fs::exists(verifyPath));
Marri Devender Rao9abfae82018-10-03 08:10:35 -0500301}
302
Marri Devender Raoddf64862018-10-03 07:11:02 -0500303/**
304 * Class to generate private and certificate only file and test verification
305 */
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600306class TestInvalidCertificate : public ::testing::Test
Marri Devender Raoddf64862018-10-03 07:11:02 -0500307{
308 public:
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600309 TestInvalidCertificate() : bus(sdbusplus::bus::new_default())
Marri Devender Raoddf64862018-10-03 07:11:02 -0500310 {
311 }
312 void SetUp() override
313 {
314 char dirTemplate[] = "/tmp/FakeCerts.XXXXXX";
315 auto dirPtr = mkdtemp(dirTemplate);
316 if (dirPtr == NULL)
317 {
318 throw std::bad_alloc();
319 }
320 certDir = dirPtr;
321 certificateFile = "cert.pem";
322 keyFile = "key.pem";
323 std::string cmd = "openssl req -x509 -sha256 -newkey rsa:2048 ";
324 cmd += "-keyout key.pem -out cert.pem -days 3650 ";
325 cmd += "-subj "
326 "/O=openbmc-project.xyz/CN=localhost"
327 " -nodes";
328
329 auto val = std::system(cmd.c_str());
330 if (val)
331 {
332 std::cout << "command Error: " << val << std::endl;
333 }
334 }
335 void TearDown() override
336 {
337 fs::remove_all(certDir);
338 fs::remove(certificateFile);
339 fs::remove(keyFile);
340 }
341
342 protected:
343 sdbusplus::bus::bus bus;
344 std::string certificateFile;
345 std::string keyFile;
346 std::string certDir;
347};
348
349/** @brief Check install fails if private key is missing in certificate file
350 */
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600351TEST_F(TestInvalidCertificate, TestMissingPrivateKey)
Marri Devender Raoddf64862018-10-03 07:11:02 -0500352{
353 std::string endpoint("ldap");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600354 std::string unit("");
Marri Devender Raoddf64862018-10-03 07:11:02 -0500355 std::string type("client");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600356 std::string installPath(certDir + "/" + certificateFile);
357 std::string verifyPath(installPath);
Jayanth Othayothb50789c2018-10-09 07:13:54 -0500358 std::string verifyUnit(unit);
Marri Devender Raoddf64862018-10-03 07:11:02 -0500359 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600360 EXPECT_THROW(
361 {
362 try
363 {
364 Certificate certificate(bus, objPath, type, unit, installPath,
365 certificateFile);
366 }
367 catch (const InvalidCertificate& e)
368 {
369 throw;
370 }
371 },
372 InvalidCertificate);
373 EXPECT_FALSE(fs::exists(verifyPath));
374}
375
376/** @brief Check install fails if ceritificate is missing in certificate file
377 */
378TEST_F(TestInvalidCertificate, TestMissingCeritificate)
379{
380 std::string endpoint("ldap");
381 std::string unit("");
382 std::string type("client");
383 std::string installPath(certDir + "/" + keyFile);
384 std::string verifyPath(installPath);
385 std::string verifyUnit(unit);
386
387 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
388 EXPECT_THROW(
389 {
390 try
391 {
392 Certificate certificate(bus, objPath, type, unit, installPath,
393 keyFile);
394 }
395 catch (const InvalidCertificate& e)
396 {
397 throw;
398 }
399 },
400 InvalidCertificate);
401 EXPECT_FALSE(fs::exists(verifyPath));
402}
403
404/** @brief Check if Manager install method fails for invalid certificate file
405 */
406TEST_F(TestInvalidCertificate, TestCertManagerInstall)
407{
408 std::string endpoint("ldap");
409 std::string unit("");
410 std::string type("client");
411 std::string installPath(certDir + "/" + certificateFile);
412 std::string verifyPath(installPath);
413 std::string verifyUnit(unit);
414 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
415 Manager manager(bus, objPath.c_str(), type, std::move(unit),
416 std::move(installPath));
Marri Devender Raoddf64862018-10-03 07:11:02 -0500417 MainApp mainApp(&manager);
418 EXPECT_THROW(
419 {
420 try
421 {
422 mainApp.install(certificateFile);
423 }
424 catch (const InvalidCertificate& e)
425 {
426 throw;
427 }
428 },
429 InvalidCertificate);
430 EXPECT_FALSE(fs::exists(verifyPath));
431}
432
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600433/** @brief Check if error is thrown when multiple certificates are installed
434 * At present only one certificate per service is allowed
Marri Devender Raoddf64862018-10-03 07:11:02 -0500435 */
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600436TEST_F(TestCertificates, TestCertInstallNotAllowed)
Marri Devender Raoddf64862018-10-03 07:11:02 -0500437{
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600438 using NotAllowed =
439 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
Marri Devender Raoddf64862018-10-03 07:11:02 -0500440 std::string endpoint("ldap");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600441 std::string unit("");
Marri Devender Raoddf64862018-10-03 07:11:02 -0500442 std::string type("client");
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600443 std::string installPath(certDir + "/" + certificateFile);
444 std::string verifyPath(installPath);
Jayanth Othayothb50789c2018-10-09 07:13:54 -0500445 std::string verifyUnit(unit);
Marri Devender Raoddf64862018-10-03 07:11:02 -0500446 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600447 Manager manager(bus, objPath.c_str(), type, std::move(unit),
448 std::move(installPath));
Marri Devender Raoddf64862018-10-03 07:11:02 -0500449 MainApp mainApp(&manager);
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600450 mainApp.install(certificateFile);
451 EXPECT_TRUE(fs::exists(verifyPath));
Marri Devender Raoddf64862018-10-03 07:11:02 -0500452 EXPECT_THROW(
453 {
454 try
455 {
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600456 // install second certificate
457 mainApp.install(certificateFile);
Marri Devender Raoddf64862018-10-03 07:11:02 -0500458 }
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600459 catch (const NotAllowed& e)
Marri Devender Raoddf64862018-10-03 07:11:02 -0500460 {
461 throw;
462 }
463 },
Marri Devender Rao8841dbd2019-03-04 05:43:55 -0600464 NotAllowed);
Marri Devender Rao9abfae82018-10-03 08:10:35 -0500465}