blob: 4ec61d52e82d194c33f5f25b4ecb118b952497df [file] [log] [blame]
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#include "user_mgmt.hpp"
17
18#include "apphandler.hpp"
Saravanan Palanisamy77381f12019-05-15 22:33:17 +000019#include "channel_layer.hpp"
Johnathan Manteyfd61fc32021-04-08 11:05:38 -070020#include "channel_mgmt.hpp"
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053021
Suryakanth Sekar90b00c72019-01-16 10:37:57 +053022#include <security/pam_appl.h>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053023#include <sys/stat.h>
24#include <unistd.h>
25
26#include <boost/interprocess/sync/named_recursive_mutex.hpp>
27#include <boost/interprocess/sync/scoped_lock.hpp>
Snehalatha Venkatesh745164c2021-06-25 10:02:25 +000028#include <ipmid/types.hpp>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053029#include <nlohmann/json.hpp>
30#include <phosphor-logging/elog-errors.hpp>
31#include <phosphor-logging/log.hpp>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053032#include <sdbusplus/bus/match.hpp>
33#include <sdbusplus/server/object.hpp>
34#include <xyz/openbmc_project/Common/error.hpp>
35#include <xyz/openbmc_project/User/Common/error.hpp>
36
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050037#include <cerrno>
38#include <fstream>
39#include <regex>
40#include <variant>
41
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053042namespace ipmi
43{
44
45// TODO: Move D-Bus & Object Manager related stuff, to common files
46// D-Bus property related
47static constexpr const char* dBusPropertiesInterface =
48 "org.freedesktop.DBus.Properties";
49static constexpr const char* getAllPropertiesMethod = "GetAll";
50static constexpr const char* propertiesChangedSignal = "PropertiesChanged";
51static constexpr const char* setPropertiesMethod = "Set";
52
53// Object Manager related
54static constexpr const char* dBusObjManager =
55 "org.freedesktop.DBus.ObjectManager";
56static constexpr const char* getManagedObjectsMethod = "GetManagedObjects";
57// Object Manager signals
58static constexpr const char* intfAddedSignal = "InterfacesAdded";
59static constexpr const char* intfRemovedSignal = "InterfacesRemoved";
60
61// Object Mapper related
62static constexpr const char* objMapperService =
63 "xyz.openbmc_project.ObjectMapper";
64static constexpr const char* objMapperPath =
65 "/xyz/openbmc_project/object_mapper";
66static constexpr const char* objMapperInterface =
67 "xyz.openbmc_project.ObjectMapper";
68static constexpr const char* getSubTreeMethod = "GetSubTree";
69static constexpr const char* getObjectMethod = "GetObject";
70
71static constexpr const char* ipmiUserMutex = "ipmi_usr_mutex";
72static constexpr const char* ipmiMutexCleanupLockFile =
73 "/var/lib/ipmi/ipmi_usr_mutex_cleanup";
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +000074static constexpr const char* ipmiUserSignalLockFile =
75 "/var/lib/ipmi/ipmi_usr_signal_mutex";
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053076static constexpr const char* ipmiUserDataFile = "/var/lib/ipmi/ipmi_user.json";
77static constexpr const char* ipmiGrpName = "ipmi";
78static constexpr size_t privNoAccess = 0xF;
79static constexpr size_t privMask = 0xF;
80
81// User manager related
82static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user";
83static constexpr const char* userObjBasePath = "/xyz/openbmc_project/user";
84static constexpr const char* userMgrInterface =
85 "xyz.openbmc_project.User.Manager";
86static constexpr const char* usersInterface =
87 "xyz.openbmc_project.User.Attributes";
88static constexpr const char* deleteUserInterface =
89 "xyz.openbmc_project.Object.Delete";
90
91static constexpr const char* createUserMethod = "CreateUser";
92static constexpr const char* deleteUserMethod = "Delete";
93static constexpr const char* renameUserMethod = "RenameUser";
94// User manager signal memebers
95static constexpr const char* userRenamedSignal = "UserRenamed";
96// Mgr interface properties
97static constexpr const char* allPrivProperty = "AllPrivileges";
98static constexpr const char* allGrpProperty = "AllGroups";
99// User interface properties
100static constexpr const char* userPrivProperty = "UserPrivilege";
101static constexpr const char* userGrpProperty = "UserGroups";
102static constexpr const char* userEnabledProperty = "UserEnabled";
103
104static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = {
105 "priv-reserved", // PRIVILEGE_RESERVED - 0
106 "priv-callback", // PRIVILEGE_CALLBACK - 1
107 "priv-user", // PRIVILEGE_USER - 2
108 "priv-operator", // PRIVILEGE_OPERATOR - 3
109 "priv-admin", // PRIVILEGE_ADMIN - 4
110 "priv-custom" // PRIVILEGE_OEM - 5
111};
112
113using namespace phosphor::logging;
114using Json = nlohmann::json;
115
Vernon Mauery16b86932019-05-01 08:36:11 -0700116using PrivAndGroupType = std::variant<std::string, std::vector<std::string>>;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530117
118using NoResource =
Willy Tu523e2d12023-09-05 11:36:48 -0700119 sdbusplus::error::xyz::openbmc_project::user::common::NoResource;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530120
121using InternalFailure =
Willy Tu523e2d12023-09-05 11:36:48 -0700122 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530123
Lei YU4b0ddb62019-01-25 16:43:50 +0800124std::unique_ptr<sdbusplus::bus::match_t> userUpdatedSignal
125 __attribute__((init_priority(101)));
126std::unique_ptr<sdbusplus::bus::match_t> userMgrRenamedSignal
127 __attribute__((init_priority(101)));
128std::unique_ptr<sdbusplus::bus::match_t> userPropertiesSignal
129 __attribute__((init_priority(101)));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530130
131// TODO: Below code can be removed once it is moved to common layer libmiscutil
Patrick Williams5d82f472022-07-22 19:26:53 -0500132std::string getUserService(sdbusplus::bus_t& bus, const std::string& intf,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530133 const std::string& path)
134{
135 auto mapperCall = bus.new_method_call(objMapperService, objMapperPath,
136 objMapperInterface, getObjectMethod);
137
138 mapperCall.append(path);
139 mapperCall.append(std::vector<std::string>({intf}));
140
141 auto mapperResponseMsg = bus.call(mapperCall);
142
143 std::map<std::string, std::vector<std::string>> mapperResponse;
144 mapperResponseMsg.read(mapperResponse);
145
146 if (mapperResponse.begin() == mapperResponse.end())
147 {
148 throw sdbusplus::exception::SdBusError(
149 -EIO, "ERROR in reading the mapper response");
150 }
151
152 return mapperResponse.begin()->first;
153}
154
Patrick Williams5d82f472022-07-22 19:26:53 -0500155void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530156 const std::string& objPath, const std::string& interface,
157 const std::string& property,
158 const DbusUserPropVariant& value)
159{
160 try
161 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500162 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
163 dBusPropertiesInterface,
164 setPropertiesMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530165 method.append(interface, property, value);
166 bus.call(method);
167 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500168 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530169 {
170 log<level::ERR>("Failed to set property",
171 entry("PROPERTY=%s", property.c_str()),
172 entry("PATH=%s", objPath.c_str()),
173 entry("INTERFACE=%s", interface.c_str()));
174 throw;
175 }
176}
177
Willy Tu523e2d12023-09-05 11:36:48 -0700178std::string getUserServiceName()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530179{
Patrick Williams5d82f472022-07-22 19:26:53 -0500180 static sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530181 static std::string userMgmtService;
182 if (userMgmtService.empty())
183 {
184 try
185 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500186 userMgmtService = ipmi::getUserService(bus, userMgrInterface,
187 userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530188 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500189 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530190 {
191 userMgmtService.clear();
192 }
193 }
194 return userMgmtService;
195}
196
197UserAccess& getUserAccessObject()
198{
199 static UserAccess userAccess;
200 return userAccess;
201}
202
203int getUserNameFromPath(const std::string& path, std::string& userName)
204{
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530205 sdbusplus::message::object_path objPath(path);
206 userName.assign(objPath.filename());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530207 return 0;
208}
209
210void userUpdateHelper(UserAccess& usrAccess, const UserUpdateEvent& userEvent,
211 const std::string& userName, const std::string& priv,
212 const bool& enabled, const std::string& newUserName)
213{
214 UsersTbl* userData = usrAccess.getUsersTblPtr();
215 if (userEvent == UserUpdateEvent::userCreated)
216 {
217 if (usrAccess.addUserEntry(userName, priv, enabled) == false)
218 {
219 return;
220 }
221 }
222 else
223 {
224 // user index 0 is reserved, starts with 1
225 size_t usrIndex = 1;
226 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
227 {
228 std::string curName(
229 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
230 ipmiMaxUserName);
231 if (userName == curName)
232 {
233 break; // found the entry
234 }
235 }
236 if (usrIndex > ipmiMaxUsers)
237 {
238 log<level::DEBUG>("User not found for signal",
239 entry("USER_NAME=%s", userName.c_str()),
240 entry("USER_EVENT=%d", userEvent));
241 return;
242 }
243 switch (userEvent)
244 {
245 case UserUpdateEvent::userDeleted:
246 {
247 usrAccess.deleteUserIndex(usrIndex);
248 break;
249 }
250 case UserUpdateEvent::userPrivUpdated:
251 {
252 uint8_t userPriv =
253 static_cast<uint8_t>(
254 UserAccess::convertToIPMIPrivilege(priv)) &
255 privMask;
256 // Update all channels privileges, only if it is not equivalent
257 // to getUsrMgmtSyncIndex()
258 if (userData->user[usrIndex]
259 .userPrivAccess[UserAccess::getUsrMgmtSyncIndex()]
260 .privilege != userPriv)
261 {
262 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
263 ++chIndex)
264 {
265 userData->user[usrIndex]
266 .userPrivAccess[chIndex]
267 .privilege = userPriv;
268 }
269 }
270 break;
271 }
272 case UserUpdateEvent::userRenamed:
273 {
274 std::fill(
275 static_cast<uint8_t*>(userData->user[usrIndex].userName),
276 static_cast<uint8_t*>(userData->user[usrIndex].userName) +
277 sizeof(userData->user[usrIndex].userName),
278 0);
279 std::strncpy(
280 reinterpret_cast<char*>(userData->user[usrIndex].userName),
281 newUserName.c_str(), ipmiMaxUserName);
282 ipmiRenameUserEntryPassword(userName, newUserName);
283 break;
284 }
285 case UserUpdateEvent::userStateUpdated:
286 {
287 userData->user[usrIndex].userEnabled = enabled;
288 break;
289 }
290 default:
291 {
292 log<level::ERR>("Unhandled user event",
293 entry("USER_EVENT=%d", userEvent));
294 return;
295 }
296 }
297 }
298 usrAccess.writeUserData();
299 log<level::DEBUG>("User event handled successfully",
300 entry("USER_NAME=%s", userName.c_str()),
301 entry("USER_EVENT=%d", userEvent));
302
303 return;
304}
305
Patrick Williams5d82f472022-07-22 19:26:53 -0500306void userUpdatedSignalHandler(UserAccess& usrAccess, sdbusplus::message_t& msg)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530307{
Patrick Williams5d82f472022-07-22 19:26:53 -0500308 static sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530309 std::string signal = msg.get_member();
Patrick Venture3a697ad2019-08-19 11:12:05 -0700310 std::string userName, priv, newUserName;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530311 std::vector<std::string> groups;
312 bool enabled = false;
313 UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent;
314 if (signal == intfAddedSignal)
315 {
316 DbusUserObjPath objPath;
317 DbusUserObjValue objValue;
318 msg.read(objPath, objValue);
319 getUserNameFromPath(objPath.str, userName);
320 if (usrAccess.getUserObjProperties(objValue, groups, priv, enabled) !=
321 0)
322 {
323 return;
324 }
325 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
326 groups.end())
327 {
328 return;
329 }
330 userEvent = UserUpdateEvent::userCreated;
331 }
332 else if (signal == intfRemovedSignal)
333 {
334 DbusUserObjPath objPath;
335 std::vector<std::string> interfaces;
336 msg.read(objPath, interfaces);
337 getUserNameFromPath(objPath.str, userName);
338 userEvent = UserUpdateEvent::userDeleted;
339 }
340 else if (signal == userRenamedSignal)
341 {
342 msg.read(userName, newUserName);
343 userEvent = UserUpdateEvent::userRenamed;
344 }
345 else if (signal == propertiesChangedSignal)
346 {
347 getUserNameFromPath(msg.get_path(), userName);
348 }
349 else
350 {
351 log<level::ERR>("Unknown user update signal",
352 entry("SIGNAL=%s", signal.c_str()));
353 return;
354 }
355
356 if (signal.empty() || userName.empty() ||
357 (signal == userRenamedSignal && newUserName.empty()))
358 {
359 log<level::ERR>("Invalid inputs received");
360 return;
361 }
362
363 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
364 userLock{*(usrAccess.userMutex)};
365 usrAccess.checkAndReloadUserData();
366
367 if (signal == propertiesChangedSignal)
368 {
369 std::string intfName;
370 DbusUserObjProperties chProperties;
371 msg.read(intfName, chProperties); // skip reading 3rd argument.
372 for (const auto& prop : chProperties)
373 {
374 userEvent = UserUpdateEvent::reservedEvent;
375 std::string member = prop.first;
376 if (member == userPrivProperty)
377 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700378 priv = std::get<std::string>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530379 userEvent = UserUpdateEvent::userPrivUpdated;
380 }
381 else if (member == userGrpProperty)
382 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700383 groups = std::get<std::vector<std::string>>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530384 userEvent = UserUpdateEvent::userGrpUpdated;
385 }
386 else if (member == userEnabledProperty)
387 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700388 enabled = std::get<bool>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530389 userEvent = UserUpdateEvent::userStateUpdated;
390 }
391 // Process based on event type.
392 if (userEvent == UserUpdateEvent::userGrpUpdated)
393 {
394 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
395 groups.end())
396 {
397 // remove user from ipmi user list.
398 userUpdateHelper(usrAccess, UserUpdateEvent::userDeleted,
399 userName, priv, enabled, newUserName);
400 }
401 else
402 {
403 DbusUserObjProperties properties;
404 try
405 {
406 auto method = bus.new_method_call(
407 getUserServiceName().c_str(), msg.get_path(),
408 dBusPropertiesInterface, getAllPropertiesMethod);
409 method.append(usersInterface);
410 auto reply = bus.call(method);
411 reply.read(properties);
412 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500413 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530414 {
415 log<level::DEBUG>(
416 "Failed to excute method",
417 entry("METHOD=%s", getAllPropertiesMethod),
418 entry("PATH=%s", msg.get_path()));
419 return;
420 }
421 usrAccess.getUserProperties(properties, groups, priv,
422 enabled);
423 // add user to ipmi user list.
424 userUpdateHelper(usrAccess, UserUpdateEvent::userCreated,
425 userName, priv, enabled, newUserName);
426 }
427 }
428 else if (userEvent != UserUpdateEvent::reservedEvent)
429 {
430 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
431 newUserName);
432 }
433 }
434 }
435 else if (userEvent != UserUpdateEvent::reservedEvent)
436 {
437 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
438 newUserName);
439 }
440 return;
441}
442
443UserAccess::~UserAccess()
444{
445 if (signalHndlrObject)
446 {
447 userUpdatedSignal.reset();
448 userMgrRenamedSignal.reset();
449 userPropertiesSignal.reset();
450 sigHndlrLock.unlock();
451 }
452}
453
454UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection())
455{
456 std::ofstream mutexCleanUpFile;
457 mutexCleanUpFile.open(ipmiMutexCleanupLockFile,
458 std::ofstream::out | std::ofstream::app);
459 if (!mutexCleanUpFile.good())
460 {
461 log<level::DEBUG>("Unable to open mutex cleanup file");
462 return;
463 }
464 mutexCleanUpFile.close();
465 mutexCleanupLock = boost::interprocess::file_lock(ipmiMutexCleanupLockFile);
466 if (mutexCleanupLock.try_lock())
467 {
468 boost::interprocess::named_recursive_mutex::remove(ipmiUserMutex);
469 }
470 mutexCleanupLock.lock_sharable();
471 userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>(
472 boost::interprocess::open_or_create, ipmiUserMutex);
473
arun-pmbbe728c2020-01-10 15:18:04 +0530474 cacheUserDataFile();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530475 getSystemPrivAndGroups();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530476}
477
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530478UserInfo* UserAccess::getUserInfo(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530479{
480 checkAndReloadUserData();
481 return &usersTbl.user[userId];
482}
483
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530484void UserAccess::setUserInfo(const uint8_t userId, UserInfo* userInfo)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530485{
486 checkAndReloadUserData();
487 std::copy(reinterpret_cast<uint8_t*>(userInfo),
488 reinterpret_cast<uint8_t*>(userInfo) + sizeof(*userInfo),
489 reinterpret_cast<uint8_t*>(&usersTbl.user[userId]));
490 writeUserData();
491}
492
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530493bool UserAccess::isValidChannel(const uint8_t chNum)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530494{
495 return (chNum < ipmiMaxChannels);
496}
497
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530498bool UserAccess::isValidUserId(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530499{
500 return ((userId <= ipmiMaxUsers) && (userId != reservedUserId));
501}
502
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530503bool UserAccess::isValidPrivilege(const uint8_t priv)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530504{
jayaprakash Mutyala0e2dbee2019-12-26 13:03:04 +0000505 // Callback privilege is deprecated in OpenBMC
Alexander Filippovfc24fa52022-02-01 14:57:59 +0300506 return isValidPrivLimit(priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530507}
508
509uint8_t UserAccess::getUsrMgmtSyncIndex()
510{
Johnathan Manteyfd61fc32021-04-08 11:05:38 -0700511 // Identify the IPMI channel used to assign system user privilege levels
512 // in phosphor-user-manager. The default value is IPMI Channel 1. To
513 // assign a different channel add:
514 // "is_management_nic" : true
515 // into the channel_config.json file describing the assignment of the IPMI
516 // channels. It is only necessary to add the string above to ONE record in
517 // the channel_config.json file. All other records will be automatically
518 // assigned a "false" value.
519 return getChannelConfigObject().getManagementNICID();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530520}
521
522CommandPrivilege UserAccess::convertToIPMIPrivilege(const std::string& value)
523{
524 auto iter = std::find(ipmiPrivIndex.begin(), ipmiPrivIndex.end(), value);
525 if (iter == ipmiPrivIndex.end())
526 {
527 if (value == "")
528 {
529 return static_cast<CommandPrivilege>(privNoAccess);
530 }
531 log<level::ERR>("Error in converting to IPMI privilege",
532 entry("PRIV=%s", value.c_str()));
533 throw std::out_of_range("Out of range - convertToIPMIPrivilege");
534 }
535 else
536 {
537 return static_cast<CommandPrivilege>(
538 std::distance(ipmiPrivIndex.begin(), iter));
539 }
540}
541
542std::string UserAccess::convertToSystemPrivilege(const CommandPrivilege& value)
543{
544 if (value == static_cast<CommandPrivilege>(privNoAccess))
545 {
546 return "";
547 }
548 try
549 {
550 return ipmiPrivIndex.at(value);
551 }
552 catch (const std::out_of_range& e)
553 {
554 log<level::ERR>("Error in converting to system privilege",
555 entry("PRIV=%d", static_cast<uint8_t>(value)));
556 throw std::out_of_range("Out of range - convertToSystemPrivilege");
557 }
558}
559
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000560bool UserAccess::isValidUserName(const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530561{
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000562 if (userName.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530563 {
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000564 log<level::ERR>("userName is empty");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530565 return false;
566 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530567 if (!std::regex_match(userName.c_str(),
568 std::regex("[a-zA-z_][a-zA-Z_0-9]*")))
569 {
570 log<level::ERR>("Unsupported characters in user name");
571 return false;
572 }
573 if (userName == "root")
574 {
575 log<level::ERR>("Invalid user name - root");
576 return false;
577 }
578 std::map<DbusUserObjPath, DbusUserObjValue> properties;
579 try
580 {
581 auto method = bus.new_method_call(getUserServiceName().c_str(),
582 userMgrObjBasePath, dBusObjManager,
583 getManagedObjectsMethod);
584 auto reply = bus.call(method);
585 reply.read(properties);
586 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500587 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530588 {
589 log<level::ERR>("Failed to excute method",
590 entry("METHOD=%s", getSubTreeMethod),
591 entry("PATH=%s", userMgrObjBasePath));
592 return false;
593 }
594
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530595 sdbusplus::message::object_path tempUserPath(userObjBasePath);
596 tempUserPath /= userName;
597 std::string usersPath(tempUserPath);
598
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530599 if (properties.find(usersPath) != properties.end())
600 {
601 log<level::DEBUG>("User name already exists",
602 entry("USER_NAME=%s", userName.c_str()));
603 return false;
604 }
605
606 return true;
607}
608
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530609/** @brief Information exchanged by pam module and application.
610 *
611 * @param[in] numMsg - length of the array of pointers,msg.
612 *
613 * @param[in] msg - pointer to an array of pointers to pam_message structure
614 *
615 * @param[out] resp - struct pam response array
616 *
617 * @param[in] appdataPtr - member of pam_conv structure
618 *
619 * @return the response in pam response structure.
620 */
621
622static int pamFunctionConversation(int numMsg, const struct pam_message** msg,
623 struct pam_response** resp, void* appdataPtr)
624{
625 if (appdataPtr == nullptr)
626 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530627 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530628 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530629
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530630 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
631 {
632 return PAM_CONV_ERR;
633 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530634
635 for (int i = 0; i < numMsg; ++i)
636 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530637 /* Ignore all PAM messages except prompting for hidden input */
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530638 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
639 {
640 continue;
641 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530642
643 /* Assume PAM is only prompting for the password as hidden input */
644 /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */
645
646 char* appPass = reinterpret_cast<char*>(appdataPtr);
647 size_t appPassSize = std::strlen(appPass);
648
649 if (appPassSize >= PAM_MAX_RESP_SIZE)
650 {
651 return PAM_CONV_ERR;
652 }
653
654 char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1));
655 if (pass == nullptr)
656 {
657 return PAM_BUF_ERR;
658 }
659
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500660 void* ptr = calloc(static_cast<size_t>(numMsg),
661 sizeof(struct pam_response));
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530662 if (ptr == nullptr)
663 {
664 free(pass);
665 return PAM_BUF_ERR;
666 }
667
668 std::strncpy(pass, appPass, appPassSize + 1);
669
670 *resp = reinterpret_cast<pam_response*>(ptr);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530671 resp[i]->resp = pass;
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530672
673 return PAM_SUCCESS;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530674 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530675
676 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530677}
678
679/** @brief Updating the PAM password
680 *
681 * @param[in] username - username in string
682 *
683 * @param[in] password - new password in string
684 *
685 * @return status
686 */
687
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000688int pamUpdatePasswd(const char* username, const char* password)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530689{
690 const struct pam_conv localConversation = {pamFunctionConversation,
691 const_cast<char*>(password)};
692 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
693
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500694 int retval = pam_start("passwd", username, &localConversation,
695 &localAuthHandle);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530696
697 if (retval != PAM_SUCCESS)
698 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000699 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530700 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000701
702 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
703 if (retval != PAM_SUCCESS)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530704 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000705 pam_end(localAuthHandle, retval);
706 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530707 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000708
709 return pam_end(localAuthHandle, PAM_SUCCESS);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530710}
711
Ayushi Smriti02650d52019-05-15 11:59:09 +0000712bool pamUserCheckAuthenticate(std::string_view username,
713 std::string_view password)
714{
715 const struct pam_conv localConversation = {
716 pamFunctionConversation, const_cast<char*>(password.data())};
717
718 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
719
720 if (pam_start("dropbear", username.data(), &localConversation,
721 &localAuthHandle) != PAM_SUCCESS)
722 {
723 log<level::ERR>("User Authentication Failure");
724 return false;
725 }
726
727 int retval = pam_authenticate(localAuthHandle,
728 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
729
730 if (retval != PAM_SUCCESS)
731 {
732 log<level::DEBUG>("pam_authenticate returned failure",
733 entry("ERROR=%d", retval));
734
735 pam_end(localAuthHandle, retval);
736 return false;
737 }
738
739 if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
740 PAM_SUCCESS)
741 {
742 pam_end(localAuthHandle, PAM_SUCCESS);
743 return false;
744 }
745
746 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
747 {
748 return false;
749 }
750 return true;
751}
752
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000753Cc UserAccess::setSpecialUserPassword(const std::string& userName,
Vernon Mauery1e22a0f2021-07-30 13:36:54 -0700754 const SecureString& userPassword)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530755{
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000756 if (pamUpdatePasswd(userName.c_str(), userPassword.c_str()) != PAM_SUCCESS)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530757 {
758 log<level::DEBUG>("Failed to update password");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000759 return ccUnspecifiedError;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530760 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000761 return ccSuccess;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530762}
763
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000764Cc UserAccess::setUserPassword(const uint8_t userId, const char* userPassword)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530765{
766 std::string userName;
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000767 if (ipmiUserGetUserName(userId, userName) != ccSuccess)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530768 {
769 log<level::DEBUG>("User Name not found",
Ayushi Smriti05ad3412019-10-16 16:10:18 +0530770 entry("USER-ID=%d", (uint8_t)userId));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000771 return ccParmOutOfRange;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530772 }
Snehalatha Venkatesh61024d72021-04-08 16:24:39 +0000773
774 ipmi::SecureString passwd;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530775 passwd.assign(reinterpret_cast<const char*>(userPassword), 0,
776 maxIpmi20PasswordSize);
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000777 int retval = pamUpdatePasswd(userName.c_str(), passwd.c_str());
778
779 switch (retval)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530780 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000781 case PAM_SUCCESS:
782 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000783 return ccSuccess;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000784 }
785 case PAM_AUTHTOK_ERR:
786 {
787 log<level::DEBUG>("Bad authentication token");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000788 return ccInvalidFieldRequest;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000789 }
790 default:
791 {
792 log<level::DEBUG>("Failed to update password",
793 entry("USER-ID=%d", (uint8_t)userId));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000794 return ccUnspecifiedError;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000795 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530796 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530797}
798
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000799Cc UserAccess::setUserEnabledState(const uint8_t userId,
800 const bool& enabledState)
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530801{
802 if (!isValidUserId(userId))
803 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000804 return ccParmOutOfRange;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530805 }
806 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
807 userLock{*userMutex};
808 UserInfo* userInfo = getUserInfo(userId);
809 std::string userName;
810 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
811 ipmiMaxUserName);
812 if (userName.empty())
813 {
814 log<level::DEBUG>("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000815 return ccUnspecifiedError;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530816 }
817 if (userInfo->userEnabled != enabledState)
818 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530819 sdbusplus::message::object_path tempUserPath(userObjBasePath);
820 tempUserPath /= userName;
821 std::string userPath(tempUserPath);
Patrick Venture99d1ba02019-02-21 15:11:24 -0800822 setDbusProperty(bus, getUserServiceName(), userPath, usersInterface,
823 userEnabledProperty, enabledState);
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530824 userInfo->userEnabled = enabledState;
825 try
826 {
827 writeUserData();
828 }
829 catch (const std::exception& e)
830 {
831 log<level::DEBUG>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000832 return ccUnspecifiedError;
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530833 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530834 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000835 return ccSuccess;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530836}
837
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000838Cc UserAccess::setUserPayloadAccess(const uint8_t chNum,
839 const uint8_t operation,
840 const uint8_t userId,
841 const PayloadAccess& payloadAccess)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000842{
843 constexpr uint8_t enable = 0x0;
844 constexpr uint8_t disable = 0x1;
845
846 if (!isValidChannel(chNum))
847 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000848 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000849 }
850 if (!isValidUserId(userId))
851 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000852 return ccParmOutOfRange;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000853 }
854 if (operation != enable && operation != disable)
855 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000856 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000857 }
858 // Check operation & payloadAccess if required.
859 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
860 userLock{*userMutex};
861 UserInfo* userInfo = getUserInfo(userId);
862
863 if (operation == enable)
864 {
865 userInfo->payloadAccess[chNum].stdPayloadEnables1 |=
866 payloadAccess.stdPayloadEnables1;
867
868 userInfo->payloadAccess[chNum].oemPayloadEnables1 |=
869 payloadAccess.oemPayloadEnables1;
870 }
871 else
872 {
873 userInfo->payloadAccess[chNum].stdPayloadEnables1 &=
874 ~(payloadAccess.stdPayloadEnables1);
875
876 userInfo->payloadAccess[chNum].oemPayloadEnables1 &=
877 ~(payloadAccess.oemPayloadEnables1);
878 }
879
880 try
881 {
882 writeUserData();
883 }
884 catch (const std::exception& e)
885 {
886 log<level::ERR>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000887 return ccUnspecifiedError;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000888 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000889 return ccSuccess;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000890}
891
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000892Cc UserAccess::setUserPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
893 const UserPrivAccess& privAccess,
894 const bool& otherPrivUpdates)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530895{
896 if (!isValidChannel(chNum))
897 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000898 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530899 }
900 if (!isValidUserId(userId))
901 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000902 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530903 }
904 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
905 userLock{*userMutex};
906 UserInfo* userInfo = getUserInfo(userId);
907 std::string userName;
908 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
909 ipmiMaxUserName);
910 if (userName.empty())
911 {
912 log<level::DEBUG>("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000913 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530914 }
915 std::string priv = convertToSystemPrivilege(
916 static_cast<CommandPrivilege>(privAccess.privilege));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530917 uint8_t syncIndex = getUsrMgmtSyncIndex();
918 if (chNum == syncIndex &&
919 privAccess.privilege != userInfo->userPrivAccess[syncIndex].privilege)
920 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530921 sdbusplus::message::object_path tempUserPath(userObjBasePath);
922 tempUserPath /= userName;
923 std::string userPath(tempUserPath);
Patrick Venture99d1ba02019-02-21 15:11:24 -0800924 setDbusProperty(bus, getUserServiceName(), userPath, usersInterface,
925 userPrivProperty, priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530926 }
927 userInfo->userPrivAccess[chNum].privilege = privAccess.privilege;
928
929 if (otherPrivUpdates)
930 {
931 userInfo->userPrivAccess[chNum].ipmiEnabled = privAccess.ipmiEnabled;
932 userInfo->userPrivAccess[chNum].linkAuthEnabled =
933 privAccess.linkAuthEnabled;
934 userInfo->userPrivAccess[chNum].accessCallback =
935 privAccess.accessCallback;
936 }
937 try
938 {
939 writeUserData();
940 }
941 catch (const std::exception& e)
942 {
943 log<level::DEBUG>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000944 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530945 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000946 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530947}
948
949uint8_t UserAccess::getUserId(const std::string& userName)
950{
951 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
952 userLock{*userMutex};
953 checkAndReloadUserData();
954 // user index 0 is reserved, starts with 1
955 size_t usrIndex = 1;
956 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
957 {
958 std::string curName(
959 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
960 ipmiMaxUserName);
961 if (userName == curName)
962 {
963 break; // found the entry
964 }
965 }
966 if (usrIndex > ipmiMaxUsers)
967 {
968 log<level::DEBUG>("User not found",
969 entry("USER_NAME=%s", userName.c_str()));
970 return invalidUserId;
971 }
972
973 return usrIndex;
974}
975
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000976Cc UserAccess::getUserName(const uint8_t userId, std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530977{
978 if (!isValidUserId(userId))
979 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000980 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530981 }
982 UserInfo* userInfo = getUserInfo(userId);
983 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
984 ipmiMaxUserName);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000985 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530986}
987
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530988bool UserAccess::isIpmiInAvailableGroupList()
989{
990 if (std::find(availableGroups.begin(), availableGroups.end(),
991 ipmiGrpName) != availableGroups.end())
992 {
993 return true;
994 }
995 if (availableGroups.empty())
996 {
997 // available groups shouldn't be empty, re-query
998 getSystemPrivAndGroups();
999 if (std::find(availableGroups.begin(), availableGroups.end(),
1000 ipmiGrpName) != availableGroups.end())
1001 {
1002 return true;
1003 }
1004 }
1005 return false;
1006}
1007
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001008Cc UserAccess::setUserName(const uint8_t userId, const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301009{
1010 if (!isValidUserId(userId))
1011 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001012 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301013 }
1014
1015 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1016 userLock{*userMutex};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301017 std::string oldUser;
1018 getUserName(userId, oldUser);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301019
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001020 if (oldUser == userName)
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301021 {
1022 // requesting to set the same user name, return success.
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001023 return ccSuccess;
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301024 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001025
1026 bool validUser = isValidUserName(userName);
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301027 UserInfo* userInfo = getUserInfo(userId);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001028 if (userName.empty() && !oldUser.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301029 {
1030 // Delete existing user
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301031 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1032 tempUserPath /= oldUser;
1033 std::string userPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301034 try
1035 {
1036 auto method = bus.new_method_call(
1037 getUserServiceName().c_str(), userPath.c_str(),
1038 deleteUserInterface, deleteUserMethod);
1039 auto reply = bus.call(method);
1040 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001041 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301042 {
1043 log<level::DEBUG>("Failed to excute method",
1044 entry("METHOD=%s", deleteUserMethod),
1045 entry("PATH=%s", userPath.c_str()));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001046 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301047 }
Richard Marian Thomaiyar02710bb2018-11-28 20:42:25 +05301048 deleteUserIndex(userId);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301049 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001050 else if (oldUser.empty() && !userName.empty() && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301051 {
1052 try
1053 {
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301054 if (!isIpmiInAvailableGroupList())
1055 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001056 return ccUnspecifiedError;
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301057 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301058 // Create new user
1059 auto method = bus.new_method_call(
1060 getUserServiceName().c_str(), userMgrObjBasePath,
1061 userMgrInterface, createUserMethod);
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001062 method.append(userName.c_str(), availableGroups,
1063 ipmiPrivIndex[PRIVILEGE_USER], false);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301064 auto reply = bus.call(method);
1065 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001066 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301067 {
1068 log<level::DEBUG>("Failed to excute method",
1069 entry("METHOD=%s", createUserMethod),
1070 entry("PATH=%s", userMgrObjBasePath));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001071 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301072 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001073
1074 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1075 std::memcpy(userInfo->userName,
1076 static_cast<const void*>(userName.data()), userName.size());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301077 userInfo->userInSystem = true;
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001078 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1079 {
1080 userInfo->userPrivAccess[chIndex].privilege =
1081 static_cast<uint8_t>(PRIVILEGE_USER);
1082 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301083 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001084 else if (oldUser != userName && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301085 {
1086 try
1087 {
1088 // User rename
1089 auto method = bus.new_method_call(
1090 getUserServiceName().c_str(), userMgrObjBasePath,
1091 userMgrInterface, renameUserMethod);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001092 method.append(oldUser.c_str(), userName.c_str());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301093 auto reply = bus.call(method);
1094 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001095 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301096 {
1097 log<level::DEBUG>("Failed to excute method",
1098 entry("METHOD=%s", renameUserMethod),
1099 entry("PATH=%s", userMgrObjBasePath));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001100 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301101 }
1102 std::fill(static_cast<uint8_t*>(userInfo->userName),
1103 static_cast<uint8_t*>(userInfo->userName) +
1104 sizeof(userInfo->userName),
1105 0);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001106
1107 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1108 std::memcpy(userInfo->userName,
1109 static_cast<const void*>(userName.data()), userName.size());
1110
1111 ipmiRenameUserEntryPassword(oldUser, userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301112 userInfo->userInSystem = true;
1113 }
1114 else if (!validUser)
1115 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001116 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301117 }
1118 try
1119 {
1120 writeUserData();
1121 }
1122 catch (const std::exception& e)
1123 {
1124 log<level::DEBUG>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001125 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301126 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001127 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301128}
1129
1130static constexpr const char* jsonUserName = "user_name";
1131static constexpr const char* jsonPriv = "privilege";
1132static constexpr const char* jsonIpmiEnabled = "ipmi_enabled";
1133static constexpr const char* jsonLinkAuthEnabled = "link_auth_enabled";
1134static constexpr const char* jsonAccCallbk = "access_callback";
1135static constexpr const char* jsonUserEnabled = "user_enabled";
1136static constexpr const char* jsonUserInSys = "user_in_system";
1137static constexpr const char* jsonFixedUser = "fixed_user_name";
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001138static constexpr const char* payloadEnabledStr = "payload_enabled";
1139static constexpr const char* stdPayloadStr = "std_payload";
1140static constexpr const char* oemPayloadStr = "OEM_payload";
1141
1142/** @brief to construct a JSON object from the given payload access details.
1143 *
1144 * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. (input)
1145 * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. (input)
1146 *
1147 * @details Sample output JSON object format :
1148 * "payload_enabled":{
1149 * "OEM_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1150 * "OEM_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1151 * "OEM_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1152 * "OEM_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1153 * "OEM_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1154 * "OEM_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1155 * "OEM_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1156 * "OEM_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1157 * "std_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1158 * "std_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1159 * "std_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1160 * "std_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1161 * "std_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1162 * "std_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1163 * "std_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1164 * "std_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1165 * }
1166 */
1167static const Json constructJsonPayloadEnables(
1168 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1169 stdPayload,
1170 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1171 oemPayload)
1172{
1173 Json jsonPayloadEnabled;
1174
1175 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1176 {
1177 std::ostringstream stdPayloadStream;
1178 std::ostringstream oemPayloadStream;
1179
1180 stdPayloadStream << stdPayloadStr << payloadNum;
1181 oemPayloadStream << oemPayloadStr << payloadNum;
1182
1183 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1184 stdPayloadStream.str(), stdPayload[payloadNum]));
1185
1186 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1187 oemPayloadStream.str(), oemPayload[payloadNum]));
1188 }
1189 return jsonPayloadEnabled;
1190}
1191
1192void UserAccess::readPayloadAccessFromUserInfo(
1193 const UserInfo& userInfo,
1194 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& stdPayload,
1195 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& oemPayload)
1196{
1197 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1198 {
1199 for (auto chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1200 {
1201 stdPayload[payloadNum][chIndex] =
1202 userInfo.payloadAccess[chIndex].stdPayloadEnables1[payloadNum];
1203
1204 oemPayload[payloadNum][chIndex] =
1205 userInfo.payloadAccess[chIndex].oemPayloadEnables1[payloadNum];
1206 }
1207 }
1208}
1209
1210void UserAccess::updatePayloadAccessInUserInfo(
1211 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1212 stdPayload,
Willy Tu11d68892022-01-20 10:37:34 -08001213 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&,
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001214 UserInfo& userInfo)
1215{
1216 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1217 {
1218 // Ensure that reserved/unsupported payloads are marked to zero.
1219 userInfo.payloadAccess[chIndex].stdPayloadEnables1.reset();
1220 userInfo.payloadAccess[chIndex].oemPayloadEnables1.reset();
1221 userInfo.payloadAccess[chIndex].stdPayloadEnables2Reserved.reset();
1222 userInfo.payloadAccess[chIndex].oemPayloadEnables2Reserved.reset();
1223 // Update SOL status as it is the only supported payload currently.
1224 userInfo.payloadAccess[chIndex]
1225 .stdPayloadEnables1[static_cast<uint8_t>(ipmi::PayloadType::SOL)] =
1226 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)][chIndex];
1227 }
1228}
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301229
1230void UserAccess::readUserData()
1231{
1232 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1233 userLock{*userMutex};
1234
1235 std::ifstream iUsrData(ipmiUserDataFile, std::ios::in | std::ios::binary);
1236 if (!iUsrData.good())
1237 {
1238 log<level::ERR>("Error in reading IPMI user data file");
1239 throw std::ios_base::failure("Error opening IPMI user data file");
1240 }
1241
1242 Json jsonUsersTbl = Json::array();
1243 jsonUsersTbl = Json::parse(iUsrData, nullptr, false);
1244
1245 if (jsonUsersTbl.size() != ipmiMaxUsers)
1246 {
1247 log<level::ERR>(
1248 "Error in reading IPMI user data file - User count issues");
1249 throw std::runtime_error(
1250 "Corrupted IPMI user data file - invalid user count");
1251 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001252
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301253 // user index 0 is reserved, starts with 1
1254 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1255 {
1256 Json userInfo = jsonUsersTbl[usrIndex - 1]; // json array starts with 0.
1257 if (userInfo.is_null())
1258 {
1259 log<level::ERR>("Error in reading IPMI user data file - "
1260 "user info corrupted");
1261 throw std::runtime_error(
1262 "Corrupted IPMI user data file - invalid user info");
1263 }
1264 std::string userName = userInfo[jsonUserName].get<std::string>();
1265 std::strncpy(reinterpret_cast<char*>(usersTbl.user[usrIndex].userName),
1266 userName.c_str(), ipmiMaxUserName);
1267
1268 std::vector<std::string> privilege =
1269 userInfo[jsonPriv].get<std::vector<std::string>>();
1270 std::vector<bool> ipmiEnabled =
1271 userInfo[jsonIpmiEnabled].get<std::vector<bool>>();
1272 std::vector<bool> linkAuthEnabled =
1273 userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
1274 std::vector<bool> accessCallback =
1275 userInfo[jsonAccCallbk].get<std::vector<bool>>();
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001276
1277 // Payload Enables Processing.
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001278 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1279 stdPayload = {};
1280 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1281 oemPayload = {};
1282 try
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001283 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001284 const auto jsonPayloadEnabled = userInfo.at(payloadEnabledStr);
1285 for (auto payloadNum = 0; payloadNum < payloadsPerByte;
1286 payloadNum++)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001287 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001288 std::ostringstream stdPayloadStream;
1289 std::ostringstream oemPayloadStream;
1290
1291 stdPayloadStream << stdPayloadStr << payloadNum;
1292 oemPayloadStream << oemPayloadStr << payloadNum;
1293
1294 stdPayload[payloadNum] =
1295 jsonPayloadEnabled[stdPayloadStream.str()]
1296 .get<std::array<bool, ipmiMaxChannels>>();
1297 oemPayload[payloadNum] =
1298 jsonPayloadEnabled[oemPayloadStream.str()]
1299 .get<std::array<bool, ipmiMaxChannels>>();
1300
1301 if (stdPayload[payloadNum].size() != ipmiMaxChannels ||
1302 oemPayload[payloadNum].size() != ipmiMaxChannels)
1303 {
1304 log<level::ERR>("Error in reading IPMI user data file - "
1305 "payload properties corrupted");
1306 throw std::runtime_error(
1307 "Corrupted IPMI user data file - payload properties");
1308 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001309 }
1310 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001311 catch (const Json::out_of_range& e)
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001312 {
1313 // Key not found in 'userInfo'; possibly an old JSON file. Use
1314 // default values for all payloads, and SOL payload default is true.
1315 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)].fill(true);
1316 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001317
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301318 if (privilege.size() != ipmiMaxChannels ||
1319 ipmiEnabled.size() != ipmiMaxChannels ||
1320 linkAuthEnabled.size() != ipmiMaxChannels ||
1321 accessCallback.size() != ipmiMaxChannels)
1322 {
1323 log<level::ERR>("Error in reading IPMI user data file - "
1324 "properties corrupted");
1325 throw std::runtime_error(
1326 "Corrupted IPMI user data file - properties");
1327 }
1328 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1329 {
1330 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege =
1331 static_cast<uint8_t>(
1332 convertToIPMIPrivilege(privilege[chIndex]));
1333 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled =
1334 ipmiEnabled[chIndex];
1335 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled =
1336 linkAuthEnabled[chIndex];
1337 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
1338 accessCallback[chIndex];
1339 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001340 updatePayloadAccessInUserInfo(stdPayload, oemPayload,
1341 usersTbl.user[usrIndex]);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301342 usersTbl.user[usrIndex].userEnabled =
1343 userInfo[jsonUserEnabled].get<bool>();
1344 usersTbl.user[usrIndex].userInSystem =
1345 userInfo[jsonUserInSys].get<bool>();
1346 usersTbl.user[usrIndex].fixedUserName =
1347 userInfo[jsonFixedUser].get<bool>();
1348 }
1349
1350 log<level::DEBUG>("User data read from IPMI data file");
1351 iUsrData.close();
1352 // Update the timestamp
1353 fileLastUpdatedTime = getUpdatedFileTime();
1354 return;
1355}
1356
1357void UserAccess::writeUserData()
1358{
1359 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1360 userLock{*userMutex};
1361
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301362 Json jsonUsersTbl = Json::array();
1363 // user index 0 is reserved, starts with 1
1364 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1365 {
1366 Json jsonUserInfo;
1367 jsonUserInfo[jsonUserName] = std::string(
1368 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
1369 ipmiMaxUserName);
1370 std::vector<std::string> privilege(ipmiMaxChannels);
1371 std::vector<bool> ipmiEnabled(ipmiMaxChannels);
1372 std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
1373 std::vector<bool> accessCallback(ipmiMaxChannels);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001374
1375 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1376 stdPayload;
1377 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1378 oemPayload;
1379
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301380 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1381 {
1382 privilege[chIndex] =
1383 convertToSystemPrivilege(static_cast<CommandPrivilege>(
1384 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege));
1385 ipmiEnabled[chIndex] =
1386 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled;
1387 linkAuthEnabled[chIndex] =
1388 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled;
1389 accessCallback[chIndex] =
1390 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback;
1391 }
1392 jsonUserInfo[jsonPriv] = privilege;
1393 jsonUserInfo[jsonIpmiEnabled] = ipmiEnabled;
1394 jsonUserInfo[jsonLinkAuthEnabled] = linkAuthEnabled;
1395 jsonUserInfo[jsonAccCallbk] = accessCallback;
1396 jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
1397 jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
1398 jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001399
1400 readPayloadAccessFromUserInfo(usersTbl.user[usrIndex], stdPayload,
1401 oemPayload);
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001402 Json jsonPayloadEnabledInfo = constructJsonPayloadEnables(stdPayload,
1403 oemPayload);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001404 jsonUserInfo[payloadEnabledStr] = jsonPayloadEnabledInfo;
1405
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301406 jsonUsersTbl.push_back(jsonUserInfo);
1407 }
1408
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301409 static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
1410 int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
1411 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1412 if (fd < 0)
1413 {
1414 log<level::ERR>("Error in creating temporary IPMI user data file");
1415 throw std::ios_base::failure(
1416 "Error in creating temporary IPMI user data file");
1417 }
1418 const auto& writeStr = jsonUsersTbl.dump();
1419 if (write(fd, writeStr.c_str(), writeStr.size()) !=
1420 static_cast<ssize_t>(writeStr.size()))
1421 {
1422 close(fd);
1423 log<level::ERR>("Error in writing temporary IPMI user data file");
1424 throw std::ios_base::failure(
1425 "Error in writing temporary IPMI user data file");
1426 }
1427 close(fd);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301428
1429 if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
1430 {
1431 log<level::ERR>("Error in renaming temporary IPMI user data file");
1432 throw std::runtime_error("Error in renaming IPMI user data file");
1433 }
1434 // Update the timestamp
1435 fileLastUpdatedTime = getUpdatedFileTime();
1436 return;
1437}
1438
1439bool UserAccess::addUserEntry(const std::string& userName,
1440 const std::string& sysPriv, const bool& enabled)
1441{
1442 UsersTbl* userData = getUsersTblPtr();
1443 size_t freeIndex = 0xFF;
1444 // user index 0 is reserved, starts with 1
1445 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1446 {
1447 std::string curName(
1448 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
1449 ipmiMaxUserName);
1450 if (userName == curName)
1451 {
1452 log<level::DEBUG>("User name exists",
1453 entry("USER_NAME=%s", userName.c_str()));
1454 return false; // user name exists.
1455 }
1456
1457 if ((!userData->user[usrIndex].userInSystem) &&
1458 (userData->user[usrIndex].userName[0] == '\0') &&
1459 (freeIndex == 0xFF))
1460 {
1461 freeIndex = usrIndex;
1462 }
1463 }
1464 if (freeIndex == 0xFF)
1465 {
1466 log<level::ERR>("No empty slots found");
1467 return false;
1468 }
1469 std::strncpy(reinterpret_cast<char*>(userData->user[freeIndex].userName),
1470 userName.c_str(), ipmiMaxUserName);
1471 uint8_t priv =
1472 static_cast<uint8_t>(UserAccess::convertToIPMIPrivilege(sysPriv)) &
1473 privMask;
1474 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1475 {
1476 userData->user[freeIndex].userPrivAccess[chIndex].privilege = priv;
1477 userData->user[freeIndex].userPrivAccess[chIndex].ipmiEnabled = true;
1478 userData->user[freeIndex].userPrivAccess[chIndex].linkAuthEnabled =
1479 true;
1480 userData->user[freeIndex].userPrivAccess[chIndex].accessCallback = true;
1481 }
1482 userData->user[freeIndex].userInSystem = true;
1483 userData->user[freeIndex].userEnabled = enabled;
1484
1485 return true;
1486}
1487
1488void UserAccess::deleteUserIndex(const size_t& usrIdx)
1489{
1490 UsersTbl* userData = getUsersTblPtr();
1491
1492 std::string userName(
1493 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1494 ipmiMaxUserName);
1495 ipmiClearUserEntryPassword(userName);
1496 std::fill(static_cast<uint8_t*>(userData->user[usrIdx].userName),
1497 static_cast<uint8_t*>(userData->user[usrIdx].userName) +
1498 sizeof(userData->user[usrIdx].userName),
1499 0);
1500 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1501 {
1502 userData->user[usrIdx].userPrivAccess[chIndex].privilege = privNoAccess;
1503 userData->user[usrIdx].userPrivAccess[chIndex].ipmiEnabled = false;
1504 userData->user[usrIdx].userPrivAccess[chIndex].linkAuthEnabled = false;
1505 userData->user[usrIdx].userPrivAccess[chIndex].accessCallback = false;
1506 }
1507 userData->user[usrIdx].userInSystem = false;
1508 userData->user[usrIdx].userEnabled = false;
1509 return;
1510}
1511
1512void UserAccess::checkAndReloadUserData()
1513{
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001514 std::timespec updateTime = getUpdatedFileTime();
1515 if ((updateTime.tv_sec != fileLastUpdatedTime.tv_sec ||
1516 updateTime.tv_nsec != fileLastUpdatedTime.tv_nsec) ||
1517 (updateTime.tv_sec == 0 && updateTime.tv_nsec == 0))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301518 {
1519 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1520 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1521 readUserData();
1522 }
1523 return;
1524}
1525
1526UsersTbl* UserAccess::getUsersTblPtr()
1527{
1528 // reload data before using it.
1529 checkAndReloadUserData();
1530 return &usersTbl;
1531}
1532
1533void UserAccess::getSystemPrivAndGroups()
1534{
1535 std::map<std::string, PrivAndGroupType> properties;
1536 try
1537 {
1538 auto method = bus.new_method_call(
1539 getUserServiceName().c_str(), userMgrObjBasePath,
1540 dBusPropertiesInterface, getAllPropertiesMethod);
1541 method.append(userMgrInterface);
1542
1543 auto reply = bus.call(method);
1544 reply.read(properties);
1545 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001546 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301547 {
1548 log<level::DEBUG>("Failed to excute method",
1549 entry("METHOD=%s", getAllPropertiesMethod),
1550 entry("PATH=%s", userMgrObjBasePath));
1551 return;
1552 }
1553 for (const auto& t : properties)
1554 {
1555 auto key = t.first;
1556 if (key == allPrivProperty)
1557 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001558 availablePrivileges = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301559 }
1560 else if (key == allGrpProperty)
1561 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001562 availableGroups = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301563 }
1564 }
1565 // TODO: Implement Supported Privilege & Groups verification logic
1566 return;
1567}
1568
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001569std::timespec UserAccess::getUpdatedFileTime()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301570{
1571 struct stat fileStat;
1572 if (stat(ipmiUserDataFile, &fileStat) != 0)
1573 {
1574 log<level::DEBUG>("Error in getting last updated time stamp");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001575 return std::timespec{0, 0};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301576 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001577 return fileStat.st_mtim;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301578}
1579
1580void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
1581 std::vector<std::string>& usrGrps,
1582 std::string& usrPriv, bool& usrEnabled)
1583{
1584 for (const auto& t : properties)
1585 {
1586 std::string key = t.first;
1587 if (key == userPrivProperty)
1588 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001589 usrPriv = std::get<std::string>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301590 }
1591 else if (key == userGrpProperty)
1592 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001593 usrGrps = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301594 }
1595 else if (key == userEnabledProperty)
1596 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001597 usrEnabled = std::get<bool>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301598 }
1599 }
1600 return;
1601}
1602
1603int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
1604 std::vector<std::string>& usrGrps,
1605 std::string& usrPriv, bool& usrEnabled)
1606{
1607 auto usrObj = userObjs.find(usersInterface);
1608 if (usrObj != userObjs.end())
1609 {
1610 getUserProperties(usrObj->second, usrGrps, usrPriv, usrEnabled);
1611 return 0;
1612 }
1613 return -EIO;
1614}
1615
arun-pmbbe728c2020-01-10 15:18:04 +05301616void UserAccess::cacheUserDataFile()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301617{
1618 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1619 userLock{*userMutex};
1620 try
1621 {
1622 readUserData();
1623 }
1624 catch (const std::ios_base::failure& e)
1625 { // File is empty, create it for the first time
1626 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1627 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1628 // user index 0 is reserved, starts with 1
1629 for (size_t userIndex = 1; userIndex <= ipmiMaxUsers; ++userIndex)
1630 {
1631 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1632 {
1633 usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
1634 privNoAccess;
Saravanan Palanisamy92d81192019-08-07 18:00:04 +00001635 usersTbl.user[userIndex]
1636 .payloadAccess[chIndex]
1637 .stdPayloadEnables1[static_cast<uint8_t>(
1638 ipmi::PayloadType::SOL)] = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301639 }
1640 }
1641 writeUserData();
1642 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001643 // Create lock file if it does not exist
1644 int fd = open(ipmiUserSignalLockFile, O_CREAT | O_TRUNC | O_SYNC,
1645 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1646 if (fd < 0)
1647 {
1648 log<level::ERR>("Error in creating IPMI user signal lock file");
1649 throw std::ios_base::failure(
1650 "Error in creating temporary IPMI user signal lock file");
1651 }
1652 close(fd);
1653
1654 sigHndlrLock = boost::interprocess::file_lock(ipmiUserSignalLockFile);
George Liu1a2e1502022-07-08 12:20:19 +08001655 // Register it for single object and single process either netipmid /
arun-pmbbe728c2020-01-10 15:18:04 +05301656 // host-ipmid
1657 if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
1658 {
1659 log<level::DEBUG>("Registering signal handler");
1660 userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
1661 bus,
1662 sdbusplus::bus::match::rules::type::signal() +
1663 sdbusplus::bus::match::rules::interface(dBusObjManager) +
1664 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001665 [&](sdbusplus::message_t& msg) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001666 userUpdatedSignalHandler(*this, msg);
Patrick Williams369824e2023-10-20 11:18:23 -05001667 });
arun-pmbbe728c2020-01-10 15:18:04 +05301668 userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
1669 bus,
1670 sdbusplus::bus::match::rules::type::signal() +
1671 sdbusplus::bus::match::rules::interface(userMgrInterface) +
1672 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001673 [&](sdbusplus::message_t& msg) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001674 userUpdatedSignalHandler(*this, msg);
Patrick Williams369824e2023-10-20 11:18:23 -05001675 });
arun-pmbbe728c2020-01-10 15:18:04 +05301676 userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
1677 bus,
1678 sdbusplus::bus::match::rules::type::signal() +
1679 sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
1680 sdbusplus::bus::match::rules::interface(
1681 dBusPropertiesInterface) +
1682 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
1683 sdbusplus::bus::match::rules::argN(0, usersInterface),
Patrick Williams5d82f472022-07-22 19:26:53 -05001684 [&](sdbusplus::message_t& msg) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001685 userUpdatedSignalHandler(*this, msg);
Patrick Williams369824e2023-10-20 11:18:23 -05001686 });
arun-pmbbe728c2020-01-10 15:18:04 +05301687 signalHndlrObject = true;
1688 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301689 std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
1690 try
1691 {
1692 auto method = bus.new_method_call(getUserServiceName().c_str(),
1693 userMgrObjBasePath, dBusObjManager,
1694 getManagedObjectsMethod);
1695 auto reply = bus.call(method);
1696 reply.read(managedObjs);
1697 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001698 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301699 {
1700 log<level::DEBUG>("Failed to excute method",
1701 entry("METHOD=%s", getSubTreeMethod),
1702 entry("PATH=%s", userMgrObjBasePath));
1703 return;
1704 }
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301705 bool updateRequired = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301706 UsersTbl* userData = &usersTbl;
1707 // user index 0 is reserved, starts with 1
1708 for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
1709 {
1710 if ((userData->user[usrIdx].userInSystem) &&
1711 (userData->user[usrIdx].userName[0] != '\0'))
1712 {
1713 std::vector<std::string> usrGrps;
1714 std::string usrPriv;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301715
1716 std::string userName(
1717 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1718 ipmiMaxUserName);
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301719 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1720 tempUserPath /= userName;
1721 std::string usersPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301722
1723 auto usrObj = managedObjs.find(usersPath);
1724 if (usrObj != managedObjs.end())
1725 {
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001726 bool usrEnabled = false;
Patrick Venture3a697ad2019-08-19 11:12:05 -07001727
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301728 // User exist. Lets check and update other fileds
1729 getUserObjProperties(usrObj->second, usrGrps, usrPriv,
1730 usrEnabled);
1731 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
1732 usrGrps.end())
1733 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301734 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301735 // Group "ipmi" is removed so lets remove user in IPMI
1736 deleteUserIndex(usrIdx);
1737 }
1738 else
1739 {
1740 // Group "ipmi" is present so lets update other properties
1741 // in IPMI
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001742 uint8_t priv = UserAccess::convertToIPMIPrivilege(usrPriv) &
1743 privMask;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301744 // Update all channels priv, only if it is not equivalent to
1745 // getUsrMgmtSyncIndex()
1746 if (userData->user[usrIdx]
1747 .userPrivAccess[getUsrMgmtSyncIndex()]
1748 .privilege != priv)
1749 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301750 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301751 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
1752 ++chIndex)
1753 {
1754 userData->user[usrIdx]
1755 .userPrivAccess[chIndex]
1756 .privilege = priv;
1757 }
1758 }
1759 if (userData->user[usrIdx].userEnabled != usrEnabled)
1760 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301761 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301762 userData->user[usrIdx].userEnabled = usrEnabled;
1763 }
1764 }
1765
1766 // We are done with this obj. lets delete from MAP
1767 managedObjs.erase(usrObj);
1768 }
1769 else
1770 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301771 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301772 deleteUserIndex(usrIdx);
1773 }
1774 }
1775 }
1776
1777 // Walk through remnaining managedObj users list
1778 // Add them to ipmi data base
1779 for (const auto& usrObj : managedObjs)
1780 {
1781 std::vector<std::string> usrGrps;
1782 std::string usrPriv, userName;
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001783 bool usrEnabled = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301784 std::string usrObjPath = std::string(usrObj.first);
1785 if (getUserNameFromPath(usrObj.first.str, userName) != 0)
1786 {
1787 log<level::ERR>("Error in user object path");
1788 continue;
1789 }
1790 getUserObjProperties(usrObj.second, usrGrps, usrPriv, usrEnabled);
1791 // Add 'ipmi' group users
1792 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
1793 usrGrps.end())
1794 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301795 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301796 // CREATE NEW USER
1797 if (true != addUserEntry(userName, usrPriv, usrEnabled))
1798 {
1799 break;
1800 }
1801 }
1802 }
1803
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301804 if (updateRequired)
1805 {
1806 // All userData slots update done. Lets write the data
1807 writeUserData();
1808 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301809
1810 return;
1811}
1812} // namespace ipmi