blob: 60576537ae594685eb324ce6be651a7efd6312b8 [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 }
123
Gunnar Millse11a2022018-03-23 12:04:48 -0500124 log<level::DEBUG>("Successfully completed Signature vaildation.");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600125
126 return true;
127 }
128 catch (const InternalFailure& e)
129 {
130 return false;
131 }
132 catch (const std::exception& e)
133 {
134 log<level::ERR>(e.what());
135 return false;
136 }
137}
138
139bool Signature::systemLevelVerify()
140{
141 // Get available key types from the system.
142 auto keyTypes = getAvailableKeyTypesFromSystem();
143 if (keyTypes.empty())
144 {
145 log<level::ERR>("Missing Signature configuration data in system");
146 elog<InternalFailure>();
147 }
148
149 // Build publickey and its signature file name.
150 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
151 fs::path pkeyFileSig(pkeyFile);
152 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
153
154 // Build manifest and its signature file name.
155 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
156 fs::path manifestFileSig(manifestFile);
157 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
158
159 auto valid = false;
160
161 // Verify the file signature with available key types
162 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600163 // For any internal failure during the key/hash pair specific
164 // validation, should continue the validation with next
165 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600166 for (const auto& keyType : keyTypes)
167 {
168 auto keyHashPair = getKeyHashFileNames(keyType);
169
170 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
171
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600172 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600173 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600174 // Verify manifest file signature
175 valid = verifyFile(manifestFile, manifestFileSig,
176 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600177 if (valid)
178 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600179 // Verify publickey file signature.
180 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
181 hashFunc);
182 if (valid)
183 {
184 break;
185 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600186 }
187 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600188 catch (const InternalFailure& e)
189 {
190 valid = false;
191 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600192 }
193 return valid;
194}
195
196bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
197 const fs::path& publicKey,
198 const std::string& hashFunc)
199{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600200
201 // Check existence of the files in the system.
202 if (!(fs::exists(file) && fs::exists(sigFile)))
203 {
204 log<level::ERR>("Failed to find the Data or signature file.",
205 entry("FILE=%s", file.c_str()));
206 elog<InternalFailure>();
207 }
208
209 // Create RSA.
210 auto publicRSA = createPublicRSA(publicKey);
211 if (publicRSA == nullptr)
212 {
213 log<level::ERR>("Failed to create RSA",
214 entry("FILE=%s", publicKey.c_str()));
215 elog<InternalFailure>();
216 }
217
218 // Assign key to RSA.
219 EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free);
220 EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA);
221
222 // Initializes a digest context.
Adriana Kobylak5ed9b2d2018-09-06 13:15:34 -0500223 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600224
225 // Adds all digest algorithms to the internal table
226 OpenSSL_add_all_digests();
227
Gunnar Mills2bcba022018-04-08 15:02:04 -0500228 // Create Hash structure.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600229 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
230 if (!hashStruct)
231 {
232 log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
233 entry("HASH=%s", hashFunc.c_str()));
234 elog<InternalFailure>();
235 }
236
237 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
238 nullptr, pKeyPtr.get());
239
240 if (result <= 0)
241 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500242 log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600243 entry("ERRCODE=%lu", ERR_get_error()));
244 elog<InternalFailure>();
245 }
246
247 // Hash the data file and update the verification context
248 auto size = fs::file_size(file);
249 auto dataPtr = mapFile(file, size);
250
251 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
252 if (result <= 0)
253 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500254 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600255 entry("ERRCODE=%lu", ERR_get_error()));
256 elog<InternalFailure>();
257 }
258
259 // Verify the data with signature.
260 size = fs::file_size(sigFile);
261 auto signature = mapFile(sigFile, size);
262
263 result = EVP_DigestVerifyFinal(
264 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
265 size);
266
267 // Check the verification result.
268 if (result < 0)
269 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500270 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600271 entry("ERRCODE=%lu", ERR_get_error()));
272 elog<InternalFailure>();
273 }
274
275 if (result == 0)
276 {
277 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
278 entry("PATH=%s", sigFile.c_str()));
279 return false;
280 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600281 return true;
282}
283
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600284inline RSA* Signature::createPublicRSA(const fs::path& publicKey)
285{
286 RSA* rsa = nullptr;
287 auto size = fs::file_size(publicKey);
288
289 // Read public key file
290 auto data = mapFile(publicKey, size);
291
292 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
293 if (keyBio.get() == nullptr)
294 {
295 log<level::ERR>("Failed to create new BIO Memory buffer");
296 elog<InternalFailure>();
297 }
298
299 rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr);
300
301 return rsa;
302}
303
304CustomMap Signature::mapFile(const fs::path& path, size_t size)
305{
306
307 CustomFd fd(open(path.c_str(), O_RDONLY));
308
309 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
310 size);
311}
312
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600313} // namespace image
314} // namespace software
315} // namespace phosphor