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