blob: 09d5cec2701fbef4151c8db961cf6a7c122d5d8e [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
Manojkiran Eda96442c82024-06-17 10:24:05 +0530125 log<level::DEBUG>("Successfully completed Signature validation.");
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);
Patrick Williams7fb6c342023-05-10 07:50:18 -0500170 auto keyValues = Version::getValue(keyHashPair.first,
171 {{hashFunctionTag, " "}});
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500172 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{
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500203 // Check existence of the files in the system.
Brad Bishop9f44c992020-11-06 14:48:46 -0500204 if (!(std::filesystem::exists(file) && std::filesystem::exists(sigFile)))
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500205 {
206 log<level::ERR>("Failed to find the Data or signature file.",
207 entry("FILE=%s", file.c_str()));
208 elog<InternalFailure>();
209 }
210
211 // Create RSA.
212 auto publicRSA = createPublicRSA(publicKey);
Adriana Kobylak3ce1a4c2021-12-08 14:48:52 +0000213 if (!publicRSA)
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500214 {
215 log<level::ERR>("Failed to create RSA",
216 entry("FILE=%s", publicKey.c_str()));
217 elog<InternalFailure>();
218 }
219
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500220 // Initializes a digest context.
Adriana Kobylak70ca2422018-09-06 14:23:38 -0500221 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500222
223 // Adds all digest algorithms to the internal table
224 OpenSSL_add_all_digests();
225
Gunnar Millsa9cfced2018-04-08 15:01:57 -0500226 // Create Hash structure.
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500227 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
228 if (!hashStruct)
229 {
230 log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
231 entry("HASH=%s", hashFunc.c_str()));
232 elog<InternalFailure>();
233 }
234
235 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
Adriana Kobylak3ce1a4c2021-12-08 14:48:52 +0000236 nullptr, publicRSA.get());
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500237
238 if (result <= 0)
239 {
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500240 log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500241 entry("ERRCODE=%lu", ERR_get_error()));
242 elog<InternalFailure>();
243 }
244
245 // Hash the data file and update the verification context
Brad Bishop9f44c992020-11-06 14:48:46 -0500246 auto size = std::filesystem::file_size(file);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500247 auto dataPtr = mapFile(file, size);
248
249 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
250 if (result <= 0)
251 {
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500252 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500253 entry("ERRCODE=%lu", ERR_get_error()));
254 elog<InternalFailure>();
255 }
256
257 // Verify the data with signature.
Brad Bishop9f44c992020-11-06 14:48:46 -0500258 size = std::filesystem::file_size(sigFile);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500259 auto signature = mapFile(sigFile, size);
260
261 result = EVP_DigestVerifyFinal(
262 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
263 size);
264
265 // Check the verification result.
266 if (result < 0)
267 {
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500268 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500269 entry("ERRCODE=%lu", ERR_get_error()));
270 elog<InternalFailure>();
271 }
272
273 if (result == 0)
274 {
275 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
276 entry("PATH=%s", sigFile.c_str()));
277 return false;
278 }
279 return true;
280}
281
Adriana Kobylak3ce1a4c2021-12-08 14:48:52 +0000282inline EVP_PKEY_Ptr
283 Signature::createPublicRSA(const std::filesystem::path& publicKey)
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500284{
Brad Bishop9f44c992020-11-06 14:48:46 -0500285 auto size = std::filesystem::file_size(publicKey);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500286
287 // Read public key file
288 auto data = mapFile(publicKey, size);
289
290 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
291 if (keyBio.get() == nullptr)
292 {
293 log<level::ERR>("Failed to create new BIO Memory buffer");
294 elog<InternalFailure>();
295 }
296
Adriana Kobylak3ce1a4c2021-12-08 14:48:52 +0000297 return {PEM_read_bio_PUBKEY(keyBio.get(), nullptr, nullptr, nullptr),
298 &::EVP_PKEY_free};
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500299}
300
Brad Bishop9f44c992020-11-06 14:48:46 -0500301CustomMap Signature::mapFile(const std::filesystem::path& path, size_t size)
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500302{
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500303 CustomFd fd(open(path.c_str(), O_RDONLY));
304
305 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
306 size);
307}
308
309} // namespace image
310} // namespace software
311} // namespace openpower