blob: 917128c16f25199ae7a4f8c18de5fb22353f7871 [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);
Adriana Kobylak3ce1a4c2021-12-08 14:48:52 +0000214 if (!publicRSA)
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500215 {
216 log<level::ERR>("Failed to create RSA",
217 entry("FILE=%s", publicKey.c_str()));
218 elog<InternalFailure>();
219 }
220
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500221 // Initializes a digest context.
Adriana Kobylak70ca2422018-09-06 14:23:38 -0500222 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500223
224 // Adds all digest algorithms to the internal table
225 OpenSSL_add_all_digests();
226
Gunnar Millsa9cfced2018-04-08 15:01:57 -0500227 // Create Hash structure.
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500228 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,
Adriana Kobylak3ce1a4c2021-12-08 14:48:52 +0000237 nullptr, publicRSA.get());
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500238
239 if (result <= 0)
240 {
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500241 log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500242 entry("ERRCODE=%lu", ERR_get_error()));
243 elog<InternalFailure>();
244 }
245
246 // Hash the data file and update the verification context
Brad Bishop9f44c992020-11-06 14:48:46 -0500247 auto size = std::filesystem::file_size(file);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500248 auto dataPtr = mapFile(file, size);
249
250 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
251 if (result <= 0)
252 {
Gunnar Mills4c7d2062018-03-23 12:12:20 -0500253 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500254 entry("ERRCODE=%lu", ERR_get_error()));
255 elog<InternalFailure>();
256 }
257
258 // Verify the data with signature.
Brad Bishop9f44c992020-11-06 14:48:46 -0500259 size = std::filesystem::file_size(sigFile);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500260 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 Mills4c7d2062018-03-23 12:12:20 -0500269 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500270 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 }
280 return true;
281}
282
Adriana Kobylak3ce1a4c2021-12-08 14:48:52 +0000283inline EVP_PKEY_Ptr
284 Signature::createPublicRSA(const std::filesystem::path& publicKey)
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500285{
Brad Bishop9f44c992020-11-06 14:48:46 -0500286 auto size = std::filesystem::file_size(publicKey);
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500287
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
Adriana Kobylak3ce1a4c2021-12-08 14:48:52 +0000298 return {PEM_read_bio_PUBKEY(keyBio.get(), nullptr, nullptr, nullptr),
299 &::EVP_PKEY_free};
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500300}
301
Brad Bishop9f44c992020-11-06 14:48:46 -0500302CustomMap Signature::mapFile(const std::filesystem::path& path, size_t size)
Jayanth Othayoth70804dc2018-03-20 06:31:59 -0500303{
304
305 CustomFd fd(open(path.c_str(), O_RDONLY));
306
307 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
308 size);
309}
310
311} // namespace image
312} // namespace software
313} // namespace openpower