blob: e51e4e53b626f91ac35902d958ba30c5e4cfb405 [file] [log] [blame]
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#include "user_mgmt.hpp"
17
Saravanan Palanisamy77381f12019-05-15 22:33:17 +000018#include "channel_layer.hpp"
Johnathan Manteyfd61fc32021-04-08 11:05:38 -070019#include "channel_mgmt.hpp"
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053020
Suryakanth Sekar90b00c72019-01-16 10:37:57 +053021#include <security/pam_appl.h>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053022#include <sys/stat.h>
23#include <unistd.h>
24
25#include <boost/interprocess/sync/named_recursive_mutex.hpp>
26#include <boost/interprocess/sync/scoped_lock.hpp>
Snehalatha Venkatesh745164c2021-06-25 10:02:25 +000027#include <ipmid/types.hpp>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053028#include <nlohmann/json.hpp>
29#include <phosphor-logging/elog-errors.hpp>
George Liu82844ef2024-07-17 17:03:56 +080030#include <phosphor-logging/lg2.hpp>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053031#include <sdbusplus/bus/match.hpp>
32#include <sdbusplus/server/object.hpp>
33#include <xyz/openbmc_project/Common/error.hpp>
34#include <xyz/openbmc_project/User/Common/error.hpp>
35
Orit Kashanyf7616312025-08-24 04:48:41 -070036#include <algorithm>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050037#include <cerrno>
Orit Kashanyf7616312025-08-24 04:48:41 -070038#include <cstring>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050039#include <fstream>
40#include <regex>
41#include <variant>
42
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053043namespace ipmi
44{
45
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053046using namespace phosphor::logging;
47using Json = nlohmann::json;
48
Vernon Mauery16b86932019-05-01 08:36:11 -070049using PrivAndGroupType = std::variant<std::string, std::vector<std::string>>;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053050
51using NoResource =
Willy Tu523e2d12023-09-05 11:36:48 -070052 sdbusplus::error::xyz::openbmc_project::user::common::NoResource;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053053
54using InternalFailure =
Willy Tu523e2d12023-09-05 11:36:48 -070055 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053056
Lei YU4b0ddb62019-01-25 16:43:50 +080057std::unique_ptr<sdbusplus::bus::match_t> userUpdatedSignal
58 __attribute__((init_priority(101)));
59std::unique_ptr<sdbusplus::bus::match_t> userMgrRenamedSignal
60 __attribute__((init_priority(101)));
61std::unique_ptr<sdbusplus::bus::match_t> userPropertiesSignal
62 __attribute__((init_priority(101)));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053063
Patrick Williams5d82f472022-07-22 19:26:53 -050064void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053065 const std::string& objPath, const std::string& interface,
66 const std::string& property,
67 const DbusUserPropVariant& value)
68{
69 try
70 {
Patrick Williams1318a5e2024-08-16 15:19:54 -040071 auto method =
72 bus.new_method_call(service.c_str(), objPath.c_str(),
73 dBusPropertiesInterface, setPropertiesMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053074 method.append(interface, property, value);
75 bus.call(method);
76 }
Patrick Williams5d82f472022-07-22 19:26:53 -050077 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053078 {
George Liu82844ef2024-07-17 17:03:56 +080079 lg2::error("Failed to set {PROPERTY}, path: {PATH}, "
80 "interface: {INTERFACE}",
81 "PROPERTY", property, "PATH", objPath, "INTERFACE",
82 interface);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053083 throw;
84 }
85}
86
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053087UserAccess& getUserAccessObject()
88{
89 static UserAccess userAccess;
90 return userAccess;
91}
92
93int getUserNameFromPath(const std::string& path, std::string& userName)
94{
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +053095 sdbusplus::message::object_path objPath(path);
96 userName.assign(objPath.filename());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053097 return 0;
98}
99
100void userUpdateHelper(UserAccess& usrAccess, const UserUpdateEvent& userEvent,
101 const std::string& userName, const std::string& priv,
102 const bool& enabled, const std::string& newUserName)
103{
104 UsersTbl* userData = usrAccess.getUsersTblPtr();
105 if (userEvent == UserUpdateEvent::userCreated)
106 {
107 if (usrAccess.addUserEntry(userName, priv, enabled) == false)
108 {
109 return;
110 }
111 }
112 else
113 {
114 // user index 0 is reserved, starts with 1
115 size_t usrIndex = 1;
116 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
117 {
Orit Kashanyf7616312025-08-24 04:48:41 -0700118 std::string curName =
119 safeUsernameString(userData->user[usrIndex].userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530120 if (userName == curName)
121 {
122 break; // found the entry
123 }
124 }
125 if (usrIndex > ipmiMaxUsers)
126 {
George Liu82844ef2024-07-17 17:03:56 +0800127 lg2::debug("User not found for signal, user name: {USER_NAME}, "
128 "user event: {USER_EVENT}",
129 "USER_NAME", userName, "USER_EVENT", userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530130 return;
131 }
132 switch (userEvent)
133 {
134 case UserUpdateEvent::userDeleted:
135 {
136 usrAccess.deleteUserIndex(usrIndex);
137 break;
138 }
139 case UserUpdateEvent::userPrivUpdated:
140 {
141 uint8_t userPriv =
142 static_cast<uint8_t>(
143 UserAccess::convertToIPMIPrivilege(priv)) &
144 privMask;
145 // Update all channels privileges, only if it is not equivalent
146 // to getUsrMgmtSyncIndex()
147 if (userData->user[usrIndex]
148 .userPrivAccess[UserAccess::getUsrMgmtSyncIndex()]
149 .privilege != userPriv)
150 {
151 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
152 ++chIndex)
153 {
154 userData->user[usrIndex]
155 .userPrivAccess[chIndex]
156 .privilege = userPriv;
157 }
158 }
159 break;
160 }
161 case UserUpdateEvent::userRenamed:
162 {
Orit Kashanyf7616312025-08-24 04:48:41 -0700163 safeUsernameCopyToBuffer(
164 userData->user[usrIndex].userName,
165 sizeof(userData->user[usrIndex].userName), newUserName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530166 ipmiRenameUserEntryPassword(userName, newUserName);
167 break;
168 }
169 case UserUpdateEvent::userStateUpdated:
170 {
171 userData->user[usrIndex].userEnabled = enabled;
172 break;
173 }
174 default:
175 {
George Liu82844ef2024-07-17 17:03:56 +0800176 lg2::error("Unhandled user event: {USER_EVENT}", "USER_EVENT",
177 userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530178 return;
179 }
180 }
181 }
182 usrAccess.writeUserData();
George Liu82844ef2024-07-17 17:03:56 +0800183 lg2::debug("User event handled successfully, user name: {USER_NAME}, "
184 "user event: {USER_EVENT}",
185 "USER_NAME", userName.c_str(), "USER_EVENT", userEvent);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530186
187 return;
188}
189
Patrick Williams5d82f472022-07-22 19:26:53 -0500190void userUpdatedSignalHandler(UserAccess& usrAccess, sdbusplus::message_t& msg)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530191{
Patrick Williams5d82f472022-07-22 19:26:53 -0500192 static sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530193 std::string signal = msg.get_member();
Patrick Venture3a697ad2019-08-19 11:12:05 -0700194 std::string userName, priv, newUserName;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530195 std::vector<std::string> groups;
196 bool enabled = false;
197 UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent;
198 if (signal == intfAddedSignal)
199 {
200 DbusUserObjPath objPath;
201 DbusUserObjValue objValue;
202 msg.read(objPath, objValue);
203 getUserNameFromPath(objPath.str, userName);
204 if (usrAccess.getUserObjProperties(objValue, groups, priv, enabled) !=
205 0)
206 {
207 return;
208 }
209 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
210 groups.end())
211 {
212 return;
213 }
214 userEvent = UserUpdateEvent::userCreated;
215 }
216 else if (signal == intfRemovedSignal)
217 {
218 DbusUserObjPath objPath;
219 std::vector<std::string> interfaces;
220 msg.read(objPath, interfaces);
221 getUserNameFromPath(objPath.str, userName);
222 userEvent = UserUpdateEvent::userDeleted;
223 }
224 else if (signal == userRenamedSignal)
225 {
226 msg.read(userName, newUserName);
227 userEvent = UserUpdateEvent::userRenamed;
228 }
229 else if (signal == propertiesChangedSignal)
230 {
231 getUserNameFromPath(msg.get_path(), userName);
232 }
233 else
234 {
George Liu82844ef2024-07-17 17:03:56 +0800235 lg2::error("Unknown user update signal: {SIGNAL}", "SIGNAL", signal);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530236 return;
237 }
238
239 if (signal.empty() || userName.empty() ||
240 (signal == userRenamedSignal && newUserName.empty()))
241 {
George Liu82844ef2024-07-17 17:03:56 +0800242 lg2::error("Invalid inputs received");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530243 return;
244 }
245
246 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
247 userLock{*(usrAccess.userMutex)};
248 usrAccess.checkAndReloadUserData();
249
250 if (signal == propertiesChangedSignal)
251 {
252 std::string intfName;
253 DbusUserObjProperties chProperties;
254 msg.read(intfName, chProperties); // skip reading 3rd argument.
255 for (const auto& prop : chProperties)
256 {
257 userEvent = UserUpdateEvent::reservedEvent;
258 std::string member = prop.first;
259 if (member == userPrivProperty)
260 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700261 priv = std::get<std::string>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530262 userEvent = UserUpdateEvent::userPrivUpdated;
263 }
264 else if (member == userGrpProperty)
265 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700266 groups = std::get<std::vector<std::string>>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530267 userEvent = UserUpdateEvent::userGrpUpdated;
268 }
269 else if (member == userEnabledProperty)
270 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700271 enabled = std::get<bool>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530272 userEvent = UserUpdateEvent::userStateUpdated;
273 }
274 // Process based on event type.
275 if (userEvent == UserUpdateEvent::userGrpUpdated)
276 {
277 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
278 groups.end())
279 {
280 // remove user from ipmi user list.
281 userUpdateHelper(usrAccess, UserUpdateEvent::userDeleted,
282 userName, priv, enabled, newUserName);
283 }
284 else
285 {
286 DbusUserObjProperties properties;
287 try
288 {
289 auto method = bus.new_method_call(
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700290 userMgrService, msg.get_path(),
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530291 dBusPropertiesInterface, getAllPropertiesMethod);
292 method.append(usersInterface);
293 auto reply = bus.call(method);
294 reply.read(properties);
295 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500296 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530297 {
George Liu82844ef2024-07-17 17:03:56 +0800298 lg2::debug("Failed to excute {METHOD}, path: {PATH}",
299 "METHOD", getAllPropertiesMethod, "PATH",
300 msg.get_path());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530301 return;
302 }
303 usrAccess.getUserProperties(properties, groups, priv,
304 enabled);
305 // add user to ipmi user list.
306 userUpdateHelper(usrAccess, UserUpdateEvent::userCreated,
307 userName, priv, enabled, newUserName);
308 }
309 }
310 else if (userEvent != UserUpdateEvent::reservedEvent)
311 {
312 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
313 newUserName);
314 }
315 }
316 }
317 else if (userEvent != UserUpdateEvent::reservedEvent)
318 {
319 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
320 newUserName);
321 }
322 return;
323}
324
325UserAccess::~UserAccess()
326{
327 if (signalHndlrObject)
328 {
329 userUpdatedSignal.reset();
330 userMgrRenamedSignal.reset();
331 userPropertiesSignal.reset();
332 sigHndlrLock.unlock();
333 }
334}
335
336UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection())
337{
338 std::ofstream mutexCleanUpFile;
339 mutexCleanUpFile.open(ipmiMutexCleanupLockFile,
340 std::ofstream::out | std::ofstream::app);
341 if (!mutexCleanUpFile.good())
342 {
George Liu82844ef2024-07-17 17:03:56 +0800343 lg2::debug("Unable to open mutex cleanup file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530344 return;
345 }
346 mutexCleanUpFile.close();
347 mutexCleanupLock = boost::interprocess::file_lock(ipmiMutexCleanupLockFile);
348 if (mutexCleanupLock.try_lock())
349 {
350 boost::interprocess::named_recursive_mutex::remove(ipmiUserMutex);
351 }
352 mutexCleanupLock.lock_sharable();
353 userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>(
354 boost::interprocess::open_or_create, ipmiUserMutex);
355
arun-pmbbe728c2020-01-10 15:18:04 +0530356 cacheUserDataFile();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530357 getSystemPrivAndGroups();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530358}
359
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530360UserInfo* UserAccess::getUserInfo(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530361{
362 checkAndReloadUserData();
363 return &usersTbl.user[userId];
364}
365
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530366void UserAccess::setUserInfo(const uint8_t userId, UserInfo* userInfo)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530367{
368 checkAndReloadUserData();
369 std::copy(reinterpret_cast<uint8_t*>(userInfo),
370 reinterpret_cast<uint8_t*>(userInfo) + sizeof(*userInfo),
371 reinterpret_cast<uint8_t*>(&usersTbl.user[userId]));
372 writeUserData();
373}
374
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530375bool UserAccess::isValidChannel(const uint8_t chNum)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530376{
377 return (chNum < ipmiMaxChannels);
378}
379
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530380bool UserAccess::isValidUserId(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530381{
382 return ((userId <= ipmiMaxUsers) && (userId != reservedUserId));
383}
384
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530385bool UserAccess::isValidPrivilege(const uint8_t priv)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530386{
jayaprakash Mutyala0e2dbee2019-12-26 13:03:04 +0000387 // Callback privilege is deprecated in OpenBMC
Alexander Filippovfc24fa52022-02-01 14:57:59 +0300388 return isValidPrivLimit(priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530389}
390
391uint8_t UserAccess::getUsrMgmtSyncIndex()
392{
Johnathan Manteyfd61fc32021-04-08 11:05:38 -0700393 // Identify the IPMI channel used to assign system user privilege levels
394 // in phosphor-user-manager. The default value is IPMI Channel 1. To
395 // assign a different channel add:
396 // "is_management_nic" : true
397 // into the channel_config.json file describing the assignment of the IPMI
398 // channels. It is only necessary to add the string above to ONE record in
399 // the channel_config.json file. All other records will be automatically
400 // assigned a "false" value.
401 return getChannelConfigObject().getManagementNICID();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530402}
403
404CommandPrivilege UserAccess::convertToIPMIPrivilege(const std::string& value)
405{
406 auto iter = std::find(ipmiPrivIndex.begin(), ipmiPrivIndex.end(), value);
407 if (iter == ipmiPrivIndex.end())
408 {
409 if (value == "")
410 {
411 return static_cast<CommandPrivilege>(privNoAccess);
412 }
George Liu82844ef2024-07-17 17:03:56 +0800413 lg2::error("Error in converting to IPMI privilege: {PRIV}", "PRIV",
414 value);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530415 throw std::out_of_range("Out of range - convertToIPMIPrivilege");
416 }
417 else
418 {
419 return static_cast<CommandPrivilege>(
420 std::distance(ipmiPrivIndex.begin(), iter));
421 }
422}
423
424std::string UserAccess::convertToSystemPrivilege(const CommandPrivilege& value)
425{
426 if (value == static_cast<CommandPrivilege>(privNoAccess))
427 {
428 return "";
429 }
430 try
431 {
432 return ipmiPrivIndex.at(value);
433 }
434 catch (const std::out_of_range& e)
435 {
George Liu82844ef2024-07-17 17:03:56 +0800436 lg2::error("Error in converting to system privilege: {PRIV}", "PRIV",
437 value);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530438 throw std::out_of_range("Out of range - convertToSystemPrivilege");
439 }
440}
441
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000442bool UserAccess::isValidUserName(const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530443{
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000444 if (userName.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530445 {
George Liu82844ef2024-07-17 17:03:56 +0800446 lg2::error("userName is empty");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530447 return false;
448 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530449 if (!std::regex_match(userName.c_str(),
nichanghao.nch0c96fdf2024-01-17 22:13:35 +0800450 std::regex("[a-zA-Z_][a-zA-Z_0-9]*")))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530451 {
George Liu82844ef2024-07-17 17:03:56 +0800452 lg2::error("Unsupported characters in user name");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530453 return false;
454 }
455 if (userName == "root")
456 {
George Liu82844ef2024-07-17 17:03:56 +0800457 lg2::error("Invalid user name - root");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530458 return false;
459 }
460 std::map<DbusUserObjPath, DbusUserObjValue> properties;
461 try
462 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700463 auto method =
464 bus.new_method_call(userMgrService, userMgrObjBasePath,
465 dBusObjManager, getManagedObjectsMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530466 auto reply = bus.call(method);
467 reply.read(properties);
468 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500469 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530470 {
George Liu82844ef2024-07-17 17:03:56 +0800471 lg2::error("Failed to excute {METHOD}, path: {PATH}", "METHOD",
472 getManagedObjectsMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530473 return false;
474 }
475
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530476 sdbusplus::message::object_path tempUserPath(userObjBasePath);
477 tempUserPath /= userName;
478 std::string usersPath(tempUserPath);
479
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530480 if (properties.find(usersPath) != properties.end())
481 {
George Liu82844ef2024-07-17 17:03:56 +0800482 lg2::debug("Username {USER_NAME} already exists", "USER_NAME",
483 userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530484 return false;
485 }
486
487 return true;
488}
489
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530490/** @brief Information exchanged by pam module and application.
491 *
492 * @param[in] numMsg - length of the array of pointers,msg.
493 *
494 * @param[in] msg - pointer to an array of pointers to pam_message structure
495 *
496 * @param[out] resp - struct pam response array
497 *
498 * @param[in] appdataPtr - member of pam_conv structure
499 *
500 * @return the response in pam response structure.
501 */
502
503static int pamFunctionConversation(int numMsg, const struct pam_message** msg,
504 struct pam_response** resp, void* appdataPtr)
505{
506 if (appdataPtr == nullptr)
507 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530508 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530509 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530510
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530511 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
512 {
513 return PAM_CONV_ERR;
514 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530515
516 for (int i = 0; i < numMsg; ++i)
517 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530518 /* Ignore all PAM messages except prompting for hidden input */
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530519 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
520 {
521 continue;
522 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530523
524 /* Assume PAM is only prompting for the password as hidden input */
525 /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */
526
527 char* appPass = reinterpret_cast<char*>(appdataPtr);
528 size_t appPassSize = std::strlen(appPass);
529
530 if (appPassSize >= PAM_MAX_RESP_SIZE)
531 {
532 return PAM_CONV_ERR;
533 }
534
535 char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1));
536 if (pass == nullptr)
537 {
538 return PAM_BUF_ERR;
539 }
540
Patrick Williams1318a5e2024-08-16 15:19:54 -0400541 void* ptr =
542 calloc(static_cast<size_t>(numMsg), sizeof(struct pam_response));
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530543 if (ptr == nullptr)
544 {
545 free(pass);
546 return PAM_BUF_ERR;
547 }
548
549 std::strncpy(pass, appPass, appPassSize + 1);
550
551 *resp = reinterpret_cast<pam_response*>(ptr);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530552 resp[i]->resp = pass;
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530553
554 return PAM_SUCCESS;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530555 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530556
557 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530558}
559
560/** @brief Updating the PAM password
561 *
562 * @param[in] username - username in string
563 *
564 * @param[in] password - new password in string
565 *
566 * @return status
567 */
568
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000569int pamUpdatePasswd(const char* username, const char* password)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530570{
571 const struct pam_conv localConversation = {pamFunctionConversation,
572 const_cast<char*>(password)};
Jayanth Othayotha6fb32d2024-12-15 10:55:22 -0600573 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530574
Patrick Williams1318a5e2024-08-16 15:19:54 -0400575 int retval =
576 pam_start("passwd", username, &localConversation, &localAuthHandle);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530577
578 if (retval != PAM_SUCCESS)
579 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000580 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530581 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000582
583 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
584 if (retval != PAM_SUCCESS)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530585 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000586 pam_end(localAuthHandle, retval);
587 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530588 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000589
590 return pam_end(localAuthHandle, PAM_SUCCESS);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530591}
592
Ayushi Smriti02650d52019-05-15 11:59:09 +0000593bool pamUserCheckAuthenticate(std::string_view username,
594 std::string_view password)
595{
596 const struct pam_conv localConversation = {
597 pamFunctionConversation, const_cast<char*>(password.data())};
598
Jayanth Othayotha6fb32d2024-12-15 10:55:22 -0600599 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ayushi Smriti02650d52019-05-15 11:59:09 +0000600
601 if (pam_start("dropbear", username.data(), &localConversation,
602 &localAuthHandle) != PAM_SUCCESS)
603 {
George Liu82844ef2024-07-17 17:03:56 +0800604 lg2::error("User Authentication Failure");
Ayushi Smriti02650d52019-05-15 11:59:09 +0000605 return false;
606 }
607
608 int retval = pam_authenticate(localAuthHandle,
609 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
610
611 if (retval != PAM_SUCCESS)
612 {
George Liu82844ef2024-07-17 17:03:56 +0800613 lg2::debug("pam_authenticate returned failure: {ERROR}", "ERROR",
614 retval);
Ayushi Smriti02650d52019-05-15 11:59:09 +0000615
616 pam_end(localAuthHandle, retval);
617 return false;
618 }
619
620 if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
621 PAM_SUCCESS)
622 {
623 pam_end(localAuthHandle, PAM_SUCCESS);
624 return false;
625 }
626
627 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
628 {
629 return false;
630 }
631 return true;
632}
633
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000634Cc UserAccess::setSpecialUserPassword(const std::string& userName,
Vernon Mauery1e22a0f2021-07-30 13:36:54 -0700635 const SecureString& userPassword)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530636{
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000637 if (pamUpdatePasswd(userName.c_str(), userPassword.c_str()) != PAM_SUCCESS)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530638 {
George Liu82844ef2024-07-17 17:03:56 +0800639 lg2::debug("Failed to update password");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000640 return ccUnspecifiedError;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530641 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000642 return ccSuccess;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530643}
644
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000645Cc UserAccess::setUserPassword(const uint8_t userId, const char* userPassword)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530646{
647 std::string userName;
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000648 if (ipmiUserGetUserName(userId, userName) != ccSuccess)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530649 {
George Liu82844ef2024-07-17 17:03:56 +0800650 lg2::debug("User Name not found, user Id: {USER_ID}", "USER_ID",
651 userId);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000652 return ccParmOutOfRange;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530653 }
Snehalatha Venkatesh61024d72021-04-08 16:24:39 +0000654
655 ipmi::SecureString passwd;
Orit Kashanyf7616312025-08-24 04:48:41 -0700656 size_t len = strnlen(userPassword, maxIpmi20PasswordSize);
657 passwd.assign(reinterpret_cast<const char*>(userPassword), len);
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000658 int retval = pamUpdatePasswd(userName.c_str(), passwd.c_str());
659
660 switch (retval)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530661 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000662 case PAM_SUCCESS:
663 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000664 return ccSuccess;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000665 }
666 case PAM_AUTHTOK_ERR:
667 {
George Liu82844ef2024-07-17 17:03:56 +0800668 lg2::debug("Bad authentication token");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000669 return ccInvalidFieldRequest;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000670 }
671 default:
672 {
George Liu82844ef2024-07-17 17:03:56 +0800673 lg2::debug("Failed to update password, user Id: {USER_ID}",
674 "USER_ID", userId);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000675 return ccUnspecifiedError;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000676 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530677 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530678}
679
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000680Cc UserAccess::setUserEnabledState(const uint8_t userId,
681 const bool& enabledState)
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530682{
683 if (!isValidUserId(userId))
684 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000685 return ccParmOutOfRange;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530686 }
687 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
688 userLock{*userMutex};
689 UserInfo* userInfo = getUserInfo(userId);
690 std::string userName;
Orit Kashanyf7616312025-08-24 04:48:41 -0700691
692 safeUsernameAssign(userName, userInfo->userName);
693
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530694 if (userName.empty())
695 {
George Liu82844ef2024-07-17 17:03:56 +0800696 lg2::debug("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000697 return ccUnspecifiedError;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530698 }
699 if (userInfo->userEnabled != enabledState)
700 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530701 sdbusplus::message::object_path tempUserPath(userObjBasePath);
702 tempUserPath /= userName;
703 std::string userPath(tempUserPath);
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700704 setDbusProperty(bus, userMgrService, userPath, usersInterface,
Patrick Venture99d1ba02019-02-21 15:11:24 -0800705 userEnabledProperty, enabledState);
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530706 userInfo->userEnabled = enabledState;
707 try
708 {
709 writeUserData();
710 }
711 catch (const std::exception& e)
712 {
George Liu82844ef2024-07-17 17:03:56 +0800713 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000714 return ccUnspecifiedError;
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530715 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530716 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000717 return ccSuccess;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530718}
719
Patrick Williams1318a5e2024-08-16 15:19:54 -0400720Cc UserAccess::setUserPayloadAccess(
721 const uint8_t chNum, const uint8_t operation, const uint8_t userId,
722 const PayloadAccess& payloadAccess)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000723{
724 constexpr uint8_t enable = 0x0;
725 constexpr uint8_t disable = 0x1;
726
727 if (!isValidChannel(chNum))
728 {
George Liua0e545d2025-01-24 09:50:22 +0800729 lg2::debug(
730 "Set user payload access - Invalid channel request: {CHANNEL}",
731 "CHANNEL", chNum);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000732 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000733 }
734 if (!isValidUserId(userId))
735 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000736 return ccParmOutOfRange;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000737 }
738 if (operation != enable && operation != disable)
739 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000740 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000741 }
742 // Check operation & payloadAccess if required.
743 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
744 userLock{*userMutex};
745 UserInfo* userInfo = getUserInfo(userId);
746
747 if (operation == enable)
748 {
749 userInfo->payloadAccess[chNum].stdPayloadEnables1 |=
750 payloadAccess.stdPayloadEnables1;
751
752 userInfo->payloadAccess[chNum].oemPayloadEnables1 |=
753 payloadAccess.oemPayloadEnables1;
754 }
755 else
756 {
757 userInfo->payloadAccess[chNum].stdPayloadEnables1 &=
758 ~(payloadAccess.stdPayloadEnables1);
759
760 userInfo->payloadAccess[chNum].oemPayloadEnables1 &=
761 ~(payloadAccess.oemPayloadEnables1);
762 }
763
764 try
765 {
766 writeUserData();
767 }
768 catch (const std::exception& e)
769 {
George Liu82844ef2024-07-17 17:03:56 +0800770 lg2::error("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000771 return ccUnspecifiedError;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000772 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000773 return ccSuccess;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000774}
775
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000776Cc UserAccess::setUserPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
777 const UserPrivAccess& privAccess,
778 const bool& otherPrivUpdates)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530779{
780 if (!isValidChannel(chNum))
781 {
George Liua0e545d2025-01-24 09:50:22 +0800782 lg2::debug(
783 "Set user privilege access - Invalid channel request: {CHANNEL}",
784 "CHANNEL", chNum);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000785 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530786 }
787 if (!isValidUserId(userId))
788 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000789 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530790 }
791 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
792 userLock{*userMutex};
793 UserInfo* userInfo = getUserInfo(userId);
794 std::string userName;
Orit Kashanyf7616312025-08-24 04:48:41 -0700795 safeUsernameAssign(userName, userInfo->userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530796 if (userName.empty())
797 {
George Liu82844ef2024-07-17 17:03:56 +0800798 lg2::debug("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000799 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530800 }
801 std::string priv = convertToSystemPrivilege(
802 static_cast<CommandPrivilege>(privAccess.privilege));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530803 uint8_t syncIndex = getUsrMgmtSyncIndex();
804 if (chNum == syncIndex &&
805 privAccess.privilege != userInfo->userPrivAccess[syncIndex].privilege)
806 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530807 sdbusplus::message::object_path tempUserPath(userObjBasePath);
808 tempUserPath /= userName;
809 std::string userPath(tempUserPath);
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700810 setDbusProperty(bus, userMgrService, userPath, usersInterface,
Patrick Venture99d1ba02019-02-21 15:11:24 -0800811 userPrivProperty, priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530812 }
813 userInfo->userPrivAccess[chNum].privilege = privAccess.privilege;
814
815 if (otherPrivUpdates)
816 {
817 userInfo->userPrivAccess[chNum].ipmiEnabled = privAccess.ipmiEnabled;
818 userInfo->userPrivAccess[chNum].linkAuthEnabled =
819 privAccess.linkAuthEnabled;
820 userInfo->userPrivAccess[chNum].accessCallback =
821 privAccess.accessCallback;
822 }
823 try
824 {
825 writeUserData();
826 }
827 catch (const std::exception& e)
828 {
George Liu82844ef2024-07-17 17:03:56 +0800829 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000830 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530831 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000832 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530833}
834
835uint8_t UserAccess::getUserId(const std::string& userName)
836{
837 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
838 userLock{*userMutex};
839 checkAndReloadUserData();
840 // user index 0 is reserved, starts with 1
841 size_t usrIndex = 1;
842 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
843 {
Orit Kashanyf7616312025-08-24 04:48:41 -0700844 std::string curName =
845 safeUsernameString(usersTbl.user[usrIndex].userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530846 if (userName == curName)
847 {
848 break; // found the entry
849 }
850 }
851 if (usrIndex > ipmiMaxUsers)
852 {
George Liu82844ef2024-07-17 17:03:56 +0800853 lg2::debug("Username {USER_NAME} not found", "USER_NAME", userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530854 return invalidUserId;
855 }
856
857 return usrIndex;
858}
859
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000860Cc UserAccess::getUserName(const uint8_t userId, std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530861{
862 if (!isValidUserId(userId))
863 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000864 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530865 }
866 UserInfo* userInfo = getUserInfo(userId);
Orit Kashanyf7616312025-08-24 04:48:41 -0700867 safeUsernameAssign(userName, userInfo->userName);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000868 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530869}
870
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530871bool UserAccess::isIpmiInAvailableGroupList()
872{
873 if (std::find(availableGroups.begin(), availableGroups.end(),
874 ipmiGrpName) != availableGroups.end())
875 {
876 return true;
877 }
878 if (availableGroups.empty())
879 {
880 // available groups shouldn't be empty, re-query
881 getSystemPrivAndGroups();
882 if (std::find(availableGroups.begin(), availableGroups.end(),
883 ipmiGrpName) != availableGroups.end())
884 {
885 return true;
886 }
887 }
888 return false;
889}
890
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000891Cc UserAccess::setUserName(const uint8_t userId, const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530892{
893 if (!isValidUserId(userId))
894 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000895 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530896 }
897
898 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
899 userLock{*userMutex};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530900 std::string oldUser;
901 getUserName(userId, oldUser);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530902
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000903 if (oldUser == userName)
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +0530904 {
905 // requesting to set the same user name, return success.
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000906 return ccSuccess;
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +0530907 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000908
909 bool validUser = isValidUserName(userName);
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +0530910 UserInfo* userInfo = getUserInfo(userId);
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000911 if (userName.empty() && !oldUser.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530912 {
913 // Delete existing user
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530914 sdbusplus::message::object_path tempUserPath(userObjBasePath);
915 tempUserPath /= oldUser;
916 std::string userPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530917 try
918 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700919 auto method =
920 bus.new_method_call(userMgrService, userPath.c_str(),
921 deleteUserInterface, deleteUserMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530922 auto reply = bus.call(method);
923 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500924 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530925 {
George Liu82844ef2024-07-17 17:03:56 +0800926 lg2::debug("Failed to excute {METHOD}, path:{PATH}", "METHOD",
927 deleteUserMethod, "PATH", userPath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000928 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530929 }
Richard Marian Thomaiyar02710bb2018-11-28 20:42:25 +0530930 deleteUserIndex(userId);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530931 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000932 else if (oldUser.empty() && !userName.empty() && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530933 {
934 try
935 {
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530936 if (!isIpmiInAvailableGroupList())
937 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000938 return ccUnspecifiedError;
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530939 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530940 // Create new user
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700941 auto method =
942 bus.new_method_call(userMgrService, userMgrObjBasePath,
943 userMgrInterface, createUserMethod);
Alexander Filippovf6f3bb02022-02-01 14:38:40 +0300944 method.append(userName.c_str(), availableGroups,
945 ipmiPrivIndex[PRIVILEGE_USER], false);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530946 auto reply = bus.call(method);
947 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500948 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530949 {
George Liu82844ef2024-07-17 17:03:56 +0800950 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
951 createUserMethod, "PATH", userMgrObjBasePath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000952 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530953 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000954
Orit Kashanyf7616312025-08-24 04:48:41 -0700955 safeUsernameCopyToBuffer(userInfo->userName, sizeof(userInfo->userName),
956 userName);
957
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530958 userInfo->userInSystem = true;
Alexander Filippovf6f3bb02022-02-01 14:38:40 +0300959 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
960 {
961 userInfo->userPrivAccess[chIndex].privilege =
962 static_cast<uint8_t>(PRIVILEGE_USER);
963 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530964 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000965 else if (oldUser != userName && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530966 {
967 try
968 {
969 // User rename
Khang D Nguyen078aa6a2025-03-06 00:03:42 +0700970 auto method =
971 bus.new_method_call(userMgrService, userMgrObjBasePath,
972 userMgrInterface, renameUserMethod);
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000973 method.append(oldUser.c_str(), userName.c_str());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530974 auto reply = bus.call(method);
975 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500976 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530977 {
George Liu82844ef2024-07-17 17:03:56 +0800978 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
979 renameUserMethod, "PATH", userMgrObjBasePath);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000980 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530981 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000982
Orit Kashanyf7616312025-08-24 04:48:41 -0700983 safeUsernameCopyToBuffer(userInfo->userName, sizeof(userInfo->userName),
984 userName);
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000985
986 ipmiRenameUserEntryPassword(oldUser, userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530987 userInfo->userInSystem = true;
988 }
989 else if (!validUser)
990 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000991 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530992 }
993 try
994 {
995 writeUserData();
996 }
997 catch (const std::exception& e)
998 {
George Liu82844ef2024-07-17 17:03:56 +0800999 lg2::debug("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001000 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301001 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001002 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301003}
1004
1005static constexpr const char* jsonUserName = "user_name";
1006static constexpr const char* jsonPriv = "privilege";
1007static constexpr const char* jsonIpmiEnabled = "ipmi_enabled";
1008static constexpr const char* jsonLinkAuthEnabled = "link_auth_enabled";
1009static constexpr const char* jsonAccCallbk = "access_callback";
1010static constexpr const char* jsonUserEnabled = "user_enabled";
1011static constexpr const char* jsonUserInSys = "user_in_system";
1012static constexpr const char* jsonFixedUser = "fixed_user_name";
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001013static constexpr const char* payloadEnabledStr = "payload_enabled";
1014static constexpr const char* stdPayloadStr = "std_payload";
1015static constexpr const char* oemPayloadStr = "OEM_payload";
1016
1017/** @brief to construct a JSON object from the given payload access details.
1018 *
1019 * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. (input)
1020 * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. (input)
1021 *
1022 * @details Sample output JSON object format :
1023 * "payload_enabled":{
1024 * "OEM_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1025 * "OEM_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1026 * "OEM_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1027 * "OEM_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1028 * "OEM_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1029 * "OEM_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1030 * "OEM_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1031 * "OEM_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1032 * "std_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1033 * "std_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1034 * "std_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1035 * "std_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1036 * "std_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1037 * "std_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1038 * "std_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1039 * "std_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1040 * }
1041 */
1042static const Json constructJsonPayloadEnables(
1043 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1044 stdPayload,
1045 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1046 oemPayload)
1047{
1048 Json jsonPayloadEnabled;
1049
1050 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1051 {
1052 std::ostringstream stdPayloadStream;
1053 std::ostringstream oemPayloadStream;
1054
1055 stdPayloadStream << stdPayloadStr << payloadNum;
1056 oemPayloadStream << oemPayloadStr << payloadNum;
1057
1058 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1059 stdPayloadStream.str(), stdPayload[payloadNum]));
1060
1061 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1062 oemPayloadStream.str(), oemPayload[payloadNum]));
1063 }
1064 return jsonPayloadEnabled;
1065}
1066
1067void UserAccess::readPayloadAccessFromUserInfo(
1068 const UserInfo& userInfo,
1069 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& stdPayload,
1070 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& oemPayload)
1071{
1072 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1073 {
1074 for (auto chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1075 {
1076 stdPayload[payloadNum][chIndex] =
1077 userInfo.payloadAccess[chIndex].stdPayloadEnables1[payloadNum];
1078
1079 oemPayload[payloadNum][chIndex] =
1080 userInfo.payloadAccess[chIndex].oemPayloadEnables1[payloadNum];
1081 }
1082 }
1083}
1084
1085void UserAccess::updatePayloadAccessInUserInfo(
1086 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1087 stdPayload,
Willy Tu11d68892022-01-20 10:37:34 -08001088 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&,
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001089 UserInfo& userInfo)
1090{
1091 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1092 {
1093 // Ensure that reserved/unsupported payloads are marked to zero.
1094 userInfo.payloadAccess[chIndex].stdPayloadEnables1.reset();
1095 userInfo.payloadAccess[chIndex].oemPayloadEnables1.reset();
1096 userInfo.payloadAccess[chIndex].stdPayloadEnables2Reserved.reset();
1097 userInfo.payloadAccess[chIndex].oemPayloadEnables2Reserved.reset();
1098 // Update SOL status as it is the only supported payload currently.
1099 userInfo.payloadAccess[chIndex]
1100 .stdPayloadEnables1[static_cast<uint8_t>(ipmi::PayloadType::SOL)] =
1101 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)][chIndex];
1102 }
1103}
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301104
1105void UserAccess::readUserData()
1106{
1107 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1108 userLock{*userMutex};
1109
1110 std::ifstream iUsrData(ipmiUserDataFile, std::ios::in | std::ios::binary);
1111 if (!iUsrData.good())
1112 {
George Liu82844ef2024-07-17 17:03:56 +08001113 lg2::error("Error in reading IPMI user data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301114 throw std::ios_base::failure("Error opening IPMI user data file");
1115 }
1116
1117 Json jsonUsersTbl = Json::array();
1118 jsonUsersTbl = Json::parse(iUsrData, nullptr, false);
1119
1120 if (jsonUsersTbl.size() != ipmiMaxUsers)
1121 {
George Liu82844ef2024-07-17 17:03:56 +08001122 lg2::error("Error in reading IPMI user data file - User count issues");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301123 throw std::runtime_error(
1124 "Corrupted IPMI user data file - invalid user count");
1125 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001126
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301127 // user index 0 is reserved, starts with 1
1128 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1129 {
1130 Json userInfo = jsonUsersTbl[usrIndex - 1]; // json array starts with 0.
1131 if (userInfo.is_null())
1132 {
George Liu82844ef2024-07-17 17:03:56 +08001133 lg2::error("Error in reading IPMI user data file - "
1134 "user info corrupted");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301135 throw std::runtime_error(
1136 "Corrupted IPMI user data file - invalid user info");
1137 }
1138 std::string userName = userInfo[jsonUserName].get<std::string>();
Orit Kashanyf7616312025-08-24 04:48:41 -07001139
1140 // Fixed-width username buffer in struct
1141 safeUsernameCopyToBuffer(usersTbl.user[usrIndex].userName,
1142 sizeof(usersTbl.user[usrIndex].userName),
1143 userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301144
1145 std::vector<std::string> privilege =
1146 userInfo[jsonPriv].get<std::vector<std::string>>();
1147 std::vector<bool> ipmiEnabled =
1148 userInfo[jsonIpmiEnabled].get<std::vector<bool>>();
1149 std::vector<bool> linkAuthEnabled =
1150 userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
1151 std::vector<bool> accessCallback =
1152 userInfo[jsonAccCallbk].get<std::vector<bool>>();
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001153
1154 // Payload Enables Processing.
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001155 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1156 stdPayload = {};
1157 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1158 oemPayload = {};
1159 try
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001160 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001161 const auto jsonPayloadEnabled = userInfo.at(payloadEnabledStr);
1162 for (auto payloadNum = 0; payloadNum < payloadsPerByte;
1163 payloadNum++)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001164 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001165 std::ostringstream stdPayloadStream;
1166 std::ostringstream oemPayloadStream;
1167
1168 stdPayloadStream << stdPayloadStr << payloadNum;
1169 oemPayloadStream << oemPayloadStr << payloadNum;
1170
1171 stdPayload[payloadNum] =
1172 jsonPayloadEnabled[stdPayloadStream.str()]
1173 .get<std::array<bool, ipmiMaxChannels>>();
1174 oemPayload[payloadNum] =
1175 jsonPayloadEnabled[oemPayloadStream.str()]
1176 .get<std::array<bool, ipmiMaxChannels>>();
1177
1178 if (stdPayload[payloadNum].size() != ipmiMaxChannels ||
1179 oemPayload[payloadNum].size() != ipmiMaxChannels)
1180 {
George Liu82844ef2024-07-17 17:03:56 +08001181 lg2::error("Error in reading IPMI user data file - "
1182 "payload properties corrupted");
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001183 throw std::runtime_error(
1184 "Corrupted IPMI user data file - payload properties");
1185 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001186 }
1187 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001188 catch (const Json::out_of_range& e)
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001189 {
1190 // Key not found in 'userInfo'; possibly an old JSON file. Use
1191 // default values for all payloads, and SOL payload default is true.
1192 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)].fill(true);
1193 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001194
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301195 if (privilege.size() != ipmiMaxChannels ||
1196 ipmiEnabled.size() != ipmiMaxChannels ||
1197 linkAuthEnabled.size() != ipmiMaxChannels ||
1198 accessCallback.size() != ipmiMaxChannels)
1199 {
George Liu82844ef2024-07-17 17:03:56 +08001200 lg2::error("Error in reading IPMI user data file - "
1201 "properties corrupted");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301202 throw std::runtime_error(
1203 "Corrupted IPMI user data file - properties");
1204 }
1205 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1206 {
1207 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege =
1208 static_cast<uint8_t>(
1209 convertToIPMIPrivilege(privilege[chIndex]));
1210 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled =
1211 ipmiEnabled[chIndex];
1212 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled =
1213 linkAuthEnabled[chIndex];
1214 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
1215 accessCallback[chIndex];
1216 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001217 updatePayloadAccessInUserInfo(stdPayload, oemPayload,
1218 usersTbl.user[usrIndex]);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301219 usersTbl.user[usrIndex].userEnabled =
1220 userInfo[jsonUserEnabled].get<bool>();
1221 usersTbl.user[usrIndex].userInSystem =
1222 userInfo[jsonUserInSys].get<bool>();
1223 usersTbl.user[usrIndex].fixedUserName =
1224 userInfo[jsonFixedUser].get<bool>();
1225 }
1226
George Liu82844ef2024-07-17 17:03:56 +08001227 lg2::debug("User data read from IPMI data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301228 iUsrData.close();
1229 // Update the timestamp
1230 fileLastUpdatedTime = getUpdatedFileTime();
1231 return;
1232}
1233
1234void UserAccess::writeUserData()
1235{
1236 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1237 userLock{*userMutex};
1238
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301239 Json jsonUsersTbl = Json::array();
1240 // user index 0 is reserved, starts with 1
1241 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1242 {
1243 Json jsonUserInfo;
Orit Kashanyf7616312025-08-24 04:48:41 -07001244 jsonUserInfo[jsonUserName] =
1245 safeUsernameString(usersTbl.user[usrIndex].userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301246 std::vector<std::string> privilege(ipmiMaxChannels);
1247 std::vector<bool> ipmiEnabled(ipmiMaxChannels);
1248 std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
1249 std::vector<bool> accessCallback(ipmiMaxChannels);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001250
1251 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1252 stdPayload;
1253 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1254 oemPayload;
1255
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301256 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1257 {
1258 privilege[chIndex] =
1259 convertToSystemPrivilege(static_cast<CommandPrivilege>(
1260 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege));
1261 ipmiEnabled[chIndex] =
1262 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled;
1263 linkAuthEnabled[chIndex] =
1264 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled;
1265 accessCallback[chIndex] =
1266 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback;
1267 }
1268 jsonUserInfo[jsonPriv] = privilege;
1269 jsonUserInfo[jsonIpmiEnabled] = ipmiEnabled;
1270 jsonUserInfo[jsonLinkAuthEnabled] = linkAuthEnabled;
1271 jsonUserInfo[jsonAccCallbk] = accessCallback;
1272 jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
1273 jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
1274 jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001275
1276 readPayloadAccessFromUserInfo(usersTbl.user[usrIndex], stdPayload,
1277 oemPayload);
Patrick Williams1318a5e2024-08-16 15:19:54 -04001278 Json jsonPayloadEnabledInfo =
1279 constructJsonPayloadEnables(stdPayload, oemPayload);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001280 jsonUserInfo[payloadEnabledStr] = jsonPayloadEnabledInfo;
1281
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301282 jsonUsersTbl.push_back(jsonUserInfo);
1283 }
1284
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301285 static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
1286 int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
1287 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1288 if (fd < 0)
1289 {
George Liu82844ef2024-07-17 17:03:56 +08001290 lg2::error("Error in creating temporary IPMI user data file");
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301291 throw std::ios_base::failure(
1292 "Error in creating temporary IPMI user data file");
1293 }
1294 const auto& writeStr = jsonUsersTbl.dump();
1295 if (write(fd, writeStr.c_str(), writeStr.size()) !=
1296 static_cast<ssize_t>(writeStr.size()))
1297 {
1298 close(fd);
George Liu82844ef2024-07-17 17:03:56 +08001299 lg2::error("Error in writing temporary IPMI user data file");
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301300 throw std::ios_base::failure(
1301 "Error in writing temporary IPMI user data file");
1302 }
1303 close(fd);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301304
1305 if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
1306 {
George Liu82844ef2024-07-17 17:03:56 +08001307 lg2::error("Error in renaming temporary IPMI user data file");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301308 throw std::runtime_error("Error in renaming IPMI user data file");
1309 }
1310 // Update the timestamp
1311 fileLastUpdatedTime = getUpdatedFileTime();
1312 return;
1313}
1314
1315bool UserAccess::addUserEntry(const std::string& userName,
1316 const std::string& sysPriv, const bool& enabled)
1317{
1318 UsersTbl* userData = getUsersTblPtr();
1319 size_t freeIndex = 0xFF;
1320 // user index 0 is reserved, starts with 1
1321 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1322 {
Orit Kashanyf7616312025-08-24 04:48:41 -07001323 std::string curName =
1324 safeUsernameString(userData->user[usrIndex].userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301325 if (userName == curName)
1326 {
George Liu82844ef2024-07-17 17:03:56 +08001327 lg2::debug("Username {USER_NAME} exists", "USER_NAME", userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301328 return false; // user name exists.
1329 }
1330
1331 if ((!userData->user[usrIndex].userInSystem) &&
1332 (userData->user[usrIndex].userName[0] == '\0') &&
1333 (freeIndex == 0xFF))
1334 {
1335 freeIndex = usrIndex;
1336 }
1337 }
1338 if (freeIndex == 0xFF)
1339 {
George Liu82844ef2024-07-17 17:03:56 +08001340 lg2::error("No empty slots found");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301341 return false;
1342 }
Orit Kashanyf7616312025-08-24 04:48:41 -07001343
1344 safeUsernameCopyToBuffer(userData->user[freeIndex].userName,
1345 sizeof(userData->user[freeIndex].userName),
1346 userName);
1347
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301348 uint8_t priv =
1349 static_cast<uint8_t>(UserAccess::convertToIPMIPrivilege(sysPriv)) &
1350 privMask;
1351 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1352 {
1353 userData->user[freeIndex].userPrivAccess[chIndex].privilege = priv;
1354 userData->user[freeIndex].userPrivAccess[chIndex].ipmiEnabled = true;
1355 userData->user[freeIndex].userPrivAccess[chIndex].linkAuthEnabled =
1356 true;
1357 userData->user[freeIndex].userPrivAccess[chIndex].accessCallback = true;
1358 }
1359 userData->user[freeIndex].userInSystem = true;
1360 userData->user[freeIndex].userEnabled = enabled;
1361
1362 return true;
1363}
1364
1365void UserAccess::deleteUserIndex(const size_t& usrIdx)
1366{
1367 UsersTbl* userData = getUsersTblPtr();
1368
Orit Kashanyf7616312025-08-24 04:48:41 -07001369 std::string userName = safeUsernameString(userData->user[usrIdx].userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301370 ipmiClearUserEntryPassword(userName);
Orit Kashanyf7616312025-08-24 04:48:41 -07001371 std::memset(userData->user[usrIdx].userName, 0,
1372 sizeof(userData->user[usrIdx].userName));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301373 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1374 {
1375 userData->user[usrIdx].userPrivAccess[chIndex].privilege = privNoAccess;
1376 userData->user[usrIdx].userPrivAccess[chIndex].ipmiEnabled = false;
1377 userData->user[usrIdx].userPrivAccess[chIndex].linkAuthEnabled = false;
1378 userData->user[usrIdx].userPrivAccess[chIndex].accessCallback = false;
1379 }
1380 userData->user[usrIdx].userInSystem = false;
1381 userData->user[usrIdx].userEnabled = false;
1382 return;
1383}
1384
1385void UserAccess::checkAndReloadUserData()
1386{
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001387 std::timespec updateTime = getUpdatedFileTime();
1388 if ((updateTime.tv_sec != fileLastUpdatedTime.tv_sec ||
1389 updateTime.tv_nsec != fileLastUpdatedTime.tv_nsec) ||
1390 (updateTime.tv_sec == 0 && updateTime.tv_nsec == 0))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301391 {
1392 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1393 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1394 readUserData();
1395 }
1396 return;
1397}
1398
1399UsersTbl* UserAccess::getUsersTblPtr()
1400{
1401 // reload data before using it.
1402 checkAndReloadUserData();
1403 return &usersTbl;
1404}
1405
1406void UserAccess::getSystemPrivAndGroups()
1407{
1408 std::map<std::string, PrivAndGroupType> properties;
1409 try
1410 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +07001411 auto method = bus.new_method_call(userMgrService, userMgrObjBasePath,
1412 dBusPropertiesInterface,
1413 getAllPropertiesMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301414 method.append(userMgrInterface);
1415
1416 auto reply = bus.call(method);
1417 reply.read(properties);
1418 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001419 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301420 {
George Liu82844ef2024-07-17 17:03:56 +08001421 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1422 getAllPropertiesMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301423 return;
1424 }
1425 for (const auto& t : properties)
1426 {
1427 auto key = t.first;
1428 if (key == allPrivProperty)
1429 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001430 availablePrivileges = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301431 }
1432 else if (key == allGrpProperty)
1433 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001434 availableGroups = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301435 }
1436 }
1437 // TODO: Implement Supported Privilege & Groups verification logic
1438 return;
1439}
1440
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001441std::timespec UserAccess::getUpdatedFileTime()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301442{
1443 struct stat fileStat;
1444 if (stat(ipmiUserDataFile, &fileStat) != 0)
1445 {
George Liu82844ef2024-07-17 17:03:56 +08001446 lg2::debug("Error in getting last updated time stamp");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001447 return std::timespec{0, 0};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301448 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001449 return fileStat.st_mtim;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301450}
1451
1452void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
1453 std::vector<std::string>& usrGrps,
1454 std::string& usrPriv, bool& usrEnabled)
1455{
1456 for (const auto& t : properties)
1457 {
1458 std::string key = t.first;
1459 if (key == userPrivProperty)
1460 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001461 usrPriv = std::get<std::string>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301462 }
1463 else if (key == userGrpProperty)
1464 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001465 usrGrps = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301466 }
1467 else if (key == userEnabledProperty)
1468 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001469 usrEnabled = std::get<bool>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301470 }
1471 }
1472 return;
1473}
1474
1475int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
1476 std::vector<std::string>& usrGrps,
1477 std::string& usrPriv, bool& usrEnabled)
1478{
1479 auto usrObj = userObjs.find(usersInterface);
1480 if (usrObj != userObjs.end())
1481 {
1482 getUserProperties(usrObj->second, usrGrps, usrPriv, usrEnabled);
1483 return 0;
1484 }
1485 return -EIO;
1486}
1487
arun-pmbbe728c2020-01-10 15:18:04 +05301488void UserAccess::cacheUserDataFile()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301489{
1490 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1491 userLock{*userMutex};
1492 try
1493 {
1494 readUserData();
1495 }
1496 catch (const std::ios_base::failure& e)
1497 { // File is empty, create it for the first time
1498 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1499 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1500 // user index 0 is reserved, starts with 1
1501 for (size_t userIndex = 1; userIndex <= ipmiMaxUsers; ++userIndex)
1502 {
1503 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1504 {
1505 usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
1506 privNoAccess;
Saravanan Palanisamy92d81192019-08-07 18:00:04 +00001507 usersTbl.user[userIndex]
1508 .payloadAccess[chIndex]
1509 .stdPayloadEnables1[static_cast<uint8_t>(
1510 ipmi::PayloadType::SOL)] = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301511 }
1512 }
1513 writeUserData();
1514 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001515 // Create lock file if it does not exist
1516 int fd = open(ipmiUserSignalLockFile, O_CREAT | O_TRUNC | O_SYNC,
1517 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1518 if (fd < 0)
1519 {
George Liu82844ef2024-07-17 17:03:56 +08001520 lg2::error("Error in creating IPMI user signal lock file");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001521 throw std::ios_base::failure(
1522 "Error in creating temporary IPMI user signal lock file");
1523 }
1524 close(fd);
1525
1526 sigHndlrLock = boost::interprocess::file_lock(ipmiUserSignalLockFile);
George Liu1a2e1502022-07-08 12:20:19 +08001527 // Register it for single object and single process either netipmid /
arun-pmbbe728c2020-01-10 15:18:04 +05301528 // host-ipmid
1529 if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
1530 {
George Liu82844ef2024-07-17 17:03:56 +08001531 lg2::debug("Registering signal handler");
arun-pmbbe728c2020-01-10 15:18:04 +05301532 userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
1533 bus,
1534 sdbusplus::bus::match::rules::type::signal() +
1535 sdbusplus::bus::match::rules::interface(dBusObjManager) +
1536 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001537 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001538 userUpdatedSignalHandler(*this, msg);
1539 });
arun-pmbbe728c2020-01-10 15:18:04 +05301540 userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
1541 bus,
1542 sdbusplus::bus::match::rules::type::signal() +
1543 sdbusplus::bus::match::rules::interface(userMgrInterface) +
1544 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001545 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001546 userUpdatedSignalHandler(*this, msg);
1547 });
arun-pmbbe728c2020-01-10 15:18:04 +05301548 userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
1549 bus,
1550 sdbusplus::bus::match::rules::type::signal() +
1551 sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
1552 sdbusplus::bus::match::rules::interface(
1553 dBusPropertiesInterface) +
1554 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
1555 sdbusplus::bus::match::rules::argN(0, usersInterface),
Patrick Williams5d82f472022-07-22 19:26:53 -05001556 [&](sdbusplus::message_t& msg) {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001557 userUpdatedSignalHandler(*this, msg);
1558 });
arun-pmbbe728c2020-01-10 15:18:04 +05301559 signalHndlrObject = true;
1560 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301561 std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
1562 try
1563 {
Khang D Nguyen078aa6a2025-03-06 00:03:42 +07001564 auto method =
1565 bus.new_method_call(userMgrService, userMgrObjBasePath,
1566 dBusObjManager, getManagedObjectsMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301567 auto reply = bus.call(method);
1568 reply.read(managedObjs);
1569 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001570 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301571 {
George Liu82844ef2024-07-17 17:03:56 +08001572 lg2::debug("Failed to excute {METHOD}, path: {PATH}", "METHOD",
1573 getManagedObjectsMethod, "PATH", userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301574 return;
1575 }
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301576 bool updateRequired = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301577 UsersTbl* userData = &usersTbl;
1578 // user index 0 is reserved, starts with 1
1579 for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
1580 {
1581 if ((userData->user[usrIdx].userInSystem) &&
1582 (userData->user[usrIdx].userName[0] != '\0'))
1583 {
1584 std::vector<std::string> usrGrps;
1585 std::string usrPriv;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301586
Orit Kashanyf7616312025-08-24 04:48:41 -07001587 std::string userName =
1588 safeUsernameString(userData->user[usrIdx].userName);
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301589 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1590 tempUserPath /= userName;
1591 std::string usersPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301592
1593 auto usrObj = managedObjs.find(usersPath);
1594 if (usrObj != managedObjs.end())
1595 {
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001596 bool usrEnabled = false;
Patrick Venture3a697ad2019-08-19 11:12:05 -07001597
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301598 // User exist. Lets check and update other fileds
1599 getUserObjProperties(usrObj->second, usrGrps, usrPriv,
1600 usrEnabled);
1601 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
1602 usrGrps.end())
1603 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301604 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301605 // Group "ipmi" is removed so lets remove user in IPMI
1606 deleteUserIndex(usrIdx);
1607 }
1608 else
1609 {
1610 // Group "ipmi" is present so lets update other properties
1611 // in IPMI
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001612 uint8_t priv = UserAccess::convertToIPMIPrivilege(usrPriv) &
1613 privMask;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301614 // Update all channels priv, only if it is not equivalent to
1615 // getUsrMgmtSyncIndex()
1616 if (userData->user[usrIdx]
1617 .userPrivAccess[getUsrMgmtSyncIndex()]
1618 .privilege != priv)
1619 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301620 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301621 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
1622 ++chIndex)
1623 {
1624 userData->user[usrIdx]
1625 .userPrivAccess[chIndex]
1626 .privilege = priv;
1627 }
1628 }
1629 if (userData->user[usrIdx].userEnabled != usrEnabled)
1630 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301631 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301632 userData->user[usrIdx].userEnabled = usrEnabled;
1633 }
1634 }
1635
1636 // We are done with this obj. lets delete from MAP
1637 managedObjs.erase(usrObj);
1638 }
1639 else
1640 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301641 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301642 deleteUserIndex(usrIdx);
1643 }
1644 }
1645 }
1646
1647 // Walk through remnaining managedObj users list
1648 // Add them to ipmi data base
1649 for (const auto& usrObj : managedObjs)
1650 {
1651 std::vector<std::string> usrGrps;
1652 std::string usrPriv, userName;
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001653 bool usrEnabled = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301654 std::string usrObjPath = std::string(usrObj.first);
1655 if (getUserNameFromPath(usrObj.first.str, userName) != 0)
1656 {
George Liu82844ef2024-07-17 17:03:56 +08001657 lg2::error("Error in user object path");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301658 continue;
1659 }
1660 getUserObjProperties(usrObj.second, usrGrps, usrPriv, usrEnabled);
1661 // Add 'ipmi' group users
1662 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
1663 usrGrps.end())
1664 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301665 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301666 // CREATE NEW USER
1667 if (true != addUserEntry(userName, usrPriv, usrEnabled))
1668 {
1669 break;
1670 }
1671 }
1672 }
1673
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301674 if (updateRequired)
1675 {
1676 // All userData slots update done. Lets write the data
1677 writeUserData();
1678 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301679
1680 return;
1681}
1682} // namespace ipmi