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