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