blob: d9679c9854b91ecd65f6c877a9906b068c17cdff [file] [log] [blame]
Gunnar Millsb0ce9962018-09-07 13:39:10 -05001#include "config.h"
2
3#include "image_verify.hpp"
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -06004
Lei YU1be8d502018-06-20 11:48:36 +08005#include "images.hpp"
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -06006#include "version.hpp"
7
Gunnar Millsb0ce9962018-09-07 13:39:10 -05008#include <fcntl.h>
9#include <openssl/err.h>
10#include <sys/stat.h>
11
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060012#include <phosphor-logging/elog-errors.hpp>
Gunnar Millsb0ce9962018-09-07 13:39:10 -050013#include <phosphor-logging/elog.hpp>
14#include <phosphor-logging/log.hpp>
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060015#include <xyz/openbmc_project/Common/error.hpp>
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060016
Adriana Kobylak58aa7502020-06-08 11:12:11 -050017#include <fstream>
18#include <set>
19
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060020namespace phosphor
21{
22namespace software
23{
24namespace image
25{
26
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060027using namespace phosphor::logging;
28using namespace phosphor::software::manager;
29using InternalFailure =
30 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
31
32constexpr auto keyTypeTag = "KeyType";
33constexpr auto hashFunctionTag = "HashType";
34
35Signature::Signature(const fs::path& imageDirPath,
36 const fs::path& signedConfPath) :
37 imageDirPath(imageDirPath),
38 signedConfPath(signedConfPath)
39{
40 fs::path file(imageDirPath / MANIFEST_FILE_NAME);
41
42 keyType = Version::getValue(file, keyTypeTag);
43 hashType = Version::getValue(file, hashFunctionTag);
44}
45
46AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
47{
48 AvailableKeyTypes keyTypes{};
49
50 // Find the path of all the files
51 if (!fs::is_directory(signedConfPath))
52 {
53 log<level::ERR>("Signed configuration path not found in the system");
54 elog<InternalFailure>();
55 }
56
57 // Look for all the hash and public key file names get the key value
58 // For example:
59 // /etc/activationdata/OpenBMC/publickey
60 // /etc/activationdata/OpenBMC/hashfunc
61 // /etc/activationdata/GA/publickey
62 // /etc/activationdata/GA/hashfunc
63 // Set will have OpenBMC, GA
64
65 for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
66 {
67 if ((p.path().filename() == HASH_FILE_NAME) ||
68 (p.path().filename() == PUBLICKEY_FILE_NAME))
69 {
70 // extract the key types
71 // /etc/activationdata/OpenBMC/ -> get OpenBMC from the path
72 auto key = p.path().parent_path();
73 keyTypes.insert(key.filename());
74 }
75 }
76
77 return keyTypes;
78}
79
80inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
81{
82 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
83 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
84
85 return std::make_pair(std::move(hashpath), std::move(keyPath));
86}
87
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060088bool Signature::verify()
89{
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060090 try
91 {
Henry Tian574f94b2021-01-06 10:33:59 +080092 bool valid;
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060093 // Verify the MANIFEST and publickey file using available
94 // public keys and hash on the system.
95 if (false == systemLevelVerify())
96 {
97 log<level::ERR>("System level Signature Validation failed");
98 return false;
99 }
100
Gunnar Millse11a2022018-03-23 12:04:48 -0500101 // image specific publickey file name.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600102 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
103
Henry Tian574f94b2021-01-06 10:33:59 +0800104 // Record the images which are being updated
105 // First check and Validate for the fullimage, then check and Validate
106 // for images with partitions
107 std::vector<std::string> imageUpdateList = {bmcFullImages};
108 valid =
109 checkAndVerifyImage(imageDirPath, publicKeyFile, imageUpdateList);
110 if (!valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600111 {
Henry Tian574f94b2021-01-06 10:33:59 +0800112 imageUpdateList.clear();
113 imageUpdateList.assign(bmcImages.begin(), bmcImages.end());
114 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
115 imageUpdateList);
116 if (!valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600117 {
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600118 return false;
119 }
120 }
Henry Tian574f94b2021-01-06 10:33:59 +0800121
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500122 // Validate the optional image files.
123 auto optionalImages = getOptionalImages();
124 for (const auto& optionalImage : optionalImages)
125 {
126 // Build Image File name
127 fs::path file(imageDirPath);
128 file /= optionalImage;
129
130 if (fs::exists(file))
131 {
132 // Build Signature File name
133 fs::path sigFile(file);
134 sigFile.replace_extension(SIGNATURE_FILE_EXT);
135
136 // Verify the signature.
Henry Tian574f94b2021-01-06 10:33:59 +0800137 valid = verifyFile(file, sigFile, publicKeyFile, hashType);
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500138 if (valid == false)
139 {
140 log<level::ERR>("Image file Signature Validation failed",
141 entry("IMAGE=%s", optionalImage.c_str()));
142 return false;
143 }
144 }
145 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600146
Gunnar Millse11a2022018-03-23 12:04:48 -0500147 log<level::DEBUG>("Successfully completed Signature vaildation.");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600148
149 return true;
150 }
151 catch (const InternalFailure& e)
152 {
153 return false;
154 }
155 catch (const std::exception& e)
156 {
157 log<level::ERR>(e.what());
158 return false;
159 }
160}
161
162bool Signature::systemLevelVerify()
163{
164 // Get available key types from the system.
165 auto keyTypes = getAvailableKeyTypesFromSystem();
166 if (keyTypes.empty())
167 {
168 log<level::ERR>("Missing Signature configuration data in system");
169 elog<InternalFailure>();
170 }
171
172 // Build publickey and its signature file name.
173 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
174 fs::path pkeyFileSig(pkeyFile);
175 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
176
177 // Build manifest and its signature file name.
178 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
179 fs::path manifestFileSig(manifestFile);
180 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
181
182 auto valid = false;
183
184 // Verify the file signature with available key types
185 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600186 // For any internal failure during the key/hash pair specific
187 // validation, should continue the validation with next
188 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600189 for (const auto& keyType : keyTypes)
190 {
191 auto keyHashPair = getKeyHashFileNames(keyType);
192
193 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
194
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600195 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600196 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600197 // Verify manifest file signature
198 valid = verifyFile(manifestFile, manifestFileSig,
199 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600200 if (valid)
201 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600202 // Verify publickey file signature.
203 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
204 hashFunc);
205 if (valid)
206 {
207 break;
208 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600209 }
210 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600211 catch (const InternalFailure& e)
212 {
213 valid = false;
214 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600215 }
216 return valid;
217}
218
219bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
220 const fs::path& publicKey,
221 const std::string& hashFunc)
222{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600223
224 // Check existence of the files in the system.
225 if (!(fs::exists(file) && fs::exists(sigFile)))
226 {
227 log<level::ERR>("Failed to find the Data or signature file.",
228 entry("FILE=%s", file.c_str()));
229 elog<InternalFailure>();
230 }
231
232 // Create RSA.
233 auto publicRSA = createPublicRSA(publicKey);
234 if (publicRSA == nullptr)
235 {
236 log<level::ERR>("Failed to create RSA",
237 entry("FILE=%s", publicKey.c_str()));
238 elog<InternalFailure>();
239 }
240
241 // Assign key to RSA.
242 EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free);
243 EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA);
244
245 // Initializes a digest context.
Adriana Kobylak5ed9b2d2018-09-06 13:15:34 -0500246 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600247
248 // Adds all digest algorithms to the internal table
249 OpenSSL_add_all_digests();
250
Gunnar Mills2bcba022018-04-08 15:02:04 -0500251 // Create Hash structure.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600252 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
253 if (!hashStruct)
254 {
255 log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
256 entry("HASH=%s", hashFunc.c_str()));
257 elog<InternalFailure>();
258 }
259
260 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
261 nullptr, pKeyPtr.get());
262
263 if (result <= 0)
264 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500265 log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600266 entry("ERRCODE=%lu", ERR_get_error()));
267 elog<InternalFailure>();
268 }
269
270 // Hash the data file and update the verification context
271 auto size = fs::file_size(file);
272 auto dataPtr = mapFile(file, size);
273
274 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
275 if (result <= 0)
276 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500277 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600278 entry("ERRCODE=%lu", ERR_get_error()));
279 elog<InternalFailure>();
280 }
281
282 // Verify the data with signature.
283 size = fs::file_size(sigFile);
284 auto signature = mapFile(sigFile, size);
285
286 result = EVP_DigestVerifyFinal(
287 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
288 size);
289
290 // Check the verification result.
291 if (result < 0)
292 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500293 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600294 entry("ERRCODE=%lu", ERR_get_error()));
295 elog<InternalFailure>();
296 }
297
298 if (result == 0)
299 {
300 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
301 entry("PATH=%s", sigFile.c_str()));
302 return false;
303 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600304 return true;
305}
306
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600307inline RSA* Signature::createPublicRSA(const fs::path& publicKey)
308{
309 RSA* rsa = nullptr;
310 auto size = fs::file_size(publicKey);
311
312 // Read public key file
313 auto data = mapFile(publicKey, size);
314
315 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
316 if (keyBio.get() == nullptr)
317 {
318 log<level::ERR>("Failed to create new BIO Memory buffer");
319 elog<InternalFailure>();
320 }
321
322 rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr);
323
324 return rsa;
325}
326
327CustomMap Signature::mapFile(const fs::path& path, size_t size)
328{
329
330 CustomFd fd(open(path.c_str(), O_RDONLY));
331
332 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
333 size);
334}
335
Henry Tian574f94b2021-01-06 10:33:59 +0800336bool Signature::checkAndVerifyImage(const std::string& filePath,
337 const std::string& publicKeyPath,
338 const std::vector<std::string>& imageList)
339{
340 bool valid = true;
341
342 for (auto& bmcImage : imageList)
343 {
344 fs::path file(filePath);
345 file /= bmcImage;
346
347 if (!fs::exists(file))
348 {
349 valid = false;
350 break;
351 }
352
353 fs::path sigFile(file);
354 sigFile.replace_extension(SIGNATURE_FILE_EXT);
355
356 // Verify the signature.
357 auto valid = verifyFile(file, sigFile, publicKeyPath, hashType);
358 if (valid == false)
359 {
360 log<level::ERR>("Image file Signature Validation failed",
361 entry("IMAGE=%s", bmcImage.c_str()));
362 return false;
363 }
364 }
365
366 return valid;
367}
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600368} // namespace image
369} // namespace software
370} // namespace phosphor