blob: 687fff706a523680fb8e91a412467eaf0dd89ed7 [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) :
Patrick Williamsfc33ba82024-08-16 15:19:54 -040041 imageDirPath(imageDirPath), signedConfPath(signedConfPath)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060042{
43 fs::path file(imageDirPath / MANIFEST_FILE_NAME);
44
45 keyType = Version::getValue(file, keyTypeTag);
46 hashType = Version::getValue(file, hashFunctionTag);
Lei YU6173a072022-05-23 19:18:57 +080047
48 // Get purpose
49 auto purposeString = Version::getValue(file, "purpose");
50 auto convertedPurpose =
51 sdbusplus::message::convert_from_string<VersionPurpose>(purposeString);
52 purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060053}
54
55AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
56{
57 AvailableKeyTypes keyTypes{};
58
59 // Find the path of all the files
George Liu44b9fef2023-02-07 14:31:32 +080060 std::error_code ec;
61 if (!fs::is_directory(signedConfPath, ec))
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060062 {
George Liu44b9fef2023-02-07 14:31:32 +080063 error("Signed configuration path not found in the system: {ERROR_MSG}",
64 "ERROR_MSG", ec.message());
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060065 elog<InternalFailure>();
66 }
67
68 // Look for all the hash and public key file names get the key value
69 // For example:
70 // /etc/activationdata/OpenBMC/publickey
71 // /etc/activationdata/OpenBMC/hashfunc
72 // /etc/activationdata/GA/publickey
73 // /etc/activationdata/GA/hashfunc
74 // Set will have OpenBMC, GA
75
76 for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
77 {
78 if ((p.path().filename() == HASH_FILE_NAME) ||
79 (p.path().filename() == PUBLICKEY_FILE_NAME))
80 {
81 // extract the key types
82 // /etc/activationdata/OpenBMC/ -> get OpenBMC from the path
83 auto key = p.path().parent_path();
84 keyTypes.insert(key.filename());
85 }
86 }
87
88 return keyTypes;
89}
90
91inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
92{
93 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
94 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
95
96 return std::make_pair(std::move(hashpath), std::move(keyPath));
97}
98
George Liu0a06e972020-12-17 09:17:04 +080099bool Signature::verifyFullImage()
100{
101 bool ret = true;
Konstantin Aladyshev294991a2023-04-19 15:24:20 +0300102#ifdef WANT_SIGNATURE_VERIFY
Lei YU6173a072022-05-23 19:18:57 +0800103 // Only verify full image for BMC
104 if (purpose != VersionPurpose::BMC)
105 {
106 return ret;
107 }
108
George Liu0a06e972020-12-17 09:17:04 +0800109 std::vector<std::string> fullImages = {
110 fs::path(imageDirPath) / "image-bmc.sig",
111 fs::path(imageDirPath) / "image-hostfw.sig",
112 fs::path(imageDirPath) / "image-kernel.sig",
113 fs::path(imageDirPath) / "image-rofs.sig",
114 fs::path(imageDirPath) / "image-rwfs.sig",
115 fs::path(imageDirPath) / "image-u-boot.sig",
116 fs::path(imageDirPath) / "MANIFEST.sig",
117 fs::path(imageDirPath) / "publickey.sig"};
118
119 // Merge files
120 std::string tmpFullFile = "/tmp/image-full";
121 utils::mergeFiles(fullImages, tmpFullFile);
122
123 // Validate the full image files
124 fs::path pkeyFullFile(tmpFullFile);
125
126 std::string imageFullSig = "image-full.sig";
127 fs::path pkeyFullFileSig(imageDirPath / imageFullSig);
128 pkeyFullFileSig.replace_extension(SIGNATURE_FILE_EXT);
129
130 // image specific publickey file name.
131 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
132
133 ret = verifyFile(pkeyFullFile, pkeyFullFileSig, publicKeyFile, hashType);
George Liu44b9fef2023-02-07 14:31:32 +0800134
135 std::error_code ec;
136 fs::remove(tmpFullFile, ec);
George Liu0a06e972020-12-17 09:17:04 +0800137#endif
138
139 return ret;
140}
141
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600142bool Signature::verify()
143{
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600144 try
145 {
Henry Tian574f94b2021-01-06 10:33:59 +0800146 bool valid;
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600147 // Verify the MANIFEST and publickey file using available
148 // public keys and hash on the system.
Pavithra Barithaya2d5704e2024-06-26 02:35:08 -0500149 if (!systemLevelVerify())
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600150 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500151 error("System level Signature Validation failed");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600152 return false;
153 }
154
Lei YU7ab55e22021-05-19 13:26:53 +0800155 bool bmcFilesFound = false;
Gunnar Millse11a2022018-03-23 12:04:48 -0500156 // image specific publickey file name.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600157 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
158
Henry Tian574f94b2021-01-06 10:33:59 +0800159 // Record the images which are being updated
160 // First check and Validate for the fullimage, then check and Validate
161 // for images with partitions
162 std::vector<std::string> imageUpdateList = {bmcFullImages};
Lei YU7ab55e22021-05-19 13:26:53 +0800163 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
164 imageUpdateList, bmcFilesFound);
165 if (bmcFilesFound && !valid)
166 {
167 return false;
168 }
169
Henry Tian574f94b2021-01-06 10:33:59 +0800170 if (!valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600171 {
Lei YU7ab55e22021-05-19 13:26:53 +0800172 // Validate bmcImages
Henry Tian574f94b2021-01-06 10:33:59 +0800173 imageUpdateList.clear();
174 imageUpdateList.assign(bmcImages.begin(), bmcImages.end());
175 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
Lei YU7ab55e22021-05-19 13:26:53 +0800176 imageUpdateList, bmcFilesFound);
177 if (bmcFilesFound && !valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600178 {
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600179 return false;
180 }
181 }
Henry Tian574f94b2021-01-06 10:33:59 +0800182
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500183 // Validate the optional image files.
184 auto optionalImages = getOptionalImages();
Lei YU7ab55e22021-05-19 13:26:53 +0800185 bool optionalFilesFound = false;
186 bool optionalImagesValid = false;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500187 for (const auto& optionalImage : optionalImages)
188 {
189 // Build Image File name
190 fs::path file(imageDirPath);
191 file /= optionalImage;
192
George Liu44b9fef2023-02-07 14:31:32 +0800193 std::error_code ec;
194 if (fs::exists(file, ec))
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500195 {
Lei YU7ab55e22021-05-19 13:26:53 +0800196 optionalFilesFound = true;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500197 // Build Signature File name
198 fs::path sigFile(file);
Lei YU92c7e9e2021-05-20 16:32:28 +0800199 sigFile += SIGNATURE_FILE_EXT;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500200
201 // Verify the signature.
Patrick Williamsfc33ba82024-08-16 15:19:54 -0400202 optionalImagesValid =
203 verifyFile(file, sigFile, publicKeyFile, hashType);
Lei YU7ab55e22021-05-19 13:26:53 +0800204 if (!optionalImagesValid)
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500205 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500206 error("Image file Signature Validation failed on {IMAGE}",
207 "IMAGE", optionalImage);
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500208 return false;
209 }
210 }
211 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600212
Pavithra Barithaya2d5704e2024-06-26 02:35:08 -0500213 if (!verifyFullImage())
George Liu0a06e972020-12-17 09:17:04 +0800214 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500215 error("Image full file Signature Validation failed");
George Liu0a06e972020-12-17 09:17:04 +0800216 return false;
217 }
218
Lei YU7ab55e22021-05-19 13:26:53 +0800219 if (!bmcFilesFound && !optionalFilesFound)
220 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500221 error("Unable to find files to verify");
Lei YU7ab55e22021-05-19 13:26:53 +0800222 return false;
223 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600224
Lei YU7ab55e22021-05-19 13:26:53 +0800225 // Either BMC images or optional images shall be valid
226 assert(valid || optionalImagesValid);
227
Manojkiran Eda19dd56b2024-06-17 14:29:52 +0530228 debug("Successfully completed Signature validation.");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600229 return true;
230 }
231 catch (const InternalFailure& e)
232 {
233 return false;
234 }
235 catch (const std::exception& e)
236 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500237 error("Error during processing: {ERROR}", "ERROR", e);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600238 return false;
239 }
240}
241
242bool Signature::systemLevelVerify()
243{
244 // Get available key types from the system.
245 auto keyTypes = getAvailableKeyTypesFromSystem();
246 if (keyTypes.empty())
247 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500248 error("Missing Signature configuration data in system");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600249 elog<InternalFailure>();
250 }
251
252 // Build publickey and its signature file name.
253 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
254 fs::path pkeyFileSig(pkeyFile);
255 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
256
257 // Build manifest and its signature file name.
258 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
259 fs::path manifestFileSig(manifestFile);
260 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
261
262 auto valid = false;
263
264 // Verify the file signature with available key types
265 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600266 // For any internal failure during the key/hash pair specific
267 // validation, should continue the validation with next
268 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600269 for (const auto& keyType : keyTypes)
270 {
271 auto keyHashPair = getKeyHashFileNames(keyType);
272
273 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
274
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600275 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600276 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600277 // Verify manifest file signature
278 valid = verifyFile(manifestFile, manifestFileSig,
279 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600280 if (valid)
281 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600282 // Verify publickey file signature.
283 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
284 hashFunc);
285 if (valid)
286 {
287 break;
288 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600289 }
290 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600291 catch (const InternalFailure& e)
292 {
293 valid = false;
294 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600295 }
296 return valid;
297}
298
299bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
300 const fs::path& publicKey,
301 const std::string& hashFunc)
302{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600303 // Check existence of the files in the system.
George Liu44b9fef2023-02-07 14:31:32 +0800304 std::error_code ec;
305 if (!(fs::exists(file, ec) && fs::exists(sigFile, ec)))
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600306 {
George Liu2851e0d2023-12-05 14:44:11 +0800307 error("Failed to find the Data or signature file {PATH}", "PATH", file);
308 if (ec)
309 {
310 error("Error message: {ERROR_MSG}", "ERROR_MSG", ec.message());
311 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600312 elog<InternalFailure>();
313 }
314
315 // Create RSA.
316 auto publicRSA = createPublicRSA(publicKey);
Patrick Williamsd75c8692021-12-07 16:25:10 -0600317 if (!publicRSA)
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600318 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500319 error("Failed to create RSA from {PATH}", "PATH", publicKey);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600320 elog<InternalFailure>();
321 }
322
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600323 // Initializes a digest context.
Adriana Kobylak5ed9b2d2018-09-06 13:15:34 -0500324 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600325
326 // Adds all digest algorithms to the internal table
327 OpenSSL_add_all_digests();
328
Gunnar Mills2bcba022018-04-08 15:02:04 -0500329 // Create Hash structure.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600330 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
331 if (!hashStruct)
332 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500333 error("EVP_get_digestbynam: Unknown message digest: {HASH}", "HASH",
334 hashFunc);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600335 elog<InternalFailure>();
336 }
337
338 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
Patrick Williamsd75c8692021-12-07 16:25:10 -0600339 nullptr, publicRSA.get());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600340
341 if (result <= 0)
342 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500343 error("Error ({RC}) occurred during EVP_DigestVerifyInit", "RC",
344 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600345 elog<InternalFailure>();
346 }
347
348 // Hash the data file and update the verification context
George Liu44b9fef2023-02-07 14:31:32 +0800349 auto size = fs::file_size(file, ec);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600350 auto dataPtr = mapFile(file, size);
351
352 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
353 if (result <= 0)
354 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500355 error("Error ({RC}) occurred during EVP_DigestVerifyUpdate", "RC",
356 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600357 elog<InternalFailure>();
358 }
359
360 // Verify the data with signature.
George Liu44b9fef2023-02-07 14:31:32 +0800361 size = fs::file_size(sigFile, ec);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600362 auto signature = mapFile(sigFile, size);
363
364 result = EVP_DigestVerifyFinal(
365 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
366 size);
367
368 // Check the verification result.
369 if (result < 0)
370 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500371 error("Error ({RC}) occurred during EVP_DigestVerifyFinal", "RC",
372 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600373 elog<InternalFailure>();
374 }
375
376 if (result == 0)
377 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500378 error("EVP_DigestVerifyFinal:Signature validation failed on {PATH}",
379 "PATH", sigFile);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600380 return false;
381 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600382 return true;
383}
384
Patrick Williamsd75c8692021-12-07 16:25:10 -0600385inline EVP_PKEY_Ptr Signature::createPublicRSA(const fs::path& publicKey)
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600386{
George Liu44b9fef2023-02-07 14:31:32 +0800387 std::error_code ec;
388 auto size = fs::file_size(publicKey, ec);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600389
390 // Read public key file
391 auto data = mapFile(publicKey, size);
392
393 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
394 if (keyBio.get() == nullptr)
395 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500396 error("Failed to create new BIO Memory buffer");
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600397 elog<InternalFailure>();
398 }
399
Patrick Williamsd75c8692021-12-07 16:25:10 -0600400 return {PEM_read_bio_PUBKEY(keyBio.get(), nullptr, nullptr, nullptr),
401 &::EVP_PKEY_free};
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600402}
403
404CustomMap Signature::mapFile(const fs::path& path, size_t size)
405{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600406 CustomFd fd(open(path.c_str(), O_RDONLY));
407
408 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
409 size);
410}
411
Patrick Williamsfc33ba82024-08-16 15:19:54 -0400412bool Signature::checkAndVerifyImage(
413 const std::string& filePath, const std::string& publicKeyPath,
414 const std::vector<std::string>& imageList, bool& fileFound)
Henry Tian574f94b2021-01-06 10:33:59 +0800415{
416 bool valid = true;
417
Lei YU7ab55e22021-05-19 13:26:53 +0800418 fileFound = false;
Henry Tian574f94b2021-01-06 10:33:59 +0800419 for (auto& bmcImage : imageList)
420 {
421 fs::path file(filePath);
422 file /= bmcImage;
423
George Liu44b9fef2023-02-07 14:31:32 +0800424 std::error_code ec;
425 if (!fs::exists(file, ec))
Henry Tian574f94b2021-01-06 10:33:59 +0800426 {
427 valid = false;
428 break;
429 }
Lei YU7ab55e22021-05-19 13:26:53 +0800430 fileFound = true;
Henry Tian574f94b2021-01-06 10:33:59 +0800431
432 fs::path sigFile(file);
Lei YU92c7e9e2021-05-20 16:32:28 +0800433 sigFile += SIGNATURE_FILE_EXT;
Henry Tian574f94b2021-01-06 10:33:59 +0800434
435 // Verify the signature.
Lei YU0cd6d842021-12-27 11:56:02 +0800436 valid = verifyFile(file, sigFile, publicKeyPath, hashType);
Pavithra Barithaya2d5704e2024-06-26 02:35:08 -0500437 if (!valid)
Henry Tian574f94b2021-01-06 10:33:59 +0800438 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500439 error("Image file Signature Validation failed on {PATH}", "PATH",
440 bmcImage);
Henry Tian574f94b2021-01-06 10:33:59 +0800441 return false;
442 }
443 }
444
445 return valid;
446}
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600447} // namespace image
448} // namespace software
449} // namespace phosphor