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