blob: bf114629bdfc86791bc858f64f7e5f50dffe8087 [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
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050036#include <cerrno>
37#include <fstream>
38#include <regex>
39#include <variant>
40
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053041namespace ipmi
42{
43
44// TODO: Move D-Bus & Object Manager related stuff, to common files
45// D-Bus property related
46static constexpr const char* dBusPropertiesInterface =
47 "org.freedesktop.DBus.Properties";
48static constexpr const char* getAllPropertiesMethod = "GetAll";
49static constexpr const char* propertiesChangedSignal = "PropertiesChanged";
50static constexpr const char* setPropertiesMethod = "Set";
51
52// Object Manager related
53static constexpr const char* dBusObjManager =
54 "org.freedesktop.DBus.ObjectManager";
55static constexpr const char* getManagedObjectsMethod = "GetManagedObjects";
56// Object Manager signals
57static constexpr const char* intfAddedSignal = "InterfacesAdded";
58static constexpr const char* intfRemovedSignal = "InterfacesRemoved";
59
60// Object Mapper related
61static constexpr const char* objMapperService =
62 "xyz.openbmc_project.ObjectMapper";
63static constexpr const char* objMapperPath =
64 "/xyz/openbmc_project/object_mapper";
65static constexpr const char* objMapperInterface =
66 "xyz.openbmc_project.ObjectMapper";
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053067static constexpr const char* getObjectMethod = "GetObject";
68
69static constexpr const char* ipmiUserMutex = "ipmi_usr_mutex";
70static constexpr const char* ipmiMutexCleanupLockFile =
Tyson Tuckerbear8c974f72022-09-19 15:49:06 -070071 "/var/run/ipmi/ipmi_usr_mutex_cleanup";
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +000072static constexpr const char* ipmiUserSignalLockFile =
Tyson Tuckerbear8c974f72022-09-19 15:49:06 -070073 "/var/run/ipmi/ipmi_usr_signal_mutex";
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053074static constexpr const char* ipmiUserDataFile = "/var/lib/ipmi/ipmi_user.json";
75static constexpr const char* ipmiGrpName = "ipmi";
76static constexpr size_t privNoAccess = 0xF;
77static constexpr size_t privMask = 0xF;
78
79// User manager related
80static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user";
81static constexpr const char* userObjBasePath = "/xyz/openbmc_project/user";
82static constexpr const char* userMgrInterface =
83 "xyz.openbmc_project.User.Manager";
84static constexpr const char* usersInterface =
85 "xyz.openbmc_project.User.Attributes";
86static constexpr const char* deleteUserInterface =
87 "xyz.openbmc_project.Object.Delete";
88
89static constexpr const char* createUserMethod = "CreateUser";
90static constexpr const char* deleteUserMethod = "Delete";
91static constexpr const char* renameUserMethod = "RenameUser";
92// User manager signal memebers
93static constexpr const char* userRenamedSignal = "UserRenamed";
94// Mgr interface properties
95static constexpr const char* allPrivProperty = "AllPrivileges";
96static constexpr const char* allGrpProperty = "AllGroups";
97// User interface properties
98static constexpr const char* userPrivProperty = "UserPrivilege";
99static constexpr const char* userGrpProperty = "UserGroups";
100static constexpr const char* userEnabledProperty = "UserEnabled";
101
102static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = {
103 "priv-reserved", // PRIVILEGE_RESERVED - 0
104 "priv-callback", // PRIVILEGE_CALLBACK - 1
105 "priv-user", // PRIVILEGE_USER - 2
106 "priv-operator", // PRIVILEGE_OPERATOR - 3
107 "priv-admin", // PRIVILEGE_ADMIN - 4
108 "priv-custom" // PRIVILEGE_OEM - 5
109};
110
111using namespace phosphor::logging;
112using Json = nlohmann::json;
113
Vernon Mauery16b86932019-05-01 08:36:11 -0700114using PrivAndGroupType = std::variant<std::string, std::vector<std::string>>;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530115
116using NoResource =
Willy Tu523e2d12023-09-05 11:36:48 -0700117 sdbusplus::error::xyz::openbmc_project::user::common::NoResource;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530118
119using InternalFailure =
Willy Tu523e2d12023-09-05 11:36:48 -0700120 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530121
Lei YU4b0ddb62019-01-25 16:43:50 +0800122std::unique_ptr<sdbusplus::bus::match_t> userUpdatedSignal
123 __attribute__((init_priority(101)));
124std::unique_ptr<sdbusplus::bus::match_t> userMgrRenamedSignal
125 __attribute__((init_priority(101)));
126std::unique_ptr<sdbusplus::bus::match_t> userPropertiesSignal
127 __attribute__((init_priority(101)));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530128
129// TODO: Below code can be removed once it is moved to common layer libmiscutil
Patrick Williams5d82f472022-07-22 19:26:53 -0500130std::string getUserService(sdbusplus::bus_t& bus, const std::string& intf,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530131 const std::string& path)
132{
133 auto mapperCall = bus.new_method_call(objMapperService, objMapperPath,
134 objMapperInterface, getObjectMethod);
135
136 mapperCall.append(path);
137 mapperCall.append(std::vector<std::string>({intf}));
138
139 auto mapperResponseMsg = bus.call(mapperCall);
140
141 std::map<std::string, std::vector<std::string>> mapperResponse;
142 mapperResponseMsg.read(mapperResponse);
143
144 if (mapperResponse.begin() == mapperResponse.end())
145 {
146 throw sdbusplus::exception::SdBusError(
147 -EIO, "ERROR in reading the mapper response");
148 }
149
150 return mapperResponse.begin()->first;
151}
152
Patrick Williams5d82f472022-07-22 19:26:53 -0500153void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530154 const std::string& objPath, const std::string& interface,
155 const std::string& property,
156 const DbusUserPropVariant& value)
157{
158 try
159 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400160 auto method =
161 bus.new_method_call(service.c_str(), objPath.c_str(),
162 dBusPropertiesInterface, setPropertiesMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530163 method.append(interface, property, value);
164 bus.call(method);
165 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500166 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530167 {
George Liu82844ef2024-07-17 17:03:56 +0800168 lg2::error("Failed to set {PROPERTY}, path: {PATH}, "
169 "interface: {INTERFACE}",
170 "PROPERTY", property, "PATH", objPath, "INTERFACE",
171 interface);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530172 throw;
173 }
174}
175
Willy Tu523e2d12023-09-05 11:36:48 -0700176std::string getUserServiceName()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530177{
Patrick Williams5d82f472022-07-22 19:26:53 -0500178 static sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530179 static std::string userMgmtService;
180 if (userMgmtService.empty())
181 {
182 try
183 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400184 userMgmtService =
185 ipmi::getUserService(bus, userMgrInterface, userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530186 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500187 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530188 {
189 userMgmtService.clear();
190 }
191 }
192 return userMgmtService;
193}
194
195UserAccess& getUserAccessObject()
196{
197 static UserAccess userAccess;
198 return userAccess;
199}
200
201int getUserNameFromPath(const std::string& path, std::string& userName)
202{
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530203 sdbusplus::message::object_path objPath(path);
204 userName.assign(objPath.filename());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530205 return 0;
206}
207
208void userUpdateHelper(UserAccess& usrAccess, const UserUpdateEvent& userEvent,
209 const std::string& userName, const std::string& priv,
210 const bool& enabled, const std::string& newUserName)
211{
212 UsersTbl* userData = usrAccess.getUsersTblPtr();
213 if (userEvent == UserUpdateEvent::userCreated)
214 {
215 if (usrAccess.addUserEntry(userName, priv, enabled) == false)
216 {
217 return;
218 }
219 }
220 else
221 {
222 // user index 0 is reserved, starts with 1
223 size_t usrIndex = 1;
224 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
225 {
226 std::string curName(
227 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
228 ipmiMaxUserName);
229 if (userName == curName)
230 {
231 break; // found the entry
232 }
233 }
234 if (usrIndex > ipmiMaxUsers)
235 {
George Liu82844ef2024-07-17 17:03:56 +0800236 lg2::debug("User not found for signal, user name: {USER_NAME}, "
237 "user event: {USER_EVENT}",
238 "USER_NAME", userName, "USER_EVENT", userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530239 return;
240 }
241 switch (userEvent)
242 {
243 case UserUpdateEvent::userDeleted:
244 {
245 usrAccess.deleteUserIndex(usrIndex);
246 break;
247 }
248 case UserUpdateEvent::userPrivUpdated:
249 {
250 uint8_t userPriv =
251 static_cast<uint8_t>(
252 UserAccess::convertToIPMIPrivilege(priv)) &
253 privMask;
254 // Update all channels privileges, only if it is not equivalent
255 // to getUsrMgmtSyncIndex()
256 if (userData->user[usrIndex]
257 .userPrivAccess[UserAccess::getUsrMgmtSyncIndex()]
258 .privilege != userPriv)
259 {
260 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
261 ++chIndex)
262 {
263 userData->user[usrIndex]
264 .userPrivAccess[chIndex]
265 .privilege = userPriv;
266 }
267 }
268 break;
269 }
270 case UserUpdateEvent::userRenamed:
271 {
272 std::fill(
273 static_cast<uint8_t*>(userData->user[usrIndex].userName),
274 static_cast<uint8_t*>(userData->user[usrIndex].userName) +
275 sizeof(userData->user[usrIndex].userName),
276 0);
277 std::strncpy(
278 reinterpret_cast<char*>(userData->user[usrIndex].userName),
279 newUserName.c_str(), ipmiMaxUserName);
280 ipmiRenameUserEntryPassword(userName, newUserName);
281 break;
282 }
283 case UserUpdateEvent::userStateUpdated:
284 {
285 userData->user[usrIndex].userEnabled = enabled;
286 break;
287 }
288 default:
289 {
George Liu82844ef2024-07-17 17:03:56 +0800290 lg2::error("Unhandled user event: {USER_EVENT}", "USER_EVENT",
291 userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530292 return;
293 }
294 }
295 }
296 usrAccess.writeUserData();
George Liu82844ef2024-07-17 17:03:56 +0800297 lg2::debug("User event handled successfully, user name: {USER_NAME}, "
298 "user event: {USER_EVENT}",
299 "USER_NAME", userName.c_str(), "USER_EVENT", userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530300
301 return;
302}
303
Patrick Williams5d82f472022-07-22 19:26:53 -0500304void userUpdatedSignalHandler(UserAccess& usrAccess, sdbusplus::message_t& msg)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530305{
Patrick Williams5d82f472022-07-22 19:26:53 -0500306 static sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530307 std::string signal = msg.get_member();
Patrick Venture3a697ad2019-08-19 11:12:05 -0700308 std::string userName, priv, newUserName;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530309 std::vector<std::string> groups;
310 bool enabled = false;
311 UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent;
312 if (signal == intfAddedSignal)
313 {
314 DbusUserObjPath objPath;
315 DbusUserObjValue objValue;
316 msg.read(objPath, objValue);
317 getUserNameFromPath(objPath.str, userName);
318 if (usrAccess.getUserObjProperties(objValue, groups, priv, enabled) !=
319 0)
320 {
321 return;
322 }
323 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
324 groups.end())
325 {
326 return;
327 }
328 userEvent = UserUpdateEvent::userCreated;
329 }
330 else if (signal == intfRemovedSignal)
331 {
332 DbusUserObjPath objPath;
333 std::vector<std::string> interfaces;
334 msg.read(objPath, interfaces);
335 getUserNameFromPath(objPath.str, userName);
336 userEvent = UserUpdateEvent::userDeleted;
337 }
338 else if (signal == userRenamedSignal)
339 {
340 msg.read(userName, newUserName);
341 userEvent = UserUpdateEvent::userRenamed;
342 }
343 else if (signal == propertiesChangedSignal)
344 {
345 getUserNameFromPath(msg.get_path(), userName);
346 }
347 else
348 {
George Liu82844ef2024-07-17 17:03:56 +0800349 lg2::error("Unknown user update signal: {SIGNAL}", "SIGNAL", signal);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530350 return;
351 }
352
353 if (signal.empty() || userName.empty() ||
354 (signal == userRenamedSignal && newUserName.empty()))
355 {
George Liu82844ef2024-07-17 17:03:56 +0800356 lg2::error("Invalid inputs received");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530357 return;
358 }
359
360 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
361 userLock{*(usrAccess.userMutex)};
362 usrAccess.checkAndReloadUserData();
363
364 if (signal == propertiesChangedSignal)
365 {
366 std::string intfName;
367 DbusUserObjProperties chProperties;
368 msg.read(intfName, chProperties); // skip reading 3rd argument.
369 for (const auto& prop : chProperties)
370 {
371 userEvent = UserUpdateEvent::reservedEvent;
372 std::string member = prop.first;
373 if (member == userPrivProperty)
374 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700375 priv = std::get<std::string>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530376 userEvent = UserUpdateEvent::userPrivUpdated;
377 }
378 else if (member == userGrpProperty)
379 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700380 groups = std::get<std::vector<std::string>>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530381 userEvent = UserUpdateEvent::userGrpUpdated;
382 }
383 else if (member == userEnabledProperty)
384 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700385 enabled = std::get<bool>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530386 userEvent = UserUpdateEvent::userStateUpdated;
387 }
388 // Process based on event type.
389 if (userEvent == UserUpdateEvent::userGrpUpdated)
390 {
391 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
392 groups.end())
393 {
394 // remove user from ipmi user list.
395 userUpdateHelper(usrAccess, UserUpdateEvent::userDeleted,
396 userName, priv, enabled, newUserName);
397 }
398 else
399 {
400 DbusUserObjProperties properties;
401 try
402 {
403 auto method = bus.new_method_call(
404 getUserServiceName().c_str(), msg.get_path(),
405 dBusPropertiesInterface, getAllPropertiesMethod);
406 method.append(usersInterface);
407 auto reply = bus.call(method);
408 reply.read(properties);
409 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500410 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530411 {
George Liu82844ef2024-07-17 17:03:56 +0800412 lg2::debug("Failed to excute {METHOD}, path: {PATH}",
413 "METHOD", getAllPropertiesMethod, "PATH",
414 msg.get_path());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530415 return;
416 }
417 usrAccess.getUserProperties(properties, groups, priv,
418 enabled);
419 // add user to ipmi user list.
420 userUpdateHelper(usrAccess, UserUpdateEvent::userCreated,
421 userName, priv, enabled, newUserName);
422 }
423 }
424 else if (userEvent != UserUpdateEvent::reservedEvent)
425 {
426 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
427 newUserName);
428 }
429 }
430 }
431 else if (userEvent != UserUpdateEvent::reservedEvent)
432 {
433 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
434 newUserName);
435 }
436 return;
437}
438
439UserAccess::~UserAccess()
440{
441 if (signalHndlrObject)
442 {
443 userUpdatedSignal.reset();
444 userMgrRenamedSignal.reset();
445 userPropertiesSignal.reset();
446 sigHndlrLock.unlock();
447 }
448}
449
450UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection())
451{
452 std::ofstream mutexCleanUpFile;
453 mutexCleanUpFile.open(ipmiMutexCleanupLockFile,
454 std::ofstream::out | std::ofstream::app);
455 if (!mutexCleanUpFile.good())
456 {
George Liu82844ef2024-07-17 17:03:56 +0800457 lg2::debug("Unable to open mutex cleanup file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530458 return;
459 }
460 mutexCleanUpFile.close();
461 mutexCleanupLock = boost::interprocess::file_lock(ipmiMutexCleanupLockFile);
462 if (mutexCleanupLock.try_lock())
463 {
464 boost::interprocess::named_recursive_mutex::remove(ipmiUserMutex);
465 }
466 mutexCleanupLock.lock_sharable();
467 userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>(
468 boost::interprocess::open_or_create, ipmiUserMutex);
469
arun-pmbbe728c2020-01-10 15:18:04 +0530470 cacheUserDataFile();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530471 getSystemPrivAndGroups();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530472}
473
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530474UserInfo* UserAccess::getUserInfo(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530475{
476 checkAndReloadUserData();
477 return &usersTbl.user[userId];
478}
479
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530480void UserAccess::setUserInfo(const uint8_t userId, UserInfo* userInfo)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530481{
482 checkAndReloadUserData();
483 std::copy(reinterpret_cast<uint8_t*>(userInfo),
484 reinterpret_cast<uint8_t*>(userInfo) + sizeof(*userInfo),
485 reinterpret_cast<uint8_t*>(&usersTbl.user[userId]));
486 writeUserData();
487}
488
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530489bool UserAccess::isValidChannel(const uint8_t chNum)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530490{
491 return (chNum < ipmiMaxChannels);
492}
493
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530494bool UserAccess::isValidUserId(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530495{
496 return ((userId <= ipmiMaxUsers) && (userId != reservedUserId));
497}
498
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530499bool UserAccess::isValidPrivilege(const uint8_t priv)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530500{
jayaprakash Mutyala0e2dbee2019-12-26 13:03:04 +0000501 // Callback privilege is deprecated in OpenBMC
Alexander Filippovfc24fa52022-02-01 14:57:59 +0300502 return isValidPrivLimit(priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530503}
504
505uint8_t UserAccess::getUsrMgmtSyncIndex()
506{
Johnathan Manteyfd61fc32021-04-08 11:05:38 -0700507 // Identify the IPMI channel used to assign system user privilege levels
508 // in phosphor-user-manager. The default value is IPMI Channel 1. To
509 // assign a different channel add:
510 // "is_management_nic" : true
511 // into the channel_config.json file describing the assignment of the IPMI
512 // channels. It is only necessary to add the string above to ONE record in
513 // the channel_config.json file. All other records will be automatically
514 // assigned a "false" value.
515 return getChannelConfigObject().getManagementNICID();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530516}
517
518CommandPrivilege UserAccess::convertToIPMIPrivilege(const std::string& value)
519{
520 auto iter = std::find(ipmiPrivIndex.begin(), ipmiPrivIndex.end(), value);
521 if (iter == ipmiPrivIndex.end())
522 {
523 if (value == "")
524 {
525 return static_cast<CommandPrivilege>(privNoAccess);
526 }
George Liu82844ef2024-07-17 17:03:56 +0800527 lg2::error("Error in converting to IPMI privilege: {PRIV}", "PRIV",
528 value);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530529 throw std::out_of_range("Out of range - convertToIPMIPrivilege");
530 }
531 else
532 {
533 return static_cast<CommandPrivilege>(
534 std::distance(ipmiPrivIndex.begin(), iter));
535 }
536}
537
538std::string UserAccess::convertToSystemPrivilege(const CommandPrivilege& value)
539{
540 if (value == static_cast<CommandPrivilege>(privNoAccess))
541 {
542 return "";
543 }
544 try
545 {
546 return ipmiPrivIndex.at(value);
547 }
548 catch (const std::out_of_range& e)
549 {
George Liu82844ef2024-07-17 17:03:56 +0800550 lg2::error("Error in converting to system privilege: {PRIV}", "PRIV",
551 value);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530552 throw std::out_of_range("Out of range - convertToSystemPrivilege");
553 }
554}
555
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000556bool UserAccess::isValidUserName(const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530557{
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000558 if (userName.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530559 {
George Liu82844ef2024-07-17 17:03:56 +0800560 lg2::error("userName is empty");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530561 return false;
562 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530563 if (!std::regex_match(userName.c_str(),
nichanghao.nch0c96fdf2024-01-17 22:13:35 +0800564 std::regex("[a-zA-Z_][a-zA-Z_0-9]*")))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530565 {
George Liu82844ef2024-07-17 17:03:56 +0800566 lg2::error("Unsupported characters in user name");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530567 return false;
568 }
569 if (userName == "root")
570 {
George Liu82844ef2024-07-17 17:03:56 +0800571 lg2::error("Invalid user name - root");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530572 return false;
573 }
574 std::map<DbusUserObjPath, DbusUserObjValue> properties;
575 try
576 {
577 auto method = bus.new_method_call(getUserServiceName().c_str(),
578 userMgrObjBasePath, dBusObjManager,
579 getManagedObjectsMethod);
580 auto reply = bus.call(method);
581 reply.read(properties);
582 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500583 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530584 {
George Liu82844ef2024-07-17 17:03:56 +0800585 lg2::error("Failed to excute {METHOD}, path: {PATH}", "METHOD",
586 getManagedObjectsMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530587 return false;
588 }
589
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530590 sdbusplus::message::object_path tempUserPath(userObjBasePath);
591 tempUserPath /= userName;
592 std::string usersPath(tempUserPath);
593
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530594 if (properties.find(usersPath) != properties.end())
595 {
George Liu82844ef2024-07-17 17:03:56 +0800596 lg2::debug("Username {USER_NAME} already exists", "USER_NAME",
597 userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530598 return false;
599 }
600
601 return true;
602}
603
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530604/** @brief Information exchanged by pam module and application.
605 *
606 * @param[in] numMsg - length of the array of pointers,msg.
607 *
608 * @param[in] msg - pointer to an array of pointers to pam_message structure
609 *
610 * @param[out] resp - struct pam response array
611 *
612 * @param[in] appdataPtr - member of pam_conv structure
613 *
614 * @return the response in pam response structure.
615 */
616
617static int pamFunctionConversation(int numMsg, const struct pam_message** msg,
618 struct pam_response** resp, void* appdataPtr)
619{
620 if (appdataPtr == nullptr)
621 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530622 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530623 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530624
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530625 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
626 {
627 return PAM_CONV_ERR;
628 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530629
630 for (int i = 0; i < numMsg; ++i)
631 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530632 /* Ignore all PAM messages except prompting for hidden input */
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530633 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
634 {
635 continue;
636 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530637
638 /* Assume PAM is only prompting for the password as hidden input */
639 /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */
640
641 char* appPass = reinterpret_cast<char*>(appdataPtr);
642 size_t appPassSize = std::strlen(appPass);
643
644 if (appPassSize >= PAM_MAX_RESP_SIZE)
645 {
646 return PAM_CONV_ERR;
647 }
648
649 char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1));
650 if (pass == nullptr)
651 {
652 return PAM_BUF_ERR;
653 }
654
Patrick Williams1318a5e2024-08-16 15:19:54 -0400655 void* ptr =
656 calloc(static_cast<size_t>(numMsg), sizeof(struct pam_response));
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530657 if (ptr == nullptr)
658 {
659 free(pass);
660 return PAM_BUF_ERR;
661 }
662
663 std::strncpy(pass, appPass, appPassSize + 1);
664
665 *resp = reinterpret_cast<pam_response*>(ptr);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530666 resp[i]->resp = pass;
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530667
668 return PAM_SUCCESS;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530669 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530670
671 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530672}
673
674/** @brief Updating the PAM password
675 *
676 * @param[in] username - username in string
677 *
678 * @param[in] password - new password in string
679 *
680 * @return status
681 */
682
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000683int pamUpdatePasswd(const char* username, const char* password)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530684{
685 const struct pam_conv localConversation = {pamFunctionConversation,
686 const_cast<char*>(password)};
Jayanth Othayotha6fb32d2024-12-15 10:55:22 -0600687 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530688
Patrick Williams1318a5e2024-08-16 15:19:54 -0400689 int retval =
690 pam_start("passwd", username, &localConversation, &localAuthHandle);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530691
692 if (retval != PAM_SUCCESS)
693 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000694 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530695 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000696
697 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
698 if (retval != PAM_SUCCESS)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530699 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000700 pam_end(localAuthHandle, retval);
701 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530702 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000703
704 return pam_end(localAuthHandle, PAM_SUCCESS);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530705}
706
Ayushi Smriti02650d52019-05-15 11:59:09 +0000707bool pamUserCheckAuthenticate(std::string_view username,
708 std::string_view password)
709{
710 const struct pam_conv localConversation = {
711 pamFunctionConversation, const_cast<char*>(password.data())};
712
Jayanth Othayotha6fb32d2024-12-15 10:55:22 -0600713 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ayushi Smriti02650d52019-05-15 11:59:09 +0000714
715 if (pam_start("dropbear", username.data(), &localConversation,
716 &localAuthHandle) != PAM_SUCCESS)
717 {
George Liu82844ef2024-07-17 17:03:56 +0800718 lg2::error("User Authentication Failure");
Ayushi Smriti02650d52019-05-15 11:59:09 +0000719 return false;
720 }
721
722 int retval = pam_authenticate(localAuthHandle,
723 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
724
725 if (retval != PAM_SUCCESS)
726 {
George Liu82844ef2024-07-17 17:03:56 +0800727 lg2::debug("pam_authenticate returned failure: {ERROR}", "ERROR",
728 retval);
Ayushi Smriti02650d52019-05-15 11:59:09 +0000729
730 pam_end(localAuthHandle, retval);
731 return false;
732 }
733
734 if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
735 PAM_SUCCESS)
736 {
737 pam_end(localAuthHandle, PAM_SUCCESS);
738 return false;
739 }
740
741 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
742 {
743 return false;
744 }
745 return true;
746}
747
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000748Cc UserAccess::setSpecialUserPassword(const std::string& userName,
Vernon Mauery1e22a0f2021-07-30 13:36:54 -0700749 const SecureString& userPassword)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530750{
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000751 if (pamUpdatePasswd(userName.c_str(), userPassword.c_str()) != PAM_SUCCESS)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530752 {
George Liu82844ef2024-07-17 17:03:56 +0800753 lg2::debug("Failed to update password");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000754 return ccUnspecifiedError;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530755 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000756 return ccSuccess;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530757}
758
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000759Cc UserAccess::setUserPassword(const uint8_t userId, const char* userPassword)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530760{
761 std::string userName;
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000762 if (ipmiUserGetUserName(userId, userName) != ccSuccess)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530763 {
George Liu82844ef2024-07-17 17:03:56 +0800764 lg2::debug("User Name not found, user Id: {USER_ID}", "USER_ID",
765 userId);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000766 return ccParmOutOfRange;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530767 }
Snehalatha Venkatesh61024d72021-04-08 16:24:39 +0000768
769 ipmi::SecureString passwd;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530770 passwd.assign(reinterpret_cast<const char*>(userPassword), 0,
771 maxIpmi20PasswordSize);
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000772 int retval = pamUpdatePasswd(userName.c_str(), passwd.c_str());
773
774 switch (retval)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530775 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000776 case PAM_SUCCESS:
777 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000778 return ccSuccess;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000779 }
780 case PAM_AUTHTOK_ERR:
781 {
George Liu82844ef2024-07-17 17:03:56 +0800782 lg2::debug("Bad authentication token");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000783 return ccInvalidFieldRequest;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000784 }
785 default:
786 {
George Liu82844ef2024-07-17 17:03:56 +0800787 lg2::debug("Failed to update password, user Id: {USER_ID}",
788 "USER_ID", userId);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000789 return ccUnspecifiedError;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000790 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530791 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530792}
793
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000794Cc UserAccess::setUserEnabledState(const uint8_t userId,
795 const bool& enabledState)
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530796{
797 if (!isValidUserId(userId))
798 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000799 return ccParmOutOfRange;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530800 }
801 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
802 userLock{*userMutex};
803 UserInfo* userInfo = getUserInfo(userId);
804 std::string userName;
805 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
806 ipmiMaxUserName);
807 if (userName.empty())
808 {
George Liu82844ef2024-07-17 17:03:56 +0800809 lg2::debug("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000810 return ccUnspecifiedError;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530811 }
812 if (userInfo->userEnabled != enabledState)
813 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530814 sdbusplus::message::object_path tempUserPath(userObjBasePath);
815 tempUserPath /= userName;
816 std::string userPath(tempUserPath);
Patrick Venture99d1ba02019-02-21 15:11:24 -0800817 setDbusProperty(bus, getUserServiceName(), userPath, usersInterface,
818 userEnabledProperty, enabledState);
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530819 userInfo->userEnabled = enabledState;
820 try
821 {
822 writeUserData();
823 }
824 catch (const std::exception& e)
825 {
George Liu82844ef2024-07-17 17:03:56 +0800826 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000827 return ccUnspecifiedError;
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530828 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530829 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000830 return ccSuccess;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530831}
832
Patrick Williams1318a5e2024-08-16 15:19:54 -0400833Cc UserAccess::setUserPayloadAccess(
834 const uint8_t chNum, const uint8_t operation, const uint8_t userId,
835 const PayloadAccess& payloadAccess)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000836{
837 constexpr uint8_t enable = 0x0;
838 constexpr uint8_t disable = 0x1;
839
840 if (!isValidChannel(chNum))
841 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000842 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000843 }
844 if (!isValidUserId(userId))
845 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000846 return ccParmOutOfRange;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000847 }
848 if (operation != enable && operation != disable)
849 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000850 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000851 }
852 // Check operation & payloadAccess if required.
853 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
854 userLock{*userMutex};
855 UserInfo* userInfo = getUserInfo(userId);
856
857 if (operation == enable)
858 {
859 userInfo->payloadAccess[chNum].stdPayloadEnables1 |=
860 payloadAccess.stdPayloadEnables1;
861
862 userInfo->payloadAccess[chNum].oemPayloadEnables1 |=
863 payloadAccess.oemPayloadEnables1;
864 }
865 else
866 {
867 userInfo->payloadAccess[chNum].stdPayloadEnables1 &=
868 ~(payloadAccess.stdPayloadEnables1);
869
870 userInfo->payloadAccess[chNum].oemPayloadEnables1 &=
871 ~(payloadAccess.oemPayloadEnables1);
872 }
873
874 try
875 {
876 writeUserData();
877 }
878 catch (const std::exception& e)
879 {
George Liu82844ef2024-07-17 17:03:56 +0800880 lg2::error("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000881 return ccUnspecifiedError;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000882 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000883 return ccSuccess;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000884}
885
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000886Cc UserAccess::setUserPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
887 const UserPrivAccess& privAccess,
888 const bool& otherPrivUpdates)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530889{
890 if (!isValidChannel(chNum))
891 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000892 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530893 }
894 if (!isValidUserId(userId))
895 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000896 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530897 }
898 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
899 userLock{*userMutex};
900 UserInfo* userInfo = getUserInfo(userId);
901 std::string userName;
902 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
903 ipmiMaxUserName);
904 if (userName.empty())
905 {
George Liu82844ef2024-07-17 17:03:56 +0800906 lg2::debug("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000907 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530908 }
909 std::string priv = convertToSystemPrivilege(
910 static_cast<CommandPrivilege>(privAccess.privilege));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530911 uint8_t syncIndex = getUsrMgmtSyncIndex();
912 if (chNum == syncIndex &&
913 privAccess.privilege != userInfo->userPrivAccess[syncIndex].privilege)
914 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530915 sdbusplus::message::object_path tempUserPath(userObjBasePath);
916 tempUserPath /= userName;
917 std::string userPath(tempUserPath);
Patrick Venture99d1ba02019-02-21 15:11:24 -0800918 setDbusProperty(bus, getUserServiceName(), userPath, usersInterface,
919 userPrivProperty, priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530920 }
921 userInfo->userPrivAccess[chNum].privilege = privAccess.privilege;
922
923 if (otherPrivUpdates)
924 {
925 userInfo->userPrivAccess[chNum].ipmiEnabled = privAccess.ipmiEnabled;
926 userInfo->userPrivAccess[chNum].linkAuthEnabled =
927 privAccess.linkAuthEnabled;
928 userInfo->userPrivAccess[chNum].accessCallback =
929 privAccess.accessCallback;
930 }
931 try
932 {
933 writeUserData();
934 }
935 catch (const std::exception& e)
936 {
George Liu82844ef2024-07-17 17:03:56 +0800937 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000938 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530939 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000940 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530941}
942
943uint8_t UserAccess::getUserId(const std::string& userName)
944{
945 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
946 userLock{*userMutex};
947 checkAndReloadUserData();
948 // user index 0 is reserved, starts with 1
949 size_t usrIndex = 1;
950 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
951 {
952 std::string curName(
953 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
954 ipmiMaxUserName);
955 if (userName == curName)
956 {
957 break; // found the entry
958 }
959 }
960 if (usrIndex > ipmiMaxUsers)
961 {
George Liu82844ef2024-07-17 17:03:56 +0800962 lg2::debug("Username {USER_NAME} not found", "USER_NAME", userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530963 return invalidUserId;
964 }
965
966 return usrIndex;
967}
968
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000969Cc UserAccess::getUserName(const uint8_t userId, std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530970{
971 if (!isValidUserId(userId))
972 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000973 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530974 }
975 UserInfo* userInfo = getUserInfo(userId);
976 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
977 ipmiMaxUserName);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000978 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530979}
980
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530981bool UserAccess::isIpmiInAvailableGroupList()
982{
983 if (std::find(availableGroups.begin(), availableGroups.end(),
984 ipmiGrpName) != availableGroups.end())
985 {
986 return true;
987 }
988 if (availableGroups.empty())
989 {
990 // available groups shouldn't be empty, re-query
991 getSystemPrivAndGroups();
992 if (std::find(availableGroups.begin(), availableGroups.end(),
993 ipmiGrpName) != availableGroups.end())
994 {
995 return true;
996 }
997 }
998 return false;
999}
1000
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001001Cc UserAccess::setUserName(const uint8_t userId, const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301002{
1003 if (!isValidUserId(userId))
1004 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001005 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301006 }
1007
1008 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1009 userLock{*userMutex};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301010 std::string oldUser;
1011 getUserName(userId, oldUser);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301012
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001013 if (oldUser == userName)
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301014 {
1015 // requesting to set the same user name, return success.
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001016 return ccSuccess;
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301017 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001018
1019 bool validUser = isValidUserName(userName);
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301020 UserInfo* userInfo = getUserInfo(userId);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001021 if (userName.empty() && !oldUser.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301022 {
1023 // Delete existing user
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301024 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1025 tempUserPath /= oldUser;
1026 std::string userPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301027 try
1028 {
1029 auto method = bus.new_method_call(
1030 getUserServiceName().c_str(), userPath.c_str(),
1031 deleteUserInterface, deleteUserMethod);
1032 auto reply = bus.call(method);
1033 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001034 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301035 {
George Liu82844ef2024-07-17 17:03:56 +08001036 lg2::debug("Failed to excute {METHOD}, path:{PATH}", "METHOD",
1037 deleteUserMethod, "PATH", userPath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001038 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301039 }
Richard Marian Thomaiyar02710bb2018-11-28 20:42:25 +05301040 deleteUserIndex(userId);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301041 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001042 else if (oldUser.empty() && !userName.empty() && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301043 {
1044 try
1045 {
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301046 if (!isIpmiInAvailableGroupList())
1047 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001048 return ccUnspecifiedError;
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301049 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301050 // Create new user
1051 auto method = bus.new_method_call(
1052 getUserServiceName().c_str(), userMgrObjBasePath,
1053 userMgrInterface, createUserMethod);
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001054 method.append(userName.c_str(), availableGroups,
1055 ipmiPrivIndex[PRIVILEGE_USER], false);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301056 auto reply = bus.call(method);
1057 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001058 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301059 {
George Liu82844ef2024-07-17 17:03:56 +08001060 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1061 createUserMethod, "PATH", userMgrObjBasePath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001062 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301063 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001064
1065 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1066 std::memcpy(userInfo->userName,
1067 static_cast<const void*>(userName.data()), userName.size());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301068 userInfo->userInSystem = true;
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001069 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1070 {
1071 userInfo->userPrivAccess[chIndex].privilege =
1072 static_cast<uint8_t>(PRIVILEGE_USER);
1073 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301074 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001075 else if (oldUser != userName && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301076 {
1077 try
1078 {
1079 // User rename
1080 auto method = bus.new_method_call(
1081 getUserServiceName().c_str(), userMgrObjBasePath,
1082 userMgrInterface, renameUserMethod);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001083 method.append(oldUser.c_str(), userName.c_str());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301084 auto reply = bus.call(method);
1085 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001086 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301087 {
George Liu82844ef2024-07-17 17:03:56 +08001088 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1089 renameUserMethod, "PATH", userMgrObjBasePath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001090 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301091 }
1092 std::fill(static_cast<uint8_t*>(userInfo->userName),
1093 static_cast<uint8_t*>(userInfo->userName) +
1094 sizeof(userInfo->userName),
1095 0);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001096
1097 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1098 std::memcpy(userInfo->userName,
1099 static_cast<const void*>(userName.data()), userName.size());
1100
1101 ipmiRenameUserEntryPassword(oldUser, userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301102 userInfo->userInSystem = true;
1103 }
1104 else if (!validUser)
1105 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001106 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301107 }
1108 try
1109 {
1110 writeUserData();
1111 }
1112 catch (const std::exception& e)
1113 {
George Liu82844ef2024-07-17 17:03:56 +08001114 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001115 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301116 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001117 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301118}
1119
1120static constexpr const char* jsonUserName = "user_name";
1121static constexpr const char* jsonPriv = "privilege";
1122static constexpr const char* jsonIpmiEnabled = "ipmi_enabled";
1123static constexpr const char* jsonLinkAuthEnabled = "link_auth_enabled";
1124static constexpr const char* jsonAccCallbk = "access_callback";
1125static constexpr const char* jsonUserEnabled = "user_enabled";
1126static constexpr const char* jsonUserInSys = "user_in_system";
1127static constexpr const char* jsonFixedUser = "fixed_user_name";
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001128static constexpr const char* payloadEnabledStr = "payload_enabled";
1129static constexpr const char* stdPayloadStr = "std_payload";
1130static constexpr const char* oemPayloadStr = "OEM_payload";
1131
1132/** @brief to construct a JSON object from the given payload access details.
1133 *
1134 * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. (input)
1135 * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. (input)
1136 *
1137 * @details Sample output JSON object format :
1138 * "payload_enabled":{
1139 * "OEM_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1140 * "OEM_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1141 * "OEM_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1142 * "OEM_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1143 * "OEM_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1144 * "OEM_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1145 * "OEM_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1146 * "OEM_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1147 * "std_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1148 * "std_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1149 * "std_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1150 * "std_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1151 * "std_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1152 * "std_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1153 * "std_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1154 * "std_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1155 * }
1156 */
1157static const Json constructJsonPayloadEnables(
1158 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1159 stdPayload,
1160 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1161 oemPayload)
1162{
1163 Json jsonPayloadEnabled;
1164
1165 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1166 {
1167 std::ostringstream stdPayloadStream;
1168 std::ostringstream oemPayloadStream;
1169
1170 stdPayloadStream << stdPayloadStr << payloadNum;
1171 oemPayloadStream << oemPayloadStr << payloadNum;
1172
1173 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1174 stdPayloadStream.str(), stdPayload[payloadNum]));
1175
1176 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1177 oemPayloadStream.str(), oemPayload[payloadNum]));
1178 }
1179 return jsonPayloadEnabled;
1180}
1181
1182void UserAccess::readPayloadAccessFromUserInfo(
1183 const UserInfo& userInfo,
1184 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& stdPayload,
1185 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& oemPayload)
1186{
1187 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1188 {
1189 for (auto chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1190 {
1191 stdPayload[payloadNum][chIndex] =
1192 userInfo.payloadAccess[chIndex].stdPayloadEnables1[payloadNum];
1193
1194 oemPayload[payloadNum][chIndex] =
1195 userInfo.payloadAccess[chIndex].oemPayloadEnables1[payloadNum];
1196 }
1197 }
1198}
1199
1200void UserAccess::updatePayloadAccessInUserInfo(
1201 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1202 stdPayload,
Willy Tu11d68892022-01-20 10:37:34 -08001203 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&,
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001204 UserInfo& userInfo)
1205{
1206 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1207 {
1208 // Ensure that reserved/unsupported payloads are marked to zero.
1209 userInfo.payloadAccess[chIndex].stdPayloadEnables1.reset();
1210 userInfo.payloadAccess[chIndex].oemPayloadEnables1.reset();
1211 userInfo.payloadAccess[chIndex].stdPayloadEnables2Reserved.reset();
1212 userInfo.payloadAccess[chIndex].oemPayloadEnables2Reserved.reset();
1213 // Update SOL status as it is the only supported payload currently.
1214 userInfo.payloadAccess[chIndex]
1215 .stdPayloadEnables1[static_cast<uint8_t>(ipmi::PayloadType::SOL)] =
1216 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)][chIndex];
1217 }
1218}
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301219
1220void UserAccess::readUserData()
1221{
1222 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1223 userLock{*userMutex};
1224
1225 std::ifstream iUsrData(ipmiUserDataFile, std::ios::in | std::ios::binary);
1226 if (!iUsrData.good())
1227 {
George Liu82844ef2024-07-17 17:03:56 +08001228 lg2::error("Error in reading IPMI user data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301229 throw std::ios_base::failure("Error opening IPMI user data file");
1230 }
1231
1232 Json jsonUsersTbl = Json::array();
1233 jsonUsersTbl = Json::parse(iUsrData, nullptr, false);
1234
1235 if (jsonUsersTbl.size() != ipmiMaxUsers)
1236 {
George Liu82844ef2024-07-17 17:03:56 +08001237 lg2::error("Error in reading IPMI user data file - User count issues");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301238 throw std::runtime_error(
1239 "Corrupted IPMI user data file - invalid user count");
1240 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001241
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301242 // user index 0 is reserved, starts with 1
1243 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1244 {
1245 Json userInfo = jsonUsersTbl[usrIndex - 1]; // json array starts with 0.
1246 if (userInfo.is_null())
1247 {
George Liu82844ef2024-07-17 17:03:56 +08001248 lg2::error("Error in reading IPMI user data file - "
1249 "user info corrupted");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301250 throw std::runtime_error(
1251 "Corrupted IPMI user data file - invalid user info");
1252 }
1253 std::string userName = userInfo[jsonUserName].get<std::string>();
1254 std::strncpy(reinterpret_cast<char*>(usersTbl.user[usrIndex].userName),
1255 userName.c_str(), ipmiMaxUserName);
1256
1257 std::vector<std::string> privilege =
1258 userInfo[jsonPriv].get<std::vector<std::string>>();
1259 std::vector<bool> ipmiEnabled =
1260 userInfo[jsonIpmiEnabled].get<std::vector<bool>>();
1261 std::vector<bool> linkAuthEnabled =
1262 userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
1263 std::vector<bool> accessCallback =
1264 userInfo[jsonAccCallbk].get<std::vector<bool>>();
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001265
1266 // Payload Enables Processing.
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001267 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1268 stdPayload = {};
1269 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1270 oemPayload = {};
1271 try
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001272 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001273 const auto jsonPayloadEnabled = userInfo.at(payloadEnabledStr);
1274 for (auto payloadNum = 0; payloadNum < payloadsPerByte;
1275 payloadNum++)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001276 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001277 std::ostringstream stdPayloadStream;
1278 std::ostringstream oemPayloadStream;
1279
1280 stdPayloadStream << stdPayloadStr << payloadNum;
1281 oemPayloadStream << oemPayloadStr << payloadNum;
1282
1283 stdPayload[payloadNum] =
1284 jsonPayloadEnabled[stdPayloadStream.str()]
1285 .get<std::array<bool, ipmiMaxChannels>>();
1286 oemPayload[payloadNum] =
1287 jsonPayloadEnabled[oemPayloadStream.str()]
1288 .get<std::array<bool, ipmiMaxChannels>>();
1289
1290 if (stdPayload[payloadNum].size() != ipmiMaxChannels ||
1291 oemPayload[payloadNum].size() != ipmiMaxChannels)
1292 {
George Liu82844ef2024-07-17 17:03:56 +08001293 lg2::error("Error in reading IPMI user data file - "
1294 "payload properties corrupted");
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001295 throw std::runtime_error(
1296 "Corrupted IPMI user data file - payload properties");
1297 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001298 }
1299 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001300 catch (const Json::out_of_range& e)
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001301 {
1302 // Key not found in 'userInfo'; possibly an old JSON file. Use
1303 // default values for all payloads, and SOL payload default is true.
1304 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)].fill(true);
1305 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001306
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301307 if (privilege.size() != ipmiMaxChannels ||
1308 ipmiEnabled.size() != ipmiMaxChannels ||
1309 linkAuthEnabled.size() != ipmiMaxChannels ||
1310 accessCallback.size() != ipmiMaxChannels)
1311 {
George Liu82844ef2024-07-17 17:03:56 +08001312 lg2::error("Error in reading IPMI user data file - "
1313 "properties corrupted");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301314 throw std::runtime_error(
1315 "Corrupted IPMI user data file - properties");
1316 }
1317 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1318 {
1319 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege =
1320 static_cast<uint8_t>(
1321 convertToIPMIPrivilege(privilege[chIndex]));
1322 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled =
1323 ipmiEnabled[chIndex];
1324 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled =
1325 linkAuthEnabled[chIndex];
1326 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
1327 accessCallback[chIndex];
1328 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001329 updatePayloadAccessInUserInfo(stdPayload, oemPayload,
1330 usersTbl.user[usrIndex]);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301331 usersTbl.user[usrIndex].userEnabled =
1332 userInfo[jsonUserEnabled].get<bool>();
1333 usersTbl.user[usrIndex].userInSystem =
1334 userInfo[jsonUserInSys].get<bool>();
1335 usersTbl.user[usrIndex].fixedUserName =
1336 userInfo[jsonFixedUser].get<bool>();
1337 }
1338
George Liu82844ef2024-07-17 17:03:56 +08001339 lg2::debug("User data read from IPMI data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301340 iUsrData.close();
1341 // Update the timestamp
1342 fileLastUpdatedTime = getUpdatedFileTime();
1343 return;
1344}
1345
1346void UserAccess::writeUserData()
1347{
1348 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1349 userLock{*userMutex};
1350
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301351 Json jsonUsersTbl = Json::array();
1352 // user index 0 is reserved, starts with 1
1353 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1354 {
1355 Json jsonUserInfo;
1356 jsonUserInfo[jsonUserName] = std::string(
1357 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
1358 ipmiMaxUserName);
1359 std::vector<std::string> privilege(ipmiMaxChannels);
1360 std::vector<bool> ipmiEnabled(ipmiMaxChannels);
1361 std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
1362 std::vector<bool> accessCallback(ipmiMaxChannels);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001363
1364 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1365 stdPayload;
1366 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1367 oemPayload;
1368
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301369 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1370 {
1371 privilege[chIndex] =
1372 convertToSystemPrivilege(static_cast<CommandPrivilege>(
1373 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege));
1374 ipmiEnabled[chIndex] =
1375 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled;
1376 linkAuthEnabled[chIndex] =
1377 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled;
1378 accessCallback[chIndex] =
1379 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback;
1380 }
1381 jsonUserInfo[jsonPriv] = privilege;
1382 jsonUserInfo[jsonIpmiEnabled] = ipmiEnabled;
1383 jsonUserInfo[jsonLinkAuthEnabled] = linkAuthEnabled;
1384 jsonUserInfo[jsonAccCallbk] = accessCallback;
1385 jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
1386 jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
1387 jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001388
1389 readPayloadAccessFromUserInfo(usersTbl.user[usrIndex], stdPayload,
1390 oemPayload);
Patrick Williams1318a5e2024-08-16 15:19:54 -04001391 Json jsonPayloadEnabledInfo =
1392 constructJsonPayloadEnables(stdPayload, oemPayload);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001393 jsonUserInfo[payloadEnabledStr] = jsonPayloadEnabledInfo;
1394
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301395 jsonUsersTbl.push_back(jsonUserInfo);
1396 }
1397
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301398 static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
1399 int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
1400 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1401 if (fd < 0)
1402 {
George Liu82844ef2024-07-17 17:03:56 +08001403 lg2::error("Error in creating temporary IPMI user data file");
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301404 throw std::ios_base::failure(
1405 "Error in creating temporary IPMI user data file");
1406 }
1407 const auto& writeStr = jsonUsersTbl.dump();
1408 if (write(fd, writeStr.c_str(), writeStr.size()) !=
1409 static_cast<ssize_t>(writeStr.size()))
1410 {
1411 close(fd);
George Liu82844ef2024-07-17 17:03:56 +08001412 lg2::error("Error in writing temporary IPMI user data file");
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301413 throw std::ios_base::failure(
1414 "Error in writing temporary IPMI user data file");
1415 }
1416 close(fd);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301417
1418 if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
1419 {
George Liu82844ef2024-07-17 17:03:56 +08001420 lg2::error("Error in renaming temporary IPMI user data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301421 throw std::runtime_error("Error in renaming IPMI user data file");
1422 }
1423 // Update the timestamp
1424 fileLastUpdatedTime = getUpdatedFileTime();
1425 return;
1426}
1427
1428bool UserAccess::addUserEntry(const std::string& userName,
1429 const std::string& sysPriv, const bool& enabled)
1430{
1431 UsersTbl* userData = getUsersTblPtr();
1432 size_t freeIndex = 0xFF;
1433 // user index 0 is reserved, starts with 1
1434 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1435 {
1436 std::string curName(
1437 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
1438 ipmiMaxUserName);
1439 if (userName == curName)
1440 {
George Liu82844ef2024-07-17 17:03:56 +08001441 lg2::debug("Username {USER_NAME} exists", "USER_NAME", userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301442 return false; // user name exists.
1443 }
1444
1445 if ((!userData->user[usrIndex].userInSystem) &&
1446 (userData->user[usrIndex].userName[0] == '\0') &&
1447 (freeIndex == 0xFF))
1448 {
1449 freeIndex = usrIndex;
1450 }
1451 }
1452 if (freeIndex == 0xFF)
1453 {
George Liu82844ef2024-07-17 17:03:56 +08001454 lg2::error("No empty slots found");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301455 return false;
1456 }
1457 std::strncpy(reinterpret_cast<char*>(userData->user[freeIndex].userName),
1458 userName.c_str(), ipmiMaxUserName);
1459 uint8_t priv =
1460 static_cast<uint8_t>(UserAccess::convertToIPMIPrivilege(sysPriv)) &
1461 privMask;
1462 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1463 {
1464 userData->user[freeIndex].userPrivAccess[chIndex].privilege = priv;
1465 userData->user[freeIndex].userPrivAccess[chIndex].ipmiEnabled = true;
1466 userData->user[freeIndex].userPrivAccess[chIndex].linkAuthEnabled =
1467 true;
1468 userData->user[freeIndex].userPrivAccess[chIndex].accessCallback = true;
1469 }
1470 userData->user[freeIndex].userInSystem = true;
1471 userData->user[freeIndex].userEnabled = enabled;
1472
1473 return true;
1474}
1475
1476void UserAccess::deleteUserIndex(const size_t& usrIdx)
1477{
1478 UsersTbl* userData = getUsersTblPtr();
1479
1480 std::string userName(
1481 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1482 ipmiMaxUserName);
1483 ipmiClearUserEntryPassword(userName);
1484 std::fill(static_cast<uint8_t*>(userData->user[usrIdx].userName),
1485 static_cast<uint8_t*>(userData->user[usrIdx].userName) +
1486 sizeof(userData->user[usrIdx].userName),
1487 0);
1488 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1489 {
1490 userData->user[usrIdx].userPrivAccess[chIndex].privilege = privNoAccess;
1491 userData->user[usrIdx].userPrivAccess[chIndex].ipmiEnabled = false;
1492 userData->user[usrIdx].userPrivAccess[chIndex].linkAuthEnabled = false;
1493 userData->user[usrIdx].userPrivAccess[chIndex].accessCallback = false;
1494 }
1495 userData->user[usrIdx].userInSystem = false;
1496 userData->user[usrIdx].userEnabled = false;
1497 return;
1498}
1499
1500void UserAccess::checkAndReloadUserData()
1501{
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001502 std::timespec updateTime = getUpdatedFileTime();
1503 if ((updateTime.tv_sec != fileLastUpdatedTime.tv_sec ||
1504 updateTime.tv_nsec != fileLastUpdatedTime.tv_nsec) ||
1505 (updateTime.tv_sec == 0 && updateTime.tv_nsec == 0))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301506 {
1507 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1508 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1509 readUserData();
1510 }
1511 return;
1512}
1513
1514UsersTbl* UserAccess::getUsersTblPtr()
1515{
1516 // reload data before using it.
1517 checkAndReloadUserData();
1518 return &usersTbl;
1519}
1520
1521void UserAccess::getSystemPrivAndGroups()
1522{
1523 std::map<std::string, PrivAndGroupType> properties;
1524 try
1525 {
1526 auto method = bus.new_method_call(
1527 getUserServiceName().c_str(), userMgrObjBasePath,
1528 dBusPropertiesInterface, getAllPropertiesMethod);
1529 method.append(userMgrInterface);
1530
1531 auto reply = bus.call(method);
1532 reply.read(properties);
1533 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001534 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301535 {
George Liu82844ef2024-07-17 17:03:56 +08001536 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1537 getAllPropertiesMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301538 return;
1539 }
1540 for (const auto& t : properties)
1541 {
1542 auto key = t.first;
1543 if (key == allPrivProperty)
1544 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001545 availablePrivileges = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301546 }
1547 else if (key == allGrpProperty)
1548 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001549 availableGroups = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301550 }
1551 }
1552 // TODO: Implement Supported Privilege & Groups verification logic
1553 return;
1554}
1555
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001556std::timespec UserAccess::getUpdatedFileTime()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301557{
1558 struct stat fileStat;
1559 if (stat(ipmiUserDataFile, &fileStat) != 0)
1560 {
George Liu82844ef2024-07-17 17:03:56 +08001561 lg2::debug("Error in getting last updated time stamp");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001562 return std::timespec{0, 0};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301563 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001564 return fileStat.st_mtim;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301565}
1566
1567void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
1568 std::vector<std::string>& usrGrps,
1569 std::string& usrPriv, bool& usrEnabled)
1570{
1571 for (const auto& t : properties)
1572 {
1573 std::string key = t.first;
1574 if (key == userPrivProperty)
1575 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001576 usrPriv = std::get<std::string>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301577 }
1578 else if (key == userGrpProperty)
1579 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001580 usrGrps = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301581 }
1582 else if (key == userEnabledProperty)
1583 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001584 usrEnabled = std::get<bool>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301585 }
1586 }
1587 return;
1588}
1589
1590int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
1591 std::vector<std::string>& usrGrps,
1592 std::string& usrPriv, bool& usrEnabled)
1593{
1594 auto usrObj = userObjs.find(usersInterface);
1595 if (usrObj != userObjs.end())
1596 {
1597 getUserProperties(usrObj->second, usrGrps, usrPriv, usrEnabled);
1598 return 0;
1599 }
1600 return -EIO;
1601}
1602
arun-pmbbe728c2020-01-10 15:18:04 +05301603void UserAccess::cacheUserDataFile()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301604{
1605 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1606 userLock{*userMutex};
1607 try
1608 {
1609 readUserData();
1610 }
1611 catch (const std::ios_base::failure& e)
1612 { // File is empty, create it for the first time
1613 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1614 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1615 // user index 0 is reserved, starts with 1
1616 for (size_t userIndex = 1; userIndex <= ipmiMaxUsers; ++userIndex)
1617 {
1618 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1619 {
1620 usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
1621 privNoAccess;
Saravanan Palanisamy92d81192019-08-07 18:00:04 +00001622 usersTbl.user[userIndex]
1623 .payloadAccess[chIndex]
1624 .stdPayloadEnables1[static_cast<uint8_t>(
1625 ipmi::PayloadType::SOL)] = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301626 }
1627 }
1628 writeUserData();
1629 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001630 // Create lock file if it does not exist
1631 int fd = open(ipmiUserSignalLockFile, O_CREAT | O_TRUNC | O_SYNC,
1632 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1633 if (fd < 0)
1634 {
George Liu82844ef2024-07-17 17:03:56 +08001635 lg2::error("Error in creating IPMI user signal lock file");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001636 throw std::ios_base::failure(
1637 "Error in creating temporary IPMI user signal lock file");
1638 }
1639 close(fd);
1640
1641 sigHndlrLock = boost::interprocess::file_lock(ipmiUserSignalLockFile);
George Liu1a2e1502022-07-08 12:20:19 +08001642 // Register it for single object and single process either netipmid /
arun-pmbbe728c2020-01-10 15:18:04 +05301643 // host-ipmid
1644 if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
1645 {
George Liu82844ef2024-07-17 17:03:56 +08001646 lg2::debug("Registering signal handler");
arun-pmbbe728c2020-01-10 15:18:04 +05301647 userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
1648 bus,
1649 sdbusplus::bus::match::rules::type::signal() +
1650 sdbusplus::bus::match::rules::interface(dBusObjManager) +
1651 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001652 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001653 userUpdatedSignalHandler(*this, msg);
1654 });
arun-pmbbe728c2020-01-10 15:18:04 +05301655 userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
1656 bus,
1657 sdbusplus::bus::match::rules::type::signal() +
1658 sdbusplus::bus::match::rules::interface(userMgrInterface) +
1659 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001660 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001661 userUpdatedSignalHandler(*this, msg);
1662 });
arun-pmbbe728c2020-01-10 15:18:04 +05301663 userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
1664 bus,
1665 sdbusplus::bus::match::rules::type::signal() +
1666 sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
1667 sdbusplus::bus::match::rules::interface(
1668 dBusPropertiesInterface) +
1669 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
1670 sdbusplus::bus::match::rules::argN(0, usersInterface),
Patrick Williams5d82f472022-07-22 19:26:53 -05001671 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001672 userUpdatedSignalHandler(*this, msg);
1673 });
arun-pmbbe728c2020-01-10 15:18:04 +05301674 signalHndlrObject = true;
1675 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301676 std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
1677 try
1678 {
1679 auto method = bus.new_method_call(getUserServiceName().c_str(),
1680 userMgrObjBasePath, dBusObjManager,
1681 getManagedObjectsMethod);
1682 auto reply = bus.call(method);
1683 reply.read(managedObjs);
1684 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001685 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301686 {
George Liu82844ef2024-07-17 17:03:56 +08001687 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1688 getManagedObjectsMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301689 return;
1690 }
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301691 bool updateRequired = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301692 UsersTbl* userData = &usersTbl;
1693 // user index 0 is reserved, starts with 1
1694 for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
1695 {
1696 if ((userData->user[usrIdx].userInSystem) &&
1697 (userData->user[usrIdx].userName[0] != '\0'))
1698 {
1699 std::vector<std::string> usrGrps;
1700 std::string usrPriv;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301701
1702 std::string userName(
1703 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1704 ipmiMaxUserName);
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301705 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1706 tempUserPath /= userName;
1707 std::string usersPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301708
1709 auto usrObj = managedObjs.find(usersPath);
1710 if (usrObj != managedObjs.end())
1711 {
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001712 bool usrEnabled = false;
Patrick Venture3a697ad2019-08-19 11:12:05 -07001713
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301714 // User exist. Lets check and update other fileds
1715 getUserObjProperties(usrObj->second, usrGrps, usrPriv,
1716 usrEnabled);
1717 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
1718 usrGrps.end())
1719 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301720 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301721 // Group "ipmi" is removed so lets remove user in IPMI
1722 deleteUserIndex(usrIdx);
1723 }
1724 else
1725 {
1726 // Group "ipmi" is present so lets update other properties
1727 // in IPMI
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001728 uint8_t priv = UserAccess::convertToIPMIPrivilege(usrPriv) &
1729 privMask;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301730 // Update all channels priv, only if it is not equivalent to
1731 // getUsrMgmtSyncIndex()
1732 if (userData->user[usrIdx]
1733 .userPrivAccess[getUsrMgmtSyncIndex()]
1734 .privilege != priv)
1735 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301736 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301737 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
1738 ++chIndex)
1739 {
1740 userData->user[usrIdx]
1741 .userPrivAccess[chIndex]
1742 .privilege = priv;
1743 }
1744 }
1745 if (userData->user[usrIdx].userEnabled != usrEnabled)
1746 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301747 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301748 userData->user[usrIdx].userEnabled = usrEnabled;
1749 }
1750 }
1751
1752 // We are done with this obj. lets delete from MAP
1753 managedObjs.erase(usrObj);
1754 }
1755 else
1756 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301757 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301758 deleteUserIndex(usrIdx);
1759 }
1760 }
1761 }
1762
1763 // Walk through remnaining managedObj users list
1764 // Add them to ipmi data base
1765 for (const auto& usrObj : managedObjs)
1766 {
1767 std::vector<std::string> usrGrps;
1768 std::string usrPriv, userName;
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001769 bool usrEnabled = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301770 std::string usrObjPath = std::string(usrObj.first);
1771 if (getUserNameFromPath(usrObj.first.str, userName) != 0)
1772 {
George Liu82844ef2024-07-17 17:03:56 +08001773 lg2::error("Error in user object path");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301774 continue;
1775 }
1776 getUserObjProperties(usrObj.second, usrGrps, usrPriv, usrEnabled);
1777 // Add 'ipmi' group users
1778 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
1779 usrGrps.end())
1780 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301781 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301782 // CREATE NEW USER
1783 if (true != addUserEntry(userName, usrPriv, usrEnabled))
1784 {
1785 break;
1786 }
1787 }
1788 }
1789
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301790 if (updateRequired)
1791 {
1792 // All userData slots update done. Lets write the data
1793 writeUserData();
1794 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301795
1796 return;
1797}
1798} // namespace ipmi