xref: /openbmc/phosphor-user-manager/user_mgr.hpp (revision 8c5c4aab519130adb56f66b1477ff5ed82731926)
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 #pragma once
17 #include "json_serializer.hpp"
18 #include "users.hpp"
19 
20 #include <shadow.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 
24 #include <boost/process/v1/child.hpp>
25 #include <boost/process/v1/io.hpp>
26 #include <phosphor-logging/elog-errors.hpp>
27 #include <phosphor-logging/elog.hpp>
28 #include <phosphor-logging/lg2.hpp>
29 #include <sdbusplus/bus.hpp>
30 #include <sdbusplus/server/object.hpp>
31 #include <xyz/openbmc_project/Common/error.hpp>
32 #include <xyz/openbmc_project/User/AccountPolicy/server.hpp>
33 #include <xyz/openbmc_project/User/Manager/server.hpp>
34 #include <xyz/openbmc_project/User/MultiFactorAuthConfiguration/server.hpp>
35 #include <xyz/openbmc_project/User/TOTPState/server.hpp>
36 
37 #include <optional>
38 #include <span>
39 #include <string>
40 #include <unordered_map>
41 #include <variant>
42 #include <vector>
43 
44 namespace phosphor
45 {
46 namespace user
47 {
48 
49 inline constexpr size_t ipmiMaxUsers = 15;
50 inline constexpr size_t maxSystemUsers = 30;
51 inline constexpr uint8_t minPasswdLength = 8;
52 extern uint8_t maxPasswdLength; // MAX_PASSWORD_LENGTH;
53 inline constexpr size_t maxSystemGroupNameLength = 32;
54 inline constexpr size_t maxSystemGroupCount = 64;
55 
56 using UserMgrIface = sdbusplus::xyz::openbmc_project::User::server::Manager;
57 using UserSSHLists =
58     std::pair<std::vector<std::string>, std::vector<std::string>>;
59 using AccountPolicyIface =
60     sdbusplus::xyz::openbmc_project::User::server::AccountPolicy;
61 
62 using MultiFactorAuthConfigurationIface =
63     sdbusplus::xyz::openbmc_project::User::server::MultiFactorAuthConfiguration;
64 
65 using TOTPStateIface = sdbusplus::xyz::openbmc_project::User::server::TOTPState;
66 
67 using UserProperty =
68     sdbusplus::common::xyz::openbmc_project::user::Manager::UserProperty;
69 
70 using Ifaces = sdbusplus::server::object_t<UserMgrIface, AccountPolicyIface,
71                                            MultiFactorAuthConfigurationIface,
72                                            TOTPStateIface>;
73 
74 using Privilege = std::string;
75 using GroupList = std::vector<std::string>;
76 using UserEnabled = bool;
77 using PropertyName = std::string;
78 using ServiceEnabled = bool;
79 using PasswordExpiration = uint64_t;
80 
81 using UserInfo =
82     std::variant<Privilege, GroupList, UserEnabled, PasswordExpiration>;
83 
84 using UserInfoMap = std::map<PropertyName, UserInfo>;
85 
86 using UserCreateMap = std::map<UserProperty, UserInfo>;
87 
88 using DbusUserObjPath = sdbusplus::message::object_path;
89 
90 using DbusUserPropVariant = std::variant<Privilege, ServiceEnabled>;
91 
92 using DbusUserObjProperties = std::map<PropertyName, DbusUserPropVariant>;
93 
94 using Interface = std::string;
95 
96 using DbusUserObjValue = std::map<Interface, DbusUserObjProperties>;
97 
98 using DbusUserObj = std::map<DbusUserObjPath, DbusUserObjValue>;
99 
100 using MultiFactorAuthType = sdbusplus::common::xyz::openbmc_project::user::
101     MultiFactorAuthConfiguration::Type;
102 std::string getCSVFromVector(std::span<const std::string> vec);
103 
104 bool removeStringFromCSV(std::string& csvStr, const std::string& delStr);
105 
106 template <typename... ArgTypes>
executeCmd(const char * path,ArgTypes &&...tArgs)107 std::vector<std::string> executeCmd(const char* path, ArgTypes&&... tArgs)
108 {
109     int pipefd[2];
110 
111     if (pipe(pipefd) == -1)
112     {
113         lg2::error("Failed to create pipe: {ERROR}", "ERROR", strerror(errno));
114         phosphor::logging::elog<
115             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
116         return {};
117     }
118 
119     pid_t pid = fork();
120 
121     if (pid == -1)
122     {
123         lg2::error("Failed to fork process: {ERROR}", "ERROR", strerror(errno));
124         phosphor::logging::elog<
125             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
126         close(pipefd[0]); // Close read end of pipe
127         close(pipefd[1]); // Close write end of pipe
128         return {};
129     }
130 
131     if (pid == 0)         // Child process
132     {
133         close(pipefd[0]); // Close read end of pipe
134 
135         // Redirect write end of the pipe to stdout.
136         if (dup2(pipefd[1], STDOUT_FILENO) == -1)
137         {
138             lg2::error("Failed to redirect stdout: {ERROR}", "ERROR",
139                        strerror(errno));
140             _exit(EXIT_FAILURE);
141         }
142         close(pipefd[1]); // Close write end of pipe
143 
144         std::vector<const char*> args = {path};
145         (args.emplace_back(const_cast<const char*>(tArgs)), ...);
146         args.emplace_back(nullptr);
147 
148         execv(path, const_cast<char* const*>(args.data()));
149 
150         // If exec returns, an error occurred
151         lg2::error("Failed to execute command '{PATH}': {ERROR}", "PATH", path,
152                    "ERROR", strerror(errno));
153         _exit(EXIT_FAILURE);
154     }
155 
156     // Parent process.
157 
158     close(pipefd[1]); // Close write end of pipe
159 
160     FILE* fp = fdopen(pipefd[0], "r");
161     if (fp == nullptr)
162     {
163         lg2::error("Failed to open pipe for reading: {ERROR}", "ERROR",
164                    strerror(errno));
165         close(pipefd[0]);
166         phosphor::logging::elog<
167             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
168         return {};
169     }
170 
171     std::vector<std::string> results;
172     char buffer[256];
173     while (fgets(buffer, sizeof(buffer), fp) != nullptr)
174     {
175         std::string line = buffer;
176         if (!line.empty() && line.back() == '\n')
177         {
178             line.pop_back(); // Remove newline character
179         }
180         results.emplace_back(line);
181     }
182 
183     fclose(fp);
184     close(pipefd[0]);
185 
186     int status;
187     while (waitpid(pid, &status, 0) == -1)
188     {
189         // Need to retry on EINTR.
190         if (errno == EINTR)
191         {
192             continue;
193         }
194 
195         lg2::error("Failed to wait for child process: {ERROR}", "ERROR",
196                    strerror(errno));
197         phosphor::logging::elog<
198             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
199         return {};
200     }
201 
202     if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
203     {
204         lg2::error("Command {PATH} execution failed, return code {RETCODE}",
205                    "PATH", path, "RETCODE", WEXITSTATUS(status));
206         phosphor::logging::elog<
207             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
208     }
209 
210     return results;
211 }
212 
213 /** @class UserMgr
214  *  @brief Responsible for managing user accounts over the D-Bus interface.
215  */
216 class UserMgr : public Ifaces
217 {
218   public:
219     UserMgr() = delete;
220     ~UserMgr() = default;
221     UserMgr(const UserMgr&) = delete;
222     UserMgr& operator=(const UserMgr&) = delete;
223     UserMgr(UserMgr&&) = delete;
224     UserMgr& operator=(UserMgr&&) = delete;
225 
226     /** @brief Constructs UserMgr object.
227      *
228      *  @param[in] bus  - sdbusplus handler
229      *  @param[in] path - D-Bus path
230      */
231     UserMgr(sdbusplus::bus_t& bus, const char* path);
232 
233     /** @brief create user method.
234      *  This method creates a new user as requested
235      *
236      *  @param[in] userName - Name of the user which has to be created
237      *  @param[in] groupNames - Group names list, to which user has to be added.
238      *  @param[in] priv - Privilege of the user.
239      *  @param[in] enabled - State of the user enabled / disabled.
240      */
241     void createUser(std::string userName, std::vector<std::string> groupNames,
242                     std::string priv, bool enabled) override;
243 
244     /** @brief create user with password expiration method.
245      *  This method creates a new user as requested
246      *
247      *  @param[in] userName - Name of the user which has to be created
248      *  @param[in] props - Create user properties.
249      */
250     void createUser2(std::string userName, UserCreateMap props) override;
251 
252     /** @brief rename user method.
253      *  This method renames the user as requested
254      *
255      *  @param[in] userName - current name of the user
256      *  @param[in] newUserName - new user name to which it has to be renamed.
257      */
258     void renameUser(std::string userName, std::string newUserName) override;
259 
260     /** @brief delete user method.
261      *  This method deletes the user as requested
262      *
263      *  @param[in] userName - Name of the user which has to be deleted
264      */
265     void deleteUser(std::string userName);
266 
267     /** @brief Update user groups & privilege.
268      *  This method updates user groups & privilege
269      *
270      *  @param[in] userName - user name, for which update is requested
271      *  @param[in] groupName - Group to be updated..
272      *  @param[in] priv - Privilege to be updated.
273      */
274     void updateGroupsAndPriv(const std::string& userName,
275                              std::vector<std::string> groups,
276                              const std::string& priv);
277 
278     /** @brief Update user enabled state.
279      *  This method enables / disables user
280      *
281      *  @param[in] userName - user name, for which update is requested
282      *  @param[in] enabled - enable / disable the user
283      */
284     void userEnable(const std::string& userName, bool enabled);
285 
286     /** @brief get user enabled state
287      *  method to get user enabled state.
288      *
289      *  @param[in] userName - name of the user
290      *  @return - user enabled status (true/false)
291      */
292     virtual bool isUserEnabled(const std::string& userName);
293 
294     /** @brief update minimum password length requirement
295      *
296      *  @param[in] val - minimum password length
297      *  @return - minimum password length
298      */
299     uint8_t minPasswordLength(uint8_t val) override;
300 
301     /** @brief update old password history count
302      *
303      *  @param[in] val - number of times old passwords has to be avoided
304      *  @return - number of times old password has to be avoided
305      */
306     uint8_t rememberOldPasswordTimes(uint8_t val) override;
307 
308     /** @brief update maximum number of failed login attempt before locked
309      *  out.
310      *
311      *  @param[in] val - number of allowed attempt
312      *  @return - number of allowed attempt
313      */
314     uint16_t maxLoginAttemptBeforeLockout(uint16_t val) override;
315 
316     /** @brief update timeout to unlock the account
317      *
318      *  @param[in] val - value in seconds
319      *  @return - value in seconds
320      */
321     uint32_t accountUnlockTimeout(uint32_t val) override;
322 
323     /** @brief parses the faillock output for locked user status
324      *
325      * @param[in] - output from faillock for the user
326      * @return - true / false indicating user locked / un-locked
327      **/
328     bool parseFaillockForLockout(
329         const std::vector<std::string>& faillockOutput);
330 
331     /** @brief lists user locked state for failed attempt
332      *
333      * @param[in] - user name
334      * @return - true / false indicating user locked / un-locked
335      **/
336     virtual bool userLockedForFailedAttempt(const std::string& userName);
337 
338     /** @brief lists user locked state for failed attempt
339      *
340      * @param[in]: user name
341      * @param[in]: value - false -unlock user account, true - no action taken
342      **/
343     bool userLockedForFailedAttempt(const std::string& userName,
344                                     const bool& value);
345 
346     /** @brief shows if the user's password is expired
347      *
348      * @param[in]: user name
349      * @return - true / false indicating user password expired
350      **/
351     virtual bool userPasswordExpired(const std::string& userName);
352 
353     /** @brief returns user info
354      * Checks if user is local user, then returns map of properties of user.
355      * like user privilege, list of user groups, user enabled state and user
356      * locked state. If its not local user, then it checks if its a ldap user,
357      * then it gets the privilege mapping of the LDAP group.
358      *
359      * @param[in] - user name
360      * @return -  map of user properties
361      **/
362     UserInfoMap getUserInfo(std::string userName) override;
363 
364     /** @brief get IPMI user count
365      *  method to get IPMI user count
366      *
367      * @return - returns user count
368      */
369     virtual size_t getIpmiUsersCount(void);
370 
371     void createGroup(std::string groupName) override;
372 
373     void deleteGroup(std::string groupName) override;
enabled() const374     MultiFactorAuthType enabled() const override
375     {
376         return MultiFactorAuthConfigurationIface::enabled();
377     }
378     MultiFactorAuthType enabled(MultiFactorAuthType value,
379                                 bool skipSignal) override;
380     bool secretKeyRequired(std::string userName) override;
381     static std::vector<std::string> readAllGroupsOnSystem();
382     void load();
getSerializer()383     JsonSerializer& getSerializer()
384     {
385         return serializer;
386     }
387 
388     /** @brief user password expiration
389      *
390      * Password expiration is date time when the user password expires. The time
391      * is the Epoch time, number of seconds since 1 Jan 1970 00::00::00 UTC.
392      *When zero value is returned, it means that password does not expire.
393      *
394      * @param[in]: user name
395      * @return - Epoch time when the user password expires
396      **/
397     uint64_t getPasswordExpiration(const std::string& userName) const;
398 
399     /** @brief update user password expiration
400      *
401      * Password expiration is date time when the user password expires. The time
402      * is the Epoch time, number of seconds since 1 Jan 1970 00::00::00 UTC.
403      *When zero value is provided, it means that password does not expire.
404      *
405      * @param[in]: user name
406      * @param[in]: Epoch time when the user password expires
407      **/
408     void setPasswordExpiration(const std::string& userName,
409                                const uint64_t value);
410 
411   protected:
412     /** @brief get pam argument value
413      *  method to get argument value from pam configuration
414      *
415      *  @param[in] moduleName - name of the module from where arg has to be read
416      *  @param[in] argName - argument name
417      *  @param[out] argValue - argument value
418      *
419      *  @return 0 - success state of the function
420      */
421     int getPamModuleArgValue(const std::string& moduleName,
422                              const std::string& argName, std::string& argValue);
423 
424     /** @brief get pam argument value
425      *  method to get argument value from pam configuration
426      *
427      *  @param[in] confFile - path of the module config file from where arg has
428      * to be read
429      *  @param[in] argName - argument name
430      *  @param[out] argValue - argument value
431      *
432      *  @return 0 - success state of the function
433      */
434     int getPamModuleConfValue(const std::string& confFile,
435                               const std::string& argName,
436                               std::string& argValue);
437 
438     /** @brief set pam argument value
439      *  method to set argument value in pam configuration
440      *
441      *  @param[in] moduleName - name of the module in which argument value has
442      * to be set
443      *  @param[in] argName - argument name
444      *  @param[out] argValue - argument value
445      *
446      *  @return 0 - success state of the function
447      */
448     int setPamModuleArgValue(const std::string& moduleName,
449                              const std::string& argName,
450                              const std::string& argValue);
451 
452     /** @brief set pam argument value
453      *  method to set argument value in pam configuration
454      *
455      *  @param[in] confFile - path of the module config file in which argument
456      * value has to be set
457      *  @param[in] argName - argument name
458      *  @param[out] argValue - argument value
459      *
460      *  @return 0 - success state of the function
461      */
462     int setPamModuleConfValue(const std::string& confFile,
463                               const std::string& argName,
464                               const std::string& argValue);
465 
466     /** @brief check for user presence
467      *  method to check for user existence
468      *
469      *  @param[in] userName - name of the user
470      *  @return -true if user exists and false if not.
471      */
472     bool isUserExist(const std::string& userName) const;
473 
474     /** @brief check for user presence at the system level
475      *  method to check for user existence
476      *
477      *  @param[in] userName - name of the user
478      *  @return -true if user exists and false if not.
479      */
480     virtual bool isUserExistSystem(const std::string& userName);
481 
482     size_t getNonIpmiUsersCount();
483 
484     /** @brief check user exists
485      *  method to check whether user exist, and throw if not.
486      *
487      *  @param[in] userName - name of the user
488      */
489     void throwForUserDoesNotExist(const std::string& userName) const;
490 
491     /** @brief check user does not exist
492      *  method to check whether does not exist, and throw if exists.
493      *
494      *  @param[in] userName - name of the user
495      */
496     void throwForUserExists(const std::string& userName);
497 
498     /** @brief check user name constraints
499      *  method to check user name constraints and throw if failed.
500      *
501      *  @param[in] userName - name of the user
502      *  @param[in] groupNames - user groups
503      */
504     void throwForUserNameConstraints(
505         const std::string& userName,
506         const std::vector<std::string>& groupNames);
507 
508     /** @brief check group user count
509      *  method to check max group user count, and throw if limit reached
510      *
511      *  @param[in] groupNames - group name
512      */
513     void throwForMaxGrpUserCount(const std::vector<std::string>& groupNames);
514 
515     virtual void executeUserAdd(const char* userName, const char* groups,
516                                 bool sshRequested, bool enabled);
517 
518     virtual void executeUserDelete(const char* userName);
519 
520     /** @brief clear user's failure records
521      *  method to clear user fail records and throw if failed.
522      *
523      *  @param[in] userName - name of the user
524      */
525     virtual void executeUserClearFailRecords(const char* userName);
526 
527     virtual void executeUserRename(const char* userName,
528                                    const char* newUserName);
529 
530     virtual void executeUserModify(const char* userName, const char* newGroups,
531                                    bool sshRequested);
532 
533     virtual void executeUserModifyUserEnable(const char* userName,
534                                              bool enabled);
535 
536     virtual void executeGroupCreation(const char* groupName);
537 
538     virtual void executeGroupDeletion(const char* groupName);
539 
540     virtual void executeUserPasswordExpiration(
541         const char* userName, const long int passwordLastChange,
542         const long int passwordAge) const;
543 
544     virtual std::vector<std::string> getFailedAttempt(const char* userName);
545 
546     /** @brief check for valid privielge
547      *  method to check valid privilege, and throw if invalid
548      *
549      *  @param[in] priv - privilege of the user
550      */
551     void throwForInvalidPrivilege(const std::string& priv);
552 
553     /** @brief check for valid groups
554      *  method to check valid groups, and throw if invalid
555      *
556      *  @param[in] groupNames - user groups
557      */
558     void throwForInvalidGroups(const std::vector<std::string>& groupName);
559 
560     void initializeAccountPolicy();
561 
562     /** @brief checks if the group creation meets all constraints
563      * @param groupName - group to check
564      */
565     void checkCreateGroupConstraints(const std::string& groupName);
566 
567     /** @brief checks if the group deletion meets all constraints
568      * @param groupName - group to check
569      */
570     void checkDeleteGroupConstraints(const std::string& groupName);
571 
572     /** @brief checks if the group name is legal and whether it's allowed to
573      * change. The daemon doesn't allow arbitrary group to be created
574      * @param groupName - group to check
575      */
576     void checkAndThrowForDisallowedGroupCreation(const std::string& groupName);
577 
578   private:
579     /** @brief sdbusplus handler */
580     sdbusplus::bus_t& bus;
581 
582     /** @brief object path */
583     const std::string path;
584 
585     /** @brief serializer for mfa */
586     JsonSerializer serializer;
587     /** @brief privilege manager container */
588     const std::vector<std::string> privMgr = {"priv-admin", "priv-operator",
589                                               "priv-user"};
590 
591     /** @brief groups manager container */
592     std::vector<std::string> groupsMgr;
593 
594     /** @brief map container to hold users object */
595 
596     std::unordered_map<std::string, std::unique_ptr<phosphor::user::Users>>
597         usersList;
598 
599     /** @brief get users in group
600      *  method to get group user list
601      *
602      *  @param[in] groupName - group name
603      *
604      *  @return userList  - list of users in the group.
605      */
606     std::vector<std::string> getUsersInGroup(const std::string& groupName);
607 
608     /** @brief get user & SSH users list
609      *  method to get the users and ssh users list.
610      *
611      *@return - vector of User & SSH user lists
612      */
613     UserSSHLists getUserAndSshGrpList(void);
614 
615     /** @brief initialize the user manager objects
616      *  method to initialize the user manager objects accordingly
617      *
618      */
619     void initUserObjects(void);
620 
621     /** @brief get service name
622      *  method to get dbus service name
623      *
624      *  @param[in] path - object path
625      *  @param[in] intf - interface
626      *  @return - service name
627      */
628     std::string getServiceName(std::string&& path, std::string&& intf);
629 
630     /** @brief get primary group ID of specified user
631      *
632      * @param[in] - userName
633      * @return - primary group ID
634      */
635     virtual gid_t getPrimaryGroup(const std::string& userName) const;
636 
637     /** @brief check whether if the user is a member of the group
638      *
639      * @param[in] - userName
640      * @param[in] - ID of the user's primary group
641      * @param[in] - groupName
642      * @return - true if the user is a member of the group
643      */
644     virtual bool isGroupMember(const std::string& userName, gid_t primaryGid,
645                                const std::string& groupName) const;
646 
647   protected:
648     /** @brief get privilege mapper object
649      *  method to get dbus privilege mapper object
650      *
651      *  @return - map of user object
652      */
653     virtual DbusUserObj getPrivilegeMapperObject(void);
654 
655     friend class TestUserMgr;
656 
657     std::string faillockConfigFile;
658     std::string pwHistoryConfigFile;
659     std::string pwQualityConfigFile;
660 
661   private:
662     void createUserImpl(const std::string& userName, UserCreateMap props);
663 
664     void setPasswordExpirationImpl(const std::string& userName,
665                                    const uint64_t value);
666 
667     void deleteUserImpl(const std::string& userName);
668 
669   public:
670     // This functions need to be public for tests
671 
672     /** @brief value of a password maximum age indicating that the password does
673      *  not expire
674      *
675      **/
getUnexpiringPasswordAge()676     static constexpr long int getUnexpiringPasswordAge()
677     {
678         return -1;
679     }
680 
681     /** @brief date time value indicating that a password does not expire
682      *
683      **/
getUnexpiringPasswordTime()684     static constexpr uint64_t getUnexpiringPasswordTime()
685     {
686         return 0;
687     };
688 
689     /** @brief date time value indicating that a password expiration is not set
690      *
691      **/
getDefaultPasswordExpiration()692     static constexpr uint64_t getDefaultPasswordExpiration()
693     {
694         // default password expiration value
695         return std::numeric_limits<uint64_t>::max();
696     };
697 
698   protected:
699     // This function needs to be virtual and protected for tests
700     virtual void getShadowData(const std::string& userName,
701                                struct spwd& spwd) const;
702 };
703 
704 } // namespace user
705 } // namespace phosphor
706