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