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