blob: 979aaa651dfa762d181b7728458aed24d1cd50fb [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
18#include "apphandler.hpp"
Saravanan Palanisamy77381f12019-05-15 22:33:17 +000019#include "channel_layer.hpp"
Johnathan Manteyfd61fc32021-04-08 11:05:38 -070020#include "channel_mgmt.hpp"
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053021
Suryakanth Sekar90b00c72019-01-16 10:37:57 +053022#include <security/pam_appl.h>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053023#include <sys/stat.h>
24#include <unistd.h>
25
26#include <boost/interprocess/sync/named_recursive_mutex.hpp>
27#include <boost/interprocess/sync/scoped_lock.hpp>
Snehalatha Venkatesh745164c2021-06-25 10:02:25 +000028#include <ipmid/types.hpp>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053029#include <nlohmann/json.hpp>
30#include <phosphor-logging/elog-errors.hpp>
George Liu82844ef2024-07-17 17:03:56 +080031#include <phosphor-logging/lg2.hpp>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053032#include <sdbusplus/bus/match.hpp>
33#include <sdbusplus/server/object.hpp>
34#include <xyz/openbmc_project/Common/error.hpp>
35#include <xyz/openbmc_project/User/Common/error.hpp>
36
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050037#include <cerrno>
38#include <fstream>
39#include <regex>
40#include <variant>
41
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053042namespace ipmi
43{
44
45// TODO: Move D-Bus & Object Manager related stuff, to common files
46// D-Bus property related
47static constexpr const char* dBusPropertiesInterface =
48 "org.freedesktop.DBus.Properties";
49static constexpr const char* getAllPropertiesMethod = "GetAll";
50static constexpr const char* propertiesChangedSignal = "PropertiesChanged";
51static constexpr const char* setPropertiesMethod = "Set";
52
53// Object Manager related
54static constexpr const char* dBusObjManager =
55 "org.freedesktop.DBus.ObjectManager";
56static constexpr const char* getManagedObjectsMethod = "GetManagedObjects";
57// Object Manager signals
58static constexpr const char* intfAddedSignal = "InterfacesAdded";
59static constexpr const char* intfRemovedSignal = "InterfacesRemoved";
60
61// Object Mapper related
62static constexpr const char* objMapperService =
63 "xyz.openbmc_project.ObjectMapper";
64static constexpr const char* objMapperPath =
65 "/xyz/openbmc_project/object_mapper";
66static constexpr const char* objMapperInterface =
67 "xyz.openbmc_project.ObjectMapper";
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053068static constexpr const char* getObjectMethod = "GetObject";
69
70static constexpr const char* ipmiUserMutex = "ipmi_usr_mutex";
71static constexpr const char* ipmiMutexCleanupLockFile =
72 "/var/lib/ipmi/ipmi_usr_mutex_cleanup";
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +000073static constexpr const char* ipmiUserSignalLockFile =
74 "/var/lib/ipmi/ipmi_usr_signal_mutex";
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053075static constexpr const char* ipmiUserDataFile = "/var/lib/ipmi/ipmi_user.json";
76static constexpr const char* ipmiGrpName = "ipmi";
77static constexpr size_t privNoAccess = 0xF;
78static constexpr size_t privMask = 0xF;
79
80// User manager related
81static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user";
82static constexpr const char* userObjBasePath = "/xyz/openbmc_project/user";
83static constexpr const char* userMgrInterface =
84 "xyz.openbmc_project.User.Manager";
85static constexpr const char* usersInterface =
86 "xyz.openbmc_project.User.Attributes";
87static constexpr const char* deleteUserInterface =
88 "xyz.openbmc_project.Object.Delete";
89
90static constexpr const char* createUserMethod = "CreateUser";
91static constexpr const char* deleteUserMethod = "Delete";
92static constexpr const char* renameUserMethod = "RenameUser";
93// User manager signal memebers
94static constexpr const char* userRenamedSignal = "UserRenamed";
95// Mgr interface properties
96static constexpr const char* allPrivProperty = "AllPrivileges";
97static constexpr const char* allGrpProperty = "AllGroups";
98// User interface properties
99static constexpr const char* userPrivProperty = "UserPrivilege";
100static constexpr const char* userGrpProperty = "UserGroups";
101static constexpr const char* userEnabledProperty = "UserEnabled";
102
103static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = {
104 "priv-reserved", // PRIVILEGE_RESERVED - 0
105 "priv-callback", // PRIVILEGE_CALLBACK - 1
106 "priv-user", // PRIVILEGE_USER - 2
107 "priv-operator", // PRIVILEGE_OPERATOR - 3
108 "priv-admin", // PRIVILEGE_ADMIN - 4
109 "priv-custom" // PRIVILEGE_OEM - 5
110};
111
112using namespace phosphor::logging;
113using Json = nlohmann::json;
114
Vernon Mauery16b86932019-05-01 08:36:11 -0700115using PrivAndGroupType = std::variant<std::string, std::vector<std::string>>;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530116
117using NoResource =
Willy Tu523e2d12023-09-05 11:36:48 -0700118 sdbusplus::error::xyz::openbmc_project::user::common::NoResource;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530119
120using InternalFailure =
Willy Tu523e2d12023-09-05 11:36:48 -0700121 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530122
Lei YU4b0ddb62019-01-25 16:43:50 +0800123std::unique_ptr<sdbusplus::bus::match_t> userUpdatedSignal
124 __attribute__((init_priority(101)));
125std::unique_ptr<sdbusplus::bus::match_t> userMgrRenamedSignal
126 __attribute__((init_priority(101)));
127std::unique_ptr<sdbusplus::bus::match_t> userPropertiesSignal
128 __attribute__((init_priority(101)));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530129
130// TODO: Below code can be removed once it is moved to common layer libmiscutil
Patrick Williams5d82f472022-07-22 19:26:53 -0500131std::string getUserService(sdbusplus::bus_t& bus, const std::string& intf,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530132 const std::string& path)
133{
134 auto mapperCall = bus.new_method_call(objMapperService, objMapperPath,
135 objMapperInterface, getObjectMethod);
136
137 mapperCall.append(path);
138 mapperCall.append(std::vector<std::string>({intf}));
139
140 auto mapperResponseMsg = bus.call(mapperCall);
141
142 std::map<std::string, std::vector<std::string>> mapperResponse;
143 mapperResponseMsg.read(mapperResponse);
144
145 if (mapperResponse.begin() == mapperResponse.end())
146 {
147 throw sdbusplus::exception::SdBusError(
148 -EIO, "ERROR in reading the mapper response");
149 }
150
151 return mapperResponse.begin()->first;
152}
153
Patrick Williams5d82f472022-07-22 19:26:53 -0500154void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530155 const std::string& objPath, const std::string& interface,
156 const std::string& property,
157 const DbusUserPropVariant& value)
158{
159 try
160 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500161 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
162 dBusPropertiesInterface,
163 setPropertiesMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530164 method.append(interface, property, value);
165 bus.call(method);
166 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500167 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530168 {
George Liu82844ef2024-07-17 17:03:56 +0800169 lg2::error("Failed to set {PROPERTY}, path: {PATH}, "
170 "interface: {INTERFACE}",
171 "PROPERTY", property, "PATH", objPath, "INTERFACE",
172 interface);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530173 throw;
174 }
175}
176
Willy Tu523e2d12023-09-05 11:36:48 -0700177std::string getUserServiceName()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530178{
Patrick Williams5d82f472022-07-22 19:26:53 -0500179 static sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530180 static std::string userMgmtService;
181 if (userMgmtService.empty())
182 {
183 try
184 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500185 userMgmtService = ipmi::getUserService(bus, userMgrInterface,
186 userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530187 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500188 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530189 {
190 userMgmtService.clear();
191 }
192 }
193 return userMgmtService;
194}
195
196UserAccess& getUserAccessObject()
197{
198 static UserAccess userAccess;
199 return userAccess;
200}
201
202int getUserNameFromPath(const std::string& path, std::string& userName)
203{
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530204 sdbusplus::message::object_path objPath(path);
205 userName.assign(objPath.filename());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530206 return 0;
207}
208
209void userUpdateHelper(UserAccess& usrAccess, const UserUpdateEvent& userEvent,
210 const std::string& userName, const std::string& priv,
211 const bool& enabled, const std::string& newUserName)
212{
213 UsersTbl* userData = usrAccess.getUsersTblPtr();
214 if (userEvent == UserUpdateEvent::userCreated)
215 {
216 if (usrAccess.addUserEntry(userName, priv, enabled) == false)
217 {
218 return;
219 }
220 }
221 else
222 {
223 // user index 0 is reserved, starts with 1
224 size_t usrIndex = 1;
225 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
226 {
227 std::string curName(
228 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
229 ipmiMaxUserName);
230 if (userName == curName)
231 {
232 break; // found the entry
233 }
234 }
235 if (usrIndex > ipmiMaxUsers)
236 {
George Liu82844ef2024-07-17 17:03:56 +0800237 lg2::debug("User not found for signal, user name: {USER_NAME}, "
238 "user event: {USER_EVENT}",
239 "USER_NAME", userName, "USER_EVENT", userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530240 return;
241 }
242 switch (userEvent)
243 {
244 case UserUpdateEvent::userDeleted:
245 {
246 usrAccess.deleteUserIndex(usrIndex);
247 break;
248 }
249 case UserUpdateEvent::userPrivUpdated:
250 {
251 uint8_t userPriv =
252 static_cast<uint8_t>(
253 UserAccess::convertToIPMIPrivilege(priv)) &
254 privMask;
255 // Update all channels privileges, only if it is not equivalent
256 // to getUsrMgmtSyncIndex()
257 if (userData->user[usrIndex]
258 .userPrivAccess[UserAccess::getUsrMgmtSyncIndex()]
259 .privilege != userPriv)
260 {
261 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
262 ++chIndex)
263 {
264 userData->user[usrIndex]
265 .userPrivAccess[chIndex]
266 .privilege = userPriv;
267 }
268 }
269 break;
270 }
271 case UserUpdateEvent::userRenamed:
272 {
273 std::fill(
274 static_cast<uint8_t*>(userData->user[usrIndex].userName),
275 static_cast<uint8_t*>(userData->user[usrIndex].userName) +
276 sizeof(userData->user[usrIndex].userName),
277 0);
278 std::strncpy(
279 reinterpret_cast<char*>(userData->user[usrIndex].userName),
280 newUserName.c_str(), ipmiMaxUserName);
281 ipmiRenameUserEntryPassword(userName, newUserName);
282 break;
283 }
284 case UserUpdateEvent::userStateUpdated:
285 {
286 userData->user[usrIndex].userEnabled = enabled;
287 break;
288 }
289 default:
290 {
George Liu82844ef2024-07-17 17:03:56 +0800291 lg2::error("Unhandled user event: {USER_EVENT}", "USER_EVENT",
292 userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530293 return;
294 }
295 }
296 }
297 usrAccess.writeUserData();
George Liu82844ef2024-07-17 17:03:56 +0800298 lg2::debug("User event handled successfully, user name: {USER_NAME}, "
299 "user event: {USER_EVENT}",
300 "USER_NAME", userName.c_str(), "USER_EVENT", userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530301
302 return;
303}
304
Patrick Williams5d82f472022-07-22 19:26:53 -0500305void userUpdatedSignalHandler(UserAccess& usrAccess, sdbusplus::message_t& msg)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530306{
Patrick Williams5d82f472022-07-22 19:26:53 -0500307 static sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530308 std::string signal = msg.get_member();
Patrick Venture3a697ad2019-08-19 11:12:05 -0700309 std::string userName, priv, newUserName;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530310 std::vector<std::string> groups;
311 bool enabled = false;
312 UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent;
313 if (signal == intfAddedSignal)
314 {
315 DbusUserObjPath objPath;
316 DbusUserObjValue objValue;
317 msg.read(objPath, objValue);
318 getUserNameFromPath(objPath.str, userName);
319 if (usrAccess.getUserObjProperties(objValue, groups, priv, enabled) !=
320 0)
321 {
322 return;
323 }
324 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
325 groups.end())
326 {
327 return;
328 }
329 userEvent = UserUpdateEvent::userCreated;
330 }
331 else if (signal == intfRemovedSignal)
332 {
333 DbusUserObjPath objPath;
334 std::vector<std::string> interfaces;
335 msg.read(objPath, interfaces);
336 getUserNameFromPath(objPath.str, userName);
337 userEvent = UserUpdateEvent::userDeleted;
338 }
339 else if (signal == userRenamedSignal)
340 {
341 msg.read(userName, newUserName);
342 userEvent = UserUpdateEvent::userRenamed;
343 }
344 else if (signal == propertiesChangedSignal)
345 {
346 getUserNameFromPath(msg.get_path(), userName);
347 }
348 else
349 {
George Liu82844ef2024-07-17 17:03:56 +0800350 lg2::error("Unknown user update signal: {SIGNAL}", "SIGNAL", signal);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530351 return;
352 }
353
354 if (signal.empty() || userName.empty() ||
355 (signal == userRenamedSignal && newUserName.empty()))
356 {
George Liu82844ef2024-07-17 17:03:56 +0800357 lg2::error("Invalid inputs received");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530358 return;
359 }
360
361 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
362 userLock{*(usrAccess.userMutex)};
363 usrAccess.checkAndReloadUserData();
364
365 if (signal == propertiesChangedSignal)
366 {
367 std::string intfName;
368 DbusUserObjProperties chProperties;
369 msg.read(intfName, chProperties); // skip reading 3rd argument.
370 for (const auto& prop : chProperties)
371 {
372 userEvent = UserUpdateEvent::reservedEvent;
373 std::string member = prop.first;
374 if (member == userPrivProperty)
375 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700376 priv = std::get<std::string>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530377 userEvent = UserUpdateEvent::userPrivUpdated;
378 }
379 else if (member == userGrpProperty)
380 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700381 groups = std::get<std::vector<std::string>>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530382 userEvent = UserUpdateEvent::userGrpUpdated;
383 }
384 else if (member == userEnabledProperty)
385 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700386 enabled = std::get<bool>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530387 userEvent = UserUpdateEvent::userStateUpdated;
388 }
389 // Process based on event type.
390 if (userEvent == UserUpdateEvent::userGrpUpdated)
391 {
392 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
393 groups.end())
394 {
395 // remove user from ipmi user list.
396 userUpdateHelper(usrAccess, UserUpdateEvent::userDeleted,
397 userName, priv, enabled, newUserName);
398 }
399 else
400 {
401 DbusUserObjProperties properties;
402 try
403 {
404 auto method = bus.new_method_call(
405 getUserServiceName().c_str(), msg.get_path(),
406 dBusPropertiesInterface, getAllPropertiesMethod);
407 method.append(usersInterface);
408 auto reply = bus.call(method);
409 reply.read(properties);
410 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500411 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530412 {
George Liu82844ef2024-07-17 17:03:56 +0800413 lg2::debug("Failed to excute {METHOD}, path: {PATH}",
414 "METHOD", getAllPropertiesMethod, "PATH",
415 msg.get_path());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530416 return;
417 }
418 usrAccess.getUserProperties(properties, groups, priv,
419 enabled);
420 // add user to ipmi user list.
421 userUpdateHelper(usrAccess, UserUpdateEvent::userCreated,
422 userName, priv, enabled, newUserName);
423 }
424 }
425 else if (userEvent != UserUpdateEvent::reservedEvent)
426 {
427 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
428 newUserName);
429 }
430 }
431 }
432 else if (userEvent != UserUpdateEvent::reservedEvent)
433 {
434 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
435 newUserName);
436 }
437 return;
438}
439
440UserAccess::~UserAccess()
441{
442 if (signalHndlrObject)
443 {
444 userUpdatedSignal.reset();
445 userMgrRenamedSignal.reset();
446 userPropertiesSignal.reset();
447 sigHndlrLock.unlock();
448 }
449}
450
451UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection())
452{
453 std::ofstream mutexCleanUpFile;
454 mutexCleanUpFile.open(ipmiMutexCleanupLockFile,
455 std::ofstream::out | std::ofstream::app);
456 if (!mutexCleanUpFile.good())
457 {
George Liu82844ef2024-07-17 17:03:56 +0800458 lg2::debug("Unable to open mutex cleanup file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530459 return;
460 }
461 mutexCleanUpFile.close();
462 mutexCleanupLock = boost::interprocess::file_lock(ipmiMutexCleanupLockFile);
463 if (mutexCleanupLock.try_lock())
464 {
465 boost::interprocess::named_recursive_mutex::remove(ipmiUserMutex);
466 }
467 mutexCleanupLock.lock_sharable();
468 userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>(
469 boost::interprocess::open_or_create, ipmiUserMutex);
470
arun-pmbbe728c2020-01-10 15:18:04 +0530471 cacheUserDataFile();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530472 getSystemPrivAndGroups();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530473}
474
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530475UserInfo* UserAccess::getUserInfo(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530476{
477 checkAndReloadUserData();
478 return &usersTbl.user[userId];
479}
480
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530481void UserAccess::setUserInfo(const uint8_t userId, UserInfo* userInfo)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530482{
483 checkAndReloadUserData();
484 std::copy(reinterpret_cast<uint8_t*>(userInfo),
485 reinterpret_cast<uint8_t*>(userInfo) + sizeof(*userInfo),
486 reinterpret_cast<uint8_t*>(&usersTbl.user[userId]));
487 writeUserData();
488}
489
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530490bool UserAccess::isValidChannel(const uint8_t chNum)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530491{
492 return (chNum < ipmiMaxChannels);
493}
494
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530495bool UserAccess::isValidUserId(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530496{
497 return ((userId <= ipmiMaxUsers) && (userId != reservedUserId));
498}
499
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530500bool UserAccess::isValidPrivilege(const uint8_t priv)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530501{
jayaprakash Mutyala0e2dbee2019-12-26 13:03:04 +0000502 // Callback privilege is deprecated in OpenBMC
Alexander Filippovfc24fa52022-02-01 14:57:59 +0300503 return isValidPrivLimit(priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530504}
505
506uint8_t UserAccess::getUsrMgmtSyncIndex()
507{
Johnathan Manteyfd61fc32021-04-08 11:05:38 -0700508 // Identify the IPMI channel used to assign system user privilege levels
509 // in phosphor-user-manager. The default value is IPMI Channel 1. To
510 // assign a different channel add:
511 // "is_management_nic" : true
512 // into the channel_config.json file describing the assignment of the IPMI
513 // channels. It is only necessary to add the string above to ONE record in
514 // the channel_config.json file. All other records will be automatically
515 // assigned a "false" value.
516 return getChannelConfigObject().getManagementNICID();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530517}
518
519CommandPrivilege UserAccess::convertToIPMIPrivilege(const std::string& value)
520{
521 auto iter = std::find(ipmiPrivIndex.begin(), ipmiPrivIndex.end(), value);
522 if (iter == ipmiPrivIndex.end())
523 {
524 if (value == "")
525 {
526 return static_cast<CommandPrivilege>(privNoAccess);
527 }
George Liu82844ef2024-07-17 17:03:56 +0800528 lg2::error("Error in converting to IPMI privilege: {PRIV}", "PRIV",
529 value);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530530 throw std::out_of_range("Out of range - convertToIPMIPrivilege");
531 }
532 else
533 {
534 return static_cast<CommandPrivilege>(
535 std::distance(ipmiPrivIndex.begin(), iter));
536 }
537}
538
539std::string UserAccess::convertToSystemPrivilege(const CommandPrivilege& value)
540{
541 if (value == static_cast<CommandPrivilege>(privNoAccess))
542 {
543 return "";
544 }
545 try
546 {
547 return ipmiPrivIndex.at(value);
548 }
549 catch (const std::out_of_range& e)
550 {
George Liu82844ef2024-07-17 17:03:56 +0800551 lg2::error("Error in converting to system privilege: {PRIV}", "PRIV",
552 value);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530553 throw std::out_of_range("Out of range - convertToSystemPrivilege");
554 }
555}
556
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000557bool UserAccess::isValidUserName(const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530558{
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000559 if (userName.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530560 {
George Liu82844ef2024-07-17 17:03:56 +0800561 lg2::error("userName is empty");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530562 return false;
563 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530564 if (!std::regex_match(userName.c_str(),
nichanghao.nch0c96fdf2024-01-17 22:13:35 +0800565 std::regex("[a-zA-Z_][a-zA-Z_0-9]*")))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530566 {
George Liu82844ef2024-07-17 17:03:56 +0800567 lg2::error("Unsupported characters in user name");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530568 return false;
569 }
570 if (userName == "root")
571 {
George Liu82844ef2024-07-17 17:03:56 +0800572 lg2::error("Invalid user name - root");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530573 return false;
574 }
575 std::map<DbusUserObjPath, DbusUserObjValue> properties;
576 try
577 {
578 auto method = bus.new_method_call(getUserServiceName().c_str(),
579 userMgrObjBasePath, dBusObjManager,
580 getManagedObjectsMethod);
581 auto reply = bus.call(method);
582 reply.read(properties);
583 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500584 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530585 {
George Liu82844ef2024-07-17 17:03:56 +0800586 lg2::error("Failed to excute {METHOD}, path: {PATH}", "METHOD",
587 getManagedObjectsMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530588 return false;
589 }
590
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530591 sdbusplus::message::object_path tempUserPath(userObjBasePath);
592 tempUserPath /= userName;
593 std::string usersPath(tempUserPath);
594
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530595 if (properties.find(usersPath) != properties.end())
596 {
George Liu82844ef2024-07-17 17:03:56 +0800597 lg2::debug("Username {USER_NAME} already exists", "USER_NAME",
598 userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530599 return false;
600 }
601
602 return true;
603}
604
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530605/** @brief Information exchanged by pam module and application.
606 *
607 * @param[in] numMsg - length of the array of pointers,msg.
608 *
609 * @param[in] msg - pointer to an array of pointers to pam_message structure
610 *
611 * @param[out] resp - struct pam response array
612 *
613 * @param[in] appdataPtr - member of pam_conv structure
614 *
615 * @return the response in pam response structure.
616 */
617
618static int pamFunctionConversation(int numMsg, const struct pam_message** msg,
619 struct pam_response** resp, void* appdataPtr)
620{
621 if (appdataPtr == nullptr)
622 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530623 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530624 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530625
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530626 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
627 {
628 return PAM_CONV_ERR;
629 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530630
631 for (int i = 0; i < numMsg; ++i)
632 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530633 /* Ignore all PAM messages except prompting for hidden input */
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530634 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
635 {
636 continue;
637 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530638
639 /* Assume PAM is only prompting for the password as hidden input */
640 /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */
641
642 char* appPass = reinterpret_cast<char*>(appdataPtr);
643 size_t appPassSize = std::strlen(appPass);
644
645 if (appPassSize >= PAM_MAX_RESP_SIZE)
646 {
647 return PAM_CONV_ERR;
648 }
649
650 char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1));
651 if (pass == nullptr)
652 {
653 return PAM_BUF_ERR;
654 }
655
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500656 void* ptr = calloc(static_cast<size_t>(numMsg),
657 sizeof(struct pam_response));
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530658 if (ptr == nullptr)
659 {
660 free(pass);
661 return PAM_BUF_ERR;
662 }
663
664 std::strncpy(pass, appPass, appPassSize + 1);
665
666 *resp = reinterpret_cast<pam_response*>(ptr);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530667 resp[i]->resp = pass;
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530668
669 return PAM_SUCCESS;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530670 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530671
672 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530673}
674
675/** @brief Updating the PAM password
676 *
677 * @param[in] username - username in string
678 *
679 * @param[in] password - new password in string
680 *
681 * @return status
682 */
683
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000684int pamUpdatePasswd(const char* username, const char* password)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530685{
686 const struct pam_conv localConversation = {pamFunctionConversation,
687 const_cast<char*>(password)};
688 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
689
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500690 int retval = pam_start("passwd", username, &localConversation,
691 &localAuthHandle);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530692
693 if (retval != PAM_SUCCESS)
694 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000695 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530696 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000697
698 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
699 if (retval != PAM_SUCCESS)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530700 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000701 pam_end(localAuthHandle, retval);
702 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530703 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000704
705 return pam_end(localAuthHandle, PAM_SUCCESS);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530706}
707
Ayushi Smriti02650d52019-05-15 11:59:09 +0000708bool pamUserCheckAuthenticate(std::string_view username,
709 std::string_view password)
710{
711 const struct pam_conv localConversation = {
712 pamFunctionConversation, const_cast<char*>(password.data())};
713
714 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
715
716 if (pam_start("dropbear", username.data(), &localConversation,
717 &localAuthHandle) != PAM_SUCCESS)
718 {
George Liu82844ef2024-07-17 17:03:56 +0800719 lg2::error("User Authentication Failure");
Ayushi Smriti02650d52019-05-15 11:59:09 +0000720 return false;
721 }
722
723 int retval = pam_authenticate(localAuthHandle,
724 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
725
726 if (retval != PAM_SUCCESS)
727 {
George Liu82844ef2024-07-17 17:03:56 +0800728 lg2::debug("pam_authenticate returned failure: {ERROR}", "ERROR",
729 retval);
Ayushi Smriti02650d52019-05-15 11:59:09 +0000730
731 pam_end(localAuthHandle, retval);
732 return false;
733 }
734
735 if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
736 PAM_SUCCESS)
737 {
738 pam_end(localAuthHandle, PAM_SUCCESS);
739 return false;
740 }
741
742 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
743 {
744 return false;
745 }
746 return true;
747}
748
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000749Cc UserAccess::setSpecialUserPassword(const std::string& userName,
Vernon Mauery1e22a0f2021-07-30 13:36:54 -0700750 const SecureString& userPassword)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530751{
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000752 if (pamUpdatePasswd(userName.c_str(), userPassword.c_str()) != PAM_SUCCESS)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530753 {
George Liu82844ef2024-07-17 17:03:56 +0800754 lg2::debug("Failed to update password");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000755 return ccUnspecifiedError;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530756 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000757 return ccSuccess;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530758}
759
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000760Cc UserAccess::setUserPassword(const uint8_t userId, const char* userPassword)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530761{
762 std::string userName;
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000763 if (ipmiUserGetUserName(userId, userName) != ccSuccess)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530764 {
George Liu82844ef2024-07-17 17:03:56 +0800765 lg2::debug("User Name not found, user Id: {USER_ID}", "USER_ID",
766 userId);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000767 return ccParmOutOfRange;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530768 }
Snehalatha Venkatesh61024d72021-04-08 16:24:39 +0000769
770 ipmi::SecureString passwd;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530771 passwd.assign(reinterpret_cast<const char*>(userPassword), 0,
772 maxIpmi20PasswordSize);
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000773 int retval = pamUpdatePasswd(userName.c_str(), passwd.c_str());
774
775 switch (retval)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530776 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000777 case PAM_SUCCESS:
778 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000779 return ccSuccess;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000780 }
781 case PAM_AUTHTOK_ERR:
782 {
George Liu82844ef2024-07-17 17:03:56 +0800783 lg2::debug("Bad authentication token");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000784 return ccInvalidFieldRequest;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000785 }
786 default:
787 {
George Liu82844ef2024-07-17 17:03:56 +0800788 lg2::debug("Failed to update password, user Id: {USER_ID}",
789 "USER_ID", userId);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000790 return ccUnspecifiedError;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000791 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530792 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530793}
794
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000795Cc UserAccess::setUserEnabledState(const uint8_t userId,
796 const bool& enabledState)
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530797{
798 if (!isValidUserId(userId))
799 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000800 return ccParmOutOfRange;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530801 }
802 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
803 userLock{*userMutex};
804 UserInfo* userInfo = getUserInfo(userId);
805 std::string userName;
806 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
807 ipmiMaxUserName);
808 if (userName.empty())
809 {
George Liu82844ef2024-07-17 17:03:56 +0800810 lg2::debug("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000811 return ccUnspecifiedError;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530812 }
813 if (userInfo->userEnabled != enabledState)
814 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530815 sdbusplus::message::object_path tempUserPath(userObjBasePath);
816 tempUserPath /= userName;
817 std::string userPath(tempUserPath);
Patrick Venture99d1ba02019-02-21 15:11:24 -0800818 setDbusProperty(bus, getUserServiceName(), userPath, usersInterface,
819 userEnabledProperty, enabledState);
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530820 userInfo->userEnabled = enabledState;
821 try
822 {
823 writeUserData();
824 }
825 catch (const std::exception& e)
826 {
George Liu82844ef2024-07-17 17:03:56 +0800827 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000828 return ccUnspecifiedError;
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530829 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530830 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000831 return ccSuccess;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530832}
833
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000834Cc UserAccess::setUserPayloadAccess(const uint8_t chNum,
835 const uint8_t operation,
836 const uint8_t userId,
837 const PayloadAccess& payloadAccess)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000838{
839 constexpr uint8_t enable = 0x0;
840 constexpr uint8_t disable = 0x1;
841
842 if (!isValidChannel(chNum))
843 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000844 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000845 }
846 if (!isValidUserId(userId))
847 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000848 return ccParmOutOfRange;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000849 }
850 if (operation != enable && operation != disable)
851 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000852 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000853 }
854 // Check operation & payloadAccess if required.
855 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
856 userLock{*userMutex};
857 UserInfo* userInfo = getUserInfo(userId);
858
859 if (operation == enable)
860 {
861 userInfo->payloadAccess[chNum].stdPayloadEnables1 |=
862 payloadAccess.stdPayloadEnables1;
863
864 userInfo->payloadAccess[chNum].oemPayloadEnables1 |=
865 payloadAccess.oemPayloadEnables1;
866 }
867 else
868 {
869 userInfo->payloadAccess[chNum].stdPayloadEnables1 &=
870 ~(payloadAccess.stdPayloadEnables1);
871
872 userInfo->payloadAccess[chNum].oemPayloadEnables1 &=
873 ~(payloadAccess.oemPayloadEnables1);
874 }
875
876 try
877 {
878 writeUserData();
879 }
880 catch (const std::exception& e)
881 {
George Liu82844ef2024-07-17 17:03:56 +0800882 lg2::error("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000883 return ccUnspecifiedError;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000884 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000885 return ccSuccess;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000886}
887
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000888Cc UserAccess::setUserPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
889 const UserPrivAccess& privAccess,
890 const bool& otherPrivUpdates)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530891{
892 if (!isValidChannel(chNum))
893 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000894 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530895 }
896 if (!isValidUserId(userId))
897 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000898 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530899 }
900 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
901 userLock{*userMutex};
902 UserInfo* userInfo = getUserInfo(userId);
903 std::string userName;
904 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
905 ipmiMaxUserName);
906 if (userName.empty())
907 {
George Liu82844ef2024-07-17 17:03:56 +0800908 lg2::debug("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000909 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530910 }
911 std::string priv = convertToSystemPrivilege(
912 static_cast<CommandPrivilege>(privAccess.privilege));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530913 uint8_t syncIndex = getUsrMgmtSyncIndex();
914 if (chNum == syncIndex &&
915 privAccess.privilege != userInfo->userPrivAccess[syncIndex].privilege)
916 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530917 sdbusplus::message::object_path tempUserPath(userObjBasePath);
918 tempUserPath /= userName;
919 std::string userPath(tempUserPath);
Patrick Venture99d1ba02019-02-21 15:11:24 -0800920 setDbusProperty(bus, getUserServiceName(), userPath, usersInterface,
921 userPrivProperty, priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530922 }
923 userInfo->userPrivAccess[chNum].privilege = privAccess.privilege;
924
925 if (otherPrivUpdates)
926 {
927 userInfo->userPrivAccess[chNum].ipmiEnabled = privAccess.ipmiEnabled;
928 userInfo->userPrivAccess[chNum].linkAuthEnabled =
929 privAccess.linkAuthEnabled;
930 userInfo->userPrivAccess[chNum].accessCallback =
931 privAccess.accessCallback;
932 }
933 try
934 {
935 writeUserData();
936 }
937 catch (const std::exception& e)
938 {
George Liu82844ef2024-07-17 17:03:56 +0800939 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000940 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530941 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000942 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530943}
944
945uint8_t UserAccess::getUserId(const std::string& userName)
946{
947 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
948 userLock{*userMutex};
949 checkAndReloadUserData();
950 // user index 0 is reserved, starts with 1
951 size_t usrIndex = 1;
952 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
953 {
954 std::string curName(
955 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
956 ipmiMaxUserName);
957 if (userName == curName)
958 {
959 break; // found the entry
960 }
961 }
962 if (usrIndex > ipmiMaxUsers)
963 {
George Liu82844ef2024-07-17 17:03:56 +0800964 lg2::debug("Username {USER_NAME} not found", "USER_NAME", userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530965 return invalidUserId;
966 }
967
968 return usrIndex;
969}
970
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000971Cc UserAccess::getUserName(const uint8_t userId, std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530972{
973 if (!isValidUserId(userId))
974 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000975 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530976 }
977 UserInfo* userInfo = getUserInfo(userId);
978 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
979 ipmiMaxUserName);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000980 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530981}
982
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530983bool UserAccess::isIpmiInAvailableGroupList()
984{
985 if (std::find(availableGroups.begin(), availableGroups.end(),
986 ipmiGrpName) != availableGroups.end())
987 {
988 return true;
989 }
990 if (availableGroups.empty())
991 {
992 // available groups shouldn't be empty, re-query
993 getSystemPrivAndGroups();
994 if (std::find(availableGroups.begin(), availableGroups.end(),
995 ipmiGrpName) != availableGroups.end())
996 {
997 return true;
998 }
999 }
1000 return false;
1001}
1002
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001003Cc UserAccess::setUserName(const uint8_t userId, const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301004{
1005 if (!isValidUserId(userId))
1006 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001007 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301008 }
1009
1010 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1011 userLock{*userMutex};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301012 std::string oldUser;
1013 getUserName(userId, oldUser);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301014
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001015 if (oldUser == userName)
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301016 {
1017 // requesting to set the same user name, return success.
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001018 return ccSuccess;
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301019 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001020
1021 bool validUser = isValidUserName(userName);
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301022 UserInfo* userInfo = getUserInfo(userId);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001023 if (userName.empty() && !oldUser.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301024 {
1025 // Delete existing user
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301026 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1027 tempUserPath /= oldUser;
1028 std::string userPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301029 try
1030 {
1031 auto method = bus.new_method_call(
1032 getUserServiceName().c_str(), userPath.c_str(),
1033 deleteUserInterface, deleteUserMethod);
1034 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 deleteUserMethod, "PATH", userPath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001040 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301041 }
Richard Marian Thomaiyar02710bb2018-11-28 20:42:25 +05301042 deleteUserIndex(userId);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301043 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001044 else if (oldUser.empty() && !userName.empty() && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301045 {
1046 try
1047 {
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301048 if (!isIpmiInAvailableGroupList())
1049 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001050 return ccUnspecifiedError;
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301051 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301052 // Create new user
1053 auto method = bus.new_method_call(
1054 getUserServiceName().c_str(), userMgrObjBasePath,
1055 userMgrInterface, createUserMethod);
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001056 method.append(userName.c_str(), availableGroups,
1057 ipmiPrivIndex[PRIVILEGE_USER], false);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301058 auto reply = bus.call(method);
1059 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001060 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301061 {
George Liu82844ef2024-07-17 17:03:56 +08001062 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1063 createUserMethod, "PATH", userMgrObjBasePath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001064 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301065 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001066
1067 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1068 std::memcpy(userInfo->userName,
1069 static_cast<const void*>(userName.data()), userName.size());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301070 userInfo->userInSystem = true;
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001071 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1072 {
1073 userInfo->userPrivAccess[chIndex].privilege =
1074 static_cast<uint8_t>(PRIVILEGE_USER);
1075 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301076 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001077 else if (oldUser != userName && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301078 {
1079 try
1080 {
1081 // User rename
1082 auto method = bus.new_method_call(
1083 getUserServiceName().c_str(), userMgrObjBasePath,
1084 userMgrInterface, renameUserMethod);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001085 method.append(oldUser.c_str(), userName.c_str());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301086 auto reply = bus.call(method);
1087 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001088 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301089 {
George Liu82844ef2024-07-17 17:03:56 +08001090 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1091 renameUserMethod, "PATH", userMgrObjBasePath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001092 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301093 }
1094 std::fill(static_cast<uint8_t*>(userInfo->userName),
1095 static_cast<uint8_t*>(userInfo->userName) +
1096 sizeof(userInfo->userName),
1097 0);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001098
1099 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1100 std::memcpy(userInfo->userName,
1101 static_cast<const void*>(userName.data()), userName.size());
1102
1103 ipmiRenameUserEntryPassword(oldUser, userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301104 userInfo->userInSystem = true;
1105 }
1106 else if (!validUser)
1107 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001108 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301109 }
1110 try
1111 {
1112 writeUserData();
1113 }
1114 catch (const std::exception& e)
1115 {
George Liu82844ef2024-07-17 17:03:56 +08001116 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001117 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301118 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001119 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301120}
1121
1122static constexpr const char* jsonUserName = "user_name";
1123static constexpr const char* jsonPriv = "privilege";
1124static constexpr const char* jsonIpmiEnabled = "ipmi_enabled";
1125static constexpr const char* jsonLinkAuthEnabled = "link_auth_enabled";
1126static constexpr const char* jsonAccCallbk = "access_callback";
1127static constexpr const char* jsonUserEnabled = "user_enabled";
1128static constexpr const char* jsonUserInSys = "user_in_system";
1129static constexpr const char* jsonFixedUser = "fixed_user_name";
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001130static constexpr const char* payloadEnabledStr = "payload_enabled";
1131static constexpr const char* stdPayloadStr = "std_payload";
1132static constexpr const char* oemPayloadStr = "OEM_payload";
1133
1134/** @brief to construct a JSON object from the given payload access details.
1135 *
1136 * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. (input)
1137 * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. (input)
1138 *
1139 * @details Sample output JSON object format :
1140 * "payload_enabled":{
1141 * "OEM_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1142 * "OEM_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1143 * "OEM_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1144 * "OEM_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1145 * "OEM_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1146 * "OEM_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1147 * "OEM_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1148 * "OEM_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1149 * "std_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1150 * "std_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1151 * "std_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1152 * "std_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1153 * "std_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1154 * "std_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1155 * "std_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1156 * "std_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1157 * }
1158 */
1159static const Json constructJsonPayloadEnables(
1160 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1161 stdPayload,
1162 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1163 oemPayload)
1164{
1165 Json jsonPayloadEnabled;
1166
1167 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1168 {
1169 std::ostringstream stdPayloadStream;
1170 std::ostringstream oemPayloadStream;
1171
1172 stdPayloadStream << stdPayloadStr << payloadNum;
1173 oemPayloadStream << oemPayloadStr << payloadNum;
1174
1175 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1176 stdPayloadStream.str(), stdPayload[payloadNum]));
1177
1178 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1179 oemPayloadStream.str(), oemPayload[payloadNum]));
1180 }
1181 return jsonPayloadEnabled;
1182}
1183
1184void UserAccess::readPayloadAccessFromUserInfo(
1185 const UserInfo& userInfo,
1186 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& stdPayload,
1187 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& oemPayload)
1188{
1189 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1190 {
1191 for (auto chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1192 {
1193 stdPayload[payloadNum][chIndex] =
1194 userInfo.payloadAccess[chIndex].stdPayloadEnables1[payloadNum];
1195
1196 oemPayload[payloadNum][chIndex] =
1197 userInfo.payloadAccess[chIndex].oemPayloadEnables1[payloadNum];
1198 }
1199 }
1200}
1201
1202void UserAccess::updatePayloadAccessInUserInfo(
1203 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1204 stdPayload,
Willy Tu11d68892022-01-20 10:37:34 -08001205 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&,
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001206 UserInfo& userInfo)
1207{
1208 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1209 {
1210 // Ensure that reserved/unsupported payloads are marked to zero.
1211 userInfo.payloadAccess[chIndex].stdPayloadEnables1.reset();
1212 userInfo.payloadAccess[chIndex].oemPayloadEnables1.reset();
1213 userInfo.payloadAccess[chIndex].stdPayloadEnables2Reserved.reset();
1214 userInfo.payloadAccess[chIndex].oemPayloadEnables2Reserved.reset();
1215 // Update SOL status as it is the only supported payload currently.
1216 userInfo.payloadAccess[chIndex]
1217 .stdPayloadEnables1[static_cast<uint8_t>(ipmi::PayloadType::SOL)] =
1218 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)][chIndex];
1219 }
1220}
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301221
1222void UserAccess::readUserData()
1223{
1224 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1225 userLock{*userMutex};
1226
1227 std::ifstream iUsrData(ipmiUserDataFile, std::ios::in | std::ios::binary);
1228 if (!iUsrData.good())
1229 {
George Liu82844ef2024-07-17 17:03:56 +08001230 lg2::error("Error in reading IPMI user data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301231 throw std::ios_base::failure("Error opening IPMI user data file");
1232 }
1233
1234 Json jsonUsersTbl = Json::array();
1235 jsonUsersTbl = Json::parse(iUsrData, nullptr, false);
1236
1237 if (jsonUsersTbl.size() != ipmiMaxUsers)
1238 {
George Liu82844ef2024-07-17 17:03:56 +08001239 lg2::error("Error in reading IPMI user data file - User count issues");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301240 throw std::runtime_error(
1241 "Corrupted IPMI user data file - invalid user count");
1242 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001243
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301244 // user index 0 is reserved, starts with 1
1245 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1246 {
1247 Json userInfo = jsonUsersTbl[usrIndex - 1]; // json array starts with 0.
1248 if (userInfo.is_null())
1249 {
George Liu82844ef2024-07-17 17:03:56 +08001250 lg2::error("Error in reading IPMI user data file - "
1251 "user info corrupted");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301252 throw std::runtime_error(
1253 "Corrupted IPMI user data file - invalid user info");
1254 }
1255 std::string userName = userInfo[jsonUserName].get<std::string>();
1256 std::strncpy(reinterpret_cast<char*>(usersTbl.user[usrIndex].userName),
1257 userName.c_str(), ipmiMaxUserName);
1258
1259 std::vector<std::string> privilege =
1260 userInfo[jsonPriv].get<std::vector<std::string>>();
1261 std::vector<bool> ipmiEnabled =
1262 userInfo[jsonIpmiEnabled].get<std::vector<bool>>();
1263 std::vector<bool> linkAuthEnabled =
1264 userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
1265 std::vector<bool> accessCallback =
1266 userInfo[jsonAccCallbk].get<std::vector<bool>>();
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001267
1268 // Payload Enables Processing.
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001269 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1270 stdPayload = {};
1271 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1272 oemPayload = {};
1273 try
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001274 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001275 const auto jsonPayloadEnabled = userInfo.at(payloadEnabledStr);
1276 for (auto payloadNum = 0; payloadNum < payloadsPerByte;
1277 payloadNum++)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001278 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001279 std::ostringstream stdPayloadStream;
1280 std::ostringstream oemPayloadStream;
1281
1282 stdPayloadStream << stdPayloadStr << payloadNum;
1283 oemPayloadStream << oemPayloadStr << payloadNum;
1284
1285 stdPayload[payloadNum] =
1286 jsonPayloadEnabled[stdPayloadStream.str()]
1287 .get<std::array<bool, ipmiMaxChannels>>();
1288 oemPayload[payloadNum] =
1289 jsonPayloadEnabled[oemPayloadStream.str()]
1290 .get<std::array<bool, ipmiMaxChannels>>();
1291
1292 if (stdPayload[payloadNum].size() != ipmiMaxChannels ||
1293 oemPayload[payloadNum].size() != ipmiMaxChannels)
1294 {
George Liu82844ef2024-07-17 17:03:56 +08001295 lg2::error("Error in reading IPMI user data file - "
1296 "payload properties corrupted");
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001297 throw std::runtime_error(
1298 "Corrupted IPMI user data file - payload properties");
1299 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001300 }
1301 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001302 catch (const Json::out_of_range& e)
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001303 {
1304 // Key not found in 'userInfo'; possibly an old JSON file. Use
1305 // default values for all payloads, and SOL payload default is true.
1306 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)].fill(true);
1307 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001308
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301309 if (privilege.size() != ipmiMaxChannels ||
1310 ipmiEnabled.size() != ipmiMaxChannels ||
1311 linkAuthEnabled.size() != ipmiMaxChannels ||
1312 accessCallback.size() != ipmiMaxChannels)
1313 {
George Liu82844ef2024-07-17 17:03:56 +08001314 lg2::error("Error in reading IPMI user data file - "
1315 "properties corrupted");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301316 throw std::runtime_error(
1317 "Corrupted IPMI user data file - properties");
1318 }
1319 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1320 {
1321 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege =
1322 static_cast<uint8_t>(
1323 convertToIPMIPrivilege(privilege[chIndex]));
1324 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled =
1325 ipmiEnabled[chIndex];
1326 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled =
1327 linkAuthEnabled[chIndex];
1328 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
1329 accessCallback[chIndex];
1330 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001331 updatePayloadAccessInUserInfo(stdPayload, oemPayload,
1332 usersTbl.user[usrIndex]);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301333 usersTbl.user[usrIndex].userEnabled =
1334 userInfo[jsonUserEnabled].get<bool>();
1335 usersTbl.user[usrIndex].userInSystem =
1336 userInfo[jsonUserInSys].get<bool>();
1337 usersTbl.user[usrIndex].fixedUserName =
1338 userInfo[jsonFixedUser].get<bool>();
1339 }
1340
George Liu82844ef2024-07-17 17:03:56 +08001341 lg2::debug("User data read from IPMI data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301342 iUsrData.close();
1343 // Update the timestamp
1344 fileLastUpdatedTime = getUpdatedFileTime();
1345 return;
1346}
1347
1348void UserAccess::writeUserData()
1349{
1350 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1351 userLock{*userMutex};
1352
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301353 Json jsonUsersTbl = Json::array();
1354 // user index 0 is reserved, starts with 1
1355 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1356 {
1357 Json jsonUserInfo;
1358 jsonUserInfo[jsonUserName] = std::string(
1359 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
1360 ipmiMaxUserName);
1361 std::vector<std::string> privilege(ipmiMaxChannels);
1362 std::vector<bool> ipmiEnabled(ipmiMaxChannels);
1363 std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
1364 std::vector<bool> accessCallback(ipmiMaxChannels);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001365
1366 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1367 stdPayload;
1368 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1369 oemPayload;
1370
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301371 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1372 {
1373 privilege[chIndex] =
1374 convertToSystemPrivilege(static_cast<CommandPrivilege>(
1375 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege));
1376 ipmiEnabled[chIndex] =
1377 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled;
1378 linkAuthEnabled[chIndex] =
1379 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled;
1380 accessCallback[chIndex] =
1381 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback;
1382 }
1383 jsonUserInfo[jsonPriv] = privilege;
1384 jsonUserInfo[jsonIpmiEnabled] = ipmiEnabled;
1385 jsonUserInfo[jsonLinkAuthEnabled] = linkAuthEnabled;
1386 jsonUserInfo[jsonAccCallbk] = accessCallback;
1387 jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
1388 jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
1389 jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001390
1391 readPayloadAccessFromUserInfo(usersTbl.user[usrIndex], stdPayload,
1392 oemPayload);
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001393 Json jsonPayloadEnabledInfo = constructJsonPayloadEnables(stdPayload,
1394 oemPayload);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001395 jsonUserInfo[payloadEnabledStr] = jsonPayloadEnabledInfo;
1396
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301397 jsonUsersTbl.push_back(jsonUserInfo);
1398 }
1399
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301400 static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
1401 int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
1402 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1403 if (fd < 0)
1404 {
George Liu82844ef2024-07-17 17:03:56 +08001405 lg2::error("Error in creating temporary IPMI user data file");
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301406 throw std::ios_base::failure(
1407 "Error in creating temporary IPMI user data file");
1408 }
1409 const auto& writeStr = jsonUsersTbl.dump();
1410 if (write(fd, writeStr.c_str(), writeStr.size()) !=
1411 static_cast<ssize_t>(writeStr.size()))
1412 {
1413 close(fd);
George Liu82844ef2024-07-17 17:03:56 +08001414 lg2::error("Error in writing temporary IPMI user data file");
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301415 throw std::ios_base::failure(
1416 "Error in writing temporary IPMI user data file");
1417 }
1418 close(fd);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301419
1420 if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
1421 {
George Liu82844ef2024-07-17 17:03:56 +08001422 lg2::error("Error in renaming temporary IPMI user data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301423 throw std::runtime_error("Error in renaming IPMI user data file");
1424 }
1425 // Update the timestamp
1426 fileLastUpdatedTime = getUpdatedFileTime();
1427 return;
1428}
1429
1430bool UserAccess::addUserEntry(const std::string& userName,
1431 const std::string& sysPriv, const bool& enabled)
1432{
1433 UsersTbl* userData = getUsersTblPtr();
1434 size_t freeIndex = 0xFF;
1435 // user index 0 is reserved, starts with 1
1436 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1437 {
1438 std::string curName(
1439 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
1440 ipmiMaxUserName);
1441 if (userName == curName)
1442 {
George Liu82844ef2024-07-17 17:03:56 +08001443 lg2::debug("Username {USER_NAME} exists", "USER_NAME", userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301444 return false; // user name exists.
1445 }
1446
1447 if ((!userData->user[usrIndex].userInSystem) &&
1448 (userData->user[usrIndex].userName[0] == '\0') &&
1449 (freeIndex == 0xFF))
1450 {
1451 freeIndex = usrIndex;
1452 }
1453 }
1454 if (freeIndex == 0xFF)
1455 {
George Liu82844ef2024-07-17 17:03:56 +08001456 lg2::error("No empty slots found");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301457 return false;
1458 }
1459 std::strncpy(reinterpret_cast<char*>(userData->user[freeIndex].userName),
1460 userName.c_str(), ipmiMaxUserName);
1461 uint8_t priv =
1462 static_cast<uint8_t>(UserAccess::convertToIPMIPrivilege(sysPriv)) &
1463 privMask;
1464 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1465 {
1466 userData->user[freeIndex].userPrivAccess[chIndex].privilege = priv;
1467 userData->user[freeIndex].userPrivAccess[chIndex].ipmiEnabled = true;
1468 userData->user[freeIndex].userPrivAccess[chIndex].linkAuthEnabled =
1469 true;
1470 userData->user[freeIndex].userPrivAccess[chIndex].accessCallback = true;
1471 }
1472 userData->user[freeIndex].userInSystem = true;
1473 userData->user[freeIndex].userEnabled = enabled;
1474
1475 return true;
1476}
1477
1478void UserAccess::deleteUserIndex(const size_t& usrIdx)
1479{
1480 UsersTbl* userData = getUsersTblPtr();
1481
1482 std::string userName(
1483 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1484 ipmiMaxUserName);
1485 ipmiClearUserEntryPassword(userName);
1486 std::fill(static_cast<uint8_t*>(userData->user[usrIdx].userName),
1487 static_cast<uint8_t*>(userData->user[usrIdx].userName) +
1488 sizeof(userData->user[usrIdx].userName),
1489 0);
1490 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1491 {
1492 userData->user[usrIdx].userPrivAccess[chIndex].privilege = privNoAccess;
1493 userData->user[usrIdx].userPrivAccess[chIndex].ipmiEnabled = false;
1494 userData->user[usrIdx].userPrivAccess[chIndex].linkAuthEnabled = false;
1495 userData->user[usrIdx].userPrivAccess[chIndex].accessCallback = false;
1496 }
1497 userData->user[usrIdx].userInSystem = false;
1498 userData->user[usrIdx].userEnabled = false;
1499 return;
1500}
1501
1502void UserAccess::checkAndReloadUserData()
1503{
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001504 std::timespec updateTime = getUpdatedFileTime();
1505 if ((updateTime.tv_sec != fileLastUpdatedTime.tv_sec ||
1506 updateTime.tv_nsec != fileLastUpdatedTime.tv_nsec) ||
1507 (updateTime.tv_sec == 0 && updateTime.tv_nsec == 0))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301508 {
1509 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1510 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1511 readUserData();
1512 }
1513 return;
1514}
1515
1516UsersTbl* UserAccess::getUsersTblPtr()
1517{
1518 // reload data before using it.
1519 checkAndReloadUserData();
1520 return &usersTbl;
1521}
1522
1523void UserAccess::getSystemPrivAndGroups()
1524{
1525 std::map<std::string, PrivAndGroupType> properties;
1526 try
1527 {
1528 auto method = bus.new_method_call(
1529 getUserServiceName().c_str(), userMgrObjBasePath,
1530 dBusPropertiesInterface, getAllPropertiesMethod);
1531 method.append(userMgrInterface);
1532
1533 auto reply = bus.call(method);
1534 reply.read(properties);
1535 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001536 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301537 {
George Liu82844ef2024-07-17 17:03:56 +08001538 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1539 getAllPropertiesMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301540 return;
1541 }
1542 for (const auto& t : properties)
1543 {
1544 auto key = t.first;
1545 if (key == allPrivProperty)
1546 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001547 availablePrivileges = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301548 }
1549 else if (key == allGrpProperty)
1550 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001551 availableGroups = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301552 }
1553 }
1554 // TODO: Implement Supported Privilege & Groups verification logic
1555 return;
1556}
1557
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001558std::timespec UserAccess::getUpdatedFileTime()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301559{
1560 struct stat fileStat;
1561 if (stat(ipmiUserDataFile, &fileStat) != 0)
1562 {
George Liu82844ef2024-07-17 17:03:56 +08001563 lg2::debug("Error in getting last updated time stamp");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001564 return std::timespec{0, 0};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301565 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001566 return fileStat.st_mtim;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301567}
1568
1569void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
1570 std::vector<std::string>& usrGrps,
1571 std::string& usrPriv, bool& usrEnabled)
1572{
1573 for (const auto& t : properties)
1574 {
1575 std::string key = t.first;
1576 if (key == userPrivProperty)
1577 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001578 usrPriv = std::get<std::string>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301579 }
1580 else if (key == userGrpProperty)
1581 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001582 usrGrps = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301583 }
1584 else if (key == userEnabledProperty)
1585 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001586 usrEnabled = std::get<bool>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301587 }
1588 }
1589 return;
1590}
1591
1592int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
1593 std::vector<std::string>& usrGrps,
1594 std::string& usrPriv, bool& usrEnabled)
1595{
1596 auto usrObj = userObjs.find(usersInterface);
1597 if (usrObj != userObjs.end())
1598 {
1599 getUserProperties(usrObj->second, usrGrps, usrPriv, usrEnabled);
1600 return 0;
1601 }
1602 return -EIO;
1603}
1604
arun-pmbbe728c2020-01-10 15:18:04 +05301605void UserAccess::cacheUserDataFile()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301606{
1607 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1608 userLock{*userMutex};
1609 try
1610 {
1611 readUserData();
1612 }
1613 catch (const std::ios_base::failure& e)
1614 { // File is empty, create it for the first time
1615 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1616 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1617 // user index 0 is reserved, starts with 1
1618 for (size_t userIndex = 1; userIndex <= ipmiMaxUsers; ++userIndex)
1619 {
1620 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1621 {
1622 usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
1623 privNoAccess;
Saravanan Palanisamy92d81192019-08-07 18:00:04 +00001624 usersTbl.user[userIndex]
1625 .payloadAccess[chIndex]
1626 .stdPayloadEnables1[static_cast<uint8_t>(
1627 ipmi::PayloadType::SOL)] = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301628 }
1629 }
1630 writeUserData();
1631 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001632 // Create lock file if it does not exist
1633 int fd = open(ipmiUserSignalLockFile, O_CREAT | O_TRUNC | O_SYNC,
1634 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1635 if (fd < 0)
1636 {
George Liu82844ef2024-07-17 17:03:56 +08001637 lg2::error("Error in creating IPMI user signal lock file");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001638 throw std::ios_base::failure(
1639 "Error in creating temporary IPMI user signal lock file");
1640 }
1641 close(fd);
1642
1643 sigHndlrLock = boost::interprocess::file_lock(ipmiUserSignalLockFile);
George Liu1a2e1502022-07-08 12:20:19 +08001644 // Register it for single object and single process either netipmid /
arun-pmbbe728c2020-01-10 15:18:04 +05301645 // host-ipmid
1646 if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
1647 {
George Liu82844ef2024-07-17 17:03:56 +08001648 lg2::debug("Registering signal handler");
arun-pmbbe728c2020-01-10 15:18:04 +05301649 userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
1650 bus,
1651 sdbusplus::bus::match::rules::type::signal() +
1652 sdbusplus::bus::match::rules::interface(dBusObjManager) +
1653 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001654 [&](sdbusplus::message_t& msg) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001655 userUpdatedSignalHandler(*this, msg);
Patrick Williams369824e2023-10-20 11:18:23 -05001656 });
arun-pmbbe728c2020-01-10 15:18:04 +05301657 userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
1658 bus,
1659 sdbusplus::bus::match::rules::type::signal() +
1660 sdbusplus::bus::match::rules::interface(userMgrInterface) +
1661 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001662 [&](sdbusplus::message_t& msg) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001663 userUpdatedSignalHandler(*this, msg);
Patrick Williams369824e2023-10-20 11:18:23 -05001664 });
arun-pmbbe728c2020-01-10 15:18:04 +05301665 userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
1666 bus,
1667 sdbusplus::bus::match::rules::type::signal() +
1668 sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
1669 sdbusplus::bus::match::rules::interface(
1670 dBusPropertiesInterface) +
1671 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
1672 sdbusplus::bus::match::rules::argN(0, usersInterface),
Patrick Williams5d82f472022-07-22 19:26:53 -05001673 [&](sdbusplus::message_t& msg) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001674 userUpdatedSignalHandler(*this, msg);
Patrick Williams369824e2023-10-20 11:18:23 -05001675 });
arun-pmbbe728c2020-01-10 15:18:04 +05301676 signalHndlrObject = true;
1677 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301678 std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
1679 try
1680 {
1681 auto method = bus.new_method_call(getUserServiceName().c_str(),
1682 userMgrObjBasePath, dBusObjManager,
1683 getManagedObjectsMethod);
1684 auto reply = bus.call(method);
1685 reply.read(managedObjs);
1686 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001687 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301688 {
George Liu82844ef2024-07-17 17:03:56 +08001689 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1690 getManagedObjectsMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301691 return;
1692 }
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301693 bool updateRequired = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301694 UsersTbl* userData = &usersTbl;
1695 // user index 0 is reserved, starts with 1
1696 for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
1697 {
1698 if ((userData->user[usrIdx].userInSystem) &&
1699 (userData->user[usrIdx].userName[0] != '\0'))
1700 {
1701 std::vector<std::string> usrGrps;
1702 std::string usrPriv;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301703
1704 std::string userName(
1705 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1706 ipmiMaxUserName);
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301707 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1708 tempUserPath /= userName;
1709 std::string usersPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301710
1711 auto usrObj = managedObjs.find(usersPath);
1712 if (usrObj != managedObjs.end())
1713 {
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001714 bool usrEnabled = false;
Patrick Venture3a697ad2019-08-19 11:12:05 -07001715
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301716 // User exist. Lets check and update other fileds
1717 getUserObjProperties(usrObj->second, usrGrps, usrPriv,
1718 usrEnabled);
1719 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
1720 usrGrps.end())
1721 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301722 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301723 // Group "ipmi" is removed so lets remove user in IPMI
1724 deleteUserIndex(usrIdx);
1725 }
1726 else
1727 {
1728 // Group "ipmi" is present so lets update other properties
1729 // in IPMI
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001730 uint8_t priv = UserAccess::convertToIPMIPrivilege(usrPriv) &
1731 privMask;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301732 // Update all channels priv, only if it is not equivalent to
1733 // getUsrMgmtSyncIndex()
1734 if (userData->user[usrIdx]
1735 .userPrivAccess[getUsrMgmtSyncIndex()]
1736 .privilege != priv)
1737 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301738 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301739 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
1740 ++chIndex)
1741 {
1742 userData->user[usrIdx]
1743 .userPrivAccess[chIndex]
1744 .privilege = priv;
1745 }
1746 }
1747 if (userData->user[usrIdx].userEnabled != usrEnabled)
1748 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301749 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301750 userData->user[usrIdx].userEnabled = usrEnabled;
1751 }
1752 }
1753
1754 // We are done with this obj. lets delete from MAP
1755 managedObjs.erase(usrObj);
1756 }
1757 else
1758 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301759 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301760 deleteUserIndex(usrIdx);
1761 }
1762 }
1763 }
1764
1765 // Walk through remnaining managedObj users list
1766 // Add them to ipmi data base
1767 for (const auto& usrObj : managedObjs)
1768 {
1769 std::vector<std::string> usrGrps;
1770 std::string usrPriv, userName;
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001771 bool usrEnabled = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301772 std::string usrObjPath = std::string(usrObj.first);
1773 if (getUserNameFromPath(usrObj.first.str, userName) != 0)
1774 {
George Liu82844ef2024-07-17 17:03:56 +08001775 lg2::error("Error in user object path");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301776 continue;
1777 }
1778 getUserObjProperties(usrObj.second, usrGrps, usrPriv, usrEnabled);
1779 // Add 'ipmi' group users
1780 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
1781 usrGrps.end())
1782 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301783 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301784 // CREATE NEW USER
1785 if (true != addUserEntry(userName, usrPriv, usrEnabled))
1786 {
1787 break;
1788 }
1789 }
1790 }
1791
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301792 if (updateRequired)
1793 {
1794 // All userData slots update done. Lets write the data
1795 writeUserData();
1796 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301797
1798 return;
1799}
1800} // namespace ipmi