blob: 2459de706746fc8b93c07bc1cd5d567bc9a6b1d1 [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>
28#include <cerrno>
29#include <fstream>
Snehalatha Venkatesh745164c2021-06-25 10:02:25 +000030#include <ipmid/types.hpp>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053031#include <nlohmann/json.hpp>
32#include <phosphor-logging/elog-errors.hpp>
33#include <phosphor-logging/log.hpp>
34#include <regex>
35#include <sdbusplus/bus/match.hpp>
36#include <sdbusplus/server/object.hpp>
Vernon Mauery16b86932019-05-01 08:36:11 -070037#include <variant>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053038#include <xyz/openbmc_project/Common/error.hpp>
39#include <xyz/openbmc_project/User/Common/error.hpp>
40
41namespace ipmi
42{
43
44// TODO: Move D-Bus & Object Manager related stuff, to common files
45// D-Bus property related
46static constexpr const char* dBusPropertiesInterface =
47 "org.freedesktop.DBus.Properties";
48static constexpr const char* getAllPropertiesMethod = "GetAll";
49static constexpr const char* propertiesChangedSignal = "PropertiesChanged";
50static constexpr const char* setPropertiesMethod = "Set";
51
52// Object Manager related
53static constexpr const char* dBusObjManager =
54 "org.freedesktop.DBus.ObjectManager";
55static constexpr const char* getManagedObjectsMethod = "GetManagedObjects";
56// Object Manager signals
57static constexpr const char* intfAddedSignal = "InterfacesAdded";
58static constexpr const char* intfRemovedSignal = "InterfacesRemoved";
59
60// Object Mapper related
61static constexpr const char* objMapperService =
62 "xyz.openbmc_project.ObjectMapper";
63static constexpr const char* objMapperPath =
64 "/xyz/openbmc_project/object_mapper";
65static constexpr const char* objMapperInterface =
66 "xyz.openbmc_project.ObjectMapper";
67static constexpr const char* getSubTreeMethod = "GetSubTree";
68static 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";
73static constexpr const char* ipmiUserDataFile = "/var/lib/ipmi/ipmi_user.json";
74static constexpr const char* ipmiGrpName = "ipmi";
75static constexpr size_t privNoAccess = 0xF;
76static constexpr size_t privMask = 0xF;
77
78// User manager related
79static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user";
80static constexpr const char* userObjBasePath = "/xyz/openbmc_project/user";
81static constexpr const char* userMgrInterface =
82 "xyz.openbmc_project.User.Manager";
83static constexpr const char* usersInterface =
84 "xyz.openbmc_project.User.Attributes";
85static constexpr const char* deleteUserInterface =
86 "xyz.openbmc_project.Object.Delete";
87
88static constexpr const char* createUserMethod = "CreateUser";
89static constexpr const char* deleteUserMethod = "Delete";
90static constexpr const char* renameUserMethod = "RenameUser";
91// User manager signal memebers
92static constexpr const char* userRenamedSignal = "UserRenamed";
93// Mgr interface properties
94static constexpr const char* allPrivProperty = "AllPrivileges";
95static constexpr const char* allGrpProperty = "AllGroups";
96// User interface properties
97static constexpr const char* userPrivProperty = "UserPrivilege";
98static constexpr const char* userGrpProperty = "UserGroups";
99static constexpr const char* userEnabledProperty = "UserEnabled";
100
101static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = {
102 "priv-reserved", // PRIVILEGE_RESERVED - 0
103 "priv-callback", // PRIVILEGE_CALLBACK - 1
104 "priv-user", // PRIVILEGE_USER - 2
105 "priv-operator", // PRIVILEGE_OPERATOR - 3
106 "priv-admin", // PRIVILEGE_ADMIN - 4
107 "priv-custom" // PRIVILEGE_OEM - 5
108};
109
110using namespace phosphor::logging;
111using Json = nlohmann::json;
112
Vernon Mauery16b86932019-05-01 08:36:11 -0700113using PrivAndGroupType = std::variant<std::string, std::vector<std::string>>;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530114
115using NoResource =
116 sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource;
117
118using InternalFailure =
119 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
120
Lei YU4b0ddb62019-01-25 16:43:50 +0800121std::unique_ptr<sdbusplus::bus::match_t> userUpdatedSignal
122 __attribute__((init_priority(101)));
123std::unique_ptr<sdbusplus::bus::match_t> userMgrRenamedSignal
124 __attribute__((init_priority(101)));
125std::unique_ptr<sdbusplus::bus::match_t> userPropertiesSignal
126 __attribute__((init_priority(101)));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530127
128// TODO: Below code can be removed once it is moved to common layer libmiscutil
129std::string getUserService(sdbusplus::bus::bus& bus, const std::string& intf,
130 const std::string& path)
131{
132 auto mapperCall = bus.new_method_call(objMapperService, objMapperPath,
133 objMapperInterface, getObjectMethod);
134
135 mapperCall.append(path);
136 mapperCall.append(std::vector<std::string>({intf}));
137
138 auto mapperResponseMsg = bus.call(mapperCall);
139
140 std::map<std::string, std::vector<std::string>> mapperResponse;
141 mapperResponseMsg.read(mapperResponse);
142
143 if (mapperResponse.begin() == mapperResponse.end())
144 {
145 throw sdbusplus::exception::SdBusError(
146 -EIO, "ERROR in reading the mapper response");
147 }
148
149 return mapperResponse.begin()->first;
150}
151
152void setDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
153 const std::string& objPath, const std::string& interface,
154 const std::string& property,
155 const DbusUserPropVariant& value)
156{
157 try
158 {
159 auto method =
160 bus.new_method_call(service.c_str(), objPath.c_str(),
161 dBusPropertiesInterface, setPropertiesMethod);
162 method.append(interface, property, value);
163 bus.call(method);
164 }
Patrick Williamsef1259b2021-09-02 09:12:33 -0500165 catch (const sdbusplus::exception::exception& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530166 {
167 log<level::ERR>("Failed to set property",
168 entry("PROPERTY=%s", property.c_str()),
169 entry("PATH=%s", objPath.c_str()),
170 entry("INTERFACE=%s", interface.c_str()));
171 throw;
172 }
173}
174
175static std::string getUserServiceName()
176{
177 static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
178 static std::string userMgmtService;
179 if (userMgmtService.empty())
180 {
181 try
182 {
183 userMgmtService =
184 ipmi::getUserService(bus, userMgrInterface, userMgrObjBasePath);
185 }
Patrick Williamsef1259b2021-09-02 09:12:33 -0500186 catch (const sdbusplus::exception::exception& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530187 {
188 userMgmtService.clear();
189 }
190 }
191 return userMgmtService;
192}
193
194UserAccess& getUserAccessObject()
195{
196 static UserAccess userAccess;
197 return userAccess;
198}
199
200int getUserNameFromPath(const std::string& path, std::string& userName)
201{
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530202 sdbusplus::message::object_path objPath(path);
203 userName.assign(objPath.filename());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530204 return 0;
205}
206
207void userUpdateHelper(UserAccess& usrAccess, const UserUpdateEvent& userEvent,
208 const std::string& userName, const std::string& priv,
209 const bool& enabled, const std::string& newUserName)
210{
211 UsersTbl* userData = usrAccess.getUsersTblPtr();
212 if (userEvent == UserUpdateEvent::userCreated)
213 {
214 if (usrAccess.addUserEntry(userName, priv, enabled) == false)
215 {
216 return;
217 }
218 }
219 else
220 {
221 // user index 0 is reserved, starts with 1
222 size_t usrIndex = 1;
223 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
224 {
225 std::string curName(
226 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
227 ipmiMaxUserName);
228 if (userName == curName)
229 {
230 break; // found the entry
231 }
232 }
233 if (usrIndex > ipmiMaxUsers)
234 {
235 log<level::DEBUG>("User not found for signal",
236 entry("USER_NAME=%s", userName.c_str()),
237 entry("USER_EVENT=%d", userEvent));
238 return;
239 }
240 switch (userEvent)
241 {
242 case UserUpdateEvent::userDeleted:
243 {
244 usrAccess.deleteUserIndex(usrIndex);
245 break;
246 }
247 case UserUpdateEvent::userPrivUpdated:
248 {
249 uint8_t userPriv =
250 static_cast<uint8_t>(
251 UserAccess::convertToIPMIPrivilege(priv)) &
252 privMask;
253 // Update all channels privileges, only if it is not equivalent
254 // to getUsrMgmtSyncIndex()
255 if (userData->user[usrIndex]
256 .userPrivAccess[UserAccess::getUsrMgmtSyncIndex()]
257 .privilege != userPriv)
258 {
259 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
260 ++chIndex)
261 {
262 userData->user[usrIndex]
263 .userPrivAccess[chIndex]
264 .privilege = userPriv;
265 }
266 }
267 break;
268 }
269 case UserUpdateEvent::userRenamed:
270 {
271 std::fill(
272 static_cast<uint8_t*>(userData->user[usrIndex].userName),
273 static_cast<uint8_t*>(userData->user[usrIndex].userName) +
274 sizeof(userData->user[usrIndex].userName),
275 0);
276 std::strncpy(
277 reinterpret_cast<char*>(userData->user[usrIndex].userName),
278 newUserName.c_str(), ipmiMaxUserName);
279 ipmiRenameUserEntryPassword(userName, newUserName);
280 break;
281 }
282 case UserUpdateEvent::userStateUpdated:
283 {
284 userData->user[usrIndex].userEnabled = enabled;
285 break;
286 }
287 default:
288 {
289 log<level::ERR>("Unhandled user event",
290 entry("USER_EVENT=%d", userEvent));
291 return;
292 }
293 }
294 }
295 usrAccess.writeUserData();
296 log<level::DEBUG>("User event handled successfully",
297 entry("USER_NAME=%s", userName.c_str()),
298 entry("USER_EVENT=%d", userEvent));
299
300 return;
301}
302
303void userUpdatedSignalHandler(UserAccess& usrAccess,
304 sdbusplus::message::message& msg)
305{
306 static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
307 std::string signal = msg.get_member();
Patrick Venture3a697ad2019-08-19 11:12:05 -0700308 std::string userName, priv, newUserName;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530309 std::vector<std::string> groups;
310 bool enabled = false;
311 UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent;
312 if (signal == intfAddedSignal)
313 {
314 DbusUserObjPath objPath;
315 DbusUserObjValue objValue;
316 msg.read(objPath, objValue);
317 getUserNameFromPath(objPath.str, userName);
318 if (usrAccess.getUserObjProperties(objValue, groups, priv, enabled) !=
319 0)
320 {
321 return;
322 }
323 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
324 groups.end())
325 {
326 return;
327 }
328 userEvent = UserUpdateEvent::userCreated;
329 }
330 else if (signal == intfRemovedSignal)
331 {
332 DbusUserObjPath objPath;
333 std::vector<std::string> interfaces;
334 msg.read(objPath, interfaces);
335 getUserNameFromPath(objPath.str, userName);
336 userEvent = UserUpdateEvent::userDeleted;
337 }
338 else if (signal == userRenamedSignal)
339 {
340 msg.read(userName, newUserName);
341 userEvent = UserUpdateEvent::userRenamed;
342 }
343 else if (signal == propertiesChangedSignal)
344 {
345 getUserNameFromPath(msg.get_path(), userName);
346 }
347 else
348 {
349 log<level::ERR>("Unknown user update signal",
350 entry("SIGNAL=%s", signal.c_str()));
351 return;
352 }
353
354 if (signal.empty() || userName.empty() ||
355 (signal == userRenamedSignal && newUserName.empty()))
356 {
357 log<level::ERR>("Invalid inputs received");
358 return;
359 }
360
361 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
362 userLock{*(usrAccess.userMutex)};
363 usrAccess.checkAndReloadUserData();
364
365 if (signal == propertiesChangedSignal)
366 {
367 std::string intfName;
368 DbusUserObjProperties chProperties;
369 msg.read(intfName, chProperties); // skip reading 3rd argument.
370 for (const auto& prop : chProperties)
371 {
372 userEvent = UserUpdateEvent::reservedEvent;
373 std::string member = prop.first;
374 if (member == userPrivProperty)
375 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700376 priv = std::get<std::string>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530377 userEvent = UserUpdateEvent::userPrivUpdated;
378 }
379 else if (member == userGrpProperty)
380 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700381 groups = std::get<std::vector<std::string>>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530382 userEvent = UserUpdateEvent::userGrpUpdated;
383 }
384 else if (member == userEnabledProperty)
385 {
Vernon Maueryf442e112019-04-09 11:44:36 -0700386 enabled = std::get<bool>(prop.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530387 userEvent = UserUpdateEvent::userStateUpdated;
388 }
389 // Process based on event type.
390 if (userEvent == UserUpdateEvent::userGrpUpdated)
391 {
392 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
393 groups.end())
394 {
395 // remove user from ipmi user list.
396 userUpdateHelper(usrAccess, UserUpdateEvent::userDeleted,
397 userName, priv, enabled, newUserName);
398 }
399 else
400 {
401 DbusUserObjProperties properties;
402 try
403 {
404 auto method = bus.new_method_call(
405 getUserServiceName().c_str(), msg.get_path(),
406 dBusPropertiesInterface, getAllPropertiesMethod);
407 method.append(usersInterface);
408 auto reply = bus.call(method);
409 reply.read(properties);
410 }
Patrick Williamsef1259b2021-09-02 09:12:33 -0500411 catch (const sdbusplus::exception::exception& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530412 {
413 log<level::DEBUG>(
414 "Failed to excute method",
415 entry("METHOD=%s", getAllPropertiesMethod),
416 entry("PATH=%s", msg.get_path()));
417 return;
418 }
419 usrAccess.getUserProperties(properties, groups, priv,
420 enabled);
421 // add user to ipmi user list.
422 userUpdateHelper(usrAccess, UserUpdateEvent::userCreated,
423 userName, priv, enabled, newUserName);
424 }
425 }
426 else if (userEvent != UserUpdateEvent::reservedEvent)
427 {
428 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
429 newUserName);
430 }
431 }
432 }
433 else if (userEvent != UserUpdateEvent::reservedEvent)
434 {
435 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
436 newUserName);
437 }
438 return;
439}
440
441UserAccess::~UserAccess()
442{
443 if (signalHndlrObject)
444 {
445 userUpdatedSignal.reset();
446 userMgrRenamedSignal.reset();
447 userPropertiesSignal.reset();
448 sigHndlrLock.unlock();
449 }
450}
451
452UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection())
453{
454 std::ofstream mutexCleanUpFile;
455 mutexCleanUpFile.open(ipmiMutexCleanupLockFile,
456 std::ofstream::out | std::ofstream::app);
457 if (!mutexCleanUpFile.good())
458 {
459 log<level::DEBUG>("Unable to open mutex cleanup file");
460 return;
461 }
462 mutexCleanUpFile.close();
463 mutexCleanupLock = boost::interprocess::file_lock(ipmiMutexCleanupLockFile);
464 if (mutexCleanupLock.try_lock())
465 {
466 boost::interprocess::named_recursive_mutex::remove(ipmiUserMutex);
467 }
468 mutexCleanupLock.lock_sharable();
469 userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>(
470 boost::interprocess::open_or_create, ipmiUserMutex);
471
arun-pmbbe728c2020-01-10 15:18:04 +0530472 cacheUserDataFile();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530473 getSystemPrivAndGroups();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530474}
475
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530476UserInfo* UserAccess::getUserInfo(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530477{
478 checkAndReloadUserData();
479 return &usersTbl.user[userId];
480}
481
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530482void UserAccess::setUserInfo(const uint8_t userId, UserInfo* userInfo)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530483{
484 checkAndReloadUserData();
485 std::copy(reinterpret_cast<uint8_t*>(userInfo),
486 reinterpret_cast<uint8_t*>(userInfo) + sizeof(*userInfo),
487 reinterpret_cast<uint8_t*>(&usersTbl.user[userId]));
488 writeUserData();
489}
490
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530491bool UserAccess::isValidChannel(const uint8_t chNum)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530492{
493 return (chNum < ipmiMaxChannels);
494}
495
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530496bool UserAccess::isValidUserId(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530497{
498 return ((userId <= ipmiMaxUsers) && (userId != reservedUserId));
499}
500
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530501bool UserAccess::isValidPrivilege(const uint8_t priv)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530502{
jayaprakash Mutyala0e2dbee2019-12-26 13:03:04 +0000503 // Callback privilege is deprecated in OpenBMC
504 return (isValidPrivLimit(priv) || priv == privNoAccess);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530505}
506
507uint8_t UserAccess::getUsrMgmtSyncIndex()
508{
Johnathan Manteyfd61fc32021-04-08 11:05:38 -0700509 // Identify the IPMI channel used to assign system user privilege levels
510 // in phosphor-user-manager. The default value is IPMI Channel 1. To
511 // assign a different channel add:
512 // "is_management_nic" : true
513 // into the channel_config.json file describing the assignment of the IPMI
514 // channels. It is only necessary to add the string above to ONE record in
515 // the channel_config.json file. All other records will be automatically
516 // assigned a "false" value.
517 return getChannelConfigObject().getManagementNICID();
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530518}
519
520CommandPrivilege UserAccess::convertToIPMIPrivilege(const std::string& value)
521{
522 auto iter = std::find(ipmiPrivIndex.begin(), ipmiPrivIndex.end(), value);
523 if (iter == ipmiPrivIndex.end())
524 {
525 if (value == "")
526 {
527 return static_cast<CommandPrivilege>(privNoAccess);
528 }
529 log<level::ERR>("Error in converting to IPMI privilege",
530 entry("PRIV=%s", value.c_str()));
531 throw std::out_of_range("Out of range - convertToIPMIPrivilege");
532 }
533 else
534 {
535 return static_cast<CommandPrivilege>(
536 std::distance(ipmiPrivIndex.begin(), iter));
537 }
538}
539
540std::string UserAccess::convertToSystemPrivilege(const CommandPrivilege& value)
541{
542 if (value == static_cast<CommandPrivilege>(privNoAccess))
543 {
544 return "";
545 }
546 try
547 {
548 return ipmiPrivIndex.at(value);
549 }
550 catch (const std::out_of_range& e)
551 {
552 log<level::ERR>("Error in converting to system privilege",
553 entry("PRIV=%d", static_cast<uint8_t>(value)));
554 throw std::out_of_range("Out of range - convertToSystemPrivilege");
555 }
556}
557
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000558bool UserAccess::isValidUserName(const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530559{
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000560 if (userName.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530561 {
jayaprakash Mutyala76363302020-02-14 23:50:38 +0000562 log<level::ERR>("userName is empty");
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530563 return false;
564 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530565 if (!std::regex_match(userName.c_str(),
566 std::regex("[a-zA-z_][a-zA-Z_0-9]*")))
567 {
568 log<level::ERR>("Unsupported characters in user name");
569 return false;
570 }
571 if (userName == "root")
572 {
573 log<level::ERR>("Invalid user name - root");
574 return false;
575 }
576 std::map<DbusUserObjPath, DbusUserObjValue> properties;
577 try
578 {
579 auto method = bus.new_method_call(getUserServiceName().c_str(),
580 userMgrObjBasePath, dBusObjManager,
581 getManagedObjectsMethod);
582 auto reply = bus.call(method);
583 reply.read(properties);
584 }
Patrick Williamsef1259b2021-09-02 09:12:33 -0500585 catch (const sdbusplus::exception::exception& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530586 {
587 log<level::ERR>("Failed to excute method",
588 entry("METHOD=%s", getSubTreeMethod),
589 entry("PATH=%s", userMgrObjBasePath));
590 return false;
591 }
592
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530593 sdbusplus::message::object_path tempUserPath(userObjBasePath);
594 tempUserPath /= userName;
595 std::string usersPath(tempUserPath);
596
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530597 if (properties.find(usersPath) != properties.end())
598 {
599 log<level::DEBUG>("User name already exists",
600 entry("USER_NAME=%s", userName.c_str()));
601 return false;
602 }
603
604 return true;
605}
606
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530607/** @brief Information exchanged by pam module and application.
608 *
609 * @param[in] numMsg - length of the array of pointers,msg.
610 *
611 * @param[in] msg - pointer to an array of pointers to pam_message structure
612 *
613 * @param[out] resp - struct pam response array
614 *
615 * @param[in] appdataPtr - member of pam_conv structure
616 *
617 * @return the response in pam response structure.
618 */
619
620static int pamFunctionConversation(int numMsg, const struct pam_message** msg,
621 struct pam_response** resp, void* appdataPtr)
622{
623 if (appdataPtr == nullptr)
624 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530625 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530626 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530627
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530628 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
629 {
630 return PAM_CONV_ERR;
631 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530632
633 for (int i = 0; i < numMsg; ++i)
634 {
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530635 /* Ignore all PAM messages except prompting for hidden input */
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530636 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
637 {
638 continue;
639 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530640
641 /* Assume PAM is only prompting for the password as hidden input */
642 /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */
643
644 char* appPass = reinterpret_cast<char*>(appdataPtr);
645 size_t appPassSize = std::strlen(appPass);
646
647 if (appPassSize >= PAM_MAX_RESP_SIZE)
648 {
649 return PAM_CONV_ERR;
650 }
651
652 char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1));
653 if (pass == nullptr)
654 {
655 return PAM_BUF_ERR;
656 }
657
658 void* ptr =
659 calloc(static_cast<size_t>(numMsg), sizeof(struct pam_response));
660 if (ptr == nullptr)
661 {
662 free(pass);
663 return PAM_BUF_ERR;
664 }
665
666 std::strncpy(pass, appPass, appPassSize + 1);
667
668 *resp = reinterpret_cast<pam_response*>(ptr);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530669 resp[i]->resp = pass;
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530670
671 return PAM_SUCCESS;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530672 }
P Dheeraj Srujan Kumar2aeb1c12021-07-20 04:26:13 +0530673
674 return PAM_CONV_ERR;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530675}
676
677/** @brief Updating the PAM password
678 *
679 * @param[in] username - username in string
680 *
681 * @param[in] password - new password in string
682 *
683 * @return status
684 */
685
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000686int pamUpdatePasswd(const char* username, const char* password)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530687{
688 const struct pam_conv localConversation = {pamFunctionConversation,
689 const_cast<char*>(password)};
690 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
691
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000692 int retval =
693 pam_start("passwd", username, &localConversation, &localAuthHandle);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530694
695 if (retval != PAM_SUCCESS)
696 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000697 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530698 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000699
700 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
701 if (retval != PAM_SUCCESS)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530702 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000703 pam_end(localAuthHandle, retval);
704 return retval;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530705 }
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000706
707 return pam_end(localAuthHandle, PAM_SUCCESS);
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530708}
709
Ayushi Smriti02650d52019-05-15 11:59:09 +0000710bool pamUserCheckAuthenticate(std::string_view username,
711 std::string_view password)
712{
713 const struct pam_conv localConversation = {
714 pamFunctionConversation, const_cast<char*>(password.data())};
715
716 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
717
718 if (pam_start("dropbear", username.data(), &localConversation,
719 &localAuthHandle) != PAM_SUCCESS)
720 {
721 log<level::ERR>("User Authentication Failure");
722 return false;
723 }
724
725 int retval = pam_authenticate(localAuthHandle,
726 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
727
728 if (retval != PAM_SUCCESS)
729 {
730 log<level::DEBUG>("pam_authenticate returned failure",
731 entry("ERROR=%d", retval));
732
733 pam_end(localAuthHandle, retval);
734 return false;
735 }
736
737 if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
738 PAM_SUCCESS)
739 {
740 pam_end(localAuthHandle, PAM_SUCCESS);
741 return false;
742 }
743
744 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
745 {
746 return false;
747 }
748 return true;
749}
750
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000751Cc UserAccess::setSpecialUserPassword(const std::string& userName,
Vernon Mauery1e22a0f2021-07-30 13:36:54 -0700752 const SecureString& userPassword)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530753{
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000754 if (pamUpdatePasswd(userName.c_str(), userPassword.c_str()) != PAM_SUCCESS)
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530755 {
756 log<level::DEBUG>("Failed to update password");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000757 return ccUnspecifiedError;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530758 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000759 return ccSuccess;
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530760}
761
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000762Cc UserAccess::setUserPassword(const uint8_t userId, const char* userPassword)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530763{
764 std::string userName;
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000765 if (ipmiUserGetUserName(userId, userName) != ccSuccess)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530766 {
767 log<level::DEBUG>("User Name not found",
Ayushi Smriti05ad3412019-10-16 16:10:18 +0530768 entry("USER-ID=%d", (uint8_t)userId));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000769 return ccParmOutOfRange;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530770 }
Snehalatha Venkatesh61024d72021-04-08 16:24:39 +0000771
772 ipmi::SecureString passwd;
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530773 passwd.assign(reinterpret_cast<const char*>(userPassword), 0,
774 maxIpmi20PasswordSize);
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000775 int retval = pamUpdatePasswd(userName.c_str(), passwd.c_str());
776
777 switch (retval)
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530778 {
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000779 case PAM_SUCCESS:
780 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000781 return ccSuccess;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000782 }
783 case PAM_AUTHTOK_ERR:
784 {
785 log<level::DEBUG>("Bad authentication token");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000786 return ccInvalidFieldRequest;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000787 }
788 default:
789 {
790 log<level::DEBUG>("Failed to update password",
791 entry("USER-ID=%d", (uint8_t)userId));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000792 return ccUnspecifiedError;
jayaprakash Mutyala9fc5fa12019-08-29 15:14:06 +0000793 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530794 }
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530795}
796
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000797Cc UserAccess::setUserEnabledState(const uint8_t userId,
798 const bool& enabledState)
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530799{
800 if (!isValidUserId(userId))
801 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000802 return ccParmOutOfRange;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530803 }
804 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
805 userLock{*userMutex};
806 UserInfo* userInfo = getUserInfo(userId);
807 std::string userName;
808 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
809 ipmiMaxUserName);
810 if (userName.empty())
811 {
812 log<level::DEBUG>("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000813 return ccUnspecifiedError;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530814 }
815 if (userInfo->userEnabled != enabledState)
816 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530817 sdbusplus::message::object_path tempUserPath(userObjBasePath);
818 tempUserPath /= userName;
819 std::string userPath(tempUserPath);
Patrick Venture99d1ba02019-02-21 15:11:24 -0800820 setDbusProperty(bus, getUserServiceName(), userPath, usersInterface,
821 userEnabledProperty, enabledState);
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530822 userInfo->userEnabled = enabledState;
823 try
824 {
825 writeUserData();
826 }
827 catch (const std::exception& e)
828 {
829 log<level::DEBUG>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000830 return ccUnspecifiedError;
Richard Marian Thomaiyar2fe92822019-03-02 22:07:03 +0530831 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530832 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000833 return ccSuccess;
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530834}
835
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000836Cc UserAccess::setUserPayloadAccess(const uint8_t chNum,
837 const uint8_t operation,
838 const uint8_t userId,
839 const PayloadAccess& payloadAccess)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000840{
841 constexpr uint8_t enable = 0x0;
842 constexpr uint8_t disable = 0x1;
843
844 if (!isValidChannel(chNum))
845 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000846 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000847 }
848 if (!isValidUserId(userId))
849 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000850 return ccParmOutOfRange;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000851 }
852 if (operation != enable && operation != disable)
853 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000854 return ccInvalidFieldRequest;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000855 }
856 // Check operation & payloadAccess if required.
857 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
858 userLock{*userMutex};
859 UserInfo* userInfo = getUserInfo(userId);
860
861 if (operation == enable)
862 {
863 userInfo->payloadAccess[chNum].stdPayloadEnables1 |=
864 payloadAccess.stdPayloadEnables1;
865
866 userInfo->payloadAccess[chNum].oemPayloadEnables1 |=
867 payloadAccess.oemPayloadEnables1;
868 }
869 else
870 {
871 userInfo->payloadAccess[chNum].stdPayloadEnables1 &=
872 ~(payloadAccess.stdPayloadEnables1);
873
874 userInfo->payloadAccess[chNum].oemPayloadEnables1 &=
875 ~(payloadAccess.oemPayloadEnables1);
876 }
877
878 try
879 {
880 writeUserData();
881 }
882 catch (const std::exception& e)
883 {
884 log<level::ERR>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000885 return ccUnspecifiedError;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000886 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000887 return ccSuccess;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +0000888}
889
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000890Cc UserAccess::setUserPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
891 const UserPrivAccess& privAccess,
892 const bool& otherPrivUpdates)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530893{
894 if (!isValidChannel(chNum))
895 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000896 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530897 }
898 if (!isValidUserId(userId))
899 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000900 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530901 }
902 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
903 userLock{*userMutex};
904 UserInfo* userInfo = getUserInfo(userId);
905 std::string userName;
906 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
907 ipmiMaxUserName);
908 if (userName.empty())
909 {
910 log<level::DEBUG>("User name not set / invalid");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000911 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530912 }
913 std::string priv = convertToSystemPrivilege(
914 static_cast<CommandPrivilege>(privAccess.privilege));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530915 uint8_t syncIndex = getUsrMgmtSyncIndex();
916 if (chNum == syncIndex &&
917 privAccess.privilege != userInfo->userPrivAccess[syncIndex].privilege)
918 {
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +0530919 sdbusplus::message::object_path tempUserPath(userObjBasePath);
920 tempUserPath /= userName;
921 std::string userPath(tempUserPath);
Patrick Venture99d1ba02019-02-21 15:11:24 -0800922 setDbusProperty(bus, getUserServiceName(), userPath, usersInterface,
923 userPrivProperty, priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530924 }
925 userInfo->userPrivAccess[chNum].privilege = privAccess.privilege;
926
927 if (otherPrivUpdates)
928 {
929 userInfo->userPrivAccess[chNum].ipmiEnabled = privAccess.ipmiEnabled;
930 userInfo->userPrivAccess[chNum].linkAuthEnabled =
931 privAccess.linkAuthEnabled;
932 userInfo->userPrivAccess[chNum].accessCallback =
933 privAccess.accessCallback;
934 }
935 try
936 {
937 writeUserData();
938 }
939 catch (const std::exception& e)
940 {
941 log<level::DEBUG>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000942 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530943 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000944 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530945}
946
947uint8_t UserAccess::getUserId(const std::string& userName)
948{
949 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
950 userLock{*userMutex};
951 checkAndReloadUserData();
952 // user index 0 is reserved, starts with 1
953 size_t usrIndex = 1;
954 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
955 {
956 std::string curName(
957 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
958 ipmiMaxUserName);
959 if (userName == curName)
960 {
961 break; // found the entry
962 }
963 }
964 if (usrIndex > ipmiMaxUsers)
965 {
966 log<level::DEBUG>("User not found",
967 entry("USER_NAME=%s", userName.c_str()));
968 return invalidUserId;
969 }
970
971 return usrIndex;
972}
973
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000974Cc UserAccess::getUserName(const uint8_t userId, std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530975{
976 if (!isValidUserId(userId))
977 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000978 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530979 }
980 UserInfo* userInfo = getUserInfo(userId);
981 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
982 ipmiMaxUserName);
NITIN SHARMAb541a5a2019-07-18 12:46:59 +0000983 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530984}
985
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +0530986bool UserAccess::isIpmiInAvailableGroupList()
987{
988 if (std::find(availableGroups.begin(), availableGroups.end(),
989 ipmiGrpName) != availableGroups.end())
990 {
991 return true;
992 }
993 if (availableGroups.empty())
994 {
995 // available groups shouldn't be empty, re-query
996 getSystemPrivAndGroups();
997 if (std::find(availableGroups.begin(), availableGroups.end(),
998 ipmiGrpName) != availableGroups.end())
999 {
1000 return true;
1001 }
1002 }
1003 return false;
1004}
1005
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001006Cc UserAccess::setUserName(const uint8_t userId, const std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301007{
1008 if (!isValidUserId(userId))
1009 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001010 return ccParmOutOfRange;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301011 }
1012
1013 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1014 userLock{*userMutex};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301015 std::string oldUser;
1016 getUserName(userId, oldUser);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301017
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001018 if (oldUser == userName)
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301019 {
1020 // requesting to set the same user name, return success.
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001021 return ccSuccess;
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301022 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001023
1024 bool validUser = isValidUserName(userName);
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +05301025 UserInfo* userInfo = getUserInfo(userId);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001026 if (userName.empty() && !oldUser.empty())
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301027 {
1028 // Delete existing user
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301029 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1030 tempUserPath /= oldUser;
1031 std::string userPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301032 try
1033 {
1034 auto method = bus.new_method_call(
1035 getUserServiceName().c_str(), userPath.c_str(),
1036 deleteUserInterface, deleteUserMethod);
1037 auto reply = bus.call(method);
1038 }
Patrick Williamsef1259b2021-09-02 09:12:33 -05001039 catch (const sdbusplus::exception::exception& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301040 {
1041 log<level::DEBUG>("Failed to excute method",
1042 entry("METHOD=%s", deleteUserMethod),
1043 entry("PATH=%s", userPath.c_str()));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001044 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301045 }
Richard Marian Thomaiyar02710bb2018-11-28 20:42:25 +05301046 deleteUserIndex(userId);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301047 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001048 else if (oldUser.empty() && !userName.empty() && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301049 {
1050 try
1051 {
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301052 if (!isIpmiInAvailableGroupList())
1053 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001054 return ccUnspecifiedError;
Richard Marian Thomaiyar489a4ed2020-01-17 11:48:40 +05301055 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301056 // Create new user
1057 auto method = bus.new_method_call(
1058 getUserServiceName().c_str(), userMgrObjBasePath,
1059 userMgrInterface, createUserMethod);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001060 method.append(userName.c_str(), availableGroups, "", false);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301061 auto reply = bus.call(method);
1062 }
Patrick Williamsef1259b2021-09-02 09:12:33 -05001063 catch (const sdbusplus::exception::exception& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301064 {
1065 log<level::DEBUG>("Failed to excute method",
1066 entry("METHOD=%s", createUserMethod),
1067 entry("PATH=%s", userMgrObjBasePath));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001068 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301069 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001070
1071 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1072 std::memcpy(userInfo->userName,
1073 static_cast<const void*>(userName.data()), userName.size());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301074 userInfo->userInSystem = true;
1075 }
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001076 else if (oldUser != userName && validUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301077 {
1078 try
1079 {
1080 // User rename
1081 auto method = bus.new_method_call(
1082 getUserServiceName().c_str(), userMgrObjBasePath,
1083 userMgrInterface, renameUserMethod);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001084 method.append(oldUser.c_str(), userName.c_str());
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301085 auto reply = bus.call(method);
1086 }
Patrick Williamsef1259b2021-09-02 09:12:33 -05001087 catch (const sdbusplus::exception::exception& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301088 {
1089 log<level::DEBUG>("Failed to excute method",
1090 entry("METHOD=%s", renameUserMethod),
1091 entry("PATH=%s", userMgrObjBasePath));
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001092 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301093 }
1094 std::fill(static_cast<uint8_t*>(userInfo->userName),
1095 static_cast<uint8_t*>(userInfo->userName) +
1096 sizeof(userInfo->userName),
1097 0);
jayaprakash Mutyala76363302020-02-14 23:50:38 +00001098
1099 std::memset(userInfo->userName, 0, sizeof(userInfo->userName));
1100 std::memcpy(userInfo->userName,
1101 static_cast<const void*>(userName.data()), userName.size());
1102
1103 ipmiRenameUserEntryPassword(oldUser, userName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301104 userInfo->userInSystem = true;
1105 }
1106 else if (!validUser)
1107 {
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001108 return ccInvalidFieldRequest;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301109 }
1110 try
1111 {
1112 writeUserData();
1113 }
1114 catch (const std::exception& e)
1115 {
1116 log<level::DEBUG>("Write user data failed");
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001117 return ccUnspecifiedError;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301118 }
NITIN SHARMAb541a5a2019-07-18 12:46:59 +00001119 return ccSuccess;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301120}
1121
1122static constexpr const char* jsonUserName = "user_name";
1123static constexpr const char* jsonPriv = "privilege";
1124static constexpr const char* jsonIpmiEnabled = "ipmi_enabled";
1125static constexpr const char* jsonLinkAuthEnabled = "link_auth_enabled";
1126static constexpr const char* jsonAccCallbk = "access_callback";
1127static constexpr const char* jsonUserEnabled = "user_enabled";
1128static constexpr const char* jsonUserInSys = "user_in_system";
1129static constexpr const char* jsonFixedUser = "fixed_user_name";
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001130static constexpr const char* payloadEnabledStr = "payload_enabled";
1131static constexpr const char* stdPayloadStr = "std_payload";
1132static constexpr const char* oemPayloadStr = "OEM_payload";
1133
1134/** @brief to construct a JSON object from the given payload access details.
1135 *
1136 * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. (input)
1137 * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. (input)
1138 *
1139 * @details Sample output JSON object format :
1140 * "payload_enabled":{
1141 * "OEM_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1142 * "OEM_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1143 * "OEM_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1144 * "OEM_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1145 * "OEM_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1146 * "OEM_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1147 * "OEM_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1148 * "OEM_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1149 * "std_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1150 * "std_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1151 * "std_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1152 * "std_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1153 * "std_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1154 * "std_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1155 * "std_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1156 * "std_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
1157 * }
1158 */
1159static const Json constructJsonPayloadEnables(
1160 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1161 stdPayload,
1162 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1163 oemPayload)
1164{
1165 Json jsonPayloadEnabled;
1166
1167 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1168 {
1169 std::ostringstream stdPayloadStream;
1170 std::ostringstream oemPayloadStream;
1171
1172 stdPayloadStream << stdPayloadStr << payloadNum;
1173 oemPayloadStream << oemPayloadStr << payloadNum;
1174
1175 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1176 stdPayloadStream.str(), stdPayload[payloadNum]));
1177
1178 jsonPayloadEnabled.push_back(Json::object_t::value_type(
1179 oemPayloadStream.str(), oemPayload[payloadNum]));
1180 }
1181 return jsonPayloadEnabled;
1182}
1183
1184void UserAccess::readPayloadAccessFromUserInfo(
1185 const UserInfo& userInfo,
1186 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& stdPayload,
1187 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& oemPayload)
1188{
1189 for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
1190 {
1191 for (auto chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1192 {
1193 stdPayload[payloadNum][chIndex] =
1194 userInfo.payloadAccess[chIndex].stdPayloadEnables1[payloadNum];
1195
1196 oemPayload[payloadNum][chIndex] =
1197 userInfo.payloadAccess[chIndex].oemPayloadEnables1[payloadNum];
1198 }
1199 }
1200}
1201
1202void UserAccess::updatePayloadAccessInUserInfo(
1203 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1204 stdPayload,
1205 const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
1206 oemPayload,
1207 UserInfo& userInfo)
1208{
1209 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1210 {
1211 // Ensure that reserved/unsupported payloads are marked to zero.
1212 userInfo.payloadAccess[chIndex].stdPayloadEnables1.reset();
1213 userInfo.payloadAccess[chIndex].oemPayloadEnables1.reset();
1214 userInfo.payloadAccess[chIndex].stdPayloadEnables2Reserved.reset();
1215 userInfo.payloadAccess[chIndex].oemPayloadEnables2Reserved.reset();
1216 // Update SOL status as it is the only supported payload currently.
1217 userInfo.payloadAccess[chIndex]
1218 .stdPayloadEnables1[static_cast<uint8_t>(ipmi::PayloadType::SOL)] =
1219 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)][chIndex];
1220 }
1221}
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301222
1223void UserAccess::readUserData()
1224{
1225 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1226 userLock{*userMutex};
1227
1228 std::ifstream iUsrData(ipmiUserDataFile, std::ios::in | std::ios::binary);
1229 if (!iUsrData.good())
1230 {
1231 log<level::ERR>("Error in reading IPMI user data file");
1232 throw std::ios_base::failure("Error opening IPMI user data file");
1233 }
1234
1235 Json jsonUsersTbl = Json::array();
1236 jsonUsersTbl = Json::parse(iUsrData, nullptr, false);
1237
1238 if (jsonUsersTbl.size() != ipmiMaxUsers)
1239 {
1240 log<level::ERR>(
1241 "Error in reading IPMI user data file - User count issues");
1242 throw std::runtime_error(
1243 "Corrupted IPMI user data file - invalid user count");
1244 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001245
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301246 // user index 0 is reserved, starts with 1
1247 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1248 {
1249 Json userInfo = jsonUsersTbl[usrIndex - 1]; // json array starts with 0.
1250 if (userInfo.is_null())
1251 {
1252 log<level::ERR>("Error in reading IPMI user data file - "
1253 "user info corrupted");
1254 throw std::runtime_error(
1255 "Corrupted IPMI user data file - invalid user info");
1256 }
1257 std::string userName = userInfo[jsonUserName].get<std::string>();
1258 std::strncpy(reinterpret_cast<char*>(usersTbl.user[usrIndex].userName),
1259 userName.c_str(), ipmiMaxUserName);
1260
1261 std::vector<std::string> privilege =
1262 userInfo[jsonPriv].get<std::vector<std::string>>();
1263 std::vector<bool> ipmiEnabled =
1264 userInfo[jsonIpmiEnabled].get<std::vector<bool>>();
1265 std::vector<bool> linkAuthEnabled =
1266 userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
1267 std::vector<bool> accessCallback =
1268 userInfo[jsonAccCallbk].get<std::vector<bool>>();
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001269
1270 // Payload Enables Processing.
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001271 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1272 stdPayload = {};
1273 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1274 oemPayload = {};
1275 try
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001276 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001277 const auto jsonPayloadEnabled = userInfo.at(payloadEnabledStr);
1278 for (auto payloadNum = 0; payloadNum < payloadsPerByte;
1279 payloadNum++)
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001280 {
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001281 std::ostringstream stdPayloadStream;
1282 std::ostringstream oemPayloadStream;
1283
1284 stdPayloadStream << stdPayloadStr << payloadNum;
1285 oemPayloadStream << oemPayloadStr << payloadNum;
1286
1287 stdPayload[payloadNum] =
1288 jsonPayloadEnabled[stdPayloadStream.str()]
1289 .get<std::array<bool, ipmiMaxChannels>>();
1290 oemPayload[payloadNum] =
1291 jsonPayloadEnabled[oemPayloadStream.str()]
1292 .get<std::array<bool, ipmiMaxChannels>>();
1293
1294 if (stdPayload[payloadNum].size() != ipmiMaxChannels ||
1295 oemPayload[payloadNum].size() != ipmiMaxChannels)
1296 {
1297 log<level::ERR>("Error in reading IPMI user data file - "
1298 "payload properties corrupted");
1299 throw std::runtime_error(
1300 "Corrupted IPMI user data file - payload properties");
1301 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001302 }
1303 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001304 catch (const Json::out_of_range& e)
Saravanan Palanisamyc86045c2019-07-26 22:52:40 +00001305 {
1306 // Key not found in 'userInfo'; possibly an old JSON file. Use
1307 // default values for all payloads, and SOL payload default is true.
1308 stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)].fill(true);
1309 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001310
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301311 if (privilege.size() != ipmiMaxChannels ||
1312 ipmiEnabled.size() != ipmiMaxChannels ||
1313 linkAuthEnabled.size() != ipmiMaxChannels ||
1314 accessCallback.size() != ipmiMaxChannels)
1315 {
1316 log<level::ERR>("Error in reading IPMI user data file - "
1317 "properties corrupted");
1318 throw std::runtime_error(
1319 "Corrupted IPMI user data file - properties");
1320 }
1321 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1322 {
1323 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege =
1324 static_cast<uint8_t>(
1325 convertToIPMIPrivilege(privilege[chIndex]));
1326 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled =
1327 ipmiEnabled[chIndex];
1328 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled =
1329 linkAuthEnabled[chIndex];
1330 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
1331 accessCallback[chIndex];
1332 }
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001333 updatePayloadAccessInUserInfo(stdPayload, oemPayload,
1334 usersTbl.user[usrIndex]);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301335 usersTbl.user[usrIndex].userEnabled =
1336 userInfo[jsonUserEnabled].get<bool>();
1337 usersTbl.user[usrIndex].userInSystem =
1338 userInfo[jsonUserInSys].get<bool>();
1339 usersTbl.user[usrIndex].fixedUserName =
1340 userInfo[jsonFixedUser].get<bool>();
1341 }
1342
1343 log<level::DEBUG>("User data read from IPMI data file");
1344 iUsrData.close();
1345 // Update the timestamp
1346 fileLastUpdatedTime = getUpdatedFileTime();
1347 return;
1348}
1349
1350void UserAccess::writeUserData()
1351{
1352 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1353 userLock{*userMutex};
1354
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301355 Json jsonUsersTbl = Json::array();
1356 // user index 0 is reserved, starts with 1
1357 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1358 {
1359 Json jsonUserInfo;
1360 jsonUserInfo[jsonUserName] = std::string(
1361 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
1362 ipmiMaxUserName);
1363 std::vector<std::string> privilege(ipmiMaxChannels);
1364 std::vector<bool> ipmiEnabled(ipmiMaxChannels);
1365 std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
1366 std::vector<bool> accessCallback(ipmiMaxChannels);
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001367
1368 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1369 stdPayload;
1370 std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
1371 oemPayload;
1372
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301373 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1374 {
1375 privilege[chIndex] =
1376 convertToSystemPrivilege(static_cast<CommandPrivilege>(
1377 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege));
1378 ipmiEnabled[chIndex] =
1379 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled;
1380 linkAuthEnabled[chIndex] =
1381 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled;
1382 accessCallback[chIndex] =
1383 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback;
1384 }
1385 jsonUserInfo[jsonPriv] = privilege;
1386 jsonUserInfo[jsonIpmiEnabled] = ipmiEnabled;
1387 jsonUserInfo[jsonLinkAuthEnabled] = linkAuthEnabled;
1388 jsonUserInfo[jsonAccCallbk] = accessCallback;
1389 jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
1390 jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
1391 jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
Saravanan Palanisamy77381f12019-05-15 22:33:17 +00001392
1393 readPayloadAccessFromUserInfo(usersTbl.user[usrIndex], stdPayload,
1394 oemPayload);
1395 Json jsonPayloadEnabledInfo =
1396 constructJsonPayloadEnables(stdPayload, oemPayload);
1397 jsonUserInfo[payloadEnabledStr] = jsonPayloadEnabledInfo;
1398
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301399 jsonUsersTbl.push_back(jsonUserInfo);
1400 }
1401
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301402 static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
1403 int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
1404 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1405 if (fd < 0)
1406 {
1407 log<level::ERR>("Error in creating temporary IPMI user data file");
1408 throw std::ios_base::failure(
1409 "Error in creating temporary IPMI user data file");
1410 }
1411 const auto& writeStr = jsonUsersTbl.dump();
1412 if (write(fd, writeStr.c_str(), writeStr.size()) !=
1413 static_cast<ssize_t>(writeStr.size()))
1414 {
1415 close(fd);
1416 log<level::ERR>("Error in writing temporary IPMI user data file");
1417 throw std::ios_base::failure(
1418 "Error in writing temporary IPMI user data file");
1419 }
1420 close(fd);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301421
1422 if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
1423 {
1424 log<level::ERR>("Error in renaming temporary IPMI user data file");
1425 throw std::runtime_error("Error in renaming IPMI user data file");
1426 }
1427 // Update the timestamp
1428 fileLastUpdatedTime = getUpdatedFileTime();
1429 return;
1430}
1431
1432bool UserAccess::addUserEntry(const std::string& userName,
1433 const std::string& sysPriv, const bool& enabled)
1434{
1435 UsersTbl* userData = getUsersTblPtr();
1436 size_t freeIndex = 0xFF;
1437 // user index 0 is reserved, starts with 1
1438 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1439 {
1440 std::string curName(
1441 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
1442 ipmiMaxUserName);
1443 if (userName == curName)
1444 {
1445 log<level::DEBUG>("User name exists",
1446 entry("USER_NAME=%s", userName.c_str()));
1447 return false; // user name exists.
1448 }
1449
1450 if ((!userData->user[usrIndex].userInSystem) &&
1451 (userData->user[usrIndex].userName[0] == '\0') &&
1452 (freeIndex == 0xFF))
1453 {
1454 freeIndex = usrIndex;
1455 }
1456 }
1457 if (freeIndex == 0xFF)
1458 {
1459 log<level::ERR>("No empty slots found");
1460 return false;
1461 }
1462 std::strncpy(reinterpret_cast<char*>(userData->user[freeIndex].userName),
1463 userName.c_str(), ipmiMaxUserName);
1464 uint8_t priv =
1465 static_cast<uint8_t>(UserAccess::convertToIPMIPrivilege(sysPriv)) &
1466 privMask;
1467 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1468 {
1469 userData->user[freeIndex].userPrivAccess[chIndex].privilege = priv;
1470 userData->user[freeIndex].userPrivAccess[chIndex].ipmiEnabled = true;
1471 userData->user[freeIndex].userPrivAccess[chIndex].linkAuthEnabled =
1472 true;
1473 userData->user[freeIndex].userPrivAccess[chIndex].accessCallback = true;
1474 }
1475 userData->user[freeIndex].userInSystem = true;
1476 userData->user[freeIndex].userEnabled = enabled;
1477
1478 return true;
1479}
1480
1481void UserAccess::deleteUserIndex(const size_t& usrIdx)
1482{
1483 UsersTbl* userData = getUsersTblPtr();
1484
1485 std::string userName(
1486 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1487 ipmiMaxUserName);
1488 ipmiClearUserEntryPassword(userName);
1489 std::fill(static_cast<uint8_t*>(userData->user[usrIdx].userName),
1490 static_cast<uint8_t*>(userData->user[usrIdx].userName) +
1491 sizeof(userData->user[usrIdx].userName),
1492 0);
1493 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1494 {
1495 userData->user[usrIdx].userPrivAccess[chIndex].privilege = privNoAccess;
1496 userData->user[usrIdx].userPrivAccess[chIndex].ipmiEnabled = false;
1497 userData->user[usrIdx].userPrivAccess[chIndex].linkAuthEnabled = false;
1498 userData->user[usrIdx].userPrivAccess[chIndex].accessCallback = false;
1499 }
1500 userData->user[usrIdx].userInSystem = false;
1501 userData->user[usrIdx].userEnabled = false;
1502 return;
1503}
1504
1505void UserAccess::checkAndReloadUserData()
1506{
1507 std::time_t updateTime = getUpdatedFileTime();
1508 if (updateTime != fileLastUpdatedTime || updateTime == -EIO)
1509 {
1510 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1511 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1512 readUserData();
1513 }
1514 return;
1515}
1516
1517UsersTbl* UserAccess::getUsersTblPtr()
1518{
1519 // reload data before using it.
1520 checkAndReloadUserData();
1521 return &usersTbl;
1522}
1523
1524void UserAccess::getSystemPrivAndGroups()
1525{
1526 std::map<std::string, PrivAndGroupType> properties;
1527 try
1528 {
1529 auto method = bus.new_method_call(
1530 getUserServiceName().c_str(), userMgrObjBasePath,
1531 dBusPropertiesInterface, getAllPropertiesMethod);
1532 method.append(userMgrInterface);
1533
1534 auto reply = bus.call(method);
1535 reply.read(properties);
1536 }
Patrick Williamsef1259b2021-09-02 09:12:33 -05001537 catch (const sdbusplus::exception::exception& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301538 {
1539 log<level::DEBUG>("Failed to excute method",
1540 entry("METHOD=%s", getAllPropertiesMethod),
1541 entry("PATH=%s", userMgrObjBasePath));
1542 return;
1543 }
1544 for (const auto& t : properties)
1545 {
1546 auto key = t.first;
1547 if (key == allPrivProperty)
1548 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001549 availablePrivileges = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301550 }
1551 else if (key == allGrpProperty)
1552 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001553 availableGroups = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301554 }
1555 }
1556 // TODO: Implement Supported Privilege & Groups verification logic
1557 return;
1558}
1559
1560std::time_t UserAccess::getUpdatedFileTime()
1561{
1562 struct stat fileStat;
1563 if (stat(ipmiUserDataFile, &fileStat) != 0)
1564 {
1565 log<level::DEBUG>("Error in getting last updated time stamp");
1566 return -EIO;
1567 }
1568 return fileStat.st_mtime;
1569}
1570
1571void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
1572 std::vector<std::string>& usrGrps,
1573 std::string& usrPriv, bool& usrEnabled)
1574{
1575 for (const auto& t : properties)
1576 {
1577 std::string key = t.first;
1578 if (key == userPrivProperty)
1579 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001580 usrPriv = std::get<std::string>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301581 }
1582 else if (key == userGrpProperty)
1583 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001584 usrGrps = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301585 }
1586 else if (key == userEnabledProperty)
1587 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001588 usrEnabled = std::get<bool>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301589 }
1590 }
1591 return;
1592}
1593
1594int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
1595 std::vector<std::string>& usrGrps,
1596 std::string& usrPriv, bool& usrEnabled)
1597{
1598 auto usrObj = userObjs.find(usersInterface);
1599 if (usrObj != userObjs.end())
1600 {
1601 getUserProperties(usrObj->second, usrGrps, usrPriv, usrEnabled);
1602 return 0;
1603 }
1604 return -EIO;
1605}
1606
arun-pmbbe728c2020-01-10 15:18:04 +05301607void UserAccess::cacheUserDataFile()
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301608{
1609 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1610 userLock{*userMutex};
1611 try
1612 {
1613 readUserData();
1614 }
1615 catch (const std::ios_base::failure& e)
1616 { // File is empty, create it for the first time
1617 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1618 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1619 // user index 0 is reserved, starts with 1
1620 for (size_t userIndex = 1; userIndex <= ipmiMaxUsers; ++userIndex)
1621 {
1622 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1623 {
1624 usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
1625 privNoAccess;
Saravanan Palanisamy92d81192019-08-07 18:00:04 +00001626 usersTbl.user[userIndex]
1627 .payloadAccess[chIndex]
1628 .stdPayloadEnables1[static_cast<uint8_t>(
1629 ipmi::PayloadType::SOL)] = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301630 }
1631 }
1632 writeUserData();
1633 }
arun-pmbbe728c2020-01-10 15:18:04 +05301634 sigHndlrLock = boost::interprocess::file_lock(ipmiUserDataFile);
1635 // Register it for single object and single process either netipimd /
1636 // host-ipmid
1637 if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
1638 {
1639 log<level::DEBUG>("Registering signal handler");
1640 userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
1641 bus,
1642 sdbusplus::bus::match::rules::type::signal() +
1643 sdbusplus::bus::match::rules::interface(dBusObjManager) +
1644 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
1645 [&](sdbusplus::message::message& msg) {
1646 userUpdatedSignalHandler(*this, msg);
1647 });
1648 userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
1649 bus,
1650 sdbusplus::bus::match::rules::type::signal() +
1651 sdbusplus::bus::match::rules::interface(userMgrInterface) +
1652 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
1653 [&](sdbusplus::message::message& msg) {
1654 userUpdatedSignalHandler(*this, msg);
1655 });
1656 userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
1657 bus,
1658 sdbusplus::bus::match::rules::type::signal() +
1659 sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
1660 sdbusplus::bus::match::rules::interface(
1661 dBusPropertiesInterface) +
1662 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
1663 sdbusplus::bus::match::rules::argN(0, usersInterface),
1664 [&](sdbusplus::message::message& msg) {
1665 userUpdatedSignalHandler(*this, msg);
1666 });
1667 signalHndlrObject = true;
1668 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301669 std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
1670 try
1671 {
1672 auto method = bus.new_method_call(getUserServiceName().c_str(),
1673 userMgrObjBasePath, dBusObjManager,
1674 getManagedObjectsMethod);
1675 auto reply = bus.call(method);
1676 reply.read(managedObjs);
1677 }
Patrick Williamsef1259b2021-09-02 09:12:33 -05001678 catch (const sdbusplus::exception::exception& e)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301679 {
1680 log<level::DEBUG>("Failed to excute method",
1681 entry("METHOD=%s", getSubTreeMethod),
1682 entry("PATH=%s", userMgrObjBasePath));
1683 return;
1684 }
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301685 bool updateRequired = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301686 UsersTbl* userData = &usersTbl;
1687 // user index 0 is reserved, starts with 1
1688 for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
1689 {
1690 if ((userData->user[usrIdx].userInSystem) &&
1691 (userData->user[usrIdx].userName[0] != '\0'))
1692 {
1693 std::vector<std::string> usrGrps;
1694 std::string usrPriv;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301695
1696 std::string userName(
1697 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1698 ipmiMaxUserName);
P Dheeraj Srujan Kumar0ce6a572021-12-13 09:01:55 +05301699 sdbusplus::message::object_path tempUserPath(userObjBasePath);
1700 tempUserPath /= userName;
1701 std::string usersPath(tempUserPath);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301702
1703 auto usrObj = managedObjs.find(usersPath);
1704 if (usrObj != managedObjs.end())
1705 {
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001706 bool usrEnabled = false;
Patrick Venture3a697ad2019-08-19 11:12:05 -07001707
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301708 // User exist. Lets check and update other fileds
1709 getUserObjProperties(usrObj->second, usrGrps, usrPriv,
1710 usrEnabled);
1711 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
1712 usrGrps.end())
1713 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301714 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301715 // Group "ipmi" is removed so lets remove user in IPMI
1716 deleteUserIndex(usrIdx);
1717 }
1718 else
1719 {
1720 // Group "ipmi" is present so lets update other properties
1721 // in IPMI
1722 uint8_t priv =
1723 UserAccess::convertToIPMIPrivilege(usrPriv) & privMask;
1724 // Update all channels priv, only if it is not equivalent to
1725 // getUsrMgmtSyncIndex()
1726 if (userData->user[usrIdx]
1727 .userPrivAccess[getUsrMgmtSyncIndex()]
1728 .privilege != priv)
1729 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301730 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301731 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
1732 ++chIndex)
1733 {
1734 userData->user[usrIdx]
1735 .userPrivAccess[chIndex]
1736 .privilege = priv;
1737 }
1738 }
1739 if (userData->user[usrIdx].userEnabled != usrEnabled)
1740 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301741 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301742 userData->user[usrIdx].userEnabled = usrEnabled;
1743 }
1744 }
1745
1746 // We are done with this obj. lets delete from MAP
1747 managedObjs.erase(usrObj);
1748 }
1749 else
1750 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301751 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301752 deleteUserIndex(usrIdx);
1753 }
1754 }
1755 }
1756
1757 // Walk through remnaining managedObj users list
1758 // Add them to ipmi data base
1759 for (const auto& usrObj : managedObjs)
1760 {
1761 std::vector<std::string> usrGrps;
1762 std::string usrPriv, userName;
Chen,Yugang0e862fa2019-09-06 11:03:05 +08001763 bool usrEnabled = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301764 std::string usrObjPath = std::string(usrObj.first);
1765 if (getUserNameFromPath(usrObj.first.str, userName) != 0)
1766 {
1767 log<level::ERR>("Error in user object path");
1768 continue;
1769 }
1770 getUserObjProperties(usrObj.second, usrGrps, usrPriv, usrEnabled);
1771 // Add 'ipmi' group users
1772 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
1773 usrGrps.end())
1774 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301775 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301776 // CREATE NEW USER
1777 if (true != addUserEntry(userName, usrPriv, usrEnabled))
1778 {
1779 break;
1780 }
1781 }
1782 }
1783
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301784 if (updateRequired)
1785 {
1786 // All userData slots update done. Lets write the data
1787 writeUserData();
1788 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301789
1790 return;
1791}
1792} // namespace ipmi