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