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