xref: /openbmc/bios-settings-mgr/src/password.cpp (revision d0f034a2c8a15a380f80980d791d62476a78fdb2)
1 /*
2 // Copyright (c) 2020 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #include "password.hpp"
17 
18 #include "xyz/openbmc_project/BIOSConfig/Common/error.hpp"
19 #include "xyz/openbmc_project/Common/error.hpp"
20 
21 #include <boost/algorithm/hex.hpp>
22 #include <boost/asio.hpp>
23 #include <phosphor-logging/elog-errors.hpp>
24 #include <phosphor-logging/lg2.hpp>
25 #include <sdbusplus/asio/connection.hpp>
26 #include <sdbusplus/asio/object_server.hpp>
27 
28 #include <fstream>
29 #include <iostream>
30 
31 namespace bios_config_pwd
32 {
33 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
34 using namespace sdbusplus::xyz::openbmc_project::BIOSConfig::Common::Error;
35 
36 bool Password::compareDigest(const EVP_MD* digestFunc, size_t digestLen,
37                              const std::array<uint8_t, maxHashSize>& expected,
38                              const std::array<uint8_t, maxSeedSize>& seed,
39                              const std::string& rawData)
40 {
41     std::vector<uint8_t> output(digestLen);
42     unsigned int hashLen = digestLen;
43 
44     if (!PKCS5_PBKDF2_HMAC(reinterpret_cast<const char*>(rawData.c_str()),
45                            rawData.length() + 1,
46                            reinterpret_cast<const unsigned char*>(seed.data()),
47                            seed.size(), iterValue, digestFunc, hashLen,
48                            output.data()))
49     {
50         lg2::error("Generate PKCS5_PBKDF2_HMAC Integrity Check Value failed");
51         throw InternalFailure();
52     }
53 
54     if (std::memcmp(output.data(), expected.data(),
55                     output.size() * sizeof(uint8_t)) == 0)
56     {
57         return true;
58     }
59 
60     return false;
61 }
62 
63 bool Password::isMatch(const std::array<uint8_t, maxHashSize>& expected,
64                        const std::array<uint8_t, maxSeedSize>& seed,
65                        const std::string& rawData, const std::string& algo)
66 {
67     lg2::error("isMatch");
68 
69     if (algo == "SHA256")
70     {
71         return compareDigest(EVP_sha256(), SHA256_DIGEST_LENGTH, expected, seed,
72                              rawData);
73     }
74 
75     if (algo == "SHA384")
76     {
77         return compareDigest(EVP_sha384(), SHA384_DIGEST_LENGTH, expected, seed,
78                              rawData);
79     }
80 
81     return false;
82 }
83 
84 void Password::verifyPassword(std::string userName, std::string currentPassword,
85                               std::string newPassword)
86 {
87     if (fs::exists(seedFile.c_str()))
88     {
89         std::array<uint8_t, maxHashSize> orgUsrPwdHash;
90         std::array<uint8_t, maxHashSize> orgAdminPwdHash;
91         std::array<uint8_t, maxSeedSize> seed;
92         std::string hashAlgo = "";
93         try
94         {
95             nlohmann::json json = nullptr;
96             std::ifstream ifs(seedFile.c_str());
97             if (ifs.is_open())
98             {
99                 try
100                 {
101                     json = nlohmann::json::parse(ifs, nullptr, false);
102                 }
103                 catch (const nlohmann::json::parse_error& e)
104                 {
105                     lg2::error("Failed to parse JSON file: {ERROR}", "ERROR",
106                                e);
107                     throw InternalFailure();
108                 }
109 
110                 if (json.is_discarded())
111                 {
112                     return;
113                 }
114                 orgUsrPwdHash = json["UserPwdHash"];
115                 orgAdminPwdHash = json["AdminPwdHash"];
116                 seed = json["Seed"];
117                 hashAlgo = json["HashAlgo"];
118             }
119             else
120             {
121                 return;
122             }
123         }
124         catch (nlohmann::detail::exception& e)
125         {
126             lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
127             throw InternalFailure();
128         }
129         if (userName == "AdminPassword")
130         {
131             if (!isMatch(orgAdminPwdHash, seed, currentPassword, hashAlgo))
132             {
133                 throw InvalidCurrentPassword();
134             }
135         }
136         else
137         {
138             if (!isMatch(orgUsrPwdHash, seed, currentPassword, hashAlgo))
139             {
140                 throw InvalidCurrentPassword();
141             }
142         }
143         if (hashAlgo == "SHA256")
144         {
145             unsigned int mdLen = 32;
146             mNewPwdHash.fill(0);
147 
148             if (!PKCS5_PBKDF2_HMAC(
149                     reinterpret_cast<const char*>(newPassword.c_str()),
150                     newPassword.length() + 1,
151                     reinterpret_cast<const unsigned char*>(seed.data()),
152                     seed.size(), iterValue, EVP_sha256(), mdLen,
153                     mNewPwdHash.data()))
154             {
155                 lg2::error(
156                     "Verify PKCS5_PBKDF2_HMAC_SHA256 Integrity Check failed");
157                 throw InternalFailure();
158             }
159         }
160         if (hashAlgo == "SHA384")
161         {
162             unsigned int mdLen = 48;
163             mNewPwdHash.fill(0);
164 
165             if (!PKCS5_PBKDF2_HMAC(
166                     reinterpret_cast<const char*>(newPassword.c_str()),
167                     newPassword.length() + 1,
168                     reinterpret_cast<const unsigned char*>(seed.data()),
169                     seed.size(), iterValue, EVP_sha384(), mdLen,
170                     mNewPwdHash.data()))
171             {
172                 lg2::error(
173                     "Verify PKCS5_PBKDF2_HMAC_SHA384 Integrity Check failed");
174                 throw InternalFailure();
175             }
176         }
177         return;
178     }
179     throw InternalFailure();
180 }
181 void Password::changePassword(std::string userName, std::string currentPassword,
182                               std::string newPassword)
183 {
184     lg2::debug("BIOS config changePassword");
185     verifyPassword(userName, currentPassword, newPassword);
186 
187     std::ifstream fs(seedFile.c_str());
188     nlohmann::json json = nullptr;
189 
190     if (fs.is_open())
191     {
192         try
193         {
194             json = nlohmann::json::parse(fs, nullptr, false);
195         }
196         catch (const nlohmann::json::parse_error& e)
197         {
198             lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
199             throw InternalFailure();
200         }
201 
202         if (json.is_discarded())
203         {
204             throw InternalFailure();
205         }
206         json["AdminPwdHash"] = mNewPwdHash;
207         json["IsAdminPwdChanged"] = true;
208 
209         std::ofstream ofs(seedFile.c_str(), std::ios::out);
210         const auto& writeData = json.dump();
211         ofs << writeData;
212         ofs.close();
213     }
214     else
215     {
216         lg2::debug("Cannot open file stream");
217         throw InternalFailure();
218     }
219 }
220 Password::Password(sdbusplus::asio::object_server& objectServer,
221                    std::shared_ptr<sdbusplus::asio::connection>& systemBus) :
222     sdbusplus::xyz::openbmc_project::BIOSConfig::server::Password(
223         *systemBus, objectPathPwd),
224     objServer(objectServer), systemBus(systemBus)
225 {
226     lg2::debug("BIOS config password is runing");
227     try
228     {
229         fs::path biosDir(BIOS_PERSIST_PATH);
230         fs::create_directories(biosDir);
231         seedFile = biosDir / biosSeedFile;
232     }
233     catch (const fs::filesystem_error& e)
234     {
235         lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
236         throw InternalFailure();
237     }
238 }
239 
240 } // namespace bios_config_pwd
241 
242 int main()
243 {
244     boost::asio::io_service io;
245     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
246 
247     systemBus->request_name(bios_config_pwd::servicePwd);
248     sdbusplus::asio::object_server objectServer(systemBus);
249     bios_config_pwd::Password password(objectServer, systemBus);
250 
251     io.run();
252     return 0;
253 }
254