blob: 8c5a1bf86f076012d304a83368c86dc26c38f56a [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
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053060static constexpr const char* ipmiUserMutex = "ipmi_usr_mutex";
61static constexpr const char* ipmiMutexCleanupLockFile =
Patrick Williams02844912025-03-20 20:11:10 -040062 "/run/ipmi/ipmi_usr_mutex_cleanup";
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +000063static constexpr const char* ipmiUserSignalLockFile =
Patrick Williams02844912025-03-20 20:11:10 -040064 "/run/ipmi/ipmi_usr_signal_mutex";
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053065static constexpr const char* ipmiUserDataFile = "/var/lib/ipmi/ipmi_user.json";
66static constexpr const char* ipmiGrpName = "ipmi";
67static constexpr size_t privNoAccess = 0xF;
68static constexpr size_t privMask = 0xF;
69
70// User manager related
Khang D Nguyen078aa6a2025-03-06 00:03:42 +070071static constexpr const char* userMgrService =
72 "xyz.openbmc_project.User.Manager";
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053073static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user";
74static constexpr const char* userObjBasePath = "/xyz/openbmc_project/user";
75static constexpr const char* userMgrInterface =
76 "xyz.openbmc_project.User.Manager";
77static constexpr const char* usersInterface =
78 "xyz.openbmc_project.User.Attributes";
79static constexpr const char* deleteUserInterface =
80 "xyz.openbmc_project.Object.Delete";
81
82static constexpr const char* createUserMethod = "CreateUser";
83static constexpr const char* deleteUserMethod = "Delete";
84static constexpr const char* renameUserMethod = "RenameUser";
85// User manager signal memebers
86static constexpr const char* userRenamedSignal = "UserRenamed";
87// Mgr interface properties
88static constexpr const char* allPrivProperty = "AllPrivileges";
89static constexpr const char* allGrpProperty = "AllGroups";
90// User interface properties
91static constexpr const char* userPrivProperty = "UserPrivilege";
92static constexpr const char* userGrpProperty = "UserGroups";
93static constexpr const char* userEnabledProperty = "UserEnabled";
94
95static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = {
96 "priv-reserved", // PRIVILEGE_RESERVED - 0
97 "priv-callback", // PRIVILEGE_CALLBACK - 1
98 "priv-user", // PRIVILEGE_USER - 2
99 "priv-operator", // PRIVILEGE_OPERATOR - 3
100 "priv-admin", // PRIVILEGE_ADMIN - 4
101 "priv-custom" // PRIVILEGE_OEM - 5
102};
103
104using namespace phosphor::logging;
105using Json = nlohmann::json;
106
Vernon Mauery16b86932019-05-01 08:36:11 -0700107using PrivAndGroupType = std::variant<std::string, std::vector<std::string>>;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530108
109using NoResource =
Willy Tu523e2d12023-09-05 11:36:48 -0700110 sdbusplus::error::xyz::openbmc_project::user::common::NoResource;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530111
112using InternalFailure =
Willy Tu523e2d12023-09-05 11:36:48 -0700113 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530114
Lei YU4b0ddb62019-01-25 16:43:50 +0800115std::unique_ptr<sdbusplus::bus::match_t> userUpdatedSignal
116 __attribute__((init_priority(101)));
117std::unique_ptr<sdbusplus::bus::match_t> userMgrRenamedSignal
118 __attribute__((init_priority(101)));
119std::unique_ptr<sdbusplus::bus::match_t> userPropertiesSignal
120 __attribute__((init_priority(101)));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530121
Patrick Williams5d82f472022-07-22 19:26:53 -0500122void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530123 const std::string& objPath, const std::string& interface,
124 const std::string& property,
125 const DbusUserPropVariant& value)
126{
127 try
128 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400129 auto method =
130 bus.new_method_call(service.c_str(), objPath.c_str(),
131 dBusPropertiesInterface, setPropertiesMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530132 method.append(interface, property, value);
133 bus.call(method);
134 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500135 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530136 {
George Liu82844ef2024-07-17 17:03:56 +0800137 lg2::error("Failed to set {PROPERTY}, path: {PATH}, "
138 "interface: {INTERFACE}",
139 "PROPERTY", property, "PATH", objPath, "INTERFACE",
140 interface);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530141 throw;
142 }
143}
144
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530145UserAccess& getUserAccessObject()
146{
147 static UserAccess userAccess;
148 return userAccess;
149}
150
151int getUserNameFromPath(const std::string& path, std::string& userName)
152{
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530153 sdbusplus::message::object_path objPath(path);
154 userName.assign(objPath.filename());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530155 return 0;
156}
157
158void userUpdateHelper(UserAccess& usrAccess, const UserUpdateEvent& userEvent,
159 const std::string& userName, const std::string& priv,
160 const bool& enabled, const std::string& newUserName)
161{
162 UsersTbl* userData = usrAccess.getUsersTblPtr();
163 if (userEvent == UserUpdateEvent::userCreated)
164 {
165 if (usrAccess.addUserEntry(userName, priv, enabled) == false)
166 {
167 return;
168 }
169 }
170 else
171 {
172 // user index 0 is reserved, starts with 1
173 size_t usrIndex = 1;
174 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
175 {
176 std::string curName(
177 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
178 ipmiMaxUserName);
179 if (userName == curName)
180 {
181 break; // found the entry
182 }
183 }
184 if (usrIndex > ipmiMaxUsers)
185 {
George Liu82844ef2024-07-17 17:03:56 +0800186 lg2::debug("User not found for signal, user name: {USER_NAME}, "
187 "user event: {USER_EVENT}",
188 "USER_NAME", userName, "USER_EVENT", userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530189 return;
190 }
191 switch (userEvent)
192 {
193 case UserUpdateEvent::userDeleted:
194 {
195 usrAccess.deleteUserIndex(usrIndex);
196 break;
197 }
198 case UserUpdateEvent::userPrivUpdated:
199 {
200 uint8_t userPriv =
201 static_cast<uint8_t>(
202 UserAccess::convertToIPMIPrivilege(priv)) &
203 privMask;
204 // Update all channels privileges, only if it is not equivalent
205 // to getUsrMgmtSyncIndex()
206 if (userData->user[usrIndex]
207 .userPrivAccess[UserAccess::getUsrMgmtSyncIndex()]
208 .privilege != userPriv)
209 {
210 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
211 ++chIndex)
212 {
213 userData->user[usrIndex]
214 .userPrivAccess[chIndex]
215 .privilege = userPriv;
216 }
217 }
218 break;
219 }
220 case UserUpdateEvent::userRenamed:
221 {
222 std::fill(
223 static_cast<uint8_t*>(userData->user[usrIndex].userName),
224 static_cast<uint8_t*>(userData->user[usrIndex].userName) +
225 sizeof(userData->user[usrIndex].userName),
226 0);
227 std::strncpy(
228 reinterpret_cast<char*>(userData->user[usrIndex].userName),
229 newUserName.c_str(), ipmiMaxUserName);
230 ipmiRenameUserEntryPassword(userName, newUserName);
231 break;
232 }
233 case UserUpdateEvent::userStateUpdated:
234 {
235 userData->user[usrIndex].userEnabled = enabled;
236 break;
237 }
238 default:
239 {
George Liu82844ef2024-07-17 17:03:56 +0800240 lg2::error("Unhandled user event: {USER_EVENT}", "USER_EVENT",
241 userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530242 return;
243 }
244 }
245 }
246 usrAccess.writeUserData();
George Liu82844ef2024-07-17 17:03:56 +0800247 lg2::debug("User event handled successfully, user name: {USER_NAME}, "
248 "user event: {USER_EVENT}",
249 "USER_NAME", userName.c_str(), "USER_EVENT", userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530250
251 return;
252}
253
Patrick Williams5d82f472022-07-22 19:26:53 -0500254void userUpdatedSignalHandler(UserAccess& usrAccess, sdbusplus::message_t& msg)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530255{
Patrick Williams5d82f472022-07-22 19:26:53 -0500256 static sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530257 std::string signal = msg.get_member();
Patrick Venture3a697ad2019-08-19 11:12:05 -0700258 std::string userName, priv, newUserName;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530259 std::vector<std::string> groups;
260 bool enabled = false;
261 UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent;
262 if (signal == intfAddedSignal)
263 {
264 DbusUserObjPath objPath;
265 DbusUserObjValue objValue;
266 msg.read(objPath, objValue);
267 getUserNameFromPath(objPath.str, userName);
268 if (usrAccess.getUserObjProperties(objValue, groups, priv, enabled) !=
269 0)
270 {
271 return;
272 }
273 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
274 groups.end())
275 {
276 return;
277 }
278 userEvent = UserUpdateEvent::userCreated;
279 }
280 else if (signal == intfRemovedSignal)
281 {
282 DbusUserObjPath objPath;
283 std::vector<std::string> interfaces;
284 msg.read(objPath, interfaces);
285 getUserNameFromPath(objPath.str, userName);
286 userEvent = UserUpdateEvent::userDeleted;
287 }
288 else if (signal == userRenamedSignal)
289 {
290 msg.read(userName, newUserName);
291 userEvent = UserUpdateEvent::userRenamed;
292 }
293 else if (signal == propertiesChangedSignal)
294 {
295 getUserNameFromPath(msg.get_path(), userName);
296 }
297 else
298 {
George Liu82844ef2024-07-17 17:03:56 +0800299 lg2::error("Unknown user update signal: {SIGNAL}", "SIGNAL", signal);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530300 return;
301 }
302
303 if (signal.empty() || userName.empty() ||
304 (signal == userRenamedSignal && newUserName.empty()))
305 {
George Liu82844ef2024-07-17 17:03:56 +0800306 lg2::error("Invalid inputs received");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530307 return;
308 }
309
310 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
311 userLock{*(usrAccess.userMutex)};
312 usrAccess.checkAndReloadUserData();
313
314 if (signal == propertiesChangedSignal)
315 {
316 std::string intfName;
317 DbusUserObjProperties chProperties;
318 msg.read(intfName, chProperties); // skip reading 3rd argument.
319 for (const auto& prop : chProperties)
320 {
321 userEvent = UserUpdateEvent::reservedEvent;
322 std::string member = prop.first;
323 if (member == userPrivProperty)
324 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700325 priv = std::get<std::string>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530326 userEvent = UserUpdateEvent::userPrivUpdated;
327 }
328 else if (member == userGrpProperty)
329 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700330 groups = std::get<std::vector<std::string>>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530331 userEvent = UserUpdateEvent::userGrpUpdated;
332 }
333 else if (member == userEnabledProperty)
334 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700335 enabled = std::get<bool>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530336 userEvent = UserUpdateEvent::userStateUpdated;
337 }
338 // Process based on event type.
339 if (userEvent == UserUpdateEvent::userGrpUpdated)
340 {
341 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
342 groups.end())
343 {
344 // remove user from ipmi user list.
345 userUpdateHelper(usrAccess, UserUpdateEvent::userDeleted,
346 userName, priv, enabled, newUserName);
347 }
348 else
349 {
350 DbusUserObjProperties properties;
351 try
352 {
353 auto method = bus.new_method_call(
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700354 userMgrService, msg.get_path(),
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530355 dBusPropertiesInterface, getAllPropertiesMethod);
356 method.append(usersInterface);
357 auto reply = bus.call(method);
358 reply.read(properties);
359 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500360 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530361 {
George Liu82844ef2024-07-17 17:03:56 +0800362 lg2::debug("Failed to excute {METHOD}, path: {PATH}",
363 "METHOD", getAllPropertiesMethod, "PATH",
364 msg.get_path());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530365 return;
366 }
367 usrAccess.getUserProperties(properties, groups, priv,
368 enabled);
369 // add user to ipmi user list.
370 userUpdateHelper(usrAccess, UserUpdateEvent::userCreated,
371 userName, priv, enabled, newUserName);
372 }
373 }
374 else if (userEvent != UserUpdateEvent::reservedEvent)
375 {
376 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
377 newUserName);
378 }
379 }
380 }
381 else if (userEvent != UserUpdateEvent::reservedEvent)
382 {
383 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
384 newUserName);
385 }
386 return;
387}
388
389UserAccess::~UserAccess()
390{
391 if (signalHndlrObject)
392 {
393 userUpdatedSignal.reset();
394 userMgrRenamedSignal.reset();
395 userPropertiesSignal.reset();
396 sigHndlrLock.unlock();
397 }
398}
399
400UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection())
401{
402 std::ofstream mutexCleanUpFile;
403 mutexCleanUpFile.open(ipmiMutexCleanupLockFile,
404 std::ofstream::out | std::ofstream::app);
405 if (!mutexCleanUpFile.good())
406 {
George Liu82844ef2024-07-17 17:03:56 +0800407 lg2::debug("Unable to open mutex cleanup file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530408 return;
409 }
410 mutexCleanUpFile.close();
411 mutexCleanupLock = boost::interprocess::file_lock(ipmiMutexCleanupLockFile);
412 if (mutexCleanupLock.try_lock())
413 {
414 boost::interprocess::named_recursive_mutex::remove(ipmiUserMutex);
415 }
416 mutexCleanupLock.lock_sharable();
417 userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>(
418 boost::interprocess::open_or_create, ipmiUserMutex);
419
arun-pmbbe728c2020-01-10 15:18:04 +0530420 cacheUserDataFile();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530421 getSystemPrivAndGroups();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530422}
423
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530424UserInfo* UserAccess::getUserInfo(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530425{
426 checkAndReloadUserData();
427 return &usersTbl.user[userId];
428}
429
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530430void UserAccess::setUserInfo(const uint8_t userId, UserInfo* userInfo)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530431{
432 checkAndReloadUserData();
433 std::copy(reinterpret_cast<uint8_t*>(userInfo),
434 reinterpret_cast<uint8_t*>(userInfo) + sizeof(*userInfo),
435 reinterpret_cast<uint8_t*>(&usersTbl.user[userId]));
436 writeUserData();
437}
438
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530439bool UserAccess::isValidChannel(const uint8_t chNum)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530440{
441 return (chNum < ipmiMaxChannels);
442}
443
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530444bool UserAccess::isValidUserId(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530445{
446 return ((userId <= ipmiMaxUsers) && (userId != reservedUserId));
447}
448
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530449bool UserAccess::isValidPrivilege(const uint8_t priv)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530450{
jayaprakash Mutyala0e2dbee2019-12-26 13:03:04 +0000451 // Callback privilege is deprecated in OpenBMC
Alexander Filippovfc24fa52022-02-01 14:57:59 +0300452 return isValidPrivLimit(priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530453}
454
455uint8_t UserAccess::getUsrMgmtSyncIndex()
456{
Johnathan Manteyfd61fc32021-04-08 11:05:38 -0700457 // Identify the IPMI channel used to assign system user privilege levels
458 // in phosphor-user-manager. The default value is IPMI Channel 1. To
459 // assign a different channel add:
460 // "is_management_nic" : true
461 // into the channel_config.json file describing the assignment of the IPMI
462 // channels. It is only necessary to add the string above to ONE record in
463 // the channel_config.json file. All other records will be automatically
464 // assigned a "false" value.
465 return getChannelConfigObject().getManagementNICID();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530466}
467
468CommandPrivilege UserAccess::convertToIPMIPrivilege(const std::string& value)
469{
470 auto iter = std::find(ipmiPrivIndex.begin(), ipmiPrivIndex.end(), value);
471 if (iter == ipmiPrivIndex.end())
472 {
473 if (value == "")
474 {
475 return static_cast<CommandPrivilege>(privNoAccess);
476 }
George Liu82844ef2024-07-17 17:03:56 +0800477 lg2::error("Error in converting to IPMI privilege: {PRIV}", "PRIV",
478 value);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530479 throw std::out_of_range("Out of range - convertToIPMIPrivilege");
480 }
481 else
482 {
483 return static_cast<CommandPrivilege>(
484 std::distance(ipmiPrivIndex.begin(), iter));
485 }
486}
487
488std::string UserAccess::convertToSystemPrivilege(const CommandPrivilege& value)
489{
490 if (value == static_cast<CommandPrivilege>(privNoAccess))
491 {
492 return "";
493 }
494 try
495 {
496 return ipmiPrivIndex.at(value);
497 }
498 catch (const std::out_of_range& e)
499 {
George Liu82844ef2024-07-17 17:03:56 +0800500 lg2::error("Error in converting to system privilege: {PRIV}", "PRIV",
501 value);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530502 throw std::out_of_range("Out of range - convertToSystemPrivilege");
503 }
504}
505
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000506bool UserAccess::isValidUserName(const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530507{
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000508 if (userName.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530509 {
George Liu82844ef2024-07-17 17:03:56 +0800510 lg2::error("userName is empty");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530511 return false;
512 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530513 if (!std::regex_match(userName.c_str(),
nichanghao.nch0c96fdf2024-01-17 22:13:35 +0800514 std::regex("[a-zA-Z_][a-zA-Z_0-9]*")))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530515 {
George Liu82844ef2024-07-17 17:03:56 +0800516 lg2::error("Unsupported characters in user name");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530517 return false;
518 }
519 if (userName == "root")
520 {
George Liu82844ef2024-07-17 17:03:56 +0800521 lg2::error("Invalid user name - root");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530522 return false;
523 }
524 std::map<DbusUserObjPath, DbusUserObjValue> properties;
525 try
526 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700527 auto method =
528 bus.new_method_call(userMgrService, userMgrObjBasePath,
529 dBusObjManager, getManagedObjectsMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530530 auto reply = bus.call(method);
531 reply.read(properties);
532 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500533 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530534 {
George Liu82844ef2024-07-17 17:03:56 +0800535 lg2::error("Failed to excute {METHOD}, path: {PATH}", "METHOD",
536 getManagedObjectsMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530537 return false;
538 }
539
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530540 sdbusplus::message::object_path tempUserPath(userObjBasePath);
541 tempUserPath /= userName;
542 std::string usersPath(tempUserPath);
543
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530544 if (properties.find(usersPath) != properties.end())
545 {
George Liu82844ef2024-07-17 17:03:56 +0800546 lg2::debug("Username {USER_NAME} already exists", "USER_NAME",
547 userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530548 return false;
549 }
550
551 return true;
552}
553
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530554/** @brief Information exchanged by pam module and application.
555 *
556 * @param[in] numMsg - length of the array of pointers,msg.
557 *
558 * @param[in] msg - pointer to an array of pointers to pam_message structure
559 *
560 * @param[out] resp - struct pam response array
561 *
562 * @param[in] appdataPtr - member of pam_conv structure
563 *
564 * @return the response in pam response structure.
565 */
566
567static int pamFunctionConversation(int numMsg, const struct pam_message** msg,
568 struct pam_response** resp, void* appdataPtr)
569{
570 if (appdataPtr == nullptr)
571 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530572 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530573 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530574
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530575 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
576 {
577 return PAM_CONV_ERR;
578 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530579
580 for (int i = 0; i < numMsg; ++i)
581 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530582 /* Ignore all PAM messages except prompting for hidden input */
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530583 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
584 {
585 continue;
586 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530587
588 /* Assume PAM is only prompting for the password as hidden input */
589 /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */
590
591 char* appPass = reinterpret_cast<char*>(appdataPtr);
592 size_t appPassSize = std::strlen(appPass);
593
594 if (appPassSize >= PAM_MAX_RESP_SIZE)
595 {
596 return PAM_CONV_ERR;
597 }
598
599 char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1));
600 if (pass == nullptr)
601 {
602 return PAM_BUF_ERR;
603 }
604
Patrick Williams1318a5e2024-08-16 15:19:54 -0400605 void* ptr =
606 calloc(static_cast<size_t>(numMsg), sizeof(struct pam_response));
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530607 if (ptr == nullptr)
608 {
609 free(pass);
610 return PAM_BUF_ERR;
611 }
612
613 std::strncpy(pass, appPass, appPassSize + 1);
614
615 *resp = reinterpret_cast<pam_response*>(ptr);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530616 resp[i]->resp = pass;
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530617
618 return PAM_SUCCESS;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530619 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530620
621 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530622}
623
624/** @brief Updating the PAM password
625 *
626 * @param[in] username - username in string
627 *
628 * @param[in] password - new password in string
629 *
630 * @return status
631 */
632
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000633int pamUpdatePasswd(const char* username, const char* password)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530634{
635 const struct pam_conv localConversation = {pamFunctionConversation,
636 const_cast<char*>(password)};
Jayanth Othayotha6fb32d2024-12-15 10:55:22 -0600637 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530638
Patrick Williams1318a5e2024-08-16 15:19:54 -0400639 int retval =
640 pam_start("passwd", username, &localConversation, &localAuthHandle);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530641
642 if (retval != PAM_SUCCESS)
643 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000644 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530645 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000646
647 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
648 if (retval != PAM_SUCCESS)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530649 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000650 pam_end(localAuthHandle, retval);
651 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530652 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000653
654 return pam_end(localAuthHandle, PAM_SUCCESS);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530655}
656
Ayushi Smriti02650d52019-05-15 11:59:09 +0000657bool pamUserCheckAuthenticate(std::string_view username,
658 std::string_view password)
659{
660 const struct pam_conv localConversation = {
661 pamFunctionConversation, const_cast<char*>(password.data())};
662
Jayanth Othayotha6fb32d2024-12-15 10:55:22 -0600663 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ayushi Smriti02650d52019-05-15 11:59:09 +0000664
665 if (pam_start("dropbear", username.data(), &localConversation,
666 &localAuthHandle) != PAM_SUCCESS)
667 {
George Liu82844ef2024-07-17 17:03:56 +0800668 lg2::error("User Authentication Failure");
Ayushi Smriti02650d52019-05-15 11:59:09 +0000669 return false;
670 }
671
672 int retval = pam_authenticate(localAuthHandle,
673 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
674
675 if (retval != PAM_SUCCESS)
676 {
George Liu82844ef2024-07-17 17:03:56 +0800677 lg2::debug("pam_authenticate returned failure: {ERROR}", "ERROR",
678 retval);
Ayushi Smriti02650d52019-05-15 11:59:09 +0000679
680 pam_end(localAuthHandle, retval);
681 return false;
682 }
683
684 if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
685 PAM_SUCCESS)
686 {
687 pam_end(localAuthHandle, PAM_SUCCESS);
688 return false;
689 }
690
691 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
692 {
693 return false;
694 }
695 return true;
696}
697
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000698Cc UserAccess::setSpecialUserPassword(const std::string& userName,
Vernon Mauery1e22a0f2021-07-30 13:36:54 -0700699 const SecureString& userPassword)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530700{
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000701 if (pamUpdatePasswd(userName.c_str(), userPassword.c_str()) != PAM_SUCCESS)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530702 {
George Liu82844ef2024-07-17 17:03:56 +0800703 lg2::debug("Failed to update password");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000704 return ccUnspecifiedError;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530705 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000706 return ccSuccess;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530707}
708
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000709Cc UserAccess::setUserPassword(const uint8_t userId, const char* userPassword)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530710{
711 std::string userName;
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000712 if (ipmiUserGetUserName(userId, userName) != ccSuccess)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530713 {
George Liu82844ef2024-07-17 17:03:56 +0800714 lg2::debug("User Name not found, user Id: {USER_ID}", "USER_ID",
715 userId);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000716 return ccParmOutOfRange;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530717 }
Snehalatha Venkatesh61024d72021-04-08 16:24:39 +0000718
719 ipmi::SecureString passwd;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530720 passwd.assign(reinterpret_cast<const char*>(userPassword), 0,
721 maxIpmi20PasswordSize);
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000722 int retval = pamUpdatePasswd(userName.c_str(), passwd.c_str());
723
724 switch (retval)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530725 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000726 case PAM_SUCCESS:
727 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000728 return ccSuccess;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000729 }
730 case PAM_AUTHTOK_ERR:
731 {
George Liu82844ef2024-07-17 17:03:56 +0800732 lg2::debug("Bad authentication token");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000733 return ccInvalidFieldRequest;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000734 }
735 default:
736 {
George Liu82844ef2024-07-17 17:03:56 +0800737 lg2::debug("Failed to update password, user Id: {USER_ID}",
738 "USER_ID", userId);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000739 return ccUnspecifiedError;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000740 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530741 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530742}
743
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000744Cc UserAccess::setUserEnabledState(const uint8_t userId,
745 const bool& enabledState)
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530746{
747 if (!isValidUserId(userId))
748 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000749 return ccParmOutOfRange;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530750 }
751 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
752 userLock{*userMutex};
753 UserInfo* userInfo = getUserInfo(userId);
754 std::string userName;
755 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
756 ipmiMaxUserName);
757 if (userName.empty())
758 {
George Liu82844ef2024-07-17 17:03:56 +0800759 lg2::debug("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000760 return ccUnspecifiedError;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530761 }
762 if (userInfo->userEnabled != enabledState)
763 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530764 sdbusplus::message::object_path tempUserPath(userObjBasePath);
765 tempUserPath /= userName;
766 std::string userPath(tempUserPath);
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700767 setDbusProperty(bus, userMgrService, userPath, usersInterface,
Patrick Venture99d1ba02019-02-21 15:11:24 -0800768 userEnabledProperty, enabledState);
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530769 userInfo->userEnabled = enabledState;
770 try
771 {
772 writeUserData();
773 }
774 catch (const std::exception& e)
775 {
George Liu82844ef2024-07-17 17:03:56 +0800776 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000777 return ccUnspecifiedError;
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530778 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530779 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000780 return ccSuccess;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530781}
782
Patrick Williams1318a5e2024-08-16 15:19:54 -0400783Cc UserAccess::setUserPayloadAccess(
784 const uint8_t chNum, const uint8_t operation, const uint8_t userId,
785 const PayloadAccess& payloadAccess)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000786{
787 constexpr uint8_t enable = 0x0;
788 constexpr uint8_t disable = 0x1;
789
790 if (!isValidChannel(chNum))
791 {
George Liua0e545d2025-01-24 09:50:22 +0800792 lg2::debug(
793 "Set user payload access - Invalid channel request: {CHANNEL}",
794 "CHANNEL", chNum);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000795 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000796 }
797 if (!isValidUserId(userId))
798 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000799 return ccParmOutOfRange;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000800 }
801 if (operation != enable && operation != disable)
802 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000803 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000804 }
805 // Check operation & payloadAccess if required.
806 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
807 userLock{*userMutex};
808 UserInfo* userInfo = getUserInfo(userId);
809
810 if (operation == enable)
811 {
812 userInfo->payloadAccess[chNum].stdPayloadEnables1 |=
813 payloadAccess.stdPayloadEnables1;
814
815 userInfo->payloadAccess[chNum].oemPayloadEnables1 |=
816 payloadAccess.oemPayloadEnables1;
817 }
818 else
819 {
820 userInfo->payloadAccess[chNum].stdPayloadEnables1 &=
821 ~(payloadAccess.stdPayloadEnables1);
822
823 userInfo->payloadAccess[chNum].oemPayloadEnables1 &=
824 ~(payloadAccess.oemPayloadEnables1);
825 }
826
827 try
828 {
829 writeUserData();
830 }
831 catch (const std::exception& e)
832 {
George Liu82844ef2024-07-17 17:03:56 +0800833 lg2::error("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000834 return ccUnspecifiedError;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000835 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000836 return ccSuccess;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000837}
838
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000839Cc UserAccess::setUserPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
840 const UserPrivAccess& privAccess,
841 const bool& otherPrivUpdates)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530842{
843 if (!isValidChannel(chNum))
844 {
George Liua0e545d2025-01-24 09:50:22 +0800845 lg2::debug(
846 "Set user privilege access - Invalid channel request: {CHANNEL}",
847 "CHANNEL", chNum);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000848 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530849 }
850 if (!isValidUserId(userId))
851 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000852 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530853 }
854 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
855 userLock{*userMutex};
856 UserInfo* userInfo = getUserInfo(userId);
857 std::string userName;
858 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
859 ipmiMaxUserName);
860 if (userName.empty())
861 {
George Liu82844ef2024-07-17 17:03:56 +0800862 lg2::debug("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000863 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530864 }
865 std::string priv = convertToSystemPrivilege(
866 static_cast<CommandPrivilege>(privAccess.privilege));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530867 uint8_t syncIndex = getUsrMgmtSyncIndex();
868 if (chNum == syncIndex &&
869 privAccess.privilege != userInfo->userPrivAccess[syncIndex].privilege)
870 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530871 sdbusplus::message::object_path tempUserPath(userObjBasePath);
872 tempUserPath /= userName;
873 std::string userPath(tempUserPath);
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700874 setDbusProperty(bus, userMgrService, userPath, usersInterface,
Patrick Venture99d1ba02019-02-21 15:11:24 -0800875 userPrivProperty, priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530876 }
877 userInfo->userPrivAccess[chNum].privilege = privAccess.privilege;
878
879 if (otherPrivUpdates)
880 {
881 userInfo->userPrivAccess[chNum].ipmiEnabled = privAccess.ipmiEnabled;
882 userInfo->userPrivAccess[chNum].linkAuthEnabled =
883 privAccess.linkAuthEnabled;
884 userInfo->userPrivAccess[chNum].accessCallback =
885 privAccess.accessCallback;
886 }
887 try
888 {
889 writeUserData();
890 }
891 catch (const std::exception& e)
892 {
George Liu82844ef2024-07-17 17:03:56 +0800893 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000894 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530895 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000896 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530897}
898
899uint8_t UserAccess::getUserId(const std::string& userName)
900{
901 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
902 userLock{*userMutex};
903 checkAndReloadUserData();
904 // user index 0 is reserved, starts with 1
905 size_t usrIndex = 1;
906 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
907 {
908 std::string curName(
909 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
910 ipmiMaxUserName);
911 if (userName == curName)
912 {
913 break; // found the entry
914 }
915 }
916 if (usrIndex > ipmiMaxUsers)
917 {
George Liu82844ef2024-07-17 17:03:56 +0800918 lg2::debug("Username {USER_NAME} not found", "USER_NAME", userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530919 return invalidUserId;
920 }
921
922 return usrIndex;
923}
924
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000925Cc UserAccess::getUserName(const uint8_t userId, std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530926{
927 if (!isValidUserId(userId))
928 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000929 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530930 }
931 UserInfo* userInfo = getUserInfo(userId);
932 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
933 ipmiMaxUserName);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000934 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530935}
936
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530937bool UserAccess::isIpmiInAvailableGroupList()
938{
939 if (std::find(availableGroups.begin(), availableGroups.end(),
940 ipmiGrpName) != availableGroups.end())
941 {
942 return true;
943 }
944 if (availableGroups.empty())
945 {
946 // available groups shouldn't be empty, re-query
947 getSystemPrivAndGroups();
948 if (std::find(availableGroups.begin(), availableGroups.end(),
949 ipmiGrpName) != availableGroups.end())
950 {
951 return true;
952 }
953 }
954 return false;
955}
956
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000957Cc UserAccess::setUserName(const uint8_t userId, const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530958{
959 if (!isValidUserId(userId))
960 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000961 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530962 }
963
964 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
965 userLock{*userMutex};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530966 std::string oldUser;
967 getUserName(userId, oldUser);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530968
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000969 if (oldUser == userName)
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +0530970 {
971 // requesting to set the same user name, return success.
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000972 return ccSuccess;
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +0530973 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000974
975 bool validUser = isValidUserName(userName);
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +0530976 UserInfo* userInfo = getUserInfo(userId);
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000977 if (userName.empty() && !oldUser.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530978 {
979 // Delete existing user
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530980 sdbusplus::message::object_path tempUserPath(userObjBasePath);
981 tempUserPath /= oldUser;
982 std::string userPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530983 try
984 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700985 auto method =
986 bus.new_method_call(userMgrService, userPath.c_str(),
987 deleteUserInterface, deleteUserMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530988 auto reply = bus.call(method);
989 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500990 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530991 {
George Liu82844ef2024-07-17 17:03:56 +0800992 lg2::debug("Failed to excute {METHOD}, path:{PATH}", "METHOD",
993 deleteUserMethod, "PATH", userPath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000994 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530995 }
Richard Marian Thomaiyar02710bb2018-11-28 20:42:25 +0530996 deleteUserIndex(userId);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530997 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000998 else if (oldUser.empty() && !userName.empty() && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530999 {
1000 try
1001 {
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301002 if (!isIpmiInAvailableGroupList())
1003 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001004 return ccUnspecifiedError;
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301005 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301006 // Create new user
Khang D Nguyen078aa6a2025-03-06 00:03:42 +07001007 auto method =
1008 bus.new_method_call(userMgrService, userMgrObjBasePath,
1009 userMgrInterface, createUserMethod);
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001010 method.append(userName.c_str(), availableGroups,
1011 ipmiPrivIndex[PRIVILEGE_USER], false);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301012 auto reply = bus.call(method);
1013 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001014 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301015 {
George Liu82844ef2024-07-17 17:03:56 +08001016 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1017 createUserMethod, "PATH", userMgrObjBasePath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001018 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301019 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001020
1021 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1022 std::memcpy(userInfo->userName,
1023 static_cast<const void*>(userName.data()), userName.size());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301024 userInfo->userInSystem = true;
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001025 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1026 {
1027 userInfo->userPrivAccess[chIndex].privilege =
1028 static_cast<uint8_t>(PRIVILEGE_USER);
1029 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301030 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001031 else if (oldUser != userName && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301032 {
1033 try
1034 {
1035 // User rename
Khang D Nguyen078aa6a2025-03-06 00:03:42 +07001036 auto method =
1037 bus.new_method_call(userMgrService, userMgrObjBasePath,
1038 userMgrInterface, renameUserMethod);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001039 method.append(oldUser.c_str(), userName.c_str());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301040 auto reply = bus.call(method);
1041 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001042 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301043 {
George Liu82844ef2024-07-17 17:03:56 +08001044 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1045 renameUserMethod, "PATH", userMgrObjBasePath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001046 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301047 }
1048 std::fill(static_cast<uint8_t*>(userInfo->userName),
1049 static_cast<uint8_t*>(userInfo->userName) +
1050 sizeof(userInfo->userName),
1051 0);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001052
1053 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1054 std::memcpy(userInfo->userName,
1055 static_cast<const void*>(userName.data()), userName.size());
1056
1057 ipmiRenameUserEntryPassword(oldUser, userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301058 userInfo->userInSystem = true;
1059 }
1060 else if (!validUser)
1061 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001062 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301063 }
1064 try
1065 {
1066 writeUserData();
1067 }
1068 catch (const std::exception& e)
1069 {
George Liu82844ef2024-07-17 17:03:56 +08001070 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001071 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301072 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001073 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301074}
1075
1076static constexpr const char* jsonUserName = "user_name";
1077static constexpr const char* jsonPriv = "privilege";
1078static constexpr const char* jsonIpmiEnabled = "ipmi_enabled";
1079static constexpr const char* jsonLinkAuthEnabled = "link_auth_enabled";
1080static constexpr const char* jsonAccCallbk = "access_callback";
1081static constexpr const char* jsonUserEnabled = "user_enabled";
1082static constexpr const char* jsonUserInSys = "user_in_system";
1083static constexpr const char* jsonFixedUser = "fixed_user_name";
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001084static constexpr const char* payloadEnabledStr = "payload_enabled";
1085static constexpr const char* stdPayloadStr = "std_payload";
1086static constexpr const char* oemPayloadStr = "OEM_payload";
1087
1088/** @brief to construct a JSON object from the given payload access details.
1089 *
1090 * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. (input)
1091 * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. (input)
1092 *
1093 * @details Sample output JSON object format :
1094 * "payload_enabled":{
1095 * "OEM_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1096 * "OEM_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1097 * "OEM_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1098 * "OEM_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1099 * "OEM_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1100 * "OEM_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1101 * "OEM_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1102 * "OEM_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1103 * "std_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1104 * "std_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1105 * "std_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1106 * "std_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1107 * "std_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1108 * "std_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1109 * "std_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1110 * "std_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1111 * }
1112 */
1113static const Json constructJsonPayloadEnables(
1114 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1115 stdPayload,
1116 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1117 oemPayload)
1118{
1119 Json jsonPayloadEnabled;
1120
1121 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1122 {
1123 std::ostringstream stdPayloadStream;
1124 std::ostringstream oemPayloadStream;
1125
1126 stdPayloadStream << stdPayloadStr << payloadNum;
1127 oemPayloadStream << oemPayloadStr << payloadNum;
1128
1129 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1130 stdPayloadStream.str(), stdPayload[payloadNum]));
1131
1132 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1133 oemPayloadStream.str(), oemPayload[payloadNum]));
1134 }
1135 return jsonPayloadEnabled;
1136}
1137
1138void UserAccess::readPayloadAccessFromUserInfo(
1139 const UserInfo& userInfo,
1140 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& stdPayload,
1141 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& oemPayload)
1142{
1143 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1144 {
1145 for (auto chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1146 {
1147 stdPayload[payloadNum][chIndex] =
1148 userInfo.payloadAccess[chIndex].stdPayloadEnables1[payloadNum];
1149
1150 oemPayload[payloadNum][chIndex] =
1151 userInfo.payloadAccess[chIndex].oemPayloadEnables1[payloadNum];
1152 }
1153 }
1154}
1155
1156void UserAccess::updatePayloadAccessInUserInfo(
1157 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1158 stdPayload,
Willy Tu11d68892022-01-20 10:37:34 -08001159 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&,
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001160 UserInfo& userInfo)
1161{
1162 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1163 {
1164 // Ensure that reserved/unsupported payloads are marked to zero.
1165 userInfo.payloadAccess[chIndex].stdPayloadEnables1.reset();
1166 userInfo.payloadAccess[chIndex].oemPayloadEnables1.reset();
1167 userInfo.payloadAccess[chIndex].stdPayloadEnables2Reserved.reset();
1168 userInfo.payloadAccess[chIndex].oemPayloadEnables2Reserved.reset();
1169 // Update SOL status as it is the only supported payload currently.
1170 userInfo.payloadAccess[chIndex]
1171 .stdPayloadEnables1[static_cast<uint8_t>(ipmi::PayloadType::SOL)] =
1172 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)][chIndex];
1173 }
1174}
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301175
1176void UserAccess::readUserData()
1177{
1178 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1179 userLock{*userMutex};
1180
1181 std::ifstream iUsrData(ipmiUserDataFile, std::ios::in | std::ios::binary);
1182 if (!iUsrData.good())
1183 {
George Liu82844ef2024-07-17 17:03:56 +08001184 lg2::error("Error in reading IPMI user data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301185 throw std::ios_base::failure("Error opening IPMI user data file");
1186 }
1187
1188 Json jsonUsersTbl = Json::array();
1189 jsonUsersTbl = Json::parse(iUsrData, nullptr, false);
1190
1191 if (jsonUsersTbl.size() != ipmiMaxUsers)
1192 {
George Liu82844ef2024-07-17 17:03:56 +08001193 lg2::error("Error in reading IPMI user data file - User count issues");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301194 throw std::runtime_error(
1195 "Corrupted IPMI user data file - invalid user count");
1196 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001197
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301198 // user index 0 is reserved, starts with 1
1199 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1200 {
1201 Json userInfo = jsonUsersTbl[usrIndex - 1]; // json array starts with 0.
1202 if (userInfo.is_null())
1203 {
George Liu82844ef2024-07-17 17:03:56 +08001204 lg2::error("Error in reading IPMI user data file - "
1205 "user info corrupted");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301206 throw std::runtime_error(
1207 "Corrupted IPMI user data file - invalid user info");
1208 }
1209 std::string userName = userInfo[jsonUserName].get<std::string>();
1210 std::strncpy(reinterpret_cast<char*>(usersTbl.user[usrIndex].userName),
1211 userName.c_str(), ipmiMaxUserName);
1212
1213 std::vector<std::string> privilege =
1214 userInfo[jsonPriv].get<std::vector<std::string>>();
1215 std::vector<bool> ipmiEnabled =
1216 userInfo[jsonIpmiEnabled].get<std::vector<bool>>();
1217 std::vector<bool> linkAuthEnabled =
1218 userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
1219 std::vector<bool> accessCallback =
1220 userInfo[jsonAccCallbk].get<std::vector<bool>>();
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001221
1222 // Payload Enables Processing.
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001223 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1224 stdPayload = {};
1225 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1226 oemPayload = {};
1227 try
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001228 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001229 const auto jsonPayloadEnabled = userInfo.at(payloadEnabledStr);
1230 for (auto payloadNum = 0; payloadNum < payloadsPerByte;
1231 payloadNum++)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001232 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001233 std::ostringstream stdPayloadStream;
1234 std::ostringstream oemPayloadStream;
1235
1236 stdPayloadStream << stdPayloadStr << payloadNum;
1237 oemPayloadStream << oemPayloadStr << payloadNum;
1238
1239 stdPayload[payloadNum] =
1240 jsonPayloadEnabled[stdPayloadStream.str()]
1241 .get<std::array<bool, ipmiMaxChannels>>();
1242 oemPayload[payloadNum] =
1243 jsonPayloadEnabled[oemPayloadStream.str()]
1244 .get<std::array<bool, ipmiMaxChannels>>();
1245
1246 if (stdPayload[payloadNum].size() != ipmiMaxChannels ||
1247 oemPayload[payloadNum].size() != ipmiMaxChannels)
1248 {
George Liu82844ef2024-07-17 17:03:56 +08001249 lg2::error("Error in reading IPMI user data file - "
1250 "payload properties corrupted");
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001251 throw std::runtime_error(
1252 "Corrupted IPMI user data file - payload properties");
1253 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001254 }
1255 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001256 catch (const Json::out_of_range& e)
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001257 {
1258 // Key not found in 'userInfo'; possibly an old JSON file. Use
1259 // default values for all payloads, and SOL payload default is true.
1260 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)].fill(true);
1261 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001262
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301263 if (privilege.size() != ipmiMaxChannels ||
1264 ipmiEnabled.size() != ipmiMaxChannels ||
1265 linkAuthEnabled.size() != ipmiMaxChannels ||
1266 accessCallback.size() != ipmiMaxChannels)
1267 {
George Liu82844ef2024-07-17 17:03:56 +08001268 lg2::error("Error in reading IPMI user data file - "
1269 "properties corrupted");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301270 throw std::runtime_error(
1271 "Corrupted IPMI user data file - properties");
1272 }
1273 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1274 {
1275 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege =
1276 static_cast<uint8_t>(
1277 convertToIPMIPrivilege(privilege[chIndex]));
1278 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled =
1279 ipmiEnabled[chIndex];
1280 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled =
1281 linkAuthEnabled[chIndex];
1282 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
1283 accessCallback[chIndex];
1284 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001285 updatePayloadAccessInUserInfo(stdPayload, oemPayload,
1286 usersTbl.user[usrIndex]);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301287 usersTbl.user[usrIndex].userEnabled =
1288 userInfo[jsonUserEnabled].get<bool>();
1289 usersTbl.user[usrIndex].userInSystem =
1290 userInfo[jsonUserInSys].get<bool>();
1291 usersTbl.user[usrIndex].fixedUserName =
1292 userInfo[jsonFixedUser].get<bool>();
1293 }
1294
George Liu82844ef2024-07-17 17:03:56 +08001295 lg2::debug("User data read from IPMI data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301296 iUsrData.close();
1297 // Update the timestamp
1298 fileLastUpdatedTime = getUpdatedFileTime();
1299 return;
1300}
1301
1302void UserAccess::writeUserData()
1303{
1304 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1305 userLock{*userMutex};
1306
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301307 Json jsonUsersTbl = Json::array();
1308 // user index 0 is reserved, starts with 1
1309 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1310 {
1311 Json jsonUserInfo;
1312 jsonUserInfo[jsonUserName] = std::string(
1313 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
1314 ipmiMaxUserName);
1315 std::vector<std::string> privilege(ipmiMaxChannels);
1316 std::vector<bool> ipmiEnabled(ipmiMaxChannels);
1317 std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
1318 std::vector<bool> accessCallback(ipmiMaxChannels);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001319
1320 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1321 stdPayload;
1322 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1323 oemPayload;
1324
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301325 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1326 {
1327 privilege[chIndex] =
1328 convertToSystemPrivilege(static_cast<CommandPrivilege>(
1329 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege));
1330 ipmiEnabled[chIndex] =
1331 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled;
1332 linkAuthEnabled[chIndex] =
1333 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled;
1334 accessCallback[chIndex] =
1335 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback;
1336 }
1337 jsonUserInfo[jsonPriv] = privilege;
1338 jsonUserInfo[jsonIpmiEnabled] = ipmiEnabled;
1339 jsonUserInfo[jsonLinkAuthEnabled] = linkAuthEnabled;
1340 jsonUserInfo[jsonAccCallbk] = accessCallback;
1341 jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
1342 jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
1343 jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001344
1345 readPayloadAccessFromUserInfo(usersTbl.user[usrIndex], stdPayload,
1346 oemPayload);
Patrick Williams1318a5e2024-08-16 15:19:54 -04001347 Json jsonPayloadEnabledInfo =
1348 constructJsonPayloadEnables(stdPayload, oemPayload);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001349 jsonUserInfo[payloadEnabledStr] = jsonPayloadEnabledInfo;
1350
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301351 jsonUsersTbl.push_back(jsonUserInfo);
1352 }
1353
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301354 static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
1355 int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
1356 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1357 if (fd < 0)
1358 {
George Liu82844ef2024-07-17 17:03:56 +08001359 lg2::error("Error in creating temporary IPMI user data file");
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301360 throw std::ios_base::failure(
1361 "Error in creating temporary IPMI user data file");
1362 }
1363 const auto& writeStr = jsonUsersTbl.dump();
1364 if (write(fd, writeStr.c_str(), writeStr.size()) !=
1365 static_cast<ssize_t>(writeStr.size()))
1366 {
1367 close(fd);
George Liu82844ef2024-07-17 17:03:56 +08001368 lg2::error("Error in writing temporary IPMI user data file");
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301369 throw std::ios_base::failure(
1370 "Error in writing temporary IPMI user data file");
1371 }
1372 close(fd);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301373
1374 if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
1375 {
George Liu82844ef2024-07-17 17:03:56 +08001376 lg2::error("Error in renaming temporary IPMI user data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301377 throw std::runtime_error("Error in renaming IPMI user data file");
1378 }
1379 // Update the timestamp
1380 fileLastUpdatedTime = getUpdatedFileTime();
1381 return;
1382}
1383
1384bool UserAccess::addUserEntry(const std::string& userName,
1385 const std::string& sysPriv, const bool& enabled)
1386{
1387 UsersTbl* userData = getUsersTblPtr();
1388 size_t freeIndex = 0xFF;
1389 // user index 0 is reserved, starts with 1
1390 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1391 {
1392 std::string curName(
1393 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
1394 ipmiMaxUserName);
1395 if (userName == curName)
1396 {
George Liu82844ef2024-07-17 17:03:56 +08001397 lg2::debug("Username {USER_NAME} exists", "USER_NAME", userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301398 return false; // user name exists.
1399 }
1400
1401 if ((!userData->user[usrIndex].userInSystem) &&
1402 (userData->user[usrIndex].userName[0] == '\0') &&
1403 (freeIndex == 0xFF))
1404 {
1405 freeIndex = usrIndex;
1406 }
1407 }
1408 if (freeIndex == 0xFF)
1409 {
George Liu82844ef2024-07-17 17:03:56 +08001410 lg2::error("No empty slots found");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301411 return false;
1412 }
1413 std::strncpy(reinterpret_cast<char*>(userData->user[freeIndex].userName),
1414 userName.c_str(), ipmiMaxUserName);
1415 uint8_t priv =
1416 static_cast<uint8_t>(UserAccess::convertToIPMIPrivilege(sysPriv)) &
1417 privMask;
1418 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1419 {
1420 userData->user[freeIndex].userPrivAccess[chIndex].privilege = priv;
1421 userData->user[freeIndex].userPrivAccess[chIndex].ipmiEnabled = true;
1422 userData->user[freeIndex].userPrivAccess[chIndex].linkAuthEnabled =
1423 true;
1424 userData->user[freeIndex].userPrivAccess[chIndex].accessCallback = true;
1425 }
1426 userData->user[freeIndex].userInSystem = true;
1427 userData->user[freeIndex].userEnabled = enabled;
1428
1429 return true;
1430}
1431
1432void UserAccess::deleteUserIndex(const size_t& usrIdx)
1433{
1434 UsersTbl* userData = getUsersTblPtr();
1435
1436 std::string userName(
1437 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1438 ipmiMaxUserName);
1439 ipmiClearUserEntryPassword(userName);
1440 std::fill(static_cast<uint8_t*>(userData->user[usrIdx].userName),
1441 static_cast<uint8_t*>(userData->user[usrIdx].userName) +
1442 sizeof(userData->user[usrIdx].userName),
1443 0);
1444 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1445 {
1446 userData->user[usrIdx].userPrivAccess[chIndex].privilege = privNoAccess;
1447 userData->user[usrIdx].userPrivAccess[chIndex].ipmiEnabled = false;
1448 userData->user[usrIdx].userPrivAccess[chIndex].linkAuthEnabled = false;
1449 userData->user[usrIdx].userPrivAccess[chIndex].accessCallback = false;
1450 }
1451 userData->user[usrIdx].userInSystem = false;
1452 userData->user[usrIdx].userEnabled = false;
1453 return;
1454}
1455
1456void UserAccess::checkAndReloadUserData()
1457{
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001458 std::timespec updateTime = getUpdatedFileTime();
1459 if ((updateTime.tv_sec != fileLastUpdatedTime.tv_sec ||
1460 updateTime.tv_nsec != fileLastUpdatedTime.tv_nsec) ||
1461 (updateTime.tv_sec == 0 && updateTime.tv_nsec == 0))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301462 {
1463 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1464 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1465 readUserData();
1466 }
1467 return;
1468}
1469
1470UsersTbl* UserAccess::getUsersTblPtr()
1471{
1472 // reload data before using it.
1473 checkAndReloadUserData();
1474 return &usersTbl;
1475}
1476
1477void UserAccess::getSystemPrivAndGroups()
1478{
1479 std::map<std::string, PrivAndGroupType> properties;
1480 try
1481 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +07001482 auto method = bus.new_method_call(userMgrService, userMgrObjBasePath,
1483 dBusPropertiesInterface,
1484 getAllPropertiesMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301485 method.append(userMgrInterface);
1486
1487 auto reply = bus.call(method);
1488 reply.read(properties);
1489 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001490 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301491 {
George Liu82844ef2024-07-17 17:03:56 +08001492 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1493 getAllPropertiesMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301494 return;
1495 }
1496 for (const auto& t : properties)
1497 {
1498 auto key = t.first;
1499 if (key == allPrivProperty)
1500 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001501 availablePrivileges = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301502 }
1503 else if (key == allGrpProperty)
1504 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001505 availableGroups = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301506 }
1507 }
1508 // TODO: Implement Supported Privilege & Groups verification logic
1509 return;
1510}
1511
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001512std::timespec UserAccess::getUpdatedFileTime()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301513{
1514 struct stat fileStat;
1515 if (stat(ipmiUserDataFile, &fileStat) != 0)
1516 {
George Liu82844ef2024-07-17 17:03:56 +08001517 lg2::debug("Error in getting last updated time stamp");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001518 return std::timespec{0, 0};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301519 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001520 return fileStat.st_mtim;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301521}
1522
1523void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
1524 std::vector<std::string>& usrGrps,
1525 std::string& usrPriv, bool& usrEnabled)
1526{
1527 for (const auto& t : properties)
1528 {
1529 std::string key = t.first;
1530 if (key == userPrivProperty)
1531 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001532 usrPriv = std::get<std::string>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301533 }
1534 else if (key == userGrpProperty)
1535 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001536 usrGrps = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301537 }
1538 else if (key == userEnabledProperty)
1539 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001540 usrEnabled = std::get<bool>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301541 }
1542 }
1543 return;
1544}
1545
1546int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
1547 std::vector<std::string>& usrGrps,
1548 std::string& usrPriv, bool& usrEnabled)
1549{
1550 auto usrObj = userObjs.find(usersInterface);
1551 if (usrObj != userObjs.end())
1552 {
1553 getUserProperties(usrObj->second, usrGrps, usrPriv, usrEnabled);
1554 return 0;
1555 }
1556 return -EIO;
1557}
1558
arun-pmbbe728c2020-01-10 15:18:04 +05301559void UserAccess::cacheUserDataFile()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301560{
1561 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1562 userLock{*userMutex};
1563 try
1564 {
1565 readUserData();
1566 }
1567 catch (const std::ios_base::failure& e)
1568 { // File is empty, create it for the first time
1569 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1570 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1571 // user index 0 is reserved, starts with 1
1572 for (size_t userIndex = 1; userIndex <= ipmiMaxUsers; ++userIndex)
1573 {
1574 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1575 {
1576 usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
1577 privNoAccess;
Saravanan Palanisamy92d81192019-08-07 18:00:04 +00001578 usersTbl.user[userIndex]
1579 .payloadAccess[chIndex]
1580 .stdPayloadEnables1[static_cast<uint8_t>(
1581 ipmi::PayloadType::SOL)] = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301582 }
1583 }
1584 writeUserData();
1585 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001586 // Create lock file if it does not exist
1587 int fd = open(ipmiUserSignalLockFile, O_CREAT | O_TRUNC | O_SYNC,
1588 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1589 if (fd < 0)
1590 {
George Liu82844ef2024-07-17 17:03:56 +08001591 lg2::error("Error in creating IPMI user signal lock file");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001592 throw std::ios_base::failure(
1593 "Error in creating temporary IPMI user signal lock file");
1594 }
1595 close(fd);
1596
1597 sigHndlrLock = boost::interprocess::file_lock(ipmiUserSignalLockFile);
George Liu1a2e1502022-07-08 12:20:19 +08001598 // Register it for single object and single process either netipmid /
arun-pmbbe728c2020-01-10 15:18:04 +05301599 // host-ipmid
1600 if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
1601 {
George Liu82844ef2024-07-17 17:03:56 +08001602 lg2::debug("Registering signal handler");
arun-pmbbe728c2020-01-10 15:18:04 +05301603 userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
1604 bus,
1605 sdbusplus::bus::match::rules::type::signal() +
1606 sdbusplus::bus::match::rules::interface(dBusObjManager) +
1607 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001608 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001609 userUpdatedSignalHandler(*this, msg);
1610 });
arun-pmbbe728c2020-01-10 15:18:04 +05301611 userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
1612 bus,
1613 sdbusplus::bus::match::rules::type::signal() +
1614 sdbusplus::bus::match::rules::interface(userMgrInterface) +
1615 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001616 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001617 userUpdatedSignalHandler(*this, msg);
1618 });
arun-pmbbe728c2020-01-10 15:18:04 +05301619 userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
1620 bus,
1621 sdbusplus::bus::match::rules::type::signal() +
1622 sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
1623 sdbusplus::bus::match::rules::interface(
1624 dBusPropertiesInterface) +
1625 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
1626 sdbusplus::bus::match::rules::argN(0, usersInterface),
Patrick Williams5d82f472022-07-22 19:26:53 -05001627 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001628 userUpdatedSignalHandler(*this, msg);
1629 });
arun-pmbbe728c2020-01-10 15:18:04 +05301630 signalHndlrObject = true;
1631 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301632 std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
1633 try
1634 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +07001635 auto method =
1636 bus.new_method_call(userMgrService, userMgrObjBasePath,
1637 dBusObjManager, getManagedObjectsMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301638 auto reply = bus.call(method);
1639 reply.read(managedObjs);
1640 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001641 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301642 {
George Liu82844ef2024-07-17 17:03:56 +08001643 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1644 getManagedObjectsMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301645 return;
1646 }
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301647 bool updateRequired = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301648 UsersTbl* userData = &usersTbl;
1649 // user index 0 is reserved, starts with 1
1650 for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
1651 {
1652 if ((userData->user[usrIdx].userInSystem) &&
1653 (userData->user[usrIdx].userName[0] != '\0'))
1654 {
1655 std::vector<std::string> usrGrps;
1656 std::string usrPriv;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301657
1658 std::string userName(
1659 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1660 ipmiMaxUserName);
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301661 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1662 tempUserPath /= userName;
1663 std::string usersPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301664
1665 auto usrObj = managedObjs.find(usersPath);
1666 if (usrObj != managedObjs.end())
1667 {
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001668 bool usrEnabled = false;
Patrick Venture3a697ad2019-08-19 11:12:05 -07001669
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301670 // User exist. Lets check and update other fileds
1671 getUserObjProperties(usrObj->second, usrGrps, usrPriv,
1672 usrEnabled);
1673 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
1674 usrGrps.end())
1675 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301676 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301677 // Group "ipmi" is removed so lets remove user in IPMI
1678 deleteUserIndex(usrIdx);
1679 }
1680 else
1681 {
1682 // Group "ipmi" is present so lets update other properties
1683 // in IPMI
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001684 uint8_t priv = UserAccess::convertToIPMIPrivilege(usrPriv) &
1685 privMask;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301686 // Update all channels priv, only if it is not equivalent to
1687 // getUsrMgmtSyncIndex()
1688 if (userData->user[usrIdx]
1689 .userPrivAccess[getUsrMgmtSyncIndex()]
1690 .privilege != priv)
1691 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301692 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301693 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
1694 ++chIndex)
1695 {
1696 userData->user[usrIdx]
1697 .userPrivAccess[chIndex]
1698 .privilege = priv;
1699 }
1700 }
1701 if (userData->user[usrIdx].userEnabled != usrEnabled)
1702 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301703 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301704 userData->user[usrIdx].userEnabled = usrEnabled;
1705 }
1706 }
1707
1708 // We are done with this obj. lets delete from MAP
1709 managedObjs.erase(usrObj);
1710 }
1711 else
1712 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301713 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301714 deleteUserIndex(usrIdx);
1715 }
1716 }
1717 }
1718
1719 // Walk through remnaining managedObj users list
1720 // Add them to ipmi data base
1721 for (const auto& usrObj : managedObjs)
1722 {
1723 std::vector<std::string> usrGrps;
1724 std::string usrPriv, userName;
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001725 bool usrEnabled = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301726 std::string usrObjPath = std::string(usrObj.first);
1727 if (getUserNameFromPath(usrObj.first.str, userName) != 0)
1728 {
George Liu82844ef2024-07-17 17:03:56 +08001729 lg2::error("Error in user object path");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301730 continue;
1731 }
1732 getUserObjProperties(usrObj.second, usrGrps, usrPriv, usrEnabled);
1733 // Add 'ipmi' group users
1734 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
1735 usrGrps.end())
1736 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301737 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301738 // CREATE NEW USER
1739 if (true != addUserEntry(userName, usrPriv, usrEnabled))
1740 {
1741 break;
1742 }
1743 }
1744 }
1745
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301746 if (updateRequired)
1747 {
1748 // All userData slots update done. Lets write the data
1749 writeUserData();
1750 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301751
1752 return;
1753}
1754} // namespace ipmi