blob: 8f3678226b502aebe9135733c58c120dd8f6c29f [file] [log] [blame]
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -05001#include "certs_manager.hpp"
2
Jayanth Othayothdd74bd22018-09-28 06:13:35 -05003#include <openssl/bio.h>
4#include <openssl/crypto.h>
5#include <openssl/err.h>
6#include <openssl/evp.h>
7#include <openssl/pem.h>
8#include <openssl/x509v3.h>
9
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050010#include <experimental/filesystem>
11#include <phosphor-logging/elog-errors.hpp>
12#include <phosphor-logging/elog.hpp>
13#include <phosphor-logging/log.hpp>
14#include <sdbusplus/bus.hpp>
Jayanth Othayothdd74bd22018-09-28 06:13:35 -050015#include <xyz/openbmc_project/Certs/Install/error.hpp>
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050016#include <xyz/openbmc_project/Common/error.hpp>
17
18namespace phosphor
19{
20namespace certs
21{
Jayanth Othayothdd74bd22018-09-28 06:13:35 -050022// RAII support for openSSL functions.
23using BIO_MEM_Ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
24using X509_STORE_CTX_Ptr =
25 std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;
26using X509_LOOKUP_Ptr =
27 std::unique_ptr<X509_LOOKUP, decltype(&::X509_LOOKUP_free)>;
Jayanth Othayoth589159f2018-09-28 08:32:39 -050028using EVP_PKEY_Ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050029
Jayanth Othayothdd74bd22018-09-28 06:13:35 -050030namespace fs = std::experimental::filesystem;
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050031using namespace phosphor::logging;
32using InternalFailure =
33 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Jayanth Othayothdd74bd22018-09-28 06:13:35 -050034using InvalidCertificate =
35 sdbusplus::xyz::openbmc_project::Certs::Install::Error::InvalidCertificate;
36using Reason = xyz::openbmc_project::Certs::Install::InvalidCertificate::REASON;
Jayanth Othayoth8f600852018-10-08 20:44:45 -050037// Trust chain related errors.`
38#define TRUST_CHAIN_ERR(errnum) \
39 ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) || \
40 (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) || \
41 (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) || \
42 (errnum == X509_V_ERR_CERT_UNTRUSTED) || \
43 (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050044
45void Manager::install(const std::string path)
46{
Jayanth Othayothdd74bd22018-09-28 06:13:35 -050047 // Verify the certificate file
48 auto rc = verifyCert(path);
Jayanth Othayoth8f600852018-10-08 20:44:45 -050049 // Allow certificate upload, for "certificate is not yet valid" and
50 // trust chain related errors.
51 if (!((rc == X509_V_OK) || (rc == X509_V_ERR_CERT_NOT_YET_VALID) ||
52 TRUST_CHAIN_ERR(rc)))
Jayanth Othayothdd74bd22018-09-28 06:13:35 -050053 {
54 if (rc == X509_V_ERR_CERT_HAS_EXPIRED)
55 {
56 elog<InvalidCertificate>(Reason("Expired Certificate"));
57 }
58 // Loging general error here.
59 elog<InvalidCertificate>(Reason("Certificate validation failed"));
60 }
Jayanth Othayoth589159f2018-09-28 08:32:39 -050061
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050062 // Invoke type specific install function.
63 auto iter = typeFuncMap.find(type);
64 if (iter == typeFuncMap.end())
65 {
66 log<level::ERR>("Unsupported Type", entry("TYPE=%s", type.c_str()));
67 elog<InternalFailure>();
68 }
Jayanth Othayothb50789c2018-10-09 07:13:54 -050069 iter->second(path);
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050070
Jayanth Othayothb50789c2018-10-09 07:13:54 -050071 // Copy the certificate file
72 copy(path, certPath);
73
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050074 if (!unit.empty())
75 {
Jayanth Othayothe8199a82018-09-29 00:46:10 -050076 reloadOrReset(unit);
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050077 }
78}
79
Jayanth Othayothb50789c2018-10-09 07:13:54 -050080void Manager::serverInstallHelper(const std::string& filePath)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050081{
Jayanth Othayothb50789c2018-10-09 07:13:54 -050082 if (!compareKeys(filePath))
Jayanth Othayothe8199a82018-09-29 00:46:10 -050083 {
Jayanth Othayothb50789c2018-10-09 07:13:54 -050084 elog<InvalidCertificate>(
85 Reason("Private key does not match the Certificate"));
Jayanth Othayothe8199a82018-09-29 00:46:10 -050086 }
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050087}
88
Jayanth Othayothb50789c2018-10-09 07:13:54 -050089void Manager::clientInstallHelper(const std::string& filePath)
90{
91 if (!compareKeys(filePath))
92 {
93 elog<InvalidCertificate>(
94 Reason("Private key does not match the Certificate"));
95 }
96}
97
98void Manager::authorityInstallHelper(const std::string& filePath)
99{
100 // No additional steps required now.
101}
102
Jayanth Othayothe8199a82018-09-29 00:46:10 -0500103void Manager::reloadOrReset(const std::string& unit)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500104{
105 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
106 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
107 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
108
109 try
110 {
Jayanth Othayothe8199a82018-09-29 00:46:10 -0500111 auto method =
112 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
113 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500114
115 method.append(unit, "replace");
116
117 bus.call_noreply(method);
118 }
119 catch (const sdbusplus::exception::SdBusError& e)
120 {
Jayanth Othayothe8199a82018-09-29 00:46:10 -0500121 log<level::ERR>("Failed to reload or restart service",
122 entry("ERR=%s", e.what()),
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500123 entry("UNIT=%s", unit.c_str()));
124 elog<InternalFailure>();
125 }
126}
127
128void Manager::copy(const std::string& src, const std::string& dst)
129{
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500130 try
131 {
132 auto path = fs::path(dst).parent_path();
133 // create dst path folder by default
134 fs::create_directories(path);
135 fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
136 }
137 catch (fs::filesystem_error& e)
138 {
139 log<level::ERR>("Failed to copy certificate", entry("ERR=%s", e.what()),
140 entry("SRC=%s", src.c_str()),
141 entry("DST=%s", dst.c_str()));
142 elog<InternalFailure>();
143 }
144}
145
Jayanth Othayothdd74bd22018-09-28 06:13:35 -0500146X509_Ptr Manager::loadCert(const std::string& filePath)
147{
148 // Read Certificate file
149 X509_Ptr cert(X509_new(), ::X509_free);
150 if (!cert)
151 {
152 log<level::ERR>("Error occured during X509_new call",
153 entry("FILE=%s", filePath.c_str()),
154 entry("ERRCODE=%lu", ERR_get_error()));
155 elog<InternalFailure>();
156 }
157
158 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
159 if (!bioCert)
160 {
161 log<level::ERR>("Error occured during BIO_new_file call",
162 entry("FILE=%s", filePath.c_str()));
163 elog<InternalFailure>();
164 }
165
166 X509* x509 = cert.get();
167 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
168 {
169 log<level::ERR>("Error occured during PEM_read_bio_X509 call",
170 entry("FILE=%s", filePath.c_str()));
171 elog<InternalFailure>();
172 }
173 return cert;
174}
175
176int32_t Manager::verifyCert(const std::string& filePath)
177{
178 auto errCode = X509_V_OK;
179
180 fs::path file(filePath);
181 if (!fs::exists(file))
182 {
183 log<level::ERR>("File is Missing", entry("FILE=%s", filePath.c_str()));
184 elog<InternalFailure>();
185 }
186
187 try
188 {
189 if (fs::file_size(filePath) == 0)
190 {
191 // file is empty
192 log<level::ERR>("File is empty",
193 entry("FILE=%s", filePath.c_str()));
194 elog<InvalidCertificate>(Reason("File is empty"));
195 }
196 }
197 catch (const fs::filesystem_error& e)
198 {
199 // Log Error message
200 log<level::ERR>(e.what(), entry("FILE=%s", filePath.c_str()));
201 elog<InternalFailure>();
202 }
203
204 // Defining store object as RAW to avoid double free.
205 // X509_LOOKUP_free free up store object.
206 // Create an empty X509_STORE structure for certificate validation.
207 auto x509Store = X509_STORE_new();
208 if (!x509Store)
209 {
210 log<level::ERR>("Error occured during X509_STORE_new call");
211 elog<InternalFailure>();
212 }
213
214 OpenSSL_add_all_algorithms();
215
216 // ADD Certificate Lookup method.
217 X509_LOOKUP_Ptr lookup(X509_STORE_add_lookup(x509Store, X509_LOOKUP_file()),
218 ::X509_LOOKUP_free);
219 if (!lookup)
220 {
221 // Normally lookup cleanup function interanlly does X509Store cleanup
222 // Free up the X509Store.
223 X509_STORE_free(x509Store);
224 log<level::ERR>("Error occured during X509_STORE_add_lookup call");
225 elog<InternalFailure>();
226 }
227 // Load Certificate file.
228 int32_t rc = X509_LOOKUP_load_file(lookup.get(), filePath.c_str(),
229 X509_FILETYPE_PEM);
230 if (rc != 1)
231 {
232 log<level::ERR>("Error occured during X509_LOOKUP_load_file call",
233 entry("FILE=%s", filePath.c_str()));
234 elog<InvalidCertificate>(Reason("Invalid certificate file format"));
235 }
236
237 // Load Certificate file into the X509 structre.
238 X509_Ptr cert = std::move(loadCert(filePath));
239 X509_STORE_CTX_Ptr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
240 if (!storeCtx)
241 {
242 log<level::ERR>("Error occured during X509_STORE_CTX_new call",
243 entry("FILE=%s", filePath.c_str()));
244 elog<InternalFailure>();
245 }
246
247 rc = X509_STORE_CTX_init(storeCtx.get(), x509Store, cert.get(), NULL);
248 if (rc != 1)
249 {
250 log<level::ERR>("Error occured during X509_STORE_CTX_init call",
251 entry("FILE=%s", filePath.c_str()));
252 elog<InternalFailure>();
253 }
254
255 // Set time to current time.
256 auto locTime = time(nullptr);
257
258 X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
259 locTime);
260
261 rc = X509_verify_cert(storeCtx.get());
262 if (rc == 1)
263 {
264 errCode = X509_V_OK;
265 }
266 else if (rc == 0)
267 {
268 errCode = X509_STORE_CTX_get_error(storeCtx.get());
269 log<level::ERR>("Certificate verification failed",
270 entry("FILE=%s", filePath.c_str()),
271 entry("ERRCODE=%d", errCode));
272 }
273 else
274 {
275 log<level::ERR>("Error occured during X509_verify_cert call",
276 entry("FILE=%s", filePath.c_str()));
277 elog<InternalFailure>();
278 }
279 return errCode;
280}
281
Jayanth Othayoth589159f2018-09-28 08:32:39 -0500282bool Manager::compareKeys(const std::string& filePath)
283{
284 X509_Ptr cert(X509_new(), ::X509_free);
285 if (!cert)
286 {
287 log<level::ERR>("Error occured during X509_new call",
288 entry("FILE=%s", filePath.c_str()),
289 entry("ERRCODE=%lu", ERR_get_error()));
290 elog<InternalFailure>();
291 }
292
293 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
294 if (!bioCert)
295 {
296 log<level::ERR>("Error occured during BIO_new_file call",
297 entry("FILE=%s", filePath.c_str()));
298 elog<InternalFailure>();
299 }
300
301 X509* x509 = cert.get();
302 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
303
304 EVP_PKEY_Ptr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
305 if (!pubKey)
306 {
307 log<level::ERR>("Error occurred during X509_get_pubkey",
308 entry("FILE=%s", filePath.c_str()),
309 entry("ERRCODE=%lu", ERR_get_error()));
310 elog<InvalidCertificate>(Reason("Failed to get public key info"));
311 }
312
313 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
314 if (!keyBio)
315 {
316 log<level::ERR>("Error occured during BIO_s_file call",
317 entry("FILE=%s", filePath.c_str()));
318 elog<InternalFailure>();
319 }
320 BIO_read_filename(keyBio.get(), filePath.c_str());
321
322 EVP_PKEY_Ptr priKey(
323 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
324 ::EVP_PKEY_free);
325
326 if (!priKey)
327 {
328 log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey",
329 entry("FILE=%s", filePath.c_str()),
330 entry("ERRCODE=%lu", ERR_get_error()));
331 elog<InvalidCertificate>(Reason("Failed to get private key info"));
332 }
333
334 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
335 if (rc != 1)
336 {
337 log<level::ERR>("Private key is not matching with Certificate",
338 entry("FILE=%s", filePath.c_str()),
339 entry("ERRCODE=%d", rc));
340 return false;
341 }
342
343 return true;
344}
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500345
346void Manager::delete_()
347{
348 try
349 {
350 if (!fs::remove(certPath))
351 {
352 log<level::INFO>("Certificate file not found!",
353 entry("PATH=%s", certPath.c_str()));
354 }
Jayanth Othayotha1c55952018-10-26 04:31:53 -0500355 else if (!unit.empty())
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500356 {
357 reloadOrReset(unit);
358 }
359 }
360 catch (const InternalFailure& e)
361 {
362 throw;
363 }
364 catch (const std::exception& e)
365 {
366 log<level::ERR>(
367 "Failed to delete certificate", entry("UNIT=%s", unit.c_str()),
368 entry("ERR=%s", e.what()), entry("PATH=%s", certPath.c_str()));
369 elog<InternalFailure>();
370 }
371}
372
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500373} // namespace certs
374} // namespace phosphor