blob: 71bcb5810e848908c4a7f73f9a32ec2281a8b7e8 [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
Brad Bishop9f44c992020-11-06 14:48:46 -050034Signature::Signature(const std::filesystem::path& imageDirPath,
Lei YU2b2d2292019-03-18 15:22:56 +080035 const std::string& pnorFileName,
Brad Bishop9f44c992020-11-06 14:48:46 -050036 const std::filesystem::path& signedConfPath) :
Jayanth Othayoth70804dc2018-03-20 06:31:59 -050037 imageDirPath(imageDirPath),
Lei YU2b2d2292019-03-18 15:22:56 +080038 pnorFileName(pnorFileName), signedConfPath(signedConfPath)
Jayanth Othayoth70804dc2018-03-20 06:31:59 -050039{
Brad Bishop9f44c992020-11-06 14:48:46 -050040 std::filesystem::path file(imageDirPath / MANIFEST_FILE);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -050041
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
Brad Bishop9f44c992020-11-06 14:48:46 -050053 if (!std::filesystem::is_directory(signedConfPath))
Jayanth Othayoth70804dc2018-03-20 06:31:59 -050054 {
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
Brad Bishop9f44c992020-11-06 14:48:46 -050067 for (const auto& p :
68 std::filesystem::recursive_directory_iterator(signedConfPath))
Jayanth Othayoth70804dc2018-03-20 06:31:59 -050069 {
70 if ((p.path().filename() == HASH_FILE_NAME) ||
71 (p.path().filename() == PUBLICKEY_FILE_NAME))
72 {
73 // extract the key types
74 // /etc/activationdata/GA/ -> get GA from the path
75 auto key = p.path().parent_path();
76 keyTypes.insert(key.filename());
77 }
78 }
79
80 return keyTypes;
81}
82
83inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
84{
Brad Bishop9f44c992020-11-06 14:48:46 -050085 std::filesystem::path hashpath(signedConfPath / key / HASH_FILE_NAME);
86 std::filesystem::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -050087
88 return std::make_pair(std::move(hashpath), std::move(keyPath));
89}
90
91bool Signature::verify()
92{
93 try
94 {
95 // Verify the MANIFEST and publickey file using available
96 // public keys and hash on the system.
97 if (false == systemLevelVerify())
98 {
99 log<level::ERR>("System level Signature Validation failed");
100 return false;
101 }
102
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500103 // image specific publickey file name.
Brad Bishop9f44c992020-11-06 14:48:46 -0500104 std::filesystem::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500105
106 // Validate the PNOR image file.
107 // Build Image File name
Brad Bishop9f44c992020-11-06 14:48:46 -0500108 std::filesystem::path file(imageDirPath);
Lei YU2b2d2292019-03-18 15:22:56 +0800109 file /= pnorFileName;
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500110
111 // Build Signature File name
112 std::string fileName = file.filename();
Brad Bishop9f44c992020-11-06 14:48:46 -0500113 std::filesystem::path sigFile(imageDirPath);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500114 sigFile /= fileName + SIGNATURE_FILE_EXT;
115
116 // Verify the signature.
117 auto valid = verifyFile(file, sigFile, publicKeyFile, hashType);
118 if (valid == false)
119 {
120 log<level::ERR>("Image file Signature Validation failed",
Lei YU2b2d2292019-03-18 15:22:56 +0800121 entry("IMAGE=%s", pnorFileName.c_str()));
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500122 return false;
123 }
124
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500125 log<level::DEBUG>("Successfully completed Signature vaildation.");
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500126
127 return true;
128 }
129 catch (const InternalFailure& e)
130 {
131 return false;
132 }
133 catch (const std::exception& e)
134 {
135 log<level::ERR>(e.what());
136 return false;
137 }
138}
139
140bool Signature::systemLevelVerify()
141{
142 // Get available key types from the system.
143 auto keyTypes = getAvailableKeyTypesFromSystem();
144 if (keyTypes.empty())
145 {
146 log<level::ERR>("Missing Signature configuration data in system");
147 elog<InternalFailure>();
148 }
149
150 // Build publickey and its signature file name.
Brad Bishop9f44c992020-11-06 14:48:46 -0500151 std::filesystem::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
152 std::filesystem::path pkeyFileSig(pkeyFile);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500153 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
154
155 // Build manifest and its signature file name.
Brad Bishop9f44c992020-11-06 14:48:46 -0500156 std::filesystem::path manifestFile(imageDirPath / MANIFEST_FILE);
157 std::filesystem::path manifestFileSig(manifestFile);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500158 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
159
160 auto valid = false;
161
162 // Verify the file signature with available key types
163 // public keys and hash function.
164 // For any internal failure during the key/hash pair specific
165 // validation, should continue the validation with next
166 // available Key/hash pair.
167 for (const auto& keyType : keyTypes)
168 {
169 auto keyHashPair = getKeyHashFileNames(keyType);
170 auto keyValues =
171 Version::getValue(keyHashPair.first, {{hashFunctionTag, " "}});
172 auto hashFunc = keyValues.at(hashFunctionTag);
173
174 try
175 {
176 // Verify manifest file signature
177 valid = verifyFile(manifestFile, manifestFileSig,
178 keyHashPair.second, hashFunc);
179 if (valid)
180 {
181 // Verify publickey file signature.
182 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
183 hashFunc);
184 if (valid)
185 {
186 break;
187 }
188 }
189 }
190 catch (const InternalFailure& e)
191 {
192 valid = false;
193 }
194 }
195 return valid;
196}
197
Brad Bishop9f44c992020-11-06 14:48:46 -0500198bool Signature::verifyFile(const std::filesystem::path& file,
199 const std::filesystem::path& sigFile,
200 const std::filesystem::path& publicKey,
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500201 const std::string& hashFunc)
202{
203
204 // Check existence of the files in the system.
Brad Bishop9f44c992020-11-06 14:48:46 -0500205 if (!(std::filesystem::exists(file) && std::filesystem::exists(sigFile)))
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500206 {
207 log<level::ERR>("Failed to find the Data or signature file.",
208 entry("FILE=%s", file.c_str()));
209 elog<InternalFailure>();
210 }
211
212 // Create RSA.
213 auto publicRSA = createPublicRSA(publicKey);
214 if (publicRSA == nullptr)
215 {
216 log<level::ERR>("Failed to create RSA",
217 entry("FILE=%s", publicKey.c_str()));
218 elog<InternalFailure>();
219 }
220
221 // Assign key to RSA.
222 EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free);
223 EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA);
224
225 // Initializes a digest context.
Adriana Kobylak70ca2422018-09-06 14:23:38 -0500226 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500227
228 // Adds all digest algorithms to the internal table
229 OpenSSL_add_all_digests();
230
Gunnar Millsa9cfced2018-04-08 15:01:57 -0500231 // Create Hash structure.
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500232 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
233 if (!hashStruct)
234 {
235 log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
236 entry("HASH=%s", hashFunc.c_str()));
237 elog<InternalFailure>();
238 }
239
240 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
241 nullptr, pKeyPtr.get());
242
243 if (result <= 0)
244 {
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500245 log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500246 entry("ERRCODE=%lu", ERR_get_error()));
247 elog<InternalFailure>();
248 }
249
250 // Hash the data file and update the verification context
Brad Bishop9f44c992020-11-06 14:48:46 -0500251 auto size = std::filesystem::file_size(file);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500252 auto dataPtr = mapFile(file, size);
253
254 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
255 if (result <= 0)
256 {
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500257 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500258 entry("ERRCODE=%lu", ERR_get_error()));
259 elog<InternalFailure>();
260 }
261
262 // Verify the data with signature.
Brad Bishop9f44c992020-11-06 14:48:46 -0500263 size = std::filesystem::file_size(sigFile);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500264 auto signature = mapFile(sigFile, size);
265
266 result = EVP_DigestVerifyFinal(
267 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
268 size);
269
270 // Check the verification result.
271 if (result < 0)
272 {
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500273 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500274 entry("ERRCODE=%lu", ERR_get_error()));
275 elog<InternalFailure>();
276 }
277
278 if (result == 0)
279 {
280 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
281 entry("PATH=%s", sigFile.c_str()));
282 return false;
283 }
284 return true;
285}
286
Brad Bishop9f44c992020-11-06 14:48:46 -0500287inline RSA* Signature::createPublicRSA(const std::filesystem::path& publicKey)
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500288{
289 RSA* rsa = nullptr;
Brad Bishop9f44c992020-11-06 14:48:46 -0500290 auto size = std::filesystem::file_size(publicKey);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500291
292 // Read public key file
293 auto data = mapFile(publicKey, size);
294
295 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
296 if (keyBio.get() == nullptr)
297 {
298 log<level::ERR>("Failed to create new BIO Memory buffer");
299 elog<InternalFailure>();
300 }
301
302 rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr);
303
304 return rsa;
305}
306
Brad Bishop9f44c992020-11-06 14:48:46 -0500307CustomMap Signature::mapFile(const std::filesystem::path& path, size_t size)
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500308{
309
310 CustomFd fd(open(path.c_str(), O_RDONLY));
311
312 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
313 size);
314}
315
316} // namespace image
317} // namespace software
318} // namespace openpower