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 <sdbusplus/asio/connection.hpp> 25 #include <sdbusplus/asio/object_server.hpp> 26 27 #include <fstream> 28 #include <iostream> 29 30 namespace bios_config_pwd 31 { 32 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 33 using namespace sdbusplus::xyz::openbmc_project::BIOSConfig::Common::Error; 34 35 bool Password::isMatch(const std::array<uint8_t, maxHashSize>& expected, 36 const std::array<uint8_t, maxSeedSize>& seed, 37 const std::string rawData, const std::string algo) 38 { 39 phosphor::logging::log<phosphor::logging::level::ERR>("isMatch"); 40 41 if (algo == "SHA256") 42 { 43 std::vector<uint8_t> output(SHA256_DIGEST_LENGTH); 44 unsigned int hashLen = SHA256_DIGEST_LENGTH; 45 46 if (!PKCS5_PBKDF2_HMAC( 47 reinterpret_cast<const char*>(rawData.c_str()), 48 rawData.length() + 1, 49 reinterpret_cast<const unsigned char*>(seed.data()), 50 seed.size(), iterValue, EVP_sha256(), hashLen, output.data())) 51 { 52 phosphor::logging::log<phosphor::logging::level::ERR>( 53 "Generate PKCS5_PBKDF2_HMAC_SHA256 Integrity Check Value " 54 "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 phosphor::logging::log<phosphor::logging::level::ERR>( 82 "Generate PKCS5_PBKDF2_HMAC_SHA384 Integrity Check Value " 83 "failed"); 84 throw InternalFailure(); 85 } 86 87 int cmp; 88 cmp = std::memcmp(output.data(), expected.data(), 89 output.size() * sizeof(uint8_t)); 90 if (cmp == 0) 91 { 92 return true; 93 } 94 else 95 { 96 return false; 97 } 98 } 99 100 return false; 101 } 102 103 void Password::verifyPassword(std::string userName, std::string currentPassword, 104 std::string newPassword) 105 { 106 if (fs::exists(seedFile.c_str())) 107 { 108 std::array<uint8_t, maxHashSize> orgUsrPwdHash; 109 std::array<uint8_t, maxHashSize> orgAdminPwdHash; 110 std::array<uint8_t, maxSeedSize> seed; 111 std::string hashAlgo = ""; 112 try 113 { 114 nlohmann::json json = nullptr; 115 std::ifstream ifs(seedFile.c_str()); 116 if (ifs.is_open()) 117 { 118 try 119 { 120 json = nlohmann::json::parse(ifs, nullptr, false); 121 } 122 catch (const nlohmann::json::parse_error& e) 123 { 124 phosphor::logging::log<phosphor::logging::level::ERR>( 125 e.what()); 126 throw InternalFailure(); 127 } 128 129 if (json.is_discarded()) 130 { 131 return; 132 } 133 orgUsrPwdHash = json["UserPwdHash"]; 134 orgAdminPwdHash = json["AdminPwdHash"]; 135 seed = json["Seed"]; 136 hashAlgo = json["HashAlgo"]; 137 } 138 else 139 { 140 return; 141 } 142 } 143 catch (nlohmann::detail::exception& e) 144 { 145 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 146 throw InternalFailure(); 147 } 148 if (userName == "AdminPassword") 149 { 150 if (!isMatch(orgAdminPwdHash, seed, currentPassword, hashAlgo)) 151 { 152 throw InvalidCurrentPassword(); 153 } 154 } 155 else 156 { 157 if (!isMatch(orgUsrPwdHash, seed, currentPassword, hashAlgo)) 158 { 159 throw InvalidCurrentPassword(); 160 } 161 } 162 if (hashAlgo == "SHA256") 163 { 164 unsigned int mdLen = 32; 165 mNewPwdHash.fill(0); 166 167 if (!PKCS5_PBKDF2_HMAC( 168 reinterpret_cast<const char*>(newPassword.c_str()), 169 newPassword.length() + 1, 170 reinterpret_cast<const unsigned char*>(seed.data()), 171 seed.size(), iterValue, EVP_sha256(), mdLen, 172 mNewPwdHash.data())) 173 { 174 phosphor::logging::log<phosphor::logging::level::ERR>( 175 "Verify PKCS5_PBKDF2_HMAC_SHA256 Integrity Check failed"); 176 throw InternalFailure(); 177 } 178 } 179 if (hashAlgo == "SHA384") 180 { 181 unsigned int mdLen = 48; 182 mNewPwdHash.fill(0); 183 184 if (!PKCS5_PBKDF2_HMAC( 185 reinterpret_cast<const char*>(newPassword.c_str()), 186 newPassword.length() + 1, 187 reinterpret_cast<const unsigned char*>(seed.data()), 188 seed.size(), iterValue, EVP_sha384(), mdLen, 189 mNewPwdHash.data())) 190 { 191 phosphor::logging::log<phosphor::logging::level::ERR>( 192 "Verify PKCS5_PBKDF2_HMAC_SHA384 Integrity Check failed"); 193 throw InternalFailure(); 194 } 195 } 196 return; 197 } 198 throw InternalFailure(); 199 } 200 void Password::changePassword(std::string userName, std::string currentPassword, 201 std::string newPassword) 202 { 203 phosphor::logging::log<phosphor::logging::level::DEBUG>( 204 "BIOS config changePassword"); 205 verifyPassword(userName, currentPassword, newPassword); 206 207 std::ifstream fs(seedFile.c_str()); 208 nlohmann::json json = nullptr; 209 210 if (fs.is_open()) 211 { 212 try 213 { 214 json = nlohmann::json::parse(fs, nullptr, false); 215 } 216 catch (const nlohmann::json::parse_error& e) 217 { 218 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 219 throw InternalFailure(); 220 } 221 222 if (json.is_discarded()) 223 { 224 throw InternalFailure(); 225 } 226 json["AdminPwdHash"] = mNewPwdHash; 227 json["IsAdminPwdChanged"] = true; 228 229 std::ofstream ofs(seedFile.c_str(), std::ios::out); 230 const auto& writeData = json.dump(); 231 ofs << writeData; 232 ofs.close(); 233 } 234 else 235 { 236 phosphor::logging::log<phosphor::logging::level::DEBUG>( 237 "Cannot open file stream"); 238 throw InternalFailure(); 239 } 240 } 241 Password::Password(sdbusplus::asio::object_server& objectServer, 242 std::shared_ptr<sdbusplus::asio::connection>& systemBus) : 243 sdbusplus::xyz::openbmc_project::BIOSConfig::server::Password( 244 *systemBus, objectPathPwd), 245 objServer(objectServer), systemBus(systemBus) 246 { 247 phosphor::logging::log<phosphor::logging::level::DEBUG>( 248 "BIOS config password is runing"); 249 try 250 { 251 fs::path biosDir(BIOS_PERSIST_PATH); 252 fs::create_directories(biosDir); 253 seedFile = biosDir / biosSeedFile; 254 } 255 catch (const fs::filesystem_error& e) 256 { 257 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 258 throw InternalFailure(); 259 } 260 } 261 262 } // namespace bios_config_pwd 263 264 int main() 265 { 266 boost::asio::io_service io; 267 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); 268 269 systemBus->request_name(bios_config_pwd::servicePwd); 270 sdbusplus::asio::object_server objectServer(systemBus); 271 bios_config_pwd::Password password(objectServer, systemBus); 272 273 io.run(); 274 return 0; 275 } 276