blob: bf45db9018048bd8bbd51f5b5234871c8d4e0fe5 [file] [log] [blame]
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +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#include "user_mgmt.hpp"
17
Saravanan Palanisamy77381f12019-05-15 22:33:17 +000018#include "channel_layer.hpp"
Johnathan Manteyfd61fc32021-04-08 11:05:38 -070019#include "channel_mgmt.hpp"
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053020
Suryakanth Sekar90b00c72019-01-16 10:37:57 +053021#include <security/pam_appl.h>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053022#include <sys/stat.h>
23#include <unistd.h>
24
25#include <boost/interprocess/sync/named_recursive_mutex.hpp>
26#include <boost/interprocess/sync/scoped_lock.hpp>
Snehalatha Venkatesh745164c2021-06-25 10:02:25 +000027#include <ipmid/types.hpp>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053028#include <nlohmann/json.hpp>
29#include <phosphor-logging/elog-errors.hpp>
George Liu82844ef2024-07-17 17:03:56 +080030#include <phosphor-logging/lg2.hpp>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053031#include <sdbusplus/bus/match.hpp>
32#include <sdbusplus/server/object.hpp>
33#include <xyz/openbmc_project/Common/error.hpp>
34#include <xyz/openbmc_project/User/Common/error.hpp>
35
Orit Kashanyf7616312025-08-24 04:48:41 -070036#include <algorithm>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050037#include <cerrno>
Orit Kashanyf7616312025-08-24 04:48:41 -070038#include <cstring>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050039#include <fstream>
40#include <regex>
41#include <variant>
42
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053043namespace ipmi
44{
45
46// TODO: Move D-Bus & Object Manager related stuff, to common files
47// D-Bus property related
48static constexpr const char* dBusPropertiesInterface =
49 "org.freedesktop.DBus.Properties";
50static constexpr const char* getAllPropertiesMethod = "GetAll";
51static constexpr const char* propertiesChangedSignal = "PropertiesChanged";
52static constexpr const char* setPropertiesMethod = "Set";
53
54// Object Manager related
55static constexpr const char* dBusObjManager =
56 "org.freedesktop.DBus.ObjectManager";
57static constexpr const char* getManagedObjectsMethod = "GetManagedObjects";
58// Object Manager signals
59static constexpr const char* intfAddedSignal = "InterfacesAdded";
60static constexpr const char* intfRemovedSignal = "InterfacesRemoved";
61
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053062static constexpr const char* ipmiUserMutex = "ipmi_usr_mutex";
63static constexpr const char* ipmiMutexCleanupLockFile =
Patrick Williams02844912025-03-20 20:11:10 -040064 "/run/ipmi/ipmi_usr_mutex_cleanup";
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +000065static constexpr const char* ipmiUserSignalLockFile =
Patrick Williams02844912025-03-20 20:11:10 -040066 "/run/ipmi/ipmi_usr_signal_mutex";
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053067static constexpr const char* ipmiUserDataFile = "/var/lib/ipmi/ipmi_user.json";
68static constexpr const char* ipmiGrpName = "ipmi";
69static constexpr size_t privNoAccess = 0xF;
70static constexpr size_t privMask = 0xF;
71
72// User manager related
Khang D Nguyen078aa6a2025-03-06 00:03:42 +070073static constexpr const char* userMgrService =
74 "xyz.openbmc_project.User.Manager";
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053075static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user";
76static constexpr const char* userObjBasePath = "/xyz/openbmc_project/user";
77static constexpr const char* userMgrInterface =
78 "xyz.openbmc_project.User.Manager";
79static constexpr const char* usersInterface =
80 "xyz.openbmc_project.User.Attributes";
81static constexpr const char* deleteUserInterface =
82 "xyz.openbmc_project.Object.Delete";
83
84static constexpr const char* createUserMethod = "CreateUser";
85static constexpr const char* deleteUserMethod = "Delete";
86static constexpr const char* renameUserMethod = "RenameUser";
87// User manager signal memebers
88static constexpr const char* userRenamedSignal = "UserRenamed";
89// Mgr interface properties
90static constexpr const char* allPrivProperty = "AllPrivileges";
91static constexpr const char* allGrpProperty = "AllGroups";
92// User interface properties
93static constexpr const char* userPrivProperty = "UserPrivilege";
94static constexpr const char* userGrpProperty = "UserGroups";
95static constexpr const char* userEnabledProperty = "UserEnabled";
96
97static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = {
98 "priv-reserved", // PRIVILEGE_RESERVED - 0
99 "priv-callback", // PRIVILEGE_CALLBACK - 1
100 "priv-user", // PRIVILEGE_USER - 2
101 "priv-operator", // PRIVILEGE_OPERATOR - 3
102 "priv-admin", // PRIVILEGE_ADMIN - 4
103 "priv-custom" // PRIVILEGE_OEM - 5
104};
105
106using namespace phosphor::logging;
107using Json = nlohmann::json;
108
Vernon Mauery16b86932019-05-01 08:36:11 -0700109using PrivAndGroupType = std::variant<std::string, std::vector<std::string>>;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530110
111using NoResource =
Willy Tu523e2d12023-09-05 11:36:48 -0700112 sdbusplus::error::xyz::openbmc_project::user::common::NoResource;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530113
114using InternalFailure =
Willy Tu523e2d12023-09-05 11:36:48 -0700115 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530116
Lei YU4b0ddb62019-01-25 16:43:50 +0800117std::unique_ptr<sdbusplus::bus::match_t> userUpdatedSignal
118 __attribute__((init_priority(101)));
119std::unique_ptr<sdbusplus::bus::match_t> userMgrRenamedSignal
120 __attribute__((init_priority(101)));
121std::unique_ptr<sdbusplus::bus::match_t> userPropertiesSignal
122 __attribute__((init_priority(101)));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530123
Patrick Williams5d82f472022-07-22 19:26:53 -0500124void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530125 const std::string& objPath, const std::string& interface,
126 const std::string& property,
127 const DbusUserPropVariant& value)
128{
129 try
130 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400131 auto method =
132 bus.new_method_call(service.c_str(), objPath.c_str(),
133 dBusPropertiesInterface, setPropertiesMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530134 method.append(interface, property, value);
135 bus.call(method);
136 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500137 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530138 {
George Liu82844ef2024-07-17 17:03:56 +0800139 lg2::error("Failed to set {PROPERTY}, path: {PATH}, "
140 "interface: {INTERFACE}",
141 "PROPERTY", property, "PATH", objPath, "INTERFACE",
142 interface);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530143 throw;
144 }
145}
146
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530147UserAccess& getUserAccessObject()
148{
149 static UserAccess userAccess;
150 return userAccess;
151}
152
153int getUserNameFromPath(const std::string& path, std::string& userName)
154{
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530155 sdbusplus::message::object_path objPath(path);
156 userName.assign(objPath.filename());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530157 return 0;
158}
159
160void userUpdateHelper(UserAccess& usrAccess, const UserUpdateEvent& userEvent,
161 const std::string& userName, const std::string& priv,
162 const bool& enabled, const std::string& newUserName)
163{
164 UsersTbl* userData = usrAccess.getUsersTblPtr();
165 if (userEvent == UserUpdateEvent::userCreated)
166 {
167 if (usrAccess.addUserEntry(userName, priv, enabled) == false)
168 {
169 return;
170 }
171 }
172 else
173 {
174 // user index 0 is reserved, starts with 1
175 size_t usrIndex = 1;
176 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
177 {
Orit Kashanyf7616312025-08-24 04:48:41 -0700178 std::string curName =
179 safeUsernameString(userData->user[usrIndex].userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530180 if (userName == curName)
181 {
182 break; // found the entry
183 }
184 }
185 if (usrIndex > ipmiMaxUsers)
186 {
George Liu82844ef2024-07-17 17:03:56 +0800187 lg2::debug("User not found for signal, user name: {USER_NAME}, "
188 "user event: {USER_EVENT}",
189 "USER_NAME", userName, "USER_EVENT", userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530190 return;
191 }
192 switch (userEvent)
193 {
194 case UserUpdateEvent::userDeleted:
195 {
196 usrAccess.deleteUserIndex(usrIndex);
197 break;
198 }
199 case UserUpdateEvent::userPrivUpdated:
200 {
201 uint8_t userPriv =
202 static_cast<uint8_t>(
203 UserAccess::convertToIPMIPrivilege(priv)) &
204 privMask;
205 // Update all channels privileges, only if it is not equivalent
206 // to getUsrMgmtSyncIndex()
207 if (userData->user[usrIndex]
208 .userPrivAccess[UserAccess::getUsrMgmtSyncIndex()]
209 .privilege != userPriv)
210 {
211 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
212 ++chIndex)
213 {
214 userData->user[usrIndex]
215 .userPrivAccess[chIndex]
216 .privilege = userPriv;
217 }
218 }
219 break;
220 }
221 case UserUpdateEvent::userRenamed:
222 {
Orit Kashanyf7616312025-08-24 04:48:41 -0700223 safeUsernameCopyToBuffer(
224 userData->user[usrIndex].userName,
225 sizeof(userData->user[usrIndex].userName), newUserName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530226 ipmiRenameUserEntryPassword(userName, newUserName);
227 break;
228 }
229 case UserUpdateEvent::userStateUpdated:
230 {
231 userData->user[usrIndex].userEnabled = enabled;
232 break;
233 }
234 default:
235 {
George Liu82844ef2024-07-17 17:03:56 +0800236 lg2::error("Unhandled user event: {USER_EVENT}", "USER_EVENT",
237 userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530238 return;
239 }
240 }
241 }
242 usrAccess.writeUserData();
George Liu82844ef2024-07-17 17:03:56 +0800243 lg2::debug("User event handled successfully, user name: {USER_NAME}, "
244 "user event: {USER_EVENT}",
245 "USER_NAME", userName.c_str(), "USER_EVENT", userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530246
247 return;
248}
249
Patrick Williams5d82f472022-07-22 19:26:53 -0500250void userUpdatedSignalHandler(UserAccess& usrAccess, sdbusplus::message_t& msg)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530251{
Patrick Williams5d82f472022-07-22 19:26:53 -0500252 static sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530253 std::string signal = msg.get_member();
Patrick Venture3a697ad2019-08-19 11:12:05 -0700254 std::string userName, priv, newUserName;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530255 std::vector<std::string> groups;
256 bool enabled = false;
257 UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent;
258 if (signal == intfAddedSignal)
259 {
260 DbusUserObjPath objPath;
261 DbusUserObjValue objValue;
262 msg.read(objPath, objValue);
263 getUserNameFromPath(objPath.str, userName);
264 if (usrAccess.getUserObjProperties(objValue, groups, priv, enabled) !=
265 0)
266 {
267 return;
268 }
269 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
270 groups.end())
271 {
272 return;
273 }
274 userEvent = UserUpdateEvent::userCreated;
275 }
276 else if (signal == intfRemovedSignal)
277 {
278 DbusUserObjPath objPath;
279 std::vector<std::string> interfaces;
280 msg.read(objPath, interfaces);
281 getUserNameFromPath(objPath.str, userName);
282 userEvent = UserUpdateEvent::userDeleted;
283 }
284 else if (signal == userRenamedSignal)
285 {
286 msg.read(userName, newUserName);
287 userEvent = UserUpdateEvent::userRenamed;
288 }
289 else if (signal == propertiesChangedSignal)
290 {
291 getUserNameFromPath(msg.get_path(), userName);
292 }
293 else
294 {
George Liu82844ef2024-07-17 17:03:56 +0800295 lg2::error("Unknown user update signal: {SIGNAL}", "SIGNAL", signal);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530296 return;
297 }
298
299 if (signal.empty() || userName.empty() ||
300 (signal == userRenamedSignal && newUserName.empty()))
301 {
George Liu82844ef2024-07-17 17:03:56 +0800302 lg2::error("Invalid inputs received");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530303 return;
304 }
305
306 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
307 userLock{*(usrAccess.userMutex)};
308 usrAccess.checkAndReloadUserData();
309
310 if (signal == propertiesChangedSignal)
311 {
312 std::string intfName;
313 DbusUserObjProperties chProperties;
314 msg.read(intfName, chProperties); // skip reading 3rd argument.
315 for (const auto& prop : chProperties)
316 {
317 userEvent = UserUpdateEvent::reservedEvent;
318 std::string member = prop.first;
319 if (member == userPrivProperty)
320 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700321 priv = std::get<std::string>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530322 userEvent = UserUpdateEvent::userPrivUpdated;
323 }
324 else if (member == userGrpProperty)
325 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700326 groups = std::get<std::vector<std::string>>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530327 userEvent = UserUpdateEvent::userGrpUpdated;
328 }
329 else if (member == userEnabledProperty)
330 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700331 enabled = std::get<bool>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530332 userEvent = UserUpdateEvent::userStateUpdated;
333 }
334 // Process based on event type.
335 if (userEvent == UserUpdateEvent::userGrpUpdated)
336 {
337 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
338 groups.end())
339 {
340 // remove user from ipmi user list.
341 userUpdateHelper(usrAccess, UserUpdateEvent::userDeleted,
342 userName, priv, enabled, newUserName);
343 }
344 else
345 {
346 DbusUserObjProperties properties;
347 try
348 {
349 auto method = bus.new_method_call(
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700350 userMgrService, msg.get_path(),
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530351 dBusPropertiesInterface, getAllPropertiesMethod);
352 method.append(usersInterface);
353 auto reply = bus.call(method);
354 reply.read(properties);
355 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500356 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530357 {
George Liu82844ef2024-07-17 17:03:56 +0800358 lg2::debug("Failed to excute {METHOD}, path: {PATH}",
359 "METHOD", getAllPropertiesMethod, "PATH",
360 msg.get_path());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530361 return;
362 }
363 usrAccess.getUserProperties(properties, groups, priv,
364 enabled);
365 // add user to ipmi user list.
366 userUpdateHelper(usrAccess, UserUpdateEvent::userCreated,
367 userName, priv, enabled, newUserName);
368 }
369 }
370 else if (userEvent != UserUpdateEvent::reservedEvent)
371 {
372 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
373 newUserName);
374 }
375 }
376 }
377 else if (userEvent != UserUpdateEvent::reservedEvent)
378 {
379 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
380 newUserName);
381 }
382 return;
383}
384
385UserAccess::~UserAccess()
386{
387 if (signalHndlrObject)
388 {
389 userUpdatedSignal.reset();
390 userMgrRenamedSignal.reset();
391 userPropertiesSignal.reset();
392 sigHndlrLock.unlock();
393 }
394}
395
396UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection())
397{
398 std::ofstream mutexCleanUpFile;
399 mutexCleanUpFile.open(ipmiMutexCleanupLockFile,
400 std::ofstream::out | std::ofstream::app);
401 if (!mutexCleanUpFile.good())
402 {
George Liu82844ef2024-07-17 17:03:56 +0800403 lg2::debug("Unable to open mutex cleanup file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530404 return;
405 }
406 mutexCleanUpFile.close();
407 mutexCleanupLock = boost::interprocess::file_lock(ipmiMutexCleanupLockFile);
408 if (mutexCleanupLock.try_lock())
409 {
410 boost::interprocess::named_recursive_mutex::remove(ipmiUserMutex);
411 }
412 mutexCleanupLock.lock_sharable();
413 userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>(
414 boost::interprocess::open_or_create, ipmiUserMutex);
415
arun-pmbbe728c2020-01-10 15:18:04 +0530416 cacheUserDataFile();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530417 getSystemPrivAndGroups();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530418}
419
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530420UserInfo* UserAccess::getUserInfo(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530421{
422 checkAndReloadUserData();
423 return &usersTbl.user[userId];
424}
425
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530426void UserAccess::setUserInfo(const uint8_t userId, UserInfo* userInfo)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530427{
428 checkAndReloadUserData();
429 std::copy(reinterpret_cast<uint8_t*>(userInfo),
430 reinterpret_cast<uint8_t*>(userInfo) + sizeof(*userInfo),
431 reinterpret_cast<uint8_t*>(&usersTbl.user[userId]));
432 writeUserData();
433}
434
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530435bool UserAccess::isValidChannel(const uint8_t chNum)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530436{
437 return (chNum < ipmiMaxChannels);
438}
439
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530440bool UserAccess::isValidUserId(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530441{
442 return ((userId <= ipmiMaxUsers) && (userId != reservedUserId));
443}
444
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530445bool UserAccess::isValidPrivilege(const uint8_t priv)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530446{
jayaprakash Mutyala0e2dbee2019-12-26 13:03:04 +0000447 // Callback privilege is deprecated in OpenBMC
Alexander Filippovfc24fa52022-02-01 14:57:59 +0300448 return isValidPrivLimit(priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530449}
450
451uint8_t UserAccess::getUsrMgmtSyncIndex()
452{
Johnathan Manteyfd61fc32021-04-08 11:05:38 -0700453 // Identify the IPMI channel used to assign system user privilege levels
454 // in phosphor-user-manager. The default value is IPMI Channel 1. To
455 // assign a different channel add:
456 // "is_management_nic" : true
457 // into the channel_config.json file describing the assignment of the IPMI
458 // channels. It is only necessary to add the string above to ONE record in
459 // the channel_config.json file. All other records will be automatically
460 // assigned a "false" value.
461 return getChannelConfigObject().getManagementNICID();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530462}
463
464CommandPrivilege UserAccess::convertToIPMIPrivilege(const std::string& value)
465{
466 auto iter = std::find(ipmiPrivIndex.begin(), ipmiPrivIndex.end(), value);
467 if (iter == ipmiPrivIndex.end())
468 {
469 if (value == "")
470 {
471 return static_cast<CommandPrivilege>(privNoAccess);
472 }
George Liu82844ef2024-07-17 17:03:56 +0800473 lg2::error("Error in converting to IPMI privilege: {PRIV}", "PRIV",
474 value);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530475 throw std::out_of_range("Out of range - convertToIPMIPrivilege");
476 }
477 else
478 {
479 return static_cast<CommandPrivilege>(
480 std::distance(ipmiPrivIndex.begin(), iter));
481 }
482}
483
484std::string UserAccess::convertToSystemPrivilege(const CommandPrivilege& value)
485{
486 if (value == static_cast<CommandPrivilege>(privNoAccess))
487 {
488 return "";
489 }
490 try
491 {
492 return ipmiPrivIndex.at(value);
493 }
494 catch (const std::out_of_range& e)
495 {
George Liu82844ef2024-07-17 17:03:56 +0800496 lg2::error("Error in converting to system privilege: {PRIV}", "PRIV",
497 value);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530498 throw std::out_of_range("Out of range - convertToSystemPrivilege");
499 }
500}
501
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000502bool UserAccess::isValidUserName(const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530503{
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000504 if (userName.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530505 {
George Liu82844ef2024-07-17 17:03:56 +0800506 lg2::error("userName is empty");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530507 return false;
508 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530509 if (!std::regex_match(userName.c_str(),
nichanghao.nch0c96fdf2024-01-17 22:13:35 +0800510 std::regex("[a-zA-Z_][a-zA-Z_0-9]*")))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530511 {
George Liu82844ef2024-07-17 17:03:56 +0800512 lg2::error("Unsupported characters in user name");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530513 return false;
514 }
515 if (userName == "root")
516 {
George Liu82844ef2024-07-17 17:03:56 +0800517 lg2::error("Invalid user name - root");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530518 return false;
519 }
520 std::map<DbusUserObjPath, DbusUserObjValue> properties;
521 try
522 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700523 auto method =
524 bus.new_method_call(userMgrService, userMgrObjBasePath,
525 dBusObjManager, getManagedObjectsMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530526 auto reply = bus.call(method);
527 reply.read(properties);
528 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500529 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530530 {
George Liu82844ef2024-07-17 17:03:56 +0800531 lg2::error("Failed to excute {METHOD}, path: {PATH}", "METHOD",
532 getManagedObjectsMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530533 return false;
534 }
535
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530536 sdbusplus::message::object_path tempUserPath(userObjBasePath);
537 tempUserPath /= userName;
538 std::string usersPath(tempUserPath);
539
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530540 if (properties.find(usersPath) != properties.end())
541 {
George Liu82844ef2024-07-17 17:03:56 +0800542 lg2::debug("Username {USER_NAME} already exists", "USER_NAME",
543 userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530544 return false;
545 }
546
547 return true;
548}
549
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530550/** @brief Information exchanged by pam module and application.
551 *
552 * @param[in] numMsg - length of the array of pointers,msg.
553 *
554 * @param[in] msg - pointer to an array of pointers to pam_message structure
555 *
556 * @param[out] resp - struct pam response array
557 *
558 * @param[in] appdataPtr - member of pam_conv structure
559 *
560 * @return the response in pam response structure.
561 */
562
563static int pamFunctionConversation(int numMsg, const struct pam_message** msg,
564 struct pam_response** resp, void* appdataPtr)
565{
566 if (appdataPtr == nullptr)
567 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530568 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530569 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530570
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530571 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
572 {
573 return PAM_CONV_ERR;
574 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530575
576 for (int i = 0; i < numMsg; ++i)
577 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530578 /* Ignore all PAM messages except prompting for hidden input */
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530579 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
580 {
581 continue;
582 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530583
584 /* Assume PAM is only prompting for the password as hidden input */
585 /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */
586
587 char* appPass = reinterpret_cast<char*>(appdataPtr);
588 size_t appPassSize = std::strlen(appPass);
589
590 if (appPassSize >= PAM_MAX_RESP_SIZE)
591 {
592 return PAM_CONV_ERR;
593 }
594
595 char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1));
596 if (pass == nullptr)
597 {
598 return PAM_BUF_ERR;
599 }
600
Patrick Williams1318a5e2024-08-16 15:19:54 -0400601 void* ptr =
602 calloc(static_cast<size_t>(numMsg), sizeof(struct pam_response));
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530603 if (ptr == nullptr)
604 {
605 free(pass);
606 return PAM_BUF_ERR;
607 }
608
609 std::strncpy(pass, appPass, appPassSize + 1);
610
611 *resp = reinterpret_cast<pam_response*>(ptr);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530612 resp[i]->resp = pass;
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530613
614 return PAM_SUCCESS;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530615 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530616
617 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530618}
619
620/** @brief Updating the PAM password
621 *
622 * @param[in] username - username in string
623 *
624 * @param[in] password - new password in string
625 *
626 * @return status
627 */
628
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000629int pamUpdatePasswd(const char* username, const char* password)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530630{
631 const struct pam_conv localConversation = {pamFunctionConversation,
632 const_cast<char*>(password)};
Jayanth Othayotha6fb32d2024-12-15 10:55:22 -0600633 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530634
Patrick Williams1318a5e2024-08-16 15:19:54 -0400635 int retval =
636 pam_start("passwd", username, &localConversation, &localAuthHandle);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530637
638 if (retval != PAM_SUCCESS)
639 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000640 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530641 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000642
643 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
644 if (retval != PAM_SUCCESS)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530645 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000646 pam_end(localAuthHandle, retval);
647 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530648 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000649
650 return pam_end(localAuthHandle, PAM_SUCCESS);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530651}
652
Ayushi Smriti02650d52019-05-15 11:59:09 +0000653bool pamUserCheckAuthenticate(std::string_view username,
654 std::string_view password)
655{
656 const struct pam_conv localConversation = {
657 pamFunctionConversation, const_cast<char*>(password.data())};
658
Jayanth Othayotha6fb32d2024-12-15 10:55:22 -0600659 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ayushi Smriti02650d52019-05-15 11:59:09 +0000660
661 if (pam_start("dropbear", username.data(), &localConversation,
662 &localAuthHandle) != PAM_SUCCESS)
663 {
George Liu82844ef2024-07-17 17:03:56 +0800664 lg2::error("User Authentication Failure");
Ayushi Smriti02650d52019-05-15 11:59:09 +0000665 return false;
666 }
667
668 int retval = pam_authenticate(localAuthHandle,
669 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
670
671 if (retval != PAM_SUCCESS)
672 {
George Liu82844ef2024-07-17 17:03:56 +0800673 lg2::debug("pam_authenticate returned failure: {ERROR}", "ERROR",
674 retval);
Ayushi Smriti02650d52019-05-15 11:59:09 +0000675
676 pam_end(localAuthHandle, retval);
677 return false;
678 }
679
680 if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
681 PAM_SUCCESS)
682 {
683 pam_end(localAuthHandle, PAM_SUCCESS);
684 return false;
685 }
686
687 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
688 {
689 return false;
690 }
691 return true;
692}
693
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000694Cc UserAccess::setSpecialUserPassword(const std::string& userName,
Vernon Mauery1e22a0f2021-07-30 13:36:54 -0700695 const SecureString& userPassword)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530696{
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000697 if (pamUpdatePasswd(userName.c_str(), userPassword.c_str()) != PAM_SUCCESS)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530698 {
George Liu82844ef2024-07-17 17:03:56 +0800699 lg2::debug("Failed to update password");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000700 return ccUnspecifiedError;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530701 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000702 return ccSuccess;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530703}
704
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000705Cc UserAccess::setUserPassword(const uint8_t userId, const char* userPassword)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530706{
707 std::string userName;
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000708 if (ipmiUserGetUserName(userId, userName) != ccSuccess)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530709 {
George Liu82844ef2024-07-17 17:03:56 +0800710 lg2::debug("User Name not found, user Id: {USER_ID}", "USER_ID",
711 userId);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000712 return ccParmOutOfRange;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530713 }
Snehalatha Venkatesh61024d72021-04-08 16:24:39 +0000714
715 ipmi::SecureString passwd;
Orit Kashanyf7616312025-08-24 04:48:41 -0700716 size_t len = strnlen(userPassword, maxIpmi20PasswordSize);
717 passwd.assign(reinterpret_cast<const char*>(userPassword), len);
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000718 int retval = pamUpdatePasswd(userName.c_str(), passwd.c_str());
719
720 switch (retval)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530721 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000722 case PAM_SUCCESS:
723 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000724 return ccSuccess;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000725 }
726 case PAM_AUTHTOK_ERR:
727 {
George Liu82844ef2024-07-17 17:03:56 +0800728 lg2::debug("Bad authentication token");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000729 return ccInvalidFieldRequest;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000730 }
731 default:
732 {
George Liu82844ef2024-07-17 17:03:56 +0800733 lg2::debug("Failed to update password, user Id: {USER_ID}",
734 "USER_ID", userId);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000735 return ccUnspecifiedError;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000736 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530737 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530738}
739
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000740Cc UserAccess::setUserEnabledState(const uint8_t userId,
741 const bool& enabledState)
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530742{
743 if (!isValidUserId(userId))
744 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000745 return ccParmOutOfRange;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530746 }
747 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
748 userLock{*userMutex};
749 UserInfo* userInfo = getUserInfo(userId);
750 std::string userName;
Orit Kashanyf7616312025-08-24 04:48:41 -0700751
752 safeUsernameAssign(userName, userInfo->userName);
753
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530754 if (userName.empty())
755 {
George Liu82844ef2024-07-17 17:03:56 +0800756 lg2::debug("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000757 return ccUnspecifiedError;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530758 }
759 if (userInfo->userEnabled != enabledState)
760 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530761 sdbusplus::message::object_path tempUserPath(userObjBasePath);
762 tempUserPath /= userName;
763 std::string userPath(tempUserPath);
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700764 setDbusProperty(bus, userMgrService, userPath, usersInterface,
Patrick Venture99d1ba02019-02-21 15:11:24 -0800765 userEnabledProperty, enabledState);
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530766 userInfo->userEnabled = enabledState;
767 try
768 {
769 writeUserData();
770 }
771 catch (const std::exception& e)
772 {
George Liu82844ef2024-07-17 17:03:56 +0800773 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000774 return ccUnspecifiedError;
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530775 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530776 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000777 return ccSuccess;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530778}
779
Patrick Williams1318a5e2024-08-16 15:19:54 -0400780Cc UserAccess::setUserPayloadAccess(
781 const uint8_t chNum, const uint8_t operation, const uint8_t userId,
782 const PayloadAccess& payloadAccess)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000783{
784 constexpr uint8_t enable = 0x0;
785 constexpr uint8_t disable = 0x1;
786
787 if (!isValidChannel(chNum))
788 {
George Liua0e545d2025-01-24 09:50:22 +0800789 lg2::debug(
790 "Set user payload access - Invalid channel request: {CHANNEL}",
791 "CHANNEL", chNum);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000792 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000793 }
794 if (!isValidUserId(userId))
795 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000796 return ccParmOutOfRange;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000797 }
798 if (operation != enable && operation != disable)
799 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000800 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000801 }
802 // Check operation & payloadAccess if required.
803 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
804 userLock{*userMutex};
805 UserInfo* userInfo = getUserInfo(userId);
806
807 if (operation == enable)
808 {
809 userInfo->payloadAccess[chNum].stdPayloadEnables1 |=
810 payloadAccess.stdPayloadEnables1;
811
812 userInfo->payloadAccess[chNum].oemPayloadEnables1 |=
813 payloadAccess.oemPayloadEnables1;
814 }
815 else
816 {
817 userInfo->payloadAccess[chNum].stdPayloadEnables1 &=
818 ~(payloadAccess.stdPayloadEnables1);
819
820 userInfo->payloadAccess[chNum].oemPayloadEnables1 &=
821 ~(payloadAccess.oemPayloadEnables1);
822 }
823
824 try
825 {
826 writeUserData();
827 }
828 catch (const std::exception& e)
829 {
George Liu82844ef2024-07-17 17:03:56 +0800830 lg2::error("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000831 return ccUnspecifiedError;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000832 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000833 return ccSuccess;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000834}
835
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000836Cc UserAccess::setUserPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
837 const UserPrivAccess& privAccess,
838 const bool& otherPrivUpdates)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530839{
840 if (!isValidChannel(chNum))
841 {
George Liua0e545d2025-01-24 09:50:22 +0800842 lg2::debug(
843 "Set user privilege access - Invalid channel request: {CHANNEL}",
844 "CHANNEL", chNum);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000845 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530846 }
847 if (!isValidUserId(userId))
848 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000849 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530850 }
851 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
852 userLock{*userMutex};
853 UserInfo* userInfo = getUserInfo(userId);
854 std::string userName;
Orit Kashanyf7616312025-08-24 04:48:41 -0700855 safeUsernameAssign(userName, userInfo->userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530856 if (userName.empty())
857 {
George Liu82844ef2024-07-17 17:03:56 +0800858 lg2::debug("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000859 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530860 }
861 std::string priv = convertToSystemPrivilege(
862 static_cast<CommandPrivilege>(privAccess.privilege));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530863 uint8_t syncIndex = getUsrMgmtSyncIndex();
864 if (chNum == syncIndex &&
865 privAccess.privilege != userInfo->userPrivAccess[syncIndex].privilege)
866 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530867 sdbusplus::message::object_path tempUserPath(userObjBasePath);
868 tempUserPath /= userName;
869 std::string userPath(tempUserPath);
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700870 setDbusProperty(bus, userMgrService, userPath, usersInterface,
Patrick Venture99d1ba02019-02-21 15:11:24 -0800871 userPrivProperty, priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530872 }
873 userInfo->userPrivAccess[chNum].privilege = privAccess.privilege;
874
875 if (otherPrivUpdates)
876 {
877 userInfo->userPrivAccess[chNum].ipmiEnabled = privAccess.ipmiEnabled;
878 userInfo->userPrivAccess[chNum].linkAuthEnabled =
879 privAccess.linkAuthEnabled;
880 userInfo->userPrivAccess[chNum].accessCallback =
881 privAccess.accessCallback;
882 }
883 try
884 {
885 writeUserData();
886 }
887 catch (const std::exception& e)
888 {
George Liu82844ef2024-07-17 17:03:56 +0800889 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000890 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530891 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000892 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530893}
894
895uint8_t UserAccess::getUserId(const std::string& userName)
896{
897 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
898 userLock{*userMutex};
899 checkAndReloadUserData();
900 // user index 0 is reserved, starts with 1
901 size_t usrIndex = 1;
902 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
903 {
Orit Kashanyf7616312025-08-24 04:48:41 -0700904 std::string curName =
905 safeUsernameString(usersTbl.user[usrIndex].userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530906 if (userName == curName)
907 {
908 break; // found the entry
909 }
910 }
911 if (usrIndex > ipmiMaxUsers)
912 {
George Liu82844ef2024-07-17 17:03:56 +0800913 lg2::debug("Username {USER_NAME} not found", "USER_NAME", userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530914 return invalidUserId;
915 }
916
917 return usrIndex;
918}
919
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000920Cc UserAccess::getUserName(const uint8_t userId, std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530921{
922 if (!isValidUserId(userId))
923 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000924 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530925 }
926 UserInfo* userInfo = getUserInfo(userId);
Orit Kashanyf7616312025-08-24 04:48:41 -0700927 safeUsernameAssign(userName, userInfo->userName);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000928 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530929}
930
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530931bool UserAccess::isIpmiInAvailableGroupList()
932{
933 if (std::find(availableGroups.begin(), availableGroups.end(),
934 ipmiGrpName) != availableGroups.end())
935 {
936 return true;
937 }
938 if (availableGroups.empty())
939 {
940 // available groups shouldn't be empty, re-query
941 getSystemPrivAndGroups();
942 if (std::find(availableGroups.begin(), availableGroups.end(),
943 ipmiGrpName) != availableGroups.end())
944 {
945 return true;
946 }
947 }
948 return false;
949}
950
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000951Cc UserAccess::setUserName(const uint8_t userId, const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530952{
953 if (!isValidUserId(userId))
954 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000955 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530956 }
957
958 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
959 userLock{*userMutex};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530960 std::string oldUser;
961 getUserName(userId, oldUser);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530962
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000963 if (oldUser == userName)
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +0530964 {
965 // requesting to set the same user name, return success.
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000966 return ccSuccess;
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +0530967 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000968
969 bool validUser = isValidUserName(userName);
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +0530970 UserInfo* userInfo = getUserInfo(userId);
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000971 if (userName.empty() && !oldUser.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530972 {
973 // Delete existing user
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530974 sdbusplus::message::object_path tempUserPath(userObjBasePath);
975 tempUserPath /= oldUser;
976 std::string userPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530977 try
978 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700979 auto method =
980 bus.new_method_call(userMgrService, userPath.c_str(),
981 deleteUserInterface, deleteUserMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530982 auto reply = bus.call(method);
983 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500984 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530985 {
George Liu82844ef2024-07-17 17:03:56 +0800986 lg2::debug("Failed to excute {METHOD}, path:{PATH}", "METHOD",
987 deleteUserMethod, "PATH", userPath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000988 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530989 }
Richard Marian Thomaiyar02710bb2018-11-28 20:42:25 +0530990 deleteUserIndex(userId);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530991 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000992 else if (oldUser.empty() && !userName.empty() && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530993 {
994 try
995 {
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530996 if (!isIpmiInAvailableGroupList())
997 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000998 return ccUnspecifiedError;
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530999 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301000 // Create new user
Khang D Nguyen078aa6a2025-03-06 00:03:42 +07001001 auto method =
1002 bus.new_method_call(userMgrService, userMgrObjBasePath,
1003 userMgrInterface, createUserMethod);
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001004 method.append(userName.c_str(), availableGroups,
1005 ipmiPrivIndex[PRIVILEGE_USER], false);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301006 auto reply = bus.call(method);
1007 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001008 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301009 {
George Liu82844ef2024-07-17 17:03:56 +08001010 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1011 createUserMethod, "PATH", userMgrObjBasePath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001012 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301013 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001014
Orit Kashanyf7616312025-08-24 04:48:41 -07001015 safeUsernameCopyToBuffer(userInfo->userName, sizeof(userInfo->userName),
1016 userName);
1017
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301018 userInfo->userInSystem = true;
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001019 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1020 {
1021 userInfo->userPrivAccess[chIndex].privilege =
1022 static_cast<uint8_t>(PRIVILEGE_USER);
1023 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301024 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001025 else if (oldUser != userName && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301026 {
1027 try
1028 {
1029 // User rename
Khang D Nguyen078aa6a2025-03-06 00:03:42 +07001030 auto method =
1031 bus.new_method_call(userMgrService, userMgrObjBasePath,
1032 userMgrInterface, renameUserMethod);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001033 method.append(oldUser.c_str(), userName.c_str());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301034 auto reply = bus.call(method);
1035 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001036 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301037 {
George Liu82844ef2024-07-17 17:03:56 +08001038 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1039 renameUserMethod, "PATH", userMgrObjBasePath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001040 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301041 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001042
Orit Kashanyf7616312025-08-24 04:48:41 -07001043 safeUsernameCopyToBuffer(userInfo->userName, sizeof(userInfo->userName),
1044 userName);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001045
1046 ipmiRenameUserEntryPassword(oldUser, userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301047 userInfo->userInSystem = true;
1048 }
1049 else if (!validUser)
1050 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001051 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301052 }
1053 try
1054 {
1055 writeUserData();
1056 }
1057 catch (const std::exception& e)
1058 {
George Liu82844ef2024-07-17 17:03:56 +08001059 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001060 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301061 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001062 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301063}
1064
1065static constexpr const char* jsonUserName = "user_name";
1066static constexpr const char* jsonPriv = "privilege";
1067static constexpr const char* jsonIpmiEnabled = "ipmi_enabled";
1068static constexpr const char* jsonLinkAuthEnabled = "link_auth_enabled";
1069static constexpr const char* jsonAccCallbk = "access_callback";
1070static constexpr const char* jsonUserEnabled = "user_enabled";
1071static constexpr const char* jsonUserInSys = "user_in_system";
1072static constexpr const char* jsonFixedUser = "fixed_user_name";
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001073static constexpr const char* payloadEnabledStr = "payload_enabled";
1074static constexpr const char* stdPayloadStr = "std_payload";
1075static constexpr const char* oemPayloadStr = "OEM_payload";
1076
1077/** @brief to construct a JSON object from the given payload access details.
1078 *
1079 * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. (input)
1080 * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. (input)
1081 *
1082 * @details Sample output JSON object format :
1083 * "payload_enabled":{
1084 * "OEM_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1085 * "OEM_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1086 * "OEM_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1087 * "OEM_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1088 * "OEM_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1089 * "OEM_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1090 * "OEM_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1091 * "OEM_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1092 * "std_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1093 * "std_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1094 * "std_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1095 * "std_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1096 * "std_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1097 * "std_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1098 * "std_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1099 * "std_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1100 * }
1101 */
1102static const Json constructJsonPayloadEnables(
1103 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1104 stdPayload,
1105 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1106 oemPayload)
1107{
1108 Json jsonPayloadEnabled;
1109
1110 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1111 {
1112 std::ostringstream stdPayloadStream;
1113 std::ostringstream oemPayloadStream;
1114
1115 stdPayloadStream << stdPayloadStr << payloadNum;
1116 oemPayloadStream << oemPayloadStr << payloadNum;
1117
1118 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1119 stdPayloadStream.str(), stdPayload[payloadNum]));
1120
1121 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1122 oemPayloadStream.str(), oemPayload[payloadNum]));
1123 }
1124 return jsonPayloadEnabled;
1125}
1126
1127void UserAccess::readPayloadAccessFromUserInfo(
1128 const UserInfo& userInfo,
1129 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& stdPayload,
1130 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& oemPayload)
1131{
1132 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1133 {
1134 for (auto chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1135 {
1136 stdPayload[payloadNum][chIndex] =
1137 userInfo.payloadAccess[chIndex].stdPayloadEnables1[payloadNum];
1138
1139 oemPayload[payloadNum][chIndex] =
1140 userInfo.payloadAccess[chIndex].oemPayloadEnables1[payloadNum];
1141 }
1142 }
1143}
1144
1145void UserAccess::updatePayloadAccessInUserInfo(
1146 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1147 stdPayload,
Willy Tu11d68892022-01-20 10:37:34 -08001148 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&,
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001149 UserInfo& userInfo)
1150{
1151 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1152 {
1153 // Ensure that reserved/unsupported payloads are marked to zero.
1154 userInfo.payloadAccess[chIndex].stdPayloadEnables1.reset();
1155 userInfo.payloadAccess[chIndex].oemPayloadEnables1.reset();
1156 userInfo.payloadAccess[chIndex].stdPayloadEnables2Reserved.reset();
1157 userInfo.payloadAccess[chIndex].oemPayloadEnables2Reserved.reset();
1158 // Update SOL status as it is the only supported payload currently.
1159 userInfo.payloadAccess[chIndex]
1160 .stdPayloadEnables1[static_cast<uint8_t>(ipmi::PayloadType::SOL)] =
1161 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)][chIndex];
1162 }
1163}
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301164
1165void UserAccess::readUserData()
1166{
1167 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1168 userLock{*userMutex};
1169
1170 std::ifstream iUsrData(ipmiUserDataFile, std::ios::in | std::ios::binary);
1171 if (!iUsrData.good())
1172 {
George Liu82844ef2024-07-17 17:03:56 +08001173 lg2::error("Error in reading IPMI user data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301174 throw std::ios_base::failure("Error opening IPMI user data file");
1175 }
1176
1177 Json jsonUsersTbl = Json::array();
1178 jsonUsersTbl = Json::parse(iUsrData, nullptr, false);
1179
1180 if (jsonUsersTbl.size() != ipmiMaxUsers)
1181 {
George Liu82844ef2024-07-17 17:03:56 +08001182 lg2::error("Error in reading IPMI user data file - User count issues");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301183 throw std::runtime_error(
1184 "Corrupted IPMI user data file - invalid user count");
1185 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001186
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301187 // user index 0 is reserved, starts with 1
1188 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1189 {
1190 Json userInfo = jsonUsersTbl[usrIndex - 1]; // json array starts with 0.
1191 if (userInfo.is_null())
1192 {
George Liu82844ef2024-07-17 17:03:56 +08001193 lg2::error("Error in reading IPMI user data file - "
1194 "user info corrupted");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301195 throw std::runtime_error(
1196 "Corrupted IPMI user data file - invalid user info");
1197 }
1198 std::string userName = userInfo[jsonUserName].get<std::string>();
Orit Kashanyf7616312025-08-24 04:48:41 -07001199
1200 // Fixed-width username buffer in struct
1201 safeUsernameCopyToBuffer(usersTbl.user[usrIndex].userName,
1202 sizeof(usersTbl.user[usrIndex].userName),
1203 userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301204
1205 std::vector<std::string> privilege =
1206 userInfo[jsonPriv].get<std::vector<std::string>>();
1207 std::vector<bool> ipmiEnabled =
1208 userInfo[jsonIpmiEnabled].get<std::vector<bool>>();
1209 std::vector<bool> linkAuthEnabled =
1210 userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
1211 std::vector<bool> accessCallback =
1212 userInfo[jsonAccCallbk].get<std::vector<bool>>();
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001213
1214 // Payload Enables Processing.
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001215 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1216 stdPayload = {};
1217 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1218 oemPayload = {};
1219 try
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001220 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001221 const auto jsonPayloadEnabled = userInfo.at(payloadEnabledStr);
1222 for (auto payloadNum = 0; payloadNum < payloadsPerByte;
1223 payloadNum++)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001224 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001225 std::ostringstream stdPayloadStream;
1226 std::ostringstream oemPayloadStream;
1227
1228 stdPayloadStream << stdPayloadStr << payloadNum;
1229 oemPayloadStream << oemPayloadStr << payloadNum;
1230
1231 stdPayload[payloadNum] =
1232 jsonPayloadEnabled[stdPayloadStream.str()]
1233 .get<std::array<bool, ipmiMaxChannels>>();
1234 oemPayload[payloadNum] =
1235 jsonPayloadEnabled[oemPayloadStream.str()]
1236 .get<std::array<bool, ipmiMaxChannels>>();
1237
1238 if (stdPayload[payloadNum].size() != ipmiMaxChannels ||
1239 oemPayload[payloadNum].size() != ipmiMaxChannels)
1240 {
George Liu82844ef2024-07-17 17:03:56 +08001241 lg2::error("Error in reading IPMI user data file - "
1242 "payload properties corrupted");
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001243 throw std::runtime_error(
1244 "Corrupted IPMI user data file - payload properties");
1245 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001246 }
1247 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001248 catch (const Json::out_of_range& e)
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001249 {
1250 // Key not found in 'userInfo'; possibly an old JSON file. Use
1251 // default values for all payloads, and SOL payload default is true.
1252 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)].fill(true);
1253 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001254
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301255 if (privilege.size() != ipmiMaxChannels ||
1256 ipmiEnabled.size() != ipmiMaxChannels ||
1257 linkAuthEnabled.size() != ipmiMaxChannels ||
1258 accessCallback.size() != ipmiMaxChannels)
1259 {
George Liu82844ef2024-07-17 17:03:56 +08001260 lg2::error("Error in reading IPMI user data file - "
1261 "properties corrupted");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301262 throw std::runtime_error(
1263 "Corrupted IPMI user data file - properties");
1264 }
1265 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1266 {
1267 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege =
1268 static_cast<uint8_t>(
1269 convertToIPMIPrivilege(privilege[chIndex]));
1270 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled =
1271 ipmiEnabled[chIndex];
1272 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled =
1273 linkAuthEnabled[chIndex];
1274 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
1275 accessCallback[chIndex];
1276 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001277 updatePayloadAccessInUserInfo(stdPayload, oemPayload,
1278 usersTbl.user[usrIndex]);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301279 usersTbl.user[usrIndex].userEnabled =
1280 userInfo[jsonUserEnabled].get<bool>();
1281 usersTbl.user[usrIndex].userInSystem =
1282 userInfo[jsonUserInSys].get<bool>();
1283 usersTbl.user[usrIndex].fixedUserName =
1284 userInfo[jsonFixedUser].get<bool>();
1285 }
1286
George Liu82844ef2024-07-17 17:03:56 +08001287 lg2::debug("User data read from IPMI data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301288 iUsrData.close();
1289 // Update the timestamp
1290 fileLastUpdatedTime = getUpdatedFileTime();
1291 return;
1292}
1293
1294void UserAccess::writeUserData()
1295{
1296 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1297 userLock{*userMutex};
1298
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301299 Json jsonUsersTbl = Json::array();
1300 // user index 0 is reserved, starts with 1
1301 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1302 {
1303 Json jsonUserInfo;
Orit Kashanyf7616312025-08-24 04:48:41 -07001304 jsonUserInfo[jsonUserName] =
1305 safeUsernameString(usersTbl.user[usrIndex].userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301306 std::vector<std::string> privilege(ipmiMaxChannels);
1307 std::vector<bool> ipmiEnabled(ipmiMaxChannels);
1308 std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
1309 std::vector<bool> accessCallback(ipmiMaxChannels);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001310
1311 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1312 stdPayload;
1313 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1314 oemPayload;
1315
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301316 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1317 {
1318 privilege[chIndex] =
1319 convertToSystemPrivilege(static_cast<CommandPrivilege>(
1320 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege));
1321 ipmiEnabled[chIndex] =
1322 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled;
1323 linkAuthEnabled[chIndex] =
1324 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled;
1325 accessCallback[chIndex] =
1326 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback;
1327 }
1328 jsonUserInfo[jsonPriv] = privilege;
1329 jsonUserInfo[jsonIpmiEnabled] = ipmiEnabled;
1330 jsonUserInfo[jsonLinkAuthEnabled] = linkAuthEnabled;
1331 jsonUserInfo[jsonAccCallbk] = accessCallback;
1332 jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
1333 jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
1334 jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001335
1336 readPayloadAccessFromUserInfo(usersTbl.user[usrIndex], stdPayload,
1337 oemPayload);
Patrick Williams1318a5e2024-08-16 15:19:54 -04001338 Json jsonPayloadEnabledInfo =
1339 constructJsonPayloadEnables(stdPayload, oemPayload);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001340 jsonUserInfo[payloadEnabledStr] = jsonPayloadEnabledInfo;
1341
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301342 jsonUsersTbl.push_back(jsonUserInfo);
1343 }
1344
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301345 static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
1346 int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
1347 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1348 if (fd < 0)
1349 {
George Liu82844ef2024-07-17 17:03:56 +08001350 lg2::error("Error in creating temporary IPMI user data file");
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301351 throw std::ios_base::failure(
1352 "Error in creating temporary IPMI user data file");
1353 }
1354 const auto& writeStr = jsonUsersTbl.dump();
1355 if (write(fd, writeStr.c_str(), writeStr.size()) !=
1356 static_cast<ssize_t>(writeStr.size()))
1357 {
1358 close(fd);
George Liu82844ef2024-07-17 17:03:56 +08001359 lg2::error("Error in writing temporary IPMI user data file");
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301360 throw std::ios_base::failure(
1361 "Error in writing temporary IPMI user data file");
1362 }
1363 close(fd);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301364
1365 if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
1366 {
George Liu82844ef2024-07-17 17:03:56 +08001367 lg2::error("Error in renaming temporary IPMI user data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301368 throw std::runtime_error("Error in renaming IPMI user data file");
1369 }
1370 // Update the timestamp
1371 fileLastUpdatedTime = getUpdatedFileTime();
1372 return;
1373}
1374
1375bool UserAccess::addUserEntry(const std::string& userName,
1376 const std::string& sysPriv, const bool& enabled)
1377{
1378 UsersTbl* userData = getUsersTblPtr();
1379 size_t freeIndex = 0xFF;
1380 // user index 0 is reserved, starts with 1
1381 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1382 {
Orit Kashanyf7616312025-08-24 04:48:41 -07001383 std::string curName =
1384 safeUsernameString(userData->user[usrIndex].userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301385 if (userName == curName)
1386 {
George Liu82844ef2024-07-17 17:03:56 +08001387 lg2::debug("Username {USER_NAME} exists", "USER_NAME", userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301388 return false; // user name exists.
1389 }
1390
1391 if ((!userData->user[usrIndex].userInSystem) &&
1392 (userData->user[usrIndex].userName[0] == '\0') &&
1393 (freeIndex == 0xFF))
1394 {
1395 freeIndex = usrIndex;
1396 }
1397 }
1398 if (freeIndex == 0xFF)
1399 {
George Liu82844ef2024-07-17 17:03:56 +08001400 lg2::error("No empty slots found");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301401 return false;
1402 }
Orit Kashanyf7616312025-08-24 04:48:41 -07001403
1404 safeUsernameCopyToBuffer(userData->user[freeIndex].userName,
1405 sizeof(userData->user[freeIndex].userName),
1406 userName);
1407
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301408 uint8_t priv =
1409 static_cast<uint8_t>(UserAccess::convertToIPMIPrivilege(sysPriv)) &
1410 privMask;
1411 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1412 {
1413 userData->user[freeIndex].userPrivAccess[chIndex].privilege = priv;
1414 userData->user[freeIndex].userPrivAccess[chIndex].ipmiEnabled = true;
1415 userData->user[freeIndex].userPrivAccess[chIndex].linkAuthEnabled =
1416 true;
1417 userData->user[freeIndex].userPrivAccess[chIndex].accessCallback = true;
1418 }
1419 userData->user[freeIndex].userInSystem = true;
1420 userData->user[freeIndex].userEnabled = enabled;
1421
1422 return true;
1423}
1424
1425void UserAccess::deleteUserIndex(const size_t& usrIdx)
1426{
1427 UsersTbl* userData = getUsersTblPtr();
1428
Orit Kashanyf7616312025-08-24 04:48:41 -07001429 std::string userName = safeUsernameString(userData->user[usrIdx].userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301430 ipmiClearUserEntryPassword(userName);
Orit Kashanyf7616312025-08-24 04:48:41 -07001431 std::memset(userData->user[usrIdx].userName, 0,
1432 sizeof(userData->user[usrIdx].userName));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301433 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1434 {
1435 userData->user[usrIdx].userPrivAccess[chIndex].privilege = privNoAccess;
1436 userData->user[usrIdx].userPrivAccess[chIndex].ipmiEnabled = false;
1437 userData->user[usrIdx].userPrivAccess[chIndex].linkAuthEnabled = false;
1438 userData->user[usrIdx].userPrivAccess[chIndex].accessCallback = false;
1439 }
1440 userData->user[usrIdx].userInSystem = false;
1441 userData->user[usrIdx].userEnabled = false;
1442 return;
1443}
1444
1445void UserAccess::checkAndReloadUserData()
1446{
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001447 std::timespec updateTime = getUpdatedFileTime();
1448 if ((updateTime.tv_sec != fileLastUpdatedTime.tv_sec ||
1449 updateTime.tv_nsec != fileLastUpdatedTime.tv_nsec) ||
1450 (updateTime.tv_sec == 0 && updateTime.tv_nsec == 0))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301451 {
1452 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1453 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1454 readUserData();
1455 }
1456 return;
1457}
1458
1459UsersTbl* UserAccess::getUsersTblPtr()
1460{
1461 // reload data before using it.
1462 checkAndReloadUserData();
1463 return &usersTbl;
1464}
1465
1466void UserAccess::getSystemPrivAndGroups()
1467{
1468 std::map<std::string, PrivAndGroupType> properties;
1469 try
1470 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +07001471 auto method = bus.new_method_call(userMgrService, userMgrObjBasePath,
1472 dBusPropertiesInterface,
1473 getAllPropertiesMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301474 method.append(userMgrInterface);
1475
1476 auto reply = bus.call(method);
1477 reply.read(properties);
1478 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001479 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301480 {
George Liu82844ef2024-07-17 17:03:56 +08001481 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1482 getAllPropertiesMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301483 return;
1484 }
1485 for (const auto& t : properties)
1486 {
1487 auto key = t.first;
1488 if (key == allPrivProperty)
1489 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001490 availablePrivileges = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301491 }
1492 else if (key == allGrpProperty)
1493 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001494 availableGroups = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301495 }
1496 }
1497 // TODO: Implement Supported Privilege & Groups verification logic
1498 return;
1499}
1500
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001501std::timespec UserAccess::getUpdatedFileTime()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301502{
1503 struct stat fileStat;
1504 if (stat(ipmiUserDataFile, &fileStat) != 0)
1505 {
George Liu82844ef2024-07-17 17:03:56 +08001506 lg2::debug("Error in getting last updated time stamp");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001507 return std::timespec{0, 0};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301508 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001509 return fileStat.st_mtim;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301510}
1511
1512void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
1513 std::vector<std::string>& usrGrps,
1514 std::string& usrPriv, bool& usrEnabled)
1515{
1516 for (const auto& t : properties)
1517 {
1518 std::string key = t.first;
1519 if (key == userPrivProperty)
1520 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001521 usrPriv = std::get<std::string>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301522 }
1523 else if (key == userGrpProperty)
1524 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001525 usrGrps = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301526 }
1527 else if (key == userEnabledProperty)
1528 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001529 usrEnabled = std::get<bool>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301530 }
1531 }
1532 return;
1533}
1534
1535int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
1536 std::vector<std::string>& usrGrps,
1537 std::string& usrPriv, bool& usrEnabled)
1538{
1539 auto usrObj = userObjs.find(usersInterface);
1540 if (usrObj != userObjs.end())
1541 {
1542 getUserProperties(usrObj->second, usrGrps, usrPriv, usrEnabled);
1543 return 0;
1544 }
1545 return -EIO;
1546}
1547
arun-pmbbe728c2020-01-10 15:18:04 +05301548void UserAccess::cacheUserDataFile()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301549{
1550 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1551 userLock{*userMutex};
1552 try
1553 {
1554 readUserData();
1555 }
1556 catch (const std::ios_base::failure& e)
1557 { // File is empty, create it for the first time
1558 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1559 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1560 // user index 0 is reserved, starts with 1
1561 for (size_t userIndex = 1; userIndex <= ipmiMaxUsers; ++userIndex)
1562 {
1563 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1564 {
1565 usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
1566 privNoAccess;
Saravanan Palanisamy92d81192019-08-07 18:00:04 +00001567 usersTbl.user[userIndex]
1568 .payloadAccess[chIndex]
1569 .stdPayloadEnables1[static_cast<uint8_t>(
1570 ipmi::PayloadType::SOL)] = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301571 }
1572 }
1573 writeUserData();
1574 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001575 // Create lock file if it does not exist
1576 int fd = open(ipmiUserSignalLockFile, O_CREAT | O_TRUNC | O_SYNC,
1577 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1578 if (fd < 0)
1579 {
George Liu82844ef2024-07-17 17:03:56 +08001580 lg2::error("Error in creating IPMI user signal lock file");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001581 throw std::ios_base::failure(
1582 "Error in creating temporary IPMI user signal lock file");
1583 }
1584 close(fd);
1585
1586 sigHndlrLock = boost::interprocess::file_lock(ipmiUserSignalLockFile);
George Liu1a2e1502022-07-08 12:20:19 +08001587 // Register it for single object and single process either netipmid /
arun-pmbbe728c2020-01-10 15:18:04 +05301588 // host-ipmid
1589 if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
1590 {
George Liu82844ef2024-07-17 17:03:56 +08001591 lg2::debug("Registering signal handler");
arun-pmbbe728c2020-01-10 15:18:04 +05301592 userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
1593 bus,
1594 sdbusplus::bus::match::rules::type::signal() +
1595 sdbusplus::bus::match::rules::interface(dBusObjManager) +
1596 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001597 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001598 userUpdatedSignalHandler(*this, msg);
1599 });
arun-pmbbe728c2020-01-10 15:18:04 +05301600 userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
1601 bus,
1602 sdbusplus::bus::match::rules::type::signal() +
1603 sdbusplus::bus::match::rules::interface(userMgrInterface) +
1604 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001605 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001606 userUpdatedSignalHandler(*this, msg);
1607 });
arun-pmbbe728c2020-01-10 15:18:04 +05301608 userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
1609 bus,
1610 sdbusplus::bus::match::rules::type::signal() +
1611 sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
1612 sdbusplus::bus::match::rules::interface(
1613 dBusPropertiesInterface) +
1614 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
1615 sdbusplus::bus::match::rules::argN(0, usersInterface),
Patrick Williams5d82f472022-07-22 19:26:53 -05001616 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001617 userUpdatedSignalHandler(*this, msg);
1618 });
arun-pmbbe728c2020-01-10 15:18:04 +05301619 signalHndlrObject = true;
1620 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301621 std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
1622 try
1623 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +07001624 auto method =
1625 bus.new_method_call(userMgrService, userMgrObjBasePath,
1626 dBusObjManager, getManagedObjectsMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301627 auto reply = bus.call(method);
1628 reply.read(managedObjs);
1629 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001630 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301631 {
George Liu82844ef2024-07-17 17:03:56 +08001632 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1633 getManagedObjectsMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301634 return;
1635 }
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301636 bool updateRequired = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301637 UsersTbl* userData = &usersTbl;
1638 // user index 0 is reserved, starts with 1
1639 for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
1640 {
1641 if ((userData->user[usrIdx].userInSystem) &&
1642 (userData->user[usrIdx].userName[0] != '\0'))
1643 {
1644 std::vector<std::string> usrGrps;
1645 std::string usrPriv;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301646
Orit Kashanyf7616312025-08-24 04:48:41 -07001647 std::string userName =
1648 safeUsernameString(userData->user[usrIdx].userName);
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301649 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1650 tempUserPath /= userName;
1651 std::string usersPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301652
1653 auto usrObj = managedObjs.find(usersPath);
1654 if (usrObj != managedObjs.end())
1655 {
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001656 bool usrEnabled = false;
Patrick Venture3a697ad2019-08-19 11:12:05 -07001657
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301658 // User exist. Lets check and update other fileds
1659 getUserObjProperties(usrObj->second, usrGrps, usrPriv,
1660 usrEnabled);
1661 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
1662 usrGrps.end())
1663 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301664 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301665 // Group "ipmi" is removed so lets remove user in IPMI
1666 deleteUserIndex(usrIdx);
1667 }
1668 else
1669 {
1670 // Group "ipmi" is present so lets update other properties
1671 // in IPMI
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001672 uint8_t priv = UserAccess::convertToIPMIPrivilege(usrPriv) &
1673 privMask;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301674 // Update all channels priv, only if it is not equivalent to
1675 // getUsrMgmtSyncIndex()
1676 if (userData->user[usrIdx]
1677 .userPrivAccess[getUsrMgmtSyncIndex()]
1678 .privilege != priv)
1679 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301680 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301681 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
1682 ++chIndex)
1683 {
1684 userData->user[usrIdx]
1685 .userPrivAccess[chIndex]
1686 .privilege = priv;
1687 }
1688 }
1689 if (userData->user[usrIdx].userEnabled != usrEnabled)
1690 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301691 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301692 userData->user[usrIdx].userEnabled = usrEnabled;
1693 }
1694 }
1695
1696 // We are done with this obj. lets delete from MAP
1697 managedObjs.erase(usrObj);
1698 }
1699 else
1700 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301701 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301702 deleteUserIndex(usrIdx);
1703 }
1704 }
1705 }
1706
1707 // Walk through remnaining managedObj users list
1708 // Add them to ipmi data base
1709 for (const auto& usrObj : managedObjs)
1710 {
1711 std::vector<std::string> usrGrps;
1712 std::string usrPriv, userName;
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001713 bool usrEnabled = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301714 std::string usrObjPath = std::string(usrObj.first);
1715 if (getUserNameFromPath(usrObj.first.str, userName) != 0)
1716 {
George Liu82844ef2024-07-17 17:03:56 +08001717 lg2::error("Error in user object path");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301718 continue;
1719 }
1720 getUserObjProperties(usrObj.second, usrGrps, usrPriv, usrEnabled);
1721 // Add 'ipmi' group users
1722 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
1723 usrGrps.end())
1724 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301725 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301726 // CREATE NEW USER
1727 if (true != addUserEntry(userName, usrPriv, usrEnabled))
1728 {
1729 break;
1730 }
1731 }
1732 }
1733
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301734 if (updateRequired)
1735 {
1736 // All userData slots update done. Lets write the data
1737 writeUserData();
1738 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301739
1740 return;
1741}
1742} // namespace ipmi