xref: /openbmc/phosphor-certificate-manager/certs_manager.cpp (revision dd74bd20d4d5099ad2f4952c7217749a4936c848)
1cfbc8dc8SJayanth Othayoth #include "certs_manager.hpp"
2cfbc8dc8SJayanth Othayoth 
3*dd74bd20SJayanth Othayoth #include <openssl/bio.h>
4*dd74bd20SJayanth Othayoth #include <openssl/crypto.h>
5*dd74bd20SJayanth Othayoth #include <openssl/err.h>
6*dd74bd20SJayanth Othayoth #include <openssl/evp.h>
7*dd74bd20SJayanth Othayoth #include <openssl/pem.h>
8*dd74bd20SJayanth Othayoth #include <openssl/x509v3.h>
9*dd74bd20SJayanth Othayoth 
10cfbc8dc8SJayanth Othayoth #include <experimental/filesystem>
11cfbc8dc8SJayanth Othayoth #include <phosphor-logging/elog-errors.hpp>
12cfbc8dc8SJayanth Othayoth #include <phosphor-logging/elog.hpp>
13cfbc8dc8SJayanth Othayoth #include <phosphor-logging/log.hpp>
14cfbc8dc8SJayanth Othayoth #include <sdbusplus/bus.hpp>
15*dd74bd20SJayanth Othayoth #include <xyz/openbmc_project/Certs/Install/error.hpp>
16cfbc8dc8SJayanth Othayoth #include <xyz/openbmc_project/Common/error.hpp>
17cfbc8dc8SJayanth Othayoth 
18cfbc8dc8SJayanth Othayoth namespace phosphor
19cfbc8dc8SJayanth Othayoth {
20cfbc8dc8SJayanth Othayoth namespace certs
21cfbc8dc8SJayanth Othayoth {
22*dd74bd20SJayanth Othayoth // RAII support for openSSL functions.
23*dd74bd20SJayanth Othayoth using BIO_MEM_Ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
24*dd74bd20SJayanth Othayoth using X509_STORE_CTX_Ptr =
25*dd74bd20SJayanth Othayoth     std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;
26*dd74bd20SJayanth Othayoth using X509_LOOKUP_Ptr =
27*dd74bd20SJayanth Othayoth     std::unique_ptr<X509_LOOKUP, decltype(&::X509_LOOKUP_free)>;
28cfbc8dc8SJayanth Othayoth 
29*dd74bd20SJayanth Othayoth namespace fs = std::experimental::filesystem;
30cfbc8dc8SJayanth Othayoth using namespace phosphor::logging;
31cfbc8dc8SJayanth Othayoth using InternalFailure =
32cfbc8dc8SJayanth Othayoth     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
33*dd74bd20SJayanth Othayoth using InvalidCertificate =
34*dd74bd20SJayanth Othayoth     sdbusplus::xyz::openbmc_project::Certs::Install::Error::InvalidCertificate;
35*dd74bd20SJayanth Othayoth using Reason = xyz::openbmc_project::Certs::Install::InvalidCertificate::REASON;
36cfbc8dc8SJayanth Othayoth 
37cfbc8dc8SJayanth Othayoth void Manager::install(const std::string path)
38cfbc8dc8SJayanth Othayoth {
39*dd74bd20SJayanth Othayoth     // Verify the certificate file
40*dd74bd20SJayanth Othayoth     auto rc = verifyCert(path);
41*dd74bd20SJayanth Othayoth     if (rc != X509_V_OK)
42*dd74bd20SJayanth Othayoth     {
43*dd74bd20SJayanth Othayoth         if (rc == X509_V_ERR_CERT_HAS_EXPIRED)
44*dd74bd20SJayanth Othayoth         {
45*dd74bd20SJayanth Othayoth             elog<InvalidCertificate>(Reason("Expired Certificate"));
46*dd74bd20SJayanth Othayoth         }
47*dd74bd20SJayanth Othayoth         // Loging general error here.
48*dd74bd20SJayanth Othayoth         elog<InvalidCertificate>(Reason("Certificate validation failed"));
49*dd74bd20SJayanth Othayoth     }
50cfbc8dc8SJayanth Othayoth     // Copy the certificate file
51cfbc8dc8SJayanth Othayoth     copy(path, certPath);
52cfbc8dc8SJayanth Othayoth 
53cfbc8dc8SJayanth Othayoth     // Invoke type specific install function.
54cfbc8dc8SJayanth Othayoth     auto iter = typeFuncMap.find(type);
55cfbc8dc8SJayanth Othayoth     if (iter == typeFuncMap.end())
56cfbc8dc8SJayanth Othayoth     {
57cfbc8dc8SJayanth Othayoth         log<level::ERR>("Unsupported Type", entry("TYPE=%s", type.c_str()));
58cfbc8dc8SJayanth Othayoth         elog<InternalFailure>();
59cfbc8dc8SJayanth Othayoth     }
60cfbc8dc8SJayanth Othayoth     iter->second();
61cfbc8dc8SJayanth Othayoth }
62cfbc8dc8SJayanth Othayoth 
63cfbc8dc8SJayanth Othayoth void Manager::serverInstall()
64cfbc8dc8SJayanth Othayoth {
65cfbc8dc8SJayanth Othayoth     if (!unit.empty())
66cfbc8dc8SJayanth Othayoth     {
67cfbc8dc8SJayanth Othayoth         reload(unit);
68cfbc8dc8SJayanth Othayoth     }
69cfbc8dc8SJayanth Othayoth }
70cfbc8dc8SJayanth Othayoth 
71cfbc8dc8SJayanth Othayoth void Manager::clientInstall()
72cfbc8dc8SJayanth Othayoth {
73cfbc8dc8SJayanth Othayoth     // Do nothing now
74cfbc8dc8SJayanth Othayoth }
75cfbc8dc8SJayanth Othayoth 
76cfbc8dc8SJayanth Othayoth void Manager::reload(const std::string& unit)
77cfbc8dc8SJayanth Othayoth {
78cfbc8dc8SJayanth Othayoth     constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
79cfbc8dc8SJayanth Othayoth     constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
80cfbc8dc8SJayanth Othayoth     constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
81cfbc8dc8SJayanth Othayoth 
82cfbc8dc8SJayanth Othayoth     try
83cfbc8dc8SJayanth Othayoth     {
84cfbc8dc8SJayanth Othayoth         auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
85cfbc8dc8SJayanth Othayoth                                           SYSTEMD_INTERFACE, "ReloadUnit");
86cfbc8dc8SJayanth Othayoth 
87cfbc8dc8SJayanth Othayoth         method.append(unit, "replace");
88cfbc8dc8SJayanth Othayoth 
89cfbc8dc8SJayanth Othayoth         bus.call_noreply(method);
90cfbc8dc8SJayanth Othayoth     }
91cfbc8dc8SJayanth Othayoth     catch (const sdbusplus::exception::SdBusError& e)
92cfbc8dc8SJayanth Othayoth     {
93cfbc8dc8SJayanth Othayoth         log<level::ERR>("Failed to reload service", entry("ERR=%s", e.what()),
94cfbc8dc8SJayanth Othayoth                         entry("UNIT=%s", unit.c_str()));
95cfbc8dc8SJayanth Othayoth         elog<InternalFailure>();
96cfbc8dc8SJayanth Othayoth     }
97cfbc8dc8SJayanth Othayoth }
98cfbc8dc8SJayanth Othayoth 
99cfbc8dc8SJayanth Othayoth void Manager::copy(const std::string& src, const std::string& dst)
100cfbc8dc8SJayanth Othayoth {
101cfbc8dc8SJayanth Othayoth     namespace fs = std::experimental::filesystem;
102cfbc8dc8SJayanth Othayoth 
103cfbc8dc8SJayanth Othayoth     try
104cfbc8dc8SJayanth Othayoth     {
105cfbc8dc8SJayanth Othayoth         auto path = fs::path(dst).parent_path();
106cfbc8dc8SJayanth Othayoth         // create dst path folder by default
107cfbc8dc8SJayanth Othayoth         fs::create_directories(path);
108cfbc8dc8SJayanth Othayoth         fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
109cfbc8dc8SJayanth Othayoth     }
110cfbc8dc8SJayanth Othayoth     catch (fs::filesystem_error& e)
111cfbc8dc8SJayanth Othayoth     {
112cfbc8dc8SJayanth Othayoth         log<level::ERR>("Failed to copy certificate", entry("ERR=%s", e.what()),
113cfbc8dc8SJayanth Othayoth                         entry("SRC=%s", src.c_str()),
114cfbc8dc8SJayanth Othayoth                         entry("DST=%s", dst.c_str()));
115cfbc8dc8SJayanth Othayoth         elog<InternalFailure>();
116cfbc8dc8SJayanth Othayoth     }
117cfbc8dc8SJayanth Othayoth }
118cfbc8dc8SJayanth Othayoth 
119*dd74bd20SJayanth Othayoth X509_Ptr Manager::loadCert(const std::string& filePath)
120*dd74bd20SJayanth Othayoth {
121*dd74bd20SJayanth Othayoth     // Read Certificate file
122*dd74bd20SJayanth Othayoth     X509_Ptr cert(X509_new(), ::X509_free);
123*dd74bd20SJayanth Othayoth     if (!cert)
124*dd74bd20SJayanth Othayoth     {
125*dd74bd20SJayanth Othayoth         log<level::ERR>("Error occured during X509_new call",
126*dd74bd20SJayanth Othayoth                         entry("FILE=%s", filePath.c_str()),
127*dd74bd20SJayanth Othayoth                         entry("ERRCODE=%lu", ERR_get_error()));
128*dd74bd20SJayanth Othayoth         elog<InternalFailure>();
129*dd74bd20SJayanth Othayoth     }
130*dd74bd20SJayanth Othayoth 
131*dd74bd20SJayanth Othayoth     BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
132*dd74bd20SJayanth Othayoth     if (!bioCert)
133*dd74bd20SJayanth Othayoth     {
134*dd74bd20SJayanth Othayoth         log<level::ERR>("Error occured during BIO_new_file call",
135*dd74bd20SJayanth Othayoth                         entry("FILE=%s", filePath.c_str()));
136*dd74bd20SJayanth Othayoth         elog<InternalFailure>();
137*dd74bd20SJayanth Othayoth     }
138*dd74bd20SJayanth Othayoth 
139*dd74bd20SJayanth Othayoth     X509* x509 = cert.get();
140*dd74bd20SJayanth Othayoth     if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
141*dd74bd20SJayanth Othayoth     {
142*dd74bd20SJayanth Othayoth         log<level::ERR>("Error occured during PEM_read_bio_X509 call",
143*dd74bd20SJayanth Othayoth                         entry("FILE=%s", filePath.c_str()));
144*dd74bd20SJayanth Othayoth         elog<InternalFailure>();
145*dd74bd20SJayanth Othayoth     }
146*dd74bd20SJayanth Othayoth     return cert;
147*dd74bd20SJayanth Othayoth }
148*dd74bd20SJayanth Othayoth 
149*dd74bd20SJayanth Othayoth int32_t Manager::verifyCert(const std::string& filePath)
150*dd74bd20SJayanth Othayoth {
151*dd74bd20SJayanth Othayoth     auto errCode = X509_V_OK;
152*dd74bd20SJayanth Othayoth 
153*dd74bd20SJayanth Othayoth     fs::path file(filePath);
154*dd74bd20SJayanth Othayoth     if (!fs::exists(file))
155*dd74bd20SJayanth Othayoth     {
156*dd74bd20SJayanth Othayoth         log<level::ERR>("File is Missing", entry("FILE=%s", filePath.c_str()));
157*dd74bd20SJayanth Othayoth         elog<InternalFailure>();
158*dd74bd20SJayanth Othayoth     }
159*dd74bd20SJayanth Othayoth 
160*dd74bd20SJayanth Othayoth     try
161*dd74bd20SJayanth Othayoth     {
162*dd74bd20SJayanth Othayoth         if (fs::file_size(filePath) == 0)
163*dd74bd20SJayanth Othayoth         {
164*dd74bd20SJayanth Othayoth             // file is empty
165*dd74bd20SJayanth Othayoth             log<level::ERR>("File is empty",
166*dd74bd20SJayanth Othayoth                             entry("FILE=%s", filePath.c_str()));
167*dd74bd20SJayanth Othayoth             elog<InvalidCertificate>(Reason("File is empty"));
168*dd74bd20SJayanth Othayoth         }
169*dd74bd20SJayanth Othayoth     }
170*dd74bd20SJayanth Othayoth     catch (const fs::filesystem_error& e)
171*dd74bd20SJayanth Othayoth     {
172*dd74bd20SJayanth Othayoth         // Log Error message
173*dd74bd20SJayanth Othayoth         log<level::ERR>(e.what(), entry("FILE=%s", filePath.c_str()));
174*dd74bd20SJayanth Othayoth         elog<InternalFailure>();
175*dd74bd20SJayanth Othayoth     }
176*dd74bd20SJayanth Othayoth 
177*dd74bd20SJayanth Othayoth     // Defining store object as RAW to avoid double free.
178*dd74bd20SJayanth Othayoth     // X509_LOOKUP_free free up store object.
179*dd74bd20SJayanth Othayoth     // Create an empty X509_STORE structure for certificate validation.
180*dd74bd20SJayanth Othayoth     auto x509Store = X509_STORE_new();
181*dd74bd20SJayanth Othayoth     if (!x509Store)
182*dd74bd20SJayanth Othayoth     {
183*dd74bd20SJayanth Othayoth         log<level::ERR>("Error occured during X509_STORE_new call");
184*dd74bd20SJayanth Othayoth         elog<InternalFailure>();
185*dd74bd20SJayanth Othayoth     }
186*dd74bd20SJayanth Othayoth 
187*dd74bd20SJayanth Othayoth     OpenSSL_add_all_algorithms();
188*dd74bd20SJayanth Othayoth 
189*dd74bd20SJayanth Othayoth     // ADD Certificate Lookup method.
190*dd74bd20SJayanth Othayoth     X509_LOOKUP_Ptr lookup(X509_STORE_add_lookup(x509Store, X509_LOOKUP_file()),
191*dd74bd20SJayanth Othayoth                            ::X509_LOOKUP_free);
192*dd74bd20SJayanth Othayoth     if (!lookup)
193*dd74bd20SJayanth Othayoth     {
194*dd74bd20SJayanth Othayoth         // Normally lookup cleanup function interanlly does X509Store cleanup
195*dd74bd20SJayanth Othayoth         // Free up the X509Store.
196*dd74bd20SJayanth Othayoth         X509_STORE_free(x509Store);
197*dd74bd20SJayanth Othayoth         log<level::ERR>("Error occured during X509_STORE_add_lookup call");
198*dd74bd20SJayanth Othayoth         elog<InternalFailure>();
199*dd74bd20SJayanth Othayoth     }
200*dd74bd20SJayanth Othayoth     // Load Certificate file.
201*dd74bd20SJayanth Othayoth     int32_t rc = X509_LOOKUP_load_file(lookup.get(), filePath.c_str(),
202*dd74bd20SJayanth Othayoth                                        X509_FILETYPE_PEM);
203*dd74bd20SJayanth Othayoth     if (rc != 1)
204*dd74bd20SJayanth Othayoth     {
205*dd74bd20SJayanth Othayoth         log<level::ERR>("Error occured during X509_LOOKUP_load_file call",
206*dd74bd20SJayanth Othayoth                         entry("FILE=%s", filePath.c_str()));
207*dd74bd20SJayanth Othayoth         elog<InvalidCertificate>(Reason("Invalid certificate file format"));
208*dd74bd20SJayanth Othayoth     }
209*dd74bd20SJayanth Othayoth 
210*dd74bd20SJayanth Othayoth     // Load Certificate file into the X509 structre.
211*dd74bd20SJayanth Othayoth     X509_Ptr cert = std::move(loadCert(filePath));
212*dd74bd20SJayanth Othayoth     X509_STORE_CTX_Ptr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
213*dd74bd20SJayanth Othayoth     if (!storeCtx)
214*dd74bd20SJayanth Othayoth     {
215*dd74bd20SJayanth Othayoth         log<level::ERR>("Error occured during X509_STORE_CTX_new call",
216*dd74bd20SJayanth Othayoth                         entry("FILE=%s", filePath.c_str()));
217*dd74bd20SJayanth Othayoth         elog<InternalFailure>();
218*dd74bd20SJayanth Othayoth     }
219*dd74bd20SJayanth Othayoth 
220*dd74bd20SJayanth Othayoth     rc = X509_STORE_CTX_init(storeCtx.get(), x509Store, cert.get(), NULL);
221*dd74bd20SJayanth Othayoth     if (rc != 1)
222*dd74bd20SJayanth Othayoth     {
223*dd74bd20SJayanth Othayoth         log<level::ERR>("Error occured during X509_STORE_CTX_init call",
224*dd74bd20SJayanth Othayoth                         entry("FILE=%s", filePath.c_str()));
225*dd74bd20SJayanth Othayoth         elog<InternalFailure>();
226*dd74bd20SJayanth Othayoth     }
227*dd74bd20SJayanth Othayoth 
228*dd74bd20SJayanth Othayoth     // Set time to current time.
229*dd74bd20SJayanth Othayoth     auto locTime = time(nullptr);
230*dd74bd20SJayanth Othayoth 
231*dd74bd20SJayanth Othayoth     X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
232*dd74bd20SJayanth Othayoth                             locTime);
233*dd74bd20SJayanth Othayoth 
234*dd74bd20SJayanth Othayoth     rc = X509_verify_cert(storeCtx.get());
235*dd74bd20SJayanth Othayoth     if (rc == 1)
236*dd74bd20SJayanth Othayoth     {
237*dd74bd20SJayanth Othayoth         errCode = X509_V_OK;
238*dd74bd20SJayanth Othayoth     }
239*dd74bd20SJayanth Othayoth     else if (rc == 0)
240*dd74bd20SJayanth Othayoth     {
241*dd74bd20SJayanth Othayoth         errCode = X509_STORE_CTX_get_error(storeCtx.get());
242*dd74bd20SJayanth Othayoth         log<level::ERR>("Certificate verification failed",
243*dd74bd20SJayanth Othayoth                         entry("FILE=%s", filePath.c_str()),
244*dd74bd20SJayanth Othayoth                         entry("ERRCODE=%d", errCode));
245*dd74bd20SJayanth Othayoth     }
246*dd74bd20SJayanth Othayoth     else
247*dd74bd20SJayanth Othayoth     {
248*dd74bd20SJayanth Othayoth         log<level::ERR>("Error occured during X509_verify_cert call",
249*dd74bd20SJayanth Othayoth                         entry("FILE=%s", filePath.c_str()));
250*dd74bd20SJayanth Othayoth         elog<InternalFailure>();
251*dd74bd20SJayanth Othayoth     }
252*dd74bd20SJayanth Othayoth     return errCode;
253*dd74bd20SJayanth Othayoth }
254*dd74bd20SJayanth Othayoth 
255cfbc8dc8SJayanth Othayoth } // namespace certs
256cfbc8dc8SJayanth Othayoth } // namespace phosphor
257