blob: 0447fe76ddbd09a784e8fc6644d7efdf66013b6f [file] [log] [blame]
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +05301/*
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>
Joseph Reynolds3ab6cc22020-03-03 14:09:03 -060021#include <time.h>
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +053022#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>
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +053029#include <boost/process/io.hpp>
30#include <boost/algorithm/string/split.hpp>
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +053031#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
42namespace phosphor
43{
44namespace user
45{
46
47static constexpr const char *passwdFileName = "/etc/passwd";
48static constexpr size_t ipmiMaxUsers = 15;
49static constexpr size_t ipmiMaxUserNameLen = 16;
50static constexpr size_t systemMaxUserNameLen = 30;
51static constexpr size_t maxSystemUsers = 30;
52static constexpr const char *grpSsh = "ssh";
Richard Marian Thomaiyar9164fd92018-06-13 16:51:00 +053053static constexpr uint8_t minPasswdLength = 8;
54static constexpr int success = 0;
55static constexpr int failure = -1;
56
57// pam modules related
58static constexpr const char *pamTally2 = "pam_tally2.so";
59static constexpr const char *pamCrackLib = "pam_cracklib.so";
60static constexpr const char *pamPWHistory = "pam_pwhistory.so";
61static constexpr const char *minPasswdLenProp = "minlen";
62static constexpr const char *remOldPasswdCount = "remember";
63static constexpr const char *maxFailedAttempt = "deny";
64static constexpr const char *unlockTimeout = "unlock_time";
65static constexpr const char *pamPasswdConfigFile = "/etc/pam.d/common-password";
66static constexpr const char *pamAuthConfigFile = "/etc/pam.d/common-auth";
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +053067
Ratan Guptaaeaf9412019-02-11 04:41:52 -060068// Object Manager related
69static constexpr const char *ldapMgrObjBasePath =
70 "/xyz/openbmc_project/user/ldap";
71
72// Object Mapper related
73static constexpr const char *objMapperService =
74 "xyz.openbmc_project.ObjectMapper";
75static constexpr const char *objMapperPath =
76 "/xyz/openbmc_project/object_mapper";
77static constexpr const char *objMapperInterface =
78 "xyz.openbmc_project.ObjectMapper";
79
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +053080using namespace phosphor::logging;
81using InsufficientPermission =
82 sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission;
83using InternalFailure =
84 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
85using InvalidArgument =
86 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
87using UserNameExists =
88 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists;
89using UserNameDoesNotExist =
90 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameDoesNotExist;
91using UserNameGroupFail =
92 sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameGroupFail;
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +053093using NoResource =
94 sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource;
95
96using Argument = xyz::openbmc_project::Common::InvalidArgument;
97
98template <typename... ArgTypes>
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +053099static std::vector<std::string> executeCmd(const char *path,
100 ArgTypes &&... tArgs)
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530101{
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530102 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
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530114 execProg.wait();
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530115
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530116 int retCode = execProg.exit_code();
117 if (retCode)
118 {
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530119 log<level::ERR>("Command execution failed", entry("PATH=%d", path),
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530120 entry("RETURN_CODE:%d", retCode));
121 elog<InternalFailure>();
122 }
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530123
124 return stdOutput;
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530125}
126
127static 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
152static 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
171bool 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
186void 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
196void 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
206void 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
238void 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
266void 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
277void 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
291void 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.
Richard Marian Thomaiyar2cb2e722018-09-27 14:22:42 +0530308 if (!priv.empty())
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530309 {
Richard Marian Thomaiyar2cb2e722018-09-27 14:22:42 +0530310 if (groups.size() != 0)
311 {
312 groups += ",";
313 }
314 groups += priv;
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530315 }
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530316 try
317 {
318 executeCmd("/usr/sbin/useradd", userName.c_str(), "-G", groups.c_str(),
Richard Marian Thomaiyarf977b1a2018-07-16 23:50:51 +0530319 "-m", "-N", "-s",
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530320 (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
341void 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 {
Richard Marian Thomaiyarf977b1a2018-07-16 23:50:51 +0530348 executeCmd("/usr/sbin/userdel", userName.c_str(), "-r");
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530349 }
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
364void 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 {
Richard Marian Thomaiyarf977b1a2018-07-16 23:50:51 +0530374 std::string newHomeDir = "/home/" + newUserName;
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530375 executeCmd("/usr/sbin/usermod", "-l", newUserName.c_str(),
Richard Marian Thomaiyarf977b1a2018-07-16 23:50:51 +0530376 userName.c_str(), "-d", newHomeDir.c_str(), "-m");
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530377 }
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
401void 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.
Richard Marian Thomaiyar2cb2e722018-09-27 14:22:42 +0530429 if (!priv.empty())
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530430 {
Richard Marian Thomaiyar2cb2e722018-09-27 14:22:42 +0530431 if (groups.size() != 0)
432 {
433 groups += ",";
434 }
435 groups += priv;
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530436 }
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530437 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
Richard Marian Thomaiyar9164fd92018-06-13 16:51:00 +0530453uint8_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
472uint8_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
487uint16_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
502uint32_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
517int 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
570int 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
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530640void 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
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530662/**
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
669static constexpr size_t t2UserIdx = 0;
670static constexpr size_t t2FailCntIdx = 1;
671static constexpr size_t t2OutputIndex = 1;
672
673bool 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
Richard Marian Thomaiyarf5c2df52018-11-22 23:24:25 +0530686 try
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530687 {
Richard Marian Thomaiyarf5c2df52018-11-22 23:24:25 +0530688 unsigned long tmp = std::stoul(splitWords[t2FailCntIdx], nullptr);
689 uint16_t value16 = 0;
690 if (tmp > std::numeric_limits<decltype(value16)>::max())
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530691 {
Richard Marian Thomaiyarf5c2df52018-11-22 23:24:25 +0530692 throw std::out_of_range("Out of range");
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530693 }
Richard Marian Thomaiyarf5c2df52018-11-22 23:24:25 +0530694 value16 = static_cast<decltype(value16)>(tmp);
695 if (AccountPolicyIface::maxLoginAttemptBeforeLockout() != 0 &&
696 value16 >= AccountPolicyIface::maxLoginAttemptBeforeLockout())
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530697 {
Richard Marian Thomaiyarf5c2df52018-11-22 23:24:25 +0530698 return true; // User account is locked out
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530699 }
Richard Marian Thomaiyarf5c2df52018-11-22 23:24:25 +0530700 return false; // User account is un-locked
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530701 }
Richard Marian Thomaiyarf5c2df52018-11-22 23:24:25 +0530702 catch (const std::exception &e)
703 {
704 log<level::ERR>("Exception for userLockedForFailedAttempt",
705 entry("WHAT=%s", e.what()));
706 throw;
707 }
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530708}
709
710bool 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
Richard Marian Thomaiyarf5c2df52018-11-22 23:24:25 +0530727 return userLockedForFailedAttempt(userName);
Richard Marian Thomaiyarc7045192018-06-13 16:51:00 +0530728}
729
Joseph Reynolds3ab6cc22020-03-03 14:09:03 -0600730bool 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 {
Jayaprakash Mutyala75be4e62020-09-18 15:59:06 +0000766 // User entry is missing in /etc/shadow, indicating no SHA password.
767 // Treat this as new user without password entry in /etc/shadow
768 // TODO: Add property to indicate user password was not set yet
769 // https://github.com/openbmc/phosphor-user-manager/issues/8
770 return false;
Joseph Reynolds3ab6cc22020-03-03 14:09:03 -0600771 }
772
773 return false;
774}
775
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530776UserSSHLists UserMgr::getUserAndSshGrpList()
777{
778 // All user management lock has to be based on /etc/shadow
779 phosphor::user::shadow::Lock lock();
780
781 std::vector<std::string> userList;
782 std::vector<std::string> sshUsersList;
783 struct passwd pw, *pwp = nullptr;
784 std::array<char, 1024> buffer{};
785
786 phosphor::user::File passwd(passwdFileName, "r");
787 if ((passwd)() == NULL)
788 {
789 log<level::ERR>("Error opening the passwd file");
790 elog<InternalFailure>();
791 }
792
793 while (true)
794 {
795 auto r = fgetpwent_r((passwd)(), &pw, buffer.data(), buffer.max_size(),
796 &pwp);
797 if ((r != 0) || (pwp == NULL))
798 {
799 // Any error, break the loop.
800 break;
801 }
Richard Marian Thomaiyard4d65502019-11-02 21:02:03 +0530802#ifdef ENABLE_ROOT_USER_MGMT
Richard Marian Thomaiyar7ba3c712018-07-31 13:41:36 +0530803 // Add all users whose UID >= 1000 and < 65534
804 // and special UID 0.
805 if ((pwp->pw_uid == 0) ||
806 ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534)))
Richard Marian Thomaiyard4d65502019-11-02 21:02:03 +0530807#else
808 // Add all users whose UID >=1000 and < 65534
809 if ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534))
810#endif
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +0530811 {
812 std::string userName(pwp->pw_name);
813 userList.emplace_back(userName);
814
815 // ssh doesn't have separate group. Check login shell entry to
816 // get all users list which are member of ssh group.
817 std::string loginShell(pwp->pw_shell);
818 if (loginShell == "/bin/sh")
819 {
820 sshUsersList.emplace_back(userName);
821 }
822 }
823 }
824 endpwent();
825 return std::make_pair(std::move(userList), std::move(sshUsersList));
826}
827
828size_t UserMgr::getIpmiUsersCount()
829{
830 std::vector<std::string> userList = getUsersInGroup("ipmi");
831 return userList.size();
832}
833
834bool UserMgr::isUserEnabled(const std::string &userName)
835{
836 // All user management lock has to be based on /etc/shadow
837 phosphor::user::shadow::Lock lock();
838 std::array<char, 4096> buffer{};
839 struct spwd spwd;
840 struct spwd *resultPtr = nullptr;
841 int status = getspnam_r(userName.c_str(), &spwd, buffer.data(),
842 buffer.max_size(), &resultPtr);
843 if (!status && (&spwd == resultPtr))
844 {
845 if (resultPtr->sp_expire >= 0)
846 {
847 return false; // user locked out
848 }
849 return true;
850 }
851 return false; // assume user is disabled for any error.
852}
853
854std::vector<std::string> UserMgr::getUsersInGroup(const std::string &groupName)
855{
856 std::vector<std::string> usersInGroup;
857 // Should be more than enough to get the pwd structure.
858 std::array<char, 4096> buffer{};
859 struct group grp;
860 struct group *resultPtr = nullptr;
861
862 int status = getgrnam_r(groupName.c_str(), &grp, buffer.data(),
863 buffer.max_size(), &resultPtr);
864
865 if (!status && (&grp == resultPtr))
866 {
867 for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem))
868 {
869 usersInGroup.emplace_back(*(grp.gr_mem));
870 }
871 }
872 else
873 {
874 log<level::ERR>("Group not found",
875 entry("GROUP=%s", groupName.c_str()));
876 // Don't throw error, just return empty userList - fallback
877 }
878 return usersInGroup;
879}
880
Ratan Guptaaeaf9412019-02-11 04:41:52 -0600881DbusUserObj UserMgr::getPrivilegeMapperObject(void)
882{
883 DbusUserObj objects;
884 try
885 {
Ravi Teja5fe724a2019-05-07 05:14:42 -0500886 std::string basePath = "/xyz/openbmc_project/user/ldap/openldap";
887 std::string interface = "xyz.openbmc_project.User.Ldap.Config";
Ratan Guptaaeaf9412019-02-11 04:41:52 -0600888
889 auto ldapMgmtService =
890 getServiceName(std::move(basePath), std::move(interface));
Ratan Guptaaeaf9412019-02-11 04:41:52 -0600891 auto method = bus.new_method_call(
892 ldapMgmtService.c_str(), ldapMgrObjBasePath,
893 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
894
895 auto reply = bus.call(method);
896 reply.read(objects);
897 }
898 catch (const InternalFailure &e)
899 {
900 log<level::ERR>("Unable to get the User Service",
901 entry("WHAT=%s", e.what()));
902 throw;
903 }
904 catch (const sdbusplus::exception::SdBusError &e)
905 {
906 log<level::ERR>(
907 "Failed to excute method", entry("METHOD=%s", "GetManagedObjects"),
908 entry("PATH=%s", ldapMgrObjBasePath), entry("WHAT=%s", e.what()));
909 throw;
910 }
911 return objects;
912}
913
914std::string UserMgr::getLdapGroupName(const std::string &userName)
915{
916 struct passwd pwd
917 {
918 };
919 struct passwd *pwdPtr = nullptr;
920 auto buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
921 if (buflen < -1)
922 {
923 // Use a default size if there is no hard limit suggested by sysconf()
924 buflen = 1024;
925 }
926 std::vector<char> buffer(buflen);
927 gid_t gid = 0;
928
929 auto status =
930 getpwnam_r(userName.c_str(), &pwd, buffer.data(), buflen, &pwdPtr);
931 // On success, getpwnam_r() returns zero, and set *pwdPtr to pwd.
932 // If no matching password record was found, these functions return 0
933 // and store NULL in *pwdPtr
934 if (!status && (&pwd == pwdPtr))
935 {
936 gid = pwd.pw_gid;
937 }
938 else
939 {
940 log<level::ERR>("User does not exist",
941 entry("USER_NAME=%s", userName.c_str()));
942 elog<UserNameDoesNotExist>();
943 }
944
945 struct group *groups = nullptr;
946 std::string ldapGroupName;
947
948 while ((groups = getgrent()) != NULL)
949 {
950 if (groups->gr_gid == gid)
951 {
952 ldapGroupName = groups->gr_name;
953 break;
954 }
955 }
956 // Call endgrent() to close the group database.
957 endgrent();
958
959 return ldapGroupName;
960}
961
962std::string UserMgr::getServiceName(std::string &&path, std::string &&intf)
963{
964 auto mapperCall = bus.new_method_call(objMapperService, objMapperPath,
965 objMapperInterface, "GetObject");
966
967 mapperCall.append(std::move(path));
968 mapperCall.append(std::vector<std::string>({std::move(intf)}));
969
970 auto mapperResponseMsg = bus.call(mapperCall);
971
972 if (mapperResponseMsg.is_method_error())
973 {
974 log<level::ERR>("Error in mapper call");
975 elog<InternalFailure>();
976 }
977
978 std::map<std::string, std::vector<std::string>> mapperResponse;
979 mapperResponseMsg.read(mapperResponse);
980
981 if (mapperResponse.begin() == mapperResponse.end())
982 {
983 log<level::ERR>("Invalid response from mapper");
984 elog<InternalFailure>();
985 }
986
987 return mapperResponse.begin()->first;
988}
989
990UserInfoMap UserMgr::getUserInfo(std::string userName)
991{
992 UserInfoMap userInfo;
993 // Check whether the given user is local user or not.
994 if (isUserExist(userName) == true)
995 {
996 const auto &user = usersList[userName];
997 userInfo.emplace("UserPrivilege", user.get()->userPrivilege());
998 userInfo.emplace("UserGroups", user.get()->userGroups());
999 userInfo.emplace("UserEnabled", user.get()->userEnabled());
1000 userInfo.emplace("UserLockedForFailedAttempt",
1001 user.get()->userLockedForFailedAttempt());
Joseph Reynolds3ab6cc22020-03-03 14:09:03 -06001002 userInfo.emplace("UserPasswordExpired",
1003 user.get()->userPasswordExpired());
Ratan Guptaaeaf9412019-02-11 04:41:52 -06001004 userInfo.emplace("RemoteUser", false);
1005 }
1006 else
1007 {
1008 std::string ldapGroupName = getLdapGroupName(userName);
1009 if (ldapGroupName.empty())
1010 {
1011 log<level::ERR>("Unable to get group name",
1012 entry("USER_NAME=%s", userName.c_str()));
1013 elog<InternalFailure>();
1014 }
1015
1016 DbusUserObj objects = getPrivilegeMapperObject();
1017
1018 std::string privilege;
1019 std::string groupName;
Ravi Teja5fe724a2019-05-07 05:14:42 -05001020 std::string ldapConfigPath;
Ratan Guptaaeaf9412019-02-11 04:41:52 -06001021
1022 try
1023 {
Ravi Teja5fe724a2019-05-07 05:14:42 -05001024 for (const auto &obj : objects)
Ratan Guptaaeaf9412019-02-11 04:41:52 -06001025 {
Ravi Teja5fe724a2019-05-07 05:14:42 -05001026 for (const auto &interface : obj.second)
Ratan Guptaaeaf9412019-02-11 04:41:52 -06001027 {
Ravi Teja5fe724a2019-05-07 05:14:42 -05001028 if ((interface.first ==
1029 "xyz.openbmc_project.Object.Enable"))
1030 {
1031 for (const auto &property : interface.second)
1032 {
Patrick Williams8f8fc232020-05-13 12:25:51 -05001033 auto value = std::get<bool>(property.second);
Ravi Teja5fe724a2019-05-07 05:14:42 -05001034 if ((property.first == "Enabled") &&
1035 (value == true))
1036 {
1037 ldapConfigPath = obj.first;
1038 break;
1039 }
1040 }
1041 }
Ratan Guptaaeaf9412019-02-11 04:41:52 -06001042 }
Ravi Teja5fe724a2019-05-07 05:14:42 -05001043 if (!ldapConfigPath.empty())
Ratan Guptaaeaf9412019-02-11 04:41:52 -06001044 {
Ravi Teja5fe724a2019-05-07 05:14:42 -05001045 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))
Ratan Guptaaeaf9412019-02-11 04:41:52 -06001062 {
Ravi Teja5fe724a2019-05-07 05:14:42 -05001063
1064 for (const auto &property : interface.second)
1065 {
Patrick Williams8f8fc232020-05-13 12:25:51 -05001066 auto value = std::get<std::string>(property.second);
Ravi Teja5fe724a2019-05-07 05:14:42 -05001067 if (property.first == "GroupName")
1068 {
1069 groupName = value;
1070 }
1071 else if (property.first == "Privilege")
1072 {
1073 privilege = value;
1074 }
1075 if (groupName == ldapGroupName)
1076 {
1077 userInfo["UserPrivilege"] = privilege;
1078 }
1079 }
Ratan Guptaaeaf9412019-02-11 04:41:52 -06001080 }
1081 }
1082 }
1083 auto priv = std::get<std::string>(userInfo["UserPrivilege"]);
Ravi Teja5fe724a2019-05-07 05:14:42 -05001084
Ratan Guptaaeaf9412019-02-11 04:41:52 -06001085 if (priv.empty())
1086 {
1087 log<level::ERR>("LDAP group privilege mapping does not exist");
1088 }
1089 }
1090 catch (const std::bad_variant_access &e)
1091 {
1092 log<level::ERR>("Error while accessing variant",
1093 entry("WHAT=%s", e.what()));
1094 elog<InternalFailure>();
1095 }
1096 userInfo.emplace("RemoteUser", true);
1097 }
1098
1099 return userInfo;
1100}
1101
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +05301102void UserMgr::initUserObjects(void)
1103{
1104 // All user management lock has to be based on /etc/shadow
1105 phosphor::user::shadow::Lock lock();
1106 std::vector<std::string> userNameList;
1107 std::vector<std::string> sshGrpUsersList;
1108 UserSSHLists userSSHLists = getUserAndSshGrpList();
1109 userNameList = std::move(userSSHLists.first);
1110 sshGrpUsersList = std::move(userSSHLists.second);
1111
1112 if (!userNameList.empty())
1113 {
1114 std::map<std::string, std::vector<std::string>> groupLists;
1115 for (auto &grp : groupsMgr)
1116 {
1117 if (grp == grpSsh)
1118 {
1119 groupLists.emplace(grp, sshGrpUsersList);
1120 }
1121 else
1122 {
1123 std::vector<std::string> grpUsersList = getUsersInGroup(grp);
1124 groupLists.emplace(grp, grpUsersList);
1125 }
1126 }
1127 for (auto &grp : privMgr)
1128 {
1129 std::vector<std::string> grpUsersList = getUsersInGroup(grp);
1130 groupLists.emplace(grp, grpUsersList);
1131 }
1132
1133 for (auto &user : userNameList)
1134 {
1135 std::vector<std::string> userGroups;
1136 std::string userPriv;
1137 for (const auto &grp : groupLists)
1138 {
1139 std::vector<std::string> tempGrp = grp.second;
1140 if (std::find(tempGrp.begin(), tempGrp.end(), user) !=
1141 tempGrp.end())
1142 {
1143 if (std::find(privMgr.begin(), privMgr.end(), grp.first) !=
1144 privMgr.end())
1145 {
1146 userPriv = grp.first;
1147 }
1148 else
1149 {
1150 userGroups.emplace_back(grp.first);
1151 }
1152 }
1153 }
1154 // Add user objects to the Users path.
1155 auto objPath = std::string(usersObjPath) + "/" + user;
1156 std::sort(userGroups.begin(), userGroups.end());
1157 usersList.emplace(user,
1158 std::move(std::make_unique<phosphor::user::Users>(
1159 bus, objPath.c_str(), userGroups, userPriv,
1160 isUserEnabled(user), *this)));
1161 }
1162 }
1163}
1164
1165UserMgr::UserMgr(sdbusplus::bus::bus &bus, const char *path) :
Ratan Gupta1af12232018-11-03 00:35:38 +05301166 Ifaces(bus, path, true), bus(bus), path(path)
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +05301167{
1168 UserMgrIface::allPrivileges(privMgr);
1169 std::sort(groupsMgr.begin(), groupsMgr.end());
1170 UserMgrIface::allGroups(groupsMgr);
Richard Marian Thomaiyar9164fd92018-06-13 16:51:00 +05301171 std::string valueStr;
1172 auto value = minPasswdLength;
1173 unsigned long tmp = 0;
1174 if (getPamModuleArgValue(pamCrackLib, minPasswdLenProp, valueStr) !=
1175 success)
1176 {
1177 AccountPolicyIface::minPasswordLength(minPasswdLength);
1178 }
1179 else
1180 {
1181 try
1182 {
1183 tmp = std::stoul(valueStr, nullptr);
1184 if (tmp > std::numeric_limits<decltype(value)>::max())
1185 {
1186 throw std::out_of_range("Out of range");
1187 }
1188 value = static_cast<decltype(value)>(tmp);
1189 }
1190 catch (const std::exception &e)
1191 {
1192 log<level::ERR>("Exception for MinPasswordLength",
1193 entry("WHAT=%s", e.what()));
Patrick Venture045b1122018-10-16 15:59:29 -07001194 throw;
Richard Marian Thomaiyar9164fd92018-06-13 16:51:00 +05301195 }
1196 AccountPolicyIface::minPasswordLength(value);
1197 }
1198 valueStr.clear();
1199 if (getPamModuleArgValue(pamPWHistory, remOldPasswdCount, valueStr) !=
1200 success)
1201 {
1202 AccountPolicyIface::rememberOldPasswordTimes(0);
1203 }
1204 else
1205 {
1206 value = 0;
1207 try
1208 {
1209 tmp = std::stoul(valueStr, nullptr);
1210 if (tmp > std::numeric_limits<decltype(value)>::max())
1211 {
1212 throw std::out_of_range("Out of range");
1213 }
1214 value = static_cast<decltype(value)>(tmp);
1215 }
1216 catch (const std::exception &e)
1217 {
1218 log<level::ERR>("Exception for RememberOldPasswordTimes",
1219 entry("WHAT=%s", e.what()));
Patrick Venture045b1122018-10-16 15:59:29 -07001220 throw;
Richard Marian Thomaiyar9164fd92018-06-13 16:51:00 +05301221 }
1222 AccountPolicyIface::rememberOldPasswordTimes(value);
1223 }
1224 valueStr.clear();
1225 if (getPamModuleArgValue(pamTally2, maxFailedAttempt, valueStr) != success)
1226 {
1227 AccountPolicyIface::maxLoginAttemptBeforeLockout(0);
1228 }
1229 else
1230 {
1231 uint16_t value16 = 0;
1232 try
1233 {
1234 tmp = std::stoul(valueStr, nullptr);
1235 if (tmp > std::numeric_limits<decltype(value16)>::max())
1236 {
1237 throw std::out_of_range("Out of range");
1238 }
1239 value16 = static_cast<decltype(value16)>(tmp);
1240 }
1241 catch (const std::exception &e)
1242 {
1243 log<level::ERR>("Exception for MaxLoginAttemptBeforLockout",
1244 entry("WHAT=%s", e.what()));
Patrick Venture045b1122018-10-16 15:59:29 -07001245 throw;
Richard Marian Thomaiyar9164fd92018-06-13 16:51:00 +05301246 }
1247 AccountPolicyIface::maxLoginAttemptBeforeLockout(value16);
1248 }
1249 valueStr.clear();
1250 if (getPamModuleArgValue(pamTally2, unlockTimeout, valueStr) != success)
1251 {
1252 AccountPolicyIface::accountUnlockTimeout(0);
1253 }
1254 else
1255 {
1256 uint32_t value32 = 0;
1257 try
1258 {
1259 tmp = std::stoul(valueStr, nullptr);
1260 if (tmp > std::numeric_limits<decltype(value32)>::max())
1261 {
1262 throw std::out_of_range("Out of range");
1263 }
1264 value32 = static_cast<decltype(value32)>(tmp);
1265 }
1266 catch (const std::exception &e)
1267 {
1268 log<level::ERR>("Exception for AccountUnlockTimeout",
1269 entry("WHAT=%s", e.what()));
Patrick Venture045b1122018-10-16 15:59:29 -07001270 throw;
Richard Marian Thomaiyar9164fd92018-06-13 16:51:00 +05301271 }
1272 AccountPolicyIface::accountUnlockTimeout(value32);
1273 }
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +05301274 initUserObjects();
Ratan Gupta1af12232018-11-03 00:35:38 +05301275
1276 // emit the signal
1277 this->emit_object_added();
Richard Marian Thomaiyar9f630d92018-05-24 10:49:10 +05301278}
1279
1280} // namespace user
1281} // namespace phosphor