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( 124 std::string& newPassword, std::array<uint8_t, maxSeedSize>& seed, 125 unsigned int mdLen, const EVP_MD* digestFunc) 126 { 127 mNewPwdHash.fill(0); 128 129 if (!PKCS5_PBKDF2_HMAC(reinterpret_cast<const char*>(newPassword.c_str()), 130 newPassword.length() + 1, 131 reinterpret_cast<const unsigned char*>(seed.data()), 132 seed.size(), iterValue, digestFunc, mdLen, 133 mNewPwdHash.data())) 134 { 135 lg2::error("Verify PKCS5_PBKDF2_HMAC Integrity Check failed"); 136 return false; 137 } 138 139 return true; 140 } 141 142 void Password::verifyPassword(std::string userName, std::string currentPassword, 143 std::string newPassword) 144 { 145 if (fs::exists(seedFile.c_str())) 146 { 147 std::array<uint8_t, maxHashSize> orgUsrPwdHash; 148 std::array<uint8_t, maxHashSize> orgAdminPwdHash; 149 std::array<uint8_t, maxSeedSize> seed; 150 std::string hashAlgo = ""; 151 152 if (getParam(orgUsrPwdHash, orgAdminPwdHash, seed, hashAlgo)) 153 { 154 if (orgUsrPwdHash.empty() || orgAdminPwdHash.empty() || 155 seed.empty() || hashAlgo.empty()) 156 { 157 return; 158 } 159 } 160 else 161 { 162 throw InternalFailure(); 163 } 164 165 if (userName == "AdminPassword") 166 { 167 if (!isMatch(orgAdminPwdHash, seed, currentPassword, hashAlgo)) 168 { 169 throw InvalidCurrentPassword(); 170 } 171 } 172 else 173 { 174 if (!isMatch(orgUsrPwdHash, seed, currentPassword, hashAlgo)) 175 { 176 throw InvalidCurrentPassword(); 177 } 178 } 179 if (hashAlgo == "SHA256") 180 { 181 if (!verifyIntegrityCheck(newPassword, seed, 32, EVP_sha256())) 182 { 183 throw InternalFailure(); 184 } 185 } 186 if (hashAlgo == "SHA384") 187 { 188 if (!verifyIntegrityCheck(newPassword, seed, 48, EVP_sha384())) 189 { 190 throw InternalFailure(); 191 } 192 } 193 return; 194 } 195 throw InternalFailure(); 196 } 197 void Password::changePassword(std::string userName, std::string currentPassword, 198 std::string newPassword) 199 { 200 lg2::debug("BIOS config changePassword"); 201 verifyPassword(userName, currentPassword, newPassword); 202 203 std::ifstream fs(seedFile.c_str()); 204 nlohmann::json json = nullptr; 205 206 if (fs.is_open()) 207 { 208 try 209 { 210 json = nlohmann::json::parse(fs, nullptr, false); 211 } 212 catch (const nlohmann::json::parse_error& e) 213 { 214 lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e); 215 throw InternalFailure(); 216 } 217 218 if (json.is_discarded()) 219 { 220 throw InternalFailure(); 221 } 222 json["AdminPwdHash"] = mNewPwdHash; 223 json["IsAdminPwdChanged"] = true; 224 225 std::ofstream ofs(seedFile.c_str(), std::ios::out); 226 const auto& writeData = json.dump(); 227 ofs << writeData; 228 ofs.close(); 229 } 230 else 231 { 232 lg2::debug("Cannot open file stream"); 233 throw InternalFailure(); 234 } 235 } 236 Password::Password(sdbusplus::asio::object_server& objectServer, 237 std::shared_ptr<sdbusplus::asio::connection>& systemBus, 238 std::string persistPath) : 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(persistPath); 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