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