summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--apphandler.cpp3
-rw-r--r--user_channel/user_layer.cpp112
-rw-r--r--user_channel/user_layer.hpp143
-rw-r--r--user_channel/user_mgmt.cpp1304
-rw-r--r--user_channel/user_mgmt.hpp295
-rw-r--r--user_channel/usercommands.cpp473
-rw-r--r--user_channel/usercommands.hpp36
8 files changed, 2361 insertions, 7 deletions
diff --git a/Makefile.am b/Makefile.am
index 7be3bc9..5e03648 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -66,6 +66,7 @@ libuserlayerdir = ${libdir}
libuserlayer_LTLIBRARIES = libuserlayer.la
libuserlayer_la_SOURCES = \
user_channel/user_layer.cpp \
+ user_channel/user_mgmt.cpp \
user_channel/passwd_mgr.cpp
libuserlayer_la_LDFLAGS = $(SYSTEMD_LIBS) $(libmapper_LIBS) \
@@ -97,6 +98,7 @@ libipmi20_la_SOURCES = \
ipmi_fru_info_area.cpp \
read_fru_data.cpp \
sensordatahandler.cpp \
+ user_channel/usercommands.cpp \
$(libipmi20_BUILT_LIST)
@CODE_COVERAGE_RULES@
diff --git a/apphandler.cpp b/apphandler.cpp
index fd58218..ac06f50 100644
--- a/apphandler.cpp
+++ b/apphandler.cpp
@@ -6,6 +6,7 @@
#include "sys_info_param.hpp"
#include "transporthandler.hpp"
#include "types.hpp"
+#include "user_channel/usercommands.hpp"
#include "utils.hpp"
#include <arpa/inet.h>
@@ -874,7 +875,6 @@ void register_netfn_app_functions()
// <Get Channel Cipher Suites Command>
ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
getChannelCipherSuites, PRIVILEGE_CALLBACK);
-
// <Set Channel Access Command>
ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHAN_ACCESS, NULL,
ipmi_set_channel_access, PRIVILEGE_ADMIN);
@@ -882,5 +882,6 @@ void register_netfn_app_functions()
// <Get System Info Command>
ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
ipmi_app_get_system_info, PRIVILEGE_USER);
+ ipmi::registerUserIpmiFunctions();
return;
}
diff --git a/user_channel/user_layer.cpp b/user_channel/user_layer.cpp
index 06cdd68..3e4490c 100644
--- a/user_channel/user_layer.cpp
+++ b/user_channel/user_layer.cpp
@@ -17,6 +17,8 @@
#include "user_layer.hpp"
#include "passwd_mgr.hpp"
+#include "user_mgmt.hpp"
+
namespace
{
ipmi::PasswdMgr passwdMgr;
@@ -24,6 +26,13 @@ ipmi::PasswdMgr passwdMgr;
namespace ipmi
{
+
+ipmi_ret_t ipmiUserInit()
+{
+ getUserAccessObject();
+ return IPMI_CC_OK;
+}
+
std::string ipmiUserGetPassword(const std::string& userName)
{
return passwdMgr.getPasswdByUserName(userName);
@@ -48,4 +57,107 @@ ipmi_ret_t ipmiRenameUserEntryPassword(const std::string& userName,
return IPMI_CC_OK;
}
+bool ipmiUserIsValidUserId(const uint8_t& userId)
+{
+ return UserAccess::isValidUserId(userId);
+}
+
+bool ipmiUserIsValidChannel(const uint8_t& chNum)
+{
+ return UserAccess::isValidChannel(chNum);
+}
+
+bool ipmiUserIsValidPrivilege(const uint8_t& priv)
+{
+ return UserAccess::isValidPrivilege(priv);
+}
+
+uint8_t ipmiUserGetUserId(const std::string& userName)
+{
+ return getUserAccessObject().getUserId(userName);
+}
+
+ipmi_ret_t ipmiUserSetUserName(const uint8_t& userId, const char* userName)
+{
+ return getUserAccessObject().setUserName(userId, userName);
+}
+
+ipmi_ret_t ipmiUserGetUserName(const uint8_t& userId, std::string& userName)
+{
+ return getUserAccessObject().getUserName(userId, userName);
+}
+
+ipmi_ret_t ipmiUserGetAllCounts(uint8_t& maxChUsers, uint8_t& enabledUsers,
+ uint8_t& fixedUsers)
+{
+ maxChUsers = ipmiMaxUsers;
+ UsersTbl* userData = getUserAccessObject().getUsersTblPtr();
+ enabledUsers = 0;
+ fixedUsers = 0;
+ // user index 0 is reserved, starts with 1
+ for (size_t count = 1; count <= ipmiMaxUsers; ++count)
+ {
+ if (userData->user[count].userEnabled)
+ {
+ enabledUsers++;
+ }
+ if (userData->user[count].fixedUserName)
+ {
+ fixedUsers++;
+ }
+ }
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiUserCheckEnabled(const uint8_t& userId, bool& state)
+{
+ if (!UserAccess::isValidUserId(userId))
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ UserInfo* userInfo = getUserAccessObject().getUserInfo(userId);
+ state = userInfo->userEnabled;
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiUserGetPrivilegeAccess(const uint8_t& userId,
+ const uint8_t& chNum,
+ PrivAccess& privAccess)
+{
+
+ if (!UserAccess::isValidChannel(chNum))
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (!UserAccess::isValidUserId(userId))
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ UserInfo* userInfo = getUserAccessObject().getUserInfo(userId);
+ privAccess.privilege = userInfo->userPrivAccess[chNum].privilege;
+ privAccess.ipmiEnabled = userInfo->userPrivAccess[chNum].ipmiEnabled;
+ privAccess.linkAuthEnabled =
+ userInfo->userPrivAccess[chNum].linkAuthEnabled;
+ privAccess.accessCallback = userInfo->userPrivAccess[chNum].accessCallback;
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiUserSetPrivilegeAccess(const uint8_t& userId,
+ const uint8_t& chNum,
+ const PrivAccess& privAccess,
+ const bool& otherPrivUpdates)
+{
+ UserPrivAccess userPrivAccess;
+ userPrivAccess.privilege = privAccess.privilege;
+ if (otherPrivUpdates)
+ {
+ userPrivAccess.ipmiEnabled = privAccess.ipmiEnabled;
+ userPrivAccess.linkAuthEnabled = privAccess.linkAuthEnabled;
+ userPrivAccess.accessCallback = privAccess.accessCallback;
+ }
+ return getUserAccessObject().setUserPrivilegeAccess(
+ userId, chNum, userPrivAccess, otherPrivUpdates);
+}
+
} // namespace ipmi
diff --git a/user_channel/user_layer.hpp b/user_channel/user_layer.hpp
index a946e4c..5136e86 100644
--- a/user_channel/user_layer.hpp
+++ b/user_channel/user_layer.hpp
@@ -20,9 +20,46 @@
namespace ipmi
{
+
+// TODO: Has to be replaced with proper channel number assignment logic
+enum class EChannelID : uint8_t
+{
+ chanLan1 = 0x01
+};
+
+static constexpr uint8_t invalidUserId = 0xFF;
+static constexpr uint8_t reservedUserId = 0x0;
+static constexpr uint8_t ipmiMaxUserName = 16;
+static constexpr uint8_t ipmiMaxUsers = 15;
+static constexpr uint8_t ipmiMaxChannels = 16;
+
+struct PrivAccess
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t privilege : 4;
+ uint8_t ipmiEnabled : 1;
+ uint8_t linkAuthEnabled : 1;
+ uint8_t accessCallback : 1;
+ uint8_t reserved : 1;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ uint8_t reserved : 1;
+ uint8_t accessCallback : 1;
+ uint8_t linkAuthEnabled : 1;
+ uint8_t ipmiEnabled : 1;
+ uint8_t privilege : 4;
+#endif
+} __attribute__((packed));
+
+/** @brief initializes user management
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserInit();
+
/** @brief The ipmi get user password layer call
*
- * @param[in] userName
+ * @param[in] userName - user name
*
* @return password or empty string
*/
@@ -31,7 +68,7 @@ std::string ipmiUserGetPassword(const std::string& userName);
/** @brief The IPMI call to clear password entry associated with specified
* username
*
- * @param[in] userName
+ * @param[in] userName - user name to be removed
*
* @return 0 on success, non-zero otherwise.
*/
@@ -40,14 +77,108 @@ ipmi_ret_t ipmiClearUserEntryPassword(const std::string& userName);
/** @brief The IPMI call to reuse password entry for the renamed user
* to another one
*
- * @param[in] userName
- * @param[in] newUserName
+ * @param[in] userName - user name which has to be renamed
+ * @param[in] newUserName - new user name
*
* @return 0 on success, non-zero otherwise.
*/
ipmi_ret_t ipmiRenameUserEntryPassword(const std::string& userName,
const std::string& newUserName);
-// TODO: Define required user layer API Call's which user layer shared library
-// must implement.
+/** @brief determines valid userId
+ *
+ * @param[in] userId - user id
+ *
+ * @return true if valid, false otherwise
+ */
+bool ipmiUserIsValidUserId(const uint8_t& userId);
+
+/** @brief determines valid channel
+ *
+ * @param[in] chNum- channel number
+ *
+ * @return true if valid, false otherwise
+ */
+bool ipmiUserIsValidChannel(const uint8_t& chNum);
+
+/** @brief determines valid privilege level
+ *
+ * @param[in] priv - privilege level
+ *
+ * @return true if valid, false otherwise
+ */
+bool ipmiUserIsValidPrivilege(const uint8_t& priv);
+
+/** @brief get user id corresponding to the user name
+ *
+ * @param[in] userName - user name
+ *
+ * @return userid. Will return 0xff if no user id found
+ */
+uint8_t ipmiUserGetUserId(const std::string& userName);
+
+/** @brief set's user name
+ *
+ * @param[in] userId - user id
+ * @param[in] userName - user name
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserSetUserName(const uint8_t& userId, const char* userName);
+
+/** @brief get user name
+ *
+ * @param[in] userId - user id
+ * @param[out] userName - user name
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserGetUserName(const uint8_t& userId, std::string& userName);
+
+/** @brief provides available fixed, max, and enabled user counts
+ *
+ * @param[out] maxChUsers - max channel users
+ * @param[out] enabledUsers - enabled user count
+ * @param[out] fixedUsers - fixed user count
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserGetAllCounts(uint8_t& maxChUsers, uint8_t& enabledUsers,
+ uint8_t& fixedUsers);
+
+/** @brief determines whether user is enabled
+ *
+ * @param[in] userId - user id
+ *..@param[out] state - state of the user
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserCheckEnabled(const uint8_t& userId, bool& state);
+
+/** @brief provides user privilege access data
+ *
+ * @param[in] userId - user id
+ * @param[in] chNum - channel number
+ * @param[out] privAccess - privilege access data
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserGetPrivilegeAccess(const uint8_t& userId,
+ const uint8_t& chNum,
+ PrivAccess& privAccess);
+
+/** @brief sets user privilege access data
+ *
+ * @param[in] userId - user id
+ * @param[in] chNum - channel number
+ * @param[in] privAccess - privilege access data
+ * @param[in] otherPrivUpdate - flags to indicate other fields update
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserSetPrivilegeAccess(const uint8_t& userId,
+ const uint8_t& chNum,
+ const PrivAccess& privAccess,
+ const bool& otherPrivUpdate);
+
} // namespace ipmi
diff --git a/user_channel/user_mgmt.cpp b/user_channel/user_mgmt.cpp
new file mode 100644
index 0000000..e320e3f
--- /dev/null
+++ b/user_channel/user_mgmt.cpp
@@ -0,0 +1,1304 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#include "user_mgmt.hpp"
+
+#include "apphandler.hpp"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <boost/interprocess/sync/named_recursive_mutex.hpp>
+#include <boost/interprocess/sync/scoped_lock.hpp>
+#include <cerrno>
+#include <fstream>
+#include <host-ipmid/ipmid-host-cmd.hpp>
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <regex>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/User/Common/error.hpp>
+
+namespace ipmi
+{
+
+// TODO: Move D-Bus & Object Manager related stuff, to common files
+// D-Bus property related
+static constexpr const char* dBusPropertiesInterface =
+ "org.freedesktop.DBus.Properties";
+static constexpr const char* getAllPropertiesMethod = "GetAll";
+static constexpr const char* propertiesChangedSignal = "PropertiesChanged";
+static constexpr const char* setPropertiesMethod = "Set";
+
+// Object Manager related
+static constexpr const char* dBusObjManager =
+ "org.freedesktop.DBus.ObjectManager";
+static constexpr const char* getManagedObjectsMethod = "GetManagedObjects";
+// Object Manager signals
+static constexpr const char* intfAddedSignal = "InterfacesAdded";
+static constexpr const char* intfRemovedSignal = "InterfacesRemoved";
+
+// Object Mapper related
+static constexpr const char* objMapperService =
+ "xyz.openbmc_project.ObjectMapper";
+static constexpr const char* objMapperPath =
+ "/xyz/openbmc_project/object_mapper";
+static constexpr const char* objMapperInterface =
+ "xyz.openbmc_project.ObjectMapper";
+static constexpr const char* getSubTreeMethod = "GetSubTree";
+static constexpr const char* getObjectMethod = "GetObject";
+
+static constexpr const char* ipmiUserMutex = "ipmi_usr_mutex";
+static constexpr const char* ipmiMutexCleanupLockFile =
+ "/var/lib/ipmi/ipmi_usr_mutex_cleanup";
+static constexpr const char* ipmiUserDataFile = "/var/lib/ipmi/ipmi_user.json";
+static constexpr const char* ipmiGrpName = "ipmi";
+static constexpr size_t privNoAccess = 0xF;
+static constexpr size_t privMask = 0xF;
+
+// User manager related
+static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user";
+static constexpr const char* userObjBasePath = "/xyz/openbmc_project/user";
+static constexpr const char* userMgrInterface =
+ "xyz.openbmc_project.User.Manager";
+static constexpr const char* usersInterface =
+ "xyz.openbmc_project.User.Attributes";
+static constexpr const char* deleteUserInterface =
+ "xyz.openbmc_project.Object.Delete";
+
+static constexpr const char* createUserMethod = "CreateUser";
+static constexpr const char* deleteUserMethod = "Delete";
+static constexpr const char* renameUserMethod = "RenameUser";
+// User manager signal memebers
+static constexpr const char* userRenamedSignal = "UserRenamed";
+// Mgr interface properties
+static constexpr const char* allPrivProperty = "AllPrivileges";
+static constexpr const char* allGrpProperty = "AllGroups";
+// User interface properties
+static constexpr const char* userPrivProperty = "UserPrivilege";
+static constexpr const char* userGrpProperty = "UserGroups";
+static constexpr const char* userEnabledProperty = "UserEnabled";
+
+static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = {
+ "priv-reserved", // PRIVILEGE_RESERVED - 0
+ "priv-callback", // PRIVILEGE_CALLBACK - 1
+ "priv-user", // PRIVILEGE_USER - 2
+ "priv-operator", // PRIVILEGE_OPERATOR - 3
+ "priv-admin", // PRIVILEGE_ADMIN - 4
+ "priv-custom" // PRIVILEGE_OEM - 5
+};
+
+using namespace phosphor::logging;
+using Json = nlohmann::json;
+
+using PrivAndGroupType =
+ sdbusplus::message::variant<std::string, std::vector<std::string>>;
+
+using NoResource =
+ sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource;
+
+using InternalFailure =
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+std::unique_ptr<sdbusplus::bus::match_t> userUpdatedSignal(nullptr);
+std::unique_ptr<sdbusplus::bus::match_t> userMgrRenamedSignal(nullptr);
+std::unique_ptr<sdbusplus::bus::match_t> userPropertiesSignal(nullptr);
+
+// TODO: Below code can be removed once it is moved to common layer libmiscutil
+std::string getUserService(sdbusplus::bus::bus& bus, const std::string& intf,
+ const std::string& path)
+{
+ auto mapperCall = bus.new_method_call(objMapperService, objMapperPath,
+ objMapperInterface, getObjectMethod);
+
+ mapperCall.append(path);
+ mapperCall.append(std::vector<std::string>({intf}));
+
+ auto mapperResponseMsg = bus.call(mapperCall);
+
+ std::map<std::string, std::vector<std::string>> mapperResponse;
+ mapperResponseMsg.read(mapperResponse);
+
+ if (mapperResponse.begin() == mapperResponse.end())
+ {
+ throw sdbusplus::exception::SdBusError(
+ -EIO, "ERROR in reading the mapper response");
+ }
+
+ return mapperResponse.begin()->first;
+}
+
+void setDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath, const std::string& interface,
+ const std::string& property,
+ const DbusUserPropVariant& value)
+{
+ try
+ {
+ auto method =
+ bus.new_method_call(service.c_str(), objPath.c_str(),
+ dBusPropertiesInterface, setPropertiesMethod);
+ method.append(interface, property, value);
+ bus.call(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Failed to set property",
+ entry("PROPERTY=%s", property.c_str()),
+ entry("PATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ throw;
+ }
+}
+
+static std::string getUserServiceName()
+{
+ static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+ static std::string userMgmtService;
+ if (userMgmtService.empty())
+ {
+ try
+ {
+ userMgmtService =
+ ipmi::getUserService(bus, userMgrInterface, userMgrObjBasePath);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ userMgmtService.clear();
+ }
+ }
+ return userMgmtService;
+}
+
+UserAccess& getUserAccessObject()
+{
+ static UserAccess userAccess;
+ return userAccess;
+}
+
+int getUserNameFromPath(const std::string& path, std::string& userName)
+{
+ static size_t pos = strlen(userObjBasePath) + 1;
+ if (path.find(userObjBasePath) == std::string::npos)
+ {
+ return -EINVAL;
+ }
+ userName.assign(path, pos, path.size());
+ return 0;
+}
+
+void userUpdateHelper(UserAccess& usrAccess, const UserUpdateEvent& userEvent,
+ const std::string& userName, const std::string& priv,
+ const bool& enabled, const std::string& newUserName)
+{
+ UsersTbl* userData = usrAccess.getUsersTblPtr();
+ if (userEvent == UserUpdateEvent::userCreated)
+ {
+ if (usrAccess.addUserEntry(userName, priv, enabled) == false)
+ {
+ return;
+ }
+ }
+ else
+ {
+ // user index 0 is reserved, starts with 1
+ size_t usrIndex = 1;
+ for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
+ {
+ std::string curName(
+ reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
+ ipmiMaxUserName);
+ if (userName == curName)
+ {
+ break; // found the entry
+ }
+ }
+ if (usrIndex > ipmiMaxUsers)
+ {
+ log<level::DEBUG>("User not found for signal",
+ entry("USER_NAME=%s", userName.c_str()),
+ entry("USER_EVENT=%d", userEvent));
+ return;
+ }
+ switch (userEvent)
+ {
+ case UserUpdateEvent::userDeleted:
+ {
+ usrAccess.deleteUserIndex(usrIndex);
+ break;
+ }
+ case UserUpdateEvent::userPrivUpdated:
+ {
+ uint8_t userPriv =
+ static_cast<uint8_t>(
+ UserAccess::convertToIPMIPrivilege(priv)) &
+ privMask;
+ // Update all channels privileges, only if it is not equivalent
+ // to getUsrMgmtSyncIndex()
+ if (userData->user[usrIndex]
+ .userPrivAccess[UserAccess::getUsrMgmtSyncIndex()]
+ .privilege != userPriv)
+ {
+ for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
+ ++chIndex)
+ {
+ userData->user[usrIndex]
+ .userPrivAccess[chIndex]
+ .privilege = userPriv;
+ }
+ }
+ break;
+ }
+ case UserUpdateEvent::userRenamed:
+ {
+ std::fill(
+ static_cast<uint8_t*>(userData->user[usrIndex].userName),
+ static_cast<uint8_t*>(userData->user[usrIndex].userName) +
+ sizeof(userData->user[usrIndex].userName),
+ 0);
+ std::strncpy(
+ reinterpret_cast<char*>(userData->user[usrIndex].userName),
+ newUserName.c_str(), ipmiMaxUserName);
+ ipmiRenameUserEntryPassword(userName, newUserName);
+ break;
+ }
+ case UserUpdateEvent::userStateUpdated:
+ {
+ userData->user[usrIndex].userEnabled = enabled;
+ break;
+ }
+ default:
+ {
+ log<level::ERR>("Unhandled user event",
+ entry("USER_EVENT=%d", userEvent));
+ return;
+ }
+ }
+ }
+ usrAccess.writeUserData();
+ log<level::DEBUG>("User event handled successfully",
+ entry("USER_NAME=%s", userName.c_str()),
+ entry("USER_EVENT=%d", userEvent));
+
+ return;
+}
+
+void userUpdatedSignalHandler(UserAccess& usrAccess,
+ sdbusplus::message::message& msg)
+{
+ static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+ std::string signal = msg.get_member();
+ std::string userName, update, priv, newUserName;
+ std::vector<std::string> groups;
+ bool enabled = false;
+ UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent;
+ if (signal == intfAddedSignal)
+ {
+ DbusUserObjPath objPath;
+ DbusUserObjValue objValue;
+ msg.read(objPath, objValue);
+ getUserNameFromPath(objPath.str, userName);
+ if (usrAccess.getUserObjProperties(objValue, groups, priv, enabled) !=
+ 0)
+ {
+ return;
+ }
+ if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
+ groups.end())
+ {
+ return;
+ }
+ userEvent = UserUpdateEvent::userCreated;
+ }
+ else if (signal == intfRemovedSignal)
+ {
+ DbusUserObjPath objPath;
+ std::vector<std::string> interfaces;
+ msg.read(objPath, interfaces);
+ getUserNameFromPath(objPath.str, userName);
+ userEvent = UserUpdateEvent::userDeleted;
+ }
+ else if (signal == userRenamedSignal)
+ {
+ msg.read(userName, newUserName);
+ userEvent = UserUpdateEvent::userRenamed;
+ }
+ else if (signal == propertiesChangedSignal)
+ {
+ getUserNameFromPath(msg.get_path(), userName);
+ }
+ else
+ {
+ log<level::ERR>("Unknown user update signal",
+ entry("SIGNAL=%s", signal.c_str()));
+ return;
+ }
+
+ if (signal.empty() || userName.empty() ||
+ (signal == userRenamedSignal && newUserName.empty()))
+ {
+ log<level::ERR>("Invalid inputs received");
+ return;
+ }
+
+ boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+ userLock{*(usrAccess.userMutex)};
+ usrAccess.checkAndReloadUserData();
+
+ if (signal == propertiesChangedSignal)
+ {
+ std::string intfName;
+ DbusUserObjProperties chProperties;
+ msg.read(intfName, chProperties); // skip reading 3rd argument.
+ for (const auto& prop : chProperties)
+ {
+ userEvent = UserUpdateEvent::reservedEvent;
+ std::string member = prop.first;
+ if (member == userPrivProperty)
+ {
+ priv = prop.second.get<std::string>();
+ userEvent = UserUpdateEvent::userPrivUpdated;
+ }
+ else if (member == userGrpProperty)
+ {
+ groups = prop.second.get<std::vector<std::string>>();
+ userEvent = UserUpdateEvent::userGrpUpdated;
+ }
+ else if (member == userEnabledProperty)
+ {
+ enabled = prop.second.get<bool>();
+ userEvent = UserUpdateEvent::userStateUpdated;
+ }
+ // Process based on event type.
+ if (userEvent == UserUpdateEvent::userGrpUpdated)
+ {
+ if (std::find(groups.begin(), groups.end(), ipmiGrpName) ==
+ groups.end())
+ {
+ // remove user from ipmi user list.
+ userUpdateHelper(usrAccess, UserUpdateEvent::userDeleted,
+ userName, priv, enabled, newUserName);
+ }
+ else
+ {
+ DbusUserObjProperties properties;
+ try
+ {
+ auto method = bus.new_method_call(
+ getUserServiceName().c_str(), msg.get_path(),
+ dBusPropertiesInterface, getAllPropertiesMethod);
+ method.append(usersInterface);
+ auto reply = bus.call(method);
+ reply.read(properties);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::DEBUG>(
+ "Failed to excute method",
+ entry("METHOD=%s", getAllPropertiesMethod),
+ entry("PATH=%s", msg.get_path()));
+ return;
+ }
+ usrAccess.getUserProperties(properties, groups, priv,
+ enabled);
+ // add user to ipmi user list.
+ userUpdateHelper(usrAccess, UserUpdateEvent::userCreated,
+ userName, priv, enabled, newUserName);
+ }
+ }
+ else if (userEvent != UserUpdateEvent::reservedEvent)
+ {
+ userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
+ newUserName);
+ }
+ }
+ }
+ else if (userEvent != UserUpdateEvent::reservedEvent)
+ {
+ userUpdateHelper(usrAccess, userEvent, userName, priv, enabled,
+ newUserName);
+ }
+ return;
+}
+
+UserAccess::~UserAccess()
+{
+ if (signalHndlrObject)
+ {
+ userUpdatedSignal.reset();
+ userMgrRenamedSignal.reset();
+ userPropertiesSignal.reset();
+ sigHndlrLock.unlock();
+ }
+}
+
+UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection())
+{
+ std::ofstream mutexCleanUpFile;
+ mutexCleanUpFile.open(ipmiMutexCleanupLockFile,
+ std::ofstream::out | std::ofstream::app);
+ if (!mutexCleanUpFile.good())
+ {
+ log<level::DEBUG>("Unable to open mutex cleanup file");
+ return;
+ }
+ mutexCleanUpFile.close();
+ mutexCleanupLock = boost::interprocess::file_lock(ipmiMutexCleanupLockFile);
+ if (mutexCleanupLock.try_lock())
+ {
+ boost::interprocess::named_recursive_mutex::remove(ipmiUserMutex);
+ }
+ mutexCleanupLock.lock_sharable();
+ userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>(
+ boost::interprocess::open_or_create, ipmiUserMutex);
+
+ initUserDataFile();
+ getSystemPrivAndGroups();
+ sigHndlrLock = boost::interprocess::file_lock(ipmiUserDataFile);
+ // Register it for single object and single process either netipimd /
+ // host-ipmid
+ if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
+ {
+ log<level::DEBUG>("Registering signal handler");
+ userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::type::signal() +
+ sdbusplus::bus::match::rules::interface(dBusObjManager) +
+ sdbusplus::bus::match::rules::path(userMgrObjBasePath),
+ [&](sdbusplus::message::message& msg) {
+ userUpdatedSignalHandler(*this, msg);
+ });
+ userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::type::signal() +
+ sdbusplus::bus::match::rules::interface(userMgrInterface) +
+ sdbusplus::bus::match::rules::path(userMgrObjBasePath),
+ [&](sdbusplus::message::message& msg) {
+ userUpdatedSignalHandler(*this, msg);
+ });
+ userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::type::signal() +
+ sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
+ sdbusplus::bus::match::rules::interface(
+ dBusPropertiesInterface) +
+ sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
+ sdbusplus::bus::match::rules::argN(0, usersInterface),
+ [&](sdbusplus::message::message& msg) {
+ userUpdatedSignalHandler(*this, msg);
+ });
+ signalHndlrObject = true;
+ }
+}
+
+UserInfo* UserAccess::getUserInfo(const uint8_t& userId)
+{
+ checkAndReloadUserData();
+ return &usersTbl.user[userId];
+}
+
+void UserAccess::setUserInfo(const uint8_t& userId, UserInfo* userInfo)
+{
+ checkAndReloadUserData();
+ std::copy(reinterpret_cast<uint8_t*>(userInfo),
+ reinterpret_cast<uint8_t*>(userInfo) + sizeof(*userInfo),
+ reinterpret_cast<uint8_t*>(&usersTbl.user[userId]));
+ writeUserData();
+}
+
+bool UserAccess::isValidChannel(const uint8_t& chNum)
+{
+ return (chNum < ipmiMaxChannels);
+}
+
+bool UserAccess::isValidUserId(const uint8_t& userId)
+{
+ return ((userId <= ipmiMaxUsers) && (userId != reservedUserId));
+}
+
+bool UserAccess::isValidPrivilege(const uint8_t& priv)
+{
+ return ((priv >= PRIVILEGE_CALLBACK && priv <= PRIVILEGE_OEM) ||
+ priv == privNoAccess);
+}
+
+uint8_t UserAccess::getUsrMgmtSyncIndex()
+{
+ // TODO: Need to get LAN1 channel number dynamically,
+ // which has to be in sync with system user privilege
+ // level(Phosphor-user-manager). Note: For time being chanLan1 is marked as
+ // sync index to the user-manager privilege..
+ return static_cast<uint8_t>(EChannelID::chanLan1);
+}
+
+CommandPrivilege UserAccess::convertToIPMIPrivilege(const std::string& value)
+{
+ auto iter = std::find(ipmiPrivIndex.begin(), ipmiPrivIndex.end(), value);
+ if (iter == ipmiPrivIndex.end())
+ {
+ if (value == "")
+ {
+ return static_cast<CommandPrivilege>(privNoAccess);
+ }
+ log<level::ERR>("Error in converting to IPMI privilege",
+ entry("PRIV=%s", value.c_str()));
+ throw std::out_of_range("Out of range - convertToIPMIPrivilege");
+ }
+ else
+ {
+ return static_cast<CommandPrivilege>(
+ std::distance(ipmiPrivIndex.begin(), iter));
+ }
+}
+
+std::string UserAccess::convertToSystemPrivilege(const CommandPrivilege& value)
+{
+ if (value == static_cast<CommandPrivilege>(privNoAccess))
+ {
+ return "";
+ }
+ try
+ {
+ return ipmiPrivIndex.at(value);
+ }
+ catch (const std::out_of_range& e)
+ {
+ log<level::ERR>("Error in converting to system privilege",
+ entry("PRIV=%d", static_cast<uint8_t>(value)));
+ throw std::out_of_range("Out of range - convertToSystemPrivilege");
+ }
+}
+
+bool UserAccess::isValidUserName(const char* userNameInChar)
+{
+ if (!userNameInChar)
+ {
+ log<level::ERR>("null ptr");
+ return false;
+ }
+ std::string userName(userNameInChar, 0, ipmiMaxUserName);
+ if (!std::regex_match(userName.c_str(),
+ std::regex("[a-zA-z_][a-zA-Z_0-9]*")))
+ {
+ log<level::ERR>("Unsupported characters in user name");
+ return false;
+ }
+ if (userName == "root")
+ {
+ log<level::ERR>("Invalid user name - root");
+ return false;
+ }
+ std::map<DbusUserObjPath, DbusUserObjValue> properties;
+ try
+ {
+ auto method = bus.new_method_call(getUserServiceName().c_str(),
+ userMgrObjBasePath, dBusObjManager,
+ getManagedObjectsMethod);
+ auto reply = bus.call(method);
+ reply.read(properties);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Failed to excute method",
+ entry("METHOD=%s", getSubTreeMethod),
+ entry("PATH=%s", userMgrObjBasePath));
+ return false;
+ }
+
+ std::string usersPath = std::string(userObjBasePath) + "/" + userName;
+ if (properties.find(usersPath) != properties.end())
+ {
+ log<level::DEBUG>("User name already exists",
+ entry("USER_NAME=%s", userName.c_str()));
+ return false;
+ }
+
+ return true;
+}
+
+ipmi_ret_t UserAccess::setUserPrivilegeAccess(const uint8_t& userId,
+ const uint8_t& chNum,
+ const UserPrivAccess& privAccess,
+ const bool& otherPrivUpdates)
+{
+ if (!isValidChannel(chNum))
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (!isValidUserId(userId))
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+ userLock{*userMutex};
+ UserInfo* userInfo = getUserInfo(userId);
+ std::string userName;
+ userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
+ ipmiMaxUserName);
+ if (userName.empty())
+ {
+ log<level::DEBUG>("User name not set / invalid");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ std::string priv = convertToSystemPrivilege(
+ static_cast<CommandPrivilege>(privAccess.privilege));
+ if (priv.empty())
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ uint8_t syncIndex = getUsrMgmtSyncIndex();
+ if (chNum == syncIndex &&
+ privAccess.privilege != userInfo->userPrivAccess[syncIndex].privilege)
+ {
+ std::string userPath = std::string(userObjBasePath) + "/" + userName;
+ setDbusProperty(bus, getUserServiceName().c_str(), userPath.c_str(),
+ usersInterface, userPrivProperty, priv);
+ }
+ userInfo->userPrivAccess[chNum].privilege = privAccess.privilege;
+
+ if (otherPrivUpdates)
+ {
+ userInfo->userPrivAccess[chNum].ipmiEnabled = privAccess.ipmiEnabled;
+ userInfo->userPrivAccess[chNum].linkAuthEnabled =
+ privAccess.linkAuthEnabled;
+ userInfo->userPrivAccess[chNum].accessCallback =
+ privAccess.accessCallback;
+ }
+ try
+ {
+ writeUserData();
+ }
+ catch (const std::exception& e)
+ {
+ log<level::DEBUG>("Write user data failed");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ return IPMI_CC_OK;
+}
+
+uint8_t UserAccess::getUserId(const std::string& userName)
+{
+ boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+ userLock{*userMutex};
+ checkAndReloadUserData();
+ // user index 0 is reserved, starts with 1
+ size_t usrIndex = 1;
+ for (; usrIndex <= ipmiMaxUsers; ++usrIndex)
+ {
+ std::string curName(
+ reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
+ ipmiMaxUserName);
+ if (userName == curName)
+ {
+ break; // found the entry
+ }
+ }
+ if (usrIndex > ipmiMaxUsers)
+ {
+ log<level::DEBUG>("User not found",
+ entry("USER_NAME=%s", userName.c_str()));
+ return invalidUserId;
+ }
+
+ return usrIndex;
+}
+
+ipmi_ret_t UserAccess::getUserName(const uint8_t& userId, std::string& userName)
+{
+ if (!isValidUserId(userId))
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ UserInfo* userInfo = getUserInfo(userId);
+ userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
+ ipmiMaxUserName);
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t UserAccess::setUserName(const uint8_t& userId,
+ const char* userNameInChar)
+{
+ if (!isValidUserId(userId))
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+ userLock{*userMutex};
+ bool validUser = isValidUserName(userNameInChar);
+ std::string oldUser;
+ getUserName(userId, oldUser);
+ UserInfo* userInfo = getUserInfo(userId);
+
+ std::string newUser(userNameInChar, 0, ipmiMaxUserName);
+ if (newUser.empty() && !oldUser.empty())
+ {
+ // Delete existing user
+ std::string userPath = std::string(userObjBasePath) + "/" + oldUser;
+ try
+ {
+ auto method = bus.new_method_call(
+ getUserServiceName().c_str(), userPath.c_str(),
+ deleteUserInterface, deleteUserMethod);
+ auto reply = bus.call(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::DEBUG>("Failed to excute method",
+ entry("METHOD=%s", deleteUserMethod),
+ entry("PATH=%s", userPath.c_str()));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ std::fill(userInfo->userName,
+ userInfo->userName + sizeof(userInfo->userName), 0);
+ ipmiClearUserEntryPassword(oldUser);
+ userInfo->userInSystem = false;
+ }
+ else if (oldUser.empty() && !newUser.empty() && validUser)
+ {
+ try
+ {
+ // Create new user
+ auto method = bus.new_method_call(
+ getUserServiceName().c_str(), userMgrObjBasePath,
+ userMgrInterface, createUserMethod);
+ // TODO: Fetch proper privilege & enable state once set User access
+ // is implemented if LAN Channel specified, then create user for all
+ // groups follow channel privilege for user creation.
+ method.append(newUser.c_str(), availableGroups, "priv-admin", true);
+ auto reply = bus.call(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::DEBUG>("Failed to excute method",
+ entry("METHOD=%s", createUserMethod),
+ entry("PATH=%s", userMgrObjBasePath));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ std::strncpy(reinterpret_cast<char*>(userInfo->userName),
+ userNameInChar, ipmiMaxUserName);
+ userInfo->userInSystem = true;
+ }
+ else if (oldUser != newUser && validUser)
+ {
+ try
+ {
+ // User rename
+ auto method = bus.new_method_call(
+ getUserServiceName().c_str(), userMgrObjBasePath,
+ userMgrInterface, renameUserMethod);
+ method.append(oldUser.c_str(), newUser.c_str());
+ auto reply = bus.call(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::DEBUG>("Failed to excute method",
+ entry("METHOD=%s", renameUserMethod),
+ entry("PATH=%s", userMgrObjBasePath));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ std::fill(static_cast<uint8_t*>(userInfo->userName),
+ static_cast<uint8_t*>(userInfo->userName) +
+ sizeof(userInfo->userName),
+ 0);
+ std::strncpy(reinterpret_cast<char*>(userInfo->userName),
+ userNameInChar, ipmiMaxUserName);
+ ipmiRenameUserEntryPassword(oldUser, newUser);
+ userInfo->userInSystem = true;
+ }
+ else if (!validUser)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ try
+ {
+ writeUserData();
+ }
+ catch (const std::exception& e)
+ {
+ log<level::DEBUG>("Write user data failed");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ return IPMI_CC_OK;
+}
+
+static constexpr const char* jsonUserName = "user_name";
+static constexpr const char* jsonPriv = "privilege";
+static constexpr const char* jsonIpmiEnabled = "ipmi_enabled";
+static constexpr const char* jsonLinkAuthEnabled = "link_auth_enabled";
+static constexpr const char* jsonAccCallbk = "access_callback";
+static constexpr const char* jsonUserEnabled = "user_enabled";
+static constexpr const char* jsonUserInSys = "user_in_system";
+static constexpr const char* jsonFixedUser = "fixed_user_name";
+
+void UserAccess::readUserData()
+{
+ boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+ userLock{*userMutex};
+
+ std::ifstream iUsrData(ipmiUserDataFile, std::ios::in | std::ios::binary);
+ if (!iUsrData.good())
+ {
+ log<level::ERR>("Error in reading IPMI user data file");
+ throw std::ios_base::failure("Error opening IPMI user data file");
+ }
+
+ Json jsonUsersTbl = Json::array();
+ jsonUsersTbl = Json::parse(iUsrData, nullptr, false);
+
+ if (jsonUsersTbl.size() != ipmiMaxUsers)
+ {
+ log<level::ERR>(
+ "Error in reading IPMI user data file - User count issues");
+ throw std::runtime_error(
+ "Corrupted IPMI user data file - invalid user count");
+ }
+ // user index 0 is reserved, starts with 1
+ for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
+ {
+ Json userInfo = jsonUsersTbl[usrIndex - 1]; // json array starts with 0.
+ if (userInfo.is_null())
+ {
+ log<level::ERR>("Error in reading IPMI user data file - "
+ "user info corrupted");
+ throw std::runtime_error(
+ "Corrupted IPMI user data file - invalid user info");
+ }
+ std::string userName = userInfo[jsonUserName].get<std::string>();
+ std::strncpy(reinterpret_cast<char*>(usersTbl.user[usrIndex].userName),
+ userName.c_str(), ipmiMaxUserName);
+
+ std::vector<std::string> privilege =
+ userInfo[jsonPriv].get<std::vector<std::string>>();
+ std::vector<bool> ipmiEnabled =
+ userInfo[jsonIpmiEnabled].get<std::vector<bool>>();
+ std::vector<bool> linkAuthEnabled =
+ userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
+ std::vector<bool> accessCallback =
+ userInfo[jsonAccCallbk].get<std::vector<bool>>();
+ if (privilege.size() != ipmiMaxChannels ||
+ ipmiEnabled.size() != ipmiMaxChannels ||
+ linkAuthEnabled.size() != ipmiMaxChannels ||
+ accessCallback.size() != ipmiMaxChannels)
+ {
+ log<level::ERR>("Error in reading IPMI user data file - "
+ "properties corrupted");
+ throw std::runtime_error(
+ "Corrupted IPMI user data file - properties");
+ }
+ for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
+ {
+ usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege =
+ static_cast<uint8_t>(
+ convertToIPMIPrivilege(privilege[chIndex]));
+ usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled =
+ ipmiEnabled[chIndex];
+ usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled =
+ linkAuthEnabled[chIndex];
+ usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
+ accessCallback[chIndex];
+ }
+ usersTbl.user[usrIndex].userEnabled =
+ userInfo[jsonUserEnabled].get<bool>();
+ usersTbl.user[usrIndex].userInSystem =
+ userInfo[jsonUserInSys].get<bool>();
+ usersTbl.user[usrIndex].fixedUserName =
+ userInfo[jsonFixedUser].get<bool>();
+ }
+
+ log<level::DEBUG>("User data read from IPMI data file");
+ iUsrData.close();
+ // Update the timestamp
+ fileLastUpdatedTime = getUpdatedFileTime();
+ return;
+}
+
+void UserAccess::writeUserData()
+{
+ boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+ userLock{*userMutex};
+
+ static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
+ std::ofstream oUsrData(tmpFile, std::ios::out | std::ios::binary);
+ if (!oUsrData.good())
+ {
+ log<level::ERR>("Error in creating temporary IPMI user data file");
+ throw std::ios_base::failure(
+ "Error in creating temporary IPMI user data file");
+ }
+
+ Json jsonUsersTbl = Json::array();
+ // user index 0 is reserved, starts with 1
+ for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
+ {
+ Json jsonUserInfo;
+ jsonUserInfo[jsonUserName] = std::string(
+ reinterpret_cast<char*>(usersTbl.user[usrIndex].userName), 0,
+ ipmiMaxUserName);
+ std::vector<std::string> privilege(ipmiMaxChannels);
+ std::vector<bool> ipmiEnabled(ipmiMaxChannels);
+ std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
+ std::vector<bool> accessCallback(ipmiMaxChannels);
+ for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
+ {
+ privilege[chIndex] =
+ convertToSystemPrivilege(static_cast<CommandPrivilege>(
+ usersTbl.user[usrIndex].userPrivAccess[chIndex].privilege));
+ ipmiEnabled[chIndex] =
+ usersTbl.user[usrIndex].userPrivAccess[chIndex].ipmiEnabled;
+ linkAuthEnabled[chIndex] =
+ usersTbl.user[usrIndex].userPrivAccess[chIndex].linkAuthEnabled;
+ accessCallback[chIndex] =
+ usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback;
+ }
+ jsonUserInfo[jsonPriv] = privilege;
+ jsonUserInfo[jsonIpmiEnabled] = ipmiEnabled;
+ jsonUserInfo[jsonLinkAuthEnabled] = linkAuthEnabled;
+ jsonUserInfo[jsonAccCallbk] = accessCallback;
+ jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
+ jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
+ jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
+ jsonUsersTbl.push_back(jsonUserInfo);
+ }
+
+ oUsrData << jsonUsersTbl;
+ oUsrData.flush();
+ oUsrData.close();
+
+ if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
+ {
+ log<level::ERR>("Error in renaming temporary IPMI user data file");
+ throw std::runtime_error("Error in renaming IPMI user data file");
+ }
+ // Update the timestamp
+ fileLastUpdatedTime = getUpdatedFileTime();
+ return;
+}
+
+bool UserAccess::addUserEntry(const std::string& userName,
+ const std::string& sysPriv, const bool& enabled)
+{
+ UsersTbl* userData = getUsersTblPtr();
+ size_t freeIndex = 0xFF;
+ // user index 0 is reserved, starts with 1
+ for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
+ {
+ std::string curName(
+ reinterpret_cast<char*>(userData->user[usrIndex].userName), 0,
+ ipmiMaxUserName);
+ if (userName == curName)
+ {
+ log<level::DEBUG>("User name exists",
+ entry("USER_NAME=%s", userName.c_str()));
+ return false; // user name exists.
+ }
+
+ if ((!userData->user[usrIndex].userInSystem) &&
+ (userData->user[usrIndex].userName[0] == '\0') &&
+ (freeIndex == 0xFF))
+ {
+ freeIndex = usrIndex;
+ }
+ }
+ if (freeIndex == 0xFF)
+ {
+ log<level::ERR>("No empty slots found");
+ return false;
+ }
+ std::strncpy(reinterpret_cast<char*>(userData->user[freeIndex].userName),
+ userName.c_str(), ipmiMaxUserName);
+ uint8_t priv =
+ static_cast<uint8_t>(UserAccess::convertToIPMIPrivilege(sysPriv)) &
+ privMask;
+ for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
+ {
+ userData->user[freeIndex].userPrivAccess[chIndex].privilege = priv;
+ userData->user[freeIndex].userPrivAccess[chIndex].ipmiEnabled = true;
+ userData->user[freeIndex].userPrivAccess[chIndex].linkAuthEnabled =
+ true;
+ userData->user[freeIndex].userPrivAccess[chIndex].accessCallback = true;
+ }
+ userData->user[freeIndex].userInSystem = true;
+ userData->user[freeIndex].userEnabled = enabled;
+
+ return true;
+}
+
+void UserAccess::deleteUserIndex(const size_t& usrIdx)
+{
+ UsersTbl* userData = getUsersTblPtr();
+
+ std::string userName(
+ reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
+ ipmiMaxUserName);
+ ipmiClearUserEntryPassword(userName);
+ std::fill(static_cast<uint8_t*>(userData->user[usrIdx].userName),
+ static_cast<uint8_t*>(userData->user[usrIdx].userName) +
+ sizeof(userData->user[usrIdx].userName),
+ 0);
+ for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
+ {
+ userData->user[usrIdx].userPrivAccess[chIndex].privilege = privNoAccess;
+ userData->user[usrIdx].userPrivAccess[chIndex].ipmiEnabled = false;
+ userData->user[usrIdx].userPrivAccess[chIndex].linkAuthEnabled = false;
+ userData->user[usrIdx].userPrivAccess[chIndex].accessCallback = false;
+ }
+ userData->user[usrIdx].userInSystem = false;
+ userData->user[usrIdx].userEnabled = false;
+ return;
+}
+
+void UserAccess::checkAndReloadUserData()
+{
+ std::time_t updateTime = getUpdatedFileTime();
+ if (updateTime != fileLastUpdatedTime || updateTime == -EIO)
+ {
+ std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
+ reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
+ readUserData();
+ }
+ return;
+}
+
+UsersTbl* UserAccess::getUsersTblPtr()
+{
+ // reload data before using it.
+ checkAndReloadUserData();
+ return &usersTbl;
+}
+
+void UserAccess::getSystemPrivAndGroups()
+{
+ std::map<std::string, PrivAndGroupType> properties;
+ try
+ {
+ auto method = bus.new_method_call(
+ getUserServiceName().c_str(), userMgrObjBasePath,
+ dBusPropertiesInterface, getAllPropertiesMethod);
+ method.append(userMgrInterface);
+
+ auto reply = bus.call(method);
+ reply.read(properties);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::DEBUG>("Failed to excute method",
+ entry("METHOD=%s", getAllPropertiesMethod),
+ entry("PATH=%s", userMgrObjBasePath));
+ return;
+ }
+ for (const auto& t : properties)
+ {
+ auto key = t.first;
+ if (key == allPrivProperty)
+ {
+ availablePrivileges = t.second.get<std::vector<std::string>>();
+ }
+ else if (key == allGrpProperty)
+ {
+ availableGroups = t.second.get<std::vector<std::string>>();
+ }
+ }
+ // TODO: Implement Supported Privilege & Groups verification logic
+ return;
+}
+
+std::time_t UserAccess::getUpdatedFileTime()
+{
+ struct stat fileStat;
+ if (stat(ipmiUserDataFile, &fileStat) != 0)
+ {
+ log<level::DEBUG>("Error in getting last updated time stamp");
+ return -EIO;
+ }
+ return fileStat.st_mtime;
+}
+
+void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
+ std::vector<std::string>& usrGrps,
+ std::string& usrPriv, bool& usrEnabled)
+{
+ for (const auto& t : properties)
+ {
+ std::string key = t.first;
+ if (key == userPrivProperty)
+ {
+ usrPriv = t.second.get<std::string>();
+ }
+ else if (key == userGrpProperty)
+ {
+ usrGrps = t.second.get<std::vector<std::string>>();
+ }
+ else if (key == userEnabledProperty)
+ {
+ usrEnabled = t.second.get<bool>();
+ }
+ }
+ return;
+}
+
+int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
+ std::vector<std::string>& usrGrps,
+ std::string& usrPriv, bool& usrEnabled)
+{
+ auto usrObj = userObjs.find(usersInterface);
+ if (usrObj != userObjs.end())
+ {
+ getUserProperties(usrObj->second, usrGrps, usrPriv, usrEnabled);
+ return 0;
+ }
+ return -EIO;
+}
+
+void UserAccess::initUserDataFile()
+{
+ boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+ userLock{*userMutex};
+ try
+ {
+ readUserData();
+ }
+ catch (const std::ios_base::failure& e)
+ { // File is empty, create it for the first time
+ std::fill(reinterpret_cast<uint8_t*>(&usersTbl),
+ reinterpret_cast<uint8_t*>(&usersTbl) + sizeof(usersTbl), 0);
+ // user index 0 is reserved, starts with 1
+ for (size_t userIndex = 1; userIndex <= ipmiMaxUsers; ++userIndex)
+ {
+ for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
+ {
+ usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
+ privNoAccess;
+ }
+ }
+ writeUserData();
+ }
+ std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
+ try
+ {
+ auto method = bus.new_method_call(getUserServiceName().c_str(),
+ userMgrObjBasePath, dBusObjManager,
+ getManagedObjectsMethod);
+ auto reply = bus.call(method);
+ reply.read(managedObjs);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::DEBUG>("Failed to excute method",
+ entry("METHOD=%s", getSubTreeMethod),
+ entry("PATH=%s", userMgrObjBasePath));
+ return;
+ }
+
+ UsersTbl* userData = &usersTbl;
+ // user index 0 is reserved, starts with 1
+ for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
+ {
+ if ((userData->user[usrIdx].userInSystem) &&
+ (userData->user[usrIdx].userName[0] != '\0'))
+ {
+ std::vector<std::string> usrGrps;
+ std::string usrPriv;
+ bool usrEnabled;
+
+ std::string userName(
+ reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
+ ipmiMaxUserName);
+ std::string usersPath =
+ std::string(userObjBasePath) + "/" + userName;
+
+ auto usrObj = managedObjs.find(usersPath);
+ if (usrObj != managedObjs.end())
+ {
+ // User exist. Lets check and update other fileds
+ getUserObjProperties(usrObj->second, usrGrps, usrPriv,
+ usrEnabled);
+ if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
+ usrGrps.end())
+ {
+ // Group "ipmi" is removed so lets remove user in IPMI
+ deleteUserIndex(usrIdx);
+ }
+ else
+ {
+ // Group "ipmi" is present so lets update other properties
+ // in IPMI
+ uint8_t priv =
+ UserAccess::convertToIPMIPrivilege(usrPriv) & privMask;
+ // Update all channels priv, only if it is not equivalent to
+ // getUsrMgmtSyncIndex()
+ if (userData->user[usrIdx]
+ .userPrivAccess[getUsrMgmtSyncIndex()]
+ .privilege != priv)
+ {
+ for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
+ ++chIndex)
+ {
+ userData->user[usrIdx]
+ .userPrivAccess[chIndex]
+ .privilege = priv;
+ }
+ }
+ if (userData->user[usrIdx].userEnabled != usrEnabled)
+ {
+ userData->user[usrIdx].userEnabled = usrEnabled;
+ }
+ }
+
+ // We are done with this obj. lets delete from MAP
+ managedObjs.erase(usrObj);
+ }
+ else
+ {
+ deleteUserIndex(usrIdx);
+ }
+ }
+ }
+
+ // Walk through remnaining managedObj users list
+ // Add them to ipmi data base
+ for (const auto& usrObj : managedObjs)
+ {
+ std::vector<std::string> usrGrps;
+ std::string usrPriv, userName;
+ bool usrEnabled;
+ std::string usrObjPath = std::string(usrObj.first);
+ if (getUserNameFromPath(usrObj.first.str, userName) != 0)
+ {
+ log<level::ERR>("Error in user object path");
+ continue;
+ }
+ getUserObjProperties(usrObj.second, usrGrps, usrPriv, usrEnabled);
+ // Add 'ipmi' group users
+ if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
+ usrGrps.end())
+ {
+ // CREATE NEW USER
+ if (true != addUserEntry(userName, usrPriv, usrEnabled))
+ {
+ break;
+ }
+ }
+ }
+
+ // All userData slots update done. Lets write the data
+ writeUserData();
+
+ return;
+}
+} // namespace ipmi
diff --git a/user_channel/user_mgmt.hpp b/user_channel/user_mgmt.hpp
new file mode 100644
index 0000000..16dbd31
--- /dev/null
+++ b/user_channel/user_mgmt.hpp
@@ -0,0 +1,295 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+#include "user_layer.hpp"
+
+#include <host-ipmid/ipmid-api.h>
+
+#include <boost/interprocess/sync/file_lock.hpp>
+#include <boost/interprocess/sync/named_recursive_mutex.hpp>
+#include <cstdint>
+#include <ctime>
+#include <sdbusplus/bus.hpp>
+
+namespace ipmi
+{
+
+using DbusUserPropVariant =
+ sdbusplus::message::variant<std::vector<std::string>, std::string, bool>;
+
+using DbusUserObjPath = sdbusplus::message::object_path;
+
+using DbusUserObjProperties =
+ std::vector<std::pair<std::string, DbusUserPropVariant>>;
+
+using DbusUserObjValue = std::map<std::string, DbusUserObjProperties>;
+
+enum class UserUpdateEvent
+{
+ reservedEvent,
+ userCreated,
+ userDeleted,
+ userRenamed,
+ userGrpUpdated,
+ userPrivUpdated,
+ userStateUpdated
+};
+
+struct UserPrivAccess
+{
+ uint8_t privilege;
+ bool ipmiEnabled;
+ bool linkAuthEnabled;
+ bool accessCallback;
+};
+
+struct UserInfo
+{
+ uint8_t userName[ipmiMaxUserName];
+ UserPrivAccess userPrivAccess[ipmiMaxChannels];
+ bool userEnabled;
+ bool userInSystem;
+ bool fixedUserName;
+};
+
+struct UsersTbl
+{
+ //+1 to map with UserId directly. UserId 0 is reserved.
+ UserInfo user[ipmiMaxUsers + 1];
+};
+
+class UserAccess;
+
+UserAccess& getUserAccessObject();
+
+class UserAccess
+{
+ public:
+ UserAccess(const UserAccess&) = delete;
+ UserAccess& operator=(const UserAccess&) = delete;
+ UserAccess(UserAccess&&) = delete;
+ UserAccess& operator=(UserAccess&&) = delete;
+
+ ~UserAccess();
+ UserAccess();
+
+ /** @brief determines valid channel
+ *
+ * @param[in] chNum - channel number
+ *
+ * @return true if valid, false otherwise
+ */
+ static bool isValidChannel(const uint8_t& chNum);
+
+ /** @brief determines valid userId
+ *
+ * @param[in] userId - user id
+ *
+ * @return true if valid, false otherwise
+ */
+ static bool isValidUserId(const uint8_t& userId);
+
+ /** @brief determines valid user privilege
+ *
+ * @param[in] priv - Privilege
+ *
+ * @return true if valid, false otherwise
+ */
+ static bool isValidPrivilege(const uint8_t& priv);
+
+ /** @brief determines sync index to be mapped with common-user-management
+ *
+ * @return Index which will be used as sync index
+ */
+ static uint8_t getUsrMgmtSyncIndex();
+
+ /** @brief Converts system privilege to IPMI privilege
+ *
+ * @param[in] value - Privilege in string
+ *
+ * @return CommandPrivilege - IPMI privilege type
+ */
+ static CommandPrivilege convertToIPMIPrivilege(const std::string& value);
+
+ /** @brief Converts IPMI privilege to system privilege
+ *
+ * @param[in] value - IPMI privilege
+ *
+ * @return System privilege in string
+ */
+ static std::string convertToSystemPrivilege(const CommandPrivilege& value);
+
+ /** @brief determines whether user name is valid
+ *
+ * @param[in] userNameInChar - user name
+ *
+ * @return true if valid, false otherwise
+ */
+ bool isValidUserName(const char* userNameInChar);
+
+ /** @brief provides user id of the user
+ *
+ * @param[in] userName - user name
+ *
+ * @return user id of the user, else invalid user id (0xFF), if user not
+ * found
+ */
+ uint8_t getUserId(const std::string& userName);
+
+ /** @brief provides user information
+ *
+ * @param[in] userId - user id
+ *
+ * @return UserInfo for the specified user id
+ */
+ UserInfo* getUserInfo(const uint8_t& userId);
+
+ /** @brief sets user information
+ *
+ * @param[in] userId - user id
+ * @param[in] userInfo - user information
+ *
+ */
+ void setUserInfo(const uint8_t& userId, UserInfo* userInfo);
+
+ /** @brief provides user name
+ *
+ * @param[in] userId - user id
+ * @param[out] userName - user name
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ ipmi_ret_t getUserName(const uint8_t& userId, std::string& userName);
+
+ /** @brief to set user name
+ *
+ * @param[in] userId - user id
+ * @param[in] userNameInChar - user name
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ ipmi_ret_t setUserName(const uint8_t& userId, const char* userNameInChar);
+
+ /** @brief to set user privilege and access details
+ *
+ * @param[in] userId - user id
+ * @param[in] chNum - channel number
+ * @param[in] privAccess - privilege access
+ * @param[in] otherPrivUpdates - other privilege update flag to update ipmi
+ * enable, link authentication and access callback
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ ipmi_ret_t setUserPrivilegeAccess(const uint8_t& userId,
+ const uint8_t& chNum,
+ const UserPrivAccess& privAccess,
+ const bool& otherPrivUpdates);
+
+ /** @brief reads user management related data from configuration file
+ *
+ */
+ void readUserData();
+
+ /** @brief writes user management related data to configuration file
+ *
+ */
+ void writeUserData();
+
+ /** @brief Funtion which checks and reload configuration file data if
+ * needed.
+ *
+ */
+ void checkAndReloadUserData();
+
+ /** @brief provides user details from D-Bus user property data
+ *
+ * @param[in] properties - D-Bus user property
+ * @param[out] usrGrps - user group details
+ * @param[out] usrPriv - user privilege
+ * @param[out] usrEnabled - enabled state of the user.
+ *
+ * @return 0 for success, -errno for failure.
+ */
+ void getUserProperties(const DbusUserObjProperties& properties,
+ std::vector<std::string>& usrGrps,
+ std::string& usrPriv, bool& usrEnabled);
+
+ /** @brief provides user details from D-Bus user object data
+ *
+ * @param[in] userObjs - D-Bus user object
+ * @param[out] usrGrps - user group details
+ * @param[out] usrPriv - user privilege
+ * @param[out] usrEnabled - enabled state of the user.
+ *
+ * @return 0 for success, -errno for failure.
+ */
+ int getUserObjProperties(const DbusUserObjValue& userObjs,
+ std::vector<std::string>& usrGrps,
+ std::string& usrPriv, bool& usrEnabled);
+
+ /** @brief function to add user entry information to the configuration
+ *
+ * @param[in] userName - user name
+ * @param[in] priv - privilege of the user
+ * @param[in] enabled - enabled state of the user
+ *
+ * @return true for success, false for failure
+ */
+ bool addUserEntry(const std::string& userName, const std::string& priv,
+ const bool& enabled);
+
+ /** @brief function to delete user entry based on user index
+ *
+ * @param[in] usrIdx - user index
+ *
+ */
+ void deleteUserIndex(const size_t& usrIdx);
+
+ /** @brief function to get users table
+ *
+ */
+ UsersTbl* getUsersTblPtr();
+
+ std::unique_ptr<boost::interprocess::named_recursive_mutex> userMutex{
+ nullptr};
+
+ private:
+ UsersTbl usersTbl;
+ std::vector<std::string> availablePrivileges;
+ std::vector<std::string> availableGroups;
+ sdbusplus::bus::bus bus;
+ std::time_t fileLastUpdatedTime;
+ bool signalHndlrObject = false;
+ boost::interprocess::file_lock sigHndlrLock;
+ boost::interprocess::file_lock mutexCleanupLock;
+
+ /** @brief function to get user configuration file timestamp
+ *
+ * @return time stamp or -EIO for failure
+ */
+ std::time_t getUpdatedFileTime();
+
+ /** @brief function to available system privileges and groups
+ *
+ */
+ void getSystemPrivAndGroups();
+
+ /** @brief function to init user data from configuration & D-Bus objects
+ *
+ */
+ void initUserDataFile();
+};
+} // namespace ipmi
diff --git a/user_channel/usercommands.cpp b/user_channel/usercommands.cpp
new file mode 100644
index 0000000..0ed5b8f
--- /dev/null
+++ b/user_channel/usercommands.cpp
@@ -0,0 +1,473 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include "usercommands.hpp"
+
+#include "apphandler.hpp"
+#include "user_layer.hpp"
+
+#include <host-ipmid/ipmid-api.h>
+#include <security/pam_appl.h>
+
+#include <phosphor-logging/log.hpp>
+#include <regex>
+
+namespace ipmi
+{
+
+using namespace phosphor::logging;
+
+static constexpr uint8_t maxIpmi20PasswordSize = 20;
+static constexpr uint8_t maxIpmi15PasswordSize = 16;
+static constexpr uint8_t disableUser = 0x00;
+static constexpr uint8_t enableUser = 0x01;
+static constexpr uint8_t setPassword = 0x02;
+static constexpr uint8_t testPassword = 0x03;
+
+struct SetUserAccessReq
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t chNum : 4;
+ uint8_t ipmiEnabled : 1;
+ uint8_t linkAuthEnabled : 1;
+ uint8_t accessCallback : 1;
+ uint8_t bitsUpdate : 1;
+ uint8_t userId : 6;
+ uint8_t reserved1 : 2;
+ uint8_t privilege : 4;
+ uint8_t reserved2 : 4;
+ uint8_t sessLimit : 4; // optional byte 4
+ uint8_t reserved3 : 4;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ uint8_t bitsUpdate : 1;
+ uint8_t accessCallback : 1;
+ uint8_t linkAuthEnabled : 1;
+ uint8_t ipmiEnabled : 1;
+ uint8_t chNum : 4;
+ uint8_t reserved1 : 2;
+ uint8_t userId : 6;
+ uint8_t reserved2 : 4;
+ uint8_t privilege : 4;
+ uint8_t reserved3 : 4;
+ uint8_t sessLimit : 4; // optional byte 4
+#endif
+
+} __attribute__((packed));
+
+struct GetUserAccessReq
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t chNum : 4;
+ uint8_t reserved1 : 4;
+ uint8_t userId : 6;
+ uint8_t reserved2 : 2;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ uint8_t reserved1 : 4;
+ uint8_t chNum : 4;
+ uint8_t reserved2 : 2;
+ uint8_t userId : 6;
+#endif
+} __attribute__((packed));
+
+struct GetUserAccessResp
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t maxChUsers : 6;
+ uint8_t reserved1 : 2;
+ uint8_t enabledUsers : 6;
+ uint8_t enabledStatus : 2;
+ uint8_t fixedUsers : 6;
+ uint8_t reserved2 : 2;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ uint8_t reserved1 : 2;
+ uint8_t maxChUsers : 6;
+ uint8_t enabledStatus : 2;
+ uint8_t enabledUsers : 6;
+ uint8_t reserved2 : 2;
+ uint8_t fixedUsers : 6;
+#endif
+ PrivAccess privAccess;
+} __attribute__((packed));
+
+struct SetUserNameReq
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t userId : 6;
+ uint8_t reserved1 : 2;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ uint8_t reserved1 : 2;
+ uint8_t userId : 6;
+#endif
+ uint8_t userName[16];
+} __attribute__((packed));
+
+struct GetUserNameReq
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t userId : 6;
+ uint8_t reserved1 : 2;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ uint8_t reserved1 : 2;
+ uint8_t userId : 6;
+#endif
+} __attribute__((packed));
+
+struct GetUserNameResp
+{
+ uint8_t userName[16];
+} __attribute__((packed));
+
+struct SetUserPasswordReq
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t userId : 6;
+ uint8_t reserved1 : 1;
+ uint8_t ipmi20 : 1;
+ uint8_t operation : 2;
+ uint8_t reserved2 : 6;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ uint8_t ipmi20 : 1;
+ uint8_t reserved1 : 1;
+ uint8_t userId : 6;
+ uint8_t reserved2 : 6;
+ uint8_t operation : 2;
+#endif
+ uint8_t userPassword[maxIpmi20PasswordSize];
+} __attribute__((packed));
+
+ipmi_ret_t ipmiSetUserAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+ const SetUserAccessReq* req = static_cast<SetUserAccessReq*>(request);
+ size_t reqLength = *dataLen;
+
+ if (!(reqLength == sizeof(*req) ||
+ (reqLength == (sizeof(*req) - sizeof(uint8_t) /* skip optional*/))))
+ {
+ log<level::DEBUG>("Set user access - Invalid Length");
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ if (req->reserved1 != 0 || req->reserved2 != 0 || req->reserved3 != 0 ||
+ req->sessLimit != 0 ||
+ (!ipmiUserIsValidChannel(req->chNum) ||
+ (!ipmiUserIsValidPrivilege(req->privilege))))
+ // TODO: Need to check for session support and return invalid field in
+ // request
+ {
+ log<level::DEBUG>("Set user access - Invalid field in request");
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (!ipmiUserIsValidUserId(req->userId))
+ {
+ log<level::DEBUG>("Set user access - Parameter out of range");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ // TODO: Determine the Channel number 0xE (Self Channel number ?)
+ uint8_t chNum = req->chNum;
+ PrivAccess privAccess = {0};
+ if (req->bitsUpdate)
+ {
+ privAccess.ipmiEnabled = req->ipmiEnabled;
+ privAccess.linkAuthEnabled = req->linkAuthEnabled;
+ privAccess.accessCallback = req->accessCallback;
+ }
+ privAccess.privilege = req->privilege;
+ ipmiUserSetPrivilegeAccess(req->userId, chNum, privAccess, req->bitsUpdate);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiGetUserAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+ const GetUserAccessReq* req = static_cast<GetUserAccessReq*>(request);
+ size_t reqLength = *dataLen;
+
+ *dataLen = 0;
+
+ if (reqLength != sizeof(*req))
+ {
+ log<level::DEBUG>("Get user access - Invalid Length");
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ if (req->reserved1 != 0 || req->reserved2 != 0 ||
+ (!ipmiUserIsValidChannel(req->chNum)))
+ // TODO: Need to check for session support and return invalid field in
+ // request
+ {
+ log<level::DEBUG>("Get user access - Invalid field in request");
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (!ipmiUserIsValidUserId(req->userId))
+ {
+ log<level::DEBUG>("Get user access - Parameter out of range");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
+ bool enabledState = false;
+ // TODO: Determine the Channel number 0xE (Self Channel number ?)
+ uint8_t chNum = req->chNum;
+ GetUserAccessResp* resp = static_cast<GetUserAccessResp*>(response);
+
+ std::fill(reinterpret_cast<uint8_t*>(resp),
+ reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
+
+ ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers);
+ resp->maxChUsers = maxChUsers;
+ resp->enabledUsers = enabledUsers;
+ resp->fixedUsers = fixedUsers;
+
+ ipmiUserCheckEnabled(req->userId, enabledState);
+ resp->enabledStatus = enabledState ? userIdEnabledViaSetPassword
+ : userIdDisabledViaSetPassword;
+ ipmiUserGetPrivilegeAccess(req->userId, chNum, resp->privAccess);
+ *dataLen = sizeof(*resp);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiSetUserName(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+ const SetUserNameReq* req = static_cast<SetUserNameReq*>(request);
+ size_t reqLength = *dataLen;
+ *dataLen = 0;
+
+ if (reqLength != sizeof(*req))
+ {
+ log<level::DEBUG>("Set user name - Invalid Length");
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ if (req->reserved1)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (!ipmiUserIsValidUserId(req->userId))
+ {
+ log<level::DEBUG>("Set user name - Invalid user id");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ return ipmiUserSetUserName(req->userId,
+ reinterpret_cast<const char*>(req->userName));
+}
+
+/** @brief implementes the get user name command
+ * @param[in] netfn - specifies netfn.
+ * @param[in] cmd - specifies cmd number.
+ * @param[in] request - pointer to request data.
+ * @param[in, out] dataLen - specifies request data length, and returns
+ * response data length.
+ * @param[in] context - ipmi context.
+ * @returns ipmi completion code.
+ */
+ipmi_ret_t ipmiGetUserName(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+ const GetUserNameReq* req = static_cast<GetUserNameReq*>(request);
+ size_t reqLength = *dataLen;
+
+ *dataLen = 0;
+
+ if (reqLength != sizeof(*req))
+ {
+ log<level::DEBUG>("Get user name - Invalid Length");
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ std::string userName;
+ if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK)
+ { // Invalid User ID
+ log<level::DEBUG>("User Name not found",
+ entry("USER-ID:%d", (uint8_t)req->userId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ GetUserNameResp* resp = static_cast<GetUserNameResp*>(response);
+ std::fill(reinterpret_cast<uint8_t*>(resp),
+ reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
+ userName.copy(reinterpret_cast<char*>(resp->userName),
+ sizeof(resp->userName), 0);
+ *dataLen = sizeof(*resp);
+
+ return IPMI_CC_OK;
+}
+
+int pamFunctionConversation(int numMsg, const struct pam_message** msg,
+ struct pam_response** resp, void* appdataPtr)
+{
+ if (appdataPtr == nullptr)
+ {
+ return PAM_AUTH_ERR;
+ }
+ size_t passSize = std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1;
+ char* pass = reinterpret_cast<char*>(malloc(passSize));
+ std::strncpy(pass, reinterpret_cast<char*>(appdataPtr), passSize);
+
+ *resp = reinterpret_cast<pam_response*>(
+ calloc(numMsg, sizeof(struct pam_response)));
+
+ for (int i = 0; i < numMsg; ++i)
+ {
+ if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
+ {
+ continue;
+ }
+ resp[i]->resp = pass;
+ }
+ return PAM_SUCCESS;
+}
+
+bool pamUpdatePasswd(const char* username, const char* password)
+{
+ const struct pam_conv localConversation = {pamFunctionConversation,
+ const_cast<char*>(password)};
+ pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
+
+ if (pam_start("passwd", username, &localConversation, &localAuthHandle) !=
+ PAM_SUCCESS)
+ {
+ return false;
+ }
+ int retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
+
+ if (retval != PAM_SUCCESS)
+ {
+ if (retval == PAM_AUTHTOK_ERR)
+ {
+ log<level::DEBUG>("Authentication Failure");
+ }
+ else
+ {
+ log<level::DEBUG>("pam_chauthtok returned failure",
+ entry("ERROR=%d", retval));
+ }
+ pam_end(localAuthHandle, retval);
+ return false;
+ }
+ if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
+ {
+ return false;
+ }
+ return true;
+}
+
+/** @brief implementes the set user password command
+ * @param[in] netfn - specifies netfn.
+ * @param[in] cmd - specifies cmd number.
+ * @param[in] request - pointer to request data.
+ * @param[in, out] dataLen - specifies request data length, and returns
+ * response data length.
+ * @param[in] context - ipmi context.
+ * @returns ipmi completion code.
+ */
+ipmi_ret_t ipmiSetUserPassword(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+ const SetUserPasswordReq* req = static_cast<SetUserPasswordReq*>(request);
+ size_t reqLength = *dataLen;
+ // subtract 2 bytes header to know the password length - including NULL
+ uint8_t passwordLength = *dataLen - 2;
+ *dataLen = 0;
+
+ // verify input length based on operation. Required password size is 20
+ // bytes as we support only IPMI 2.0, but in order to be compatible with
+ // tools, accept 16 bytes of password size too.
+ if (reqLength < 2 ||
+ // If enable / disable user, reqLength has to be >=2 & <= 22
+ ((req->operation == disableUser || req->operation == enableUser) &&
+ ((reqLength < 2) || (reqLength > sizeof(SetUserPasswordReq)))))
+ {
+ log<level::DEBUG>("Invalid Length");
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ // If set / test password then password length has to be 16 or 20 bytes
+ if (((req->operation == setPassword) || (req->operation == testPassword)) &&
+ ((passwordLength != maxIpmi20PasswordSize) &&
+ (passwordLength != maxIpmi15PasswordSize)))
+ {
+ log<level::DEBUG>("Invalid Length");
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ std::string userName;
+ if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK)
+ {
+ log<level::DEBUG>("User Name not found",
+ entry("USER-ID:%d", (uint8_t)req->userId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ if (req->operation == setPassword)
+ {
+ std::string passwd;
+ passwd.assign(reinterpret_cast<const char*>(req->userPassword), 0,
+ maxIpmi20PasswordSize);
+ if (!std::regex_match(passwd.c_str(),
+ std::regex("[a-zA-z_0-9][a-zA-Z_0-9,?:`!\"]*")))
+ {
+ log<level::DEBUG>("Invalid password fields",
+ entry("USER-ID:%d", (uint8_t)req->userId));
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (!pamUpdatePasswd(userName.c_str(), passwd.c_str()))
+ {
+ log<level::DEBUG>("Failed to update password",
+ entry("USER-ID:%d", (uint8_t)req->userId));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ }
+ else
+ {
+ // TODO: test the password by reading the encrypted file
+ log<level::ERR>(
+ "Other operations not implemented - TODO yet to implement");
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ return IPMI_CC_OK;
+}
+
+void registerUserIpmiFunctions()
+{
+ ipmiUserInit();
+ ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_ACCESS, NULL,
+ ipmiSetUserAccess, PRIVILEGE_ADMIN);
+
+ ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_USER_ACCESS, NULL,
+ ipmiGetUserAccess, PRIVILEGE_OPERATOR);
+
+ ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_USER_NAME, NULL,
+ ipmiGetUserName, PRIVILEGE_OPERATOR);
+
+ ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_NAME, NULL,
+ ipmiSetUserName, PRIVILEGE_ADMIN);
+
+ ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_PASSWORD, NULL,
+ ipmiSetUserPassword, PRIVILEGE_ADMIN);
+
+ return;
+}
+} // namespace ipmi
diff --git a/user_channel/usercommands.hpp b/user_channel/usercommands.hpp
new file mode 100644
index 0000000..ee33b5a
--- /dev/null
+++ b/user_channel/usercommands.hpp
@@ -0,0 +1,36 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#pragma once
+#include <cstdint>
+
+namespace ipmi
+{
+// IPMI commands for user command NETFN:APP.
+enum ipmi_netfn_user_cmds
+{
+ IPMI_CMD_SET_USER_ACCESS = 0x43,
+ IPMI_CMD_GET_USER_ACCESS = 0x44,
+ IPMI_CMD_SET_USER_NAME = 0x45,
+ IPMI_CMD_GET_USER_NAME = 0x46,
+ IPMI_CMD_SET_USER_PASSWORD = 0x47,
+};
+
+static constexpr uint8_t userIdEnabledViaSetPassword = 0x1;
+static constexpr uint8_t userIdDisabledViaSetPassword = 0x2;
+
+void registerUserIpmiFunctions();
+} // namespace ipmi
OpenPOWER on IntegriCloud