blob: cf955d7f2cc452427d092d06487ca492d7ce150a [file] [log] [blame]
Gunnar Millsf6ed5892018-09-07 17:08:02 -05001#include "config.h"
Jayanth Othayoth70804dc2018-03-20 06:31:59 -05002
3#include "image_verify.hpp"
Gunnar Millsf6ed5892018-09-07 17:08:02 -05004
Jayanth Othayoth70804dc2018-03-20 06:31:59 -05005#include "version.hpp"
6
Gunnar Millsf6ed5892018-09-07 17:08:02 -05007#include <fcntl.h>
8#include <openssl/err.h>
9#include <sys/stat.h>
10
Jayanth Othayoth70804dc2018-03-20 06:31:59 -050011#include <phosphor-logging/elog-errors.hpp>
Gunnar Millsf6ed5892018-09-07 17:08:02 -050012#include <phosphor-logging/elog.hpp>
13#include <phosphor-logging/log.hpp>
Jayanth Othayoth70804dc2018-03-20 06:31:59 -050014#include <xyz/openbmc_project/Common/error.hpp>
15
Brad Bishop8facccf2020-11-04 09:44:58 -050016#include <fstream>
17#include <set>
18
Jayanth Othayoth70804dc2018-03-20 06:31:59 -050019namespace openpower
20{
21namespace software
22{
23namespace image
24{
25
26using namespace phosphor::logging;
27using namespace openpower::software::updater;
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,
Lei YU2b2d2292019-03-18 15:22:56 +080035 const std::string& pnorFileName,
Jayanth Othayoth70804dc2018-03-20 06:31:59 -050036 const fs::path& signedConfPath) :
37 imageDirPath(imageDirPath),
Lei YU2b2d2292019-03-18 15:22:56 +080038 pnorFileName(pnorFileName), signedConfPath(signedConfPath)
Jayanth Othayoth70804dc2018-03-20 06:31:59 -050039{
40 fs::path file(imageDirPath / MANIFEST_FILE);
41
42 auto keyValues =
43 Version::getValue(file, {{keyTypeTag, " "}, {hashFunctionTag, " "}});
44 keyType = keyValues.at(keyTypeTag);
45 hashType = keyValues.at(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/OpenPOWER/publickey
62 // /etc/activationdata/OpenPOWER/hashfunc
63 // /etc/activationdata/GA/publickey
64 // /etc/activationdata/GA/hashfunc
65 // Set will have OpenPOWER, 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/GA/ -> get GA 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
90bool Signature::verify()
91{
92 try
93 {
94 // Verify the MANIFEST and publickey file using available
95 // public keys and hash on the system.
96 if (false == systemLevelVerify())
97 {
98 log<level::ERR>("System level Signature Validation failed");
99 return false;
100 }
101
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500102 // image specific publickey file name.
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500103 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
104
105 // Validate the PNOR image file.
106 // Build Image File name
107 fs::path file(imageDirPath);
Lei YU2b2d2292019-03-18 15:22:56 +0800108 file /= pnorFileName;
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500109
110 // Build Signature File name
111 std::string fileName = file.filename();
112 fs::path sigFile(imageDirPath);
113 sigFile /= fileName + SIGNATURE_FILE_EXT;
114
115 // Verify the signature.
116 auto valid = verifyFile(file, sigFile, publicKeyFile, hashType);
117 if (valid == false)
118 {
119 log<level::ERR>("Image file Signature Validation failed",
Lei YU2b2d2292019-03-18 15:22:56 +0800120 entry("IMAGE=%s", pnorFileName.c_str()));
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500121 return false;
122 }
123
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500124 log<level::DEBUG>("Successfully completed Signature vaildation.");
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500125
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);
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.
163 // For any internal failure during the key/hash pair specific
164 // validation, should continue the validation with next
165 // available Key/hash pair.
166 for (const auto& keyType : keyTypes)
167 {
168 auto keyHashPair = getKeyHashFileNames(keyType);
169 auto keyValues =
170 Version::getValue(keyHashPair.first, {{hashFunctionTag, " "}});
171 auto hashFunc = keyValues.at(hashFunctionTag);
172
173 try
174 {
175 // Verify manifest file signature
176 valid = verifyFile(manifestFile, manifestFileSig,
177 keyHashPair.second, hashFunc);
178 if (valid)
179 {
180 // Verify publickey file signature.
181 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
182 hashFunc);
183 if (valid)
184 {
185 break;
186 }
187 }
188 }
189 catch (const InternalFailure& e)
190 {
191 valid = false;
192 }
193 }
194 return valid;
195}
196
197bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
198 const fs::path& publicKey,
199 const std::string& hashFunc)
200{
201
202 // Check existence of the files in the system.
203 if (!(fs::exists(file) && fs::exists(sigFile)))
204 {
205 log<level::ERR>("Failed to find the Data or signature file.",
206 entry("FILE=%s", file.c_str()));
207 elog<InternalFailure>();
208 }
209
210 // Create RSA.
211 auto publicRSA = createPublicRSA(publicKey);
212 if (publicRSA == nullptr)
213 {
214 log<level::ERR>("Failed to create RSA",
215 entry("FILE=%s", publicKey.c_str()));
216 elog<InternalFailure>();
217 }
218
219 // Assign key to RSA.
220 EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free);
221 EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA);
222
223 // Initializes a digest context.
Adriana Kobylak70ca2422018-09-06 14:23:38 -0500224 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500225
226 // Adds all digest algorithms to the internal table
227 OpenSSL_add_all_digests();
228
Gunnar Millsa9cfced2018-04-08 15:01:57 -0500229 // Create Hash structure.
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500230 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
231 if (!hashStruct)
232 {
233 log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
234 entry("HASH=%s", hashFunc.c_str()));
235 elog<InternalFailure>();
236 }
237
238 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
239 nullptr, pKeyPtr.get());
240
241 if (result <= 0)
242 {
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500243 log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500244 entry("ERRCODE=%lu", ERR_get_error()));
245 elog<InternalFailure>();
246 }
247
248 // Hash the data file and update the verification context
249 auto size = fs::file_size(file);
250 auto dataPtr = mapFile(file, size);
251
252 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
253 if (result <= 0)
254 {
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500255 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500256 entry("ERRCODE=%lu", ERR_get_error()));
257 elog<InternalFailure>();
258 }
259
260 // Verify the data with signature.
261 size = fs::file_size(sigFile);
262 auto signature = mapFile(sigFile, size);
263
264 result = EVP_DigestVerifyFinal(
265 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
266 size);
267
268 // Check the verification result.
269 if (result < 0)
270 {
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500271 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500272 entry("ERRCODE=%lu", ERR_get_error()));
273 elog<InternalFailure>();
274 }
275
276 if (result == 0)
277 {
278 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
279 entry("PATH=%s", sigFile.c_str()));
280 return false;
281 }
282 return true;
283}
284
285inline RSA* Signature::createPublicRSA(const fs::path& publicKey)
286{
287 RSA* rsa = nullptr;
288 auto size = fs::file_size(publicKey);
289
290 // Read public key file
291 auto data = mapFile(publicKey, size);
292
293 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
294 if (keyBio.get() == nullptr)
295 {
296 log<level::ERR>("Failed to create new BIO Memory buffer");
297 elog<InternalFailure>();
298 }
299
300 rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr);
301
302 return rsa;
303}
304
305CustomMap Signature::mapFile(const fs::path& path, size_t size)
306{
307
308 CustomFd fd(open(path.c_str(), O_RDONLY));
309
310 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
311 size);
312}
313
314} // namespace image
315} // namespace software
316} // namespace openpower