blob: 9551e4ea7ad9a0e4ba0bf98ab65e2ec1d98e50f3 [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>
21
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060022namespace phosphor
23{
24namespace software
25{
26namespace image
27{
28
Patrick Williamsc9bb6422021-08-27 06:18:35 -050029PHOSPHOR_LOG2_USING;
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060030using namespace phosphor::logging;
31using namespace phosphor::software::manager;
32using InternalFailure =
33 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
34
35constexpr auto keyTypeTag = "KeyType";
36constexpr auto hashFunctionTag = "HashType";
37
38Signature::Signature(const fs::path& imageDirPath,
39 const fs::path& signedConfPath) :
40 imageDirPath(imageDirPath),
41 signedConfPath(signedConfPath)
42{
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
60 if (!fs::is_directory(signedConfPath))
61 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -050062 error("Signed configuration path not found in the system");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060063 elog<InternalFailure>();
64 }
65
66 // Look for all the hash and public key file names get the key value
67 // For example:
68 // /etc/activationdata/OpenBMC/publickey
69 // /etc/activationdata/OpenBMC/hashfunc
70 // /etc/activationdata/GA/publickey
71 // /etc/activationdata/GA/hashfunc
72 // Set will have OpenBMC, GA
73
74 for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
75 {
76 if ((p.path().filename() == HASH_FILE_NAME) ||
77 (p.path().filename() == PUBLICKEY_FILE_NAME))
78 {
79 // extract the key types
80 // /etc/activationdata/OpenBMC/ -> get OpenBMC from the path
81 auto key = p.path().parent_path();
82 keyTypes.insert(key.filename());
83 }
84 }
85
86 return keyTypes;
87}
88
89inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
90{
91 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
92 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
93
94 return std::make_pair(std::move(hashpath), std::move(keyPath));
95}
96
George Liu0a06e972020-12-17 09:17:04 +080097bool Signature::verifyFullImage()
98{
99 bool ret = true;
100#ifdef WANT_SIGNATURE_FULL_VERIFY
Lei YU6173a072022-05-23 19:18:57 +0800101 // Only verify full image for BMC
102 if (purpose != VersionPurpose::BMC)
103 {
104 return ret;
105 }
106
George Liu0a06e972020-12-17 09:17:04 +0800107 std::vector<std::string> fullImages = {
108 fs::path(imageDirPath) / "image-bmc.sig",
109 fs::path(imageDirPath) / "image-hostfw.sig",
110 fs::path(imageDirPath) / "image-kernel.sig",
111 fs::path(imageDirPath) / "image-rofs.sig",
112 fs::path(imageDirPath) / "image-rwfs.sig",
113 fs::path(imageDirPath) / "image-u-boot.sig",
114 fs::path(imageDirPath) / "MANIFEST.sig",
115 fs::path(imageDirPath) / "publickey.sig"};
116
117 // Merge files
118 std::string tmpFullFile = "/tmp/image-full";
119 utils::mergeFiles(fullImages, tmpFullFile);
120
121 // Validate the full image files
122 fs::path pkeyFullFile(tmpFullFile);
123
124 std::string imageFullSig = "image-full.sig";
125 fs::path pkeyFullFileSig(imageDirPath / imageFullSig);
126 pkeyFullFileSig.replace_extension(SIGNATURE_FILE_EXT);
127
128 // image specific publickey file name.
129 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
130
131 ret = verifyFile(pkeyFullFile, pkeyFullFileSig, publicKeyFile, hashType);
132 fs::remove(tmpFullFile);
133#endif
134
135 return ret;
136}
137
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600138bool Signature::verify()
139{
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600140 try
141 {
Henry Tian574f94b2021-01-06 10:33:59 +0800142 bool valid;
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600143 // Verify the MANIFEST and publickey file using available
144 // public keys and hash on the system.
145 if (false == systemLevelVerify())
146 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500147 error("System level Signature Validation failed");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600148 return false;
149 }
150
Lei YU7ab55e22021-05-19 13:26:53 +0800151 bool bmcFilesFound = false;
Gunnar Millse11a2022018-03-23 12:04:48 -0500152 // image specific publickey file name.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600153 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
154
Henry Tian574f94b2021-01-06 10:33:59 +0800155 // Record the images which are being updated
156 // First check and Validate for the fullimage, then check and Validate
157 // for images with partitions
158 std::vector<std::string> imageUpdateList = {bmcFullImages};
Lei YU7ab55e22021-05-19 13:26:53 +0800159 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
160 imageUpdateList, bmcFilesFound);
161 if (bmcFilesFound && !valid)
162 {
163 return false;
164 }
165
Henry Tian574f94b2021-01-06 10:33:59 +0800166 if (!valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600167 {
Lei YU7ab55e22021-05-19 13:26:53 +0800168 // Validate bmcImages
Henry Tian574f94b2021-01-06 10:33:59 +0800169 imageUpdateList.clear();
170 imageUpdateList.assign(bmcImages.begin(), bmcImages.end());
171 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
Lei YU7ab55e22021-05-19 13:26:53 +0800172 imageUpdateList, bmcFilesFound);
173 if (bmcFilesFound && !valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600174 {
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600175 return false;
176 }
177 }
Henry Tian574f94b2021-01-06 10:33:59 +0800178
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500179 // Validate the optional image files.
180 auto optionalImages = getOptionalImages();
Lei YU7ab55e22021-05-19 13:26:53 +0800181 bool optionalFilesFound = false;
182 bool optionalImagesValid = false;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500183 for (const auto& optionalImage : optionalImages)
184 {
185 // Build Image File name
186 fs::path file(imageDirPath);
187 file /= optionalImage;
188
189 if (fs::exists(file))
190 {
Lei YU7ab55e22021-05-19 13:26:53 +0800191 optionalFilesFound = true;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500192 // Build Signature File name
193 fs::path sigFile(file);
Lei YU92c7e9e2021-05-20 16:32:28 +0800194 sigFile += SIGNATURE_FILE_EXT;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500195
196 // Verify the signature.
Lei YU7ab55e22021-05-19 13:26:53 +0800197 optionalImagesValid =
198 verifyFile(file, sigFile, publicKeyFile, hashType);
199 if (!optionalImagesValid)
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500200 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500201 error("Image file Signature Validation failed on {IMAGE}",
202 "IMAGE", optionalImage);
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500203 return false;
204 }
205 }
206 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600207
George Liu0a06e972020-12-17 09:17:04 +0800208 if (verifyFullImage() == false)
209 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500210 error("Image full file Signature Validation failed");
George Liu0a06e972020-12-17 09:17:04 +0800211 return false;
212 }
213
Lei YU7ab55e22021-05-19 13:26:53 +0800214 if (!bmcFilesFound && !optionalFilesFound)
215 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500216 error("Unable to find files to verify");
Lei YU7ab55e22021-05-19 13:26:53 +0800217 return false;
218 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600219
Lei YU7ab55e22021-05-19 13:26:53 +0800220 // Either BMC images or optional images shall be valid
221 assert(valid || optionalImagesValid);
222
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500223 debug("Successfully completed Signature vaildation.");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600224 return true;
225 }
226 catch (const InternalFailure& e)
227 {
228 return false;
229 }
230 catch (const std::exception& e)
231 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500232 error("Error during processing: {ERROR}", "ERROR", e);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600233 return false;
234 }
235}
236
237bool Signature::systemLevelVerify()
238{
239 // Get available key types from the system.
240 auto keyTypes = getAvailableKeyTypesFromSystem();
241 if (keyTypes.empty())
242 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500243 error("Missing Signature configuration data in system");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600244 elog<InternalFailure>();
245 }
246
247 // Build publickey and its signature file name.
248 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
249 fs::path pkeyFileSig(pkeyFile);
250 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
251
252 // Build manifest and its signature file name.
253 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
254 fs::path manifestFileSig(manifestFile);
255 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
256
257 auto valid = false;
258
259 // Verify the file signature with available key types
260 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600261 // For any internal failure during the key/hash pair specific
262 // validation, should continue the validation with next
263 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600264 for (const auto& keyType : keyTypes)
265 {
266 auto keyHashPair = getKeyHashFileNames(keyType);
267
268 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
269
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600270 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600271 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600272 // Verify manifest file signature
273 valid = verifyFile(manifestFile, manifestFileSig,
274 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600275 if (valid)
276 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600277 // Verify publickey file signature.
278 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
279 hashFunc);
280 if (valid)
281 {
282 break;
283 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600284 }
285 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600286 catch (const InternalFailure& e)
287 {
288 valid = false;
289 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600290 }
291 return valid;
292}
293
294bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
295 const fs::path& publicKey,
296 const std::string& hashFunc)
297{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600298
299 // Check existence of the files in the system.
300 if (!(fs::exists(file) && fs::exists(sigFile)))
301 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500302 error("Failed to find the Data or signature file {PATH}.", "PATH",
303 file);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600304 elog<InternalFailure>();
305 }
306
307 // Create RSA.
308 auto publicRSA = createPublicRSA(publicKey);
Patrick Williamsd75c8692021-12-07 16:25:10 -0600309 if (!publicRSA)
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600310 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500311 error("Failed to create RSA from {PATH}", "PATH", publicKey);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600312 elog<InternalFailure>();
313 }
314
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600315 // Initializes a digest context.
Adriana Kobylak5ed9b2d2018-09-06 13:15:34 -0500316 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600317
318 // Adds all digest algorithms to the internal table
319 OpenSSL_add_all_digests();
320
Gunnar Mills2bcba022018-04-08 15:02:04 -0500321 // Create Hash structure.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600322 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
323 if (!hashStruct)
324 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500325 error("EVP_get_digestbynam: Unknown message digest: {HASH}", "HASH",
326 hashFunc);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600327 elog<InternalFailure>();
328 }
329
330 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
Patrick Williamsd75c8692021-12-07 16:25:10 -0600331 nullptr, publicRSA.get());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600332
333 if (result <= 0)
334 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500335 error("Error ({RC}) occurred during EVP_DigestVerifyInit", "RC",
336 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600337 elog<InternalFailure>();
338 }
339
340 // Hash the data file and update the verification context
341 auto size = fs::file_size(file);
342 auto dataPtr = mapFile(file, size);
343
344 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
345 if (result <= 0)
346 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500347 error("Error ({RC}) occurred during EVP_DigestVerifyUpdate", "RC",
348 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600349 elog<InternalFailure>();
350 }
351
352 // Verify the data with signature.
353 size = fs::file_size(sigFile);
354 auto signature = mapFile(sigFile, size);
355
356 result = EVP_DigestVerifyFinal(
357 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
358 size);
359
360 // Check the verification result.
361 if (result < 0)
362 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500363 error("Error ({RC}) occurred during EVP_DigestVerifyFinal", "RC",
364 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600365 elog<InternalFailure>();
366 }
367
368 if (result == 0)
369 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500370 error("EVP_DigestVerifyFinal:Signature validation failed on {PATH}",
371 "PATH", sigFile);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600372 return false;
373 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600374 return true;
375}
376
Patrick Williamsd75c8692021-12-07 16:25:10 -0600377inline EVP_PKEY_Ptr Signature::createPublicRSA(const fs::path& publicKey)
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600378{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600379 auto size = fs::file_size(publicKey);
380
381 // Read public key file
382 auto data = mapFile(publicKey, size);
383
384 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
385 if (keyBio.get() == nullptr)
386 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500387 error("Failed to create new BIO Memory buffer");
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600388 elog<InternalFailure>();
389 }
390
Patrick Williamsd75c8692021-12-07 16:25:10 -0600391 return {PEM_read_bio_PUBKEY(keyBio.get(), nullptr, nullptr, nullptr),
392 &::EVP_PKEY_free};
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600393}
394
395CustomMap Signature::mapFile(const fs::path& path, size_t size)
396{
397
398 CustomFd fd(open(path.c_str(), O_RDONLY));
399
400 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
401 size);
402}
403
Henry Tian574f94b2021-01-06 10:33:59 +0800404bool Signature::checkAndVerifyImage(const std::string& filePath,
405 const std::string& publicKeyPath,
Lei YU7ab55e22021-05-19 13:26:53 +0800406 const std::vector<std::string>& imageList,
407 bool& fileFound)
Henry Tian574f94b2021-01-06 10:33:59 +0800408{
409 bool valid = true;
410
Lei YU7ab55e22021-05-19 13:26:53 +0800411 fileFound = false;
Henry Tian574f94b2021-01-06 10:33:59 +0800412 for (auto& bmcImage : imageList)
413 {
414 fs::path file(filePath);
415 file /= bmcImage;
416
417 if (!fs::exists(file))
418 {
419 valid = false;
420 break;
421 }
Lei YU7ab55e22021-05-19 13:26:53 +0800422 fileFound = true;
Henry Tian574f94b2021-01-06 10:33:59 +0800423
424 fs::path sigFile(file);
Lei YU92c7e9e2021-05-20 16:32:28 +0800425 sigFile += SIGNATURE_FILE_EXT;
Henry Tian574f94b2021-01-06 10:33:59 +0800426
427 // Verify the signature.
Lei YU0cd6d842021-12-27 11:56:02 +0800428 valid = verifyFile(file, sigFile, publicKeyPath, hashType);
Henry Tian574f94b2021-01-06 10:33:59 +0800429 if (valid == false)
430 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500431 error("Image file Signature Validation failed on {PATH}", "PATH",
432 bmcImage);
Henry Tian574f94b2021-01-06 10:33:59 +0800433 return false;
434 }
435 }
436
437 return valid;
438}
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600439} // namespace image
440} // namespace software
441} // namespace phosphor