blob: 098ad9db1ee66817b2995c0940146cd29d345c84 [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);
47}
48
49AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
50{
51 AvailableKeyTypes keyTypes{};
52
53 // Find the path of all the files
54 if (!fs::is_directory(signedConfPath))
55 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -050056 error("Signed configuration path not found in the system");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060057 elog<InternalFailure>();
58 }
59
60 // Look for all the hash and public key file names get the key value
61 // For example:
62 // /etc/activationdata/OpenBMC/publickey
63 // /etc/activationdata/OpenBMC/hashfunc
64 // /etc/activationdata/GA/publickey
65 // /etc/activationdata/GA/hashfunc
66 // Set will have OpenBMC, GA
67
68 for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
69 {
70 if ((p.path().filename() == HASH_FILE_NAME) ||
71 (p.path().filename() == PUBLICKEY_FILE_NAME))
72 {
73 // extract the key types
74 // /etc/activationdata/OpenBMC/ -> get OpenBMC from the path
75 auto key = p.path().parent_path();
76 keyTypes.insert(key.filename());
77 }
78 }
79
80 return keyTypes;
81}
82
83inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
84{
85 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
86 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
87
88 return std::make_pair(std::move(hashpath), std::move(keyPath));
89}
90
George Liu0a06e972020-12-17 09:17:04 +080091bool Signature::verifyFullImage()
92{
93 bool ret = true;
94#ifdef WANT_SIGNATURE_FULL_VERIFY
95 std::vector<std::string> fullImages = {
96 fs::path(imageDirPath) / "image-bmc.sig",
97 fs::path(imageDirPath) / "image-hostfw.sig",
98 fs::path(imageDirPath) / "image-kernel.sig",
99 fs::path(imageDirPath) / "image-rofs.sig",
100 fs::path(imageDirPath) / "image-rwfs.sig",
101 fs::path(imageDirPath) / "image-u-boot.sig",
102 fs::path(imageDirPath) / "MANIFEST.sig",
103 fs::path(imageDirPath) / "publickey.sig"};
104
105 // Merge files
106 std::string tmpFullFile = "/tmp/image-full";
107 utils::mergeFiles(fullImages, tmpFullFile);
108
109 // Validate the full image files
110 fs::path pkeyFullFile(tmpFullFile);
111
112 std::string imageFullSig = "image-full.sig";
113 fs::path pkeyFullFileSig(imageDirPath / imageFullSig);
114 pkeyFullFileSig.replace_extension(SIGNATURE_FILE_EXT);
115
116 // image specific publickey file name.
117 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
118
119 ret = verifyFile(pkeyFullFile, pkeyFullFileSig, publicKeyFile, hashType);
120 fs::remove(tmpFullFile);
121#endif
122
123 return ret;
124}
125
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600126bool Signature::verify()
127{
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600128 try
129 {
Henry Tian574f94b2021-01-06 10:33:59 +0800130 bool valid;
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600131 // Verify the MANIFEST and publickey file using available
132 // public keys and hash on the system.
133 if (false == systemLevelVerify())
134 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500135 error("System level Signature Validation failed");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600136 return false;
137 }
138
Lei YU7ab55e22021-05-19 13:26:53 +0800139 bool bmcFilesFound = false;
Gunnar Millse11a2022018-03-23 12:04:48 -0500140 // image specific publickey file name.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600141 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
142
Henry Tian574f94b2021-01-06 10:33:59 +0800143 // Record the images which are being updated
144 // First check and Validate for the fullimage, then check and Validate
145 // for images with partitions
146 std::vector<std::string> imageUpdateList = {bmcFullImages};
Lei YU7ab55e22021-05-19 13:26:53 +0800147 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
148 imageUpdateList, bmcFilesFound);
149 if (bmcFilesFound && !valid)
150 {
151 return false;
152 }
153
Henry Tian574f94b2021-01-06 10:33:59 +0800154 if (!valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600155 {
Lei YU7ab55e22021-05-19 13:26:53 +0800156 // Validate bmcImages
Henry Tian574f94b2021-01-06 10:33:59 +0800157 imageUpdateList.clear();
158 imageUpdateList.assign(bmcImages.begin(), bmcImages.end());
159 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
Lei YU7ab55e22021-05-19 13:26:53 +0800160 imageUpdateList, bmcFilesFound);
161 if (bmcFilesFound && !valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600162 {
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600163 return false;
164 }
165 }
Henry Tian574f94b2021-01-06 10:33:59 +0800166
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500167 // Validate the optional image files.
168 auto optionalImages = getOptionalImages();
Lei YU7ab55e22021-05-19 13:26:53 +0800169 bool optionalFilesFound = false;
170 bool optionalImagesValid = false;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500171 for (const auto& optionalImage : optionalImages)
172 {
173 // Build Image File name
174 fs::path file(imageDirPath);
175 file /= optionalImage;
176
177 if (fs::exists(file))
178 {
Lei YU7ab55e22021-05-19 13:26:53 +0800179 optionalFilesFound = true;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500180 // Build Signature File name
181 fs::path sigFile(file);
Lei YU92c7e9e2021-05-20 16:32:28 +0800182 sigFile += SIGNATURE_FILE_EXT;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500183
184 // Verify the signature.
Lei YU7ab55e22021-05-19 13:26:53 +0800185 optionalImagesValid =
186 verifyFile(file, sigFile, publicKeyFile, hashType);
187 if (!optionalImagesValid)
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500188 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500189 error("Image file Signature Validation failed on {IMAGE}",
190 "IMAGE", optionalImage);
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500191 return false;
192 }
193 }
194 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600195
George Liu0a06e972020-12-17 09:17:04 +0800196 if (verifyFullImage() == false)
197 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500198 error("Image full file Signature Validation failed");
George Liu0a06e972020-12-17 09:17:04 +0800199 return false;
200 }
201
Lei YU7ab55e22021-05-19 13:26:53 +0800202 if (!bmcFilesFound && !optionalFilesFound)
203 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500204 error("Unable to find files to verify");
Lei YU7ab55e22021-05-19 13:26:53 +0800205 return false;
206 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600207
Lei YU7ab55e22021-05-19 13:26:53 +0800208 // Either BMC images or optional images shall be valid
209 assert(valid || optionalImagesValid);
210
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500211 debug("Successfully completed Signature vaildation.");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600212 return true;
213 }
214 catch (const InternalFailure& e)
215 {
216 return false;
217 }
218 catch (const std::exception& e)
219 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500220 error("Error during processing: {ERROR}", "ERROR", e);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600221 return false;
222 }
223}
224
225bool Signature::systemLevelVerify()
226{
227 // Get available key types from the system.
228 auto keyTypes = getAvailableKeyTypesFromSystem();
229 if (keyTypes.empty())
230 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500231 error("Missing Signature configuration data in system");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600232 elog<InternalFailure>();
233 }
234
235 // Build publickey and its signature file name.
236 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
237 fs::path pkeyFileSig(pkeyFile);
238 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
239
240 // Build manifest and its signature file name.
241 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
242 fs::path manifestFileSig(manifestFile);
243 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
244
245 auto valid = false;
246
247 // Verify the file signature with available key types
248 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600249 // For any internal failure during the key/hash pair specific
250 // validation, should continue the validation with next
251 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600252 for (const auto& keyType : keyTypes)
253 {
254 auto keyHashPair = getKeyHashFileNames(keyType);
255
256 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
257
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600258 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600259 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600260 // Verify manifest file signature
261 valid = verifyFile(manifestFile, manifestFileSig,
262 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600263 if (valid)
264 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600265 // Verify publickey file signature.
266 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
267 hashFunc);
268 if (valid)
269 {
270 break;
271 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600272 }
273 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600274 catch (const InternalFailure& e)
275 {
276 valid = false;
277 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600278 }
279 return valid;
280}
281
282bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
283 const fs::path& publicKey,
284 const std::string& hashFunc)
285{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600286
287 // Check existence of the files in the system.
288 if (!(fs::exists(file) && fs::exists(sigFile)))
289 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500290 error("Failed to find the Data or signature file {PATH}.", "PATH",
291 file);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600292 elog<InternalFailure>();
293 }
294
295 // Create RSA.
296 auto publicRSA = createPublicRSA(publicKey);
Patrick Williamsd75c8692021-12-07 16:25:10 -0600297 if (!publicRSA)
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600298 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500299 error("Failed to create RSA from {PATH}", "PATH", publicKey);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600300 elog<InternalFailure>();
301 }
302
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600303 // Initializes a digest context.
Adriana Kobylak5ed9b2d2018-09-06 13:15:34 -0500304 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600305
306 // Adds all digest algorithms to the internal table
307 OpenSSL_add_all_digests();
308
Gunnar Mills2bcba022018-04-08 15:02:04 -0500309 // Create Hash structure.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600310 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
311 if (!hashStruct)
312 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500313 error("EVP_get_digestbynam: Unknown message digest: {HASH}", "HASH",
314 hashFunc);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600315 elog<InternalFailure>();
316 }
317
318 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
Patrick Williamsd75c8692021-12-07 16:25:10 -0600319 nullptr, publicRSA.get());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600320
321 if (result <= 0)
322 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500323 error("Error ({RC}) occurred during EVP_DigestVerifyInit", "RC",
324 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600325 elog<InternalFailure>();
326 }
327
328 // Hash the data file and update the verification context
329 auto size = fs::file_size(file);
330 auto dataPtr = mapFile(file, size);
331
332 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
333 if (result <= 0)
334 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500335 error("Error ({RC}) occurred during EVP_DigestVerifyUpdate", "RC",
336 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600337 elog<InternalFailure>();
338 }
339
340 // Verify the data with signature.
341 size = fs::file_size(sigFile);
342 auto signature = mapFile(sigFile, size);
343
344 result = EVP_DigestVerifyFinal(
345 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
346 size);
347
348 // Check the verification result.
349 if (result < 0)
350 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500351 error("Error ({RC}) occurred during EVP_DigestVerifyFinal", "RC",
352 ERR_get_error());
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600353 elog<InternalFailure>();
354 }
355
356 if (result == 0)
357 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500358 error("EVP_DigestVerifyFinal:Signature validation failed on {PATH}",
359 "PATH", sigFile);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600360 return false;
361 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600362 return true;
363}
364
Patrick Williamsd75c8692021-12-07 16:25:10 -0600365inline EVP_PKEY_Ptr Signature::createPublicRSA(const fs::path& publicKey)
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600366{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600367 auto size = fs::file_size(publicKey);
368
369 // Read public key file
370 auto data = mapFile(publicKey, size);
371
372 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
373 if (keyBio.get() == nullptr)
374 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500375 error("Failed to create new BIO Memory buffer");
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600376 elog<InternalFailure>();
377 }
378
Patrick Williamsd75c8692021-12-07 16:25:10 -0600379 return {PEM_read_bio_PUBKEY(keyBio.get(), nullptr, nullptr, nullptr),
380 &::EVP_PKEY_free};
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600381}
382
383CustomMap Signature::mapFile(const fs::path& path, size_t size)
384{
385
386 CustomFd fd(open(path.c_str(), O_RDONLY));
387
388 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
389 size);
390}
391
Henry Tian574f94b2021-01-06 10:33:59 +0800392bool Signature::checkAndVerifyImage(const std::string& filePath,
393 const std::string& publicKeyPath,
Lei YU7ab55e22021-05-19 13:26:53 +0800394 const std::vector<std::string>& imageList,
395 bool& fileFound)
Henry Tian574f94b2021-01-06 10:33:59 +0800396{
397 bool valid = true;
398
Lei YU7ab55e22021-05-19 13:26:53 +0800399 fileFound = false;
Henry Tian574f94b2021-01-06 10:33:59 +0800400 for (auto& bmcImage : imageList)
401 {
402 fs::path file(filePath);
403 file /= bmcImage;
404
405 if (!fs::exists(file))
406 {
407 valid = false;
408 break;
409 }
Lei YU7ab55e22021-05-19 13:26:53 +0800410 fileFound = true;
Henry Tian574f94b2021-01-06 10:33:59 +0800411
412 fs::path sigFile(file);
Lei YU92c7e9e2021-05-20 16:32:28 +0800413 sigFile += SIGNATURE_FILE_EXT;
Henry Tian574f94b2021-01-06 10:33:59 +0800414
415 // Verify the signature.
Lei YU0cd6d842021-12-27 11:56:02 +0800416 valid = verifyFile(file, sigFile, publicKeyPath, hashType);
Henry Tian574f94b2021-01-06 10:33:59 +0800417 if (valid == false)
418 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500419 error("Image file Signature Validation failed on {PATH}", "PATH",
420 bmcImage);
Henry Tian574f94b2021-01-06 10:33:59 +0800421 return false;
422 }
423 }
424
425 return valid;
426}
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600427} // namespace image
428} // namespace software
429} // namespace phosphor