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 <boost/process/io.hpp> 29 #include <boost/algorithm/string/split.hpp> 30 #include <xyz/openbmc_project/Common/error.hpp> 31 #include <xyz/openbmc_project/User/Common/error.hpp> 32 #include <phosphor-logging/log.hpp> 33 #include <phosphor-logging/elog.hpp> 34 #include <phosphor-logging/elog-errors.hpp> 35 #include "shadowlock.hpp" 36 #include "file.hpp" 37 #include "user_mgr.hpp" 38 #include "users.hpp" 39 #include "config.h" 40 41 namespace phosphor 42 { 43 namespace user 44 { 45 46 static constexpr const char *passwdFileName = "/etc/passwd"; 47 static constexpr size_t ipmiMaxUsers = 15; 48 static constexpr size_t ipmiMaxUserNameLen = 16; 49 static constexpr size_t systemMaxUserNameLen = 30; 50 static constexpr size_t maxSystemUsers = 30; 51 static constexpr const char *grpSsh = "ssh"; 52 static constexpr uint8_t minPasswdLength = 8; 53 static constexpr int success = 0; 54 static constexpr int failure = -1; 55 56 // pam modules related 57 static constexpr const char *pamTally2 = "pam_tally2.so"; 58 static constexpr const char *pamCrackLib = "pam_cracklib.so"; 59 static constexpr const char *pamPWHistory = "pam_pwhistory.so"; 60 static constexpr const char *minPasswdLenProp = "minlen"; 61 static constexpr const char *remOldPasswdCount = "remember"; 62 static constexpr const char *maxFailedAttempt = "deny"; 63 static constexpr const char *unlockTimeout = "unlock_time"; 64 static constexpr const char *pamPasswdConfigFile = "/etc/pam.d/common-password"; 65 static constexpr const char *pamAuthConfigFile = "/etc/pam.d/common-auth"; 66 67 // Object Manager related 68 static constexpr const char *ldapMgrObjBasePath = 69 "/xyz/openbmc_project/user/ldap"; 70 71 // Object Mapper related 72 static constexpr const char *objMapperService = 73 "xyz.openbmc_project.ObjectMapper"; 74 static constexpr const char *objMapperPath = 75 "/xyz/openbmc_project/object_mapper"; 76 static constexpr const char *objMapperInterface = 77 "xyz.openbmc_project.ObjectMapper"; 78 79 using namespace phosphor::logging; 80 using InsufficientPermission = 81 sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission; 82 using InternalFailure = 83 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 84 using InvalidArgument = 85 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 86 using UserNameExists = 87 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists; 88 using UserNameDoesNotExist = 89 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameDoesNotExist; 90 using UserNameGroupFail = 91 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameGroupFail; 92 using NoResource = 93 sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource; 94 95 using Argument = xyz::openbmc_project::Common::InvalidArgument; 96 97 template <typename... ArgTypes> 98 static std::vector<std::string> executeCmd(const char *path, 99 ArgTypes &&... tArgs) 100 { 101 std::vector<std::string> stdOutput; 102 boost::process::ipstream stdOutStream; 103 boost::process::child execProg(path, const_cast<char *>(tArgs)..., 104 boost::process::std_out > stdOutStream); 105 std::string stdOutLine; 106 107 while (stdOutStream && std::getline(stdOutStream, stdOutLine) && 108 !stdOutLine.empty()) 109 { 110 stdOutput.emplace_back(stdOutLine); 111 } 112 113 execProg.wait(); 114 115 int retCode = execProg.exit_code(); 116 if (retCode) 117 { 118 log<level::ERR>("Command execution failed", entry("PATH=%d", path), 119 entry("RETURN_CODE:%d", retCode)); 120 elog<InternalFailure>(); 121 } 122 123 return stdOutput; 124 } 125 126 static std::string getCSVFromVector(std::vector<std::string> vec) 127 { 128 switch (vec.size()) 129 { 130 case 0: 131 { 132 return ""; 133 } 134 break; 135 136 case 1: 137 { 138 return std::string{vec[0]}; 139 } 140 break; 141 142 default: 143 { 144 return std::accumulate( 145 std::next(vec.begin()), vec.end(), vec[0], 146 [](std::string a, std::string b) { return a + ',' + b; }); 147 } 148 } 149 } 150 151 static bool removeStringFromCSV(std::string &csvStr, const std::string &delStr) 152 { 153 std::string::size_type delStrPos = csvStr.find(delStr); 154 if (delStrPos != std::string::npos) 155 { 156 // need to also delete the comma char 157 if (delStrPos == 0) 158 { 159 csvStr.erase(delStrPos, delStr.size() + 1); 160 } 161 else 162 { 163 csvStr.erase(delStrPos - 1, delStr.size() + 1); 164 } 165 return true; 166 } 167 return false; 168 } 169 170 bool UserMgr::isUserExist(const std::string &userName) 171 { 172 if (userName.empty()) 173 { 174 log<level::ERR>("User name is empty"); 175 elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"), 176 Argument::ARGUMENT_VALUE("Null")); 177 } 178 if (usersList.find(userName) == usersList.end()) 179 { 180 return false; 181 } 182 return true; 183 } 184 185 void UserMgr::throwForUserDoesNotExist(const std::string &userName) 186 { 187 if (isUserExist(userName) == false) 188 { 189 log<level::ERR>("User does not exist", 190 entry("USER_NAME=%s", userName.c_str())); 191 elog<UserNameDoesNotExist>(); 192 } 193 } 194 195 void UserMgr::throwForUserExists(const std::string &userName) 196 { 197 if (isUserExist(userName) == true) 198 { 199 log<level::ERR>("User already exists", 200 entry("USER_NAME=%s", userName.c_str())); 201 elog<UserNameExists>(); 202 } 203 } 204 205 void UserMgr::throwForUserNameConstraints( 206 const std::string &userName, const std::vector<std::string> &groupNames) 207 { 208 if (std::find(groupNames.begin(), groupNames.end(), "ipmi") != 209 groupNames.end()) 210 { 211 if (userName.length() > ipmiMaxUserNameLen) 212 { 213 log<level::ERR>("IPMI user name length limitation", 214 entry("SIZE=%d", userName.length())); 215 elog<UserNameGroupFail>( 216 xyz::openbmc_project::User::Common::UserNameGroupFail::REASON( 217 "IPMI length")); 218 } 219 } 220 if (userName.length() > systemMaxUserNameLen) 221 { 222 log<level::ERR>("User name length limitation", 223 entry("SIZE=%d", userName.length())); 224 elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"), 225 Argument::ARGUMENT_VALUE("Invalid length")); 226 } 227 if (!std::regex_match(userName.c_str(), 228 std::regex("[a-zA-z_][a-zA-Z_0-9]*"))) 229 { 230 log<level::ERR>("Invalid user name", 231 entry("USER_NAME=%s", userName.c_str())); 232 elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"), 233 Argument::ARGUMENT_VALUE("Invalid data")); 234 } 235 } 236 237 void UserMgr::throwForMaxGrpUserCount( 238 const std::vector<std::string> &groupNames) 239 { 240 if (std::find(groupNames.begin(), groupNames.end(), "ipmi") != 241 groupNames.end()) 242 { 243 if (getIpmiUsersCount() >= ipmiMaxUsers) 244 { 245 log<level::ERR>("IPMI user limit reached"); 246 elog<NoResource>( 247 xyz::openbmc_project::User::Common::NoResource::REASON( 248 "ipmi user count reached")); 249 } 250 } 251 else 252 { 253 if (usersList.size() > 0 && (usersList.size() - getIpmiUsersCount()) >= 254 (maxSystemUsers - ipmiMaxUsers)) 255 { 256 log<level::ERR>("Non-ipmi User limit reached"); 257 elog<NoResource>( 258 xyz::openbmc_project::User::Common::NoResource::REASON( 259 "Non-ipmi user count reached")); 260 } 261 } 262 return; 263 } 264 265 void UserMgr::throwForInvalidPrivilege(const std::string &priv) 266 { 267 if (!priv.empty() && 268 (std::find(privMgr.begin(), privMgr.end(), priv) == privMgr.end())) 269 { 270 log<level::ERR>("Invalid privilege"); 271 elog<InvalidArgument>(Argument::ARGUMENT_NAME("Privilege"), 272 Argument::ARGUMENT_VALUE(priv.c_str())); 273 } 274 } 275 276 void UserMgr::throwForInvalidGroups(const std::vector<std::string> &groupNames) 277 { 278 for (auto &group : groupNames) 279 { 280 if (std::find(groupsMgr.begin(), groupsMgr.end(), group) == 281 groupsMgr.end()) 282 { 283 log<level::ERR>("Invalid Group Name listed"); 284 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GroupName"), 285 Argument::ARGUMENT_VALUE(group.c_str())); 286 } 287 } 288 } 289 290 void UserMgr::createUser(std::string userName, 291 std::vector<std::string> groupNames, std::string priv, 292 bool enabled) 293 { 294 throwForInvalidPrivilege(priv); 295 throwForInvalidGroups(groupNames); 296 // All user management lock has to be based on /etc/shadow 297 phosphor::user::shadow::Lock lock(); 298 throwForUserExists(userName); 299 throwForUserNameConstraints(userName, groupNames); 300 throwForMaxGrpUserCount(groupNames); 301 302 std::string groups = getCSVFromVector(groupNames); 303 bool sshRequested = removeStringFromCSV(groups, grpSsh); 304 305 // treat privilege as a group - This is to avoid using different file to 306 // store the same. 307 if (!priv.empty()) 308 { 309 if (groups.size() != 0) 310 { 311 groups += ","; 312 } 313 groups += priv; 314 } 315 try 316 { 317 executeCmd("/usr/sbin/useradd", userName.c_str(), "-G", groups.c_str(), 318 "-m", "-N", "-s", 319 (sshRequested ? "/bin/sh" : "/bin/nologin"), "-e", 320 (enabled ? "" : "1970-01-02")); 321 } 322 catch (const InternalFailure &e) 323 { 324 log<level::ERR>("Unable to create new user"); 325 elog<InternalFailure>(); 326 } 327 328 // Add the users object before sending out the signal 329 std::string userObj = std::string(usersObjPath) + "/" + userName; 330 std::sort(groupNames.begin(), groupNames.end()); 331 usersList.emplace( 332 userName, std::move(std::make_unique<phosphor::user::Users>( 333 bus, userObj.c_str(), groupNames, priv, enabled, *this))); 334 335 log<level::INFO>("User created successfully", 336 entry("USER_NAME=%s", userName.c_str())); 337 return; 338 } 339 340 void UserMgr::deleteUser(std::string userName) 341 { 342 // All user management lock has to be based on /etc/shadow 343 phosphor::user::shadow::Lock lock(); 344 throwForUserDoesNotExist(userName); 345 try 346 { 347 executeCmd("/usr/sbin/userdel", userName.c_str(), "-r"); 348 } 349 catch (const InternalFailure &e) 350 { 351 log<level::ERR>("User delete failed", 352 entry("USER_NAME=%s", userName.c_str())); 353 elog<InternalFailure>(); 354 } 355 356 usersList.erase(userName); 357 358 log<level::INFO>("User deleted successfully", 359 entry("USER_NAME=%s", userName.c_str())); 360 return; 361 } 362 363 void UserMgr::renameUser(std::string userName, std::string newUserName) 364 { 365 // All user management lock has to be based on /etc/shadow 366 phosphor::user::shadow::Lock lock(); 367 throwForUserDoesNotExist(userName); 368 throwForUserExists(newUserName); 369 throwForUserNameConstraints(newUserName, 370 usersList[userName].get()->userGroups()); 371 try 372 { 373 std::string newHomeDir = "/home/" + newUserName; 374 executeCmd("/usr/sbin/usermod", "-l", newUserName.c_str(), 375 userName.c_str(), "-d", newHomeDir.c_str(), "-m"); 376 } 377 catch (const InternalFailure &e) 378 { 379 log<level::ERR>("User rename failed", 380 entry("USER_NAME=%s", userName.c_str())); 381 elog<InternalFailure>(); 382 } 383 const auto &user = usersList[userName]; 384 std::string priv = user.get()->userPrivilege(); 385 std::vector<std::string> groupNames = user.get()->userGroups(); 386 bool enabled = user.get()->userEnabled(); 387 std::string newUserObj = std::string(usersObjPath) + "/" + newUserName; 388 // Special group 'ipmi' needs a way to identify user renamed, in order to 389 // update encrypted password. It can't rely only on InterfacesRemoved & 390 // InterfacesAdded. So first send out userRenamed signal. 391 this->userRenamed(userName, newUserName); 392 usersList.erase(userName); 393 usersList.emplace( 394 newUserName, 395 std::move(std::make_unique<phosphor::user::Users>( 396 bus, newUserObj.c_str(), groupNames, priv, enabled, *this))); 397 return; 398 } 399 400 void UserMgr::updateGroupsAndPriv(const std::string &userName, 401 const std::vector<std::string> &groupNames, 402 const std::string &priv) 403 { 404 throwForInvalidPrivilege(priv); 405 throwForInvalidGroups(groupNames); 406 // All user management lock has to be based on /etc/shadow 407 phosphor::user::shadow::Lock lock(); 408 throwForUserDoesNotExist(userName); 409 const std::vector<std::string> &oldGroupNames = 410 usersList[userName].get()->userGroups(); 411 std::vector<std::string> groupDiff; 412 // Note: already dealing with sorted group lists. 413 std::set_symmetric_difference(oldGroupNames.begin(), oldGroupNames.end(), 414 groupNames.begin(), groupNames.end(), 415 std::back_inserter(groupDiff)); 416 if (std::find(groupDiff.begin(), groupDiff.end(), "ipmi") != 417 groupDiff.end()) 418 { 419 throwForUserNameConstraints(userName, groupNames); 420 throwForMaxGrpUserCount(groupNames); 421 } 422 423 std::string groups = getCSVFromVector(groupNames); 424 bool sshRequested = removeStringFromCSV(groups, grpSsh); 425 426 // treat privilege as a group - This is to avoid using different file to 427 // store the same. 428 if (!priv.empty()) 429 { 430 if (groups.size() != 0) 431 { 432 groups += ","; 433 } 434 groups += priv; 435 } 436 try 437 { 438 executeCmd("/usr/sbin/usermod", userName.c_str(), "-G", groups.c_str(), 439 "-s", (sshRequested ? "/bin/sh" : "/bin/nologin")); 440 } 441 catch (const InternalFailure &e) 442 { 443 log<level::ERR>("Unable to modify user privilege / groups"); 444 elog<InternalFailure>(); 445 } 446 447 log<level::INFO>("User groups / privilege updated successfully", 448 entry("USER_NAME=%s", userName.c_str())); 449 return; 450 } 451 452 uint8_t UserMgr::minPasswordLength(uint8_t value) 453 { 454 if (value == AccountPolicyIface::minPasswordLength()) 455 { 456 return value; 457 } 458 if (value < minPasswdLength) 459 { 460 return value; 461 } 462 if (setPamModuleArgValue(pamCrackLib, minPasswdLenProp, 463 std::to_string(value)) != success) 464 { 465 log<level::ERR>("Unable to set minPasswordLength"); 466 elog<InternalFailure>(); 467 } 468 return AccountPolicyIface::minPasswordLength(value); 469 } 470 471 uint8_t UserMgr::rememberOldPasswordTimes(uint8_t value) 472 { 473 if (value == AccountPolicyIface::rememberOldPasswordTimes()) 474 { 475 return value; 476 } 477 if (setPamModuleArgValue(pamPWHistory, remOldPasswdCount, 478 std::to_string(value)) != success) 479 { 480 log<level::ERR>("Unable to set rememberOldPasswordTimes"); 481 elog<InternalFailure>(); 482 } 483 return AccountPolicyIface::rememberOldPasswordTimes(value); 484 } 485 486 uint16_t UserMgr::maxLoginAttemptBeforeLockout(uint16_t value) 487 { 488 if (value == AccountPolicyIface::maxLoginAttemptBeforeLockout()) 489 { 490 return value; 491 } 492 if (setPamModuleArgValue(pamTally2, maxFailedAttempt, 493 std::to_string(value)) != success) 494 { 495 log<level::ERR>("Unable to set maxLoginAttemptBeforeLockout"); 496 elog<InternalFailure>(); 497 } 498 return AccountPolicyIface::maxLoginAttemptBeforeLockout(value); 499 } 500 501 uint32_t UserMgr::accountUnlockTimeout(uint32_t value) 502 { 503 if (value == AccountPolicyIface::accountUnlockTimeout()) 504 { 505 return value; 506 } 507 if (setPamModuleArgValue(pamTally2, unlockTimeout, std::to_string(value)) != 508 success) 509 { 510 log<level::ERR>("Unable to set accountUnlockTimeout"); 511 elog<InternalFailure>(); 512 } 513 return AccountPolicyIface::accountUnlockTimeout(value); 514 } 515 516 int UserMgr::getPamModuleArgValue(const std::string &moduleName, 517 const std::string &argName, 518 std::string &argValue) 519 { 520 std::string fileName; 521 if (moduleName == pamTally2) 522 { 523 fileName = pamAuthConfigFile; 524 } 525 else 526 { 527 fileName = pamPasswdConfigFile; 528 } 529 std::ifstream fileToRead(fileName, std::ios::in); 530 if (!fileToRead.is_open()) 531 { 532 log<level::ERR>("Failed to open pam configuration file", 533 entry("FILE_NAME=%s", fileName.c_str())); 534 return failure; 535 } 536 std::string line; 537 auto argSearch = argName + "="; 538 size_t startPos = 0; 539 size_t endPos = 0; 540 while (getline(fileToRead, line)) 541 { 542 // skip comments section starting with # 543 if ((startPos = line.find('#')) != std::string::npos) 544 { 545 if (startPos == 0) 546 { 547 continue; 548 } 549 // skip comments after meaningful section and process those 550 line = line.substr(0, startPos); 551 } 552 if (line.find(moduleName) != std::string::npos) 553 { 554 if ((startPos = line.find(argSearch)) != std::string::npos) 555 { 556 if ((endPos = line.find(' ', startPos)) == std::string::npos) 557 { 558 endPos = line.size(); 559 } 560 startPos += argSearch.size(); 561 argValue = line.substr(startPos, endPos - startPos); 562 return success; 563 } 564 } 565 } 566 return failure; 567 } 568 569 int UserMgr::setPamModuleArgValue(const std::string &moduleName, 570 const std::string &argName, 571 const std::string &argValue) 572 { 573 std::string fileName; 574 if (moduleName == pamTally2) 575 { 576 fileName = pamAuthConfigFile; 577 } 578 else 579 { 580 fileName = pamPasswdConfigFile; 581 } 582 std::string tmpFileName = fileName + "_tmp"; 583 std::ifstream fileToRead(fileName, std::ios::in); 584 std::ofstream fileToWrite(tmpFileName, std::ios::out); 585 if (!fileToRead.is_open() || !fileToWrite.is_open()) 586 { 587 log<level::ERR>("Failed to open pam configuration /tmp file", 588 entry("FILE_NAME=%s", fileName.c_str())); 589 return failure; 590 } 591 std::string line; 592 auto argSearch = argName + "="; 593 size_t startPos = 0; 594 size_t endPos = 0; 595 bool found = false; 596 while (getline(fileToRead, line)) 597 { 598 // skip comments section starting with # 599 if ((startPos = line.find('#')) != std::string::npos) 600 { 601 if (startPos == 0) 602 { 603 fileToWrite << line << std::endl; 604 continue; 605 } 606 // skip comments after meaningful section and process those 607 line = line.substr(0, startPos); 608 } 609 if (line.find(moduleName) != std::string::npos) 610 { 611 if ((startPos = line.find(argSearch)) != std::string::npos) 612 { 613 if ((endPos = line.find(' ', startPos)) == std::string::npos) 614 { 615 endPos = line.size(); 616 } 617 startPos += argSearch.size(); 618 fileToWrite << line.substr(0, startPos) << argValue 619 << line.substr(endPos, line.size() - endPos) 620 << std::endl; 621 found = true; 622 continue; 623 } 624 } 625 fileToWrite << line << std::endl; 626 } 627 fileToWrite.close(); 628 fileToRead.close(); 629 if (found) 630 { 631 if (std::rename(tmpFileName.c_str(), fileName.c_str()) == 0) 632 { 633 return success; 634 } 635 } 636 return failure; 637 } 638 639 void UserMgr::userEnable(const std::string &userName, bool enabled) 640 { 641 // All user management lock has to be based on /etc/shadow 642 phosphor::user::shadow::Lock lock(); 643 throwForUserDoesNotExist(userName); 644 try 645 { 646 executeCmd("/usr/sbin/usermod", userName.c_str(), "-e", 647 (enabled ? "" : "1970-01-02")); 648 } 649 catch (const InternalFailure &e) 650 { 651 log<level::ERR>("Unable to modify user enabled state"); 652 elog<InternalFailure>(); 653 } 654 655 log<level::INFO>("User enabled/disabled state updated successfully", 656 entry("USER_NAME=%s", userName.c_str()), 657 entry("ENABLED=%d", enabled)); 658 return; 659 } 660 661 /** 662 * pam_tally2 app will provide the user failure count and failure status 663 * in second line of output with words position [0] - user name, 664 * [1] - failure count, [2] - latest timestamp, [3] - failure timestamp 665 * [4] - failure app 666 **/ 667 668 static constexpr size_t t2UserIdx = 0; 669 static constexpr size_t t2FailCntIdx = 1; 670 static constexpr size_t t2OutputIndex = 1; 671 672 bool UserMgr::userLockedForFailedAttempt(const std::string &userName) 673 { 674 // All user management lock has to be based on /etc/shadow 675 phosphor::user::shadow::Lock lock(); 676 std::vector<std::string> output; 677 678 output = executeCmd("/usr/sbin/pam_tally2", "-u", userName.c_str()); 679 680 std::vector<std::string> splitWords; 681 boost::algorithm::split(splitWords, output[t2OutputIndex], 682 boost::algorithm::is_any_of("\t "), 683 boost::token_compress_on); 684 685 try 686 { 687 unsigned long tmp = std::stoul(splitWords[t2FailCntIdx], nullptr); 688 uint16_t value16 = 0; 689 if (tmp > std::numeric_limits<decltype(value16)>::max()) 690 { 691 throw std::out_of_range("Out of range"); 692 } 693 value16 = static_cast<decltype(value16)>(tmp); 694 if (AccountPolicyIface::maxLoginAttemptBeforeLockout() != 0 && 695 value16 >= AccountPolicyIface::maxLoginAttemptBeforeLockout()) 696 { 697 return true; // User account is locked out 698 } 699 return false; // User account is un-locked 700 } 701 catch (const std::exception &e) 702 { 703 log<level::ERR>("Exception for userLockedForFailedAttempt", 704 entry("WHAT=%s", e.what())); 705 throw; 706 } 707 } 708 709 bool UserMgr::userLockedForFailedAttempt(const std::string &userName, 710 const bool &value) 711 { 712 // All user management lock has to be based on /etc/shadow 713 phosphor::user::shadow::Lock lock(); 714 std::vector<std::string> output; 715 if (value == true) 716 { 717 return userLockedForFailedAttempt(userName); 718 } 719 output = executeCmd("/usr/sbin/pam_tally2", "-u", userName.c_str(), "-r"); 720 721 std::vector<std::string> splitWords; 722 boost::algorithm::split(splitWords, output[t2OutputIndex], 723 boost::algorithm::is_any_of("\t "), 724 boost::token_compress_on); 725 726 return userLockedForFailedAttempt(userName); 727 } 728 729 UserSSHLists UserMgr::getUserAndSshGrpList() 730 { 731 // All user management lock has to be based on /etc/shadow 732 phosphor::user::shadow::Lock lock(); 733 734 std::vector<std::string> userList; 735 std::vector<std::string> sshUsersList; 736 struct passwd pw, *pwp = nullptr; 737 std::array<char, 1024> buffer{}; 738 739 phosphor::user::File passwd(passwdFileName, "r"); 740 if ((passwd)() == NULL) 741 { 742 log<level::ERR>("Error opening the passwd file"); 743 elog<InternalFailure>(); 744 } 745 746 while (true) 747 { 748 auto r = fgetpwent_r((passwd)(), &pw, buffer.data(), buffer.max_size(), 749 &pwp); 750 if ((r != 0) || (pwp == NULL)) 751 { 752 // Any error, break the loop. 753 break; 754 } 755 // Add all users whose UID >= 1000 and < 65534 756 // and special UID 0. 757 if ((pwp->pw_uid == 0) || 758 ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534))) 759 { 760 std::string userName(pwp->pw_name); 761 userList.emplace_back(userName); 762 763 // ssh doesn't have separate group. Check login shell entry to 764 // get all users list which are member of ssh group. 765 std::string loginShell(pwp->pw_shell); 766 if (loginShell == "/bin/sh") 767 { 768 sshUsersList.emplace_back(userName); 769 } 770 } 771 } 772 endpwent(); 773 return std::make_pair(std::move(userList), std::move(sshUsersList)); 774 } 775 776 size_t UserMgr::getIpmiUsersCount() 777 { 778 std::vector<std::string> userList = getUsersInGroup("ipmi"); 779 return userList.size(); 780 } 781 782 bool UserMgr::isUserEnabled(const std::string &userName) 783 { 784 // All user management lock has to be based on /etc/shadow 785 phosphor::user::shadow::Lock lock(); 786 std::array<char, 4096> buffer{}; 787 struct spwd spwd; 788 struct spwd *resultPtr = nullptr; 789 int status = getspnam_r(userName.c_str(), &spwd, buffer.data(), 790 buffer.max_size(), &resultPtr); 791 if (!status && (&spwd == resultPtr)) 792 { 793 if (resultPtr->sp_expire >= 0) 794 { 795 return false; // user locked out 796 } 797 return true; 798 } 799 return false; // assume user is disabled for any error. 800 } 801 802 std::vector<std::string> UserMgr::getUsersInGroup(const std::string &groupName) 803 { 804 std::vector<std::string> usersInGroup; 805 // Should be more than enough to get the pwd structure. 806 std::array<char, 4096> buffer{}; 807 struct group grp; 808 struct group *resultPtr = nullptr; 809 810 int status = getgrnam_r(groupName.c_str(), &grp, buffer.data(), 811 buffer.max_size(), &resultPtr); 812 813 if (!status && (&grp == resultPtr)) 814 { 815 for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem)) 816 { 817 usersInGroup.emplace_back(*(grp.gr_mem)); 818 } 819 } 820 else 821 { 822 log<level::ERR>("Group not found", 823 entry("GROUP=%s", groupName.c_str())); 824 // Don't throw error, just return empty userList - fallback 825 } 826 return usersInGroup; 827 } 828 829 DbusUserObj UserMgr::getPrivilegeMapperObject(void) 830 { 831 DbusUserObj objects; 832 try 833 { 834 std::string basePath = "/xyz/openbmc_project/user/ldap/openldap"; 835 std::string interface = "xyz.openbmc_project.User.Ldap.Config"; 836 837 auto ldapMgmtService = 838 getServiceName(std::move(basePath), std::move(interface)); 839 auto method = bus.new_method_call( 840 ldapMgmtService.c_str(), ldapMgrObjBasePath, 841 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 842 843 auto reply = bus.call(method); 844 reply.read(objects); 845 } 846 catch (const InternalFailure &e) 847 { 848 log<level::ERR>("Unable to get the User Service", 849 entry("WHAT=%s", e.what())); 850 throw; 851 } 852 catch (const sdbusplus::exception::SdBusError &e) 853 { 854 log<level::ERR>( 855 "Failed to excute method", entry("METHOD=%s", "GetManagedObjects"), 856 entry("PATH=%s", ldapMgrObjBasePath), entry("WHAT=%s", e.what())); 857 throw; 858 } 859 return objects; 860 } 861 862 std::string UserMgr::getLdapGroupName(const std::string &userName) 863 { 864 struct passwd pwd 865 { 866 }; 867 struct passwd *pwdPtr = nullptr; 868 auto buflen = sysconf(_SC_GETPW_R_SIZE_MAX); 869 if (buflen < -1) 870 { 871 // Use a default size if there is no hard limit suggested by sysconf() 872 buflen = 1024; 873 } 874 std::vector<char> buffer(buflen); 875 gid_t gid = 0; 876 877 auto status = 878 getpwnam_r(userName.c_str(), &pwd, buffer.data(), buflen, &pwdPtr); 879 // On success, getpwnam_r() returns zero, and set *pwdPtr to pwd. 880 // If no matching password record was found, these functions return 0 881 // and store NULL in *pwdPtr 882 if (!status && (&pwd == pwdPtr)) 883 { 884 gid = pwd.pw_gid; 885 } 886 else 887 { 888 log<level::ERR>("User does not exist", 889 entry("USER_NAME=%s", userName.c_str())); 890 elog<UserNameDoesNotExist>(); 891 } 892 893 struct group *groups = nullptr; 894 std::string ldapGroupName; 895 896 while ((groups = getgrent()) != NULL) 897 { 898 if (groups->gr_gid == gid) 899 { 900 ldapGroupName = groups->gr_name; 901 break; 902 } 903 } 904 // Call endgrent() to close the group database. 905 endgrent(); 906 907 return ldapGroupName; 908 } 909 910 std::string UserMgr::getServiceName(std::string &&path, std::string &&intf) 911 { 912 auto mapperCall = bus.new_method_call(objMapperService, objMapperPath, 913 objMapperInterface, "GetObject"); 914 915 mapperCall.append(std::move(path)); 916 mapperCall.append(std::vector<std::string>({std::move(intf)})); 917 918 auto mapperResponseMsg = bus.call(mapperCall); 919 920 if (mapperResponseMsg.is_method_error()) 921 { 922 log<level::ERR>("Error in mapper call"); 923 elog<InternalFailure>(); 924 } 925 926 std::map<std::string, std::vector<std::string>> mapperResponse; 927 mapperResponseMsg.read(mapperResponse); 928 929 if (mapperResponse.begin() == mapperResponse.end()) 930 { 931 log<level::ERR>("Invalid response from mapper"); 932 elog<InternalFailure>(); 933 } 934 935 return mapperResponse.begin()->first; 936 } 937 938 UserInfoMap UserMgr::getUserInfo(std::string userName) 939 { 940 UserInfoMap userInfo; 941 // Check whether the given user is local user or not. 942 if (isUserExist(userName) == true) 943 { 944 const auto &user = usersList[userName]; 945 userInfo.emplace("UserPrivilege", user.get()->userPrivilege()); 946 userInfo.emplace("UserGroups", user.get()->userGroups()); 947 userInfo.emplace("UserEnabled", user.get()->userEnabled()); 948 userInfo.emplace("UserLockedForFailedAttempt", 949 user.get()->userLockedForFailedAttempt()); 950 userInfo.emplace("RemoteUser", false); 951 } 952 else 953 { 954 std::string ldapGroupName = getLdapGroupName(userName); 955 if (ldapGroupName.empty()) 956 { 957 log<level::ERR>("Unable to get group name", 958 entry("USER_NAME=%s", userName.c_str())); 959 elog<InternalFailure>(); 960 } 961 962 DbusUserObj objects = getPrivilegeMapperObject(); 963 964 std::string privilege; 965 std::string groupName; 966 std::string ldapConfigPath; 967 968 try 969 { 970 for (const auto &obj : objects) 971 { 972 for (const auto &interface : obj.second) 973 { 974 if ((interface.first == 975 "xyz.openbmc_project.Object.Enable")) 976 { 977 for (const auto &property : interface.second) 978 { 979 auto value = 980 sdbusplus::message::variant_ns::get<bool>( 981 property.second); 982 if ((property.first == "Enabled") && 983 (value == true)) 984 { 985 ldapConfigPath = obj.first; 986 break; 987 } 988 } 989 } 990 } 991 if (!ldapConfigPath.empty()) 992 { 993 break; 994 } 995 } 996 997 if (ldapConfigPath.empty()) 998 { 999 return userInfo; 1000 } 1001 1002 for (const auto &obj : objects) 1003 { 1004 for (const auto &interface : obj.second) 1005 { 1006 if ((interface.first == 1007 "xyz.openbmc_project.User.PrivilegeMapperEntry") && 1008 (obj.first.str.find(ldapConfigPath) != 1009 std::string::npos)) 1010 { 1011 1012 for (const auto &property : interface.second) 1013 { 1014 auto value = sdbusplus::message::variant_ns::get< 1015 std::string>(property.second); 1016 if (property.first == "GroupName") 1017 { 1018 groupName = value; 1019 } 1020 else if (property.first == "Privilege") 1021 { 1022 privilege = value; 1023 } 1024 if (groupName == ldapGroupName) 1025 { 1026 userInfo["UserPrivilege"] = privilege; 1027 } 1028 } 1029 } 1030 } 1031 } 1032 auto priv = std::get<std::string>(userInfo["UserPrivilege"]); 1033 1034 if (priv.empty()) 1035 { 1036 log<level::ERR>("LDAP group privilege mapping does not exist"); 1037 } 1038 } 1039 catch (const std::bad_variant_access &e) 1040 { 1041 log<level::ERR>("Error while accessing variant", 1042 entry("WHAT=%s", e.what())); 1043 elog<InternalFailure>(); 1044 } 1045 userInfo.emplace("RemoteUser", true); 1046 } 1047 1048 return userInfo; 1049 } 1050 1051 void UserMgr::initUserObjects(void) 1052 { 1053 // All user management lock has to be based on /etc/shadow 1054 phosphor::user::shadow::Lock lock(); 1055 std::vector<std::string> userNameList; 1056 std::vector<std::string> sshGrpUsersList; 1057 UserSSHLists userSSHLists = getUserAndSshGrpList(); 1058 userNameList = std::move(userSSHLists.first); 1059 sshGrpUsersList = std::move(userSSHLists.second); 1060 1061 if (!userNameList.empty()) 1062 { 1063 std::map<std::string, std::vector<std::string>> groupLists; 1064 for (auto &grp : groupsMgr) 1065 { 1066 if (grp == grpSsh) 1067 { 1068 groupLists.emplace(grp, sshGrpUsersList); 1069 } 1070 else 1071 { 1072 std::vector<std::string> grpUsersList = getUsersInGroup(grp); 1073 groupLists.emplace(grp, grpUsersList); 1074 } 1075 } 1076 for (auto &grp : privMgr) 1077 { 1078 std::vector<std::string> grpUsersList = getUsersInGroup(grp); 1079 groupLists.emplace(grp, grpUsersList); 1080 } 1081 1082 for (auto &user : userNameList) 1083 { 1084 std::vector<std::string> userGroups; 1085 std::string userPriv; 1086 for (const auto &grp : groupLists) 1087 { 1088 std::vector<std::string> tempGrp = grp.second; 1089 if (std::find(tempGrp.begin(), tempGrp.end(), user) != 1090 tempGrp.end()) 1091 { 1092 if (std::find(privMgr.begin(), privMgr.end(), grp.first) != 1093 privMgr.end()) 1094 { 1095 userPriv = grp.first; 1096 } 1097 else 1098 { 1099 userGroups.emplace_back(grp.first); 1100 } 1101 } 1102 } 1103 // Add user objects to the Users path. 1104 auto objPath = std::string(usersObjPath) + "/" + user; 1105 std::sort(userGroups.begin(), userGroups.end()); 1106 usersList.emplace(user, 1107 std::move(std::make_unique<phosphor::user::Users>( 1108 bus, objPath.c_str(), userGroups, userPriv, 1109 isUserEnabled(user), *this))); 1110 } 1111 } 1112 } 1113 1114 UserMgr::UserMgr(sdbusplus::bus::bus &bus, const char *path) : 1115 Ifaces(bus, path, true), bus(bus), path(path) 1116 { 1117 UserMgrIface::allPrivileges(privMgr); 1118 std::sort(groupsMgr.begin(), groupsMgr.end()); 1119 UserMgrIface::allGroups(groupsMgr); 1120 std::string valueStr; 1121 auto value = minPasswdLength; 1122 unsigned long tmp = 0; 1123 if (getPamModuleArgValue(pamCrackLib, minPasswdLenProp, valueStr) != 1124 success) 1125 { 1126 AccountPolicyIface::minPasswordLength(minPasswdLength); 1127 } 1128 else 1129 { 1130 try 1131 { 1132 tmp = std::stoul(valueStr, nullptr); 1133 if (tmp > std::numeric_limits<decltype(value)>::max()) 1134 { 1135 throw std::out_of_range("Out of range"); 1136 } 1137 value = static_cast<decltype(value)>(tmp); 1138 } 1139 catch (const std::exception &e) 1140 { 1141 log<level::ERR>("Exception for MinPasswordLength", 1142 entry("WHAT=%s", e.what())); 1143 throw; 1144 } 1145 AccountPolicyIface::minPasswordLength(value); 1146 } 1147 valueStr.clear(); 1148 if (getPamModuleArgValue(pamPWHistory, remOldPasswdCount, valueStr) != 1149 success) 1150 { 1151 AccountPolicyIface::rememberOldPasswordTimes(0); 1152 } 1153 else 1154 { 1155 value = 0; 1156 try 1157 { 1158 tmp = std::stoul(valueStr, nullptr); 1159 if (tmp > std::numeric_limits<decltype(value)>::max()) 1160 { 1161 throw std::out_of_range("Out of range"); 1162 } 1163 value = static_cast<decltype(value)>(tmp); 1164 } 1165 catch (const std::exception &e) 1166 { 1167 log<level::ERR>("Exception for RememberOldPasswordTimes", 1168 entry("WHAT=%s", e.what())); 1169 throw; 1170 } 1171 AccountPolicyIface::rememberOldPasswordTimes(value); 1172 } 1173 valueStr.clear(); 1174 if (getPamModuleArgValue(pamTally2, maxFailedAttempt, valueStr) != success) 1175 { 1176 AccountPolicyIface::maxLoginAttemptBeforeLockout(0); 1177 } 1178 else 1179 { 1180 uint16_t value16 = 0; 1181 try 1182 { 1183 tmp = std::stoul(valueStr, nullptr); 1184 if (tmp > std::numeric_limits<decltype(value16)>::max()) 1185 { 1186 throw std::out_of_range("Out of range"); 1187 } 1188 value16 = static_cast<decltype(value16)>(tmp); 1189 } 1190 catch (const std::exception &e) 1191 { 1192 log<level::ERR>("Exception for MaxLoginAttemptBeforLockout", 1193 entry("WHAT=%s", e.what())); 1194 throw; 1195 } 1196 AccountPolicyIface::maxLoginAttemptBeforeLockout(value16); 1197 } 1198 valueStr.clear(); 1199 if (getPamModuleArgValue(pamTally2, unlockTimeout, valueStr) != success) 1200 { 1201 AccountPolicyIface::accountUnlockTimeout(0); 1202 } 1203 else 1204 { 1205 uint32_t value32 = 0; 1206 try 1207 { 1208 tmp = std::stoul(valueStr, nullptr); 1209 if (tmp > std::numeric_limits<decltype(value32)>::max()) 1210 { 1211 throw std::out_of_range("Out of range"); 1212 } 1213 value32 = static_cast<decltype(value32)>(tmp); 1214 } 1215 catch (const std::exception &e) 1216 { 1217 log<level::ERR>("Exception for AccountUnlockTimeout", 1218 entry("WHAT=%s", e.what())); 1219 throw; 1220 } 1221 AccountPolicyIface::accountUnlockTimeout(value32); 1222 } 1223 initUserObjects(); 1224 1225 // emit the signal 1226 this->emit_object_added(); 1227 } 1228 1229 } // namespace user 1230 } // namespace phosphor 1231