blob: 99c7d2b9e6dd0a4798ee36b43e04aaea20661a8f [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
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -06007#include "image_verify.hpp"
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -06008#include "config.h"
9#include "version.hpp"
10
11#include <phosphor-logging/log.hpp>
12#include <phosphor-logging/elog.hpp>
13#include <phosphor-logging/elog-errors.hpp>
14#include <xyz/openbmc_project/Common/error.hpp>
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060015
16namespace phosphor
17{
18namespace software
19{
20namespace image
21{
22
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060023using namespace phosphor::logging;
24using namespace phosphor::software::manager;
25using InternalFailure =
26 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
27
28constexpr auto keyTypeTag = "KeyType";
29constexpr auto hashFunctionTag = "HashType";
30
31Signature::Signature(const fs::path& imageDirPath,
32 const fs::path& signedConfPath) :
33 imageDirPath(imageDirPath),
34 signedConfPath(signedConfPath)
35{
36 fs::path file(imageDirPath / MANIFEST_FILE_NAME);
37
38 keyType = Version::getValue(file, keyTypeTag);
39 hashType = Version::getValue(file, hashFunctionTag);
40}
41
42AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
43{
44 AvailableKeyTypes keyTypes{};
45
46 // Find the path of all the files
47 if (!fs::is_directory(signedConfPath))
48 {
49 log<level::ERR>("Signed configuration path not found in the system");
50 elog<InternalFailure>();
51 }
52
53 // Look for all the hash and public key file names get the key value
54 // For example:
55 // /etc/activationdata/OpenBMC/publickey
56 // /etc/activationdata/OpenBMC/hashfunc
57 // /etc/activationdata/GA/publickey
58 // /etc/activationdata/GA/hashfunc
59 // Set will have OpenBMC, GA
60
61 for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
62 {
63 if ((p.path().filename() == HASH_FILE_NAME) ||
64 (p.path().filename() == PUBLICKEY_FILE_NAME))
65 {
66 // extract the key types
67 // /etc/activationdata/OpenBMC/ -> get OpenBMC from the path
68 auto key = p.path().parent_path();
69 keyTypes.insert(key.filename());
70 }
71 }
72
73 return keyTypes;
74}
75
76inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
77{
78 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
79 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
80
81 return std::make_pair(std::move(hashpath), std::move(keyPath));
82}
83
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -060084bool Signature::verify()
85{
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -060086 try
87 {
88 // Verify the MANIFEST and publickey file using available
89 // public keys and hash on the system.
90 if (false == systemLevelVerify())
91 {
92 log<level::ERR>("System level Signature Validation failed");
93 return false;
94 }
95
96 // image specfic publickey file name.
97 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
98
99 // Validate the BMC image files.
100 for (const auto& bmcImage : bmcImages)
101 {
102 // Build Image File name
103 fs::path file(imageDirPath);
104 file /= bmcImage;
105
106 // Build Signature File name
107 fs::path sigFile(file);
108 sigFile.replace_extension(SIGNATURE_FILE_EXT);
109
110 // Verify the signature.
111 auto valid = verifyFile(file, sigFile, publicKeyFile, hashType);
112 if (valid == false)
113 {
114 log<level::ERR>("Image file Signature Validation failed",
115 entry("IMAGE=%s", bmcImage.c_str()));
116 return false;
117 }
118 }
119
120 log<level::DEBUG>("Sucessfully completed Signature vaildation.");
121
122 return true;
123 }
124 catch (const InternalFailure& e)
125 {
126 return false;
127 }
128 catch (const std::exception& e)
129 {
130 log<level::ERR>(e.what());
131 return false;
132 }
133}
134
135bool Signature::systemLevelVerify()
136{
137 // Get available key types from the system.
138 auto keyTypes = getAvailableKeyTypesFromSystem();
139 if (keyTypes.empty())
140 {
141 log<level::ERR>("Missing Signature configuration data in system");
142 elog<InternalFailure>();
143 }
144
145 // Build publickey and its signature file name.
146 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
147 fs::path pkeyFileSig(pkeyFile);
148 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
149
150 // Build manifest and its signature file name.
151 fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
152 fs::path manifestFileSig(manifestFile);
153 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
154
155 auto valid = false;
156
157 // Verify the file signature with available key types
158 // public keys and hash function.
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600159 // For any internal failure during the key/hash pair specific
160 // validation, should continue the validation with next
161 // available Key/hash pair.
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600162 for (const auto& keyType : keyTypes)
163 {
164 auto keyHashPair = getKeyHashFileNames(keyType);
165
166 auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
167
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600168 try
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600169 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600170 // Verify manifest file signature
171 valid = verifyFile(manifestFile, manifestFileSig,
172 keyHashPair.second, hashFunc);
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600173 if (valid)
174 {
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600175 // Verify publickey file signature.
176 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
177 hashFunc);
178 if (valid)
179 {
180 break;
181 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600182 }
183 }
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600184 catch (const InternalFailure& e)
185 {
186 valid = false;
187 }
Jayanth Othayoth2ab9b102018-02-21 05:27:47 -0600188 }
189 return valid;
190}
191
192bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
193 const fs::path& publicKey,
194 const std::string& hashFunc)
195{
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600196
197 // Check existence of the files in the system.
198 if (!(fs::exists(file) && fs::exists(sigFile)))
199 {
200 log<level::ERR>("Failed to find the Data or signature file.",
201 entry("FILE=%s", file.c_str()));
202 elog<InternalFailure>();
203 }
204
205 // Create RSA.
206 auto publicRSA = createPublicRSA(publicKey);
207 if (publicRSA == nullptr)
208 {
209 log<level::ERR>("Failed to create RSA",
210 entry("FILE=%s", publicKey.c_str()));
211 elog<InternalFailure>();
212 }
213
214 // Assign key to RSA.
215 EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free);
216 EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA);
217
218 // Initializes a digest context.
219 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_create(), ::EVP_MD_CTX_destroy);
220
221 // Adds all digest algorithms to the internal table
222 OpenSSL_add_all_digests();
223
224 // Create Hash structre.
225 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
226 if (!hashStruct)
227 {
228 log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
229 entry("HASH=%s", hashFunc.c_str()));
230 elog<InternalFailure>();
231 }
232
233 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
234 nullptr, pKeyPtr.get());
235
236 if (result <= 0)
237 {
238 log<level::ERR>("Error occured during EVP_DigestVerifyInit",
239 entry("ERRCODE=%lu", ERR_get_error()));
240 elog<InternalFailure>();
241 }
242
243 // Hash the data file and update the verification context
244 auto size = fs::file_size(file);
245 auto dataPtr = mapFile(file, size);
246
247 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
248 if (result <= 0)
249 {
250 log<level::ERR>("Error occured during EVP_DigestVerifyUpdate",
251 entry("ERRCODE=%lu", ERR_get_error()));
252 elog<InternalFailure>();
253 }
254
255 // Verify the data with signature.
256 size = fs::file_size(sigFile);
257 auto signature = mapFile(sigFile, size);
258
259 result = EVP_DigestVerifyFinal(
260 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
261 size);
262
263 // Check the verification result.
264 if (result < 0)
265 {
266 log<level::ERR>("Error occured during EVP_DigestVerifyFinal",
267 entry("ERRCODE=%lu", ERR_get_error()));
268 elog<InternalFailure>();
269 }
270
271 if (result == 0)
272 {
273 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
274 entry("PATH=%s", sigFile.c_str()));
275 return false;
276 }
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600277 return true;
278}
279
Jayanth Othayothfb6e1fc2018-02-21 05:43:20 -0600280inline RSA* Signature::createPublicRSA(const fs::path& publicKey)
281{
282 RSA* rsa = nullptr;
283 auto size = fs::file_size(publicKey);
284
285 // Read public key file
286 auto data = mapFile(publicKey, size);
287
288 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
289 if (keyBio.get() == nullptr)
290 {
291 log<level::ERR>("Failed to create new BIO Memory buffer");
292 elog<InternalFailure>();
293 }
294
295 rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr);
296
297 return rsa;
298}
299
300CustomMap Signature::mapFile(const fs::path& path, size_t size)
301{
302
303 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
Jayanth Othayoth9d7cd832018-02-21 05:12:39 -0600309} // namespace image
310} // namespace software
311} // namespace phosphor