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