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