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