blob: ee03229c751a6d91609f6dc87919fa24a1da5bc8 [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 }
99 phosphor::certs::Manager* manager;
100};
101
102class MockCertManager : public phosphor::certs::Manager
103{
104 public:
105 MockCertManager(sdbusplus::bus::bus& bus, const char* path,
106 std::string& type, std::string&& unit,
107 std::string&& certPath) :
108 Manager(bus, path, type, std::forward<std::string>(unit),
109 std::forward<std::string>(certPath))
110 {
111 }
112 virtual ~MockCertManager()
113 {
114 }
115
116 MOCK_METHOD0(clientInstall, void());
117 MOCK_METHOD0(serverInstall, void());
118};
119
120/** @brief Check if server install routine is invoked for server setup
121 */
122TEST_F(TestCertsManager, InvokeServerInstall)
123{
124 std::string endpoint("https");
125 std::string unit("nginx.service");
126 std::string type("server");
127 std::string path(certDir + "/" + certificateFile);
128 std::string verifyPath(path);
129 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
130 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
131 std::move(path));
132 EXPECT_CALL(manager, serverInstall()).Times(1);
133
134 MainApp mainApp(&manager);
135 EXPECT_NO_THROW({ mainApp.install(certificateFile); });
136 EXPECT_TRUE(fs::exists(verifyPath));
137}
138
139/** @brief Check if client install routine is invoked for client setup
140 */
141TEST_F(TestCertsManager, InvokeClientInstall)
142{
143 std::string endpoint("ldap");
144 std::string unit("nslcd.service");
145 std::string type("client");
146 std::string path(certDir + "/" + certificateFile);
147 std::string verifyPath(path);
148 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
149 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
150 std::move(path));
151 EXPECT_CALL(manager, clientInstall()).Times(1);
152 MainApp mainApp(&manager);
153 EXPECT_NO_THROW({ mainApp.install(certificateFile); });
154 EXPECT_TRUE(fs::exists(verifyPath));
155}
156
157/** @brief Compare the installed certificate with the copied certificate
158 */
159TEST_F(TestCertsManager, CompareInstalledCertificate)
160{
161 std::string endpoint("ldap");
162 std::string unit("nslcd.service");
163 std::string type("client");
164 std::string path(certDir + "/" + certificateFile);
165 std::string verifyPath(path);
166 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
167 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
168 std::move(path));
169 EXPECT_CALL(manager, clientInstall()).Times(1);
170 MainApp mainApp(&manager);
171 EXPECT_NO_THROW({ mainApp.install(certificateFile); });
172 EXPECT_TRUE(fs::exists(verifyPath));
173 EXPECT_TRUE(compareFiles(verifyPath, certificateFile));
174}
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500175
176/** @brief Check if install fails if certificate file is not found
177 */
178TEST_F(TestCertsManager, TestNoCertificateFile)
179{
180 std::string endpoint("ldap");
181 std::string unit("nslcd.service");
182 std::string type("client");
183 std::string path(certDir + "/" + certificateFile);
184 std::string verifyPath(path);
185 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
186 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
187 std::move(path));
188 EXPECT_CALL(manager, clientInstall()).Times(0);
189 MainApp mainApp(&manager);
190 std::string certpath = "nofile.pem";
191 EXPECT_THROW(
192 {
193 try
194 {
195 mainApp.install(certpath);
196 }
197 catch (const InternalFailure& e)
198 {
199 throw;
200 }
201 },
202 InternalFailure);
203 EXPECT_FALSE(fs::exists(verifyPath));
204}
205
206/** @brief Check if install fails if certificate file is empty
207 */
208TEST_F(TestCertsManager, TestEmptyCertificateFile)
209{
210 std::string endpoint("ldap");
211 std::string unit("nslcd.service");
212 std::string type("client");
213
Marri Devender Raoddf64862018-10-03 07:11:02 -0500214 std::string emptyFile("emptycert.pem");
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500215 std::ofstream ofs;
216 ofs.open(emptyFile, std::ofstream::out);
217 ofs.close();
218
219 std::string path(certDir + "/" + emptyFile);
220 std::string verifyPath(path);
221 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
222 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
223 std::move(path));
224 EXPECT_CALL(manager, clientInstall()).Times(0);
225 MainApp mainApp(&manager);
226 EXPECT_THROW(
227 {
228 try
229 {
230 mainApp.install(emptyFile);
231 }
232 catch (const InvalidCertificate& e)
233 {
234 throw;
235 }
236 },
237 InvalidCertificate);
238 EXPECT_FALSE(fs::exists(verifyPath));
239 fs::remove(emptyFile);
240}
241
Marri Devender Raoddf64862018-10-03 07:11:02 -0500242/** @brief Check if install fails if certificate file is corrupted
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500243 */
244TEST_F(TestCertsManager, TestInvalidCertificateFile)
245{
246 std::string endpoint("ldap");
247 std::string unit("nslcd.service");
248 std::string type("client");
249
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500250 std::ofstream ofs;
Marri Devender Raoddf64862018-10-03 07:11:02 -0500251 ofs.open(certificateFile, std::ofstream::out);
252 ofs << "-----BEGIN CERTIFICATE-----";
253 ofs << "ADD_SOME_INVALID_DATA_INTO_FILE";
254 ofs << "-----END CERTIFICATE-----";
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500255 ofs.close();
256
Marri Devender Raoddf64862018-10-03 07:11:02 -0500257 std::string path(certDir + "/" + certificateFile);
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500258 std::string verifyPath(path);
259 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
260 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
261 std::move(path));
262 EXPECT_CALL(manager, clientInstall()).Times(0);
263 MainApp mainApp(&manager);
264 EXPECT_THROW(
265 {
266 try
267 {
Marri Devender Raoddf64862018-10-03 07:11:02 -0500268 mainApp.install(certificateFile);
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500269 }
270 catch (const InvalidCertificate& e)
271 {
272 throw;
273 }
274 },
275 InvalidCertificate);
276 EXPECT_FALSE(fs::exists(verifyPath));
Marri Devender Raoe6597c52018-10-01 06:36:55 -0500277}
Marri Devender Raoddf64862018-10-03 07:11:02 -0500278
279/**
280 * Class to generate private and certificate only file and test verification
281 */
282class TestInvalidCertsManager : public ::testing::Test
283{
284 public:
285 TestInvalidCertsManager() : bus(sdbusplus::bus::new_default())
286 {
287 }
288 void SetUp() override
289 {
290 char dirTemplate[] = "/tmp/FakeCerts.XXXXXX";
291 auto dirPtr = mkdtemp(dirTemplate);
292 if (dirPtr == NULL)
293 {
294 throw std::bad_alloc();
295 }
296 certDir = dirPtr;
297 certificateFile = "cert.pem";
298 keyFile = "key.pem";
299 std::string cmd = "openssl req -x509 -sha256 -newkey rsa:2048 ";
300 cmd += "-keyout key.pem -out cert.pem -days 3650 ";
301 cmd += "-subj "
302 "/O=openbmc-project.xyz/CN=localhost"
303 " -nodes";
304
305 auto val = std::system(cmd.c_str());
306 if (val)
307 {
308 std::cout << "command Error: " << val << std::endl;
309 }
310 }
311 void TearDown() override
312 {
313 fs::remove_all(certDir);
314 fs::remove(certificateFile);
315 fs::remove(keyFile);
316 }
317
318 protected:
319 sdbusplus::bus::bus bus;
320 std::string certificateFile;
321 std::string keyFile;
322 std::string certDir;
323};
324
325/** @brief Check install fails if private key is missing in certificate file
326 */
327TEST_F(TestInvalidCertsManager, TestMissingPrivateKey)
328{
329 std::string endpoint("ldap");
330 std::string unit("nslcd.service");
331 std::string type("client");
332 std::string path(certDir + "/" + certificateFile);
333 std::string verifyPath(path);
334
335 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
336 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
337 std::move(path));
338 EXPECT_CALL(manager, clientInstall()).Times(0);
339 MainApp mainApp(&manager);
340 EXPECT_THROW(
341 {
342 try
343 {
344 mainApp.install(certificateFile);
345 }
346 catch (const InvalidCertificate& e)
347 {
348 throw;
349 }
350 },
351 InvalidCertificate);
352 EXPECT_FALSE(fs::exists(verifyPath));
353}
354
355/** @brief Check install fails if ceritificate is missing in certificate file
356 */
357TEST_F(TestInvalidCertsManager, TestMissingCeritificate)
358{
359 std::string endpoint("ldap");
360 std::string unit("nslcd.service");
361 std::string type("client");
362 std::string path(certDir + "/" + keyFile);
363 std::string verifyPath(path);
364
365 auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
366 MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
367 std::move(path));
368 EXPECT_CALL(manager, clientInstall()).Times(0);
369 MainApp mainApp(&manager);
370 EXPECT_THROW(
371 {
372 try
373 {
374 mainApp.install(keyFile);
375 }
376 catch (const InvalidCertificate& e)
377 {
378 throw;
379 }
380 },
381 InvalidCertificate);
382 EXPECT_FALSE(fs::exists(verifyPath));
383}