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 "user_mgr.hpp" 20 21 #include "file.hpp" 22 #include "shadowlock.hpp" 23 #include "users.hpp" 24 25 #include <grp.h> 26 #include <pwd.h> 27 #include <shadow.h> 28 #include <sys/types.h> 29 #include <sys/wait.h> 30 #include <time.h> 31 #include <unistd.h> 32 33 #include <boost/algorithm/string/split.hpp> 34 #include <phosphor-logging/elog-errors.hpp> 35 #include <phosphor-logging/elog.hpp> 36 #include <phosphor-logging/log.hpp> 37 #include <xyz/openbmc_project/Common/error.hpp> 38 #include <xyz/openbmc_project/User/Common/error.hpp> 39 40 #include <algorithm> 41 #include <array> 42 #include <ctime> 43 #include <fstream> 44 #include <numeric> 45 #include <regex> 46 #include <span> 47 #include <string> 48 #include <string_view> 49 #include <vector> 50 51 namespace phosphor 52 { 53 namespace user 54 { 55 56 static constexpr const char* passwdFileName = "/etc/passwd"; 57 static constexpr size_t ipmiMaxUserNameLen = 16; 58 static constexpr size_t systemMaxUserNameLen = 30; 59 static constexpr const char* grpSsh = "ssh"; 60 static constexpr int success = 0; 61 static constexpr int failure = -1; 62 63 // pam modules related 64 static constexpr const char* pamTally2 = "pam_tally2.so"; 65 static constexpr const char* pamCrackLib = "pam_cracklib.so"; 66 static constexpr const char* pamPWHistory = "pam_pwhistory.so"; 67 static constexpr const char* minPasswdLenProp = "minlen"; 68 static constexpr const char* remOldPasswdCount = "remember"; 69 static constexpr const char* maxFailedAttempt = "deny"; 70 static constexpr const char* unlockTimeout = "unlock_time"; 71 static constexpr const char* defaultPamPasswdConfigFile = 72 "/etc/pam.d/common-password"; 73 static constexpr const char* defaultPamAuthConfigFile = 74 "/etc/pam.d/common-auth"; 75 76 // Object Manager related 77 static constexpr const char* ldapMgrObjBasePath = 78 "/xyz/openbmc_project/user/ldap"; 79 80 // Object Mapper related 81 static constexpr const char* objMapperService = 82 "xyz.openbmc_project.ObjectMapper"; 83 static constexpr const char* objMapperPath = 84 "/xyz/openbmc_project/object_mapper"; 85 static constexpr const char* objMapperInterface = 86 "xyz.openbmc_project.ObjectMapper"; 87 88 using namespace phosphor::logging; 89 using InsufficientPermission = 90 sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission; 91 using InternalFailure = 92 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 93 using InvalidArgument = 94 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 95 using UserNameExists = 96 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists; 97 using UserNameDoesNotExist = 98 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameDoesNotExist; 99 using UserNameGroupFail = 100 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameGroupFail; 101 using NoResource = 102 sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource; 103 using Argument = xyz::openbmc_project::Common::InvalidArgument; 104 using GroupNameExists = 105 sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists; 106 using GroupNameDoesNotExists = 107 sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameDoesNotExist; 108 109 namespace 110 { 111 112 // The hardcoded groups in OpenBMC projects 113 constexpr std::array<const char*, 4> predefinedGroups = {"web", "redfish", 114 "ipmi", "ssh"}; 115 116 // These prefixes are for Dynamic Redfish authorization. See 117 // https://github.com/openbmc/docs/blob/master/designs/redfish-authorization.md 118 119 // Base role and base privileges are added by Redfish implementation (e.g., 120 // BMCWeb) at compile time 121 constexpr std::array<const char*, 4> allowedGroupPrefix = { 122 "openbmc_rfr_", // OpenBMC Redfish Base Role 123 "openbmc_rfp_", // OpenBMC Redfish Base Privileges 124 "openbmc_orfr_", // OpenBMC Redfish OEM Role 125 "openbmc_orfp_", // OpenBMC Redfish OEM Privileges 126 }; 127 128 void checkAndThrowsForGroupChangeAllowed(const std::string& groupName) 129 { 130 bool allowed = false; 131 for (std::string_view prefix : allowedGroupPrefix) 132 { 133 if (groupName.starts_with(prefix)) 134 { 135 allowed = true; 136 break; 137 } 138 } 139 if (!allowed) 140 { 141 log<level::ERR>("Invalid group name; not in the allowed list", 142 entry("GroupName=%s", groupName.c_str())); 143 elog<InvalidArgument>(Argument::ARGUMENT_NAME("Group Name"), 144 Argument::ARGUMENT_VALUE(groupName.c_str())); 145 } 146 } 147 148 } // namespace 149 150 std::string getCSVFromVector(std::span<const std::string> vec) 151 { 152 if (vec.empty()) 153 { 154 return ""; 155 } 156 return std::accumulate(std::next(vec.begin()), vec.end(), vec[0], 157 [](std::string&& val, std::string_view element) { 158 val += ','; 159 val += element; 160 return val; 161 }); 162 } 163 164 bool removeStringFromCSV(std::string& csvStr, const std::string& delStr) 165 { 166 std::string::size_type delStrPos = csvStr.find(delStr); 167 if (delStrPos != std::string::npos) 168 { 169 // need to also delete the comma char 170 if (delStrPos == 0) 171 { 172 csvStr.erase(delStrPos, delStr.size() + 1); 173 } 174 else 175 { 176 csvStr.erase(delStrPos - 1, delStr.size() + 1); 177 } 178 return true; 179 } 180 return false; 181 } 182 183 bool UserMgr::isUserExist(const std::string& userName) 184 { 185 if (userName.empty()) 186 { 187 log<level::ERR>("User name is empty"); 188 elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"), 189 Argument::ARGUMENT_VALUE("Null")); 190 } 191 if (usersList.find(userName) == usersList.end()) 192 { 193 return false; 194 } 195 return true; 196 } 197 198 void UserMgr::throwForUserDoesNotExist(const std::string& userName) 199 { 200 if (!isUserExist(userName)) 201 { 202 log<level::ERR>("User does not exist", 203 entry("USER_NAME=%s", userName.c_str())); 204 elog<UserNameDoesNotExist>(); 205 } 206 } 207 208 void UserMgr::checkAndThrowForDisallowedGroupCreation( 209 const std::string& groupName) 210 { 211 if (groupName.size() > maxSystemGroupNameLength || 212 !std::regex_match(groupName.c_str(), 213 std::regex("[a-zA-z_][a-zA-Z_0-9]*"))) 214 { 215 log<level::ERR>("Invalid group name", 216 entry("GroupName=%s", groupName.c_str())); 217 elog<InvalidArgument>(Argument::ARGUMENT_NAME("Group Name"), 218 Argument::ARGUMENT_VALUE(groupName.c_str())); 219 } 220 checkAndThrowsForGroupChangeAllowed(groupName); 221 } 222 223 void UserMgr::throwForUserExists(const std::string& userName) 224 { 225 if (isUserExist(userName)) 226 { 227 log<level::ERR>("User already exists", 228 entry("USER_NAME=%s", userName.c_str())); 229 elog<UserNameExists>(); 230 } 231 } 232 233 void UserMgr::throwForUserNameConstraints( 234 const std::string& userName, const std::vector<std::string>& groupNames) 235 { 236 if (std::find(groupNames.begin(), groupNames.end(), "ipmi") != 237 groupNames.end()) 238 { 239 if (userName.length() > ipmiMaxUserNameLen) 240 { 241 log<level::ERR>("IPMI user name length limitation", 242 entry("SIZE=%d", userName.length())); 243 elog<UserNameGroupFail>( 244 xyz::openbmc_project::User::Common::UserNameGroupFail::REASON( 245 "IPMI length")); 246 } 247 } 248 if (userName.length() > systemMaxUserNameLen) 249 { 250 log<level::ERR>("User name length limitation", 251 entry("SIZE=%d", userName.length())); 252 elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"), 253 Argument::ARGUMENT_VALUE("Invalid length")); 254 } 255 if (!std::regex_match(userName.c_str(), 256 std::regex("[a-zA-z_][a-zA-Z_0-9]*"))) 257 { 258 log<level::ERR>("Invalid user name", 259 entry("USER_NAME=%s", userName.c_str())); 260 elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"), 261 Argument::ARGUMENT_VALUE("Invalid data")); 262 } 263 } 264 265 void UserMgr::throwForMaxGrpUserCount( 266 const std::vector<std::string>& groupNames) 267 { 268 if (std::find(groupNames.begin(), groupNames.end(), "ipmi") != 269 groupNames.end()) 270 { 271 if (getIpmiUsersCount() >= ipmiMaxUsers) 272 { 273 log<level::ERR>("IPMI user limit reached"); 274 elog<NoResource>( 275 xyz::openbmc_project::User::Common::NoResource::REASON( 276 "ipmi user count reached")); 277 } 278 } 279 else 280 { 281 if (usersList.size() > 0 && (usersList.size() - getIpmiUsersCount()) >= 282 (maxSystemUsers - ipmiMaxUsers)) 283 { 284 log<level::ERR>("Non-ipmi User limit reached"); 285 elog<NoResource>( 286 xyz::openbmc_project::User::Common::NoResource::REASON( 287 "Non-ipmi user count reached")); 288 } 289 } 290 return; 291 } 292 293 void UserMgr::throwForInvalidPrivilege(const std::string& priv) 294 { 295 if (!priv.empty() && 296 (std::find(privMgr.begin(), privMgr.end(), priv) == privMgr.end())) 297 { 298 log<level::ERR>("Invalid privilege"); 299 elog<InvalidArgument>(Argument::ARGUMENT_NAME("Privilege"), 300 Argument::ARGUMENT_VALUE(priv.c_str())); 301 } 302 } 303 304 void UserMgr::throwForInvalidGroups(const std::vector<std::string>& groupNames) 305 { 306 for (auto& group : groupNames) 307 { 308 if (std::find(groupsMgr.begin(), groupsMgr.end(), group) == 309 groupsMgr.end()) 310 { 311 log<level::ERR>("Invalid Group Name listed"); 312 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GroupName"), 313 Argument::ARGUMENT_VALUE(group.c_str())); 314 } 315 } 316 } 317 318 std::vector<std::string> UserMgr::readAllGroupsOnSystem() 319 { 320 std::vector<std::string> allGroups = {predefinedGroups.begin(), 321 predefinedGroups.end()}; 322 // rewinds to the beginning of the group database 323 setgrent(); 324 struct group* gr = getgrent(); 325 while (gr != nullptr) 326 { 327 std::string group(gr->gr_name); 328 for (std::string_view prefix : allowedGroupPrefix) 329 { 330 if (group.starts_with(prefix)) 331 { 332 allGroups.push_back(gr->gr_name); 333 } 334 } 335 gr = getgrent(); 336 } 337 // close the group database 338 endgrent(); 339 return allGroups; 340 } 341 342 void UserMgr::createUser(std::string userName, 343 std::vector<std::string> groupNames, std::string priv, 344 bool enabled) 345 { 346 throwForInvalidPrivilege(priv); 347 throwForInvalidGroups(groupNames); 348 // All user management lock has to be based on /etc/shadow 349 // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; 350 throwForUserExists(userName); 351 throwForUserNameConstraints(userName, groupNames); 352 throwForMaxGrpUserCount(groupNames); 353 354 std::string groups = getCSVFromVector(groupNames); 355 bool sshRequested = removeStringFromCSV(groups, grpSsh); 356 357 // treat privilege as a group - This is to avoid using different file to 358 // store the same. 359 if (!priv.empty()) 360 { 361 if (groups.size() != 0) 362 { 363 groups += ","; 364 } 365 groups += priv; 366 } 367 try 368 { 369 executeUserAdd(userName.c_str(), groups.c_str(), sshRequested, enabled); 370 } 371 catch (const InternalFailure& e) 372 { 373 log<level::ERR>("Unable to create new user"); 374 elog<InternalFailure>(); 375 } 376 377 // Add the users object before sending out the signal 378 sdbusplus::message::object_path tempObjPath(usersObjPath); 379 tempObjPath /= userName; 380 std::string userObj(tempObjPath); 381 std::sort(groupNames.begin(), groupNames.end()); 382 usersList.emplace( 383 userName, std::make_unique<phosphor::user::Users>( 384 bus, userObj.c_str(), groupNames, priv, enabled, *this)); 385 386 log<level::INFO>("User created successfully", 387 entry("USER_NAME=%s", userName.c_str())); 388 return; 389 } 390 391 void UserMgr::deleteUser(std::string userName) 392 { 393 // All user management lock has to be based on /etc/shadow 394 // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; 395 throwForUserDoesNotExist(userName); 396 try 397 { 398 executeUserDelete(userName.c_str()); 399 } 400 catch (const InternalFailure& e) 401 { 402 log<level::ERR>("User delete failed", 403 entry("USER_NAME=%s", userName.c_str())); 404 elog<InternalFailure>(); 405 } 406 407 usersList.erase(userName); 408 409 log<level::INFO>("User deleted successfully", 410 entry("USER_NAME=%s", userName.c_str())); 411 return; 412 } 413 414 void UserMgr::checkDeleteGroupConstraints(const std::string& groupName) 415 { 416 if (std::find(groupsMgr.begin(), groupsMgr.end(), groupName) == 417 groupsMgr.end()) 418 { 419 log<level::ERR>("Group already exists", 420 entry("GROUP_NAME=%s", groupName.c_str())); 421 elog<GroupNameDoesNotExists>(); 422 } 423 checkAndThrowsForGroupChangeAllowed(groupName); 424 } 425 426 void UserMgr::deleteGroup(std::string groupName) 427 { 428 checkDeleteGroupConstraints(groupName); 429 try 430 { 431 executeGroupDeletion(groupName.c_str()); 432 } 433 catch (const InternalFailure& e) 434 { 435 log<level::ERR>("Group delete failed", 436 entry("GROUP_NAME=%s", groupName.c_str())); 437 elog<InternalFailure>(); 438 } 439 440 groupsMgr.erase(std::find(groupsMgr.begin(), groupsMgr.end(), groupName)); 441 UserMgrIface::allGroups(groupsMgr); 442 log<level::INFO>("Group deleted successfully", 443 entry("GROUP_NAME=%s", groupName.c_str())); 444 } 445 446 void UserMgr::checkCreateGroupConstraints(const std::string& groupName) 447 { 448 if (std::find(groupsMgr.begin(), groupsMgr.end(), groupName) != 449 groupsMgr.end()) 450 { 451 log<level::ERR>("Group already exists", 452 entry("GROUP_NAME=%s", groupName.c_str())); 453 elog<GroupNameExists>(); 454 } 455 checkAndThrowForDisallowedGroupCreation(groupName); 456 if (groupsMgr.size() >= maxSystemGroupCount) 457 { 458 log<level::ERR>("Group limit reached"); 459 elog<NoResource>(xyz::openbmc_project::User::Common::NoResource::REASON( 460 "Group limit reached")); 461 } 462 } 463 464 void UserMgr::createGroup(std::string groupName) 465 { 466 checkCreateGroupConstraints(groupName); 467 try 468 { 469 executeGroupCreation(groupName.c_str()); 470 } 471 catch (const InternalFailure& e) 472 { 473 log<level::ERR>("Group create failed", 474 entry("GROUP_NAME=%s", groupName.c_str())); 475 elog<InternalFailure>(); 476 } 477 groupsMgr.push_back(groupName); 478 UserMgrIface::allGroups(groupsMgr); 479 } 480 481 void UserMgr::renameUser(std::string userName, std::string newUserName) 482 { 483 // All user management lock has to be based on /etc/shadow 484 // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; 485 throwForUserDoesNotExist(userName); 486 throwForUserExists(newUserName); 487 throwForUserNameConstraints(newUserName, 488 usersList[userName].get()->userGroups()); 489 try 490 { 491 executeUserRename(userName.c_str(), newUserName.c_str()); 492 } 493 catch (const InternalFailure& e) 494 { 495 log<level::ERR>("User rename failed", 496 entry("USER_NAME=%s", userName.c_str())); 497 elog<InternalFailure>(); 498 } 499 const auto& user = usersList[userName]; 500 std::string priv = user.get()->userPrivilege(); 501 std::vector<std::string> groupNames = user.get()->userGroups(); 502 bool enabled = user.get()->userEnabled(); 503 sdbusplus::message::object_path tempObjPath(usersObjPath); 504 tempObjPath /= newUserName; 505 std::string newUserObj(tempObjPath); 506 // Special group 'ipmi' needs a way to identify user renamed, in order to 507 // update encrypted password. It can't rely only on InterfacesRemoved & 508 // InterfacesAdded. So first send out userRenamed signal. 509 this->userRenamed(userName, newUserName); 510 usersList.erase(userName); 511 usersList.emplace(newUserName, std::make_unique<phosphor::user::Users>( 512 bus, newUserObj.c_str(), groupNames, 513 priv, enabled, *this)); 514 return; 515 } 516 517 void UserMgr::updateGroupsAndPriv(const std::string& userName, 518 std::vector<std::string> groupNames, 519 const std::string& priv) 520 { 521 throwForInvalidPrivilege(priv); 522 throwForInvalidGroups(groupNames); 523 // All user management lock has to be based on /etc/shadow 524 // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; 525 throwForUserDoesNotExist(userName); 526 const std::vector<std::string>& oldGroupNames = 527 usersList[userName].get()->userGroups(); 528 std::vector<std::string> groupDiff; 529 // Note: already dealing with sorted group lists. 530 std::set_symmetric_difference(oldGroupNames.begin(), oldGroupNames.end(), 531 groupNames.begin(), groupNames.end(), 532 std::back_inserter(groupDiff)); 533 if (std::find(groupDiff.begin(), groupDiff.end(), "ipmi") != 534 groupDiff.end()) 535 { 536 throwForUserNameConstraints(userName, groupNames); 537 throwForMaxGrpUserCount(groupNames); 538 } 539 540 std::string groups = getCSVFromVector(groupNames); 541 bool sshRequested = removeStringFromCSV(groups, grpSsh); 542 543 // treat privilege as a group - This is to avoid using different file to 544 // store the same. 545 if (!priv.empty()) 546 { 547 if (groups.size() != 0) 548 { 549 groups += ","; 550 } 551 groups += priv; 552 } 553 try 554 { 555 executeUserModify(userName.c_str(), groups.c_str(), sshRequested); 556 } 557 catch (const InternalFailure& e) 558 { 559 log<level::ERR>("Unable to modify user privilege / groups"); 560 elog<InternalFailure>(); 561 } 562 563 log<level::INFO>("User groups / privilege updated successfully", 564 entry("USER_NAME=%s", userName.c_str())); 565 std::sort(groupNames.begin(), groupNames.end()); 566 usersList[userName]->setUserGroups(groupNames); 567 usersList[userName]->setUserPrivilege(priv); 568 return; 569 } 570 571 uint8_t UserMgr::minPasswordLength(uint8_t value) 572 { 573 if (value == AccountPolicyIface::minPasswordLength()) 574 { 575 return value; 576 } 577 if (value < minPasswdLength) 578 { 579 log<level::ERR>(("Attempting to set minPasswordLength to less than " + 580 std::to_string(minPasswdLength)) 581 .c_str(), 582 entry("SIZE=%d", value)); 583 elog<InvalidArgument>( 584 Argument::ARGUMENT_NAME("minPasswordLength"), 585 Argument::ARGUMENT_VALUE(std::to_string(value).c_str())); 586 } 587 if (setPamModuleArgValue(pamCrackLib, minPasswdLenProp, 588 std::to_string(value)) != success) 589 { 590 log<level::ERR>("Unable to set minPasswordLength"); 591 elog<InternalFailure>(); 592 } 593 return AccountPolicyIface::minPasswordLength(value); 594 } 595 596 uint8_t UserMgr::rememberOldPasswordTimes(uint8_t value) 597 { 598 if (value == AccountPolicyIface::rememberOldPasswordTimes()) 599 { 600 return value; 601 } 602 if (setPamModuleArgValue(pamPWHistory, remOldPasswdCount, 603 std::to_string(value)) != success) 604 { 605 log<level::ERR>("Unable to set rememberOldPasswordTimes"); 606 elog<InternalFailure>(); 607 } 608 return AccountPolicyIface::rememberOldPasswordTimes(value); 609 } 610 611 uint16_t UserMgr::maxLoginAttemptBeforeLockout(uint16_t value) 612 { 613 if (value == AccountPolicyIface::maxLoginAttemptBeforeLockout()) 614 { 615 return value; 616 } 617 if (setPamModuleArgValue(pamTally2, maxFailedAttempt, 618 std::to_string(value)) != success) 619 { 620 log<level::ERR>("Unable to set maxLoginAttemptBeforeLockout"); 621 elog<InternalFailure>(); 622 } 623 return AccountPolicyIface::maxLoginAttemptBeforeLockout(value); 624 } 625 626 uint32_t UserMgr::accountUnlockTimeout(uint32_t value) 627 { 628 if (value == AccountPolicyIface::accountUnlockTimeout()) 629 { 630 return value; 631 } 632 if (setPamModuleArgValue(pamTally2, unlockTimeout, std::to_string(value)) != 633 success) 634 { 635 log<level::ERR>("Unable to set accountUnlockTimeout"); 636 elog<InternalFailure>(); 637 } 638 return AccountPolicyIface::accountUnlockTimeout(value); 639 } 640 641 int UserMgr::getPamModuleArgValue(const std::string& moduleName, 642 const std::string& argName, 643 std::string& argValue) 644 { 645 std::string fileName; 646 if (moduleName == pamTally2) 647 { 648 fileName = pamAuthConfigFile; 649 } 650 else 651 { 652 fileName = pamPasswdConfigFile; 653 } 654 std::ifstream fileToRead(fileName, std::ios::in); 655 if (!fileToRead.is_open()) 656 { 657 log<level::ERR>("Failed to open pam configuration file", 658 entry("FILE_NAME=%s", fileName.c_str())); 659 return failure; 660 } 661 std::string line; 662 auto argSearch = argName + "="; 663 size_t startPos = 0; 664 size_t endPos = 0; 665 while (getline(fileToRead, line)) 666 { 667 // skip comments section starting with # 668 if ((startPos = line.find('#')) != std::string::npos) 669 { 670 if (startPos == 0) 671 { 672 continue; 673 } 674 // skip comments after meaningful section and process those 675 line = line.substr(0, startPos); 676 } 677 if (line.find(moduleName) != std::string::npos) 678 { 679 if ((startPos = line.find(argSearch)) != std::string::npos) 680 { 681 if ((endPos = line.find(' ', startPos)) == std::string::npos) 682 { 683 endPos = line.size(); 684 } 685 startPos += argSearch.size(); 686 argValue = line.substr(startPos, endPos - startPos); 687 return success; 688 } 689 } 690 } 691 return failure; 692 } 693 694 int UserMgr::setPamModuleArgValue(const std::string& moduleName, 695 const std::string& argName, 696 const std::string& argValue) 697 { 698 std::string fileName; 699 if (moduleName == pamTally2) 700 { 701 fileName = pamAuthConfigFile; 702 } 703 else 704 { 705 fileName = pamPasswdConfigFile; 706 } 707 std::string tmpFileName = fileName + "_tmp"; 708 std::ifstream fileToRead(fileName, std::ios::in); 709 std::ofstream fileToWrite(tmpFileName, std::ios::out); 710 if (!fileToRead.is_open() || !fileToWrite.is_open()) 711 { 712 log<level::ERR>("Failed to open pam configuration /tmp file", 713 entry("FILE_NAME=%s", fileName.c_str())); 714 return failure; 715 } 716 std::string line; 717 auto argSearch = argName + "="; 718 size_t startPos = 0; 719 size_t endPos = 0; 720 bool found = false; 721 while (getline(fileToRead, line)) 722 { 723 // skip comments section starting with # 724 if ((startPos = line.find('#')) != std::string::npos) 725 { 726 if (startPos == 0) 727 { 728 fileToWrite << line << std::endl; 729 continue; 730 } 731 // skip comments after meaningful section and process those 732 line = line.substr(0, startPos); 733 } 734 if (line.find(moduleName) != std::string::npos) 735 { 736 if ((startPos = line.find(argSearch)) != std::string::npos) 737 { 738 if ((endPos = line.find(' ', startPos)) == std::string::npos) 739 { 740 endPos = line.size(); 741 } 742 startPos += argSearch.size(); 743 fileToWrite << line.substr(0, startPos) << argValue 744 << line.substr(endPos, line.size() - endPos) 745 << std::endl; 746 found = true; 747 continue; 748 } 749 } 750 fileToWrite << line << std::endl; 751 } 752 fileToWrite.close(); 753 fileToRead.close(); 754 if (found) 755 { 756 if (std::rename(tmpFileName.c_str(), fileName.c_str()) == 0) 757 { 758 return success; 759 } 760 } 761 return failure; 762 } 763 764 void UserMgr::userEnable(const std::string& userName, bool enabled) 765 { 766 // All user management lock has to be based on /etc/shadow 767 // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; 768 throwForUserDoesNotExist(userName); 769 try 770 { 771 executeUserModifyUserEnable(userName.c_str(), enabled); 772 } 773 catch (const InternalFailure& e) 774 { 775 log<level::ERR>("Unable to modify user enabled state"); 776 elog<InternalFailure>(); 777 } 778 779 log<level::INFO>("User enabled/disabled state updated successfully", 780 entry("USER_NAME=%s", userName.c_str()), 781 entry("ENABLED=%d", enabled)); 782 usersList[userName]->setUserEnabled(enabled); 783 return; 784 } 785 786 /** 787 * pam_tally2 app will provide the user failure count and failure status 788 * in second line of output with words position [0] - user name, 789 * [1] - failure count, [2] - latest failure date, [3] - latest failure time 790 * [4] - failure app 791 **/ 792 793 static constexpr size_t t2FailCntIdx = 1; 794 static constexpr size_t t2FailDateIdx = 2; 795 static constexpr size_t t2FailTimeIdx = 3; 796 static constexpr size_t t2OutputIndex = 1; 797 798 bool UserMgr::userLockedForFailedAttempt(const std::string& userName) 799 { 800 // All user management lock has to be based on /etc/shadow 801 // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; 802 if (AccountPolicyIface::maxLoginAttemptBeforeLockout() == 0) 803 { 804 return false; 805 } 806 807 std::vector<std::string> output; 808 try 809 { 810 output = getFailedAttempt(userName.c_str()); 811 } 812 catch (const InternalFailure& e) 813 { 814 log<level::ERR>("Unable to read login failure counter"); 815 elog<InternalFailure>(); 816 } 817 818 std::vector<std::string> splitWords; 819 boost::algorithm::split(splitWords, output[t2OutputIndex], 820 boost::algorithm::is_any_of("\t "), 821 boost::token_compress_on); 822 uint16_t failAttempts = 0; 823 try 824 { 825 unsigned long tmp = std::stoul(splitWords[t2FailCntIdx], nullptr); 826 if (tmp > std::numeric_limits<decltype(failAttempts)>::max()) 827 { 828 throw std::out_of_range("Out of range"); 829 } 830 failAttempts = static_cast<decltype(failAttempts)>(tmp); 831 } 832 catch (const std::exception& e) 833 { 834 log<level::ERR>("Exception for userLockedForFailedAttempt", 835 entry("WHAT=%s", e.what())); 836 elog<InternalFailure>(); 837 } 838 839 if (failAttempts < AccountPolicyIface::maxLoginAttemptBeforeLockout()) 840 { 841 return false; 842 } 843 844 // When failedAttempts is not 0, Latest failure date/time should be 845 // available 846 if (splitWords.size() < 4) 847 { 848 log<level::ERR>("Unable to read latest failure date/time"); 849 elog<InternalFailure>(); 850 } 851 852 const std::string failDateTime = 853 splitWords[t2FailDateIdx] + ' ' + splitWords[t2FailTimeIdx]; 854 855 // NOTE: Cannot use std::get_time() here as the implementation of %y in 856 // libstdc++ does not match POSIX strptime() before gcc 12.1.0 857 // https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=a8d3c98746098e2784be7144c1ccc9fcc34a0888 858 std::tm tmStruct = {}; 859 if (!strptime(failDateTime.c_str(), "%D %H:%M:%S", &tmStruct)) 860 { 861 log<level::ERR>("Failed to parse latest failure date/time"); 862 elog<InternalFailure>(); 863 } 864 865 time_t failTimestamp = std::mktime(&tmStruct); 866 if (failTimestamp + 867 static_cast<time_t>(AccountPolicyIface::accountUnlockTimeout()) <= 868 std::time(NULL)) 869 { 870 return false; 871 } 872 873 return true; 874 } 875 876 bool UserMgr::userLockedForFailedAttempt(const std::string& userName, 877 const bool& value) 878 { 879 // All user management lock has to be based on /etc/shadow 880 // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; 881 if (value == true) 882 { 883 return userLockedForFailedAttempt(userName); 884 } 885 886 try 887 { 888 executeCmd("/usr/sbin/pam_tally2", "-u", userName.c_str(), "-r"); 889 } 890 catch (const InternalFailure& e) 891 { 892 log<level::ERR>("Unable to reset login failure counter"); 893 elog<InternalFailure>(); 894 } 895 896 return userLockedForFailedAttempt(userName); 897 } 898 899 bool UserMgr::userPasswordExpired(const std::string& userName) 900 { 901 // All user management lock has to be based on /etc/shadow 902 // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; 903 904 struct spwd spwd 905 {}; 906 struct spwd* spwdPtr = nullptr; 907 auto buflen = sysconf(_SC_GETPW_R_SIZE_MAX); 908 if (buflen < -1) 909 { 910 // Use a default size if there is no hard limit suggested by sysconf() 911 buflen = 1024; 912 } 913 std::vector<char> buffer(buflen); 914 auto status = 915 getspnam_r(userName.c_str(), &spwd, buffer.data(), buflen, &spwdPtr); 916 // On success, getspnam_r() returns zero, and sets *spwdPtr to spwd. 917 // If no matching password record was found, these functions return 0 918 // and store NULL in *spwdPtr 919 if ((status == 0) && (&spwd == spwdPtr)) 920 { 921 // Determine password validity per "chage" docs, where: 922 // spwd.sp_lstchg == 0 means password is expired, and 923 // spwd.sp_max == -1 means the password does not expire. 924 constexpr long secondsPerDay = 60 * 60 * 24; 925 long today = static_cast<long>(time(NULL)) / secondsPerDay; 926 if ((spwd.sp_lstchg == 0) || 927 ((spwd.sp_max != -1) && ((spwd.sp_max + spwd.sp_lstchg) < today))) 928 { 929 return true; 930 } 931 } 932 else 933 { 934 // User entry is missing in /etc/shadow, indicating no SHA password. 935 // Treat this as new user without password entry in /etc/shadow 936 // TODO: Add property to indicate user password was not set yet 937 // https://github.com/openbmc/phosphor-user-manager/issues/8 938 return false; 939 } 940 941 return false; 942 } 943 944 UserSSHLists UserMgr::getUserAndSshGrpList() 945 { 946 // All user management lock has to be based on /etc/shadow 947 // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; 948 949 std::vector<std::string> userList; 950 std::vector<std::string> sshUsersList; 951 struct passwd pw, *pwp = nullptr; 952 std::array<char, 1024> buffer{}; 953 954 phosphor::user::File passwd(passwdFileName, "r"); 955 if ((passwd)() == NULL) 956 { 957 log<level::ERR>("Error opening the passwd file"); 958 elog<InternalFailure>(); 959 } 960 961 while (true) 962 { 963 auto r = fgetpwent_r((passwd)(), &pw, buffer.data(), buffer.max_size(), 964 &pwp); 965 if ((r != 0) || (pwp == NULL)) 966 { 967 // Any error, break the loop. 968 break; 969 } 970 #ifdef ENABLE_ROOT_USER_MGMT 971 // Add all users whose UID >= 1000 and < 65534 972 // and special UID 0. 973 if ((pwp->pw_uid == 0) || 974 ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534))) 975 #else 976 // Add all users whose UID >=1000 and < 65534 977 if ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534)) 978 #endif 979 { 980 std::string userName(pwp->pw_name); 981 userList.emplace_back(userName); 982 983 // ssh doesn't have separate group. Check login shell entry to 984 // get all users list which are member of ssh group. 985 std::string loginShell(pwp->pw_shell); 986 if (loginShell == "/bin/sh") 987 { 988 sshUsersList.emplace_back(userName); 989 } 990 } 991 } 992 endpwent(); 993 return std::make_pair(std::move(userList), std::move(sshUsersList)); 994 } 995 996 size_t UserMgr::getIpmiUsersCount() 997 { 998 std::vector<std::string> userList = getUsersInGroup("ipmi"); 999 return userList.size(); 1000 } 1001 1002 size_t UserMgr::getNonIpmiUsersCount() 1003 { 1004 std::vector<std::string> ipmiUsers = getUsersInGroup("ipmi"); 1005 return usersList.size() - ipmiUsers.size(); 1006 } 1007 1008 bool UserMgr::isUserEnabled(const std::string& userName) 1009 { 1010 // All user management lock has to be based on /etc/shadow 1011 // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; 1012 std::array<char, 4096> buffer{}; 1013 struct spwd spwd; 1014 struct spwd* resultPtr = nullptr; 1015 int status = getspnam_r(userName.c_str(), &spwd, buffer.data(), 1016 buffer.max_size(), &resultPtr); 1017 if (!status && (&spwd == resultPtr)) 1018 { 1019 if (resultPtr->sp_expire >= 0) 1020 { 1021 return false; // user locked out 1022 } 1023 return true; 1024 } 1025 return false; // assume user is disabled for any error. 1026 } 1027 1028 std::vector<std::string> UserMgr::getUsersInGroup(const std::string& groupName) 1029 { 1030 std::vector<std::string> usersInGroup; 1031 // Should be more than enough to get the pwd structure. 1032 std::array<char, 4096> buffer{}; 1033 struct group grp; 1034 struct group* resultPtr = nullptr; 1035 1036 int status = getgrnam_r(groupName.c_str(), &grp, buffer.data(), 1037 buffer.max_size(), &resultPtr); 1038 1039 if (!status && (&grp == resultPtr)) 1040 { 1041 for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem)) 1042 { 1043 usersInGroup.emplace_back(*(grp.gr_mem)); 1044 } 1045 } 1046 else 1047 { 1048 log<level::ERR>("Group not found", 1049 entry("GROUP=%s", groupName.c_str())); 1050 // Don't throw error, just return empty userList - fallback 1051 } 1052 return usersInGroup; 1053 } 1054 1055 DbusUserObj UserMgr::getPrivilegeMapperObject(void) 1056 { 1057 DbusUserObj objects; 1058 try 1059 { 1060 std::string basePath = "/xyz/openbmc_project/user/ldap/openldap"; 1061 std::string interface = "xyz.openbmc_project.User.Ldap.Config"; 1062 1063 auto ldapMgmtService = 1064 getServiceName(std::move(basePath), std::move(interface)); 1065 auto method = bus.new_method_call( 1066 ldapMgmtService.c_str(), ldapMgrObjBasePath, 1067 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 1068 1069 auto reply = bus.call(method); 1070 reply.read(objects); 1071 } 1072 catch (const InternalFailure& e) 1073 { 1074 log<level::ERR>("Unable to get the User Service", 1075 entry("WHAT=%s", e.what())); 1076 throw; 1077 } 1078 catch (const sdbusplus::exception_t& e) 1079 { 1080 log<level::ERR>( 1081 "Failed to excute method", entry("METHOD=%s", "GetManagedObjects"), 1082 entry("PATH=%s", ldapMgrObjBasePath), entry("WHAT=%s", e.what())); 1083 throw; 1084 } 1085 return objects; 1086 } 1087 1088 std::string UserMgr::getServiceName(std::string&& path, std::string&& intf) 1089 { 1090 auto mapperCall = bus.new_method_call(objMapperService, objMapperPath, 1091 objMapperInterface, "GetObject"); 1092 1093 mapperCall.append(std::move(path)); 1094 mapperCall.append(std::vector<std::string>({std::move(intf)})); 1095 1096 auto mapperResponseMsg = bus.call(mapperCall); 1097 1098 if (mapperResponseMsg.is_method_error()) 1099 { 1100 log<level::ERR>("Error in mapper call"); 1101 elog<InternalFailure>(); 1102 } 1103 1104 std::map<std::string, std::vector<std::string>> mapperResponse; 1105 mapperResponseMsg.read(mapperResponse); 1106 1107 if (mapperResponse.begin() == mapperResponse.end()) 1108 { 1109 log<level::ERR>("Invalid response from mapper"); 1110 elog<InternalFailure>(); 1111 } 1112 1113 return mapperResponse.begin()->first; 1114 } 1115 1116 gid_t UserMgr::getPrimaryGroup(const std::string& userName) const 1117 { 1118 static auto buflen = sysconf(_SC_GETPW_R_SIZE_MAX); 1119 if (buflen <= 0) 1120 { 1121 // Use a default size if there is no hard limit suggested by sysconf() 1122 buflen = 1024; 1123 } 1124 1125 struct passwd pwd; 1126 struct passwd* pwdPtr = nullptr; 1127 std::vector<char> buffer(buflen); 1128 1129 auto status = getpwnam_r(userName.c_str(), &pwd, buffer.data(), 1130 buffer.size(), &pwdPtr); 1131 // On success, getpwnam_r() returns zero, and set *pwdPtr to pwd. 1132 // If no matching password record was found, these functions return 0 1133 // and store NULL in *pwdPtr 1134 if (!status && (&pwd == pwdPtr)) 1135 { 1136 return pwd.pw_gid; 1137 } 1138 1139 log<level::ERR>("User noes not exist", 1140 entry("USER_NAME=%s", userName.c_str())); 1141 elog<UserNameDoesNotExist>(); 1142 } 1143 1144 bool UserMgr::isGroupMember(const std::string& userName, gid_t primaryGid, 1145 const std::string& groupName) const 1146 { 1147 static auto buflen = sysconf(_SC_GETGR_R_SIZE_MAX); 1148 if (buflen <= 0) 1149 { 1150 // Use a default size if there is no hard limit suggested by sysconf() 1151 buflen = 1024; 1152 } 1153 1154 struct group grp; 1155 struct group* grpPtr = nullptr; 1156 std::vector<char> buffer(buflen); 1157 1158 auto status = getgrnam_r(groupName.c_str(), &grp, buffer.data(), 1159 buffer.size(), &grpPtr); 1160 1161 // Groups with a lot of members may require a buffer of bigger size than 1162 // suggested by _SC_GETGR_R_SIZE_MAX. 1163 // 32K should be enough for about 2K members. 1164 constexpr auto maxBufferLength = 32 * 1024; 1165 while (status == ERANGE && buflen < maxBufferLength) 1166 { 1167 buflen *= 2; 1168 buffer.resize(buflen); 1169 1170 log<level::DEBUG>("Increase buffer for getgrnam_r()", 1171 entry("BUFFER_LENGTH=%zu", buflen)); 1172 1173 status = getgrnam_r(groupName.c_str(), &grp, buffer.data(), 1174 buffer.size(), &grpPtr); 1175 } 1176 1177 // On success, getgrnam_r() returns zero, and set *grpPtr to grp. 1178 // If no matching group record was found, these functions return 0 1179 // and store NULL in *grpPtr 1180 if (!status && (&grp == grpPtr)) 1181 { 1182 if (primaryGid == grp.gr_gid) 1183 { 1184 return true; 1185 } 1186 1187 for (auto i = 0; grp.gr_mem && grp.gr_mem[i]; ++i) 1188 { 1189 if (userName == grp.gr_mem[i]) 1190 { 1191 return true; 1192 } 1193 } 1194 } 1195 else if (status == ERANGE) 1196 { 1197 log<level::ERR>("Group info requires too much memory", 1198 entry("GROUP_NAME=%s", groupName.c_str())); 1199 } 1200 else 1201 { 1202 log<level::ERR>("Group does not exist", 1203 entry("GROUP_NAME=%s", groupName.c_str())); 1204 } 1205 1206 return false; 1207 } 1208 1209 void UserMgr::executeGroupCreation(const char* groupName) 1210 { 1211 executeCmd("/usr/sbin/groupadd", groupName); 1212 } 1213 1214 void UserMgr::executeGroupDeletion(const char* groupName) 1215 { 1216 executeCmd("/usr/sbin/groupdel", groupName); 1217 } 1218 1219 UserInfoMap UserMgr::getUserInfo(std::string userName) 1220 { 1221 UserInfoMap userInfo; 1222 // Check whether the given user is local user or not. 1223 if (isUserExist(userName)) 1224 { 1225 const auto& user = usersList[userName]; 1226 userInfo.emplace("UserPrivilege", user.get()->userPrivilege()); 1227 userInfo.emplace("UserGroups", user.get()->userGroups()); 1228 userInfo.emplace("UserEnabled", user.get()->userEnabled()); 1229 userInfo.emplace("UserLockedForFailedAttempt", 1230 user.get()->userLockedForFailedAttempt()); 1231 userInfo.emplace("UserPasswordExpired", 1232 user.get()->userPasswordExpired()); 1233 userInfo.emplace("RemoteUser", false); 1234 } 1235 else 1236 { 1237 auto primaryGid = getPrimaryGroup(userName); 1238 1239 DbusUserObj objects = getPrivilegeMapperObject(); 1240 1241 std::string ldapConfigPath; 1242 std::string userPrivilege; 1243 1244 try 1245 { 1246 for (const auto& [path, interfaces] : objects) 1247 { 1248 auto it = interfaces.find("xyz.openbmc_project.Object.Enable"); 1249 if (it != interfaces.end()) 1250 { 1251 auto propIt = it->second.find("Enabled"); 1252 if (propIt != it->second.end() && 1253 std::get<bool>(propIt->second)) 1254 { 1255 ldapConfigPath = path.str + '/'; 1256 break; 1257 } 1258 } 1259 } 1260 1261 if (ldapConfigPath.empty()) 1262 { 1263 return userInfo; 1264 } 1265 1266 for (const auto& [path, interfaces] : objects) 1267 { 1268 if (!path.str.starts_with(ldapConfigPath)) 1269 { 1270 continue; 1271 } 1272 1273 auto it = interfaces.find( 1274 "xyz.openbmc_project.User.PrivilegeMapperEntry"); 1275 if (it != interfaces.end()) 1276 { 1277 std::string privilege; 1278 std::string groupName; 1279 1280 for (const auto& [propName, propValue] : it->second) 1281 { 1282 if (propName == "GroupName") 1283 { 1284 groupName = std::get<std::string>(propValue); 1285 } 1286 else if (propName == "Privilege") 1287 { 1288 privilege = std::get<std::string>(propValue); 1289 } 1290 } 1291 1292 if (!groupName.empty() && !privilege.empty() && 1293 isGroupMember(userName, primaryGid, groupName)) 1294 { 1295 userPrivilege = privilege; 1296 break; 1297 } 1298 } 1299 if (!userPrivilege.empty()) 1300 { 1301 break; 1302 } 1303 } 1304 1305 if (!userPrivilege.empty()) 1306 { 1307 userInfo.emplace("UserPrivilege", userPrivilege); 1308 } 1309 else 1310 { 1311 log<level::WARNING>("LDAP group privilege mapping does not " 1312 "exist, default \"priv-user\" is used"); 1313 userInfo.emplace("UserPrivilege", "priv-user"); 1314 } 1315 } 1316 catch (const std::bad_variant_access& e) 1317 { 1318 log<level::ERR>("Error while accessing variant", 1319 entry("WHAT=%s", e.what())); 1320 elog<InternalFailure>(); 1321 } 1322 userInfo.emplace("RemoteUser", true); 1323 } 1324 1325 return userInfo; 1326 } 1327 1328 void UserMgr::initializeAccountPolicy() 1329 { 1330 std::string valueStr; 1331 auto value = minPasswdLength; 1332 unsigned long tmp = 0; 1333 if (getPamModuleArgValue(pamCrackLib, minPasswdLenProp, valueStr) != 1334 success) 1335 { 1336 AccountPolicyIface::minPasswordLength(minPasswdLength); 1337 } 1338 else 1339 { 1340 try 1341 { 1342 tmp = std::stoul(valueStr, nullptr); 1343 if (tmp > std::numeric_limits<decltype(value)>::max()) 1344 { 1345 throw std::out_of_range("Out of range"); 1346 } 1347 value = static_cast<decltype(value)>(tmp); 1348 } 1349 catch (const std::exception& e) 1350 { 1351 log<level::ERR>("Exception for MinPasswordLength", 1352 entry("WHAT=%s", e.what())); 1353 throw; 1354 } 1355 AccountPolicyIface::minPasswordLength(value); 1356 } 1357 valueStr.clear(); 1358 if (getPamModuleArgValue(pamPWHistory, remOldPasswdCount, valueStr) != 1359 success) 1360 { 1361 AccountPolicyIface::rememberOldPasswordTimes(0); 1362 } 1363 else 1364 { 1365 value = 0; 1366 try 1367 { 1368 tmp = std::stoul(valueStr, nullptr); 1369 if (tmp > std::numeric_limits<decltype(value)>::max()) 1370 { 1371 throw std::out_of_range("Out of range"); 1372 } 1373 value = static_cast<decltype(value)>(tmp); 1374 } 1375 catch (const std::exception& e) 1376 { 1377 log<level::ERR>("Exception for RememberOldPasswordTimes", 1378 entry("WHAT=%s", e.what())); 1379 throw; 1380 } 1381 AccountPolicyIface::rememberOldPasswordTimes(value); 1382 } 1383 valueStr.clear(); 1384 if (getPamModuleArgValue(pamTally2, maxFailedAttempt, valueStr) != success) 1385 { 1386 AccountPolicyIface::maxLoginAttemptBeforeLockout(0); 1387 } 1388 else 1389 { 1390 uint16_t value16 = 0; 1391 try 1392 { 1393 tmp = std::stoul(valueStr, nullptr); 1394 if (tmp > std::numeric_limits<decltype(value16)>::max()) 1395 { 1396 throw std::out_of_range("Out of range"); 1397 } 1398 value16 = static_cast<decltype(value16)>(tmp); 1399 } 1400 catch (const std::exception& e) 1401 { 1402 log<level::ERR>("Exception for MaxLoginAttemptBeforLockout", 1403 entry("WHAT=%s", e.what())); 1404 throw; 1405 } 1406 AccountPolicyIface::maxLoginAttemptBeforeLockout(value16); 1407 } 1408 valueStr.clear(); 1409 if (getPamModuleArgValue(pamTally2, unlockTimeout, valueStr) != success) 1410 { 1411 AccountPolicyIface::accountUnlockTimeout(0); 1412 } 1413 else 1414 { 1415 uint32_t value32 = 0; 1416 try 1417 { 1418 tmp = std::stoul(valueStr, nullptr); 1419 if (tmp > std::numeric_limits<decltype(value32)>::max()) 1420 { 1421 throw std::out_of_range("Out of range"); 1422 } 1423 value32 = static_cast<decltype(value32)>(tmp); 1424 } 1425 catch (const std::exception& e) 1426 { 1427 log<level::ERR>("Exception for AccountUnlockTimeout", 1428 entry("WHAT=%s", e.what())); 1429 throw; 1430 } 1431 AccountPolicyIface::accountUnlockTimeout(value32); 1432 } 1433 } 1434 1435 void UserMgr::initUserObjects(void) 1436 { 1437 // All user management lock has to be based on /etc/shadow 1438 // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; 1439 std::vector<std::string> userNameList; 1440 std::vector<std::string> sshGrpUsersList; 1441 UserSSHLists userSSHLists = getUserAndSshGrpList(); 1442 userNameList = std::move(userSSHLists.first); 1443 sshGrpUsersList = std::move(userSSHLists.second); 1444 1445 if (!userNameList.empty()) 1446 { 1447 std::map<std::string, std::vector<std::string>> groupLists; 1448 // We only track users that are in the |predefinedGroups| 1449 // The other groups don't contain real BMC users. 1450 for (const char* grp : predefinedGroups) 1451 { 1452 if (grp == grpSsh) 1453 { 1454 groupLists.emplace(grp, sshGrpUsersList); 1455 } 1456 else 1457 { 1458 std::vector<std::string> grpUsersList = getUsersInGroup(grp); 1459 groupLists.emplace(grp, grpUsersList); 1460 } 1461 } 1462 for (auto& grp : privMgr) 1463 { 1464 std::vector<std::string> grpUsersList = getUsersInGroup(grp); 1465 groupLists.emplace(grp, grpUsersList); 1466 } 1467 1468 for (auto& user : userNameList) 1469 { 1470 std::vector<std::string> userGroups; 1471 std::string userPriv; 1472 for (const auto& grp : groupLists) 1473 { 1474 std::vector<std::string> tempGrp = grp.second; 1475 if (std::find(tempGrp.begin(), tempGrp.end(), user) != 1476 tempGrp.end()) 1477 { 1478 if (std::find(privMgr.begin(), privMgr.end(), grp.first) != 1479 privMgr.end()) 1480 { 1481 userPriv = grp.first; 1482 } 1483 else 1484 { 1485 userGroups.emplace_back(grp.first); 1486 } 1487 } 1488 } 1489 // Add user objects to the Users path. 1490 sdbusplus::message::object_path tempObjPath(usersObjPath); 1491 tempObjPath /= user; 1492 std::string objPath(tempObjPath); 1493 std::sort(userGroups.begin(), userGroups.end()); 1494 usersList.emplace(user, std::make_unique<phosphor::user::Users>( 1495 bus, objPath.c_str(), userGroups, 1496 userPriv, isUserEnabled(user), *this)); 1497 } 1498 } 1499 } 1500 1501 UserMgr::UserMgr(sdbusplus::bus_t& bus, const char* path) : 1502 Ifaces(bus, path, Ifaces::action::defer_emit), bus(bus), path(path), 1503 pamPasswdConfigFile(defaultPamPasswdConfigFile), 1504 pamAuthConfigFile(defaultPamAuthConfigFile) 1505 { 1506 UserMgrIface::allPrivileges(privMgr); 1507 groupsMgr = readAllGroupsOnSystem(); 1508 std::sort(groupsMgr.begin(), groupsMgr.end()); 1509 UserMgrIface::allGroups(groupsMgr); 1510 initializeAccountPolicy(); 1511 initUserObjects(); 1512 1513 // emit the signal 1514 this->emit_object_added(); 1515 } 1516 1517 void UserMgr::executeUserAdd(const char* userName, const char* groups, 1518 bool sshRequested, bool enabled) 1519 { 1520 // set EXPIRE_DATE to 0 to disable user, PAM takes 0 as expire on 1521 // 1970-01-01, that's an implementation-defined behavior 1522 executeCmd("/usr/sbin/useradd", userName, "-G", groups, "-m", "-N", "-s", 1523 (sshRequested ? "/bin/sh" : "/sbin/nologin"), "-e", 1524 (enabled ? "" : "1970-01-01")); 1525 } 1526 1527 void UserMgr::executeUserDelete(const char* userName) 1528 { 1529 executeCmd("/usr/sbin/userdel", userName, "-r"); 1530 } 1531 1532 void UserMgr::executeUserRename(const char* userName, const char* newUserName) 1533 { 1534 std::string newHomeDir = "/home/"; 1535 newHomeDir += newUserName; 1536 executeCmd("/usr/sbin/usermod", "-l", newUserName, userName, "-d", 1537 newHomeDir.c_str(), "-m"); 1538 } 1539 1540 void UserMgr::executeUserModify(const char* userName, const char* newGroups, 1541 bool sshRequested) 1542 { 1543 executeCmd("/usr/sbin/usermod", userName, "-G", newGroups, "-s", 1544 (sshRequested ? "/bin/sh" : "/sbin/nologin")); 1545 } 1546 1547 void UserMgr::executeUserModifyUserEnable(const char* userName, bool enabled) 1548 { 1549 // set EXPIRE_DATE to 0 to disable user, PAM takes 0 as expire on 1550 // 1970-01-01, that's an implementation-defined behavior 1551 executeCmd("/usr/sbin/usermod", userName, "-e", 1552 (enabled ? "" : "1970-01-01")); 1553 } 1554 1555 std::vector<std::string> UserMgr::getFailedAttempt(const char* userName) 1556 { 1557 return executeCmd("/usr/sbin/pam_tally2", "-u", userName); 1558 } 1559 1560 } // namespace user 1561 } // namespace phosphor 1562