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