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