blob: 7f0bca788b26977c5c0b26d3d5f1291c9b25fedf [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
12#include <fstream>
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>
16#include <set>
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060017#include <xyz/openbmc_project/Common/error.hpp>
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060018
19namespace phosphor
20{
21namespace software
22{
23namespace image
24{
25
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060026using namespace phosphor::logging;
27using namespace phosphor::software::manager;
28using InternalFailure =
29 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
30
31constexpr auto keyTypeTag = "KeyType";
32constexpr auto hashFunctionTag = "HashType";
33
34Signature::Signature(const fs::path& imageDirPath,
35 const fs::path& signedConfPath) :
36 imageDirPath(imageDirPath),
37 signedConfPath(signedConfPath)
38{
39 fs::path file(imageDirPath / MANIFEST_FILE_NAME);
40
41 keyType = Version::getValue(file, keyTypeTag);
42 hashType = Version::getValue(file, hashFunctionTag);
43}
44
45AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
46{
47 AvailableKeyTypes keyTypes{};
48
49 // Find the path of all the files
50 if (!fs::is_directory(signedConfPath))
51 {
52 log<level::ERR>("Signed configuration path not found in the system");
53 elog<InternalFailure>();
54 }
55
56 // Look for all the hash and public key file names get the key value
57 // For example:
58 // /etc/activationdata/OpenBMC/publickey
59 // /etc/activationdata/OpenBMC/hashfunc
60 // /etc/activationdata/GA/publickey
61 // /etc/activationdata/GA/hashfunc
62 // Set will have OpenBMC, GA
63
64 for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
65 {
66 if ((p.path().filename() == HASH_FILE_NAME) ||
67 (p.path().filename() == PUBLICKEY_FILE_NAME))
68 {
69 // extract the key types
70 // /etc/activationdata/OpenBMC/ -> get OpenBMC from the path
71 auto key = p.path().parent_path();
72 keyTypes.insert(key.filename());
73 }
74 }
75
76 return keyTypes;
77}
78
79inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
80{
81 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
82 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
83
84 return std::make_pair(std::move(hashpath), std::move(keyPath));
85}
86
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060087bool Signature::verify()
88{
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060089 try
90 {
91 // Verify the MANIFEST and publickey file using available
92 // public keys and hash on the system.
93 if (false == systemLevelVerify())
94 {
95 log<level::ERR>("System level Signature Validation failed");
96 return false;
97 }
98
Gunnar Millse11a2022018-03-23 12:04:48 -050099 // image specific publickey file name.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600100 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
101
102 // Validate the BMC image files.
103 for (const auto& bmcImage : bmcImages)
104 {
105 // Build Image File name
106 fs::path file(imageDirPath);
107 file /= bmcImage;
108
109 // Build Signature File name
110 fs::path sigFile(file);
111 sigFile.replace_extension(SIGNATURE_FILE_EXT);
112
113 // Verify the signature.
114 auto valid = verifyFile(file, sigFile, publicKeyFile, hashType);
115 if (valid == false)
116 {
117 log<level::ERR>("Image file Signature Validation failed",
118 entry("IMAGE=%s", bmcImage.c_str()));
119 return false;
120 }
121 }
122
Gunnar Millse11a2022018-03-23 12:04:48 -0500123 log<level::DEBUG>("Successfully completed Signature vaildation.");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600124
125 return true;
126 }
127 catch (const InternalFailure& e)
128 {
129 return false;
130 }
131 catch (const std::exception& e)
132 {
133 log<level::ERR>(e.what());
134 return false;
135 }
136}
137
138bool Signature::systemLevelVerify()
139{
140 // Get available key types from the system.
141 auto keyTypes = getAvailableKeyTypesFromSystem();
142 if (keyTypes.empty())
143 {
144 log<level::ERR>("Missing Signature configuration data in system");
145 elog<InternalFailure>();
146 }
147
148 // Build publickey and its signature file name.
149 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
150 fs::path pkeyFileSig(pkeyFile);
151 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
152
153 // Build manifest and its signature file name.
154 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
155 fs::path manifestFileSig(manifestFile);
156 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
157
158 auto valid = false;
159
160 // Verify the file signature with available key types
161 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600162 // For any internal failure during the key/hash pair specific
163 // validation, should continue the validation with next
164 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600165 for (const auto& keyType : keyTypes)
166 {
167 auto keyHashPair = getKeyHashFileNames(keyType);
168
169 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
170
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600171 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600172 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600173 // Verify manifest file signature
174 valid = verifyFile(manifestFile, manifestFileSig,
175 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600176 if (valid)
177 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600178 // Verify publickey file signature.
179 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
180 hashFunc);
181 if (valid)
182 {
183 break;
184 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600185 }
186 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600187 catch (const InternalFailure& e)
188 {
189 valid = false;
190 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600191 }
192 return valid;
193}
194
195bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
196 const fs::path& publicKey,
197 const std::string& hashFunc)
198{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600199
200 // Check existence of the files in the system.
201 if (!(fs::exists(file) && fs::exists(sigFile)))
202 {
203 log<level::ERR>("Failed to find the Data or signature file.",
204 entry("FILE=%s", file.c_str()));
205 elog<InternalFailure>();
206 }
207
208 // Create RSA.
209 auto publicRSA = createPublicRSA(publicKey);
210 if (publicRSA == nullptr)
211 {
212 log<level::ERR>("Failed to create RSA",
213 entry("FILE=%s", publicKey.c_str()));
214 elog<InternalFailure>();
215 }
216
217 // Assign key to RSA.
218 EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free);
219 EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA);
220
221 // Initializes a digest context.
Adriana Kobylak5ed9b2d2018-09-06 13:15:34 -0500222 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600223
224 // Adds all digest algorithms to the internal table
225 OpenSSL_add_all_digests();
226
Gunnar Mills2bcba022018-04-08 15:02:04 -0500227 // Create Hash structure.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600228 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
229 if (!hashStruct)
230 {
231 log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
232 entry("HASH=%s", hashFunc.c_str()));
233 elog<InternalFailure>();
234 }
235
236 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
237 nullptr, pKeyPtr.get());
238
239 if (result <= 0)
240 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500241 log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600242 entry("ERRCODE=%lu", ERR_get_error()));
243 elog<InternalFailure>();
244 }
245
246 // Hash the data file and update the verification context
247 auto size = fs::file_size(file);
248 auto dataPtr = mapFile(file, size);
249
250 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
251 if (result <= 0)
252 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500253 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600254 entry("ERRCODE=%lu", ERR_get_error()));
255 elog<InternalFailure>();
256 }
257
258 // Verify the data with signature.
259 size = fs::file_size(sigFile);
260 auto signature = mapFile(sigFile, size);
261
262 result = EVP_DigestVerifyFinal(
263 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
264 size);
265
266 // Check the verification result.
267 if (result < 0)
268 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500269 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600270 entry("ERRCODE=%lu", ERR_get_error()));
271 elog<InternalFailure>();
272 }
273
274 if (result == 0)
275 {
276 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
277 entry("PATH=%s", sigFile.c_str()));
278 return false;
279 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600280 return true;
281}
282
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600283inline RSA* Signature::createPublicRSA(const fs::path& publicKey)
284{
285 RSA* rsa = nullptr;
286 auto size = fs::file_size(publicKey);
287
288 // Read public key file
289 auto data = mapFile(publicKey, size);
290
291 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
292 if (keyBio.get() == nullptr)
293 {
294 log<level::ERR>("Failed to create new BIO Memory buffer");
295 elog<InternalFailure>();
296 }
297
298 rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr);
299
300 return rsa;
301}
302
303CustomMap Signature::mapFile(const fs::path& path, size_t size)
304{
305
306 CustomFd fd(open(path.c_str(), O_RDONLY));
307
308 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
309 size);
310}
311
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600312} // namespace image
313} // namespace software
314} // namespace phosphor