blob: 7ca262916fb67aba87c4d6e2f3978ecb08234dbc [file] [log] [blame]
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -06001#include <set>
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -06002#include <fstream>
3#include <sys/stat.h>
4#include <fcntl.h>
5#include <openssl/err.h>
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -06006
Lei YU1be8d502018-06-20 11:48:36 +08007#include "images.hpp"
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -06008#include "image_verify.hpp"
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -06009#include "config.h"
10#include "version.hpp"
11
12#include <phosphor-logging/log.hpp>
13#include <phosphor-logging/elog.hpp>
14#include <phosphor-logging/elog-errors.hpp>
15#include <xyz/openbmc_project/Common/error.hpp>
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060016
17namespace phosphor
18{
19namespace software
20{
21namespace image
22{
23
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060024using namespace phosphor::logging;
25using namespace phosphor::software::manager;
26using InternalFailure =
27 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
28
29constexpr auto keyTypeTag = "KeyType";
30constexpr auto hashFunctionTag = "HashType";
31
32Signature::Signature(const fs::path& imageDirPath,
33 const fs::path& signedConfPath) :
34 imageDirPath(imageDirPath),
35 signedConfPath(signedConfPath)
36{
37 fs::path file(imageDirPath / MANIFEST_FILE_NAME);
38
39 keyType = Version::getValue(file, keyTypeTag);
40 hashType = Version::getValue(file, hashFunctionTag);
41}
42
43AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
44{
45 AvailableKeyTypes keyTypes{};
46
47 // Find the path of all the files
48 if (!fs::is_directory(signedConfPath))
49 {
50 log<level::ERR>("Signed configuration path not found in the system");
51 elog<InternalFailure>();
52 }
53
54 // Look for all the hash and public key file names get the key value
55 // For example:
56 // /etc/activationdata/OpenBMC/publickey
57 // /etc/activationdata/OpenBMC/hashfunc
58 // /etc/activationdata/GA/publickey
59 // /etc/activationdata/GA/hashfunc
60 // Set will have OpenBMC, GA
61
62 for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
63 {
64 if ((p.path().filename() == HASH_FILE_NAME) ||
65 (p.path().filename() == PUBLICKEY_FILE_NAME))
66 {
67 // extract the key types
68 // /etc/activationdata/OpenBMC/ -> get OpenBMC from the path
69 auto key = p.path().parent_path();
70 keyTypes.insert(key.filename());
71 }
72 }
73
74 return keyTypes;
75}
76
77inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
78{
79 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
80 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
81
82 return std::make_pair(std::move(hashpath), std::move(keyPath));
83}
84
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060085bool Signature::verify()
86{
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060087 try
88 {
89 // Verify the MANIFEST and publickey file using available
90 // public keys and hash on the system.
91 if (false == systemLevelVerify())
92 {
93 log<level::ERR>("System level Signature Validation failed");
94 return false;
95 }
96
Gunnar Millse11a2022018-03-23 12:04:48 -050097 // image specific publickey file name.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060098 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
99
100 // Validate the BMC image files.
101 for (const auto& bmcImage : bmcImages)
102 {
103 // Build Image File name
104 fs::path file(imageDirPath);
105 file /= bmcImage;
106
107 // Build Signature File name
108 fs::path sigFile(file);
109 sigFile.replace_extension(SIGNATURE_FILE_EXT);
110
111 // Verify the signature.
112 auto valid = verifyFile(file, sigFile, publicKeyFile, hashType);
113 if (valid == false)
114 {
115 log<level::ERR>("Image file Signature Validation failed",
116 entry("IMAGE=%s", bmcImage.c_str()));
117 return false;
118 }
119 }
120
Gunnar Millse11a2022018-03-23 12:04:48 -0500121 log<level::DEBUG>("Successfully completed Signature vaildation.");
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600122
123 return true;
124 }
125 catch (const InternalFailure& e)
126 {
127 return false;
128 }
129 catch (const std::exception& e)
130 {
131 log<level::ERR>(e.what());
132 return false;
133 }
134}
135
136bool Signature::systemLevelVerify()
137{
138 // Get available key types from the system.
139 auto keyTypes = getAvailableKeyTypesFromSystem();
140 if (keyTypes.empty())
141 {
142 log<level::ERR>("Missing Signature configuration data in system");
143 elog<InternalFailure>();
144 }
145
146 // Build publickey and its signature file name.
147 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
148 fs::path pkeyFileSig(pkeyFile);
149 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
150
151 // Build manifest and its signature file name.
152 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
153 fs::path manifestFileSig(manifestFile);
154 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
155
156 auto valid = false;
157
158 // Verify the file signature with available key types
159 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600160 // For any internal failure during the key/hash pair specific
161 // validation, should continue the validation with next
162 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600163 for (const auto& keyType : keyTypes)
164 {
165 auto keyHashPair = getKeyHashFileNames(keyType);
166
167 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
168
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600169 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600170 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600171 // Verify manifest file signature
172 valid = verifyFile(manifestFile, manifestFileSig,
173 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600174 if (valid)
175 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600176 // Verify publickey file signature.
177 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
178 hashFunc);
179 if (valid)
180 {
181 break;
182 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600183 }
184 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600185 catch (const InternalFailure& e)
186 {
187 valid = false;
188 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600189 }
190 return valid;
191}
192
193bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
194 const fs::path& publicKey,
195 const std::string& hashFunc)
196{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600197
198 // Check existence of the files in the system.
199 if (!(fs::exists(file) && fs::exists(sigFile)))
200 {
201 log<level::ERR>("Failed to find the Data or signature file.",
202 entry("FILE=%s", file.c_str()));
203 elog<InternalFailure>();
204 }
205
206 // Create RSA.
207 auto publicRSA = createPublicRSA(publicKey);
208 if (publicRSA == nullptr)
209 {
210 log<level::ERR>("Failed to create RSA",
211 entry("FILE=%s", publicKey.c_str()));
212 elog<InternalFailure>();
213 }
214
215 // Assign key to RSA.
216 EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free);
217 EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA);
218
219 // Initializes a digest context.
220 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_create(), ::EVP_MD_CTX_destroy);
221
222 // Adds all digest algorithms to the internal table
223 OpenSSL_add_all_digests();
224
Gunnar Mills2bcba022018-04-08 15:02:04 -0500225 // Create Hash structure.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600226 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
227 if (!hashStruct)
228 {
229 log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
230 entry("HASH=%s", hashFunc.c_str()));
231 elog<InternalFailure>();
232 }
233
234 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
235 nullptr, pKeyPtr.get());
236
237 if (result <= 0)
238 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500239 log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600240 entry("ERRCODE=%lu", ERR_get_error()));
241 elog<InternalFailure>();
242 }
243
244 // Hash the data file and update the verification context
245 auto size = fs::file_size(file);
246 auto dataPtr = mapFile(file, size);
247
248 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
249 if (result <= 0)
250 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500251 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600252 entry("ERRCODE=%lu", ERR_get_error()));
253 elog<InternalFailure>();
254 }
255
256 // Verify the data with signature.
257 size = fs::file_size(sigFile);
258 auto signature = mapFile(sigFile, size);
259
260 result = EVP_DigestVerifyFinal(
261 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
262 size);
263
264 // Check the verification result.
265 if (result < 0)
266 {
Gunnar Millse11a2022018-03-23 12:04:48 -0500267 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600268 entry("ERRCODE=%lu", ERR_get_error()));
269 elog<InternalFailure>();
270 }
271
272 if (result == 0)
273 {
274 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
275 entry("PATH=%s", sigFile.c_str()));
276 return false;
277 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600278 return true;
279}
280
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600281inline RSA* Signature::createPublicRSA(const fs::path& publicKey)
282{
283 RSA* rsa = nullptr;
284 auto size = fs::file_size(publicKey);
285
286 // Read public key file
287 auto data = mapFile(publicKey, size);
288
289 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
290 if (keyBio.get() == nullptr)
291 {
292 log<level::ERR>("Failed to create new BIO Memory buffer");
293 elog<InternalFailure>();
294 }
295
296 rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr);
297
298 return rsa;
299}
300
301CustomMap Signature::mapFile(const fs::path& path, size_t size)
302{
303
304 CustomFd fd(open(path.c_str(), O_RDONLY));
305
306 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
307 size);
308}
309
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600310} // namespace image
311} // namespace software
312} // namespace phosphor