blob: 00ed14293f9ffc1928804b5f164997e1dbd836f1 [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";
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053068static constexpr const char* getObjectMethod = "GetObject";
69
70static constexpr const char* ipmiUserMutex = "ipmi_usr_mutex";
71static constexpr const char* ipmiMutexCleanupLockFile =
72 "/var/lib/ipmi/ipmi_usr_mutex_cleanup";
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +000073static constexpr const char* ipmiUserSignalLockFile =
74 "/var/lib/ipmi/ipmi_usr_signal_mutex";
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053075static constexpr const char* ipmiUserDataFile = "/var/lib/ipmi/ipmi_user.json";
76static constexpr const char* ipmiGrpName = "ipmi";
77static constexpr size_t privNoAccess = 0xF;
78static constexpr size_t privMask = 0xF;
79
80// User manager related
81static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user";
82static constexpr const char* userObjBasePath = "/xyz/openbmc_project/user";
83static constexpr const char* userMgrInterface =
84 "xyz.openbmc_project.User.Manager";
85static constexpr const char* usersInterface =
86 "xyz.openbmc_project.User.Attributes";
87static constexpr const char* deleteUserInterface =
88 "xyz.openbmc_project.Object.Delete";
89
90static constexpr const char* createUserMethod = "CreateUser";
91static constexpr const char* deleteUserMethod = "Delete";
92static constexpr const char* renameUserMethod = "RenameUser";
93// User manager signal memebers
94static constexpr const char* userRenamedSignal = "UserRenamed";
95// Mgr interface properties
96static constexpr const char* allPrivProperty = "AllPrivileges";
97static constexpr const char* allGrpProperty = "AllGroups";
98// User interface properties
99static constexpr const char* userPrivProperty = "UserPrivilege";
100static constexpr const char* userGrpProperty = "UserGroups";
101static constexpr const char* userEnabledProperty = "UserEnabled";
102
103static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = {
104 "priv-reserved", // PRIVILEGE_RESERVED - 0
105 "priv-callback", // PRIVILEGE_CALLBACK - 1
106 "priv-user", // PRIVILEGE_USER - 2
107 "priv-operator", // PRIVILEGE_OPERATOR - 3
108 "priv-admin", // PRIVILEGE_ADMIN - 4
109 "priv-custom" // PRIVILEGE_OEM - 5
110};
111
112using namespace phosphor::logging;
113using Json = nlohmann::json;
114
Vernon Mauery16b86932019-05-01 08:36:11 -0700115using PrivAndGroupType = std::variant<std::string, std::vector<std::string>>;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530116
117using NoResource =
Willy Tu523e2d12023-09-05 11:36:48 -0700118 sdbusplus::error::xyz::openbmc_project::user::common::NoResource;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530119
120using InternalFailure =
Willy Tu523e2d12023-09-05 11:36:48 -0700121 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530122
Lei YU4b0ddb62019-01-25 16:43:50 +0800123std::unique_ptr<sdbusplus::bus::match_t> userUpdatedSignal
124 __attribute__((init_priority(101)));
125std::unique_ptr<sdbusplus::bus::match_t> userMgrRenamedSignal
126 __attribute__((init_priority(101)));
127std::unique_ptr<sdbusplus::bus::match_t> userPropertiesSignal
128 __attribute__((init_priority(101)));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530129
130// TODO: Below code can be removed once it is moved to common layer libmiscutil
Patrick Williams5d82f472022-07-22 19:26:53 -0500131std::string getUserService(sdbusplus::bus_t& bus, const std::string& intf,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530132 const std::string& path)
133{
134 auto mapperCall = bus.new_method_call(objMapperService, objMapperPath,
135 objMapperInterface, getObjectMethod);
136
137 mapperCall.append(path);
138 mapperCall.append(std::vector<std::string>({intf}));
139
140 auto mapperResponseMsg = bus.call(mapperCall);
141
142 std::map<std::string, std::vector<std::string>> mapperResponse;
143 mapperResponseMsg.read(mapperResponse);
144
145 if (mapperResponse.begin() == mapperResponse.end())
146 {
147 throw sdbusplus::exception::SdBusError(
148 -EIO, "ERROR in reading the mapper response");
149 }
150
151 return mapperResponse.begin()->first;
152}
153
Patrick Williams5d82f472022-07-22 19:26:53 -0500154void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530155 const std::string& objPath, const std::string& interface,
156 const std::string& property,
157 const DbusUserPropVariant& value)
158{
159 try
160 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500161 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
162 dBusPropertiesInterface,
163 setPropertiesMethod);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530164 method.append(interface, property, value);
165 bus.call(method);
166 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500167 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530168 {
169 log<level::ERR>("Failed to set property",
170 entry("PROPERTY=%s", property.c_str()),
171 entry("PATH=%s", objPath.c_str()),
172 entry("INTERFACE=%s", interface.c_str()));
173 throw;
174 }
175}
176
Willy Tu523e2d12023-09-05 11:36:48 -0700177std::string getUserServiceName()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530178{
Patrick Williams5d82f472022-07-22 19:26:53 -0500179 static sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530180 static std::string userMgmtService;
181 if (userMgmtService.empty())
182 {
183 try
184 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500185 userMgmtService = ipmi::getUserService(bus, userMgrInterface,
186 userMgrObjBasePath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530187 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500188 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530189 {
190 userMgmtService.clear();
191 }
192 }
193 return userMgmtService;
194}
195
196UserAccess& getUserAccessObject()
197{
198 static UserAccess userAccess;
199 return userAccess;
200}
201
202int getUserNameFromPath(const std::string& path, std::string& userName)
203{
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530204 sdbusplus::message::object_path objPath(path);
205 userName.assign(objPath.filename());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530206 return 0;
207}
208
209void userUpdateHelper(UserAccess& usrAccess, const UserUpdateEvent& userEvent,
210 const std::string& userName, const std::string& priv,
211 const bool& enabled, const std::string& newUserName)
212{
213 UsersTbl* userData = usrAccess.getUsersTblPtr();
214 if (userEvent == UserUpdateEvent::userCreated)
215 {
216 if (usrAccess.addUserEntry(userName, priv, enabled) == false)
217 {
218 return;
219 }
220 }
221 else
222 {
223 // user index 0 is reserved, starts with 1
224 size_t usrIndex = 1;
225 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
226 {
227 std::string curName(
228 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
229 ipmiMaxUserName);
230 if (userName == curName)
231 {
232 break; // found the entry
233 }
234 }
235 if (usrIndex > ipmiMaxUsers)
236 {
237 log<level::DEBUG>("User not found for signal",
238 entry("USER_NAME=%s", userName.c_str()),
239 entry("USER_EVENT=%d", userEvent));
240 return;
241 }
242 switch (userEvent)
243 {
244 case UserUpdateEvent::userDeleted:
245 {
246 usrAccess.deleteUserIndex(usrIndex);
247 break;
248 }
249 case UserUpdateEvent::userPrivUpdated:
250 {
251 uint8_t userPriv =
252 static_cast<uint8_t>(
253 UserAccess::convertToIPMIPrivilege(priv)) &
254 privMask;
255 // Update all channels privileges, only if it is not equivalent
256 // to getUsrMgmtSyncIndex()
257 if (userData->user[usrIndex]
258 .userPrivAccess[UserAccess::getUsrMgmtSyncIndex()]
259 .privilege != userPriv)
260 {
261 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
262 ++chIndex)
263 {
264 userData->user[usrIndex]
265 .userPrivAccess[chIndex]
266 .privilege = userPriv;
267 }
268 }
269 break;
270 }
271 case UserUpdateEvent::userRenamed:
272 {
273 std::fill(
274 static_cast<uint8_t*>(userData->user[usrIndex].userName),
275 static_cast<uint8_t*>(userData->user[usrIndex].userName) +
276 sizeof(userData->user[usrIndex].userName),
277 0);
278 std::strncpy(
279 reinterpret_cast<char*>(userData->user[usrIndex].userName),
280 newUserName.c_str(), ipmiMaxUserName);
281 ipmiRenameUserEntryPassword(userName, newUserName);
282 break;
283 }
284 case UserUpdateEvent::userStateUpdated:
285 {
286 userData->user[usrIndex].userEnabled = enabled;
287 break;
288 }
289 default:
290 {
291 log<level::ERR>("Unhandled user event",
292 entry("USER_EVENT=%d", userEvent));
293 return;
294 }
295 }
296 }
297 usrAccess.writeUserData();
298 log<level::DEBUG>("User event handled successfully",
299 entry("USER_NAME=%s", userName.c_str()),
300 entry("USER_EVENT=%d", userEvent));
301
302 return;
303}
304
Patrick Williams5d82f472022-07-22 19:26:53 -0500305void userUpdatedSignalHandler(UserAccess& usrAccess, sdbusplus::message_t& msg)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530306{
Patrick Williams5d82f472022-07-22 19:26:53 -0500307 static sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530308 std::string signal = msg.get_member();
Patrick Venture3a697ad2019-08-19 11:12:05 -0700309 std::string userName, priv, newUserName;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530310 std::vector<std::string> groups;
311 bool enabled = false;
312 UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent;
313 if (signal == intfAddedSignal)
314 {
315 DbusUserObjPath objPath;
316 DbusUserObjValue objValue;
317 msg.read(objPath, objValue);
318 getUserNameFromPath(objPath.str, userName);
319 if (usrAccess.getUserObjProperties(objValue, groups, priv, enabled) !=
320 0)
321 {
322 return;
323 }
324 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
325 groups.end())
326 {
327 return;
328 }
329 userEvent = UserUpdateEvent::userCreated;
330 }
331 else if (signal == intfRemovedSignal)
332 {
333 DbusUserObjPath objPath;
334 std::vector<std::string> interfaces;
335 msg.read(objPath, interfaces);
336 getUserNameFromPath(objPath.str, userName);
337 userEvent = UserUpdateEvent::userDeleted;
338 }
339 else if (signal == userRenamedSignal)
340 {
341 msg.read(userName, newUserName);
342 userEvent = UserUpdateEvent::userRenamed;
343 }
344 else if (signal == propertiesChangedSignal)
345 {
346 getUserNameFromPath(msg.get_path(), userName);
347 }
348 else
349 {
350 log<level::ERR>("Unknown user update signal",
351 entry("SIGNAL=%s", signal.c_str()));
352 return;
353 }
354
355 if (signal.empty() || userName.empty() ||
356 (signal == userRenamedSignal && newUserName.empty()))
357 {
358 log<level::ERR>("Invalid inputs received");
359 return;
360 }
361
362 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
363 userLock{*(usrAccess.userMutex)};
364 usrAccess.checkAndReloadUserData();
365
366 if (signal == propertiesChangedSignal)
367 {
368 std::string intfName;
369 DbusUserObjProperties chProperties;
370 msg.read(intfName, chProperties); // skip reading 3rd argument.
371 for (const auto& prop : chProperties)
372 {
373 userEvent = UserUpdateEvent::reservedEvent;
374 std::string member = prop.first;
375 if (member == userPrivProperty)
376 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700377 priv = std::get<std::string>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530378 userEvent = UserUpdateEvent::userPrivUpdated;
379 }
380 else if (member == userGrpProperty)
381 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700382 groups = std::get<std::vector<std::string>>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530383 userEvent = UserUpdateEvent::userGrpUpdated;
384 }
385 else if (member == userEnabledProperty)
386 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700387 enabled = std::get<bool>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530388 userEvent = UserUpdateEvent::userStateUpdated;
389 }
390 // Process based on event type.
391 if (userEvent == UserUpdateEvent::userGrpUpdated)
392 {
393 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
394 groups.end())
395 {
396 // remove user from ipmi user list.
397 userUpdateHelper(usrAccess, UserUpdateEvent::userDeleted,
398 userName, priv, enabled, newUserName);
399 }
400 else
401 {
402 DbusUserObjProperties properties;
403 try
404 {
405 auto method = bus.new_method_call(
406 getUserServiceName().c_str(), msg.get_path(),
407 dBusPropertiesInterface, getAllPropertiesMethod);
408 method.append(usersInterface);
409 auto reply = bus.call(method);
410 reply.read(properties);
411 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500412 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530413 {
414 log<level::DEBUG>(
415 "Failed to excute method",
416 entry("METHOD=%s", getAllPropertiesMethod),
417 entry("PATH=%s", msg.get_path()));
418 return;
419 }
420 usrAccess.getUserProperties(properties, groups, priv,
421 enabled);
422 // add user to ipmi user list.
423 userUpdateHelper(usrAccess, UserUpdateEvent::userCreated,
424 userName, priv, enabled, newUserName);
425 }
426 }
427 else if (userEvent != UserUpdateEvent::reservedEvent)
428 {
429 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
430 newUserName);
431 }
432 }
433 }
434 else if (userEvent != UserUpdateEvent::reservedEvent)
435 {
436 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
437 newUserName);
438 }
439 return;
440}
441
442UserAccess::~UserAccess()
443{
444 if (signalHndlrObject)
445 {
446 userUpdatedSignal.reset();
447 userMgrRenamedSignal.reset();
448 userPropertiesSignal.reset();
449 sigHndlrLock.unlock();
450 }
451}
452
453UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection())
454{
455 std::ofstream mutexCleanUpFile;
456 mutexCleanUpFile.open(ipmiMutexCleanupLockFile,
457 std::ofstream::out | std::ofstream::app);
458 if (!mutexCleanUpFile.good())
459 {
460 log<level::DEBUG>("Unable to open mutex cleanup file");
461 return;
462 }
463 mutexCleanUpFile.close();
464 mutexCleanupLock = boost::interprocess::file_lock(ipmiMutexCleanupLockFile);
465 if (mutexCleanupLock.try_lock())
466 {
467 boost::interprocess::named_recursive_mutex::remove(ipmiUserMutex);
468 }
469 mutexCleanupLock.lock_sharable();
470 userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>(
471 boost::interprocess::open_or_create, ipmiUserMutex);
472
arun-pmbbe728c2020-01-10 15:18:04 +0530473 cacheUserDataFile();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530474 getSystemPrivAndGroups();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530475}
476
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530477UserInfo* UserAccess::getUserInfo(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530478{
479 checkAndReloadUserData();
480 return &usersTbl.user[userId];
481}
482
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530483void UserAccess::setUserInfo(const uint8_t userId, UserInfo* userInfo)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530484{
485 checkAndReloadUserData();
486 std::copy(reinterpret_cast<uint8_t*>(userInfo),
487 reinterpret_cast<uint8_t*>(userInfo) + sizeof(*userInfo),
488 reinterpret_cast<uint8_t*>(&usersTbl.user[userId]));
489 writeUserData();
490}
491
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530492bool UserAccess::isValidChannel(const uint8_t chNum)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530493{
494 return (chNum < ipmiMaxChannels);
495}
496
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530497bool UserAccess::isValidUserId(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530498{
499 return ((userId <= ipmiMaxUsers) && (userId != reservedUserId));
500}
501
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530502bool UserAccess::isValidPrivilege(const uint8_t priv)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530503{
jayaprakash Mutyala0e2dbee2019-12-26 13:03:04 +0000504 // Callback privilege is deprecated in OpenBMC
Alexander Filippovfc24fa52022-02-01 14:57:59 +0300505 return isValidPrivLimit(priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530506}
507
508uint8_t UserAccess::getUsrMgmtSyncIndex()
509{
Johnathan Manteyfd61fc32021-04-08 11:05:38 -0700510 // Identify the IPMI channel used to assign system user privilege levels
511 // in phosphor-user-manager. The default value is IPMI Channel 1. To
512 // assign a different channel add:
513 // "is_management_nic" : true
514 // into the channel_config.json file describing the assignment of the IPMI
515 // channels. It is only necessary to add the string above to ONE record in
516 // the channel_config.json file. All other records will be automatically
517 // assigned a "false" value.
518 return getChannelConfigObject().getManagementNICID();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530519}
520
521CommandPrivilege UserAccess::convertToIPMIPrivilege(const std::string& value)
522{
523 auto iter = std::find(ipmiPrivIndex.begin(), ipmiPrivIndex.end(), value);
524 if (iter == ipmiPrivIndex.end())
525 {
526 if (value == "")
527 {
528 return static_cast<CommandPrivilege>(privNoAccess);
529 }
530 log<level::ERR>("Error in converting to IPMI privilege",
531 entry("PRIV=%s", value.c_str()));
532 throw std::out_of_range("Out of range - convertToIPMIPrivilege");
533 }
534 else
535 {
536 return static_cast<CommandPrivilege>(
537 std::distance(ipmiPrivIndex.begin(), iter));
538 }
539}
540
541std::string UserAccess::convertToSystemPrivilege(const CommandPrivilege& value)
542{
543 if (value == static_cast<CommandPrivilege>(privNoAccess))
544 {
545 return "";
546 }
547 try
548 {
549 return ipmiPrivIndex.at(value);
550 }
551 catch (const std::out_of_range& e)
552 {
553 log<level::ERR>("Error in converting to system privilege",
554 entry("PRIV=%d", static_cast<uint8_t>(value)));
555 throw std::out_of_range("Out of range - convertToSystemPrivilege");
556 }
557}
558
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000559bool UserAccess::isValidUserName(const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530560{
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000561 if (userName.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530562 {
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000563 log<level::ERR>("userName is empty");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530564 return false;
565 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530566 if (!std::regex_match(userName.c_str(),
nichanghao.nch0c96fdf2024-01-17 22:13:35 +0800567 std::regex("[a-zA-Z_][a-zA-Z_0-9]*")))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530568 {
569 log<level::ERR>("Unsupported characters in user name");
570 return false;
571 }
572 if (userName == "root")
573 {
574 log<level::ERR>("Invalid user name - root");
575 return false;
576 }
577 std::map<DbusUserObjPath, DbusUserObjValue> properties;
578 try
579 {
580 auto method = bus.new_method_call(getUserServiceName().c_str(),
581 userMgrObjBasePath, dBusObjManager,
582 getManagedObjectsMethod);
583 auto reply = bus.call(method);
584 reply.read(properties);
585 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500586 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530587 {
588 log<level::ERR>("Failed to excute method",
George Liu1f42d1a2024-02-04 17:28:13 +0800589 entry("METHOD=%s", getManagedObjectsMethod),
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530590 entry("PATH=%s", userMgrObjBasePath));
591 return false;
592 }
593
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530594 sdbusplus::message::object_path tempUserPath(userObjBasePath);
595 tempUserPath /= userName;
596 std::string usersPath(tempUserPath);
597
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530598 if (properties.find(usersPath) != properties.end())
599 {
600 log<level::DEBUG>("User name already exists",
601 entry("USER_NAME=%s", userName.c_str()));
602 return false;
603 }
604
605 return true;
606}
607
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530608/** @brief Information exchanged by pam module and application.
609 *
610 * @param[in] numMsg - length of the array of pointers,msg.
611 *
612 * @param[in] msg - pointer to an array of pointers to pam_message structure
613 *
614 * @param[out] resp - struct pam response array
615 *
616 * @param[in] appdataPtr - member of pam_conv structure
617 *
618 * @return the response in pam response structure.
619 */
620
621static int pamFunctionConversation(int numMsg, const struct pam_message** msg,
622 struct pam_response** resp, void* appdataPtr)
623{
624 if (appdataPtr == nullptr)
625 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530626 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530627 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530628
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530629 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
630 {
631 return PAM_CONV_ERR;
632 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530633
634 for (int i = 0; i < numMsg; ++i)
635 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530636 /* Ignore all PAM messages except prompting for hidden input */
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530637 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
638 {
639 continue;
640 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530641
642 /* Assume PAM is only prompting for the password as hidden input */
643 /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */
644
645 char* appPass = reinterpret_cast<char*>(appdataPtr);
646 size_t appPassSize = std::strlen(appPass);
647
648 if (appPassSize >= PAM_MAX_RESP_SIZE)
649 {
650 return PAM_CONV_ERR;
651 }
652
653 char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1));
654 if (pass == nullptr)
655 {
656 return PAM_BUF_ERR;
657 }
658
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500659 void* ptr = calloc(static_cast<size_t>(numMsg),
660 sizeof(struct pam_response));
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530661 if (ptr == nullptr)
662 {
663 free(pass);
664 return PAM_BUF_ERR;
665 }
666
667 std::strncpy(pass, appPass, appPassSize + 1);
668
669 *resp = reinterpret_cast<pam_response*>(ptr);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530670 resp[i]->resp = pass;
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530671
672 return PAM_SUCCESS;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530673 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530674
675 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530676}
677
678/** @brief Updating the PAM password
679 *
680 * @param[in] username - username in string
681 *
682 * @param[in] password - new password in string
683 *
684 * @return status
685 */
686
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000687int pamUpdatePasswd(const char* username, const char* password)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530688{
689 const struct pam_conv localConversation = {pamFunctionConversation,
690 const_cast<char*>(password)};
691 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
692
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500693 int retval = pam_start("passwd", username, &localConversation,
694 &localAuthHandle);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530695
696 if (retval != PAM_SUCCESS)
697 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000698 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530699 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000700
701 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
702 if (retval != PAM_SUCCESS)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530703 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000704 pam_end(localAuthHandle, retval);
705 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530706 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000707
708 return pam_end(localAuthHandle, PAM_SUCCESS);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530709}
710
Ayushi Smriti02650d52019-05-15 11:59:09 +0000711bool pamUserCheckAuthenticate(std::string_view username,
712 std::string_view password)
713{
714 const struct pam_conv localConversation = {
715 pamFunctionConversation, const_cast<char*>(password.data())};
716
717 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
718
719 if (pam_start("dropbear", username.data(), &localConversation,
720 &localAuthHandle) != PAM_SUCCESS)
721 {
722 log<level::ERR>("User Authentication Failure");
723 return false;
724 }
725
726 int retval = pam_authenticate(localAuthHandle,
727 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
728
729 if (retval != PAM_SUCCESS)
730 {
731 log<level::DEBUG>("pam_authenticate returned failure",
732 entry("ERROR=%d", retval));
733
734 pam_end(localAuthHandle, retval);
735 return false;
736 }
737
738 if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
739 PAM_SUCCESS)
740 {
741 pam_end(localAuthHandle, PAM_SUCCESS);
742 return false;
743 }
744
745 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
746 {
747 return false;
748 }
749 return true;
750}
751
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000752Cc UserAccess::setSpecialUserPassword(const std::string& userName,
Vernon Mauery1e22a0f2021-07-30 13:36:54 -0700753 const SecureString& userPassword)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530754{
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000755 if (pamUpdatePasswd(userName.c_str(), userPassword.c_str()) != PAM_SUCCESS)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530756 {
757 log<level::DEBUG>("Failed to update password");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000758 return ccUnspecifiedError;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530759 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000760 return ccSuccess;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530761}
762
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000763Cc UserAccess::setUserPassword(const uint8_t userId, const char* userPassword)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530764{
765 std::string userName;
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000766 if (ipmiUserGetUserName(userId, userName) != ccSuccess)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530767 {
768 log<level::DEBUG>("User Name not found",
Ayushi Smriti05ad3412019-10-16 16:10:18 +0530769 entry("USER-ID=%d", (uint8_t)userId));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000770 return ccParmOutOfRange;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530771 }
Snehalatha Venkatesh61024d72021-04-08 16:24:39 +0000772
773 ipmi::SecureString passwd;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530774 passwd.assign(reinterpret_cast<const char*>(userPassword), 0,
775 maxIpmi20PasswordSize);
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000776 int retval = pamUpdatePasswd(userName.c_str(), passwd.c_str());
777
778 switch (retval)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530779 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000780 case PAM_SUCCESS:
781 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000782 return ccSuccess;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000783 }
784 case PAM_AUTHTOK_ERR:
785 {
786 log<level::DEBUG>("Bad authentication token");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000787 return ccInvalidFieldRequest;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000788 }
789 default:
790 {
791 log<level::DEBUG>("Failed to update password",
792 entry("USER-ID=%d", (uint8_t)userId));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000793 return ccUnspecifiedError;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000794 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530795 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530796}
797
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000798Cc UserAccess::setUserEnabledState(const uint8_t userId,
799 const bool& enabledState)
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530800{
801 if (!isValidUserId(userId))
802 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000803 return ccParmOutOfRange;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530804 }
805 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
806 userLock{*userMutex};
807 UserInfo* userInfo = getUserInfo(userId);
808 std::string userName;
809 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
810 ipmiMaxUserName);
811 if (userName.empty())
812 {
813 log<level::DEBUG>("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000814 return ccUnspecifiedError;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530815 }
816 if (userInfo->userEnabled != enabledState)
817 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530818 sdbusplus::message::object_path tempUserPath(userObjBasePath);
819 tempUserPath /= userName;
820 std::string userPath(tempUserPath);
Patrick Venture99d1ba02019-02-21 15:11:24 -0800821 setDbusProperty(bus, getUserServiceName(), userPath, usersInterface,
822 userEnabledProperty, enabledState);
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530823 userInfo->userEnabled = enabledState;
824 try
825 {
826 writeUserData();
827 }
828 catch (const std::exception& e)
829 {
830 log<level::DEBUG>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000831 return ccUnspecifiedError;
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530832 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530833 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000834 return ccSuccess;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530835}
836
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000837Cc UserAccess::setUserPayloadAccess(const uint8_t chNum,
838 const uint8_t operation,
839 const uint8_t userId,
840 const PayloadAccess& payloadAccess)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000841{
842 constexpr uint8_t enable = 0x0;
843 constexpr uint8_t disable = 0x1;
844
845 if (!isValidChannel(chNum))
846 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000847 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000848 }
849 if (!isValidUserId(userId))
850 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000851 return ccParmOutOfRange;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000852 }
853 if (operation != enable && operation != disable)
854 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000855 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000856 }
857 // Check operation & payloadAccess if required.
858 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
859 userLock{*userMutex};
860 UserInfo* userInfo = getUserInfo(userId);
861
862 if (operation == enable)
863 {
864 userInfo->payloadAccess[chNum].stdPayloadEnables1 |=
865 payloadAccess.stdPayloadEnables1;
866
867 userInfo->payloadAccess[chNum].oemPayloadEnables1 |=
868 payloadAccess.oemPayloadEnables1;
869 }
870 else
871 {
872 userInfo->payloadAccess[chNum].stdPayloadEnables1 &=
873 ~(payloadAccess.stdPayloadEnables1);
874
875 userInfo->payloadAccess[chNum].oemPayloadEnables1 &=
876 ~(payloadAccess.oemPayloadEnables1);
877 }
878
879 try
880 {
881 writeUserData();
882 }
883 catch (const std::exception& e)
884 {
885 log<level::ERR>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000886 return ccUnspecifiedError;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000887 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000888 return ccSuccess;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000889}
890
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000891Cc UserAccess::setUserPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
892 const UserPrivAccess& privAccess,
893 const bool& otherPrivUpdates)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530894{
895 if (!isValidChannel(chNum))
896 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000897 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530898 }
899 if (!isValidUserId(userId))
900 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000901 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530902 }
903 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
904 userLock{*userMutex};
905 UserInfo* userInfo = getUserInfo(userId);
906 std::string userName;
907 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
908 ipmiMaxUserName);
909 if (userName.empty())
910 {
911 log<level::DEBUG>("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000912 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530913 }
914 std::string priv = convertToSystemPrivilege(
915 static_cast<CommandPrivilege>(privAccess.privilege));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530916 uint8_t syncIndex = getUsrMgmtSyncIndex();
917 if (chNum == syncIndex &&
918 privAccess.privilege != userInfo->userPrivAccess[syncIndex].privilege)
919 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530920 sdbusplus::message::object_path tempUserPath(userObjBasePath);
921 tempUserPath /= userName;
922 std::string userPath(tempUserPath);
Patrick Venture99d1ba02019-02-21 15:11:24 -0800923 setDbusProperty(bus, getUserServiceName(), userPath, usersInterface,
924 userPrivProperty, priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530925 }
926 userInfo->userPrivAccess[chNum].privilege = privAccess.privilege;
927
928 if (otherPrivUpdates)
929 {
930 userInfo->userPrivAccess[chNum].ipmiEnabled = privAccess.ipmiEnabled;
931 userInfo->userPrivAccess[chNum].linkAuthEnabled =
932 privAccess.linkAuthEnabled;
933 userInfo->userPrivAccess[chNum].accessCallback =
934 privAccess.accessCallback;
935 }
936 try
937 {
938 writeUserData();
939 }
940 catch (const std::exception& e)
941 {
942 log<level::DEBUG>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000943 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530944 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000945 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530946}
947
948uint8_t UserAccess::getUserId(const std::string& userName)
949{
950 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
951 userLock{*userMutex};
952 checkAndReloadUserData();
953 // user index 0 is reserved, starts with 1
954 size_t usrIndex = 1;
955 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
956 {
957 std::string curName(
958 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
959 ipmiMaxUserName);
960 if (userName == curName)
961 {
962 break; // found the entry
963 }
964 }
965 if (usrIndex > ipmiMaxUsers)
966 {
967 log<level::DEBUG>("User not found",
968 entry("USER_NAME=%s", userName.c_str()));
969 return invalidUserId;
970 }
971
972 return usrIndex;
973}
974
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000975Cc UserAccess::getUserName(const uint8_t userId, std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530976{
977 if (!isValidUserId(userId))
978 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000979 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530980 }
981 UserInfo* userInfo = getUserInfo(userId);
982 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
983 ipmiMaxUserName);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000984 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530985}
986
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530987bool UserAccess::isIpmiInAvailableGroupList()
988{
989 if (std::find(availableGroups.begin(), availableGroups.end(),
990 ipmiGrpName) != availableGroups.end())
991 {
992 return true;
993 }
994 if (availableGroups.empty())
995 {
996 // available groups shouldn't be empty, re-query
997 getSystemPrivAndGroups();
998 if (std::find(availableGroups.begin(), availableGroups.end(),
999 ipmiGrpName) != availableGroups.end())
1000 {
1001 return true;
1002 }
1003 }
1004 return false;
1005}
1006
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001007Cc UserAccess::setUserName(const uint8_t userId, const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301008{
1009 if (!isValidUserId(userId))
1010 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001011 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301012 }
1013
1014 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1015 userLock{*userMutex};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301016 std::string oldUser;
1017 getUserName(userId, oldUser);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301018
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001019 if (oldUser == userName)
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301020 {
1021 // requesting to set the same user name, return success.
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001022 return ccSuccess;
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301023 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001024
1025 bool validUser = isValidUserName(userName);
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301026 UserInfo* userInfo = getUserInfo(userId);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001027 if (userName.empty() && !oldUser.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301028 {
1029 // Delete existing user
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301030 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1031 tempUserPath /= oldUser;
1032 std::string userPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301033 try
1034 {
1035 auto method = bus.new_method_call(
1036 getUserServiceName().c_str(), userPath.c_str(),
1037 deleteUserInterface, deleteUserMethod);
1038 auto reply = bus.call(method);
1039 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001040 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301041 {
1042 log<level::DEBUG>("Failed to excute method",
1043 entry("METHOD=%s", deleteUserMethod),
1044 entry("PATH=%s", userPath.c_str()));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001045 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301046 }
Richard Marian Thomaiyar02710bb2018-11-28 20:42:25 +05301047 deleteUserIndex(userId);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301048 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001049 else if (oldUser.empty() && !userName.empty() && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301050 {
1051 try
1052 {
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301053 if (!isIpmiInAvailableGroupList())
1054 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001055 return ccUnspecifiedError;
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301056 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301057 // Create new user
1058 auto method = bus.new_method_call(
1059 getUserServiceName().c_str(), userMgrObjBasePath,
1060 userMgrInterface, createUserMethod);
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001061 method.append(userName.c_str(), availableGroups,
1062 ipmiPrivIndex[PRIVILEGE_USER], false);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301063 auto reply = bus.call(method);
1064 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001065 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301066 {
1067 log<level::DEBUG>("Failed to excute method",
1068 entry("METHOD=%s", createUserMethod),
1069 entry("PATH=%s", userMgrObjBasePath));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001070 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301071 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001072
1073 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1074 std::memcpy(userInfo->userName,
1075 static_cast<const void*>(userName.data()), userName.size());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301076 userInfo->userInSystem = true;
Alexander Filippovf6f3bb02022-02-01 14:38:40 +03001077 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1078 {
1079 userInfo->userPrivAccess[chIndex].privilege =
1080 static_cast<uint8_t>(PRIVILEGE_USER);
1081 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301082 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001083 else if (oldUser != userName && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301084 {
1085 try
1086 {
1087 // User rename
1088 auto method = bus.new_method_call(
1089 getUserServiceName().c_str(), userMgrObjBasePath,
1090 userMgrInterface, renameUserMethod);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001091 method.append(oldUser.c_str(), userName.c_str());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301092 auto reply = bus.call(method);
1093 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001094 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301095 {
1096 log<level::DEBUG>("Failed to excute method",
1097 entry("METHOD=%s", renameUserMethod),
1098 entry("PATH=%s", userMgrObjBasePath));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001099 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301100 }
1101 std::fill(static_cast<uint8_t*>(userInfo->userName),
1102 static_cast<uint8_t*>(userInfo->userName) +
1103 sizeof(userInfo->userName),
1104 0);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001105
1106 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1107 std::memcpy(userInfo->userName,
1108 static_cast<const void*>(userName.data()), userName.size());
1109
1110 ipmiRenameUserEntryPassword(oldUser, userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301111 userInfo->userInSystem = true;
1112 }
1113 else if (!validUser)
1114 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001115 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301116 }
1117 try
1118 {
1119 writeUserData();
1120 }
1121 catch (const std::exception& e)
1122 {
1123 log<level::DEBUG>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001124 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301125 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001126 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301127}
1128
1129static constexpr const char* jsonUserName = "user_name";
1130static constexpr const char* jsonPriv = "privilege";
1131static constexpr const char* jsonIpmiEnabled = "ipmi_enabled";
1132static constexpr const char* jsonLinkAuthEnabled = "link_auth_enabled";
1133static constexpr const char* jsonAccCallbk = "access_callback";
1134static constexpr const char* jsonUserEnabled = "user_enabled";
1135static constexpr const char* jsonUserInSys = "user_in_system";
1136static constexpr const char* jsonFixedUser = "fixed_user_name";
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001137static constexpr const char* payloadEnabledStr = "payload_enabled";
1138static constexpr const char* stdPayloadStr = "std_payload";
1139static constexpr const char* oemPayloadStr = "OEM_payload";
1140
1141/** @brief to construct a JSON object from the given payload access details.
1142 *
1143 * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. (input)
1144 * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. (input)
1145 *
1146 * @details Sample output JSON object format :
1147 * "payload_enabled":{
1148 * "OEM_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1149 * "OEM_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1150 * "OEM_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1151 * "OEM_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1152 * "OEM_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1153 * "OEM_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1154 * "OEM_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1155 * "OEM_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1156 * "std_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1157 * "std_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1158 * "std_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1159 * "std_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1160 * "std_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1161 * "std_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1162 * "std_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1163 * "std_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1164 * }
1165 */
1166static const Json constructJsonPayloadEnables(
1167 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1168 stdPayload,
1169 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1170 oemPayload)
1171{
1172 Json jsonPayloadEnabled;
1173
1174 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1175 {
1176 std::ostringstream stdPayloadStream;
1177 std::ostringstream oemPayloadStream;
1178
1179 stdPayloadStream << stdPayloadStr << payloadNum;
1180 oemPayloadStream << oemPayloadStr << payloadNum;
1181
1182 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1183 stdPayloadStream.str(), stdPayload[payloadNum]));
1184
1185 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1186 oemPayloadStream.str(), oemPayload[payloadNum]));
1187 }
1188 return jsonPayloadEnabled;
1189}
1190
1191void UserAccess::readPayloadAccessFromUserInfo(
1192 const UserInfo& userInfo,
1193 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& stdPayload,
1194 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& oemPayload)
1195{
1196 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1197 {
1198 for (auto chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1199 {
1200 stdPayload[payloadNum][chIndex] =
1201 userInfo.payloadAccess[chIndex].stdPayloadEnables1[payloadNum];
1202
1203 oemPayload[payloadNum][chIndex] =
1204 userInfo.payloadAccess[chIndex].oemPayloadEnables1[payloadNum];
1205 }
1206 }
1207}
1208
1209void UserAccess::updatePayloadAccessInUserInfo(
1210 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1211 stdPayload,
Willy Tu11d68892022-01-20 10:37:34 -08001212 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&,
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001213 UserInfo& userInfo)
1214{
1215 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1216 {
1217 // Ensure that reserved/unsupported payloads are marked to zero.
1218 userInfo.payloadAccess[chIndex].stdPayloadEnables1.reset();
1219 userInfo.payloadAccess[chIndex].oemPayloadEnables1.reset();
1220 userInfo.payloadAccess[chIndex].stdPayloadEnables2Reserved.reset();
1221 userInfo.payloadAccess[chIndex].oemPayloadEnables2Reserved.reset();
1222 // Update SOL status as it is the only supported payload currently.
1223 userInfo.payloadAccess[chIndex]
1224 .stdPayloadEnables1[static_cast<uint8_t>(ipmi::PayloadType::SOL)] =
1225 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)][chIndex];
1226 }
1227}
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301228
1229void UserAccess::readUserData()
1230{
1231 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1232 userLock{*userMutex};
1233
1234 std::ifstream iUsrData(ipmiUserDataFile, std::ios::in | std::ios::binary);
1235 if (!iUsrData.good())
1236 {
1237 log<level::ERR>("Error in reading IPMI user data file");
1238 throw std::ios_base::failure("Error opening IPMI user data file");
1239 }
1240
1241 Json jsonUsersTbl = Json::array();
1242 jsonUsersTbl = Json::parse(iUsrData, nullptr, false);
1243
1244 if (jsonUsersTbl.size() != ipmiMaxUsers)
1245 {
1246 log<level::ERR>(
1247 "Error in reading IPMI user data file - User count issues");
1248 throw std::runtime_error(
1249 "Corrupted IPMI user data file - invalid user count");
1250 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001251
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301252 // user index 0 is reserved, starts with 1
1253 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1254 {
1255 Json userInfo = jsonUsersTbl[usrIndex - 1]; // json array starts with 0.
1256 if (userInfo.is_null())
1257 {
1258 log<level::ERR>("Error in reading IPMI user data file - "
1259 "user info corrupted");
1260 throw std::runtime_error(
1261 "Corrupted IPMI user data file - invalid user info");
1262 }
1263 std::string userName = userInfo[jsonUserName].get<std::string>();
1264 std::strncpy(reinterpret_cast<char*>(usersTbl.user[usrIndex].userName),
1265 userName.c_str(), ipmiMaxUserName);
1266
1267 std::vector<std::string> privilege =
1268 userInfo[jsonPriv].get<std::vector<std::string>>();
1269 std::vector<bool> ipmiEnabled =
1270 userInfo[jsonIpmiEnabled].get<std::vector<bool>>();
1271 std::vector<bool> linkAuthEnabled =
1272 userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
1273 std::vector<bool> accessCallback =
1274 userInfo[jsonAccCallbk].get<std::vector<bool>>();
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001275
1276 // Payload Enables Processing.
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001277 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1278 stdPayload = {};
1279 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1280 oemPayload = {};
1281 try
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001282 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001283 const auto jsonPayloadEnabled = userInfo.at(payloadEnabledStr);
1284 for (auto payloadNum = 0; payloadNum < payloadsPerByte;
1285 payloadNum++)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001286 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001287 std::ostringstream stdPayloadStream;
1288 std::ostringstream oemPayloadStream;
1289
1290 stdPayloadStream << stdPayloadStr << payloadNum;
1291 oemPayloadStream << oemPayloadStr << payloadNum;
1292
1293 stdPayload[payloadNum] =
1294 jsonPayloadEnabled[stdPayloadStream.str()]
1295 .get<std::array<bool, ipmiMaxChannels>>();
1296 oemPayload[payloadNum] =
1297 jsonPayloadEnabled[oemPayloadStream.str()]
1298 .get<std::array<bool, ipmiMaxChannels>>();
1299
1300 if (stdPayload[payloadNum].size() != ipmiMaxChannels ||
1301 oemPayload[payloadNum].size() != ipmiMaxChannels)
1302 {
1303 log<level::ERR>("Error in reading IPMI user data file - "
1304 "payload properties corrupted");
1305 throw std::runtime_error(
1306 "Corrupted IPMI user data file - payload properties");
1307 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001308 }
1309 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001310 catch (const Json::out_of_range& e)
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001311 {
1312 // Key not found in 'userInfo'; possibly an old JSON file. Use
1313 // default values for all payloads, and SOL payload default is true.
1314 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)].fill(true);
1315 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001316
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301317 if (privilege.size() != ipmiMaxChannels ||
1318 ipmiEnabled.size() != ipmiMaxChannels ||
1319 linkAuthEnabled.size() != ipmiMaxChannels ||
1320 accessCallback.size() != ipmiMaxChannels)
1321 {
1322 log<level::ERR>("Error in reading IPMI user data file - "
1323 "properties corrupted");
1324 throw std::runtime_error(
1325 "Corrupted IPMI user data file - properties");
1326 }
1327 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1328 {
1329 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege =
1330 static_cast<uint8_t>(
1331 convertToIPMIPrivilege(privilege[chIndex]));
1332 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled =
1333 ipmiEnabled[chIndex];
1334 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled =
1335 linkAuthEnabled[chIndex];
1336 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
1337 accessCallback[chIndex];
1338 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001339 updatePayloadAccessInUserInfo(stdPayload, oemPayload,
1340 usersTbl.user[usrIndex]);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301341 usersTbl.user[usrIndex].userEnabled =
1342 userInfo[jsonUserEnabled].get<bool>();
1343 usersTbl.user[usrIndex].userInSystem =
1344 userInfo[jsonUserInSys].get<bool>();
1345 usersTbl.user[usrIndex].fixedUserName =
1346 userInfo[jsonFixedUser].get<bool>();
1347 }
1348
1349 log<level::DEBUG>("User data read from IPMI data file");
1350 iUsrData.close();
1351 // Update the timestamp
1352 fileLastUpdatedTime = getUpdatedFileTime();
1353 return;
1354}
1355
1356void UserAccess::writeUserData()
1357{
1358 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1359 userLock{*userMutex};
1360
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301361 Json jsonUsersTbl = Json::array();
1362 // user index 0 is reserved, starts with 1
1363 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1364 {
1365 Json jsonUserInfo;
1366 jsonUserInfo[jsonUserName] = std::string(
1367 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
1368 ipmiMaxUserName);
1369 std::vector<std::string> privilege(ipmiMaxChannels);
1370 std::vector<bool> ipmiEnabled(ipmiMaxChannels);
1371 std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
1372 std::vector<bool> accessCallback(ipmiMaxChannels);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001373
1374 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1375 stdPayload;
1376 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1377 oemPayload;
1378
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301379 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1380 {
1381 privilege[chIndex] =
1382 convertToSystemPrivilege(static_cast<CommandPrivilege>(
1383 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege));
1384 ipmiEnabled[chIndex] =
1385 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled;
1386 linkAuthEnabled[chIndex] =
1387 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled;
1388 accessCallback[chIndex] =
1389 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback;
1390 }
1391 jsonUserInfo[jsonPriv] = privilege;
1392 jsonUserInfo[jsonIpmiEnabled] = ipmiEnabled;
1393 jsonUserInfo[jsonLinkAuthEnabled] = linkAuthEnabled;
1394 jsonUserInfo[jsonAccCallbk] = accessCallback;
1395 jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
1396 jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
1397 jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001398
1399 readPayloadAccessFromUserInfo(usersTbl.user[usrIndex], stdPayload,
1400 oemPayload);
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001401 Json jsonPayloadEnabledInfo = constructJsonPayloadEnables(stdPayload,
1402 oemPayload);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001403 jsonUserInfo[payloadEnabledStr] = jsonPayloadEnabledInfo;
1404
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301405 jsonUsersTbl.push_back(jsonUserInfo);
1406 }
1407
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301408 static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
1409 int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
1410 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1411 if (fd < 0)
1412 {
1413 log<level::ERR>("Error in creating temporary IPMI user data file");
1414 throw std::ios_base::failure(
1415 "Error in creating temporary IPMI user data file");
1416 }
1417 const auto& writeStr = jsonUsersTbl.dump();
1418 if (write(fd, writeStr.c_str(), writeStr.size()) !=
1419 static_cast<ssize_t>(writeStr.size()))
1420 {
1421 close(fd);
1422 log<level::ERR>("Error in writing temporary IPMI user data file");
1423 throw std::ios_base::failure(
1424 "Error in writing temporary IPMI user data file");
1425 }
1426 close(fd);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301427
1428 if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
1429 {
1430 log<level::ERR>("Error in renaming temporary IPMI user data file");
1431 throw std::runtime_error("Error in renaming IPMI user data file");
1432 }
1433 // Update the timestamp
1434 fileLastUpdatedTime = getUpdatedFileTime();
1435 return;
1436}
1437
1438bool UserAccess::addUserEntry(const std::string& userName,
1439 const std::string& sysPriv, const bool& enabled)
1440{
1441 UsersTbl* userData = getUsersTblPtr();
1442 size_t freeIndex = 0xFF;
1443 // user index 0 is reserved, starts with 1
1444 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1445 {
1446 std::string curName(
1447 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
1448 ipmiMaxUserName);
1449 if (userName == curName)
1450 {
1451 log<level::DEBUG>("User name exists",
1452 entry("USER_NAME=%s", userName.c_str()));
1453 return false; // user name exists.
1454 }
1455
1456 if ((!userData->user[usrIndex].userInSystem) &&
1457 (userData->user[usrIndex].userName[0] == '\0') &&
1458 (freeIndex == 0xFF))
1459 {
1460 freeIndex = usrIndex;
1461 }
1462 }
1463 if (freeIndex == 0xFF)
1464 {
1465 log<level::ERR>("No empty slots found");
1466 return false;
1467 }
1468 std::strncpy(reinterpret_cast<char*>(userData->user[freeIndex].userName),
1469 userName.c_str(), ipmiMaxUserName);
1470 uint8_t priv =
1471 static_cast<uint8_t>(UserAccess::convertToIPMIPrivilege(sysPriv)) &
1472 privMask;
1473 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1474 {
1475 userData->user[freeIndex].userPrivAccess[chIndex].privilege = priv;
1476 userData->user[freeIndex].userPrivAccess[chIndex].ipmiEnabled = true;
1477 userData->user[freeIndex].userPrivAccess[chIndex].linkAuthEnabled =
1478 true;
1479 userData->user[freeIndex].userPrivAccess[chIndex].accessCallback = true;
1480 }
1481 userData->user[freeIndex].userInSystem = true;
1482 userData->user[freeIndex].userEnabled = enabled;
1483
1484 return true;
1485}
1486
1487void UserAccess::deleteUserIndex(const size_t& usrIdx)
1488{
1489 UsersTbl* userData = getUsersTblPtr();
1490
1491 std::string userName(
1492 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1493 ipmiMaxUserName);
1494 ipmiClearUserEntryPassword(userName);
1495 std::fill(static_cast<uint8_t*>(userData->user[usrIdx].userName),
1496 static_cast<uint8_t*>(userData->user[usrIdx].userName) +
1497 sizeof(userData->user[usrIdx].userName),
1498 0);
1499 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1500 {
1501 userData->user[usrIdx].userPrivAccess[chIndex].privilege = privNoAccess;
1502 userData->user[usrIdx].userPrivAccess[chIndex].ipmiEnabled = false;
1503 userData->user[usrIdx].userPrivAccess[chIndex].linkAuthEnabled = false;
1504 userData->user[usrIdx].userPrivAccess[chIndex].accessCallback = false;
1505 }
1506 userData->user[usrIdx].userInSystem = false;
1507 userData->user[usrIdx].userEnabled = false;
1508 return;
1509}
1510
1511void UserAccess::checkAndReloadUserData()
1512{
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001513 std::timespec updateTime = getUpdatedFileTime();
1514 if ((updateTime.tv_sec != fileLastUpdatedTime.tv_sec ||
1515 updateTime.tv_nsec != fileLastUpdatedTime.tv_nsec) ||
1516 (updateTime.tv_sec == 0 && updateTime.tv_nsec == 0))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301517 {
1518 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1519 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1520 readUserData();
1521 }
1522 return;
1523}
1524
1525UsersTbl* UserAccess::getUsersTblPtr()
1526{
1527 // reload data before using it.
1528 checkAndReloadUserData();
1529 return &usersTbl;
1530}
1531
1532void UserAccess::getSystemPrivAndGroups()
1533{
1534 std::map<std::string, PrivAndGroupType> properties;
1535 try
1536 {
1537 auto method = bus.new_method_call(
1538 getUserServiceName().c_str(), userMgrObjBasePath,
1539 dBusPropertiesInterface, getAllPropertiesMethod);
1540 method.append(userMgrInterface);
1541
1542 auto reply = bus.call(method);
1543 reply.read(properties);
1544 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001545 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301546 {
1547 log<level::DEBUG>("Failed to excute method",
1548 entry("METHOD=%s", getAllPropertiesMethod),
1549 entry("PATH=%s", userMgrObjBasePath));
1550 return;
1551 }
1552 for (const auto& t : properties)
1553 {
1554 auto key = t.first;
1555 if (key == allPrivProperty)
1556 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001557 availablePrivileges = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301558 }
1559 else if (key == allGrpProperty)
1560 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001561 availableGroups = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301562 }
1563 }
1564 // TODO: Implement Supported Privilege & Groups verification logic
1565 return;
1566}
1567
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001568std::timespec UserAccess::getUpdatedFileTime()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301569{
1570 struct stat fileStat;
1571 if (stat(ipmiUserDataFile, &fileStat) != 0)
1572 {
1573 log<level::DEBUG>("Error in getting last updated time stamp");
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001574 return std::timespec{0, 0};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301575 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001576 return fileStat.st_mtim;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301577}
1578
1579void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
1580 std::vector<std::string>& usrGrps,
1581 std::string& usrPriv, bool& usrEnabled)
1582{
1583 for (const auto& t : properties)
1584 {
1585 std::string key = t.first;
1586 if (key == userPrivProperty)
1587 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001588 usrPriv = std::get<std::string>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301589 }
1590 else if (key == userGrpProperty)
1591 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001592 usrGrps = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301593 }
1594 else if (key == userEnabledProperty)
1595 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001596 usrEnabled = std::get<bool>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301597 }
1598 }
1599 return;
1600}
1601
1602int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
1603 std::vector<std::string>& usrGrps,
1604 std::string& usrPriv, bool& usrEnabled)
1605{
1606 auto usrObj = userObjs.find(usersInterface);
1607 if (usrObj != userObjs.end())
1608 {
1609 getUserProperties(usrObj->second, usrGrps, usrPriv, usrEnabled);
1610 return 0;
1611 }
1612 return -EIO;
1613}
1614
arun-pmbbe728c2020-01-10 15:18:04 +05301615void UserAccess::cacheUserDataFile()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301616{
1617 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1618 userLock{*userMutex};
1619 try
1620 {
1621 readUserData();
1622 }
1623 catch (const std::ios_base::failure& e)
1624 { // File is empty, create it for the first time
1625 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1626 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1627 // user index 0 is reserved, starts with 1
1628 for (size_t userIndex = 1; userIndex <= ipmiMaxUsers; ++userIndex)
1629 {
1630 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1631 {
1632 usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
1633 privNoAccess;
Saravanan Palanisamy92d81192019-08-07 18:00:04 +00001634 usersTbl.user[userIndex]
1635 .payloadAccess[chIndex]
1636 .stdPayloadEnables1[static_cast<uint8_t>(
1637 ipmi::PayloadType::SOL)] = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301638 }
1639 }
1640 writeUserData();
1641 }
Jayaprakash Mutyala08d3d062021-10-01 16:01:57 +00001642 // Create lock file if it does not exist
1643 int fd = open(ipmiUserSignalLockFile, O_CREAT | O_TRUNC | O_SYNC,
1644 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1645 if (fd < 0)
1646 {
1647 log<level::ERR>("Error in creating IPMI user signal lock file");
1648 throw std::ios_base::failure(
1649 "Error in creating temporary IPMI user signal lock file");
1650 }
1651 close(fd);
1652
1653 sigHndlrLock = boost::interprocess::file_lock(ipmiUserSignalLockFile);
George Liu1a2e1502022-07-08 12:20:19 +08001654 // Register it for single object and single process either netipmid /
arun-pmbbe728c2020-01-10 15:18:04 +05301655 // host-ipmid
1656 if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
1657 {
1658 log<level::DEBUG>("Registering signal handler");
1659 userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
1660 bus,
1661 sdbusplus::bus::match::rules::type::signal() +
1662 sdbusplus::bus::match::rules::interface(dBusObjManager) +
1663 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001664 [&](sdbusplus::message_t& msg) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001665 userUpdatedSignalHandler(*this, msg);
Patrick Williams369824e2023-10-20 11:18:23 -05001666 });
arun-pmbbe728c2020-01-10 15:18:04 +05301667 userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
1668 bus,
1669 sdbusplus::bus::match::rules::type::signal() +
1670 sdbusplus::bus::match::rules::interface(userMgrInterface) +
1671 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
Patrick Williams5d82f472022-07-22 19:26:53 -05001672 [&](sdbusplus::message_t& msg) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001673 userUpdatedSignalHandler(*this, msg);
Patrick Williams369824e2023-10-20 11:18:23 -05001674 });
arun-pmbbe728c2020-01-10 15:18:04 +05301675 userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
1676 bus,
1677 sdbusplus::bus::match::rules::type::signal() +
1678 sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
1679 sdbusplus::bus::match::rules::interface(
1680 dBusPropertiesInterface) +
1681 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
1682 sdbusplus::bus::match::rules::argN(0, usersInterface),
Patrick Williams5d82f472022-07-22 19:26:53 -05001683 [&](sdbusplus::message_t& msg) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001684 userUpdatedSignalHandler(*this, msg);
Patrick Williams369824e2023-10-20 11:18:23 -05001685 });
arun-pmbbe728c2020-01-10 15:18:04 +05301686 signalHndlrObject = true;
1687 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301688 std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
1689 try
1690 {
1691 auto method = bus.new_method_call(getUserServiceName().c_str(),
1692 userMgrObjBasePath, dBusObjManager,
1693 getManagedObjectsMethod);
1694 auto reply = bus.call(method);
1695 reply.read(managedObjs);
1696 }
Patrick Williams5d82f472022-07-22 19:26:53 -05001697 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301698 {
1699 log<level::DEBUG>("Failed to excute method",
George Liu1f42d1a2024-02-04 17:28:13 +08001700 entry("METHOD=%s", getManagedObjectsMethod),
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301701 entry("PATH=%s", userMgrObjBasePath));
1702 return;
1703 }
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301704 bool updateRequired = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301705 UsersTbl* userData = &usersTbl;
1706 // user index 0 is reserved, starts with 1
1707 for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
1708 {
1709 if ((userData->user[usrIdx].userInSystem) &&
1710 (userData->user[usrIdx].userName[0] != '\0'))
1711 {
1712 std::vector<std::string> usrGrps;
1713 std::string usrPriv;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301714
1715 std::string userName(
1716 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1717 ipmiMaxUserName);
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301718 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1719 tempUserPath /= userName;
1720 std::string usersPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301721
1722 auto usrObj = managedObjs.find(usersPath);
1723 if (usrObj != managedObjs.end())
1724 {
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001725 bool usrEnabled = false;
Patrick Venture3a697ad2019-08-19 11:12:05 -07001726
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301727 // User exist. Lets check and update other fileds
1728 getUserObjProperties(usrObj->second, usrGrps, usrPriv,
1729 usrEnabled);
1730 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
1731 usrGrps.end())
1732 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301733 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301734 // Group "ipmi" is removed so lets remove user in IPMI
1735 deleteUserIndex(usrIdx);
1736 }
1737 else
1738 {
1739 // Group "ipmi" is present so lets update other properties
1740 // in IPMI
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001741 uint8_t priv = UserAccess::convertToIPMIPrivilege(usrPriv) &
1742 privMask;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301743 // Update all channels priv, only if it is not equivalent to
1744 // getUsrMgmtSyncIndex()
1745 if (userData->user[usrIdx]
1746 .userPrivAccess[getUsrMgmtSyncIndex()]
1747 .privilege != priv)
1748 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301749 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301750 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
1751 ++chIndex)
1752 {
1753 userData->user[usrIdx]
1754 .userPrivAccess[chIndex]
1755 .privilege = priv;
1756 }
1757 }
1758 if (userData->user[usrIdx].userEnabled != usrEnabled)
1759 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301760 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301761 userData->user[usrIdx].userEnabled = usrEnabled;
1762 }
1763 }
1764
1765 // We are done with this obj. lets delete from MAP
1766 managedObjs.erase(usrObj);
1767 }
1768 else
1769 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301770 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301771 deleteUserIndex(usrIdx);
1772 }
1773 }
1774 }
1775
1776 // Walk through remnaining managedObj users list
1777 // Add them to ipmi data base
1778 for (const auto& usrObj : managedObjs)
1779 {
1780 std::vector<std::string> usrGrps;
1781 std::string usrPriv, userName;
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001782 bool usrEnabled = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301783 std::string usrObjPath = std::string(usrObj.first);
1784 if (getUserNameFromPath(usrObj.first.str, userName) != 0)
1785 {
1786 log<level::ERR>("Error in user object path");
1787 continue;
1788 }
1789 getUserObjProperties(usrObj.second, usrGrps, usrPriv, usrEnabled);
1790 // Add 'ipmi' group users
1791 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
1792 usrGrps.end())
1793 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301794 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301795 // CREATE NEW USER
1796 if (true != addUserEntry(userName, usrPriv, usrEnabled))
1797 {
1798 break;
1799 }
1800 }
1801 }
1802
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301803 if (updateRequired)
1804 {
1805 // All userData slots update done. Lets write the data
1806 writeUserData();
1807 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301808
1809 return;
1810}
1811} // namespace ipmi