blob: b92956aee86fd679d0e5b491e65d6693c39c800e [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
Adriana Kobylak58aa7502020-06-08 11:12:11 -050018#include <fstream>
19#include <set>
20
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060021namespace phosphor
22{
23namespace software
24{
25namespace image
26{
27
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060028using namespace phosphor::logging;
29using namespace phosphor::software::manager;
30using InternalFailure =
31 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
32
33constexpr auto keyTypeTag = "KeyType";
34constexpr auto hashFunctionTag = "HashType";
35
36Signature::Signature(const fs::path& imageDirPath,
37 const fs::path& signedConfPath) :
38 imageDirPath(imageDirPath),
39 signedConfPath(signedConfPath)
40{
41 fs::path file(imageDirPath / MANIFEST_FILE_NAME);
42
43 keyType = Version::getValue(file, keyTypeTag);
44 hashType = Version::getValue(file, hashFunctionTag);
45}
46
47AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
48{
49 AvailableKeyTypes keyTypes{};
50
51 // Find the path of all the files
52 if (!fs::is_directory(signedConfPath))
53 {
54 log<level::ERR>("Signed configuration path not found in the system");
55 elog<InternalFailure>();
56 }
57
58 // Look for all the hash and public key file names get the key value
59 // For example:
60 // /etc/activationdata/OpenBMC/publickey
61 // /etc/activationdata/OpenBMC/hashfunc
62 // /etc/activationdata/GA/publickey
63 // /etc/activationdata/GA/hashfunc
64 // Set will have OpenBMC, GA
65
66 for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
67 {
68 if ((p.path().filename() == HASH_FILE_NAME) ||
69 (p.path().filename() == PUBLICKEY_FILE_NAME))
70 {
71 // extract the key types
72 // /etc/activationdata/OpenBMC/ -> get OpenBMC from the path
73 auto key = p.path().parent_path();
74 keyTypes.insert(key.filename());
75 }
76 }
77
78 return keyTypes;
79}
80
81inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
82{
83 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
84 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
85
86 return std::make_pair(std::move(hashpath), std::move(keyPath));
87}
88
George Liu0a06e972020-12-17 09:17:04 +080089bool Signature::verifyFullImage()
90{
91 bool ret = true;
92#ifdef WANT_SIGNATURE_FULL_VERIFY
93 std::vector<std::string> fullImages = {
94 fs::path(imageDirPath) / "image-bmc.sig",
95 fs::path(imageDirPath) / "image-hostfw.sig",
96 fs::path(imageDirPath) / "image-kernel.sig",
97 fs::path(imageDirPath) / "image-rofs.sig",
98 fs::path(imageDirPath) / "image-rwfs.sig",
99 fs::path(imageDirPath) / "image-u-boot.sig",
100 fs::path(imageDirPath) / "MANIFEST.sig",
101 fs::path(imageDirPath) / "publickey.sig"};
102
103 // Merge files
104 std::string tmpFullFile = "/tmp/image-full";
105 utils::mergeFiles(fullImages, tmpFullFile);
106
107 // Validate the full image files
108 fs::path pkeyFullFile(tmpFullFile);
109
110 std::string imageFullSig = "image-full.sig";
111 fs::path pkeyFullFileSig(imageDirPath / imageFullSig);
112 pkeyFullFileSig.replace_extension(SIGNATURE_FILE_EXT);
113
114 // image specific publickey file name.
115 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
116
117 ret = verifyFile(pkeyFullFile, pkeyFullFileSig, publicKeyFile, hashType);
118 fs::remove(tmpFullFile);
119#endif
120
121 return ret;
122}
123
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600124bool Signature::verify()
125{
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600126 try
127 {
Henry Tian574f94b2021-01-06 10:33:59 +0800128 bool valid;
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600129 // Verify the MANIFEST and publickey file using available
130 // public keys and hash on the system.
131 if (false == systemLevelVerify())
132 {
133 log<level::ERR>("System level Signature Validation failed");
134 return false;
135 }
136
Gunnar Millse11a2022018-03-23 12:04:48 -0500137 // image specific publickey file name.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600138 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
139
Henry Tian574f94b2021-01-06 10:33:59 +0800140 // Record the images which are being updated
141 // First check and Validate for the fullimage, then check and Validate
142 // for images with partitions
143 std::vector<std::string> imageUpdateList = {bmcFullImages};
144 valid =
145 checkAndVerifyImage(imageDirPath, publicKeyFile, imageUpdateList);
146 if (!valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600147 {
Henry Tian574f94b2021-01-06 10:33:59 +0800148 imageUpdateList.clear();
149 imageUpdateList.assign(bmcImages.begin(), bmcImages.end());
150 valid = checkAndVerifyImage(imageDirPath, publicKeyFile,
151 imageUpdateList);
152 if (!valid)
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600153 {
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600154 return false;
155 }
156 }
Henry Tian574f94b2021-01-06 10:33:59 +0800157
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500158 // Validate the optional image files.
159 auto optionalImages = getOptionalImages();
160 for (const auto& optionalImage : optionalImages)
161 {
162 // Build Image File name
163 fs::path file(imageDirPath);
164 file /= optionalImage;
165
166 if (fs::exists(file))
167 {
168 // Build Signature File name
169 fs::path sigFile(file);
170 sigFile.replace_extension(SIGNATURE_FILE_EXT);
171
172 // Verify the signature.
Henry Tian574f94b2021-01-06 10:33:59 +0800173 valid = verifyFile(file, sigFile, publicKeyFile, hashType);
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500174 if (valid == false)
175 {
176 log<level::ERR>("Image file Signature Validation failed",
177 entry("IMAGE=%s", optionalImage.c_str()));
178 return false;
179 }
180 }
181 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600182
George Liu0a06e972020-12-17 09:17:04 +0800183 if (verifyFullImage() == false)
184 {
185 log<level::ERR>("Image full file Signature Validation failed");
186 return false;
187 }
188
Gunnar Millse11a2022018-03-23 12:04:48 -0500189 log<level::DEBUG>("Successfully completed Signature vaildation.");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600190
191 return true;
192 }
193 catch (const InternalFailure& e)
194 {
195 return false;
196 }
197 catch (const std::exception& e)
198 {
199 log<level::ERR>(e.what());
200 return false;
201 }
202}
203
204bool Signature::systemLevelVerify()
205{
206 // Get available key types from the system.
207 auto keyTypes = getAvailableKeyTypesFromSystem();
208 if (keyTypes.empty())
209 {
210 log<level::ERR>("Missing Signature configuration data in system");
211 elog<InternalFailure>();
212 }
213
214 // Build publickey and its signature file name.
215 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
216 fs::path pkeyFileSig(pkeyFile);
217 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
218
219 // Build manifest and its signature file name.
220 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
221 fs::path manifestFileSig(manifestFile);
222 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
223
224 auto valid = false;
225
226 // Verify the file signature with available key types
227 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600228 // For any internal failure during the key/hash pair specific
229 // validation, should continue the validation with next
230 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600231 for (const auto& keyType : keyTypes)
232 {
233 auto keyHashPair = getKeyHashFileNames(keyType);
234
235 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
236
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600237 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600238 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600239 // Verify manifest file signature
240 valid = verifyFile(manifestFile, manifestFileSig,
241 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600242 if (valid)
243 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600244 // Verify publickey file signature.
245 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
246 hashFunc);
247 if (valid)
248 {
249 break;
250 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600251 }
252 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600253 catch (const InternalFailure& e)
254 {
255 valid = false;
256 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600257 }
258 return valid;
259}
260
261bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
262 const fs::path& publicKey,
263 const std::string& hashFunc)
264{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600265
266 // Check existence of the files in the system.
267 if (!(fs::exists(file) && fs::exists(sigFile)))
268 {
269 log<level::ERR>("Failed to find the Data or signature file.",
270 entry("FILE=%s", file.c_str()));
271 elog<InternalFailure>();
272 }
273
274 // Create RSA.
275 auto publicRSA = createPublicRSA(publicKey);
276 if (publicRSA == nullptr)
277 {
278 log<level::ERR>("Failed to create RSA",
279 entry("FILE=%s", publicKey.c_str()));
280 elog<InternalFailure>();
281 }
282
283 // Assign key to RSA.
284 EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free);
285 EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA);
286
287 // Initializes a digest context.
Adriana Kobylak5ed9b2d2018-09-06 13:15:34 -0500288 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600289
290 // Adds all digest algorithms to the internal table
291 OpenSSL_add_all_digests();
292
Gunnar Mills2bcba022018-04-08 15:02:04 -0500293 // Create Hash structure.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600294 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
295 if (!hashStruct)
296 {
297 log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
298 entry("HASH=%s", hashFunc.c_str()));
299 elog<InternalFailure>();
300 }
301
302 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
303 nullptr, pKeyPtr.get());
304
305 if (result <= 0)
306 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500307 log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600308 entry("ERRCODE=%lu", ERR_get_error()));
309 elog<InternalFailure>();
310 }
311
312 // Hash the data file and update the verification context
313 auto size = fs::file_size(file);
314 auto dataPtr = mapFile(file, size);
315
316 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
317 if (result <= 0)
318 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500319 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600320 entry("ERRCODE=%lu", ERR_get_error()));
321 elog<InternalFailure>();
322 }
323
324 // Verify the data with signature.
325 size = fs::file_size(sigFile);
326 auto signature = mapFile(sigFile, size);
327
328 result = EVP_DigestVerifyFinal(
329 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
330 size);
331
332 // Check the verification result.
333 if (result < 0)
334 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500335 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600336 entry("ERRCODE=%lu", ERR_get_error()));
337 elog<InternalFailure>();
338 }
339
340 if (result == 0)
341 {
342 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
343 entry("PATH=%s", sigFile.c_str()));
344 return false;
345 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600346 return true;
347}
348
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600349inline RSA* Signature::createPublicRSA(const fs::path& publicKey)
350{
351 RSA* rsa = nullptr;
352 auto size = fs::file_size(publicKey);
353
354 // Read public key file
355 auto data = mapFile(publicKey, size);
356
357 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
358 if (keyBio.get() == nullptr)
359 {
360 log<level::ERR>("Failed to create new BIO Memory buffer");
361 elog<InternalFailure>();
362 }
363
364 rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr);
365
366 return rsa;
367}
368
369CustomMap Signature::mapFile(const fs::path& path, size_t size)
370{
371
372 CustomFd fd(open(path.c_str(), O_RDONLY));
373
374 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
375 size);
376}
377
Henry Tian574f94b2021-01-06 10:33:59 +0800378bool Signature::checkAndVerifyImage(const std::string& filePath,
379 const std::string& publicKeyPath,
380 const std::vector<std::string>& imageList)
381{
382 bool valid = true;
383
384 for (auto& bmcImage : imageList)
385 {
386 fs::path file(filePath);
387 file /= bmcImage;
388
389 if (!fs::exists(file))
390 {
391 valid = false;
392 break;
393 }
394
395 fs::path sigFile(file);
396 sigFile.replace_extension(SIGNATURE_FILE_EXT);
397
398 // Verify the signature.
399 auto valid = verifyFile(file, sigFile, publicKeyPath, hashType);
400 if (valid == false)
401 {
402 log<level::ERR>("Image file Signature Validation failed",
403 entry("IMAGE=%s", bmcImage.c_str()));
404 return false;
405 }
406 }
407
408 return valid;
409}
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600410} // namespace image
411} // namespace software
412} // namespace phosphor