xref: /openbmc/bios-settings-mgr/src/password.cpp (revision 96e72ec579ea3c19ddbde4151d9f26d988c47fa6)
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 <sdbusplus/asio/connection.hpp>
25 #include <sdbusplus/asio/object_server.hpp>
26 
27 #include <fstream>
28 #include <iostream>
29 
30 namespace bios_config_pwd
31 {
32 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
33 using namespace sdbusplus::xyz::openbmc_project::BIOSConfig::Common::Error;
34 
35 uint8_t
36     Password::convertUnicode(const std::string& pwd,
37                              std::array<uint16_t, maxPasswordLen>& unicodePwd)
38 {
39     if (pwd.size() > unicodePwd.size())
40     {
41         phosphor::logging::log<phosphor::logging::level::DEBUG>(
42             "password size exceeds unicodePwd array size");
43         throw InternalFailure();
44     }
45 
46     for (std::string::size_type i = 0; i < pwd.size(); i++)
47     {
48         unicodePwd[i] = pwd[i];
49     }
50     unicodePwd[pwd.size()] = 0;
51 
52     return (pwd.size() * sizeof(uint16_t) + sizeof(uint16_t));
53 }
54 
55 bool Password::isMatch(const std::array<uint8_t, maxHashSize>& expected,
56                        const std::array<uint8_t, maxSeedSize>& seed,
57                        const std::string rawData, const std::string algo)
58 {
59     phosphor::logging::log<phosphor::logging::level::ERR>("isMatch");
60 
61     if (algo == "SHA256")
62     {
63         std::vector<uint8_t> output(SHA256_DIGEST_LENGTH);
64         unsigned int hashLen = 0;
65 
66         std::array<uint16_t, maxPasswordLen> unicodePwd = {0};
67         uint8_t unicodePwdLength = convertUnicode(rawData, unicodePwd);
68 
69         if (HMAC(EVP_sha256(), seed.data(), seed.size(),
70                  reinterpret_cast<const unsigned char*>(unicodePwd.data()),
71                  unicodePwdLength, output.data(), &hashLen) == NULL)
72         {
73             phosphor::logging::log<phosphor::logging::level::ERR>(
74                 "Generate HMAC_SHA256 Integrity Check Value failed");
75             throw InternalFailure();
76         }
77 
78         int cmp;
79         cmp = std::memcmp(output.data(), expected.data(),
80                           output.size() * sizeof(uint8_t));
81         if (cmp == 0)
82         {
83             return true;
84         }
85         else
86         {
87             return false;
88         }
89     }
90     if (algo == "SHA384")
91     {
92         std::array<uint8_t, SHA384_DIGEST_LENGTH> output;
93         unsigned int hashLen = 0;
94 
95         std::array<uint16_t, maxPasswordLen> unicodePwd = {0};
96         uint8_t unicodePwdLength = convertUnicode(rawData, unicodePwd);
97 
98         if (HMAC(EVP_sha384(), seed.data(), seed.size(),
99                  reinterpret_cast<const unsigned char*>(unicodePwd.data()),
100                  unicodePwdLength, output.data(), &hashLen) == NULL)
101         {
102             phosphor::logging::log<phosphor::logging::level::ERR>(
103                 "Generate HMAC_SHA384 Integrity Check Value failed");
104             throw InternalFailure();
105         }
106 
107         int cmp;
108         cmp = std::memcmp(output.data(), expected.data(),
109                           output.size() * sizeof(uint8_t));
110         if (cmp == 0)
111         {
112             return true;
113         }
114         else
115         {
116             return false;
117         }
118     }
119 
120     return false;
121 }
122 
123 void Password::verifyPassword(std::string userName, std::string currentPassword,
124                               std::string newPassword)
125 {
126     if (fs::exists(seedFile.c_str()))
127     {
128         std::array<uint8_t, maxHashSize> orgUsrPwdHash;
129         std::array<uint8_t, maxHashSize> orgAdminPwdHash;
130         std::array<uint8_t, maxSeedSize> seed;
131         std::string hashAlgo = "";
132         try
133         {
134             nlohmann::json json = nullptr;
135             std::ifstream ifs(seedFile.c_str());
136             if (ifs.is_open())
137             {
138                 try
139                 {
140                     json = nlohmann::json::parse(ifs, nullptr, false);
141                 }
142                 catch (const nlohmann::json::parse_error& e)
143                 {
144                     phosphor::logging::log<phosphor::logging::level::ERR>(
145                         e.what());
146                     throw InternalFailure();
147                 }
148 
149                 if (json.is_discarded())
150                 {
151                     return;
152                 }
153                 orgUsrPwdHash = json["UserPwdHash"];
154                 orgAdminPwdHash = json["AdminPwdHash"];
155                 seed = json["Seed"];
156                 hashAlgo = json["HashAlgo"];
157             }
158             else
159             {
160                 return;
161             }
162         }
163         catch (nlohmann::detail::exception& e)
164         {
165             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
166             throw InternalFailure();
167         }
168         if (userName == "AdminPassword")
169         {
170             if (!isMatch(orgAdminPwdHash, seed, currentPassword, hashAlgo))
171             {
172                 throw InvalidCurrentPassword();
173             }
174         }
175         else
176         {
177             if (!isMatch(orgUsrPwdHash, seed, currentPassword, hashAlgo))
178             {
179                 throw InvalidCurrentPassword();
180             }
181         }
182         if (hashAlgo == "SHA256")
183         {
184             std::array<uint16_t, maxPasswordLen> unicodePwd;
185             uint8_t unicodePwdlength = 0;
186             unsigned int mdLen = 0;
187             unicodePwdlength = convertUnicode(newPassword, unicodePwd);
188             mNewPwdHash.fill(0);
189 
190             if (HMAC(EVP_sha256(), seed.data(), seed.size(),
191                      reinterpret_cast<const unsigned char*>(unicodePwd.data()),
192                      unicodePwdlength, mNewPwdHash.data(), &mdLen) == NULL)
193             {
194                 throw InternalFailure();
195             }
196         }
197         if (hashAlgo == "SHA384")
198         {
199             std::array<uint16_t, maxPasswordLen> unicodePwd;
200             uint8_t unicodePwdlength = 0;
201             unsigned int mdLen = 0;
202             unicodePwdlength = convertUnicode(newPassword, unicodePwd);
203             mNewPwdHash.fill(0);
204 
205             if (HMAC(EVP_sha384(), seed.data(), seed.size(),
206                      reinterpret_cast<const unsigned char*>(unicodePwd.data()),
207                      unicodePwdlength, mNewPwdHash.data(), &mdLen) == NULL)
208             {
209                 throw InternalFailure();
210             }
211         }
212         return;
213     }
214     throw InternalFailure();
215 }
216 void Password::changePassword(std::string userName, std::string currentPassword,
217                               std::string newPassword)
218 {
219     phosphor::logging::log<phosphor::logging::level::DEBUG>(
220         "BIOS config changePassword");
221     verifyPassword(userName, currentPassword, newPassword);
222 
223     std::ifstream fs(seedFile.c_str());
224     nlohmann::json json = nullptr;
225 
226     if (fs.is_open())
227     {
228         try
229         {
230             json = nlohmann::json::parse(fs, nullptr, false);
231         }
232         catch (const nlohmann::json::parse_error& e)
233         {
234             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
235             throw InternalFailure();
236         }
237 
238         if (json.is_discarded())
239         {
240             throw InternalFailure();
241         }
242         json["AdminPwdHash"] = mNewPwdHash;
243         json["IsAdminPwdChanged"] = true;
244 
245         std::ofstream ofs(seedFile.c_str(), std::ios::out);
246         const auto& writeData = json.dump();
247         ofs << writeData;
248         ofs.close();
249     }
250     else
251     {
252         phosphor::logging::log<phosphor::logging::level::DEBUG>(
253             "Cannot open file stream");
254         throw InternalFailure();
255     }
256 }
257 Password::Password(sdbusplus::asio::object_server& objectServer,
258                    std::shared_ptr<sdbusplus::asio::connection>& systemBus) :
259     sdbusplus::xyz::openbmc_project::BIOSConfig::server::Password(
260         *systemBus, objectPathPwd),
261     objServer(objectServer), systemBus(systemBus)
262 {
263     phosphor::logging::log<phosphor::logging::level::DEBUG>(
264         "BIOS config password is runing");
265     try
266     {
267         fs::path biosDir(BIOS_PERSIST_PATH);
268         fs::create_directories(biosDir);
269         seedFile = biosDir / biosSeedFile;
270     }
271     catch (const fs::filesystem_error& e)
272     {
273         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
274         throw InternalFailure();
275     }
276 }
277 
278 } // namespace bios_config_pwd
279 
280 int main()
281 {
282     boost::asio::io_service io;
283     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
284 
285     systemBus->request_name(bios_config_pwd::servicePwd);
286     sdbusplus::asio::object_server objectServer(systemBus);
287     bios_config_pwd::Password password(objectServer, systemBus);
288 
289     io.run();
290     return 0;
291 }
292