1 /* 2 // Copyright (c) 2018 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 17 #include "config.h" 18 19 #include "users.hpp" 20 21 #include "totp.hpp" 22 #include "user_mgr.hpp" 23 24 #include <pwd.h> 25 #include <sys/types.h> 26 #include <sys/wait.h> 27 #include <unistd.h> 28 29 #include <phosphor-logging/elog-errors.hpp> 30 #include <phosphor-logging/elog.hpp> 31 #include <phosphor-logging/lg2.hpp> 32 #include <xyz/openbmc_project/Common/error.hpp> 33 #include <xyz/openbmc_project/User/Common/error.hpp> 34 35 #include <filesystem> 36 #include <fstream> 37 #include <map> 38 namespace phosphor 39 { 40 namespace user 41 { 42 43 using namespace phosphor::logging; 44 using InsufficientPermission = 45 sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission; 46 using InternalFailure = 47 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 48 using InvalidArgument = 49 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 50 using NoResource = 51 sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource; 52 using UnsupportedRequest = 53 sdbusplus::xyz::openbmc_project::Common::Error::UnsupportedRequest; 54 55 using Argument = xyz::openbmc_project::Common::InvalidArgument; 56 static constexpr auto authAppPath = "/usr/bin/google-authenticator"; 57 static constexpr auto secretKeyPath = "/home/{}/.google_authenticator"; 58 static constexpr auto secretKeyTempPath = 59 "/home/{}/.config/phosphor-user-manager/.google_authenticator.tmp"; 60 61 /** @brief Constructs Users object. 62 * 63 * @param[in] bus - sdbusplus handler 64 * @param[in] path - D-Bus path 65 * @param[in] groups - users group list 66 * @param[in] priv - user privilege 67 * @param[in] enabled - user enabled state 68 * @param[in] passwordExpiration - user password expiration Epoch time 69 * @param[in] parent - user manager - parent object 70 */ 71 Users::Users(sdbusplus::bus_t& bus, const char* path, 72 std::vector<std::string> groups, std::string priv, bool enabled, 73 std::optional<uint64_t> passwordExpiration, UserMgr& parent) : 74 Interfaces(bus, path, Interfaces::action::defer_emit), 75 userName(sdbusplus::message::object_path(path).filename()), manager(parent) 76 { 77 UsersIface::userPrivilege(priv, true); 78 UsersIface::userGroups(groups, true); 79 UsersIface::userEnabled(enabled, true); 80 if (passwordExpiration) 81 UsersIface::passwordExpiration(*passwordExpiration); 82 load(manager.getSerializer()); 83 this->emit_object_added(); 84 } 85 Users::~Users() 86 { 87 manager.getSerializer().erase(userName); 88 } 89 /** @brief delete user method. 90 * This method deletes the user as requested 91 * 92 */ 93 void Users::delete_(void) 94 { 95 manager.deleteUser(userName); 96 } 97 98 /** @brief update user privilege 99 * 100 * @param[in] value - User privilege 101 */ 102 std::string Users::userPrivilege(std::string value) 103 { 104 if (value == UsersIface::userPrivilege()) 105 { 106 return value; 107 } 108 manager.updateGroupsAndPriv(userName, UsersIface::userGroups(), value); 109 return UsersIface::userPrivilege(value); 110 } 111 112 void Users::setUserPrivilege(const std::string& value) 113 { 114 UsersIface::userPrivilege(value); 115 } 116 117 void Users::setUserGroups(const std::vector<std::string>& groups) 118 { 119 UsersIface::userGroups(groups); 120 } 121 122 /** @brief list user privilege 123 * 124 */ 125 std::string Users::userPrivilege(void) const 126 { 127 return UsersIface::userPrivilege(); 128 } 129 130 /** @brief update user groups 131 * 132 * @param[in] value - User groups 133 */ 134 std::vector<std::string> Users::userGroups(std::vector<std::string> value) 135 { 136 if (value == UsersIface::userGroups()) 137 { 138 return value; 139 } 140 std::sort(value.begin(), value.end()); 141 manager.updateGroupsAndPriv(userName, value, UsersIface::userPrivilege()); 142 return UsersIface::userGroups(value); 143 } 144 145 /** @brief list user groups 146 * 147 */ 148 std::vector<std::string> Users::userGroups(void) const 149 { 150 return UsersIface::userGroups(); 151 } 152 153 /** @brief lists user enabled state 154 * 155 */ 156 bool Users::userEnabled(void) const 157 { 158 return manager.isUserEnabled(userName); 159 } 160 161 void Users::setUserEnabled(bool value) 162 { 163 UsersIface::userEnabled(value); 164 } 165 166 /** @brief update user enabled state 167 * 168 * @param[in] value - bool value 169 */ 170 bool Users::userEnabled(bool value) 171 { 172 if (value == UsersIface::userEnabled()) 173 { 174 return value; 175 } 176 manager.userEnable(userName, value); 177 return UsersIface::userEnabled(value); 178 } 179 180 /** @brief lists user locked state for failed attempt 181 * 182 **/ 183 bool Users::userLockedForFailedAttempt(void) const 184 { 185 return manager.userLockedForFailedAttempt(userName); 186 } 187 188 /** @brief unlock user locked state for failed attempt 189 * 190 * @param[in]: value - false - unlock user account, true - no action taken 191 **/ 192 bool Users::userLockedForFailedAttempt(bool value) 193 { 194 if (value != false) 195 { 196 return userLockedForFailedAttempt(); 197 } 198 else 199 { 200 return manager.userLockedForFailedAttempt(userName, value); 201 } 202 } 203 204 /** @brief indicates if the user's password is expired 205 * 206 **/ 207 bool Users::userPasswordExpired(void) const 208 { 209 return manager.userPasswordExpired(userName); 210 } 211 bool changeFileOwnership(const std::string& userName) 212 { 213 // Get the user ID 214 passwd* pwd = getpwnam(userName.c_str()); 215 if (pwd == nullptr) 216 { 217 lg2::error("Failed to get user ID for user:{USER}", "USER", userName); 218 return false; 219 } 220 // Change the ownership of the file 221 // Change ownership recursively for the user's home directory 222 std::string homeDir = std::format("/home/{}/", userName); 223 for (const auto& entry : 224 std::filesystem::recursive_directory_iterator(homeDir)) 225 { 226 if (chown(entry.path().c_str(), pwd->pw_uid, pwd->pw_gid) != 0) 227 { 228 lg2::error("Ownership change error {PATH}", "PATH", 229 entry.path().string()); 230 return false; 231 } 232 } 233 return true; 234 } 235 bool Users::checkMfaStatus() const 236 { 237 return (manager.enabled() != MultiFactorAuthType::None && 238 Interfaces::bypassedProtocol() == MultiFactorAuthType::None); 239 } 240 std::string Users::createSecretKey() 241 { 242 if (!std::filesystem::exists(authAppPath)) 243 { 244 lg2::error("No authenticator app found at {PATH}", "PATH", authAppPath); 245 throw UnsupportedRequest(); 246 } 247 std::string path = std::format(secretKeyTempPath, userName); 248 if (!std::filesystem::exists(std::filesystem::path(path).parent_path())) 249 { 250 std::filesystem::create_directories( 251 std::filesystem::path(path).parent_path()); 252 } 253 /* 254 -u no-rate-limit 255 -W minimal-window 256 -Q qr-mode (NONE, ANSI, UTF8) 257 -t time-based 258 -f force file 259 -d disallow-reuse 260 -C no-confirm no confirmation required for code provisioned 261 */ 262 executeCmd(authAppPath, "-s", path.c_str(), "-u", "-W", "-Q", "NONE", "-t", 263 "-f", "-d", "-C"); 264 if (!std::filesystem::exists(path)) 265 { 266 lg2::error("Failed to create secret key for user {USER}", "USER", 267 userName); 268 throw UnsupportedRequest(); 269 } 270 std::ifstream file(path); 271 if (!file.is_open()) 272 { 273 lg2::error("Failed to open secret key file {PATH}", "PATH", path); 274 throw UnsupportedRequest(); 275 } 276 std::string secret; 277 std::getline(file, secret); 278 file.close(); 279 if (!changeFileOwnership(userName)) 280 { 281 throw UnsupportedRequest(); 282 } 283 return secret; 284 } 285 bool Users::verifyOTP(std::string otp) 286 { 287 if (Totp::verify(getUserName(), otp) == PAM_SUCCESS) 288 { 289 // If MFA is enabled for the user register the secret key 290 if (checkMfaStatus()) 291 { 292 try 293 { 294 std::filesystem::rename( 295 std::format(secretKeyTempPath, getUserName()), 296 std::format(secretKeyPath, getUserName())); 297 } 298 catch (const std::filesystem::filesystem_error& e) 299 { 300 lg2::error("Failed to rename file: {CODE}", "CODE", e); 301 return false; 302 } 303 } 304 else 305 { 306 std::filesystem::remove( 307 std::format(secretKeyTempPath, getUserName())); 308 } 309 return true; 310 } 311 return false; 312 } 313 static void clearSecretFile(const std::string& path) 314 { 315 if (std::filesystem::exists(path)) 316 { 317 std::filesystem::remove(path); 318 } 319 } 320 static void clearGoogleAuthenticator(Users& thisp) 321 { 322 clearSecretFile(std::format(secretKeyPath, thisp.getUserName())); 323 clearSecretFile(std::format(secretKeyTempPath, thisp.getUserName())); 324 } 325 static std::map<MultiFactorAuthType, std::function<void(Users&)>> 326 mfaBypassHandlers{{MultiFactorAuthType::GoogleAuthenticator, 327 clearGoogleAuthenticator}, 328 {MultiFactorAuthType::None, [](Users&) {}}}; 329 330 MultiFactorAuthType Users::bypassedProtocol(MultiFactorAuthType value, 331 bool skipSignal) 332 { 333 auto iter = mfaBypassHandlers.find(value); 334 if (iter != end(mfaBypassHandlers)) 335 { 336 iter->second(*this); 337 } 338 std::string path = std::format("{}/bypassedprotocol", getUserName()); 339 manager.getSerializer().serialize( 340 path, MultiFactorAuthConfiguration::convertTypeToString(value)); 341 manager.getSerializer().store(); 342 return Interfaces::bypassedProtocol(value, skipSignal); 343 } 344 345 bool Users::secretKeyIsValid() const 346 { 347 std::string path = std::format(secretKeyPath, getUserName()); 348 return std::filesystem::exists(path); 349 } 350 351 inline void googleAuthenticatorEnabled(Users& user, bool /*unused*/) 352 { 353 clearGoogleAuthenticator(user); 354 } 355 static std::map<MultiFactorAuthType, std::function<void(Users&, bool)>> 356 mfaEnableHandlers{{MultiFactorAuthType::GoogleAuthenticator, 357 googleAuthenticatorEnabled}, 358 {MultiFactorAuthType::None, [](Users&, bool) {}}}; 359 360 void Users::enableMultiFactorAuth(MultiFactorAuthType type, bool value) 361 { 362 auto iter = mfaEnableHandlers.find(type); 363 if (iter != end(mfaEnableHandlers)) 364 { 365 iter->second(*this, value); 366 } 367 } 368 bool Users::secretKeyGenerationRequired() const 369 { 370 return checkMfaStatus() && !secretKeyIsValid(); 371 } 372 void Users::clearSecretKey() 373 { 374 if (!checkMfaStatus()) 375 { 376 throw UnsupportedRequest(); 377 } 378 clearGoogleAuthenticator(*this); 379 } 380 381 void Users::load(JsonSerializer& ts) 382 { 383 std::optional<std::string> protocol; 384 std::string path = std::format("{}/bypassedprotocol", userName); 385 ts.deserialize(path, protocol); 386 if (protocol) 387 { 388 MultiFactorAuthType type = 389 MultiFactorAuthConfiguration::convertTypeFromString(*protocol); 390 bypassedProtocol(type, true); 391 return; 392 } 393 bypassedProtocol(MultiFactorAuthType::None, true); 394 ts.serialize(path, MultiFactorAuthConfiguration::convertTypeToString( 395 MultiFactorAuthType::None)); 396 } 397 398 /** @brief user password expiration Epoch time 399 * 400 **/ 401 uint64_t Users::passwordExpiration() const 402 { 403 return UsersIface::passwordExpiration(); 404 } 405 406 /** @brief update user password expiration Epoch time 407 * 408 **/ 409 uint64_t Users::passwordExpiration(uint64_t value) 410 { 411 manager.setPasswordExpiration(userName, value); 412 413 return UsersIface::passwordExpiration(value); 414 } 415 416 } // namespace user 417 } // namespace phosphor 418