blob: d7b676d0b43ba99409f5dce2e5b2ab95f0e85571 [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"
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -06006#include "version.hpp"
7
Gunnar Millsb0ce9962018-09-07 13:39:10 -05008#include <fcntl.h>
9#include <openssl/err.h>
10#include <sys/stat.h>
11
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060012#include <phosphor-logging/elog-errors.hpp>
Gunnar Millsb0ce9962018-09-07 13:39:10 -050013#include <phosphor-logging/elog.hpp>
14#include <phosphor-logging/log.hpp>
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060015#include <xyz/openbmc_project/Common/error.hpp>
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060016
Adriana Kobylak58aa7502020-06-08 11:12:11 -050017#include <fstream>
18#include <set>
19
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060020namespace phosphor
21{
22namespace software
23{
24namespace image
25{
26
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060027using namespace phosphor::logging;
28using namespace phosphor::software::manager;
29using InternalFailure =
30 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
31
32constexpr auto keyTypeTag = "KeyType";
33constexpr auto hashFunctionTag = "HashType";
34
35Signature::Signature(const fs::path& imageDirPath,
36 const fs::path& signedConfPath) :
37 imageDirPath(imageDirPath),
38 signedConfPath(signedConfPath)
39{
40 fs::path file(imageDirPath / MANIFEST_FILE_NAME);
41
42 keyType = Version::getValue(file, keyTypeTag);
43 hashType = Version::getValue(file, hashFunctionTag);
44}
45
46AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
47{
48 AvailableKeyTypes keyTypes{};
49
50 // Find the path of all the files
51 if (!fs::is_directory(signedConfPath))
52 {
53 log<level::ERR>("Signed configuration path not found in the system");
54 elog<InternalFailure>();
55 }
56
57 // Look for all the hash and public key file names get the key value
58 // For example:
59 // /etc/activationdata/OpenBMC/publickey
60 // /etc/activationdata/OpenBMC/hashfunc
61 // /etc/activationdata/GA/publickey
62 // /etc/activationdata/GA/hashfunc
63 // Set will have OpenBMC, GA
64
65 for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
66 {
67 if ((p.path().filename() == HASH_FILE_NAME) ||
68 (p.path().filename() == PUBLICKEY_FILE_NAME))
69 {
70 // extract the key types
71 // /etc/activationdata/OpenBMC/ -> get OpenBMC from the path
72 auto key = p.path().parent_path();
73 keyTypes.insert(key.filename());
74 }
75 }
76
77 return keyTypes;
78}
79
80inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
81{
82 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
83 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
84
85 return std::make_pair(std::move(hashpath), std::move(keyPath));
86}
87
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060088bool Signature::verify()
89{
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060090 try
91 {
92 // Verify the MANIFEST and publickey file using available
93 // public keys and hash on the system.
94 if (false == systemLevelVerify())
95 {
96 log<level::ERR>("System level Signature Validation failed");
97 return false;
98 }
99
Gunnar Millse11a2022018-03-23 12:04:48 -0500100 // image specific publickey file name.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600101 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
102
103 // Validate the BMC image files.
104 for (const auto& bmcImage : bmcImages)
105 {
106 // Build Image File name
107 fs::path file(imageDirPath);
108 file /= bmcImage;
109
110 // Build Signature File name
111 fs::path sigFile(file);
112 sigFile.replace_extension(SIGNATURE_FILE_EXT);
113
114 // Verify the signature.
115 auto valid = verifyFile(file, sigFile, publicKeyFile, hashType);
116 if (valid == false)
117 {
118 log<level::ERR>("Image file Signature Validation failed",
119 entry("IMAGE=%s", bmcImage.c_str()));
120 return false;
121 }
122 }
Adriana Kobylak73609bb2020-06-18 15:05:40 -0500123 // Validate the optional image files.
124 auto optionalImages = getOptionalImages();
125 for (const auto& optionalImage : optionalImages)
126 {
127 // Build Image File name
128 fs::path file(imageDirPath);
129 file /= optionalImage;
130
131 if (fs::exists(file))
132 {
133 // Build Signature File name
134 fs::path sigFile(file);
135 sigFile.replace_extension(SIGNATURE_FILE_EXT);
136
137 // Verify the signature.
138 auto valid = verifyFile(file, sigFile, publicKeyFile, hashType);
139 if (valid == false)
140 {
141 log<level::ERR>("Image file Signature Validation failed",
142 entry("IMAGE=%s", optionalImage.c_str()));
143 return false;
144 }
145 }
146 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600147
Gunnar Millse11a2022018-03-23 12:04:48 -0500148 log<level::DEBUG>("Successfully completed Signature vaildation.");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600149
150 return true;
151 }
152 catch (const InternalFailure& e)
153 {
154 return false;
155 }
156 catch (const std::exception& e)
157 {
158 log<level::ERR>(e.what());
159 return false;
160 }
161}
162
163bool Signature::systemLevelVerify()
164{
165 // Get available key types from the system.
166 auto keyTypes = getAvailableKeyTypesFromSystem();
167 if (keyTypes.empty())
168 {
169 log<level::ERR>("Missing Signature configuration data in system");
170 elog<InternalFailure>();
171 }
172
173 // Build publickey and its signature file name.
174 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
175 fs::path pkeyFileSig(pkeyFile);
176 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
177
178 // Build manifest and its signature file name.
179 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
180 fs::path manifestFileSig(manifestFile);
181 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
182
183 auto valid = false;
184
185 // Verify the file signature with available key types
186 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600187 // For any internal failure during the key/hash pair specific
188 // validation, should continue the validation with next
189 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600190 for (const auto& keyType : keyTypes)
191 {
192 auto keyHashPair = getKeyHashFileNames(keyType);
193
194 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
195
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600196 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600197 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600198 // Verify manifest file signature
199 valid = verifyFile(manifestFile, manifestFileSig,
200 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600201 if (valid)
202 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600203 // Verify publickey file signature.
204 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
205 hashFunc);
206 if (valid)
207 {
208 break;
209 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600210 }
211 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600212 catch (const InternalFailure& e)
213 {
214 valid = false;
215 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600216 }
217 return valid;
218}
219
220bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
221 const fs::path& publicKey,
222 const std::string& hashFunc)
223{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600224
225 // Check existence of the files in the system.
226 if (!(fs::exists(file) && fs::exists(sigFile)))
227 {
228 log<level::ERR>("Failed to find the Data or signature file.",
229 entry("FILE=%s", file.c_str()));
230 elog<InternalFailure>();
231 }
232
233 // Create RSA.
234 auto publicRSA = createPublicRSA(publicKey);
235 if (publicRSA == nullptr)
236 {
237 log<level::ERR>("Failed to create RSA",
238 entry("FILE=%s", publicKey.c_str()));
239 elog<InternalFailure>();
240 }
241
242 // Assign key to RSA.
243 EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free);
244 EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA);
245
246 // Initializes a digest context.
Adriana Kobylak5ed9b2d2018-09-06 13:15:34 -0500247 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600248
249 // Adds all digest algorithms to the internal table
250 OpenSSL_add_all_digests();
251
Gunnar Mills2bcba022018-04-08 15:02:04 -0500252 // Create Hash structure.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600253 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
254 if (!hashStruct)
255 {
256 log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
257 entry("HASH=%s", hashFunc.c_str()));
258 elog<InternalFailure>();
259 }
260
261 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
262 nullptr, pKeyPtr.get());
263
264 if (result <= 0)
265 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500266 log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600267 entry("ERRCODE=%lu", ERR_get_error()));
268 elog<InternalFailure>();
269 }
270
271 // Hash the data file and update the verification context
272 auto size = fs::file_size(file);
273 auto dataPtr = mapFile(file, size);
274
275 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
276 if (result <= 0)
277 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500278 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600279 entry("ERRCODE=%lu", ERR_get_error()));
280 elog<InternalFailure>();
281 }
282
283 // Verify the data with signature.
284 size = fs::file_size(sigFile);
285 auto signature = mapFile(sigFile, size);
286
287 result = EVP_DigestVerifyFinal(
288 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
289 size);
290
291 // Check the verification result.
292 if (result < 0)
293 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500294 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600295 entry("ERRCODE=%lu", ERR_get_error()));
296 elog<InternalFailure>();
297 }
298
299 if (result == 0)
300 {
301 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
302 entry("PATH=%s", sigFile.c_str()));
303 return false;
304 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600305 return true;
306}
307
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600308inline RSA* Signature::createPublicRSA(const fs::path& publicKey)
309{
310 RSA* rsa = nullptr;
311 auto size = fs::file_size(publicKey);
312
313 // Read public key file
314 auto data = mapFile(publicKey, size);
315
316 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
317 if (keyBio.get() == nullptr)
318 {
319 log<level::ERR>("Failed to create new BIO Memory buffer");
320 elog<InternalFailure>();
321 }
322
323 rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr);
324
325 return rsa;
326}
327
328CustomMap Signature::mapFile(const fs::path& path, size_t size)
329{
330
331 CustomFd fd(open(path.c_str(), O_RDONLY));
332
333 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
334 size);
335}
336
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600337} // namespace image
338} // namespace software
339} // namespace phosphor