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