blob: 3f7a17e5111a6416678a8e911dd8fe04978e5c96 [file] [log] [blame]
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -05001#include "certs_manager.hpp"
2
Marri Devender Raof4682712019-03-19 05:00:28 -05003#include <openssl/pem.h>
4#include <unistd.h>
5
Marri Devender Rao6ceec402019-02-01 03:15:19 -06006#include <phosphor-logging/elog-errors.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -05007#include <xyz/openbmc_project/Certs/error.hpp>
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -05008#include <xyz/openbmc_project/Common/error.hpp>
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -05009namespace phosphor
10{
11namespace certs
12{
Marri Devender Rao13965112019-02-27 08:47:12 -060013using InternalFailure =
14 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050015
Marri Devender Raof4682712019-03-19 05:00:28 -050016using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
17using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
18
19Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
20 const char* path, const CertificateType& type,
21 UnitsToRestart&& unit, CertInstallPath&& installPath) :
Marri Devender Rao6ceec402019-02-01 03:15:19 -060022 Ifaces(bus, path),
Marri Devender Raof4682712019-03-19 05:00:28 -050023 bus(bus), event(event), objectPath(path), certType(type),
24 unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
25 childPtr(nullptr)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050026{
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050027 using InvalidCertificate =
28 sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
29 using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
Marri Devender Raobf7c5882019-02-27 08:41:07 -060030 if (fs::exists(certInstallPath))
31 {
32 try
33 {
34 // TODO: Issue#3 At present supporting only one certificate to be
35 // uploaded this need to be revisited to support multiple
36 // certificates
37 auto certObjectPath = objectPath + '/' + '1';
38 certificatePtr = std::make_unique<Certificate>(
39 bus, certObjectPath, certType, unitToRestart, certInstallPath,
Marri Devender Rao8f80c352019-05-13 00:53:01 -050040 certInstallPath, true);
Marri Devender Raobf7c5882019-02-27 08:41:07 -060041 }
42 catch (const InternalFailure& e)
43 {
Marri Devender Raobf7c5882019-02-27 08:41:07 -060044 report<InternalFailure>();
45 }
46 catch (const InvalidCertificate& e)
47 {
Marri Devender Raobf7c5882019-02-27 08:41:07 -060048 report<InvalidCertificate>(
49 Reason("Existing certificate file is corrupted"));
50 }
51 }
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050052}
53
Marri Devender Rao6ceec402019-02-01 03:15:19 -060054void Manager::install(const std::string filePath)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050055{
Marri Devender Rao13965112019-02-27 08:47:12 -060056 using NotAllowed =
57 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
58 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
59 // TODO: Issue#3 At present supporting only one certificate to be
60 // uploaded this need to be revisited to support multiple
61 // certificates
62 if (certificatePtr != nullptr)
63 {
64 elog<NotAllowed>(Reason("Certificate already exist"));
65 }
66 auto certObjectPath = objectPath + '/' + '1';
Marri Devender Rao8f80c352019-05-13 00:53:01 -050067 certificatePtr = std::make_unique<Certificate>(
68 bus, certObjectPath, certType, unitToRestart, certInstallPath, filePath,
69 false);
Jayanth Othayoth589159f2018-09-28 08:32:39 -050070}
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -050071
72void Manager::delete_()
73{
Marri Devender Rao6ceec402019-02-01 03:15:19 -060074 // TODO: #Issue 4 when a certificate is deleted system auto generates
75 // certificate file. At present we are not supporting creation of
76 // certificate object for the auto-generated certificate file as
77 // deletion if only applicable for REST server and Bmcweb does not allow
78 // deletion of certificates
79 if (certificatePtr != nullptr)
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -050080 {
Marri Devender Rao6ceec402019-02-01 03:15:19 -060081 certificatePtr.reset(nullptr);
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -050082 }
83}
Marri Devender Raof4682712019-03-19 05:00:28 -050084
85std::string Manager::generateCSR(
86 std::vector<std::string> alternativeNames, std::string challengePassword,
87 std::string city, std::string commonName, std::string contactPerson,
88 std::string country, std::string email, std::string givenName,
89 std::string initials, int64_t keyBitLength, std::string keyCurveId,
90 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
91 std::string organization, std::string organizationalUnit, std::string state,
92 std::string surname, std::string unstructuredName)
93{
94 // We support only one CSR.
95 csrPtr.reset(nullptr);
96 auto pid = fork();
97 if (pid == -1)
98 {
99 log<level::ERR>("Error occurred during forking process");
100 report<InternalFailure>();
101 }
102 else if (pid == 0)
103 {
104 try
105 {
106 generateCSRHelper(alternativeNames, challengePassword, city,
107 commonName, contactPerson, country, email,
108 givenName, initials, keyBitLength, keyCurveId,
109 keyPairAlgorithm, keyUsage, organization,
110 organizationalUnit, state, surname,
111 unstructuredName);
112 exit(EXIT_SUCCESS);
113 }
114 catch (const InternalFailure& e)
115 {
116 // commit the error reported in child process and exit
117 // Callback method from SDEvent Loop looks for exit status
118 exit(EXIT_FAILURE);
119 commit<InternalFailure>();
120 }
121 }
122 else
123 {
124 using namespace sdeventplus::source;
125 Child::Callback callback = [this](Child& eventSource,
126 const siginfo_t* si) {
127 eventSource.set_enabled(Enabled::On);
128 if (si->si_status != 0)
129 {
130 this->createCSRObject(Status::FAILURE);
131 }
132 else
133 {
134 this->createCSRObject(Status::SUCCESS);
135 }
136 };
137 try
138 {
139 sigset_t ss;
140 if (sigemptyset(&ss) < 0)
141 {
142 log<level::ERR>("Unable to initialize signal set");
143 elog<InternalFailure>();
144 }
145 if (sigaddset(&ss, SIGCHLD) < 0)
146 {
147 log<level::ERR>("Unable to add signal to signal set");
148 elog<InternalFailure>();
149 }
150
151 // Block SIGCHLD first, so that the event loop can handle it
152 if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
153 {
154 log<level::ERR>("Unable to block signal");
155 elog<InternalFailure>();
156 }
157 if (childPtr)
158 {
159 childPtr.reset();
160 }
161 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
162 std::move(callback));
163 }
164 catch (const InternalFailure& e)
165 {
166 commit<InternalFailure>();
167 }
168 }
169 auto csrObjectPath = objectPath + '/' + "csr";
170 return csrObjectPath;
171}
172
173void Manager::generateCSRHelper(
174 std::vector<std::string> alternativeNames, std::string challengePassword,
175 std::string city, std::string commonName, std::string contactPerson,
176 std::string country, std::string email, std::string givenName,
177 std::string initials, int64_t keyBitLength, std::string keyCurveId,
178 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
179 std::string organization, std::string organizationalUnit, std::string state,
180 std::string surname, std::string unstructuredName)
181{
182 int ret = 0;
183
184 // set version of x509 req
185 int nVersion = 1;
186 // TODO: Issue#6 need to make version number configurable
187 X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
188 ret = X509_REQ_set_version(x509Req.get(), nVersion);
189 if (ret == 0)
190 {
191 log<level::ERR>("Error occured during X509_REQ_set_version call");
192 elog<InternalFailure>();
193 }
194
195 // set subject of x509 req
196 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
197
198 if (!alternativeNames.empty())
199 {
200 for (auto& name : alternativeNames)
201 {
202 addEntry(x509Name, "subjectAltName", name);
203 }
204 }
205 addEntry(x509Name, "challengePassword", challengePassword);
206 addEntry(x509Name, "L", city);
207 addEntry(x509Name, "CN", commonName);
208 addEntry(x509Name, "name", contactPerson);
209 addEntry(x509Name, "C", country);
210 addEntry(x509Name, "emailAddress", email);
211 addEntry(x509Name, "GN", givenName);
212 addEntry(x509Name, "initials", initials);
213 addEntry(x509Name, "algorithm", keyPairAlgorithm);
214 if (!keyUsage.empty())
215 {
216 for (auto& usage : keyUsage)
217 {
218 addEntry(x509Name, "keyUsage", usage);
219 }
220 }
221 addEntry(x509Name, "O", organization);
222 addEntry(x509Name, "ST", state);
223 addEntry(x509Name, "SN", surname);
224 addEntry(x509Name, "unstructuredName", unstructuredName);
225
226 // Generate private key and write to file
227 EVP_PKEY_Ptr pKey = writePrivateKey(keyBitLength, x509Req);
228
229 // set sign key of x509 req
230 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
231 if (ret <= 0)
232 {
233 log<level::ERR>("Error occured while signing key of x509");
234 elog<InternalFailure>();
235 }
236 log<level::INFO>("Writing CSR to file");
237 std::string path = fs::path(certInstallPath).parent_path();
238 std::string csrFilePath = path + '/' + CSR_FILE_NAME;
239 writeCSR(csrFilePath, x509Req);
240}
241
242EVP_PKEY_Ptr Manager::writePrivateKey(int64_t keyBitLength,
243 X509_REQ_Ptr& x509Req)
244{
245 int ret = 0;
246 // generate rsa key
247 BIGNUM_Ptr bne(BN_new(), ::BN_free);
248 ret = BN_set_word(bne.get(), RSA_F4);
249 if (ret == 0)
250 {
251 log<level::ERR>("Error occured during BN_set_word call");
252 elog<InternalFailure>();
253 }
254
255 // set keybit length to default value if not set
256 if (keyBitLength <= 0)
257 {
258 keyBitLength = 2048;
259 }
260 RSA* rsa = RSA_new();
261 ret = RSA_generate_key_ex(rsa, keyBitLength, bne.get(), NULL);
262 if (ret != 1)
263 {
264 free(rsa);
265 log<level::ERR>("Error occured during RSA_generate_key_ex call",
266 entry("KEYBITLENGTH=%PRIu64", keyBitLength));
267 elog<InternalFailure>();
268 }
269
270 // set public key of x509 req
271 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
272 EVP_PKEY_assign_RSA(pKey.get(), rsa);
273 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
274 if (ret == 0)
275 {
276 log<level::ERR>("Error occured while setting Public key");
277 elog<InternalFailure>();
278 }
279
280 log<level::ERR>("Writing private key to file");
281 // write private key to file
282 std::string path = fs::path(certInstallPath).parent_path();
283 std::string privKeyPath = path + '/' + PRIV_KEY_FILE_NAME;
284
285 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
286 if (fp == NULL)
287 {
288 ret = -1;
289 log<level::ERR>("Error occured creating private key file");
290 elog<InternalFailure>();
291 }
292 ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
293 std::fclose(fp);
294 if (ret == 0)
295 {
296 log<level::ERR>("Error occured while writing private key to file");
297 elog<InternalFailure>();
298 }
299 return pKey;
300}
301
302void Manager::addEntry(X509_NAME* x509Name, const char* field,
303 const std::string& bytes)
304{
305 if (bytes.empty())
306 {
307 return;
308 }
309 int ret = X509_NAME_add_entry_by_txt(
310 x509Name, field, MBSTRING_ASC,
311 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
312 if (ret != 1)
313 {
314 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
315 entry("VALUE=%s", bytes.c_str()));
316 elog<InternalFailure>();
317 }
318}
319
320void Manager::createCSRObject(const Status& status)
321{
322 if (csrPtr)
323 {
324 csrPtr.reset(nullptr);
325 }
326 auto csrObjectPath = objectPath + '/' + "csr";
327 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
328 certInstallPath.c_str(), status);
329}
330
331void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
332{
333 if (fs::exists(filePath))
334 {
335 log<level::INFO>("Removing the existing file",
336 entry("FILENAME=%s", filePath.c_str()));
337 if (!fs::remove(filePath.c_str()))
338 {
339 log<level::ERR>("Unable to remove the file",
340 entry("FILENAME=%s", filePath.c_str()));
341 elog<InternalFailure>();
342 }
343 }
344
345 FILE* fp = NULL;
346
347 if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
348 {
349 log<level::ERR>("Error opening the file to write the CSR",
350 entry("FILENAME=%s", filePath.c_str()));
351 elog<InternalFailure>();
352 }
353
354 int rc = PEM_write_X509_REQ(fp, x509Req.get());
355 if (!rc)
356 {
357 log<level::ERR>("PEM write routine failed",
358 entry("FILENAME=%s", filePath.c_str()));
359 std::fclose(fp);
360 elog<InternalFailure>();
361 }
362 std::fclose(fp);
363}
364
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500365} // namespace certs
366} // namespace phosphor