blob: 9b40f6cc84c2e48f5dc570d3b45980a74efcdf98 [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"
19
Suryakanth Sekar90b00c72019-01-16 10:37:57 +053020#include <security/pam_appl.h>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053021#include <sys/stat.h>
22#include <unistd.h>
23
24#include <boost/interprocess/sync/named_recursive_mutex.hpp>
25#include <boost/interprocess/sync/scoped_lock.hpp>
26#include <cerrno>
27#include <fstream>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053028#include <nlohmann/json.hpp>
29#include <phosphor-logging/elog-errors.hpp>
30#include <phosphor-logging/log.hpp>
31#include <regex>
32#include <sdbusplus/bus/match.hpp>
33#include <sdbusplus/server/object.hpp>
Vernon Mauery16b86932019-05-01 08:36:11 -070034#include <variant>
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053035#include <xyz/openbmc_project/Common/error.hpp>
36#include <xyz/openbmc_project/User/Common/error.hpp>
37
38namespace ipmi
39{
40
41// TODO: Move D-Bus & Object Manager related stuff, to common files
42// D-Bus property related
43static constexpr const char* dBusPropertiesInterface =
44 "org.freedesktop.DBus.Properties";
45static constexpr const char* getAllPropertiesMethod = "GetAll";
46static constexpr const char* propertiesChangedSignal = "PropertiesChanged";
47static constexpr const char* setPropertiesMethod = "Set";
48
49// Object Manager related
50static constexpr const char* dBusObjManager =
51 "org.freedesktop.DBus.ObjectManager";
52static constexpr const char* getManagedObjectsMethod = "GetManagedObjects";
53// Object Manager signals
54static constexpr const char* intfAddedSignal = "InterfacesAdded";
55static constexpr const char* intfRemovedSignal = "InterfacesRemoved";
56
57// Object Mapper related
58static constexpr const char* objMapperService =
59 "xyz.openbmc_project.ObjectMapper";
60static constexpr const char* objMapperPath =
61 "/xyz/openbmc_project/object_mapper";
62static constexpr const char* objMapperInterface =
63 "xyz.openbmc_project.ObjectMapper";
64static constexpr const char* getSubTreeMethod = "GetSubTree";
65static constexpr const char* getObjectMethod = "GetObject";
66
67static constexpr const char* ipmiUserMutex = "ipmi_usr_mutex";
68static constexpr const char* ipmiMutexCleanupLockFile =
69 "/var/lib/ipmi/ipmi_usr_mutex_cleanup";
70static constexpr const char* ipmiUserDataFile = "/var/lib/ipmi/ipmi_user.json";
71static constexpr const char* ipmiGrpName = "ipmi";
72static constexpr size_t privNoAccess = 0xF;
73static constexpr size_t privMask = 0xF;
74
75// User manager related
76static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user";
77static constexpr const char* userObjBasePath = "/xyz/openbmc_project/user";
78static constexpr const char* userMgrInterface =
79 "xyz.openbmc_project.User.Manager";
80static constexpr const char* usersInterface =
81 "xyz.openbmc_project.User.Attributes";
82static constexpr const char* deleteUserInterface =
83 "xyz.openbmc_project.Object.Delete";
84
85static constexpr const char* createUserMethod = "CreateUser";
86static constexpr const char* deleteUserMethod = "Delete";
87static constexpr const char* renameUserMethod = "RenameUser";
88// User manager signal memebers
89static constexpr const char* userRenamedSignal = "UserRenamed";
90// Mgr interface properties
91static constexpr const char* allPrivProperty = "AllPrivileges";
92static constexpr const char* allGrpProperty = "AllGroups";
93// User interface properties
94static constexpr const char* userPrivProperty = "UserPrivilege";
95static constexpr const char* userGrpProperty = "UserGroups";
96static constexpr const char* userEnabledProperty = "UserEnabled";
97
98static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = {
99 "priv-reserved", // PRIVILEGE_RESERVED - 0
100 "priv-callback", // PRIVILEGE_CALLBACK - 1
101 "priv-user", // PRIVILEGE_USER - 2
102 "priv-operator", // PRIVILEGE_OPERATOR - 3
103 "priv-admin", // PRIVILEGE_ADMIN - 4
104 "priv-custom" // PRIVILEGE_OEM - 5
105};
106
107using namespace phosphor::logging;
108using Json = nlohmann::json;
109
Vernon Mauery16b86932019-05-01 08:36:11 -0700110using PrivAndGroupType = std::variant<std::string, std::vector<std::string>>;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530111
112using NoResource =
113 sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource;
114
115using InternalFailure =
116 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
117
Lei YU4b0ddb62019-01-25 16:43:50 +0800118std::unique_ptr<sdbusplus::bus::match_t> userUpdatedSignal
119 __attribute__((init_priority(101)));
120std::unique_ptr<sdbusplus::bus::match_t> userMgrRenamedSignal
121 __attribute__((init_priority(101)));
122std::unique_ptr<sdbusplus::bus::match_t> userPropertiesSignal
123 __attribute__((init_priority(101)));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530124
125// TODO: Below code can be removed once it is moved to common layer libmiscutil
126std::string getUserService(sdbusplus::bus::bus& bus, const std::string& intf,
127 const std::string& path)
128{
129 auto mapperCall = bus.new_method_call(objMapperService, objMapperPath,
130 objMapperInterface, getObjectMethod);
131
132 mapperCall.append(path);
133 mapperCall.append(std::vector<std::string>({intf}));
134
135 auto mapperResponseMsg = bus.call(mapperCall);
136
137 std::map<std::string, std::vector<std::string>> mapperResponse;
138 mapperResponseMsg.read(mapperResponse);
139
140 if (mapperResponse.begin() == mapperResponse.end())
141 {
142 throw sdbusplus::exception::SdBusError(
143 -EIO, "ERROR in reading the mapper response");
144 }
145
146 return mapperResponse.begin()->first;
147}
148
149void setDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
150 const std::string& objPath, const std::string& interface,
151 const std::string& property,
152 const DbusUserPropVariant& value)
153{
154 try
155 {
156 auto method =
157 bus.new_method_call(service.c_str(), objPath.c_str(),
158 dBusPropertiesInterface, setPropertiesMethod);
159 method.append(interface, property, value);
160 bus.call(method);
161 }
162 catch (const sdbusplus::exception::SdBusError& e)
163 {
164 log<level::ERR>("Failed to set property",
165 entry("PROPERTY=%s", property.c_str()),
166 entry("PATH=%s", objPath.c_str()),
167 entry("INTERFACE=%s", interface.c_str()));
168 throw;
169 }
170}
171
172static std::string getUserServiceName()
173{
174 static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
175 static std::string userMgmtService;
176 if (userMgmtService.empty())
177 {
178 try
179 {
180 userMgmtService =
181 ipmi::getUserService(bus, userMgrInterface, userMgrObjBasePath);
182 }
183 catch (const sdbusplus::exception::SdBusError& e)
184 {
185 userMgmtService.clear();
186 }
187 }
188 return userMgmtService;
189}
190
191UserAccess& getUserAccessObject()
192{
193 static UserAccess userAccess;
194 return userAccess;
195}
196
197int getUserNameFromPath(const std::string& path, std::string& userName)
198{
199 static size_t pos = strlen(userObjBasePath) + 1;
200 if (path.find(userObjBasePath) == std::string::npos)
201 {
202 return -EINVAL;
203 }
204 userName.assign(path, pos, path.size());
205 return 0;
206}
207
208void userUpdateHelper(UserAccess& usrAccess, const UserUpdateEvent& userEvent,
209 const std::string& userName, const std::string& priv,
210 const bool& enabled, const std::string& newUserName)
211{
212 UsersTbl* userData = usrAccess.getUsersTblPtr();
213 if (userEvent == UserUpdateEvent::userCreated)
214 {
215 if (usrAccess.addUserEntry(userName, priv, enabled) == false)
216 {
217 return;
218 }
219 }
220 else
221 {
222 // user index 0 is reserved, starts with 1
223 size_t usrIndex = 1;
224 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
225 {
226 std::string curName(
227 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
228 ipmiMaxUserName);
229 if (userName == curName)
230 {
231 break; // found the entry
232 }
233 }
234 if (usrIndex > ipmiMaxUsers)
235 {
236 log<level::DEBUG>("User not found for signal",
237 entry("USER_NAME=%s", userName.c_str()),
238 entry("USER_EVENT=%d", userEvent));
239 return;
240 }
241 switch (userEvent)
242 {
243 case UserUpdateEvent::userDeleted:
244 {
245 usrAccess.deleteUserIndex(usrIndex);
246 break;
247 }
248 case UserUpdateEvent::userPrivUpdated:
249 {
250 uint8_t userPriv =
251 static_cast<uint8_t>(
252 UserAccess::convertToIPMIPrivilege(priv)) &
253 privMask;
254 // Update all channels privileges, only if it is not equivalent
255 // to getUsrMgmtSyncIndex()
256 if (userData->user[usrIndex]
257 .userPrivAccess[UserAccess::getUsrMgmtSyncIndex()]
258 .privilege != userPriv)
259 {
260 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
261 ++chIndex)
262 {
263 userData->user[usrIndex]
264 .userPrivAccess[chIndex]
265 .privilege = userPriv;
266 }
267 }
268 break;
269 }
270 case UserUpdateEvent::userRenamed:
271 {
272 std::fill(
273 static_cast<uint8_t*>(userData->user[usrIndex].userName),
274 static_cast<uint8_t*>(userData->user[usrIndex].userName) +
275 sizeof(userData->user[usrIndex].userName),
276 0);
277 std::strncpy(
278 reinterpret_cast<char*>(userData->user[usrIndex].userName),
279 newUserName.c_str(), ipmiMaxUserName);
280 ipmiRenameUserEntryPassword(userName, newUserName);
281 break;
282 }
283 case UserUpdateEvent::userStateUpdated:
284 {
285 userData->user[usrIndex].userEnabled = enabled;
286 break;
287 }
288 default:
289 {
290 log<level::ERR>("Unhandled user event",
291 entry("USER_EVENT=%d", userEvent));
292 return;
293 }
294 }
295 }
296 usrAccess.writeUserData();
297 log<level::DEBUG>("User event handled successfully",
298 entry("USER_NAME=%s", userName.c_str()),
299 entry("USER_EVENT=%d", userEvent));
300
301 return;
302}
303
304void userUpdatedSignalHandler(UserAccess& usrAccess,
305 sdbusplus::message::message& msg)
306{
307 static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
308 std::string signal = msg.get_member();
309 std::string userName, update, priv, newUserName;
310 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 }
412 catch (const sdbusplus::exception::SdBusError& e)
413 {
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
473 initUserDataFile();
474 getSystemPrivAndGroups();
475 sigHndlrLock = boost::interprocess::file_lock(ipmiUserDataFile);
476 // Register it for single object and single process either netipimd /
477 // host-ipmid
478 if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
479 {
480 log<level::DEBUG>("Registering signal handler");
481 userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
482 bus,
483 sdbusplus::bus::match::rules::type::signal() +
484 sdbusplus::bus::match::rules::interface(dBusObjManager) +
485 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
486 [&](sdbusplus::message::message& msg) {
487 userUpdatedSignalHandler(*this, msg);
488 });
489 userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
490 bus,
491 sdbusplus::bus::match::rules::type::signal() +
492 sdbusplus::bus::match::rules::interface(userMgrInterface) +
493 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
494 [&](sdbusplus::message::message& msg) {
495 userUpdatedSignalHandler(*this, msg);
496 });
497 userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
498 bus,
499 sdbusplus::bus::match::rules::type::signal() +
500 sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
501 sdbusplus::bus::match::rules::interface(
502 dBusPropertiesInterface) +
503 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
504 sdbusplus::bus::match::rules::argN(0, usersInterface),
505 [&](sdbusplus::message::message& msg) {
506 userUpdatedSignalHandler(*this, msg);
507 });
508 signalHndlrObject = true;
509 }
510}
511
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530512UserInfo* UserAccess::getUserInfo(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530513{
514 checkAndReloadUserData();
515 return &usersTbl.user[userId];
516}
517
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530518void UserAccess::setUserInfo(const uint8_t userId, UserInfo* userInfo)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530519{
520 checkAndReloadUserData();
521 std::copy(reinterpret_cast<uint8_t*>(userInfo),
522 reinterpret_cast<uint8_t*>(userInfo) + sizeof(*userInfo),
523 reinterpret_cast<uint8_t*>(&usersTbl.user[userId]));
524 writeUserData();
525}
526
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530527bool UserAccess::isValidChannel(const uint8_t chNum)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530528{
529 return (chNum < ipmiMaxChannels);
530}
531
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530532bool UserAccess::isValidUserId(const uint8_t userId)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530533{
534 return ((userId <= ipmiMaxUsers) && (userId != reservedUserId));
535}
536
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530537bool UserAccess::isValidPrivilege(const uint8_t priv)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530538{
539 return ((priv >= PRIVILEGE_CALLBACK && priv <= PRIVILEGE_OEM) ||
540 priv == privNoAccess);
541}
542
543uint8_t UserAccess::getUsrMgmtSyncIndex()
544{
545 // TODO: Need to get LAN1 channel number dynamically,
546 // which has to be in sync with system user privilege
547 // level(Phosphor-user-manager). Note: For time being chanLan1 is marked as
548 // sync index to the user-manager privilege..
549 return static_cast<uint8_t>(EChannelID::chanLan1);
550}
551
552CommandPrivilege UserAccess::convertToIPMIPrivilege(const std::string& value)
553{
554 auto iter = std::find(ipmiPrivIndex.begin(), ipmiPrivIndex.end(), value);
555 if (iter == ipmiPrivIndex.end())
556 {
557 if (value == "")
558 {
559 return static_cast<CommandPrivilege>(privNoAccess);
560 }
561 log<level::ERR>("Error in converting to IPMI privilege",
562 entry("PRIV=%s", value.c_str()));
563 throw std::out_of_range("Out of range - convertToIPMIPrivilege");
564 }
565 else
566 {
567 return static_cast<CommandPrivilege>(
568 std::distance(ipmiPrivIndex.begin(), iter));
569 }
570}
571
572std::string UserAccess::convertToSystemPrivilege(const CommandPrivilege& value)
573{
574 if (value == static_cast<CommandPrivilege>(privNoAccess))
575 {
576 return "";
577 }
578 try
579 {
580 return ipmiPrivIndex.at(value);
581 }
582 catch (const std::out_of_range& e)
583 {
584 log<level::ERR>("Error in converting to system privilege",
585 entry("PRIV=%d", static_cast<uint8_t>(value)));
586 throw std::out_of_range("Out of range - convertToSystemPrivilege");
587 }
588}
589
590bool UserAccess::isValidUserName(const char* userNameInChar)
591{
592 if (!userNameInChar)
593 {
594 log<level::ERR>("null ptr");
595 return false;
596 }
597 std::string userName(userNameInChar, 0, ipmiMaxUserName);
598 if (!std::regex_match(userName.c_str(),
599 std::regex("[a-zA-z_][a-zA-Z_0-9]*")))
600 {
601 log<level::ERR>("Unsupported characters in user name");
602 return false;
603 }
604 if (userName == "root")
605 {
606 log<level::ERR>("Invalid user name - root");
607 return false;
608 }
609 std::map<DbusUserObjPath, DbusUserObjValue> properties;
610 try
611 {
612 auto method = bus.new_method_call(getUserServiceName().c_str(),
613 userMgrObjBasePath, dBusObjManager,
614 getManagedObjectsMethod);
615 auto reply = bus.call(method);
616 reply.read(properties);
617 }
618 catch (const sdbusplus::exception::SdBusError& e)
619 {
620 log<level::ERR>("Failed to excute method",
621 entry("METHOD=%s", getSubTreeMethod),
622 entry("PATH=%s", userMgrObjBasePath));
623 return false;
624 }
625
626 std::string usersPath = std::string(userObjBasePath) + "/" + userName;
627 if (properties.find(usersPath) != properties.end())
628 {
629 log<level::DEBUG>("User name already exists",
630 entry("USER_NAME=%s", userName.c_str()));
631 return false;
632 }
633
634 return true;
635}
636
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530637/** @brief Information exchanged by pam module and application.
638 *
639 * @param[in] numMsg - length of the array of pointers,msg.
640 *
641 * @param[in] msg - pointer to an array of pointers to pam_message structure
642 *
643 * @param[out] resp - struct pam response array
644 *
645 * @param[in] appdataPtr - member of pam_conv structure
646 *
647 * @return the response in pam response structure.
648 */
649
650static int pamFunctionConversation(int numMsg, const struct pam_message** msg,
651 struct pam_response** resp, void* appdataPtr)
652{
653 if (appdataPtr == nullptr)
654 {
655 return PAM_AUTH_ERR;
656 }
657 size_t passSize = std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1;
658 char* pass = reinterpret_cast<char*>(malloc(passSize));
659 std::strncpy(pass, reinterpret_cast<char*>(appdataPtr), passSize);
660
661 *resp = reinterpret_cast<pam_response*>(
662 calloc(numMsg, sizeof(struct pam_response)));
663
664 for (int i = 0; i < numMsg; ++i)
665 {
666 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
667 {
668 continue;
669 }
670 resp[i]->resp = pass;
671 }
672 return PAM_SUCCESS;
673}
674
675/** @brief Updating the PAM password
676 *
677 * @param[in] username - username in string
678 *
679 * @param[in] password - new password in string
680 *
681 * @return status
682 */
683
684bool pamUpdatePasswd(const char* username, const char* password)
685{
686 const struct pam_conv localConversation = {pamFunctionConversation,
687 const_cast<char*>(password)};
688 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
689
690 if (pam_start("passwd", username, &localConversation, &localAuthHandle) !=
691 PAM_SUCCESS)
692 {
693 return false;
694 }
695 int retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
696
697 if (retval != PAM_SUCCESS)
698 {
699 if (retval == PAM_AUTHTOK_ERR)
700 {
701 log<level::DEBUG>("Authentication Failure");
702 }
703 else
704 {
705 log<level::DEBUG>("pam_chauthtok returned failure",
706 entry("ERROR=%d", retval));
707 }
708 pam_end(localAuthHandle, retval);
709 return false;
710 }
711 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
712 {
713 return false;
714 }
715 return true;
716}
717
Ayushi Smriti02650d52019-05-15 11:59:09 +0000718bool pamUserCheckAuthenticate(std::string_view username,
719 std::string_view password)
720{
721 const struct pam_conv localConversation = {
722 pamFunctionConversation, const_cast<char*>(password.data())};
723
724 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
725
726 if (pam_start("dropbear", username.data(), &localConversation,
727 &localAuthHandle) != PAM_SUCCESS)
728 {
729 log<level::ERR>("User Authentication Failure");
730 return false;
731 }
732
733 int retval = pam_authenticate(localAuthHandle,
734 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
735
736 if (retval != PAM_SUCCESS)
737 {
738 log<level::DEBUG>("pam_authenticate returned failure",
739 entry("ERROR=%d", retval));
740
741 pam_end(localAuthHandle, retval);
742 return false;
743 }
744
745 if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
746 PAM_SUCCESS)
747 {
748 pam_end(localAuthHandle, PAM_SUCCESS);
749 return false;
750 }
751
752 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
753 {
754 return false;
755 }
756 return true;
757}
758
Richard Marian Thomaiyar788362c2019-04-14 15:12:47 +0530759ipmi_ret_t UserAccess::setSpecialUserPassword(const std::string& userName,
760 const std::string& userPassword)
761{
762 if (!pamUpdatePasswd(userName.c_str(), userPassword.c_str()))
763 {
764 log<level::DEBUG>("Failed to update password");
765 return IPMI_CC_UNSPECIFIED_ERROR;
766 }
767 return IPMI_CC_OK;
768}
769
Suryakanth Sekar90b00c72019-01-16 10:37:57 +0530770ipmi_ret_t UserAccess::setUserPassword(const uint8_t userId,
771 const char* userPassword)
772{
773 std::string userName;
774 if (ipmiUserGetUserName(userId, userName) != IPMI_CC_OK)
775 {
776 log<level::DEBUG>("User Name not found",
777 entry("USER-ID:%d", (uint8_t)userId));
778 return IPMI_CC_PARM_OUT_OF_RANGE;
779 }
780 std::string passwd;
781 passwd.assign(reinterpret_cast<const char*>(userPassword), 0,
782 maxIpmi20PasswordSize);
783 if (!std::regex_match(passwd.c_str(),
784 std::regex("[a-zA-z_0-9][a-zA-Z_0-9,?:`!\"]*")))
785 {
786 log<level::DEBUG>("Invalid password fields",
787 entry("USER-ID:%d", (uint8_t)userId));
788 return IPMI_CC_INVALID_FIELD_REQUEST;
789 }
790 if (!pamUpdatePasswd(userName.c_str(), passwd.c_str()))
791 {
792 log<level::DEBUG>("Failed to update password",
793 entry("USER-ID:%d", (uint8_t)userId));
794 return IPMI_CC_UNSPECIFIED_ERROR;
795 }
796 return IPMI_CC_OK;
797}
798
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530799ipmi_ret_t UserAccess::setUserEnabledState(const uint8_t userId,
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530800 const bool& enabledState)
801{
802 if (!isValidUserId(userId))
803 {
804 return IPMI_CC_PARM_OUT_OF_RANGE;
805 }
806 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
807 userLock{*userMutex};
808 UserInfo* userInfo = getUserInfo(userId);
809 std::string userName;
810 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
811 ipmiMaxUserName);
812 if (userName.empty())
813 {
814 log<level::DEBUG>("User name not set / invalid");
815 return IPMI_CC_UNSPECIFIED_ERROR;
816 }
817 if (userInfo->userEnabled != enabledState)
818 {
819 std::string userPath = std::string(userObjBasePath) + "/" + userName;
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");
830 return IPMI_CC_UNSPECIFIED_ERROR;
831 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530832 }
833 return IPMI_CC_OK;
834}
835
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530836ipmi_ret_t UserAccess::setUserPrivilegeAccess(const uint8_t userId,
837 const uint8_t chNum,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530838 const UserPrivAccess& privAccess,
839 const bool& otherPrivUpdates)
840{
841 if (!isValidChannel(chNum))
842 {
843 return IPMI_CC_INVALID_FIELD_REQUEST;
844 }
845 if (!isValidUserId(userId))
846 {
847 return IPMI_CC_PARM_OUT_OF_RANGE;
848 }
849 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
850 userLock{*userMutex};
851 UserInfo* userInfo = getUserInfo(userId);
852 std::string userName;
853 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
854 ipmiMaxUserName);
855 if (userName.empty())
856 {
857 log<level::DEBUG>("User name not set / invalid");
858 return IPMI_CC_UNSPECIFIED_ERROR;
859 }
860 std::string priv = convertToSystemPrivilege(
861 static_cast<CommandPrivilege>(privAccess.privilege));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530862 uint8_t syncIndex = getUsrMgmtSyncIndex();
863 if (chNum == syncIndex &&
864 privAccess.privilege != userInfo->userPrivAccess[syncIndex].privilege)
865 {
866 std::string userPath = std::string(userObjBasePath) + "/" + userName;
Patrick Venture99d1ba02019-02-21 15:11:24 -0800867 setDbusProperty(bus, getUserServiceName(), userPath, usersInterface,
868 userPrivProperty, priv);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530869 }
870 userInfo->userPrivAccess[chNum].privilege = privAccess.privilege;
871
872 if (otherPrivUpdates)
873 {
874 userInfo->userPrivAccess[chNum].ipmiEnabled = privAccess.ipmiEnabled;
875 userInfo->userPrivAccess[chNum].linkAuthEnabled =
876 privAccess.linkAuthEnabled;
877 userInfo->userPrivAccess[chNum].accessCallback =
878 privAccess.accessCallback;
879 }
880 try
881 {
882 writeUserData();
883 }
884 catch (const std::exception& e)
885 {
886 log<level::DEBUG>("Write user data failed");
887 return IPMI_CC_UNSPECIFIED_ERROR;
888 }
889 return IPMI_CC_OK;
890}
891
892uint8_t UserAccess::getUserId(const std::string& userName)
893{
894 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
895 userLock{*userMutex};
896 checkAndReloadUserData();
897 // user index 0 is reserved, starts with 1
898 size_t usrIndex = 1;
899 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
900 {
901 std::string curName(
902 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
903 ipmiMaxUserName);
904 if (userName == curName)
905 {
906 break; // found the entry
907 }
908 }
909 if (usrIndex > ipmiMaxUsers)
910 {
911 log<level::DEBUG>("User not found",
912 entry("USER_NAME=%s", userName.c_str()));
913 return invalidUserId;
914 }
915
916 return usrIndex;
917}
918
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530919ipmi_ret_t UserAccess::getUserName(const uint8_t userId, std::string& userName)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530920{
921 if (!isValidUserId(userId))
922 {
923 return IPMI_CC_PARM_OUT_OF_RANGE;
924 }
925 UserInfo* userInfo = getUserInfo(userId);
926 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
927 ipmiMaxUserName);
928 return IPMI_CC_OK;
929}
930
Richard Marian Thomaiyara45cb342018-12-03 15:08:59 +0530931ipmi_ret_t UserAccess::setUserName(const uint8_t userId,
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530932 const char* userNameInChar)
933{
934 if (!isValidUserId(userId))
935 {
936 return IPMI_CC_PARM_OUT_OF_RANGE;
937 }
938
939 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
940 userLock{*userMutex};
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530941 std::string oldUser;
942 getUserName(userId, oldUser);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530943
944 std::string newUser(userNameInChar, 0, ipmiMaxUserName);
Richard Marian Thomaiyar8550b602018-12-06 13:20:38 +0530945 if (oldUser == newUser)
946 {
947 // requesting to set the same user name, return success.
948 return IPMI_CC_OK;
949 }
950 bool validUser = isValidUserName(userNameInChar);
951 UserInfo* userInfo = getUserInfo(userId);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530952 if (newUser.empty() && !oldUser.empty())
953 {
954 // Delete existing user
955 std::string userPath = std::string(userObjBasePath) + "/" + oldUser;
956 try
957 {
958 auto method = bus.new_method_call(
959 getUserServiceName().c_str(), userPath.c_str(),
960 deleteUserInterface, deleteUserMethod);
961 auto reply = bus.call(method);
962 }
963 catch (const sdbusplus::exception::SdBusError& e)
964 {
965 log<level::DEBUG>("Failed to excute method",
966 entry("METHOD=%s", deleteUserMethod),
967 entry("PATH=%s", userPath.c_str()));
968 return IPMI_CC_UNSPECIFIED_ERROR;
969 }
Richard Marian Thomaiyar02710bb2018-11-28 20:42:25 +0530970 deleteUserIndex(userId);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530971 }
972 else if (oldUser.empty() && !newUser.empty() && validUser)
973 {
974 try
975 {
976 // Create new user
977 auto method = bus.new_method_call(
978 getUserServiceName().c_str(), userMgrObjBasePath,
979 userMgrInterface, createUserMethod);
Richard Marian Thomaiyar02710bb2018-11-28 20:42:25 +0530980 method.append(newUser.c_str(), availableGroups, "", false);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530981 auto reply = bus.call(method);
982 }
983 catch (const sdbusplus::exception::SdBusError& e)
984 {
985 log<level::DEBUG>("Failed to excute method",
986 entry("METHOD=%s", createUserMethod),
987 entry("PATH=%s", userMgrObjBasePath));
988 return IPMI_CC_UNSPECIFIED_ERROR;
989 }
Brad Bishop77ff3fe2018-11-21 15:24:44 -0500990 std::memcpy(userInfo->userName, userNameInChar, ipmiMaxUserName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530991 userInfo->userInSystem = true;
992 }
993 else if (oldUser != newUser && validUser)
994 {
995 try
996 {
997 // User rename
998 auto method = bus.new_method_call(
999 getUserServiceName().c_str(), userMgrObjBasePath,
1000 userMgrInterface, renameUserMethod);
1001 method.append(oldUser.c_str(), newUser.c_str());
1002 auto reply = bus.call(method);
1003 }
1004 catch (const sdbusplus::exception::SdBusError& e)
1005 {
1006 log<level::DEBUG>("Failed to excute method",
1007 entry("METHOD=%s", renameUserMethod),
1008 entry("PATH=%s", userMgrObjBasePath));
1009 return IPMI_CC_UNSPECIFIED_ERROR;
1010 }
1011 std::fill(static_cast<uint8_t*>(userInfo->userName),
1012 static_cast<uint8_t*>(userInfo->userName) +
1013 sizeof(userInfo->userName),
1014 0);
Brad Bishop77ff3fe2018-11-21 15:24:44 -05001015 std::memcpy(userInfo->userName, userNameInChar, ipmiMaxUserName);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301016 ipmiRenameUserEntryPassword(oldUser, newUser);
1017 userInfo->userInSystem = true;
1018 }
1019 else if (!validUser)
1020 {
1021 return IPMI_CC_INVALID_FIELD_REQUEST;
1022 }
1023 try
1024 {
1025 writeUserData();
1026 }
1027 catch (const std::exception& e)
1028 {
1029 log<level::DEBUG>("Write user data failed");
1030 return IPMI_CC_UNSPECIFIED_ERROR;
1031 }
1032 return IPMI_CC_OK;
1033}
1034
1035static constexpr const char* jsonUserName = "user_name";
1036static constexpr const char* jsonPriv = "privilege";
1037static constexpr const char* jsonIpmiEnabled = "ipmi_enabled";
1038static constexpr const char* jsonLinkAuthEnabled = "link_auth_enabled";
1039static constexpr const char* jsonAccCallbk = "access_callback";
1040static constexpr const char* jsonUserEnabled = "user_enabled";
1041static constexpr const char* jsonUserInSys = "user_in_system";
1042static constexpr const char* jsonFixedUser = "fixed_user_name";
1043
1044void UserAccess::readUserData()
1045{
1046 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1047 userLock{*userMutex};
1048
1049 std::ifstream iUsrData(ipmiUserDataFile, std::ios::in | std::ios::binary);
1050 if (!iUsrData.good())
1051 {
1052 log<level::ERR>("Error in reading IPMI user data file");
1053 throw std::ios_base::failure("Error opening IPMI user data file");
1054 }
1055
1056 Json jsonUsersTbl = Json::array();
1057 jsonUsersTbl = Json::parse(iUsrData, nullptr, false);
1058
1059 if (jsonUsersTbl.size() != ipmiMaxUsers)
1060 {
1061 log<level::ERR>(
1062 "Error in reading IPMI user data file - User count issues");
1063 throw std::runtime_error(
1064 "Corrupted IPMI user data file - invalid user count");
1065 }
1066 // user index 0 is reserved, starts with 1
1067 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1068 {
1069 Json userInfo = jsonUsersTbl[usrIndex - 1]; // json array starts with 0.
1070 if (userInfo.is_null())
1071 {
1072 log<level::ERR>("Error in reading IPMI user data file - "
1073 "user info corrupted");
1074 throw std::runtime_error(
1075 "Corrupted IPMI user data file - invalid user info");
1076 }
1077 std::string userName = userInfo[jsonUserName].get<std::string>();
1078 std::strncpy(reinterpret_cast<char*>(usersTbl.user[usrIndex].userName),
1079 userName.c_str(), ipmiMaxUserName);
1080
1081 std::vector<std::string> privilege =
1082 userInfo[jsonPriv].get<std::vector<std::string>>();
1083 std::vector<bool> ipmiEnabled =
1084 userInfo[jsonIpmiEnabled].get<std::vector<bool>>();
1085 std::vector<bool> linkAuthEnabled =
1086 userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
1087 std::vector<bool> accessCallback =
1088 userInfo[jsonAccCallbk].get<std::vector<bool>>();
1089 if (privilege.size() != ipmiMaxChannels ||
1090 ipmiEnabled.size() != ipmiMaxChannels ||
1091 linkAuthEnabled.size() != ipmiMaxChannels ||
1092 accessCallback.size() != ipmiMaxChannels)
1093 {
1094 log<level::ERR>("Error in reading IPMI user data file - "
1095 "properties corrupted");
1096 throw std::runtime_error(
1097 "Corrupted IPMI user data file - properties");
1098 }
1099 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1100 {
1101 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege =
1102 static_cast<uint8_t>(
1103 convertToIPMIPrivilege(privilege[chIndex]));
1104 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled =
1105 ipmiEnabled[chIndex];
1106 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled =
1107 linkAuthEnabled[chIndex];
1108 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
1109 accessCallback[chIndex];
1110 }
1111 usersTbl.user[usrIndex].userEnabled =
1112 userInfo[jsonUserEnabled].get<bool>();
1113 usersTbl.user[usrIndex].userInSystem =
1114 userInfo[jsonUserInSys].get<bool>();
1115 usersTbl.user[usrIndex].fixedUserName =
1116 userInfo[jsonFixedUser].get<bool>();
1117 }
1118
1119 log<level::DEBUG>("User data read from IPMI data file");
1120 iUsrData.close();
1121 // Update the timestamp
1122 fileLastUpdatedTime = getUpdatedFileTime();
1123 return;
1124}
1125
1126void UserAccess::writeUserData()
1127{
1128 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1129 userLock{*userMutex};
1130
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301131 Json jsonUsersTbl = Json::array();
1132 // user index 0 is reserved, starts with 1
1133 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1134 {
1135 Json jsonUserInfo;
1136 jsonUserInfo[jsonUserName] = std::string(
1137 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
1138 ipmiMaxUserName);
1139 std::vector<std::string> privilege(ipmiMaxChannels);
1140 std::vector<bool> ipmiEnabled(ipmiMaxChannels);
1141 std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
1142 std::vector<bool> accessCallback(ipmiMaxChannels);
1143 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
1144 {
1145 privilege[chIndex] =
1146 convertToSystemPrivilege(static_cast<CommandPrivilege>(
1147 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege));
1148 ipmiEnabled[chIndex] =
1149 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled;
1150 linkAuthEnabled[chIndex] =
1151 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled;
1152 accessCallback[chIndex] =
1153 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback;
1154 }
1155 jsonUserInfo[jsonPriv] = privilege;
1156 jsonUserInfo[jsonIpmiEnabled] = ipmiEnabled;
1157 jsonUserInfo[jsonLinkAuthEnabled] = linkAuthEnabled;
1158 jsonUserInfo[jsonAccCallbk] = accessCallback;
1159 jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
1160 jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
1161 jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
1162 jsonUsersTbl.push_back(jsonUserInfo);
1163 }
1164
Richard Marian Thomaiyar687df402019-05-09 00:16:53 +05301165 static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
1166 int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
1167 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1168 if (fd < 0)
1169 {
1170 log<level::ERR>("Error in creating temporary IPMI user data file");
1171 throw std::ios_base::failure(
1172 "Error in creating temporary IPMI user data file");
1173 }
1174 const auto& writeStr = jsonUsersTbl.dump();
1175 if (write(fd, writeStr.c_str(), writeStr.size()) !=
1176 static_cast<ssize_t>(writeStr.size()))
1177 {
1178 close(fd);
1179 log<level::ERR>("Error in writing temporary IPMI user data file");
1180 throw std::ios_base::failure(
1181 "Error in writing temporary IPMI user data file");
1182 }
1183 close(fd);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301184
1185 if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
1186 {
1187 log<level::ERR>("Error in renaming temporary IPMI user data file");
1188 throw std::runtime_error("Error in renaming IPMI user data file");
1189 }
1190 // Update the timestamp
1191 fileLastUpdatedTime = getUpdatedFileTime();
1192 return;
1193}
1194
1195bool UserAccess::addUserEntry(const std::string& userName,
1196 const std::string& sysPriv, const bool& enabled)
1197{
1198 UsersTbl* userData = getUsersTblPtr();
1199 size_t freeIndex = 0xFF;
1200 // user index 0 is reserved, starts with 1
1201 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1202 {
1203 std::string curName(
1204 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
1205 ipmiMaxUserName);
1206 if (userName == curName)
1207 {
1208 log<level::DEBUG>("User name exists",
1209 entry("USER_NAME=%s", userName.c_str()));
1210 return false; // user name exists.
1211 }
1212
1213 if ((!userData->user[usrIndex].userInSystem) &&
1214 (userData->user[usrIndex].userName[0] == '\0') &&
1215 (freeIndex == 0xFF))
1216 {
1217 freeIndex = usrIndex;
1218 }
1219 }
1220 if (freeIndex == 0xFF)
1221 {
1222 log<level::ERR>("No empty slots found");
1223 return false;
1224 }
1225 std::strncpy(reinterpret_cast<char*>(userData->user[freeIndex].userName),
1226 userName.c_str(), ipmiMaxUserName);
1227 uint8_t priv =
1228 static_cast<uint8_t>(UserAccess::convertToIPMIPrivilege(sysPriv)) &
1229 privMask;
1230 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1231 {
1232 userData->user[freeIndex].userPrivAccess[chIndex].privilege = priv;
1233 userData->user[freeIndex].userPrivAccess[chIndex].ipmiEnabled = true;
1234 userData->user[freeIndex].userPrivAccess[chIndex].linkAuthEnabled =
1235 true;
1236 userData->user[freeIndex].userPrivAccess[chIndex].accessCallback = true;
1237 }
1238 userData->user[freeIndex].userInSystem = true;
1239 userData->user[freeIndex].userEnabled = enabled;
1240
1241 return true;
1242}
1243
1244void UserAccess::deleteUserIndex(const size_t& usrIdx)
1245{
1246 UsersTbl* userData = getUsersTblPtr();
1247
1248 std::string userName(
1249 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1250 ipmiMaxUserName);
1251 ipmiClearUserEntryPassword(userName);
1252 std::fill(static_cast<uint8_t*>(userData->user[usrIdx].userName),
1253 static_cast<uint8_t*>(userData->user[usrIdx].userName) +
1254 sizeof(userData->user[usrIdx].userName),
1255 0);
1256 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1257 {
1258 userData->user[usrIdx].userPrivAccess[chIndex].privilege = privNoAccess;
1259 userData->user[usrIdx].userPrivAccess[chIndex].ipmiEnabled = false;
1260 userData->user[usrIdx].userPrivAccess[chIndex].linkAuthEnabled = false;
1261 userData->user[usrIdx].userPrivAccess[chIndex].accessCallback = false;
1262 }
1263 userData->user[usrIdx].userInSystem = false;
1264 userData->user[usrIdx].userEnabled = false;
1265 return;
1266}
1267
1268void UserAccess::checkAndReloadUserData()
1269{
1270 std::time_t updateTime = getUpdatedFileTime();
1271 if (updateTime != fileLastUpdatedTime || updateTime == -EIO)
1272 {
1273 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1274 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1275 readUserData();
1276 }
1277 return;
1278}
1279
1280UsersTbl* UserAccess::getUsersTblPtr()
1281{
1282 // reload data before using it.
1283 checkAndReloadUserData();
1284 return &usersTbl;
1285}
1286
1287void UserAccess::getSystemPrivAndGroups()
1288{
1289 std::map<std::string, PrivAndGroupType> properties;
1290 try
1291 {
1292 auto method = bus.new_method_call(
1293 getUserServiceName().c_str(), userMgrObjBasePath,
1294 dBusPropertiesInterface, getAllPropertiesMethod);
1295 method.append(userMgrInterface);
1296
1297 auto reply = bus.call(method);
1298 reply.read(properties);
1299 }
1300 catch (const sdbusplus::exception::SdBusError& e)
1301 {
1302 log<level::DEBUG>("Failed to excute method",
1303 entry("METHOD=%s", getAllPropertiesMethod),
1304 entry("PATH=%s", userMgrObjBasePath));
1305 return;
1306 }
1307 for (const auto& t : properties)
1308 {
1309 auto key = t.first;
1310 if (key == allPrivProperty)
1311 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001312 availablePrivileges = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301313 }
1314 else if (key == allGrpProperty)
1315 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001316 availableGroups = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301317 }
1318 }
1319 // TODO: Implement Supported Privilege & Groups verification logic
1320 return;
1321}
1322
1323std::time_t UserAccess::getUpdatedFileTime()
1324{
1325 struct stat fileStat;
1326 if (stat(ipmiUserDataFile, &fileStat) != 0)
1327 {
1328 log<level::DEBUG>("Error in getting last updated time stamp");
1329 return -EIO;
1330 }
1331 return fileStat.st_mtime;
1332}
1333
1334void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
1335 std::vector<std::string>& usrGrps,
1336 std::string& usrPriv, bool& usrEnabled)
1337{
1338 for (const auto& t : properties)
1339 {
1340 std::string key = t.first;
1341 if (key == userPrivProperty)
1342 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001343 usrPriv = std::get<std::string>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301344 }
1345 else if (key == userGrpProperty)
1346 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001347 usrGrps = std::get<std::vector<std::string>>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301348 }
1349 else if (key == userEnabledProperty)
1350 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001351 usrEnabled = std::get<bool>(t.second);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301352 }
1353 }
1354 return;
1355}
1356
1357int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
1358 std::vector<std::string>& usrGrps,
1359 std::string& usrPriv, bool& usrEnabled)
1360{
1361 auto usrObj = userObjs.find(usersInterface);
1362 if (usrObj != userObjs.end())
1363 {
1364 getUserProperties(usrObj->second, usrGrps, usrPriv, usrEnabled);
1365 return 0;
1366 }
1367 return -EIO;
1368}
1369
1370void UserAccess::initUserDataFile()
1371{
1372 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1373 userLock{*userMutex};
1374 try
1375 {
1376 readUserData();
1377 }
1378 catch (const std::ios_base::failure& e)
1379 { // File is empty, create it for the first time
1380 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1381 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1382 // user index 0 is reserved, starts with 1
1383 for (size_t userIndex = 1; userIndex <= ipmiMaxUsers; ++userIndex)
1384 {
1385 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1386 {
1387 usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
1388 privNoAccess;
1389 }
1390 }
1391 writeUserData();
1392 }
1393 std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
1394 try
1395 {
1396 auto method = bus.new_method_call(getUserServiceName().c_str(),
1397 userMgrObjBasePath, dBusObjManager,
1398 getManagedObjectsMethod);
1399 auto reply = bus.call(method);
1400 reply.read(managedObjs);
1401 }
1402 catch (const sdbusplus::exception::SdBusError& e)
1403 {
1404 log<level::DEBUG>("Failed to excute method",
1405 entry("METHOD=%s", getSubTreeMethod),
1406 entry("PATH=%s", userMgrObjBasePath));
1407 return;
1408 }
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301409 bool updateRequired = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301410 UsersTbl* userData = &usersTbl;
1411 // user index 0 is reserved, starts with 1
1412 for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
1413 {
1414 if ((userData->user[usrIdx].userInSystem) &&
1415 (userData->user[usrIdx].userName[0] != '\0'))
1416 {
1417 std::vector<std::string> usrGrps;
1418 std::string usrPriv;
1419 bool usrEnabled;
1420
1421 std::string userName(
1422 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1423 ipmiMaxUserName);
1424 std::string usersPath =
1425 std::string(userObjBasePath) + "/" + userName;
1426
1427 auto usrObj = managedObjs.find(usersPath);
1428 if (usrObj != managedObjs.end())
1429 {
1430 // User exist. Lets check and update other fileds
1431 getUserObjProperties(usrObj->second, usrGrps, usrPriv,
1432 usrEnabled);
1433 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
1434 usrGrps.end())
1435 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301436 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301437 // Group "ipmi" is removed so lets remove user in IPMI
1438 deleteUserIndex(usrIdx);
1439 }
1440 else
1441 {
1442 // Group "ipmi" is present so lets update other properties
1443 // in IPMI
1444 uint8_t priv =
1445 UserAccess::convertToIPMIPrivilege(usrPriv) & privMask;
1446 // Update all channels priv, only if it is not equivalent to
1447 // getUsrMgmtSyncIndex()
1448 if (userData->user[usrIdx]
1449 .userPrivAccess[getUsrMgmtSyncIndex()]
1450 .privilege != priv)
1451 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301452 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301453 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
1454 ++chIndex)
1455 {
1456 userData->user[usrIdx]
1457 .userPrivAccess[chIndex]
1458 .privilege = priv;
1459 }
1460 }
1461 if (userData->user[usrIdx].userEnabled != usrEnabled)
1462 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301463 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301464 userData->user[usrIdx].userEnabled = usrEnabled;
1465 }
1466 }
1467
1468 // We are done with this obj. lets delete from MAP
1469 managedObjs.erase(usrObj);
1470 }
1471 else
1472 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301473 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301474 deleteUserIndex(usrIdx);
1475 }
1476 }
1477 }
1478
1479 // Walk through remnaining managedObj users list
1480 // Add them to ipmi data base
1481 for (const auto& usrObj : managedObjs)
1482 {
1483 std::vector<std::string> usrGrps;
1484 std::string usrPriv, userName;
1485 bool usrEnabled;
1486 std::string usrObjPath = std::string(usrObj.first);
1487 if (getUserNameFromPath(usrObj.first.str, userName) != 0)
1488 {
1489 log<level::ERR>("Error in user object path");
1490 continue;
1491 }
1492 getUserObjProperties(usrObj.second, usrGrps, usrPriv, usrEnabled);
1493 // Add 'ipmi' group users
1494 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
1495 usrGrps.end())
1496 {
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301497 updateRequired = true;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301498 // CREATE NEW USER
1499 if (true != addUserEntry(userName, usrPriv, usrEnabled))
1500 {
1501 break;
1502 }
1503 }
1504 }
1505
Richard Marian Thomaiyare004e222019-05-09 00:37:55 +05301506 if (updateRequired)
1507 {
1508 // All userData slots update done. Lets write the data
1509 writeUserData();
1510 }
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301511
1512 return;
1513}
1514} // namespace ipmi