blob: 4d42b49be46c9801bb647a6c3258e10b8c7b4318 [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>
15#include <phosphor-logging/log.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
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060029using namespace phosphor::logging;
30using namespace phosphor::software::manager;
31using InternalFailure =
32 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
33
34constexpr auto keyTypeTag = "KeyType";
35constexpr auto hashFunctionTag = "HashType";
36
37Signature::Signature(const fs::path& imageDirPath,
38 const fs::path& signedConfPath) :
39 imageDirPath(imageDirPath),
40 signedConfPath(signedConfPath)
41{
42 fs::path file(imageDirPath / MANIFEST_FILE_NAME);
43
44 keyType = Version::getValue(file, keyTypeTag);
45 hashType = Version::getValue(file, hashFunctionTag);
46}
47
48AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
49{
50 AvailableKeyTypes keyTypes{};
51
52 // Find the path of all the files
53 if (!fs::is_directory(signedConfPath))
54 {
55 log<level::ERR>("Signed configuration path not found in the system");
56 elog<InternalFailure>();
57 }
58
59 // Look for all the hash and public key file names get the key value
60 // For example:
61 // /etc/activationdata/OpenBMC/publickey
62 // /etc/activationdata/OpenBMC/hashfunc
63 // /etc/activationdata/GA/publickey
64 // /etc/activationdata/GA/hashfunc
65 // Set will have OpenBMC, GA
66
67 for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
68 {
69 if ((p.path().filename() == HASH_FILE_NAME) ||
70 (p.path().filename() == PUBLICKEY_FILE_NAME))
71 {
72 // extract the key types
73 // /etc/activationdata/OpenBMC/ -> get OpenBMC from the path
74 auto key = p.path().parent_path();
75 keyTypes.insert(key.filename());
76 }
77 }
78
79 return keyTypes;
80}
81
82inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
83{
84 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
85 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
86
87 return std::make_pair(std::move(hashpath), std::move(keyPath));
88}
89
George Liu0a06e972020-12-17 09:17:04 +080090bool Signature::verifyFullImage()
91{
92 bool ret = true;
93#ifdef WANT_SIGNATURE_FULL_VERIFY
94 std::vector<std::string> fullImages = {
95 fs::path(imageDirPath) / "image-bmc.sig",
96 fs::path(imageDirPath) / "image-hostfw.sig",
97 fs::path(imageDirPath) / "image-kernel.sig",
98 fs::path(imageDirPath) / "image-rofs.sig",
99 fs::path(imageDirPath) / "image-rwfs.sig",
100 fs::path(imageDirPath) / "image-u-boot.sig",
101 fs::path(imageDirPath) / "MANIFEST.sig",
102 fs::path(imageDirPath) / "publickey.sig"};
103
104 // Merge files
105 std::string tmpFullFile = "/tmp/image-full";
106 utils::mergeFiles(fullImages, tmpFullFile);
107
108 // Validate the full image files
109 fs::path pkeyFullFile(tmpFullFile);
110
111 std::string imageFullSig = "image-full.sig";
112 fs::path pkeyFullFileSig(imageDirPath / imageFullSig);
113 pkeyFullFileSig.replace_extension(SIGNATURE_FILE_EXT);
114
115 // image specific publickey file name.
116 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
117
118 ret = verifyFile(pkeyFullFile, pkeyFullFileSig, publicKeyFile, hashType);
119 fs::remove(tmpFullFile);
120#endif
121
122 return ret;
123}
124
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600125bool Signature::verify()
126{
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600127 try
128 {
Henry Tian574f94b2021-01-06 10:33:59 +0800129 bool valid;
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600130 // Verify the MANIFEST and publickey file using available
131 // public keys and hash on the system.
132 if (false == systemLevelVerify())
133 {
134 log<level::ERR>("System level Signature Validation failed");
135 return false;
136 }
137
Lei YU7ab55e22021-05-19 13:26:53 +0800138 bool bmcFilesFound = false;
Gunnar Millse11a2022018-03-23 12:04:48 -0500139 // image specific publickey file name.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600140 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
141
Henry Tian574f94b2021-01-06 10:33:59 +0800142 // Record the images which are being updated
143 // First check and Validate for the fullimage, then check and Validate
144 // for images with partitions
145 std::vector<std::string> imageUpdateList = {bmcFullImages};
Lei YU7ab55e22021-05-19 13:26:53 +0800146 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
147 imageUpdateList, bmcFilesFound);
148 if (bmcFilesFound && !valid)
149 {
150 return false;
151 }
152
Henry Tian574f94b2021-01-06 10:33:59 +0800153 if (!valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600154 {
Lei YU7ab55e22021-05-19 13:26:53 +0800155 // Validate bmcImages
Henry Tian574f94b2021-01-06 10:33:59 +0800156 imageUpdateList.clear();
157 imageUpdateList.assign(bmcImages.begin(), bmcImages.end());
158 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
Lei YU7ab55e22021-05-19 13:26:53 +0800159 imageUpdateList, bmcFilesFound);
160 if (bmcFilesFound && !valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600161 {
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600162 return false;
163 }
164 }
Henry Tian574f94b2021-01-06 10:33:59 +0800165
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500166 // Validate the optional image files.
167 auto optionalImages = getOptionalImages();
Lei YU7ab55e22021-05-19 13:26:53 +0800168 bool optionalFilesFound = false;
169 bool optionalImagesValid = false;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500170 for (const auto& optionalImage : optionalImages)
171 {
172 // Build Image File name
173 fs::path file(imageDirPath);
174 file /= optionalImage;
175
176 if (fs::exists(file))
177 {
Lei YU7ab55e22021-05-19 13:26:53 +0800178 optionalFilesFound = true;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500179 // Build Signature File name
180 fs::path sigFile(file);
Lei YU92c7e9e2021-05-20 16:32:28 +0800181 sigFile += SIGNATURE_FILE_EXT;
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500182
183 // Verify the signature.
Lei YU7ab55e22021-05-19 13:26:53 +0800184 optionalImagesValid =
185 verifyFile(file, sigFile, publicKeyFile, hashType);
186 if (!optionalImagesValid)
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500187 {
188 log<level::ERR>("Image file Signature Validation failed",
189 entry("IMAGE=%s", optionalImage.c_str()));
190 return false;
191 }
192 }
193 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600194
George Liu0a06e972020-12-17 09:17:04 +0800195 if (verifyFullImage() == false)
196 {
197 log<level::ERR>("Image full file Signature Validation failed");
198 return false;
199 }
200
Lei YU7ab55e22021-05-19 13:26:53 +0800201 if (!bmcFilesFound && !optionalFilesFound)
202 {
203 log<level::ERR>("Unable to find files to verify");
204 return false;
205 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600206
Lei YU7ab55e22021-05-19 13:26:53 +0800207 // Either BMC images or optional images shall be valid
208 assert(valid || optionalImagesValid);
209
210 log<level::DEBUG>("Successfully completed Signature vaildation.");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600211 return true;
212 }
213 catch (const InternalFailure& e)
214 {
215 return false;
216 }
217 catch (const std::exception& e)
218 {
219 log<level::ERR>(e.what());
220 return false;
221 }
222}
223
224bool Signature::systemLevelVerify()
225{
226 // Get available key types from the system.
227 auto keyTypes = getAvailableKeyTypesFromSystem();
228 if (keyTypes.empty())
229 {
230 log<level::ERR>("Missing Signature configuration data in system");
231 elog<InternalFailure>();
232 }
233
234 // Build publickey and its signature file name.
235 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
236 fs::path pkeyFileSig(pkeyFile);
237 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
238
239 // Build manifest and its signature file name.
240 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
241 fs::path manifestFileSig(manifestFile);
242 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
243
244 auto valid = false;
245
246 // Verify the file signature with available key types
247 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600248 // For any internal failure during the key/hash pair specific
249 // validation, should continue the validation with next
250 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600251 for (const auto& keyType : keyTypes)
252 {
253 auto keyHashPair = getKeyHashFileNames(keyType);
254
255 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
256
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600257 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600258 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600259 // Verify manifest file signature
260 valid = verifyFile(manifestFile, manifestFileSig,
261 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600262 if (valid)
263 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600264 // Verify publickey file signature.
265 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
266 hashFunc);
267 if (valid)
268 {
269 break;
270 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600271 }
272 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600273 catch (const InternalFailure& e)
274 {
275 valid = false;
276 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600277 }
278 return valid;
279}
280
281bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
282 const fs::path& publicKey,
283 const std::string& hashFunc)
284{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600285
286 // Check existence of the files in the system.
287 if (!(fs::exists(file) && fs::exists(sigFile)))
288 {
289 log<level::ERR>("Failed to find the Data or signature file.",
290 entry("FILE=%s", file.c_str()));
291 elog<InternalFailure>();
292 }
293
294 // Create RSA.
295 auto publicRSA = createPublicRSA(publicKey);
296 if (publicRSA == nullptr)
297 {
298 log<level::ERR>("Failed to create RSA",
299 entry("FILE=%s", publicKey.c_str()));
300 elog<InternalFailure>();
301 }
302
303 // Assign key to RSA.
304 EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free);
305 EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA);
306
307 // Initializes a digest context.
Adriana Kobylak5ed9b2d2018-09-06 13:15:34 -0500308 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600309
310 // Adds all digest algorithms to the internal table
311 OpenSSL_add_all_digests();
312
Gunnar Mills2bcba022018-04-08 15:02:04 -0500313 // Create Hash structure.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600314 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
315 if (!hashStruct)
316 {
317 log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
318 entry("HASH=%s", hashFunc.c_str()));
319 elog<InternalFailure>();
320 }
321
322 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
323 nullptr, pKeyPtr.get());
324
325 if (result <= 0)
326 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500327 log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600328 entry("ERRCODE=%lu", ERR_get_error()));
329 elog<InternalFailure>();
330 }
331
332 // Hash the data file and update the verification context
333 auto size = fs::file_size(file);
334 auto dataPtr = mapFile(file, size);
335
336 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
337 if (result <= 0)
338 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500339 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600340 entry("ERRCODE=%lu", ERR_get_error()));
341 elog<InternalFailure>();
342 }
343
344 // Verify the data with signature.
345 size = fs::file_size(sigFile);
346 auto signature = mapFile(sigFile, size);
347
348 result = EVP_DigestVerifyFinal(
349 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
350 size);
351
352 // Check the verification result.
353 if (result < 0)
354 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500355 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600356 entry("ERRCODE=%lu", ERR_get_error()));
357 elog<InternalFailure>();
358 }
359
360 if (result == 0)
361 {
362 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
363 entry("PATH=%s", sigFile.c_str()));
364 return false;
365 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600366 return true;
367}
368
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600369inline RSA* Signature::createPublicRSA(const fs::path& publicKey)
370{
371 RSA* rsa = nullptr;
372 auto size = fs::file_size(publicKey);
373
374 // Read public key file
375 auto data = mapFile(publicKey, size);
376
377 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
378 if (keyBio.get() == nullptr)
379 {
380 log<level::ERR>("Failed to create new BIO Memory buffer");
381 elog<InternalFailure>();
382 }
383
384 rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr);
385
386 return rsa;
387}
388
389CustomMap Signature::mapFile(const fs::path& path, size_t size)
390{
391
392 CustomFd fd(open(path.c_str(), O_RDONLY));
393
394 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
395 size);
396}
397
Henry Tian574f94b2021-01-06 10:33:59 +0800398bool Signature::checkAndVerifyImage(const std::string& filePath,
399 const std::string& publicKeyPath,
Lei YU7ab55e22021-05-19 13:26:53 +0800400 const std::vector<std::string>& imageList,
401 bool& fileFound)
Henry Tian574f94b2021-01-06 10:33:59 +0800402{
403 bool valid = true;
404
Lei YU7ab55e22021-05-19 13:26:53 +0800405 fileFound = false;
Henry Tian574f94b2021-01-06 10:33:59 +0800406 for (auto& bmcImage : imageList)
407 {
408 fs::path file(filePath);
409 file /= bmcImage;
410
411 if (!fs::exists(file))
412 {
413 valid = false;
414 break;
415 }
Lei YU7ab55e22021-05-19 13:26:53 +0800416 fileFound = true;
Henry Tian574f94b2021-01-06 10:33:59 +0800417
418 fs::path sigFile(file);
Lei YU92c7e9e2021-05-20 16:32:28 +0800419 sigFile += SIGNATURE_FILE_EXT;
Henry Tian574f94b2021-01-06 10:33:59 +0800420
421 // Verify the signature.
422 auto valid = verifyFile(file, sigFile, publicKeyPath, hashType);
423 if (valid == false)
424 {
425 log<level::ERR>("Image file Signature Validation failed",
426 entry("IMAGE=%s", bmcImage.c_str()));
427 return false;
428 }
429 }
430
431 return valid;
432}
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600433} // namespace image
434} // namespace software
435} // namespace phosphor