blob: e320e3fe94abd911bd43f19be2b715111c298274 [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
20#include <sys/stat.h>
21#include <unistd.h>
22
23#include <boost/interprocess/sync/named_recursive_mutex.hpp>
24#include <boost/interprocess/sync/scoped_lock.hpp>
25#include <cerrno>
26#include <fstream>
27#include <host-ipmid/ipmid-host-cmd.hpp>
28#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>
34#include <xyz/openbmc_project/Common/error.hpp>
35#include <xyz/openbmc_project/User/Common/error.hpp>
36
37namespace ipmi
38{
39
40// TODO: Move D-Bus & Object Manager related stuff, to common files
41// D-Bus property related
42static constexpr const char* dBusPropertiesInterface =
43 "org.freedesktop.DBus.Properties";
44static constexpr const char* getAllPropertiesMethod = "GetAll";
45static constexpr const char* propertiesChangedSignal = "PropertiesChanged";
46static constexpr const char* setPropertiesMethod = "Set";
47
48// Object Manager related
49static constexpr const char* dBusObjManager =
50 "org.freedesktop.DBus.ObjectManager";
51static constexpr const char* getManagedObjectsMethod = "GetManagedObjects";
52// Object Manager signals
53static constexpr const char* intfAddedSignal = "InterfacesAdded";
54static constexpr const char* intfRemovedSignal = "InterfacesRemoved";
55
56// Object Mapper related
57static constexpr const char* objMapperService =
58 "xyz.openbmc_project.ObjectMapper";
59static constexpr const char* objMapperPath =
60 "/xyz/openbmc_project/object_mapper";
61static constexpr const char* objMapperInterface =
62 "xyz.openbmc_project.ObjectMapper";
63static constexpr const char* getSubTreeMethod = "GetSubTree";
64static constexpr const char* getObjectMethod = "GetObject";
65
66static constexpr const char* ipmiUserMutex = "ipmi_usr_mutex";
67static constexpr const char* ipmiMutexCleanupLockFile =
68 "/var/lib/ipmi/ipmi_usr_mutex_cleanup";
69static constexpr const char* ipmiUserDataFile = "/var/lib/ipmi/ipmi_user.json";
70static constexpr const char* ipmiGrpName = "ipmi";
71static constexpr size_t privNoAccess = 0xF;
72static constexpr size_t privMask = 0xF;
73
74// User manager related
75static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user";
76static constexpr const char* userObjBasePath = "/xyz/openbmc_project/user";
77static constexpr const char* userMgrInterface =
78 "xyz.openbmc_project.User.Manager";
79static constexpr const char* usersInterface =
80 "xyz.openbmc_project.User.Attributes";
81static constexpr const char* deleteUserInterface =
82 "xyz.openbmc_project.Object.Delete";
83
84static constexpr const char* createUserMethod = "CreateUser";
85static constexpr const char* deleteUserMethod = "Delete";
86static constexpr const char* renameUserMethod = "RenameUser";
87// User manager signal memebers
88static constexpr const char* userRenamedSignal = "UserRenamed";
89// Mgr interface properties
90static constexpr const char* allPrivProperty = "AllPrivileges";
91static constexpr const char* allGrpProperty = "AllGroups";
92// User interface properties
93static constexpr const char* userPrivProperty = "UserPrivilege";
94static constexpr const char* userGrpProperty = "UserGroups";
95static constexpr const char* userEnabledProperty = "UserEnabled";
96
97static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = {
98 "priv-reserved", // PRIVILEGE_RESERVED - 0
99 "priv-callback", // PRIVILEGE_CALLBACK - 1
100 "priv-user", // PRIVILEGE_USER - 2
101 "priv-operator", // PRIVILEGE_OPERATOR - 3
102 "priv-admin", // PRIVILEGE_ADMIN - 4
103 "priv-custom" // PRIVILEGE_OEM - 5
104};
105
106using namespace phosphor::logging;
107using Json = nlohmann::json;
108
109using PrivAndGroupType =
110 sdbusplus::message::variant<std::string, std::vector<std::string>>;
111
112using NoResource =
113 sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource;
114
115using InternalFailure =
116 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
117
118std::unique_ptr<sdbusplus::bus::match_t> userUpdatedSignal(nullptr);
119std::unique_ptr<sdbusplus::bus::match_t> userMgrRenamedSignal(nullptr);
120std::unique_ptr<sdbusplus::bus::match_t> userPropertiesSignal(nullptr);
121
122// TODO: Below code can be removed once it is moved to common layer libmiscutil
123std::string getUserService(sdbusplus::bus::bus& bus, const std::string& intf,
124 const std::string& path)
125{
126 auto mapperCall = bus.new_method_call(objMapperService, objMapperPath,
127 objMapperInterface, getObjectMethod);
128
129 mapperCall.append(path);
130 mapperCall.append(std::vector<std::string>({intf}));
131
132 auto mapperResponseMsg = bus.call(mapperCall);
133
134 std::map<std::string, std::vector<std::string>> mapperResponse;
135 mapperResponseMsg.read(mapperResponse);
136
137 if (mapperResponse.begin() == mapperResponse.end())
138 {
139 throw sdbusplus::exception::SdBusError(
140 -EIO, "ERROR in reading the mapper response");
141 }
142
143 return mapperResponse.begin()->first;
144}
145
146void setDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
147 const std::string& objPath, const std::string& interface,
148 const std::string& property,
149 const DbusUserPropVariant& value)
150{
151 try
152 {
153 auto method =
154 bus.new_method_call(service.c_str(), objPath.c_str(),
155 dBusPropertiesInterface, setPropertiesMethod);
156 method.append(interface, property, value);
157 bus.call(method);
158 }
159 catch (const sdbusplus::exception::SdBusError& e)
160 {
161 log<level::ERR>("Failed to set property",
162 entry("PROPERTY=%s", property.c_str()),
163 entry("PATH=%s", objPath.c_str()),
164 entry("INTERFACE=%s", interface.c_str()));
165 throw;
166 }
167}
168
169static std::string getUserServiceName()
170{
171 static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
172 static std::string userMgmtService;
173 if (userMgmtService.empty())
174 {
175 try
176 {
177 userMgmtService =
178 ipmi::getUserService(bus, userMgrInterface, userMgrObjBasePath);
179 }
180 catch (const sdbusplus::exception::SdBusError& e)
181 {
182 userMgmtService.clear();
183 }
184 }
185 return userMgmtService;
186}
187
188UserAccess& getUserAccessObject()
189{
190 static UserAccess userAccess;
191 return userAccess;
192}
193
194int getUserNameFromPath(const std::string& path, std::string& userName)
195{
196 static size_t pos = strlen(userObjBasePath) + 1;
197 if (path.find(userObjBasePath) == std::string::npos)
198 {
199 return -EINVAL;
200 }
201 userName.assign(path, pos, path.size());
202 return 0;
203}
204
205void userUpdateHelper(UserAccess& usrAccess, const UserUpdateEvent& userEvent,
206 const std::string& userName, const std::string& priv,
207 const bool& enabled, const std::string& newUserName)
208{
209 UsersTbl* userData = usrAccess.getUsersTblPtr();
210 if (userEvent == UserUpdateEvent::userCreated)
211 {
212 if (usrAccess.addUserEntry(userName, priv, enabled) == false)
213 {
214 return;
215 }
216 }
217 else
218 {
219 // user index 0 is reserved, starts with 1
220 size_t usrIndex = 1;
221 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
222 {
223 std::string curName(
224 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
225 ipmiMaxUserName);
226 if (userName == curName)
227 {
228 break; // found the entry
229 }
230 }
231 if (usrIndex > ipmiMaxUsers)
232 {
233 log<level::DEBUG>("User not found for signal",
234 entry("USER_NAME=%s", userName.c_str()),
235 entry("USER_EVENT=%d", userEvent));
236 return;
237 }
238 switch (userEvent)
239 {
240 case UserUpdateEvent::userDeleted:
241 {
242 usrAccess.deleteUserIndex(usrIndex);
243 break;
244 }
245 case UserUpdateEvent::userPrivUpdated:
246 {
247 uint8_t userPriv =
248 static_cast<uint8_t>(
249 UserAccess::convertToIPMIPrivilege(priv)) &
250 privMask;
251 // Update all channels privileges, only if it is not equivalent
252 // to getUsrMgmtSyncIndex()
253 if (userData->user[usrIndex]
254 .userPrivAccess[UserAccess::getUsrMgmtSyncIndex()]
255 .privilege != userPriv)
256 {
257 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
258 ++chIndex)
259 {
260 userData->user[usrIndex]
261 .userPrivAccess[chIndex]
262 .privilege = userPriv;
263 }
264 }
265 break;
266 }
267 case UserUpdateEvent::userRenamed:
268 {
269 std::fill(
270 static_cast<uint8_t*>(userData->user[usrIndex].userName),
271 static_cast<uint8_t*>(userData->user[usrIndex].userName) +
272 sizeof(userData->user[usrIndex].userName),
273 0);
274 std::strncpy(
275 reinterpret_cast<char*>(userData->user[usrIndex].userName),
276 newUserName.c_str(), ipmiMaxUserName);
277 ipmiRenameUserEntryPassword(userName, newUserName);
278 break;
279 }
280 case UserUpdateEvent::userStateUpdated:
281 {
282 userData->user[usrIndex].userEnabled = enabled;
283 break;
284 }
285 default:
286 {
287 log<level::ERR>("Unhandled user event",
288 entry("USER_EVENT=%d", userEvent));
289 return;
290 }
291 }
292 }
293 usrAccess.writeUserData();
294 log<level::DEBUG>("User event handled successfully",
295 entry("USER_NAME=%s", userName.c_str()),
296 entry("USER_EVENT=%d", userEvent));
297
298 return;
299}
300
301void userUpdatedSignalHandler(UserAccess& usrAccess,
302 sdbusplus::message::message& msg)
303{
304 static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
305 std::string signal = msg.get_member();
306 std::string userName, update, priv, newUserName;
307 std::vector<std::string> groups;
308 bool enabled = false;
309 UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent;
310 if (signal == intfAddedSignal)
311 {
312 DbusUserObjPath objPath;
313 DbusUserObjValue objValue;
314 msg.read(objPath, objValue);
315 getUserNameFromPath(objPath.str, userName);
316 if (usrAccess.getUserObjProperties(objValue, groups, priv, enabled) !=
317 0)
318 {
319 return;
320 }
321 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
322 groups.end())
323 {
324 return;
325 }
326 userEvent = UserUpdateEvent::userCreated;
327 }
328 else if (signal == intfRemovedSignal)
329 {
330 DbusUserObjPath objPath;
331 std::vector<std::string> interfaces;
332 msg.read(objPath, interfaces);
333 getUserNameFromPath(objPath.str, userName);
334 userEvent = UserUpdateEvent::userDeleted;
335 }
336 else if (signal == userRenamedSignal)
337 {
338 msg.read(userName, newUserName);
339 userEvent = UserUpdateEvent::userRenamed;
340 }
341 else if (signal == propertiesChangedSignal)
342 {
343 getUserNameFromPath(msg.get_path(), userName);
344 }
345 else
346 {
347 log<level::ERR>("Unknown user update signal",
348 entry("SIGNAL=%s", signal.c_str()));
349 return;
350 }
351
352 if (signal.empty() || userName.empty() ||
353 (signal == userRenamedSignal && newUserName.empty()))
354 {
355 log<level::ERR>("Invalid inputs received");
356 return;
357 }
358
359 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
360 userLock{*(usrAccess.userMutex)};
361 usrAccess.checkAndReloadUserData();
362
363 if (signal == propertiesChangedSignal)
364 {
365 std::string intfName;
366 DbusUserObjProperties chProperties;
367 msg.read(intfName, chProperties); // skip reading 3rd argument.
368 for (const auto& prop : chProperties)
369 {
370 userEvent = UserUpdateEvent::reservedEvent;
371 std::string member = prop.first;
372 if (member == userPrivProperty)
373 {
374 priv = prop.second.get<std::string>();
375 userEvent = UserUpdateEvent::userPrivUpdated;
376 }
377 else if (member == userGrpProperty)
378 {
379 groups = prop.second.get<std::vector<std::string>>();
380 userEvent = UserUpdateEvent::userGrpUpdated;
381 }
382 else if (member == userEnabledProperty)
383 {
384 enabled = prop.second.get<bool>();
385 userEvent = UserUpdateEvent::userStateUpdated;
386 }
387 // Process based on event type.
388 if (userEvent == UserUpdateEvent::userGrpUpdated)
389 {
390 if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
391 groups.end())
392 {
393 // remove user from ipmi user list.
394 userUpdateHelper(usrAccess, UserUpdateEvent::userDeleted,
395 userName, priv, enabled, newUserName);
396 }
397 else
398 {
399 DbusUserObjProperties properties;
400 try
401 {
402 auto method = bus.new_method_call(
403 getUserServiceName().c_str(), msg.get_path(),
404 dBusPropertiesInterface, getAllPropertiesMethod);
405 method.append(usersInterface);
406 auto reply = bus.call(method);
407 reply.read(properties);
408 }
409 catch (const sdbusplus::exception::SdBusError& e)
410 {
411 log<level::DEBUG>(
412 "Failed to excute method",
413 entry("METHOD=%s", getAllPropertiesMethod),
414 entry("PATH=%s", msg.get_path()));
415 return;
416 }
417 usrAccess.getUserProperties(properties, groups, priv,
418 enabled);
419 // add user to ipmi user list.
420 userUpdateHelper(usrAccess, UserUpdateEvent::userCreated,
421 userName, priv, enabled, newUserName);
422 }
423 }
424 else if (userEvent != UserUpdateEvent::reservedEvent)
425 {
426 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
427 newUserName);
428 }
429 }
430 }
431 else if (userEvent != UserUpdateEvent::reservedEvent)
432 {
433 userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
434 newUserName);
435 }
436 return;
437}
438
439UserAccess::~UserAccess()
440{
441 if (signalHndlrObject)
442 {
443 userUpdatedSignal.reset();
444 userMgrRenamedSignal.reset();
445 userPropertiesSignal.reset();
446 sigHndlrLock.unlock();
447 }
448}
449
450UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection())
451{
452 std::ofstream mutexCleanUpFile;
453 mutexCleanUpFile.open(ipmiMutexCleanupLockFile,
454 std::ofstream::out | std::ofstream::app);
455 if (!mutexCleanUpFile.good())
456 {
457 log<level::DEBUG>("Unable to open mutex cleanup file");
458 return;
459 }
460 mutexCleanUpFile.close();
461 mutexCleanupLock = boost::interprocess::file_lock(ipmiMutexCleanupLockFile);
462 if (mutexCleanupLock.try_lock())
463 {
464 boost::interprocess::named_recursive_mutex::remove(ipmiUserMutex);
465 }
466 mutexCleanupLock.lock_sharable();
467 userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>(
468 boost::interprocess::open_or_create, ipmiUserMutex);
469
470 initUserDataFile();
471 getSystemPrivAndGroups();
472 sigHndlrLock = boost::interprocess::file_lock(ipmiUserDataFile);
473 // Register it for single object and single process either netipimd /
474 // host-ipmid
475 if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
476 {
477 log<level::DEBUG>("Registering signal handler");
478 userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
479 bus,
480 sdbusplus::bus::match::rules::type::signal() +
481 sdbusplus::bus::match::rules::interface(dBusObjManager) +
482 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
483 [&](sdbusplus::message::message& msg) {
484 userUpdatedSignalHandler(*this, msg);
485 });
486 userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
487 bus,
488 sdbusplus::bus::match::rules::type::signal() +
489 sdbusplus::bus::match::rules::interface(userMgrInterface) +
490 sdbusplus::bus::match::rules::path(userMgrObjBasePath),
491 [&](sdbusplus::message::message& msg) {
492 userUpdatedSignalHandler(*this, msg);
493 });
494 userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
495 bus,
496 sdbusplus::bus::match::rules::type::signal() +
497 sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
498 sdbusplus::bus::match::rules::interface(
499 dBusPropertiesInterface) +
500 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
501 sdbusplus::bus::match::rules::argN(0, usersInterface),
502 [&](sdbusplus::message::message& msg) {
503 userUpdatedSignalHandler(*this, msg);
504 });
505 signalHndlrObject = true;
506 }
507}
508
509UserInfo* UserAccess::getUserInfo(const uint8_t& userId)
510{
511 checkAndReloadUserData();
512 return &usersTbl.user[userId];
513}
514
515void UserAccess::setUserInfo(const uint8_t& userId, UserInfo* userInfo)
516{
517 checkAndReloadUserData();
518 std::copy(reinterpret_cast<uint8_t*>(userInfo),
519 reinterpret_cast<uint8_t*>(userInfo) + sizeof(*userInfo),
520 reinterpret_cast<uint8_t*>(&usersTbl.user[userId]));
521 writeUserData();
522}
523
524bool UserAccess::isValidChannel(const uint8_t& chNum)
525{
526 return (chNum < ipmiMaxChannels);
527}
528
529bool UserAccess::isValidUserId(const uint8_t& userId)
530{
531 return ((userId <= ipmiMaxUsers) && (userId != reservedUserId));
532}
533
534bool UserAccess::isValidPrivilege(const uint8_t& priv)
535{
536 return ((priv >= PRIVILEGE_CALLBACK && priv <= PRIVILEGE_OEM) ||
537 priv == privNoAccess);
538}
539
540uint8_t UserAccess::getUsrMgmtSyncIndex()
541{
542 // TODO: Need to get LAN1 channel number dynamically,
543 // which has to be in sync with system user privilege
544 // level(Phosphor-user-manager). Note: For time being chanLan1 is marked as
545 // sync index to the user-manager privilege..
546 return static_cast<uint8_t>(EChannelID::chanLan1);
547}
548
549CommandPrivilege UserAccess::convertToIPMIPrivilege(const std::string& value)
550{
551 auto iter = std::find(ipmiPrivIndex.begin(), ipmiPrivIndex.end(), value);
552 if (iter == ipmiPrivIndex.end())
553 {
554 if (value == "")
555 {
556 return static_cast<CommandPrivilege>(privNoAccess);
557 }
558 log<level::ERR>("Error in converting to IPMI privilege",
559 entry("PRIV=%s", value.c_str()));
560 throw std::out_of_range("Out of range - convertToIPMIPrivilege");
561 }
562 else
563 {
564 return static_cast<CommandPrivilege>(
565 std::distance(ipmiPrivIndex.begin(), iter));
566 }
567}
568
569std::string UserAccess::convertToSystemPrivilege(const CommandPrivilege& value)
570{
571 if (value == static_cast<CommandPrivilege>(privNoAccess))
572 {
573 return "";
574 }
575 try
576 {
577 return ipmiPrivIndex.at(value);
578 }
579 catch (const std::out_of_range& e)
580 {
581 log<level::ERR>("Error in converting to system privilege",
582 entry("PRIV=%d", static_cast<uint8_t>(value)));
583 throw std::out_of_range("Out of range - convertToSystemPrivilege");
584 }
585}
586
587bool UserAccess::isValidUserName(const char* userNameInChar)
588{
589 if (!userNameInChar)
590 {
591 log<level::ERR>("null ptr");
592 return false;
593 }
594 std::string userName(userNameInChar, 0, ipmiMaxUserName);
595 if (!std::regex_match(userName.c_str(),
596 std::regex("[a-zA-z_][a-zA-Z_0-9]*")))
597 {
598 log<level::ERR>("Unsupported characters in user name");
599 return false;
600 }
601 if (userName == "root")
602 {
603 log<level::ERR>("Invalid user name - root");
604 return false;
605 }
606 std::map<DbusUserObjPath, DbusUserObjValue> properties;
607 try
608 {
609 auto method = bus.new_method_call(getUserServiceName().c_str(),
610 userMgrObjBasePath, dBusObjManager,
611 getManagedObjectsMethod);
612 auto reply = bus.call(method);
613 reply.read(properties);
614 }
615 catch (const sdbusplus::exception::SdBusError& e)
616 {
617 log<level::ERR>("Failed to excute method",
618 entry("METHOD=%s", getSubTreeMethod),
619 entry("PATH=%s", userMgrObjBasePath));
620 return false;
621 }
622
623 std::string usersPath = std::string(userObjBasePath) + "/" + userName;
624 if (properties.find(usersPath) != properties.end())
625 {
626 log<level::DEBUG>("User name already exists",
627 entry("USER_NAME=%s", userName.c_str()));
628 return false;
629 }
630
631 return true;
632}
633
634ipmi_ret_t UserAccess::setUserPrivilegeAccess(const uint8_t& userId,
635 const uint8_t& chNum,
636 const UserPrivAccess& privAccess,
637 const bool& otherPrivUpdates)
638{
639 if (!isValidChannel(chNum))
640 {
641 return IPMI_CC_INVALID_FIELD_REQUEST;
642 }
643 if (!isValidUserId(userId))
644 {
645 return IPMI_CC_PARM_OUT_OF_RANGE;
646 }
647 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
648 userLock{*userMutex};
649 UserInfo* userInfo = getUserInfo(userId);
650 std::string userName;
651 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
652 ipmiMaxUserName);
653 if (userName.empty())
654 {
655 log<level::DEBUG>("User name not set / invalid");
656 return IPMI_CC_UNSPECIFIED_ERROR;
657 }
658 std::string priv = convertToSystemPrivilege(
659 static_cast<CommandPrivilege>(privAccess.privilege));
660 if (priv.empty())
661 {
662 return IPMI_CC_PARM_OUT_OF_RANGE;
663 }
664 uint8_t syncIndex = getUsrMgmtSyncIndex();
665 if (chNum == syncIndex &&
666 privAccess.privilege != userInfo->userPrivAccess[syncIndex].privilege)
667 {
668 std::string userPath = std::string(userObjBasePath) + "/" + userName;
669 setDbusProperty(bus, getUserServiceName().c_str(), userPath.c_str(),
670 usersInterface, userPrivProperty, priv);
671 }
672 userInfo->userPrivAccess[chNum].privilege = privAccess.privilege;
673
674 if (otherPrivUpdates)
675 {
676 userInfo->userPrivAccess[chNum].ipmiEnabled = privAccess.ipmiEnabled;
677 userInfo->userPrivAccess[chNum].linkAuthEnabled =
678 privAccess.linkAuthEnabled;
679 userInfo->userPrivAccess[chNum].accessCallback =
680 privAccess.accessCallback;
681 }
682 try
683 {
684 writeUserData();
685 }
686 catch (const std::exception& e)
687 {
688 log<level::DEBUG>("Write user data failed");
689 return IPMI_CC_UNSPECIFIED_ERROR;
690 }
691 return IPMI_CC_OK;
692}
693
694uint8_t UserAccess::getUserId(const std::string& userName)
695{
696 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
697 userLock{*userMutex};
698 checkAndReloadUserData();
699 // user index 0 is reserved, starts with 1
700 size_t usrIndex = 1;
701 for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
702 {
703 std::string curName(
704 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
705 ipmiMaxUserName);
706 if (userName == curName)
707 {
708 break; // found the entry
709 }
710 }
711 if (usrIndex > ipmiMaxUsers)
712 {
713 log<level::DEBUG>("User not found",
714 entry("USER_NAME=%s", userName.c_str()));
715 return invalidUserId;
716 }
717
718 return usrIndex;
719}
720
721ipmi_ret_t UserAccess::getUserName(const uint8_t& userId, std::string& userName)
722{
723 if (!isValidUserId(userId))
724 {
725 return IPMI_CC_PARM_OUT_OF_RANGE;
726 }
727 UserInfo* userInfo = getUserInfo(userId);
728 userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
729 ipmiMaxUserName);
730 return IPMI_CC_OK;
731}
732
733ipmi_ret_t UserAccess::setUserName(const uint8_t& userId,
734 const char* userNameInChar)
735{
736 if (!isValidUserId(userId))
737 {
738 return IPMI_CC_PARM_OUT_OF_RANGE;
739 }
740
741 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
742 userLock{*userMutex};
743 bool validUser = isValidUserName(userNameInChar);
744 std::string oldUser;
745 getUserName(userId, oldUser);
746 UserInfo* userInfo = getUserInfo(userId);
747
748 std::string newUser(userNameInChar, 0, ipmiMaxUserName);
749 if (newUser.empty() && !oldUser.empty())
750 {
751 // Delete existing user
752 std::string userPath = std::string(userObjBasePath) + "/" + oldUser;
753 try
754 {
755 auto method = bus.new_method_call(
756 getUserServiceName().c_str(), userPath.c_str(),
757 deleteUserInterface, deleteUserMethod);
758 auto reply = bus.call(method);
759 }
760 catch (const sdbusplus::exception::SdBusError& e)
761 {
762 log<level::DEBUG>("Failed to excute method",
763 entry("METHOD=%s", deleteUserMethod),
764 entry("PATH=%s", userPath.c_str()));
765 return IPMI_CC_UNSPECIFIED_ERROR;
766 }
767 std::fill(userInfo->userName,
768 userInfo->userName + sizeof(userInfo->userName), 0);
769 ipmiClearUserEntryPassword(oldUser);
770 userInfo->userInSystem = false;
771 }
772 else if (oldUser.empty() && !newUser.empty() && validUser)
773 {
774 try
775 {
776 // Create new user
777 auto method = bus.new_method_call(
778 getUserServiceName().c_str(), userMgrObjBasePath,
779 userMgrInterface, createUserMethod);
780 // TODO: Fetch proper privilege & enable state once set User access
781 // is implemented if LAN Channel specified, then create user for all
782 // groups follow channel privilege for user creation.
783 method.append(newUser.c_str(), availableGroups, "priv-admin", true);
784 auto reply = bus.call(method);
785 }
786 catch (const sdbusplus::exception::SdBusError& e)
787 {
788 log<level::DEBUG>("Failed to excute method",
789 entry("METHOD=%s", createUserMethod),
790 entry("PATH=%s", userMgrObjBasePath));
791 return IPMI_CC_UNSPECIFIED_ERROR;
792 }
793 std::strncpy(reinterpret_cast<char*>(userInfo->userName),
794 userNameInChar, ipmiMaxUserName);
795 userInfo->userInSystem = true;
796 }
797 else if (oldUser != newUser && validUser)
798 {
799 try
800 {
801 // User rename
802 auto method = bus.new_method_call(
803 getUserServiceName().c_str(), userMgrObjBasePath,
804 userMgrInterface, renameUserMethod);
805 method.append(oldUser.c_str(), newUser.c_str());
806 auto reply = bus.call(method);
807 }
808 catch (const sdbusplus::exception::SdBusError& e)
809 {
810 log<level::DEBUG>("Failed to excute method",
811 entry("METHOD=%s", renameUserMethod),
812 entry("PATH=%s", userMgrObjBasePath));
813 return IPMI_CC_UNSPECIFIED_ERROR;
814 }
815 std::fill(static_cast<uint8_t*>(userInfo->userName),
816 static_cast<uint8_t*>(userInfo->userName) +
817 sizeof(userInfo->userName),
818 0);
819 std::strncpy(reinterpret_cast<char*>(userInfo->userName),
820 userNameInChar, ipmiMaxUserName);
821 ipmiRenameUserEntryPassword(oldUser, newUser);
822 userInfo->userInSystem = true;
823 }
824 else if (!validUser)
825 {
826 return IPMI_CC_INVALID_FIELD_REQUEST;
827 }
828 try
829 {
830 writeUserData();
831 }
832 catch (const std::exception& e)
833 {
834 log<level::DEBUG>("Write user data failed");
835 return IPMI_CC_UNSPECIFIED_ERROR;
836 }
837 return IPMI_CC_OK;
838}
839
840static constexpr const char* jsonUserName = "user_name";
841static constexpr const char* jsonPriv = "privilege";
842static constexpr const char* jsonIpmiEnabled = "ipmi_enabled";
843static constexpr const char* jsonLinkAuthEnabled = "link_auth_enabled";
844static constexpr const char* jsonAccCallbk = "access_callback";
845static constexpr const char* jsonUserEnabled = "user_enabled";
846static constexpr const char* jsonUserInSys = "user_in_system";
847static constexpr const char* jsonFixedUser = "fixed_user_name";
848
849void UserAccess::readUserData()
850{
851 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
852 userLock{*userMutex};
853
854 std::ifstream iUsrData(ipmiUserDataFile, std::ios::in | std::ios::binary);
855 if (!iUsrData.good())
856 {
857 log<level::ERR>("Error in reading IPMI user data file");
858 throw std::ios_base::failure("Error opening IPMI user data file");
859 }
860
861 Json jsonUsersTbl = Json::array();
862 jsonUsersTbl = Json::parse(iUsrData, nullptr, false);
863
864 if (jsonUsersTbl.size() != ipmiMaxUsers)
865 {
866 log<level::ERR>(
867 "Error in reading IPMI user data file - User count issues");
868 throw std::runtime_error(
869 "Corrupted IPMI user data file - invalid user count");
870 }
871 // user index 0 is reserved, starts with 1
872 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
873 {
874 Json userInfo = jsonUsersTbl[usrIndex - 1]; // json array starts with 0.
875 if (userInfo.is_null())
876 {
877 log<level::ERR>("Error in reading IPMI user data file - "
878 "user info corrupted");
879 throw std::runtime_error(
880 "Corrupted IPMI user data file - invalid user info");
881 }
882 std::string userName = userInfo[jsonUserName].get<std::string>();
883 std::strncpy(reinterpret_cast<char*>(usersTbl.user[usrIndex].userName),
884 userName.c_str(), ipmiMaxUserName);
885
886 std::vector<std::string> privilege =
887 userInfo[jsonPriv].get<std::vector<std::string>>();
888 std::vector<bool> ipmiEnabled =
889 userInfo[jsonIpmiEnabled].get<std::vector<bool>>();
890 std::vector<bool> linkAuthEnabled =
891 userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
892 std::vector<bool> accessCallback =
893 userInfo[jsonAccCallbk].get<std::vector<bool>>();
894 if (privilege.size() != ipmiMaxChannels ||
895 ipmiEnabled.size() != ipmiMaxChannels ||
896 linkAuthEnabled.size() != ipmiMaxChannels ||
897 accessCallback.size() != ipmiMaxChannels)
898 {
899 log<level::ERR>("Error in reading IPMI user data file - "
900 "properties corrupted");
901 throw std::runtime_error(
902 "Corrupted IPMI user data file - properties");
903 }
904 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
905 {
906 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege =
907 static_cast<uint8_t>(
908 convertToIPMIPrivilege(privilege[chIndex]));
909 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled =
910 ipmiEnabled[chIndex];
911 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled =
912 linkAuthEnabled[chIndex];
913 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
914 accessCallback[chIndex];
915 }
916 usersTbl.user[usrIndex].userEnabled =
917 userInfo[jsonUserEnabled].get<bool>();
918 usersTbl.user[usrIndex].userInSystem =
919 userInfo[jsonUserInSys].get<bool>();
920 usersTbl.user[usrIndex].fixedUserName =
921 userInfo[jsonFixedUser].get<bool>();
922 }
923
924 log<level::DEBUG>("User data read from IPMI data file");
925 iUsrData.close();
926 // Update the timestamp
927 fileLastUpdatedTime = getUpdatedFileTime();
928 return;
929}
930
931void UserAccess::writeUserData()
932{
933 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
934 userLock{*userMutex};
935
936 static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
937 std::ofstream oUsrData(tmpFile, std::ios::out | std::ios::binary);
938 if (!oUsrData.good())
939 {
940 log<level::ERR>("Error in creating temporary IPMI user data file");
941 throw std::ios_base::failure(
942 "Error in creating temporary IPMI user data file");
943 }
944
945 Json jsonUsersTbl = Json::array();
946 // user index 0 is reserved, starts with 1
947 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
948 {
949 Json jsonUserInfo;
950 jsonUserInfo[jsonUserName] = std::string(
951 reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
952 ipmiMaxUserName);
953 std::vector<std::string> privilege(ipmiMaxChannels);
954 std::vector<bool> ipmiEnabled(ipmiMaxChannels);
955 std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
956 std::vector<bool> accessCallback(ipmiMaxChannels);
957 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
958 {
959 privilege[chIndex] =
960 convertToSystemPrivilege(static_cast<CommandPrivilege>(
961 usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege));
962 ipmiEnabled[chIndex] =
963 usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled;
964 linkAuthEnabled[chIndex] =
965 usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled;
966 accessCallback[chIndex] =
967 usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback;
968 }
969 jsonUserInfo[jsonPriv] = privilege;
970 jsonUserInfo[jsonIpmiEnabled] = ipmiEnabled;
971 jsonUserInfo[jsonLinkAuthEnabled] = linkAuthEnabled;
972 jsonUserInfo[jsonAccCallbk] = accessCallback;
973 jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
974 jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
975 jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
976 jsonUsersTbl.push_back(jsonUserInfo);
977 }
978
979 oUsrData << jsonUsersTbl;
980 oUsrData.flush();
981 oUsrData.close();
982
983 if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
984 {
985 log<level::ERR>("Error in renaming temporary IPMI user data file");
986 throw std::runtime_error("Error in renaming IPMI user data file");
987 }
988 // Update the timestamp
989 fileLastUpdatedTime = getUpdatedFileTime();
990 return;
991}
992
993bool UserAccess::addUserEntry(const std::string& userName,
994 const std::string& sysPriv, const bool& enabled)
995{
996 UsersTbl* userData = getUsersTblPtr();
997 size_t freeIndex = 0xFF;
998 // user index 0 is reserved, starts with 1
999 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
1000 {
1001 std::string curName(
1002 reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
1003 ipmiMaxUserName);
1004 if (userName == curName)
1005 {
1006 log<level::DEBUG>("User name exists",
1007 entry("USER_NAME=%s", userName.c_str()));
1008 return false; // user name exists.
1009 }
1010
1011 if ((!userData->user[usrIndex].userInSystem) &&
1012 (userData->user[usrIndex].userName[0] == '\0') &&
1013 (freeIndex == 0xFF))
1014 {
1015 freeIndex = usrIndex;
1016 }
1017 }
1018 if (freeIndex == 0xFF)
1019 {
1020 log<level::ERR>("No empty slots found");
1021 return false;
1022 }
1023 std::strncpy(reinterpret_cast<char*>(userData->user[freeIndex].userName),
1024 userName.c_str(), ipmiMaxUserName);
1025 uint8_t priv =
1026 static_cast<uint8_t>(UserAccess::convertToIPMIPrivilege(sysPriv)) &
1027 privMask;
1028 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1029 {
1030 userData->user[freeIndex].userPrivAccess[chIndex].privilege = priv;
1031 userData->user[freeIndex].userPrivAccess[chIndex].ipmiEnabled = true;
1032 userData->user[freeIndex].userPrivAccess[chIndex].linkAuthEnabled =
1033 true;
1034 userData->user[freeIndex].userPrivAccess[chIndex].accessCallback = true;
1035 }
1036 userData->user[freeIndex].userInSystem = true;
1037 userData->user[freeIndex].userEnabled = enabled;
1038
1039 return true;
1040}
1041
1042void UserAccess::deleteUserIndex(const size_t& usrIdx)
1043{
1044 UsersTbl* userData = getUsersTblPtr();
1045
1046 std::string userName(
1047 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1048 ipmiMaxUserName);
1049 ipmiClearUserEntryPassword(userName);
1050 std::fill(static_cast<uint8_t*>(userData->user[usrIdx].userName),
1051 static_cast<uint8_t*>(userData->user[usrIdx].userName) +
1052 sizeof(userData->user[usrIdx].userName),
1053 0);
1054 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1055 {
1056 userData->user[usrIdx].userPrivAccess[chIndex].privilege = privNoAccess;
1057 userData->user[usrIdx].userPrivAccess[chIndex].ipmiEnabled = false;
1058 userData->user[usrIdx].userPrivAccess[chIndex].linkAuthEnabled = false;
1059 userData->user[usrIdx].userPrivAccess[chIndex].accessCallback = false;
1060 }
1061 userData->user[usrIdx].userInSystem = false;
1062 userData->user[usrIdx].userEnabled = false;
1063 return;
1064}
1065
1066void UserAccess::checkAndReloadUserData()
1067{
1068 std::time_t updateTime = getUpdatedFileTime();
1069 if (updateTime != fileLastUpdatedTime || updateTime == -EIO)
1070 {
1071 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1072 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1073 readUserData();
1074 }
1075 return;
1076}
1077
1078UsersTbl* UserAccess::getUsersTblPtr()
1079{
1080 // reload data before using it.
1081 checkAndReloadUserData();
1082 return &usersTbl;
1083}
1084
1085void UserAccess::getSystemPrivAndGroups()
1086{
1087 std::map<std::string, PrivAndGroupType> properties;
1088 try
1089 {
1090 auto method = bus.new_method_call(
1091 getUserServiceName().c_str(), userMgrObjBasePath,
1092 dBusPropertiesInterface, getAllPropertiesMethod);
1093 method.append(userMgrInterface);
1094
1095 auto reply = bus.call(method);
1096 reply.read(properties);
1097 }
1098 catch (const sdbusplus::exception::SdBusError& e)
1099 {
1100 log<level::DEBUG>("Failed to excute method",
1101 entry("METHOD=%s", getAllPropertiesMethod),
1102 entry("PATH=%s", userMgrObjBasePath));
1103 return;
1104 }
1105 for (const auto& t : properties)
1106 {
1107 auto key = t.first;
1108 if (key == allPrivProperty)
1109 {
1110 availablePrivileges = t.second.get<std::vector<std::string>>();
1111 }
1112 else if (key == allGrpProperty)
1113 {
1114 availableGroups = t.second.get<std::vector<std::string>>();
1115 }
1116 }
1117 // TODO: Implement Supported Privilege & Groups verification logic
1118 return;
1119}
1120
1121std::time_t UserAccess::getUpdatedFileTime()
1122{
1123 struct stat fileStat;
1124 if (stat(ipmiUserDataFile, &fileStat) != 0)
1125 {
1126 log<level::DEBUG>("Error in getting last updated time stamp");
1127 return -EIO;
1128 }
1129 return fileStat.st_mtime;
1130}
1131
1132void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
1133 std::vector<std::string>& usrGrps,
1134 std::string& usrPriv, bool& usrEnabled)
1135{
1136 for (const auto& t : properties)
1137 {
1138 std::string key = t.first;
1139 if (key == userPrivProperty)
1140 {
1141 usrPriv = t.second.get<std::string>();
1142 }
1143 else if (key == userGrpProperty)
1144 {
1145 usrGrps = t.second.get<std::vector<std::string>>();
1146 }
1147 else if (key == userEnabledProperty)
1148 {
1149 usrEnabled = t.second.get<bool>();
1150 }
1151 }
1152 return;
1153}
1154
1155int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
1156 std::vector<std::string>& usrGrps,
1157 std::string& usrPriv, bool& usrEnabled)
1158{
1159 auto usrObj = userObjs.find(usersInterface);
1160 if (usrObj != userObjs.end())
1161 {
1162 getUserProperties(usrObj->second, usrGrps, usrPriv, usrEnabled);
1163 return 0;
1164 }
1165 return -EIO;
1166}
1167
1168void UserAccess::initUserDataFile()
1169{
1170 boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1171 userLock{*userMutex};
1172 try
1173 {
1174 readUserData();
1175 }
1176 catch (const std::ios_base::failure& e)
1177 { // File is empty, create it for the first time
1178 std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
1179 reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
1180 // user index 0 is reserved, starts with 1
1181 for (size_t userIndex = 1; userIndex <= ipmiMaxUsers; ++userIndex)
1182 {
1183 for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
1184 {
1185 usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
1186 privNoAccess;
1187 }
1188 }
1189 writeUserData();
1190 }
1191 std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
1192 try
1193 {
1194 auto method = bus.new_method_call(getUserServiceName().c_str(),
1195 userMgrObjBasePath, dBusObjManager,
1196 getManagedObjectsMethod);
1197 auto reply = bus.call(method);
1198 reply.read(managedObjs);
1199 }
1200 catch (const sdbusplus::exception::SdBusError& e)
1201 {
1202 log<level::DEBUG>("Failed to excute method",
1203 entry("METHOD=%s", getSubTreeMethod),
1204 entry("PATH=%s", userMgrObjBasePath));
1205 return;
1206 }
1207
1208 UsersTbl* userData = &usersTbl;
1209 // user index 0 is reserved, starts with 1
1210 for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
1211 {
1212 if ((userData->user[usrIdx].userInSystem) &&
1213 (userData->user[usrIdx].userName[0] != '\0'))
1214 {
1215 std::vector<std::string> usrGrps;
1216 std::string usrPriv;
1217 bool usrEnabled;
1218
1219 std::string userName(
1220 reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
1221 ipmiMaxUserName);
1222 std::string usersPath =
1223 std::string(userObjBasePath) + "/" + userName;
1224
1225 auto usrObj = managedObjs.find(usersPath);
1226 if (usrObj != managedObjs.end())
1227 {
1228 // User exist. Lets check and update other fileds
1229 getUserObjProperties(usrObj->second, usrGrps, usrPriv,
1230 usrEnabled);
1231 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
1232 usrGrps.end())
1233 {
1234 // Group "ipmi" is removed so lets remove user in IPMI
1235 deleteUserIndex(usrIdx);
1236 }
1237 else
1238 {
1239 // Group "ipmi" is present so lets update other properties
1240 // in IPMI
1241 uint8_t priv =
1242 UserAccess::convertToIPMIPrivilege(usrPriv) & privMask;
1243 // Update all channels priv, only if it is not equivalent to
1244 // getUsrMgmtSyncIndex()
1245 if (userData->user[usrIdx]
1246 .userPrivAccess[getUsrMgmtSyncIndex()]
1247 .privilege != priv)
1248 {
1249 for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
1250 ++chIndex)
1251 {
1252 userData->user[usrIdx]
1253 .userPrivAccess[chIndex]
1254 .privilege = priv;
1255 }
1256 }
1257 if (userData->user[usrIdx].userEnabled != usrEnabled)
1258 {
1259 userData->user[usrIdx].userEnabled = usrEnabled;
1260 }
1261 }
1262
1263 // We are done with this obj. lets delete from MAP
1264 managedObjs.erase(usrObj);
1265 }
1266 else
1267 {
1268 deleteUserIndex(usrIdx);
1269 }
1270 }
1271 }
1272
1273 // Walk through remnaining managedObj users list
1274 // Add them to ipmi data base
1275 for (const auto& usrObj : managedObjs)
1276 {
1277 std::vector<std::string> usrGrps;
1278 std::string usrPriv, userName;
1279 bool usrEnabled;
1280 std::string usrObjPath = std::string(usrObj.first);
1281 if (getUserNameFromPath(usrObj.first.str, userName) != 0)
1282 {
1283 log<level::ERR>("Error in user object path");
1284 continue;
1285 }
1286 getUserObjProperties(usrObj.second, usrGrps, usrPriv, usrEnabled);
1287 // Add 'ipmi' group users
1288 if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
1289 usrGrps.end())
1290 {
1291 // CREATE NEW USER
1292 if (true != addUserEntry(userName, usrPriv, usrEnabled))
1293 {
1294 break;
1295 }
1296 }
1297 }
1298
1299 // All userData slots update done. Lets write the data
1300 writeUserData();
1301
1302 return;
1303}
1304} // namespace ipmi