Richard Marian Thomaiyar | 9f630d9 | 2018-05-24 10:49:10 +0530 | [diff] [blame] | 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 <shadow.h> |
| 18 | #include <unistd.h> |
| 19 | #include <sys/types.h> |
| 20 | #include <sys/wait.h> |
| 21 | #include <fstream> |
| 22 | #include <grp.h> |
| 23 | #include <pwd.h> |
| 24 | #include <regex> |
| 25 | #include <algorithm> |
| 26 | #include <numeric> |
| 27 | #include <boost/process/child.hpp> |
| 28 | #include <xyz/openbmc_project/Common/error.hpp> |
| 29 | #include <xyz/openbmc_project/User/Common/error.hpp> |
| 30 | #include <phosphor-logging/log.hpp> |
| 31 | #include <phosphor-logging/elog.hpp> |
| 32 | #include <phosphor-logging/elog-errors.hpp> |
| 33 | #include "shadowlock.hpp" |
| 34 | #include "file.hpp" |
| 35 | #include "user_mgr.hpp" |
| 36 | #include "users.hpp" |
| 37 | #include "config.h" |
| 38 | |
| 39 | namespace phosphor |
| 40 | { |
| 41 | namespace user |
| 42 | { |
| 43 | |
| 44 | static constexpr const char *passwdFileName = "/etc/passwd"; |
| 45 | static constexpr size_t ipmiMaxUsers = 15; |
| 46 | static constexpr size_t ipmiMaxUserNameLen = 16; |
| 47 | static constexpr size_t systemMaxUserNameLen = 30; |
| 48 | static constexpr size_t maxSystemUsers = 30; |
| 49 | static constexpr const char *grpSsh = "ssh"; |
| 50 | |
| 51 | using namespace phosphor::logging; |
| 52 | using InsufficientPermission = |
| 53 | sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission; |
| 54 | using InternalFailure = |
| 55 | sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; |
| 56 | using InvalidArgument = |
| 57 | sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; |
| 58 | using UserNameExists = |
| 59 | sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists; |
| 60 | using UserNameDoesNotExist = |
| 61 | sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameDoesNotExist; |
| 62 | using UserNameGroupFail = |
| 63 | sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameGroupFail; |
| 64 | |
| 65 | using NoResource = |
| 66 | sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource; |
| 67 | |
| 68 | using Argument = xyz::openbmc_project::Common::InvalidArgument; |
| 69 | |
| 70 | template <typename... ArgTypes> |
| 71 | static void executeCmd(const char *path, ArgTypes &&... tArgs) |
| 72 | { |
| 73 | boost::process::child execProg(path, const_cast<char *>(tArgs)...); |
| 74 | execProg.wait(); |
| 75 | int retCode = execProg.exit_code(); |
| 76 | if (retCode) |
| 77 | { |
| 78 | log<level::ERR>("Command execution failed", entry("PATH=%s", path), |
| 79 | entry("RETURN_CODE:%d", retCode)); |
| 80 | elog<InternalFailure>(); |
| 81 | } |
| 82 | return; |
| 83 | } |
| 84 | |
| 85 | static std::string getCSVFromVector(std::vector<std::string> vec) |
| 86 | { |
| 87 | switch (vec.size()) |
| 88 | { |
| 89 | case 0: |
| 90 | { |
| 91 | return ""; |
| 92 | } |
| 93 | break; |
| 94 | |
| 95 | case 1: |
| 96 | { |
| 97 | return std::string{vec[0]}; |
| 98 | } |
| 99 | break; |
| 100 | |
| 101 | default: |
| 102 | { |
| 103 | return std::accumulate( |
| 104 | std::next(vec.begin()), vec.end(), vec[0], |
| 105 | [](std::string a, std::string b) { return a + ',' + b; }); |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | static bool removeStringFromCSV(std::string &csvStr, const std::string &delStr) |
| 111 | { |
| 112 | std::string::size_type delStrPos = csvStr.find(delStr); |
| 113 | if (delStrPos != std::string::npos) |
| 114 | { |
| 115 | // need to also delete the comma char |
| 116 | if (delStrPos == 0) |
| 117 | { |
| 118 | csvStr.erase(delStrPos, delStr.size() + 1); |
| 119 | } |
| 120 | else |
| 121 | { |
| 122 | csvStr.erase(delStrPos - 1, delStr.size() + 1); |
| 123 | } |
| 124 | return true; |
| 125 | } |
| 126 | return false; |
| 127 | } |
| 128 | |
| 129 | bool UserMgr::isUserExist(const std::string &userName) |
| 130 | { |
| 131 | if (userName.empty()) |
| 132 | { |
| 133 | log<level::ERR>("User name is empty"); |
| 134 | elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"), |
| 135 | Argument::ARGUMENT_VALUE("Null")); |
| 136 | } |
| 137 | if (usersList.find(userName) == usersList.end()) |
| 138 | { |
| 139 | return false; |
| 140 | } |
| 141 | return true; |
| 142 | } |
| 143 | |
| 144 | void UserMgr::throwForUserDoesNotExist(const std::string &userName) |
| 145 | { |
| 146 | if (isUserExist(userName) == false) |
| 147 | { |
| 148 | log<level::ERR>("User does not exist", |
| 149 | entry("USER_NAME=%s", userName.c_str())); |
| 150 | elog<UserNameDoesNotExist>(); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | void UserMgr::throwForUserExists(const std::string &userName) |
| 155 | { |
| 156 | if (isUserExist(userName) == true) |
| 157 | { |
| 158 | log<level::ERR>("User already exists", |
| 159 | entry("USER_NAME=%s", userName.c_str())); |
| 160 | elog<UserNameExists>(); |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | void UserMgr::throwForUserNameConstraints( |
| 165 | const std::string &userName, const std::vector<std::string> &groupNames) |
| 166 | { |
| 167 | if (std::find(groupNames.begin(), groupNames.end(), "ipmi") != |
| 168 | groupNames.end()) |
| 169 | { |
| 170 | if (userName.length() > ipmiMaxUserNameLen) |
| 171 | { |
| 172 | log<level::ERR>("IPMI user name length limitation", |
| 173 | entry("SIZE=%d", userName.length())); |
| 174 | elog<UserNameGroupFail>( |
| 175 | xyz::openbmc_project::User::Common::UserNameGroupFail::REASON( |
| 176 | "IPMI length")); |
| 177 | } |
| 178 | } |
| 179 | if (userName.length() > systemMaxUserNameLen) |
| 180 | { |
| 181 | log<level::ERR>("User name length limitation", |
| 182 | entry("SIZE=%d", userName.length())); |
| 183 | elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"), |
| 184 | Argument::ARGUMENT_VALUE("Invalid length")); |
| 185 | } |
| 186 | if (!std::regex_match(userName.c_str(), |
| 187 | std::regex("[a-zA-z_][a-zA-Z_0-9]*"))) |
| 188 | { |
| 189 | log<level::ERR>("Invalid user name", |
| 190 | entry("USER_NAME=%s", userName.c_str())); |
| 191 | elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"), |
| 192 | Argument::ARGUMENT_VALUE("Invalid data")); |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | void UserMgr::throwForMaxGrpUserCount( |
| 197 | const std::vector<std::string> &groupNames) |
| 198 | { |
| 199 | if (std::find(groupNames.begin(), groupNames.end(), "ipmi") != |
| 200 | groupNames.end()) |
| 201 | { |
| 202 | if (getIpmiUsersCount() >= ipmiMaxUsers) |
| 203 | { |
| 204 | log<level::ERR>("IPMI user limit reached"); |
| 205 | elog<NoResource>( |
| 206 | xyz::openbmc_project::User::Common::NoResource::REASON( |
| 207 | "ipmi user count reached")); |
| 208 | } |
| 209 | } |
| 210 | else |
| 211 | { |
| 212 | if (usersList.size() > 0 && (usersList.size() - getIpmiUsersCount()) >= |
| 213 | (maxSystemUsers - ipmiMaxUsers)) |
| 214 | { |
| 215 | log<level::ERR>("Non-ipmi User limit reached"); |
| 216 | elog<NoResource>( |
| 217 | xyz::openbmc_project::User::Common::NoResource::REASON( |
| 218 | "Non-ipmi user count reached")); |
| 219 | } |
| 220 | } |
| 221 | return; |
| 222 | } |
| 223 | |
| 224 | void UserMgr::throwForInvalidPrivilege(const std::string &priv) |
| 225 | { |
| 226 | if (!priv.empty() && |
| 227 | (std::find(privMgr.begin(), privMgr.end(), priv) == privMgr.end())) |
| 228 | { |
| 229 | log<level::ERR>("Invalid privilege"); |
| 230 | elog<InvalidArgument>(Argument::ARGUMENT_NAME("Privilege"), |
| 231 | Argument::ARGUMENT_VALUE(priv.c_str())); |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | void UserMgr::throwForInvalidGroups(const std::vector<std::string> &groupNames) |
| 236 | { |
| 237 | for (auto &group : groupNames) |
| 238 | { |
| 239 | if (std::find(groupsMgr.begin(), groupsMgr.end(), group) == |
| 240 | groupsMgr.end()) |
| 241 | { |
| 242 | log<level::ERR>("Invalid Group Name listed"); |
| 243 | elog<InvalidArgument>(Argument::ARGUMENT_NAME("GroupName"), |
| 244 | Argument::ARGUMENT_VALUE(group.c_str())); |
| 245 | } |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | void UserMgr::createUser(std::string userName, |
| 250 | std::vector<std::string> groupNames, std::string priv, |
| 251 | bool enabled) |
| 252 | { |
| 253 | throwForInvalidPrivilege(priv); |
| 254 | throwForInvalidGroups(groupNames); |
| 255 | // All user management lock has to be based on /etc/shadow |
| 256 | phosphor::user::shadow::Lock lock(); |
| 257 | throwForUserExists(userName); |
| 258 | throwForUserNameConstraints(userName, groupNames); |
| 259 | throwForMaxGrpUserCount(groupNames); |
| 260 | |
| 261 | std::string groups = getCSVFromVector(groupNames); |
| 262 | bool sshRequested = removeStringFromCSV(groups, grpSsh); |
| 263 | |
| 264 | // treat privilege as a group - This is to avoid using different file to |
| 265 | // store the same. |
Richard Marian Thomaiyar | 2cb2e72 | 2018-09-27 14:22:42 +0530 | [diff] [blame^] | 266 | if (!priv.empty()) |
Richard Marian Thomaiyar | 9f630d9 | 2018-05-24 10:49:10 +0530 | [diff] [blame] | 267 | { |
Richard Marian Thomaiyar | 2cb2e72 | 2018-09-27 14:22:42 +0530 | [diff] [blame^] | 268 | if (groups.size() != 0) |
| 269 | { |
| 270 | groups += ","; |
| 271 | } |
| 272 | groups += priv; |
Richard Marian Thomaiyar | 9f630d9 | 2018-05-24 10:49:10 +0530 | [diff] [blame] | 273 | } |
Richard Marian Thomaiyar | 9f630d9 | 2018-05-24 10:49:10 +0530 | [diff] [blame] | 274 | try |
| 275 | { |
| 276 | executeCmd("/usr/sbin/useradd", userName.c_str(), "-G", groups.c_str(), |
| 277 | "-M", "-N", "-s", |
| 278 | (sshRequested ? "/bin/sh" : "/bin/nologin"), "-e", |
| 279 | (enabled ? "" : "1970-01-02")); |
| 280 | } |
| 281 | catch (const InternalFailure &e) |
| 282 | { |
| 283 | log<level::ERR>("Unable to create new user"); |
| 284 | elog<InternalFailure>(); |
| 285 | } |
| 286 | |
| 287 | // Add the users object before sending out the signal |
| 288 | std::string userObj = std::string(usersObjPath) + "/" + userName; |
| 289 | std::sort(groupNames.begin(), groupNames.end()); |
| 290 | usersList.emplace( |
| 291 | userName, std::move(std::make_unique<phosphor::user::Users>( |
| 292 | bus, userObj.c_str(), groupNames, priv, enabled, *this))); |
| 293 | |
| 294 | log<level::INFO>("User created successfully", |
| 295 | entry("USER_NAME=%s", userName.c_str())); |
| 296 | return; |
| 297 | } |
| 298 | |
| 299 | void UserMgr::deleteUser(std::string userName) |
| 300 | { |
| 301 | // All user management lock has to be based on /etc/shadow |
| 302 | phosphor::user::shadow::Lock lock(); |
| 303 | throwForUserDoesNotExist(userName); |
| 304 | try |
| 305 | { |
| 306 | executeCmd("/usr/sbin/userdel", userName.c_str()); |
| 307 | } |
| 308 | catch (const InternalFailure &e) |
| 309 | { |
| 310 | log<level::ERR>("User delete failed", |
| 311 | entry("USER_NAME=%s", userName.c_str())); |
| 312 | elog<InternalFailure>(); |
| 313 | } |
| 314 | |
| 315 | usersList.erase(userName); |
| 316 | |
| 317 | log<level::INFO>("User deleted successfully", |
| 318 | entry("USER_NAME=%s", userName.c_str())); |
| 319 | return; |
| 320 | } |
| 321 | |
| 322 | void UserMgr::renameUser(std::string userName, std::string newUserName) |
| 323 | { |
| 324 | // All user management lock has to be based on /etc/shadow |
| 325 | phosphor::user::shadow::Lock lock(); |
| 326 | throwForUserDoesNotExist(userName); |
| 327 | throwForUserExists(newUserName); |
| 328 | throwForUserNameConstraints(newUserName, |
| 329 | usersList[userName].get()->userGroups()); |
| 330 | try |
| 331 | { |
| 332 | executeCmd("/usr/sbin/usermod", "-l", newUserName.c_str(), |
| 333 | userName.c_str()); |
| 334 | } |
| 335 | catch (const InternalFailure &e) |
| 336 | { |
| 337 | log<level::ERR>("User rename failed", |
| 338 | entry("USER_NAME=%s", userName.c_str())); |
| 339 | elog<InternalFailure>(); |
| 340 | } |
| 341 | const auto &user = usersList[userName]; |
| 342 | std::string priv = user.get()->userPrivilege(); |
| 343 | std::vector<std::string> groupNames = user.get()->userGroups(); |
| 344 | bool enabled = user.get()->userEnabled(); |
| 345 | std::string newUserObj = std::string(usersObjPath) + "/" + newUserName; |
| 346 | // Special group 'ipmi' needs a way to identify user renamed, in order to |
| 347 | // update encrypted password. It can't rely only on InterfacesRemoved & |
| 348 | // InterfacesAdded. So first send out userRenamed signal. |
| 349 | this->userRenamed(userName, newUserName); |
| 350 | usersList.erase(userName); |
| 351 | usersList.emplace( |
| 352 | newUserName, |
| 353 | std::move(std::make_unique<phosphor::user::Users>( |
| 354 | bus, newUserObj.c_str(), groupNames, priv, enabled, *this))); |
| 355 | return; |
| 356 | } |
| 357 | |
| 358 | void UserMgr::updateGroupsAndPriv(const std::string &userName, |
| 359 | const std::vector<std::string> &groupNames, |
| 360 | const std::string &priv) |
| 361 | { |
| 362 | throwForInvalidPrivilege(priv); |
| 363 | throwForInvalidGroups(groupNames); |
| 364 | // All user management lock has to be based on /etc/shadow |
| 365 | phosphor::user::shadow::Lock lock(); |
| 366 | throwForUserDoesNotExist(userName); |
| 367 | const std::vector<std::string> &oldGroupNames = |
| 368 | usersList[userName].get()->userGroups(); |
| 369 | std::vector<std::string> groupDiff; |
| 370 | // Note: already dealing with sorted group lists. |
| 371 | std::set_symmetric_difference(oldGroupNames.begin(), oldGroupNames.end(), |
| 372 | groupNames.begin(), groupNames.end(), |
| 373 | std::back_inserter(groupDiff)); |
| 374 | if (std::find(groupDiff.begin(), groupDiff.end(), "ipmi") != |
| 375 | groupDiff.end()) |
| 376 | { |
| 377 | throwForUserNameConstraints(userName, groupNames); |
| 378 | throwForMaxGrpUserCount(groupNames); |
| 379 | } |
| 380 | |
| 381 | std::string groups = getCSVFromVector(groupNames); |
| 382 | bool sshRequested = removeStringFromCSV(groups, grpSsh); |
| 383 | |
| 384 | // treat privilege as a group - This is to avoid using different file to |
| 385 | // store the same. |
Richard Marian Thomaiyar | 2cb2e72 | 2018-09-27 14:22:42 +0530 | [diff] [blame^] | 386 | if (!priv.empty()) |
Richard Marian Thomaiyar | 9f630d9 | 2018-05-24 10:49:10 +0530 | [diff] [blame] | 387 | { |
Richard Marian Thomaiyar | 2cb2e72 | 2018-09-27 14:22:42 +0530 | [diff] [blame^] | 388 | if (groups.size() != 0) |
| 389 | { |
| 390 | groups += ","; |
| 391 | } |
| 392 | groups += priv; |
Richard Marian Thomaiyar | 9f630d9 | 2018-05-24 10:49:10 +0530 | [diff] [blame] | 393 | } |
Richard Marian Thomaiyar | 9f630d9 | 2018-05-24 10:49:10 +0530 | [diff] [blame] | 394 | try |
| 395 | { |
| 396 | executeCmd("/usr/sbin/usermod", userName.c_str(), "-G", groups.c_str(), |
| 397 | "-s", (sshRequested ? "/bin/sh" : "/bin/nologin")); |
| 398 | } |
| 399 | catch (const InternalFailure &e) |
| 400 | { |
| 401 | log<level::ERR>("Unable to modify user privilege / groups"); |
| 402 | elog<InternalFailure>(); |
| 403 | } |
| 404 | |
| 405 | log<level::INFO>("User groups / privilege updated successfully", |
| 406 | entry("USER_NAME=%s", userName.c_str())); |
| 407 | return; |
| 408 | } |
| 409 | |
| 410 | void UserMgr::userEnable(const std::string &userName, bool enabled) |
| 411 | { |
| 412 | // All user management lock has to be based on /etc/shadow |
| 413 | phosphor::user::shadow::Lock lock(); |
| 414 | throwForUserDoesNotExist(userName); |
| 415 | try |
| 416 | { |
| 417 | executeCmd("/usr/sbin/usermod", userName.c_str(), "-e", |
| 418 | (enabled ? "" : "1970-01-02")); |
| 419 | } |
| 420 | catch (const InternalFailure &e) |
| 421 | { |
| 422 | log<level::ERR>("Unable to modify user enabled state"); |
| 423 | elog<InternalFailure>(); |
| 424 | } |
| 425 | |
| 426 | log<level::INFO>("User enabled/disabled state updated successfully", |
| 427 | entry("USER_NAME=%s", userName.c_str()), |
| 428 | entry("ENABLED=%d", enabled)); |
| 429 | return; |
| 430 | } |
| 431 | |
| 432 | UserSSHLists UserMgr::getUserAndSshGrpList() |
| 433 | { |
| 434 | // All user management lock has to be based on /etc/shadow |
| 435 | phosphor::user::shadow::Lock lock(); |
| 436 | |
| 437 | std::vector<std::string> userList; |
| 438 | std::vector<std::string> sshUsersList; |
| 439 | struct passwd pw, *pwp = nullptr; |
| 440 | std::array<char, 1024> buffer{}; |
| 441 | |
| 442 | phosphor::user::File passwd(passwdFileName, "r"); |
| 443 | if ((passwd)() == NULL) |
| 444 | { |
| 445 | log<level::ERR>("Error opening the passwd file"); |
| 446 | elog<InternalFailure>(); |
| 447 | } |
| 448 | |
| 449 | while (true) |
| 450 | { |
| 451 | auto r = fgetpwent_r((passwd)(), &pw, buffer.data(), buffer.max_size(), |
| 452 | &pwp); |
| 453 | if ((r != 0) || (pwp == NULL)) |
| 454 | { |
| 455 | // Any error, break the loop. |
| 456 | break; |
| 457 | } |
| 458 | // All users whose UID >= 1000 and < 65534 |
| 459 | if ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534)) |
| 460 | { |
| 461 | std::string userName(pwp->pw_name); |
| 462 | userList.emplace_back(userName); |
| 463 | |
| 464 | // ssh doesn't have separate group. Check login shell entry to |
| 465 | // get all users list which are member of ssh group. |
| 466 | std::string loginShell(pwp->pw_shell); |
| 467 | if (loginShell == "/bin/sh") |
| 468 | { |
| 469 | sshUsersList.emplace_back(userName); |
| 470 | } |
| 471 | } |
| 472 | } |
| 473 | endpwent(); |
| 474 | return std::make_pair(std::move(userList), std::move(sshUsersList)); |
| 475 | } |
| 476 | |
| 477 | size_t UserMgr::getIpmiUsersCount() |
| 478 | { |
| 479 | std::vector<std::string> userList = getUsersInGroup("ipmi"); |
| 480 | return userList.size(); |
| 481 | } |
| 482 | |
| 483 | bool UserMgr::isUserEnabled(const std::string &userName) |
| 484 | { |
| 485 | // All user management lock has to be based on /etc/shadow |
| 486 | phosphor::user::shadow::Lock lock(); |
| 487 | std::array<char, 4096> buffer{}; |
| 488 | struct spwd spwd; |
| 489 | struct spwd *resultPtr = nullptr; |
| 490 | int status = getspnam_r(userName.c_str(), &spwd, buffer.data(), |
| 491 | buffer.max_size(), &resultPtr); |
| 492 | if (!status && (&spwd == resultPtr)) |
| 493 | { |
| 494 | if (resultPtr->sp_expire >= 0) |
| 495 | { |
| 496 | return false; // user locked out |
| 497 | } |
| 498 | return true; |
| 499 | } |
| 500 | return false; // assume user is disabled for any error. |
| 501 | } |
| 502 | |
| 503 | std::vector<std::string> UserMgr::getUsersInGroup(const std::string &groupName) |
| 504 | { |
| 505 | std::vector<std::string> usersInGroup; |
| 506 | // Should be more than enough to get the pwd structure. |
| 507 | std::array<char, 4096> buffer{}; |
| 508 | struct group grp; |
| 509 | struct group *resultPtr = nullptr; |
| 510 | |
| 511 | int status = getgrnam_r(groupName.c_str(), &grp, buffer.data(), |
| 512 | buffer.max_size(), &resultPtr); |
| 513 | |
| 514 | if (!status && (&grp == resultPtr)) |
| 515 | { |
| 516 | for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem)) |
| 517 | { |
| 518 | usersInGroup.emplace_back(*(grp.gr_mem)); |
| 519 | } |
| 520 | } |
| 521 | else |
| 522 | { |
| 523 | log<level::ERR>("Group not found", |
| 524 | entry("GROUP=%s", groupName.c_str())); |
| 525 | // Don't throw error, just return empty userList - fallback |
| 526 | } |
| 527 | return usersInGroup; |
| 528 | } |
| 529 | |
| 530 | void UserMgr::initUserObjects(void) |
| 531 | { |
| 532 | // All user management lock has to be based on /etc/shadow |
| 533 | phosphor::user::shadow::Lock lock(); |
| 534 | std::vector<std::string> userNameList; |
| 535 | std::vector<std::string> sshGrpUsersList; |
| 536 | UserSSHLists userSSHLists = getUserAndSshGrpList(); |
| 537 | userNameList = std::move(userSSHLists.first); |
| 538 | sshGrpUsersList = std::move(userSSHLists.second); |
| 539 | |
| 540 | if (!userNameList.empty()) |
| 541 | { |
| 542 | std::map<std::string, std::vector<std::string>> groupLists; |
| 543 | for (auto &grp : groupsMgr) |
| 544 | { |
| 545 | if (grp == grpSsh) |
| 546 | { |
| 547 | groupLists.emplace(grp, sshGrpUsersList); |
| 548 | } |
| 549 | else |
| 550 | { |
| 551 | std::vector<std::string> grpUsersList = getUsersInGroup(grp); |
| 552 | groupLists.emplace(grp, grpUsersList); |
| 553 | } |
| 554 | } |
| 555 | for (auto &grp : privMgr) |
| 556 | { |
| 557 | std::vector<std::string> grpUsersList = getUsersInGroup(grp); |
| 558 | groupLists.emplace(grp, grpUsersList); |
| 559 | } |
| 560 | |
| 561 | for (auto &user : userNameList) |
| 562 | { |
| 563 | std::vector<std::string> userGroups; |
| 564 | std::string userPriv; |
| 565 | for (const auto &grp : groupLists) |
| 566 | { |
| 567 | std::vector<std::string> tempGrp = grp.second; |
| 568 | if (std::find(tempGrp.begin(), tempGrp.end(), user) != |
| 569 | tempGrp.end()) |
| 570 | { |
| 571 | if (std::find(privMgr.begin(), privMgr.end(), grp.first) != |
| 572 | privMgr.end()) |
| 573 | { |
| 574 | userPriv = grp.first; |
| 575 | } |
| 576 | else |
| 577 | { |
| 578 | userGroups.emplace_back(grp.first); |
| 579 | } |
| 580 | } |
| 581 | } |
| 582 | // Add user objects to the Users path. |
| 583 | auto objPath = std::string(usersObjPath) + "/" + user; |
| 584 | std::sort(userGroups.begin(), userGroups.end()); |
| 585 | usersList.emplace(user, |
| 586 | std::move(std::make_unique<phosphor::user::Users>( |
| 587 | bus, objPath.c_str(), userGroups, userPriv, |
| 588 | isUserEnabled(user), *this))); |
| 589 | } |
| 590 | } |
| 591 | } |
| 592 | |
| 593 | UserMgr::UserMgr(sdbusplus::bus::bus &bus, const char *path) : |
| 594 | UserMgrIface(bus, path), bus(bus), path(path) |
| 595 | { |
| 596 | UserMgrIface::allPrivileges(privMgr); |
| 597 | std::sort(groupsMgr.begin(), groupsMgr.end()); |
| 598 | UserMgrIface::allGroups(groupsMgr); |
| 599 | initUserObjects(); |
| 600 | } |
| 601 | |
| 602 | } // namespace user |
| 603 | } // namespace phosphor |