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