blob: 4d874b48d12d2df803d4020bba4ea2e14cbe749b [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"
George Liu0a06e972020-12-17 09:17:04 +08006#include "utils.hpp"
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -06007#include "version.hpp"
8
Gunnar Millsb0ce9962018-09-07 13:39:10 -05009#include <fcntl.h>
10#include <openssl/err.h>
11#include <sys/stat.h>
12
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060013#include <phosphor-logging/elog-errors.hpp>
Gunnar Millsb0ce9962018-09-07 13:39:10 -050014#include <phosphor-logging/elog.hpp>
Patrick Williamsc9bb6422021-08-27 06:18:35 -050015#include <phosphor-logging/lg2.hpp>
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060016#include <xyz/openbmc_project/Common/error.hpp>
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060017
Lei YU7ab55e22021-05-19 13:26:53 +080018#include <cassert>
Adriana Kobylak58aa7502020-06-08 11:12:11 -050019#include <fstream>
20#include <set>
George Liu44b9fef2023-02-07 14:31:32 +080021#include <system_error>
Adriana Kobylak58aa7502020-06-08 11:12:11 -050022
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060023namespace phosphor
24{
25namespace software
26{
27namespace image
28{
29
Patrick Williamsc9bb6422021-08-27 06:18:35 -050030PHOSPHOR_LOG2_USING;
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060031using namespace phosphor::logging;
32using namespace phosphor::software::manager;
33using InternalFailure =
Adriana Kobylakce82de52024-01-16 13:56:38 -060034 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060035
36constexpr auto keyTypeTag = "KeyType";
37constexpr auto hashFunctionTag = "HashType";
38
39Signature::Signature(const fs::path& imageDirPath,
40 const fs::path& signedConfPath) :
41 imageDirPath(imageDirPath),
42 signedConfPath(signedConfPath)
43{
44 fs::path file(imageDirPath / MANIFEST_FILE_NAME);
45
46 keyType = Version::getValue(file, keyTypeTag);
47 hashType = Version::getValue(file, hashFunctionTag);
Lei YU6173a072022-05-23 19:18:57 +080048
49 // Get purpose
50 auto purposeString = Version::getValue(file, "purpose");
51 auto convertedPurpose =
52 sdbusplus::message::convert_from_string<VersionPurpose>(purposeString);
53 purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060054}
55
56AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
57{
58 AvailableKeyTypes keyTypes{};
59
60 // Find the path of all the files
George Liu44b9fef2023-02-07 14:31:32 +080061 std::error_code ec;
62 if (!fs::is_directory(signedConfPath, ec))
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060063 {
George Liu44b9fef2023-02-07 14:31:32 +080064 error("Signed configuration path not found in the system: {ERROR_MSG}",
65 "ERROR_MSG", ec.message());
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060066 elog<InternalFailure>();
67 }
68
69 // Look for all the hash and public key file names get the key value
70 // For example:
71 // /etc/activationdata/OpenBMC/publickey
72 // /etc/activationdata/OpenBMC/hashfunc
73 // /etc/activationdata/GA/publickey
74 // /etc/activationdata/GA/hashfunc
75 // Set will have OpenBMC, GA
76
77 for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
78 {
79 if ((p.path().filename() == HASH_FILE_NAME) ||
80 (p.path().filename() == PUBLICKEY_FILE_NAME))
81 {
82 // extract the key types
83 // /etc/activationdata/OpenBMC/ -> get OpenBMC from the path
84 auto key = p.path().parent_path();
85 keyTypes.insert(key.filename());
86 }
87 }
88
89 return keyTypes;
90}
91
92inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
93{
94 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
95 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
96
97 return std::make_pair(std::move(hashpath), std::move(keyPath));
98}
99
George Liu0a06e972020-12-17 09:17:04 +0800100bool Signature::verifyFullImage()
101{
102 bool ret = true;
Konstantin Aladyshev294991a2023-04-19 15:24:20 +0300103#ifdef WANT_SIGNATURE_VERIFY
Lei YU6173a072022-05-23 19:18:57 +0800104 // Only verify full image for BMC
105 if (purpose != VersionPurpose::BMC)
106 {
107 return ret;
108 }
109
George Liu0a06e972020-12-17 09:17:04 +0800110 std::vector<std::string> fullImages = {
111 fs::path(imageDirPath) / "image-bmc.sig",
112 fs::path(imageDirPath) / "image-hostfw.sig",
113 fs::path(imageDirPath) / "image-kernel.sig",
114 fs::path(imageDirPath) / "image-rofs.sig",
115 fs::path(imageDirPath) / "image-rwfs.sig",
116 fs::path(imageDirPath) / "image-u-boot.sig",
117 fs::path(imageDirPath) / "MANIFEST.sig",
118 fs::path(imageDirPath) / "publickey.sig"};
119
120 // Merge files
121 std::string tmpFullFile = "/tmp/image-full";
122 utils::mergeFiles(fullImages, tmpFullFile);
123
124 // Validate the full image files
125 fs::path pkeyFullFile(tmpFullFile);
126
127 std::string imageFullSig = "image-full.sig";
128 fs::path pkeyFullFileSig(imageDirPath / imageFullSig);
129 pkeyFullFileSig.replace_extension(SIGNATURE_FILE_EXT);
130
131 // image specific publickey file name.
132 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
133
134 ret = verifyFile(pkeyFullFile, pkeyFullFileSig, publicKeyFile, hashType);
George Liu44b9fef2023-02-07 14:31:32 +0800135
136 std::error_code ec;
137 fs::remove(tmpFullFile, ec);
George Liu0a06e972020-12-17 09:17:04 +0800138#endif
139
140 return ret;
141}
142
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600143bool Signature::verify()
144{
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600145 try
146 {
Henry Tian574f94b2021-01-06 10:33:59 +0800147 bool valid;
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600148 // Verify the MANIFEST and publickey file using available
149 // public keys and hash on the system.
150 if (false == systemLevelVerify())
151 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500152 error("System level Signature Validation failed");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600153 return false;
154 }
155
Lei YU7ab55e22021-05-19 13:26:53 +0800156 bool bmcFilesFound = false;
Gunnar Millse11a2022018-03-23 12:04:48 -0500157 // image specific publickey file name.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600158 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
159
Henry Tian574f94b2021-01-06 10:33:59 +0800160 // Record the images which are being updated
161 // First check and Validate for the fullimage, then check and Validate
162 // for images with partitions
163 std::vector<std::string> imageUpdateList = {bmcFullImages};
Lei YU7ab55e22021-05-19 13:26:53 +0800164 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
165 imageUpdateList, bmcFilesFound);
166 if (bmcFilesFound && !valid)
167 {
168 return false;
169 }
170
Henry Tian574f94b2021-01-06 10:33:59 +0800171 if (!valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600172 {
Lei YU7ab55e22021-05-19 13:26:53 +0800173 // Validate bmcImages
Henry Tian574f94b2021-01-06 10:33:59 +0800174 imageUpdateList.clear();
175 imageUpdateList.assign(bmcImages.begin(), bmcImages.end());
176 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
Lei YU7ab55e22021-05-19 13:26:53 +0800177 imageUpdateList, bmcFilesFound);
178 if (bmcFilesFound && !valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600179 {
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600180 return false;
181 }
182 }
Henry Tian574f94b2021-01-06 10:33:59 +0800183
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500184 // Validate the optional image files.
185 auto optionalImages = getOptionalImages();
Lei YU7ab55e22021-05-19 13:26:53 +0800186 bool optionalFilesFound = false;
187 bool optionalImagesValid = false;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500188 for (const auto& optionalImage : optionalImages)
189 {
190 // Build Image File name
191 fs::path file(imageDirPath);
192 file /= optionalImage;
193
George Liu44b9fef2023-02-07 14:31:32 +0800194 std::error_code ec;
195 if (fs::exists(file, ec))
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500196 {
Lei YU7ab55e22021-05-19 13:26:53 +0800197 optionalFilesFound = true;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500198 // Build Signature File name
199 fs::path sigFile(file);
Lei YU92c7e9e2021-05-20 16:32:28 +0800200 sigFile += SIGNATURE_FILE_EXT;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500201
202 // Verify the signature.
Patrick Williamsd5e8e732023-05-10 07:50:18 -0500203 optionalImagesValid = verifyFile(file, sigFile, publicKeyFile,
204 hashType);
Lei YU7ab55e22021-05-19 13:26:53 +0800205 if (!optionalImagesValid)
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500206 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500207 error("Image file Signature Validation failed on {IMAGE}",
208 "IMAGE", optionalImage);
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500209 return false;
210 }
211 }
212 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600213
George Liu0a06e972020-12-17 09:17:04 +0800214 if (verifyFullImage() == false)
215 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500216 error("Image full file Signature Validation failed");
George Liu0a06e972020-12-17 09:17:04 +0800217 return false;
218 }
219
Lei YU7ab55e22021-05-19 13:26:53 +0800220 if (!bmcFilesFound && !optionalFilesFound)
221 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500222 error("Unable to find files to verify");
Lei YU7ab55e22021-05-19 13:26:53 +0800223 return false;
224 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600225
Lei YU7ab55e22021-05-19 13:26:53 +0800226 // Either BMC images or optional images shall be valid
227 assert(valid || optionalImagesValid);
228
Manojkiran Eda19dd56b2024-06-17 14:29:52 +0530229 debug("Successfully completed Signature validation.");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600230 return true;
231 }
232 catch (const InternalFailure& e)
233 {
234 return false;
235 }
236 catch (const std::exception& e)
237 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500238 error("Error during processing: {ERROR}", "ERROR", e);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600239 return false;
240 }
241}
242
243bool Signature::systemLevelVerify()
244{
245 // Get available key types from the system.
246 auto keyTypes = getAvailableKeyTypesFromSystem();
247 if (keyTypes.empty())
248 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500249 error("Missing Signature configuration data in system");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600250 elog<InternalFailure>();
251 }
252
253 // Build publickey and its signature file name.
254 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
255 fs::path pkeyFileSig(pkeyFile);
256 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
257
258 // Build manifest and its signature file name.
259 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
260 fs::path manifestFileSig(manifestFile);
261 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
262
263 auto valid = false;
264
265 // Verify the file signature with available key types
266 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600267 // For any internal failure during the key/hash pair specific
268 // validation, should continue the validation with next
269 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600270 for (const auto& keyType : keyTypes)
271 {
272 auto keyHashPair = getKeyHashFileNames(keyType);
273
274 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
275
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600276 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600277 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600278 // Verify manifest file signature
279 valid = verifyFile(manifestFile, manifestFileSig,
280 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600281 if (valid)
282 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600283 // Verify publickey file signature.
284 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
285 hashFunc);
286 if (valid)
287 {
288 break;
289 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600290 }
291 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600292 catch (const InternalFailure& e)
293 {
294 valid = false;
295 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600296 }
297 return valid;
298}
299
300bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
301 const fs::path& publicKey,
302 const std::string& hashFunc)
303{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600304 // Check existence of the files in the system.
George Liu44b9fef2023-02-07 14:31:32 +0800305 std::error_code ec;
306 if (!(fs::exists(file, ec) && fs::exists(sigFile, ec)))
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600307 {
George Liu2851e0d2023-12-05 14:44:11 +0800308 error("Failed to find the Data or signature file {PATH}", "PATH", file);
309 if (ec)
310 {
311 error("Error message: {ERROR_MSG}", "ERROR_MSG", ec.message());
312 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600313 elog<InternalFailure>();
314 }
315
316 // Create RSA.
317 auto publicRSA = createPublicRSA(publicKey);
Patrick Williamsd75c8692021-12-07 16:25:10 -0600318 if (!publicRSA)
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600319 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500320 error("Failed to create RSA from {PATH}", "PATH", publicKey);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600321 elog<InternalFailure>();
322 }
323
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600324 // Initializes a digest context.
Adriana Kobylak5ed9b2d2018-09-06 13:15:34 -0500325 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600326
327 // Adds all digest algorithms to the internal table
328 OpenSSL_add_all_digests();
329
Gunnar Mills2bcba022018-04-08 15:02:04 -0500330 // Create Hash structure.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600331 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
332 if (!hashStruct)
333 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500334 error("EVP_get_digestbynam: Unknown message digest: {HASH}", "HASH",
335 hashFunc);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600336 elog<InternalFailure>();
337 }
338
339 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
Patrick Williamsd75c8692021-12-07 16:25:10 -0600340 nullptr, publicRSA.get());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600341
342 if (result <= 0)
343 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500344 error("Error ({RC}) occurred during EVP_DigestVerifyInit", "RC",
345 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600346 elog<InternalFailure>();
347 }
348
349 // Hash the data file and update the verification context
George Liu44b9fef2023-02-07 14:31:32 +0800350 auto size = fs::file_size(file, ec);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600351 auto dataPtr = mapFile(file, size);
352
353 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
354 if (result <= 0)
355 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500356 error("Error ({RC}) occurred during EVP_DigestVerifyUpdate", "RC",
357 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600358 elog<InternalFailure>();
359 }
360
361 // Verify the data with signature.
George Liu44b9fef2023-02-07 14:31:32 +0800362 size = fs::file_size(sigFile, ec);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600363 auto signature = mapFile(sigFile, size);
364
365 result = EVP_DigestVerifyFinal(
366 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
367 size);
368
369 // Check the verification result.
370 if (result < 0)
371 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500372 error("Error ({RC}) occurred during EVP_DigestVerifyFinal", "RC",
373 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600374 elog<InternalFailure>();
375 }
376
377 if (result == 0)
378 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500379 error("EVP_DigestVerifyFinal:Signature validation failed on {PATH}",
380 "PATH", sigFile);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600381 return false;
382 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600383 return true;
384}
385
Patrick Williamsd75c8692021-12-07 16:25:10 -0600386inline EVP_PKEY_Ptr Signature::createPublicRSA(const fs::path& publicKey)
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600387{
George Liu44b9fef2023-02-07 14:31:32 +0800388 std::error_code ec;
389 auto size = fs::file_size(publicKey, ec);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600390
391 // Read public key file
392 auto data = mapFile(publicKey, size);
393
394 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
395 if (keyBio.get() == nullptr)
396 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500397 error("Failed to create new BIO Memory buffer");
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600398 elog<InternalFailure>();
399 }
400
Patrick Williamsd75c8692021-12-07 16:25:10 -0600401 return {PEM_read_bio_PUBKEY(keyBio.get(), nullptr, nullptr, nullptr),
402 &::EVP_PKEY_free};
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600403}
404
405CustomMap Signature::mapFile(const fs::path& path, size_t size)
406{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600407 CustomFd fd(open(path.c_str(), O_RDONLY));
408
409 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
410 size);
411}
412
Henry Tian574f94b2021-01-06 10:33:59 +0800413bool Signature::checkAndVerifyImage(const std::string& filePath,
414 const std::string& publicKeyPath,
Lei YU7ab55e22021-05-19 13:26:53 +0800415 const std::vector<std::string>& imageList,
416 bool& fileFound)
Henry Tian574f94b2021-01-06 10:33:59 +0800417{
418 bool valid = true;
419
Lei YU7ab55e22021-05-19 13:26:53 +0800420 fileFound = false;
Henry Tian574f94b2021-01-06 10:33:59 +0800421 for (auto& bmcImage : imageList)
422 {
423 fs::path file(filePath);
424 file /= bmcImage;
425
George Liu44b9fef2023-02-07 14:31:32 +0800426 std::error_code ec;
427 if (!fs::exists(file, ec))
Henry Tian574f94b2021-01-06 10:33:59 +0800428 {
429 valid = false;
430 break;
431 }
Lei YU7ab55e22021-05-19 13:26:53 +0800432 fileFound = true;
Henry Tian574f94b2021-01-06 10:33:59 +0800433
434 fs::path sigFile(file);
Lei YU92c7e9e2021-05-20 16:32:28 +0800435 sigFile += SIGNATURE_FILE_EXT;
Henry Tian574f94b2021-01-06 10:33:59 +0800436
437 // Verify the signature.
Lei YU0cd6d842021-12-27 11:56:02 +0800438 valid = verifyFile(file, sigFile, publicKeyPath, hashType);
Henry Tian574f94b2021-01-06 10:33:59 +0800439 if (valid == false)
440 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500441 error("Image file Signature Validation failed on {PATH}", "PATH",
442 bmcImage);
Henry Tian574f94b2021-01-06 10:33:59 +0800443 return false;
444 }
445 }
446
447 return valid;
448}
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600449} // namespace image
450} // namespace software
451} // namespace phosphor