diff options
Diffstat (limited to 'user_channel')
-rw-r--r-- | user_channel/Makefile.am | 9 | ||||
-rw-r--r-- | user_channel/channel_layer.cpp | 20 | ||||
-rw-r--r-- | user_channel/channel_layer.hpp | 35 | ||||
-rw-r--r-- | user_channel/channel_mgmt.cpp | 142 | ||||
-rw-r--r-- | user_channel/channel_mgmt.hpp | 24 | ||||
-rw-r--r-- | user_channel/channelcommands.cpp | 591 | ||||
-rw-r--r-- | user_channel/channelcommands.hpp | 35 | ||||
-rw-r--r-- | user_channel/passwd_mgr.cpp | 7 | ||||
-rw-r--r-- | user_channel/user_layer.cpp | 59 | ||||
-rw-r--r-- | user_channel/user_layer.hpp | 62 | ||||
-rw-r--r-- | user_channel/user_mgmt.cpp | 495 | ||||
-rw-r--r-- | user_channel/user_mgmt.hpp | 86 | ||||
-rw-r--r-- | user_channel/usercommands.cpp | 207 |
13 files changed, 1164 insertions, 608 deletions
diff --git a/user_channel/Makefile.am b/user_channel/Makefile.am index 3860a39..747c4c8 100644 --- a/user_channel/Makefile.am +++ b/user_channel/Makefile.am @@ -12,7 +12,12 @@ COMMON_CXX = \ -DBOOST_ASIO_DISABLE_THREADS \ -DBOOST_ALL_NO_LIB -lib_LTLIBRARIES = libuserlayer.la libchannellayer.la + +lib_LTLIBRARIES = + +if FEATURE_LIBUSERLAYER + +lib_LTLIBRARIES += libuserlayer.la libuserlayer_la_SOURCES = \ user_layer.cpp \ user_mgmt.cpp \ @@ -29,7 +34,9 @@ libuserlayer_la_LDFLAGS = \ libuserlayer_la_CXXFLAGS = \ -I$(top_srcdir) \ $(COMMON_CXX) +endif +lib_LTLIBRARIES += libchannellayer.la libchannellayer_la_SOURCES = \ channel_mgmt.cpp \ channel_layer.cpp diff --git a/user_channel/channel_layer.cpp b/user_channel/channel_layer.cpp index 34a596d..c6866c2 100644 --- a/user_channel/channel_layer.cpp +++ b/user_channel/channel_layer.cpp @@ -58,11 +58,6 @@ bool isValidChannel(const uint8_t chNum) return getChannelConfigObject().isValidChannel(chNum); } -uint8_t convertCurrentChannelNum(const uint8_t chNum) -{ - return getChannelConfigObject().convertToChannelIndexNumber(chNum); -} - bool isValidAuthType(const uint8_t chNum, const EAuthType& authType) { return getChannelConfigObject().isValidAuthType(chNum, authType); @@ -142,4 +137,19 @@ std::string getChannelName(const uint8_t chNum) return getChannelConfigObject().getChannelName(chNum); } +uint8_t getChannelByName(const std::string& chName) +{ + return getChannelConfigObject().getChannelByName(chName); +} + +bool isValidPayloadType(const PayloadType payloadType) +{ + return ( + payloadType == PayloadType::IPMI || payloadType == PayloadType::SOL || + payloadType == PayloadType::OPEN_SESSION_REQUEST || + payloadType == PayloadType::OPEN_SESSION_RESPONSE || + payloadType == PayloadType::RAKP1 || + payloadType == PayloadType::RAKP2 || + payloadType == PayloadType::RAKP3 || payloadType == PayloadType::RAKP4); +} } // namespace ipmi diff --git a/user_channel/channel_layer.hpp b/user_channel/channel_layer.hpp index 1a8d64c..4eb51b7 100644 --- a/user_channel/channel_layer.hpp +++ b/user_channel/channel_layer.hpp @@ -16,7 +16,6 @@ #pragma once #include <ipmid/api.h> -#include <ipmid/message.hpp> #include <string> namespace ipmi @@ -24,6 +23,7 @@ namespace ipmi static constexpr uint8_t maxIpmiChannels = 16; static constexpr uint8_t currentChNum = 0xE; +static constexpr uint8_t invalidChannel = 0xff; /** * @enum IPMI return codes specific to channel (refer spec se 22.22 response @@ -280,30 +280,21 @@ ipmi_ret_t getChannelAccessData(const uint8_t chNum, /** @brief provides function to convert current channel number (0xE) * * @param[in] chNum - channel number as requested in commands. - * @param[in] ipmi::context - ipmi context ptr, which has more details + * @param[in] devChannel - channel number as provided by device (not 0xE) * * @return same channel number or proper channel number for current channel * number (0xE). */ -inline uint8_t convertCurrentChannelNum(const uint8_t chNum, - ipmi::Context::ptr ctx) +static inline uint8_t convertCurrentChannelNum(const uint8_t chNum, + const uint8_t devChannel) { if (chNum == currentChNum) { - return ctx->channel; + return devChannel; } return chNum; } -/** @brief provides function to convert current channel number (0xE) - * - * @param[in] chNum - channel number as requested in commands. - * - * @return same channel number or proper channel number for current channel - * number (0xE). - */ -uint8_t convertCurrentChannelNum(const uint8_t chNum); - /** @brief to set channel access data * * @param[in] chNum - channel number @@ -367,4 +358,20 @@ ipmi_ret_t getChannelEnabledAuthType(const uint8_t chNum, const uint8_t priv, */ std::string getChannelName(const uint8_t chNum); +/** @brief Retrieves the LAN channel number from the IPMI channel name + * + * @param[in] chName - IPMI channel name (i.e. eth0) + * + * @return the LAN channel number + */ +uint8_t getChannelByName(const std::string& chName); + +/** @brief determines whether payload type is valid + * + * @param[in] payload type - Payload Type + * + * @return true if valid, false otherwise + */ +bool isValidPayloadType(const PayloadType payloadType); + } // namespace ipmi diff --git a/user_channel/channel_mgmt.cpp b/user_channel/channel_mgmt.cpp index 3fb19b2..759de43 100644 --- a/user_channel/channel_mgmt.cpp +++ b/user_channel/channel_mgmt.cpp @@ -34,7 +34,6 @@ namespace ipmi { -namespace variant_ns = sdbusplus::message::variant_ns; using namespace phosphor::logging; static constexpr const char* channelAccessDefaultFilename = @@ -143,7 +142,7 @@ std::string ChannelConfig::getChannelName(const uint8_t chNum) if (!isValidChannel(chNum)) { log<level::ERR>("Invalid channel number.", - entry("ChannelID:%d", chNum)); + entry("CHANNEL_ID=%d", chNum)); throw std::invalid_argument("Invalid channel number"); } @@ -161,7 +160,7 @@ int ChannelConfig::convertToChannelNumberFromChannelName( } } log<level::ERR>("Invalid channel name.", - entry("Channel:%s", chName.c_str())); + entry("CHANNEL=%s", chName.c_str())); throw std::invalid_argument("Invalid channel name"); return -1; @@ -169,15 +168,15 @@ int ChannelConfig::convertToChannelNumberFromChannelName( std::string ChannelConfig::getChannelNameFromPath(const std::string& path) { - std::size_t pos = path.find(networkIntfObjectBasePath); - if (pos == std::string::npos) + + constexpr size_t length = strlen(networkIntfObjectBasePath); + if (((length + 1) >= path.size()) || + path.compare(0, length, networkIntfObjectBasePath)) { - log<level::ERR>("Invalid interface path.", - entry("PATH:%s", path.c_str())); - throw std::invalid_argument("Invalid interface path"); + log<level::ERR>("Invalid object path.", entry("PATH=%s", path.c_str())); + throw std::invalid_argument("Invalid object path"); } - std::string chName = - path.substr(pos + strlen(networkIntfObjectBasePath) + 1); + std::string chName(path, length + 1); return chName; } @@ -192,7 +191,7 @@ void ChannelConfig::processChAccessPropChange( } catch (const std::invalid_argument& e) { - log<level::ERR>("Exception: ", entry("MSG: %s", e.what())); + log<level::ERR>("Exception: ", entry("MSG=%s", e.what())); return; } @@ -204,7 +203,7 @@ void ChannelConfig::processChAccessPropChange( if (prop.first == privilegePropertyString) { propName = privilegePropertyString; - intfPrivStr = variant_ns::get<std::string>(prop.second); + intfPrivStr = std::get<std::string>(prop.second); break; } } @@ -218,7 +217,7 @@ void ChannelConfig::processChAccessPropChange( if (intfPrivStr.empty()) { log<level::ERR>("Invalid privilege string.", - entry("INTF:%s", chName.c_str())); + entry("INTF=%s", chName.c_str())); return; } @@ -231,7 +230,7 @@ void ChannelConfig::processChAccessPropChange( } catch (const std::invalid_argument& e) { - log<level::ERR>("Exception: ", entry("MSG: %s", e.what())); + log<level::ERR>("Exception: ", entry("MSG=%s", e.what())); return; } @@ -614,7 +613,7 @@ ipmi_ret_t ChannelConfig::setChannelAccessPersistData( { log<level::DEBUG>( "Network interface does not exist", - entry("INTERFACE:%s", channelData[chNum].chName.c_str())); + entry("INTERFACE=%s", channelData[chNum].chName.c_str())); return IPMI_CC_UNSPECIFIED_ERROR; } } @@ -787,45 +786,13 @@ EChannelProtocolType return static_cast<EChannelProtocolType>(it->second); } -uint8_t ChannelConfig::convertToChannelIndexNumber(const uint8_t chNum) -{ - - // TODO: There is limitation in current design. we cannot detect exact - // LAN interface(eth0 or eth1) so Implementation may be updated - // when there is any design update to figure out all the interfaces - // independently based on the message. - - static uint8_t curChannel = 0xFF; - - if (curChannel == 0xFF) - { - auto it = interfaceMap.find(getInterfaceIndex()); - if (it == interfaceMap.end()) - { - log<level::ERR>("Invalid Interface type ", - entry("InterfaceIndex: %d", getInterfaceIndex())); - throw std::invalid_argument("Invalid interface type."); - } - - for (auto& channel : channelData) - { - std::string& interfaceName = it->second; - if (channel.chName == interfaceName) - { - curChannel = channel.chID; - break; - } - } - } - return ((chNum == currentChNum) ? curChannel : chNum); -} - Json ChannelConfig::readJsonFile(const std::string& configFile) { std::ifstream jsonFile(configFile); if (!jsonFile.good()) { - log<level::ERR>("JSON file not found"); + log<level::INFO>("JSON file not found", + entry("FILE_NAME=%s", configFile.c_str())); return nullptr; } @@ -837,7 +804,7 @@ Json ChannelConfig::readJsonFile(const std::string& configFile) catch (Json::parse_error& e) { log<level::DEBUG>("Corrupted channel config.", - entry("MSG: %s", e.what())); + entry("MSG=%s", e.what())); throw std::runtime_error("Corrupted channel config file"); } @@ -847,17 +814,33 @@ Json ChannelConfig::readJsonFile(const std::string& configFile) int ChannelConfig::writeJsonFile(const std::string& configFile, const Json& jsonData) { - std::ofstream jsonFile(configFile); - if (!jsonFile.good()) + const std::string tmpFile = configFile + "_tmp"; + int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) + { + log<level::ERR>("Error in creating json file", + entry("FILE_NAME = %s", tmpFile.c_str())); + return -EIO; + } + const auto& writeData = jsonData.dump(); + if (write(fd, writeData.c_str(), writeData.size()) != + static_cast<ssize_t>(writeData.size())) { - log<level::ERR>("JSON file not found"); + close(fd); + log<level::ERR>("Error in writing configuration file", + entry("FILE_NAME = %s", tmpFile.c_str())); return -EIO; } + close(fd); - // Write JSON to file - jsonFile << jsonData; + if (std::rename(tmpFile.c_str(), configFile.c_str()) != 0) + { + log<level::ERR>("Error in renaming temporary data file", + entry("FILE_NAME = %s", tmpFile.c_str())); + return -EIO; + } - jsonFile.flush(); return 0; } @@ -900,7 +883,7 @@ int ChannelConfig::loadChannelConfig() { log<level::WARNING>( "Channel not configured so loading default.", - entry("CHANNEL_NUM:%d", chNum)); + entry("CHANNEL_NUM=%d", chNum)); // If user didn't want to configure specific channel (say // reserved channel), then load that index with default values. setDefaultChannelConfig(chNum, defaultChannelName); @@ -938,12 +921,12 @@ int ChannelConfig::loadChannelConfig() catch (const Json::exception& e) { log<level::DEBUG>("Json Exception caught.", - entry("MSG:%s", e.what())); + entry("MSG=%s", e.what())); return -EBADMSG; } catch (const std::invalid_argument& e) { - log<level::ERR>("Corrupted config.", entry("MSG:%s", e.what())); + log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what())); return -EBADMSG; } } @@ -997,7 +980,7 @@ int ChannelConfig::readChannelVolatileData() { log<level::ERR>( "Invalid/corrupted volatile channel access file", - entry("FILE: %s", channelVolatileDataFilename)); + entry("FILE=%s", channelVolatileDataFilename)); throw std::runtime_error( "Corrupted volatile channel access file"); } @@ -1005,12 +988,12 @@ int ChannelConfig::readChannelVolatileData() } catch (const Json::exception& e) { - log<level::DEBUG>("Json Exception caught.", entry("MSG:%s", e.what())); + log<level::DEBUG>("Json Exception caught.", entry("MSG=%s", e.what())); throw std::runtime_error("Corrupted volatile channel access file"); } catch (const std::invalid_argument& e) { - log<level::ERR>("Corrupted config.", entry("MSG:%s", e.what())); + log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what())); throw std::runtime_error("Corrupted volatile channel access file"); } @@ -1065,19 +1048,19 @@ int ChannelConfig::readChannelPersistData() else { log<level::ERR>("Invalid/corrupted nv channel access file", - entry("FILE:%s", channelNvDataFilename)); + entry("FILE=%s", channelNvDataFilename)); throw std::runtime_error("Corrupted nv channel access file"); } } } catch (const Json::exception& e) { - log<level::DEBUG>("Json Exception caught.", entry("MSG:%s", e.what())); + log<level::DEBUG>("Json Exception caught.", entry("MSG=%s", e.what())); throw std::runtime_error("Corrupted nv channel access file"); } catch (const std::invalid_argument& e) { - log<level::ERR>("Corrupted config.", entry("MSG: %s", e.what())); + log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what())); throw std::runtime_error("Corrupted nv channel access file"); } @@ -1120,7 +1103,7 @@ int ChannelConfig::writeChannelVolatileData() } catch (const std::invalid_argument& e) { - log<level::ERR>("Corrupted config.", entry("MSG: %s", e.what())); + log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what())); return -EINVAL; } @@ -1171,7 +1154,7 @@ int ChannelConfig::writeChannelPersistData() } catch (const std::invalid_argument& e) { - log<level::ERR>("Corrupted config.", entry("MSG: %s", e.what())); + log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what())); return -EINVAL; } @@ -1245,10 +1228,10 @@ int ChannelConfig::setDbusProperty(const std::string& service, catch (const sdbusplus::exception::SdBusError& e) { log<level::DEBUG>("set-property failed", - entry("SERVICE:%s", service.c_str()), - entry("OBJPATH:%s", objPath.c_str()), - entry("INTERFACE:%s", interface.c_str()), - entry("PROP:%s", property.c_str())); + entry("SERVICE=%s", service.c_str()), + entry("OBJPATH=%s", objPath.c_str()), + entry("INTERFACE=%s", interface.c_str()), + entry("PROP=%s", property.c_str())); return -EIO; } @@ -1275,10 +1258,10 @@ int ChannelConfig::getDbusProperty(const std::string& service, catch (const sdbusplus::exception::SdBusError& e) { log<level::DEBUG>("get-property failed", - entry("SERVICE:%s", service.c_str()), - entry("OBJPATH:%s", objPath.c_str()), - entry("INTERFACE:%s", interface.c_str()), - entry("PROP:%s", property.c_str())); + entry("SERVICE=%s", service.c_str()), + entry("OBJPATH=%s", objPath.c_str()), + entry("INTERFACE=%s", interface.c_str()), + entry("PROP=%s", property.c_str())); return -EIO; } return 0; @@ -1305,13 +1288,13 @@ int ChannelConfig::syncNetworkChannelConfig() privilegePropertyString, variant)) { log<level::DEBUG>("Network interface does not exist", - entry("INTERFACE:%s", + entry("INTERFACE=%s", channelData[chNum].chName.c_str())); continue; } - intfPrivStr = variant_ns::get<std::string>(variant); + intfPrivStr = std::get<std::string>(variant); } - catch (const variant_ns::bad_variant_access& e) + catch (const std::bad_variant_access& e) { log<level::DEBUG>( "exception: Network interface does not exist"); @@ -1358,6 +1341,9 @@ int ChannelConfig::syncNetworkChannelConfig() void ChannelConfig::initChannelPersistData() { + boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex> + channelLock{*channelMutex}; + /* Always read the channel config */ if (loadChannelConfig() != 0) { diff --git a/user_channel/channel_mgmt.hpp b/user_channel/channel_mgmt.hpp index 35bb494..80bd6d9 100644 --- a/user_channel/channel_mgmt.hpp +++ b/user_channel/channel_mgmt.hpp @@ -23,14 +23,14 @@ #include <ctime> #include <nlohmann/json.hpp> #include <sdbusplus/bus.hpp> +#include <variant> namespace ipmi { using Json = nlohmann::json; -using DbusVariant = - sdbusplus::message::variant<std::vector<std::string>, std::string, bool>; +using DbusVariant = std::variant<std::vector<std::string>, std::string, bool>; using DbusChObjProperties = std::vector<std::pair<std::string, DbusVariant>>; @@ -105,6 +105,18 @@ class ChannelConfig */ std::string getChannelName(const uint8_t chNum); + /** @brief function to get channel number from channel name + * + * @param[in] chName - channel name + * + * @return network channel interface number + */ + + uint8_t getChannelByName(const std::string& chName) + { + return convertToChannelNumberFromChannelName(chName); + } + /** @brief determines supported session type of a channel * * @param[in] chNum - channel number @@ -212,14 +224,6 @@ class ChannelConfig */ CommandPrivilege convertToPrivLimitIndex(const std::string& value); - /** @brief function to convert channel number to channel index - * - * @param[in] chNum - channel number - * - * @return channel index - */ - uint8_t convertToChannelIndexNumber(const uint8_t chNum); - /** @brief function to write persistent channel configuration to config file * * @return 0 for success, -errno for failure. diff --git a/user_channel/channelcommands.cpp b/user_channel/channelcommands.cpp index d1b275e..adf19d5 100644 --- a/user_channel/channelcommands.cpp +++ b/user_channel/channelcommands.cpp @@ -14,11 +14,10 @@ // limitations under the License. */ -#include "channelcommands.hpp" - #include "apphandler.hpp" #include "channel_layer.hpp" +#include <ipmid/api.hpp> #include <phosphor-logging/log.hpp> #include <regex> @@ -27,241 +26,99 @@ using namespace phosphor::logging; namespace ipmi { -/** @struct SetChannelAccessReq - * - * Structure for set channel access request command (refer spec sec 22.22) - */ -struct SetChannelAccessReq -{ -#if BYTE_ORDER == LITTLE_ENDIAN - uint8_t chNum : 4; - uint8_t reserved_1 : 4; - uint8_t accessMode : 3; - uint8_t usrAuthDisabled : 1; - uint8_t msgAuthDisabled : 1; - uint8_t alertDisabled : 1; - uint8_t accessSetMode : 2; - uint8_t privLimit : 4; - uint8_t reserved_2 : 2; - uint8_t privSetMode : 2; -#endif -#if BYTE_ORDER == BIG_ENDIAN - uint8_t reserved_1 : 4; - uint8_t chNum : 4; - uint8_t accessSetMode : 2; - uint8_t alertDisabled : 1; - uint8_t msgAuthDisabled : 1; - uint8_t usrAuthDisabled : 1; - uint8_t accessMode : 3; - uint8_t privSetMode : 2; - uint8_t reserved_2 : 2; - uint8_t privLimit : 4; -#endif - -} __attribute__((packed)); - -/** @struct GetChannelAccessReq - * - * Structure for get channel access request command (refer spec sec 22.23) - */ -struct GetChannelAccessReq -{ -#if BYTE_ORDER == LITTLE_ENDIAN - uint8_t chNum : 4; - uint8_t reserved_1 : 4; - uint8_t reserved_2 : 6; - uint8_t accessSetMode : 2; -#endif -#if BYTE_ORDER == BIG_ENDIAN - uint8_t reserved_1 : 4; - uint8_t chNum : 4; - uint8_t accessSetMode : 2; - uint8_t reserved_2 : 6; -#endif -} __attribute__((packed)); - -/** @struct GetChannelAccessResp +static constexpr const uint8_t ccActionNotSupportedForChannel = 0x82; + +/** @brief implements the set channel access command + * @ param ctx - context pointer + * @ param channel - channel number + * @ param reserved - skip 4 bits + * @ param accessMode - access mode for IPMI messaging + * @ param usrAuth - user level authentication (enable/disable) + * @ param msgAuth - per message authentication (enable/disable) + * @ param alertDisabled - PEF alerting (enable/disable) + * @ param chanAccess - channel access + * @ param channelPrivLimit - channel privilege limit + * @ param reserved - skip 3 bits + * @ param channelPrivMode - channel priviledge mode * - * Structure for get channel access response command (refer spec sec 22.23) - */ -struct GetChannelAccessResp + * @ returns IPMI completion code + **/ +RspType<> ipmiSetChannelAccess(Context::ptr ctx, uint4_t channel, + uint4_t reserved1, uint3_t accessMode, + bool usrAuth, bool msgAuth, bool alertDisabled, + uint2_t chanAccess, uint4_t channelPrivLimit, + uint2_t reserved2, uint2_t channelPrivMode) { -#if BYTE_ORDER == LITTLE_ENDIAN - uint8_t accessMode : 3; - uint8_t usrAuthDisabled : 1; - uint8_t msgAuthDisabled : 1; - uint8_t alertDisabled : 1; - uint8_t reserved_1 : 2; - uint8_t privLimit : 4; - uint8_t reserved_2 : 4; -#endif -#if BYTE_ORDER == BIG_ENDIAN - uint8_t reserved_1 : 2; - uint8_t alertDisabled : 1; - uint8_t msgAuthDisabled : 1; - uint8_t usrAuthDisabled : 1; - uint8_t accessMode : 3; - uint8_t reserved_2 : 4; - uint8_t privLimit : 4; -#endif -} __attribute__((packed)); - -/** @struct GetChannelInfoReq - * - * Structure for get channel info request command (refer spec sec 22.24) - */ -struct GetChannelInfoReq -{ -#if BYTE_ORDER == LITTLE_ENDIAN - uint8_t chNum : 4; - uint8_t reserved_1 : 4; -#endif -#if BYTE_ORDER == BIG_ENDIAN - uint8_t reserved_1 : 4; - uint8_t chNum : 4; -#endif -} __attribute__((packed)); - -/** @struct GetChannelInfoResp - * - * Structure for get channel info response command (refer spec sec 22.24) - */ -struct GetChannelInfoResp -{ -#if BYTE_ORDER == LITTLE_ENDIAN - uint8_t chNum : 4; - uint8_t reserved_1 : 4; - uint8_t mediumType : 7; - uint8_t reserved_2 : 1; - uint8_t msgProtType : 5; - uint8_t reserved_3 : 3; - uint8_t actSessCount : 6; - uint8_t sessType : 2; -#endif -#if BYTE_ORDER == BIG_ENDIAN - uint8_t reserved_1 : 4; - uint8_t chNum : 4; - uint8_t reserved_2 : 1; - uint8_t mediumType : 7; - uint8_t reserved_3 : 3; - uint8_t msgProtType : 5; - uint8_t sessType : 2; - uint8_t actSessCount : 6; -#endif - uint8_t vendorId[3]; - uint8_t auxChInfo[2]; -} __attribute__((packed)); - -/** @struct GetChannelPayloadSupportReq - * - * Structure for get channel payload support command request (refer spec - * sec 24.8) - */ -struct GetChannelPayloadSupportReq -{ -#if BYTE_ORDER == LITTLE_ENDIAN - uint8_t chNum : 4; - uint8_t reserved : 4; -#endif -#if BYTE_ORDER == BIG_ENDIAN - uint8_t reserved : 4; - uint8_t chNum : 4; -#endif -} __attribute__((packed)); - -/** @struct GetChannelPayloadSupportResp - * - * Structure for get channel payload support command response (refer spec - * sec 24.8) - */ -struct GetChannelPayloadSupportResp -{ - uint8_t stdPayloadType[2]; - uint8_t sessSetupPayloadType[2]; - uint8_t OEMPayloadType[2]; - uint8_t reserved[2]; -} __attribute__((packed)); - -ipmi_ret_t ipmiSetChannelAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd, - ipmi_request_t request, - ipmi_response_t response, - ipmi_data_len_t data_len, - ipmi_context_t context) -{ - const SetChannelAccessReq* req = static_cast<SetChannelAccessReq*>(request); - size_t reqLength = *data_len; - - *data_len = 0; + const uint8_t chNum = + convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel); - if (reqLength != sizeof(*req)) - { - log<level::DEBUG>("Set channel access - Invalid Length"); - return IPMI_CC_REQ_DATA_LEN_INVALID; - } - - uint8_t chNum = convertCurrentChannelNum(req->chNum); - if (!isValidChannel(chNum) || req->reserved_1 != 0 || req->reserved_2 != 0) + if (!isValidChannel(chNum) || reserved1 != 0 || reserved2 != 0) { log<level::DEBUG>("Set channel access - Invalid field in request"); - return IPMI_CC_INVALID_FIELD_REQUEST; + return responseInvalidFieldRequest(); } - if (EChannelSessSupported::none == getChannelSessionSupport(chNum)) + if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) { log<level::DEBUG>("Set channel access - No support on channel"); - return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; + return response(ccActionNotSupportedForChannel); } ChannelAccess chActData; ChannelAccess chNVData; uint8_t setActFlag = 0; uint8_t setNVFlag = 0; - ipmi_ret_t compCode = IPMI_CC_OK; + Cc compCode; - switch (req->accessSetMode) + // cannot static cast directly from uint2_t to enum; must go via int + uint8_t channelAccessAction = static_cast<uint8_t>(chanAccess); + switch (static_cast<EChannelActionType>(channelAccessAction)) { case doNotSet: - // Do nothing break; case nvData: - chNVData.accessMode = req->accessMode; - chNVData.userAuthDisabled = req->usrAuthDisabled; - chNVData.perMsgAuthDisabled = req->msgAuthDisabled; - chNVData.alertingDisabled = req->alertDisabled; + chNVData.accessMode = static_cast<uint8_t>(accessMode); + chNVData.userAuthDisabled = usrAuth; + chNVData.perMsgAuthDisabled = msgAuth; + chNVData.alertingDisabled = alertDisabled; setNVFlag |= (setAccessMode | setUserAuthEnabled | setMsgAuthEnabled | setAlertingEnabled); break; + case activeData: - chActData.accessMode = req->accessMode; - chActData.userAuthDisabled = req->usrAuthDisabled; - chActData.perMsgAuthDisabled = req->msgAuthDisabled; - chActData.alertingDisabled = req->alertDisabled; + chActData.accessMode = static_cast<uint8_t>(accessMode); + chActData.userAuthDisabled = usrAuth; + chActData.perMsgAuthDisabled = msgAuth; + chActData.alertingDisabled = alertDisabled; setActFlag |= (setAccessMode | setUserAuthEnabled | setMsgAuthEnabled | setAlertingEnabled); break; + case reserved: default: log<level::DEBUG>("Set channel access - Invalid access set mode"); - return IPMI_CC_INVALID_FIELD_REQUEST; + return responseInvalidFieldRequest(); } - switch (req->privSetMode) + // cannot static cast directly from uint2_t to enum; must go via int + uint8_t channelPrivAction = static_cast<uint8_t>(channelPrivMode); + switch (static_cast<EChannelActionType>(channelPrivAction)) { case doNotSet: - // Do nothing break; case nvData: - chNVData.privLimit = req->privLimit; + chNVData.privLimit = static_cast<uint8_t>(channelPrivLimit); setNVFlag |= setPrivLimit; break; case activeData: - chActData.privLimit = req->privLimit; + chActData.privLimit = static_cast<uint8_t>(channelPrivLimit); + setActFlag |= setPrivLimit; break; case reserved: default: log<level::DEBUG>("Set channel access - Invalid access priv mode"); - return IPMI_CC_INVALID_FIELD_REQUEST; + return responseInvalidFieldRequest(); } if (setNVFlag != 0) @@ -270,7 +127,7 @@ ipmi_ret_t ipmiSetChannelAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd, if (compCode != IPMI_CC_OK) { log<level::DEBUG>("Set channel access - Failed to set access data"); - return compCode; + return response(compCode); } } @@ -280,219 +137,250 @@ ipmi_ret_t ipmiSetChannelAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd, if (compCode != IPMI_CC_OK) { log<level::DEBUG>("Set channel access - Failed to set access data"); - return compCode; + return response(compCode); } } - return IPMI_CC_OK; + return responseSuccess(); } -ipmi_ret_t ipmiGetChannelAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd, - ipmi_request_t request, - ipmi_response_t response, - ipmi_data_len_t data_len, - ipmi_context_t context) +/** @brief implements the get channel access command + * @ param ctx - context pointer + * @ param channel - channel number + * @ param reserved1 - skip 4 bits + * @ param reserved2 - skip 6 bits + * @ param accessMode - get access mode + * + * @returns ipmi completion code plus response data + * - accessMode - get access mode + * - usrAuthDisabled - user level authentication status + * - msgAuthDisabled - message level authentication status + * - alertDisabled - alerting status + * - reserved - skip 2 bits + * - privLimit - channel privilege limit + * - reserved - skip 4 bits + * */ +ipmi ::RspType<uint3_t, // access mode, + bool, // user authentication status, + bool, // message authentication status, + bool, // alerting status, + uint2_t, // reserved, + + uint4_t, // channel privilege, + uint4_t // reserved + > + ipmiGetChannelAccess(Context::ptr ctx, uint4_t channel, uint4_t reserved1, + uint6_t reserved2, uint2_t accessSetMode) { - const GetChannelAccessReq* req = static_cast<GetChannelAccessReq*>(request); - size_t reqLength = *data_len; - - *data_len = 0; - - if (reqLength != sizeof(*req)) - { - log<level::DEBUG>("Get channel access - Invalid Length"); - return IPMI_CC_REQ_DATA_LEN_INVALID; - } + const uint8_t chNum = + convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel); - uint8_t chNum = convertCurrentChannelNum(req->chNum); - if (!isValidChannel(chNum) || req->reserved_1 != 0 || req->reserved_2 != 0) + if (!isValidChannel(chNum) || reserved1 != 0 || reserved2 != 0) { log<level::DEBUG>("Get channel access - Invalid field in request"); - return IPMI_CC_INVALID_FIELD_REQUEST; + return responseInvalidFieldRequest(); } - if ((req->accessSetMode == doNotSet) || (req->accessSetMode == reserved)) + if ((accessSetMode == doNotSet) || (accessSetMode == reserved)) { log<level::DEBUG>("Get channel access - Invalid Access mode"); - return IPMI_CC_INVALID_FIELD_REQUEST; + return responseInvalidFieldRequest(); } - if (EChannelSessSupported::none == getChannelSessionSupport(chNum)) + if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) { log<level::DEBUG>("Get channel access - No support on channel"); - return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; + return response(ccActionNotSupportedForChannel); } - GetChannelAccessResp* resp = static_cast<GetChannelAccessResp*>(response); - - std::fill(reinterpret_cast<uint8_t*>(resp), - reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0); - ChannelAccess chAccess; - ipmi_ret_t compCode = IPMI_CC_OK; - if (req->accessSetMode == nvData) + Cc compCode; + + if (accessSetMode == nvData) { compCode = getChannelAccessPersistData(chNum, chAccess); } - else if (req->accessSetMode == activeData) + else if (accessSetMode == activeData) { compCode = getChannelAccessData(chNum, chAccess); } if (compCode != IPMI_CC_OK) { - return compCode; + return response(compCode); } - resp->accessMode = chAccess.accessMode; - resp->usrAuthDisabled = chAccess.userAuthDisabled; - resp->msgAuthDisabled = chAccess.perMsgAuthDisabled; - resp->alertDisabled = chAccess.alertingDisabled; - resp->privLimit = chAccess.privLimit; + constexpr uint2_t reservedOut1 = 0; + constexpr uint4_t reservedOut2 = 0; - *data_len = sizeof(*resp); - return IPMI_CC_OK; + return responseSuccess( + static_cast<uint3_t>(chAccess.accessMode), chAccess.userAuthDisabled, + chAccess.perMsgAuthDisabled, chAccess.alertingDisabled, reservedOut1, + static_cast<uint4_t>(chAccess.privLimit), reservedOut2); } -ipmi_ret_t ipmiGetChannelInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd, - ipmi_request_t request, ipmi_response_t response, - ipmi_data_len_t data_len, ipmi_context_t context) +/** @brief implements the get channel info command + * @ param ctx - context pointer + * @ param channel - channel number + * @ param reserved - skip 4 bits + * + * @returns ipmi completion code plus response data + * - chNum - the channel number for this request + * - mediumType - see Table 6-3, Channel Medium Type Numbers + * - protocolType - Table 6-2, Channel Protocol Type Numbers + * - activeSessionCount - number of active sessions + * - sessionType - channel support for sessions + * - vendorId - vendor for this channel protocol (IPMI - 7154) + * - auxChInfo - auxiliary info for channel + * */ +RspType<uint4_t, // chNum + uint4_t, // reserved + uint7_t, // mediumType + bool, // reserved + uint5_t, // protocolType + uint3_t, // reserved + uint6_t, // activeSessionCount + uint2_t, // sessionType + uint24_t, // Vendor IANA + uint16_t // aux info + > + ipmiGetChannelInfo(Context::ptr ctx, uint4_t channel, uint4_t reserved) { - const GetChannelInfoReq* req = static_cast<GetChannelInfoReq*>(request); - size_t reqLength = *data_len; - - *data_len = 0; - - if (reqLength != sizeof(*req)) - { - log<level::DEBUG>("Get channel info - Invalid Length"); - return IPMI_CC_REQ_DATA_LEN_INVALID; - } - - uint8_t chNum = convertCurrentChannelNum(req->chNum); - if (!isValidChannel(chNum) || req->reserved_1 != 0) + uint8_t chNum = + convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel); + if (!isValidChannel(chNum) || reserved) { - log<level::DEBUG>("Get channel info - Invalid field in request"); - return IPMI_CC_INVALID_FIELD_REQUEST; - } - - // Check the existance of device for session-less channels. - if ((EChannelSessSupported::none != getChannelSessionSupport(chNum)) && - (!(doesDeviceExist(chNum)))) - { - log<level::DEBUG>("Get channel info - Device not exist"); - return IPMI_CC_PARM_OUT_OF_RANGE; + log<level::DEBUG>("Get channel access - Invalid field in request"); + return responseInvalidFieldRequest(); } - GetChannelInfoResp* resp = static_cast<GetChannelInfoResp*>(response); - - std::fill(reinterpret_cast<uint8_t*>(resp), - reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0); - ChannelInfo chInfo; - ipmi_ret_t compCode = getChannelInfo(chNum, chInfo); - if (compCode != IPMI_CC_OK) + Cc compCode = getChannelInfo(chNum, chInfo); + if (compCode != ccSuccess) { - return compCode; + log<level::ERR>("Failed to get channel info", + entry("CHANNEL=%x", chNum), + entry("ERRNO=%x", compCode)); + return response(compCode); } - resp->chNum = chNum; - resp->mediumType = chInfo.mediumType; - resp->msgProtType = chInfo.protocolType; - resp->actSessCount = getChannelActiveSessions(chNum); - resp->sessType = chInfo.sessionSupported; - + constexpr uint4_t reserved1 = 0; + constexpr bool reserved2 = false; + constexpr uint3_t reserved3 = 0; + uint8_t mediumType = chInfo.mediumType; + uint8_t protocolType = chInfo.protocolType; + uint2_t sessionType = chInfo.sessionSupported; + uint6_t activeSessionCount = getChannelActiveSessions(chNum); // IPMI Spec: The IPMI Enterprise Number is: 7154 (decimal) - resp->vendorId[0] = 0xF2; - resp->vendorId[1] = 0x1B; - resp->vendorId[2] = 0x00; - - // Auxiliary Channel info - byte 1:2 - // TODO: For System Interface(0xF) and OEM channel types, this needs - // to be changed acoordingly. - // All other channel types, its reverved - resp->auxChInfo[0] = 0x00; - resp->auxChInfo[1] = 0x00; + constexpr uint24_t vendorId = 7154; + constexpr uint16_t auxChInfo = 0; - *data_len = sizeof(*resp); - - return IPMI_CC_OK; + return responseSuccess(chNum, reserved1, mediumType, reserved2, + protocolType, reserved3, activeSessionCount, + sessionType, vendorId, auxChInfo); } -ipmi_ret_t ipmiGetChannelPayloadSupport(ipmi_netfn_t netfn, ipmi_cmd_t cmd, - ipmi_request_t request, - ipmi_response_t response, - ipmi_data_len_t data_len, - ipmi_context_t context) +namespace { - const auto req = static_cast<GetChannelPayloadSupportReq*>(request); - size_t reqLength = *data_len; - - *data_len = 0; - - if (reqLength != sizeof(*req)) - { - log<level::DEBUG>("Get channel payload - Invalid Length"); - return IPMI_CC_REQ_DATA_LEN_INVALID; - } +constexpr uint16_t standardPayloadBit(PayloadType p) +{ + return (1 << static_cast<size_t>(p)); +} - uint8_t chNum = convertCurrentChannelNum(req->chNum); - if (!isValidChannel(chNum) || req->reserved != 0) - { - log<level::DEBUG>("Get channel payload - Invalid field in request"); - return IPMI_CC_INVALID_FIELD_REQUEST; - } +constexpr uint16_t sessionPayloadBit(PayloadType p) +{ + constexpr size_t sessionShift = + static_cast<size_t>(PayloadType::OPEN_SESSION_REQUEST); + return ((1 << static_cast<size_t>(p)) >> sessionShift); +} +} // namespace - // Not supported on sessionless channels. - if (EChannelSessSupported::none == getChannelSessionSupport(chNum)) +/** @brief implements get channel payload support command + * @ param ctx - ipmi context pointer + * @ param chNum - channel number + * @ param reserved - skip 4 bits + * + * @ returns IPMI completion code plus response data + * - stdPayloadType - bitmask of supported standard payload types + * - sessSetupPayloadType - bitmask of supported session setup payload types + * - OEMPayloadType - bitmask of supported OEM payload types + * - reserved - 2 bytes of 0 + **/ +RspType<uint16_t, // stdPayloadType + uint16_t, // sessSetupPayloadType + uint16_t, // OEMPayloadType + uint16_t // reserved + > + ipmiGetChannelPayloadSupport(Context::ptr ctx, uint4_t channel, + uint4_t reserved) +{ + uint8_t chNum = + convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel); + if (!isValidChannel(chNum) || reserved) { - log<level::DEBUG>("Get channel payload - Sessionless Channel"); - return IPMI_CC_INVALID_FIELD_REQUEST; + log<level::DEBUG>("Get channel access - Invalid field in request"); + return responseInvalidFieldRequest(); } // Session support is available in active LAN channels. - if ((EChannelSessSupported::none != getChannelSessionSupport(chNum)) && - (!(doesDeviceExist(chNum)))) + if ((getChannelSessionSupport(chNum) == EChannelSessSupported::none) || + !(doesDeviceExist(chNum))) { log<level::DEBUG>("Get channel payload - Device not exist"); - return IPMI_CC_INVALID_FIELD_REQUEST; + return responseInvalidFieldRequest(); } - auto resp = static_cast<GetChannelPayloadSupportResp*>(response); + constexpr uint16_t stdPayloadType = standardPayloadBit(PayloadType::IPMI) | + standardPayloadBit(PayloadType::SOL); + constexpr uint16_t sessSetupPayloadType = + sessionPayloadBit(PayloadType::OPEN_SESSION_REQUEST) | + sessionPayloadBit(PayloadType::OPEN_SESSION_RESPONSE) | + sessionPayloadBit(PayloadType::RAKP1) | + sessionPayloadBit(PayloadType::RAKP2) | + sessionPayloadBit(PayloadType::RAKP3) | + sessionPayloadBit(PayloadType::RAKP4); + constexpr uint16_t OEMPayloadType = 0; + constexpr uint16_t rspRsvd = 0; + return responseSuccess(stdPayloadType, sessSetupPayloadType, OEMPayloadType, + rspRsvd); +} - std::fill(reinterpret_cast<uint8_t*>(resp), - reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0); +/** @brief implements the get channel payload version command + * @param ctx - IPMI context pointer (for channel) + * @param chNum - channel number to get info about + * @param reserved - skip 4 bits + * @param payloadTypeNum - to get payload type info - // TODO: Hard coding for now. - // Mapping PayloadTypes to 'GetChannelPayloadSupportResp' fields: - // -------------------------------------------------------------- - // Mask all except least 3 significant bits to get a value in the range of - // 0-7. This value maps to the bit position of given payload type in 'resp' - // fields. + * @returns IPMI completion code plus response data + * - formatVersion - BCD encoded format version info + */ - static constexpr uint8_t payloadByteMask = 0x07; - static constexpr uint8_t stdPayloadTypeIPMI = - 1 << (static_cast<uint8_t>(PayloadType::IPMI) & payloadByteMask); - static constexpr uint8_t stdPayloadTypeSOL = - 1 << (static_cast<uint8_t>(PayloadType::SOL) & payloadByteMask); +RspType<uint8_t> // formatVersion + ipmiGetChannelPayloadVersion(Context::ptr ctx, uint4_t chNum, + uint4_t reserved, uint8_t payloadTypeNum) +{ + uint8_t channel = + convertCurrentChannelNum(static_cast<uint8_t>(chNum), ctx->channel); - static constexpr uint8_t sessPayloadTypeOpenReq = - 1 << (static_cast<uint8_t>(PayloadType::OPEN_SESSION_REQUEST) & - payloadByteMask); - static constexpr uint8_t sessPayloadTypeRAKP1 = - 1 << (static_cast<uint8_t>(PayloadType::RAKP1) & payloadByteMask); - static constexpr uint8_t sessPayloadTypeRAKP3 = - 1 << (static_cast<uint8_t>(PayloadType::RAKP3) & payloadByteMask); + if (reserved || !isValidChannel(channel) || + (getChannelSessionSupport(channel)) == EChannelSessSupported::none) + { + return responseInvalidFieldRequest(); + } + + if (!isValidPayloadType(static_cast<PayloadType>(payloadTypeNum))) + { + log<level::ERR>("Channel payload version - Payload type unavailable"); - resp->stdPayloadType[0] = stdPayloadTypeIPMI | stdPayloadTypeSOL; - // RMCP+ Open Session request, RAKP Message1 and RAKP Message3. - resp->sessSetupPayloadType[0] = - sessPayloadTypeOpenReq | sessPayloadTypeRAKP1 | sessPayloadTypeRAKP3; + constexpr uint8_t payloadTypeNotSupported = 0x80; + return response(payloadTypeNotSupported); + } - *data_len = sizeof(*resp); + // BCD encoded version representation - 1.0 + constexpr uint8_t formatVersion = 0x10; - return IPMI_CC_OK; + return responseSuccess(formatVersion); } void registerChannelFunctions() __attribute__((constructor)); @@ -500,19 +388,20 @@ void registerChannelFunctions() { ipmiChannelInit(); - ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHANNEL_ACCESS, NULL, - ipmiSetChannelAccess, PRIVILEGE_ADMIN); + registerHandler(prioOpenBmcBase, netFnApp, app::cmdSetChannelAccess, + Privilege::Admin, ipmiSetChannelAccess); - ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL, - ipmiGetChannelAccess, PRIVILEGE_USER); + registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelAccess, + Privilege::User, ipmiGetChannelAccess); - ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_INFO, NULL, - ipmiGetChannelInfo, PRIVILEGE_USER); + registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelInfoCommand, + Privilege::User, ipmiGetChannelInfo); - ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_PAYLOAD_SUPPORT, - NULL, ipmiGetChannelPayloadSupport, PRIVILEGE_USER); + registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelPayloadSupport, + Privilege::User, ipmiGetChannelPayloadSupport); - return; + registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelPayloadVersion, + Privilege::User, ipmiGetChannelPayloadVersion); } } // namespace ipmi diff --git a/user_channel/channelcommands.hpp b/user_channel/channelcommands.hpp deleted file mode 100644 index abcead6..0000000 --- a/user_channel/channelcommands.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* -// 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 -{ - -/** - * @enum IPMI commands for channel command NETFN:APP - */ -enum ipmi_netfn_channel_cmds -{ - IPMI_CMD_SET_CHANNEL_ACCESS = 0x40, - IPMI_CMD_GET_CHANNEL_ACCESS = 0x41, - IPMI_CMD_GET_CHANNEL_INFO = 0x42, - IPMI_CMD_GET_CHANNEL_PAYLOAD_SUPPORT = 0x4E, -}; - -void registerChannelFunctions(); -} // namespace ipmi diff --git a/user_channel/passwd_mgr.cpp b/user_channel/passwd_mgr.cpp index 525b2b7..66bdef0 100644 --- a/user_channel/passwd_mgr.cpp +++ b/user_channel/passwd_mgr.cpp @@ -217,9 +217,10 @@ void PasswdMgr::initPasswordMap(void) char* outPtr = reinterpret_cast<char*>(dataBuf.data()); char* nToken = NULL; char* linePtr = strtok_r(outPtr, "\n", &nToken); - size_t userEPos = 0, lineSize = 0; + size_t lineSize = 0; while (linePtr != NULL) { + size_t userEPos = 0; std::string lineStr(linePtr); if ((userEPos = lineStr.find(":")) != std::string::npos) { @@ -355,11 +356,12 @@ int PasswdMgr::updatePasswdSpecialFile(const std::string& userName, if (inBytesLen != 0) { char* outPtr = reinterpret_cast<char*>(dataBuf.data()); - size_t userEPos = 0; char* nToken = NULL; char* linePtr = strtok_r(outPtr, "\n", &nToken); while (linePtr != NULL) { + size_t userEPos = 0; + std::string lineStr(linePtr); if ((userEPos = lineStr.find(":")) != std::string::npos) { @@ -441,7 +443,6 @@ int PasswdMgr::updatePasswdSpecialFile(const std::string& userName, log<level::DEBUG>("Error creating temp file"); return -EIO; } - fd = -1; // don't use fd anymore, as the File object owns it // Set the file mode as of actual ipmi-pass file. if (fchmod(fileno((temp)()), st.st_mode) < 0) diff --git a/user_channel/user_layer.cpp b/user_channel/user_layer.cpp index b241564..b309e86 100644 --- a/user_channel/user_layer.cpp +++ b/user_channel/user_layer.cpp @@ -88,6 +88,12 @@ ipmi_ret_t ipmiUserSetUserPassword(const uint8_t userId, return getUserAccessObject().setUserPassword(userId, userPassword); } +ipmi_ret_t ipmiSetSpecialUserPassword(const std::string& userName, + const std::string& userPassword) +{ + return getUserAccessObject().setSpecialUserPassword(userName, userPassword); +} + ipmi_ret_t ipmiUserGetAllCounts(uint8_t& maxChUsers, uint8_t& enabledUsers, uint8_t& fixedUsers) { @@ -164,4 +170,57 @@ ipmi_ret_t ipmiUserSetPrivilegeAccess(const uint8_t userId, const uint8_t chNum, userId, chNum, userPrivAccess, otherPrivUpdates); } +bool ipmiUserPamAuthenticate(std::string_view userName, + std::string_view userPassword) +{ + return pamUserCheckAuthenticate(userName, userPassword); +} + +ipmi_ret_t ipmiUserSetUserPayloadAccess(const uint8_t chNum, + const uint8_t operation, + const uint8_t userId, + const PayloadAccess& payloadAccess) +{ + + if (!UserAccess::isValidChannel(chNum)) + { + return IPMI_CC_INVALID_FIELD_REQUEST; + } + if (!UserAccess::isValidUserId(userId)) + { + return IPMI_CC_PARM_OUT_OF_RANGE; + } + + return getUserAccessObject().setUserPayloadAccess(chNum, operation, userId, + payloadAccess); +} + +ipmi_ret_t ipmiUserGetUserPayloadAccess(const uint8_t chNum, + const uint8_t userId, + PayloadAccess& payloadAccess) +{ + + 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); + + payloadAccess.stdPayloadEnables1 = + userInfo->payloadAccess[chNum].stdPayloadEnables1; + payloadAccess.stdPayloadEnables2Reserved = + userInfo->payloadAccess[chNum].stdPayloadEnables2Reserved; + payloadAccess.oemPayloadEnables1 = + userInfo->payloadAccess[chNum].oemPayloadEnables1; + payloadAccess.oemPayloadEnables2Reserved = + userInfo->payloadAccess[chNum].oemPayloadEnables2Reserved; + + return IPMI_CC_OK; +} + } // namespace ipmi diff --git a/user_channel/user_layer.hpp b/user_channel/user_layer.hpp index 5f3567a..450d878 100644 --- a/user_channel/user_layer.hpp +++ b/user_channel/user_layer.hpp @@ -16,6 +16,7 @@ #pragma once #include <ipmid/api.h> +#include <bitset> #include <string> namespace ipmi @@ -37,6 +38,7 @@ static constexpr uint8_t ipmiMaxUsers = 15; static constexpr uint8_t ipmiMaxChannels = 16; static constexpr uint8_t maxIpmi20PasswordSize = 20; static constexpr uint8_t maxIpmi15PasswordSize = 16; +static constexpr uint8_t payloadsPerByte = 8; /** @struct PrivAccess * @@ -61,6 +63,19 @@ struct PrivAccess #endif } __attribute__((packed)); +/** @struct UserPayloadAccess + * + * Structure to denote payload access restrictions applicable for a + * given user and channel. (refer spec sec 24.6) + */ +struct PayloadAccess +{ + std::bitset<payloadsPerByte> stdPayloadEnables1; + std::bitset<payloadsPerByte> stdPayloadEnables2Reserved; + std::bitset<payloadsPerByte> oemPayloadEnables1; + std::bitset<payloadsPerByte> oemPayloadEnables2Reserved; +}; + /** @brief initializes user management * * @return IPMI_CC_OK for success, others for failure. @@ -138,6 +153,16 @@ ipmi_ret_t ipmiUserSetUserName(const uint8_t userId, const char* userName); ipmi_ret_t ipmiUserSetUserPassword(const uint8_t userId, const char* userPassword); +/** @brief set special user password (non-ipmi accounts) + * + * @param[in] userName - user name + * @param[in] userPassword - New Password + * + * @return IPMI_CC_OK for success, others for failure. + */ +ipmi_ret_t ipmiSetSpecialUserPassword(const std::string& userName, + const std::string& userPassword); + /** @brief get user name * * @param[in] userId - user id @@ -200,4 +225,41 @@ ipmi_ret_t ipmiUserSetPrivilegeAccess(const uint8_t userId, const uint8_t chNum, const PrivAccess& privAccess, const bool& otherPrivUpdate); +/** @brief check for user pam authentication. This is to determine, whether user + * is already locked out for failed login attempt + * + * @param[in] username - username + * @param[in] password - password + * + * @return status + */ +bool ipmiUserPamAuthenticate(std::string_view userName, + std::string_view userPassword); + +/** @brief sets user payload access data + * + * @param[in] chNum - channel number + * @param[in] operation - ENABLE / DISABLE operation + * @param[in] userId - user id + * @param[in] payloadAccess - payload access data + * + * @return IPMI_CC_OK for success, others for failure. + */ +ipmi_ret_t ipmiUserSetUserPayloadAccess(const uint8_t chNum, + const uint8_t operation, + const uint8_t userId, + const PayloadAccess& payloadAccess); + +/** @brief provides user payload access data + * + * @param[in] chNum - channel number + * @param[in] userId - user id + * @param[out] payloadAccess - payload access data + * + * @return IPMI_CC_OK for success, others for failure. + */ +ipmi_ret_t ipmiUserGetUserPayloadAccess(const uint8_t chNum, + const uint8_t userId, + PayloadAccess& payloadAccess); + } // namespace ipmi diff --git a/user_channel/user_mgmt.cpp b/user_channel/user_mgmt.cpp index f0cd7ad..102f990 100644 --- a/user_channel/user_mgmt.cpp +++ b/user_channel/user_mgmt.cpp @@ -16,6 +16,7 @@ #include "user_mgmt.hpp" #include "apphandler.hpp" +#include "channel_layer.hpp" #include <security/pam_appl.h> #include <sys/stat.h> @@ -31,6 +32,7 @@ #include <regex> #include <sdbusplus/bus/match.hpp> #include <sdbusplus/server/object.hpp> +#include <variant> #include <xyz/openbmc_project/Common/error.hpp> #include <xyz/openbmc_project/User/Common/error.hpp> @@ -103,13 +105,10 @@ static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = { "priv-custom" // PRIVILEGE_OEM - 5 }; -namespace variant_ns = sdbusplus::message::variant_ns; - using namespace phosphor::logging; using Json = nlohmann::json; -using PrivAndGroupType = - sdbusplus::message::variant<std::string, std::vector<std::string>>; +using PrivAndGroupType = std::variant<std::string, std::vector<std::string>>; using NoResource = sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource; @@ -198,12 +197,13 @@ UserAccess& getUserAccessObject() int getUserNameFromPath(const std::string& path, std::string& userName) { - static size_t pos = strlen(userObjBasePath) + 1; - if (path.find(userObjBasePath) == std::string::npos) + constexpr size_t length = strlen(userObjBasePath); + if (((length + 1) >= path.size()) || + path.compare(0, length, userObjBasePath)) { return -EINVAL; } - userName.assign(path, pos, path.size()); + userName.assign(path, length + 1, path.size()); return 0; } @@ -308,7 +308,7 @@ void userUpdatedSignalHandler(UserAccess& usrAccess, { static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); std::string signal = msg.get_member(); - std::string userName, update, priv, newUserName; + std::string userName, priv, newUserName; std::vector<std::string> groups; bool enabled = false; UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent; @@ -376,17 +376,17 @@ void userUpdatedSignalHandler(UserAccess& usrAccess, std::string member = prop.first; if (member == userPrivProperty) { - priv = variant_ns::get<std::string>(prop.second); + priv = std::get<std::string>(prop.second); userEvent = UserUpdateEvent::userPrivUpdated; } else if (member == userGrpProperty) { - groups = variant_ns::get<std::vector<std::string>>(prop.second); + groups = std::get<std::vector<std::string>>(prop.second); userEvent = UserUpdateEvent::userGrpUpdated; } else if (member == userEnabledProperty) { - enabled = variant_ns::get<bool>(prop.second); + enabled = std::get<bool>(prop.second); userEvent = UserUpdateEvent::userStateUpdated; } // Process based on event type. @@ -472,43 +472,8 @@ UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection()) userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>( boost::interprocess::open_or_create, ipmiUserMutex); - initUserDataFile(); + cacheUserDataFile(); 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) @@ -683,33 +648,64 @@ static int pamFunctionConversation(int numMsg, const struct pam_message** msg, * @return status */ -bool pamUpdatePasswd(const char* username, const char* password) +int 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) + int retval = + pam_start("passwd", username, &localConversation, &localAuthHandle); + + if (retval != PAM_SUCCESS) { + return retval; + } + + retval = pam_chauthtok(localAuthHandle, PAM_SILENT); + if (retval != PAM_SUCCESS) + { + pam_end(localAuthHandle, retval); + return retval; + } + + return pam_end(localAuthHandle, PAM_SUCCESS); +} + +bool pamUserCheckAuthenticate(std::string_view username, + std::string_view password) +{ + const struct pam_conv localConversation = { + pamFunctionConversation, const_cast<char*>(password.data())}; + + pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start + + if (pam_start("dropbear", username.data(), &localConversation, + &localAuthHandle) != PAM_SUCCESS) + { + log<level::ERR>("User Authentication Failure"); return false; } - int retval = pam_chauthtok(localAuthHandle, PAM_SILENT); + + int retval = pam_authenticate(localAuthHandle, + PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK); 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)); - } + log<level::DEBUG>("pam_authenticate returned failure", + entry("ERROR=%d", retval)); + pam_end(localAuthHandle, retval); return false; } + + if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) != + PAM_SUCCESS) + { + pam_end(localAuthHandle, PAM_SUCCESS); + return false; + } + if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS) { return false; @@ -717,33 +713,51 @@ bool pamUpdatePasswd(const char* username, const char* password) return true; } +ipmi_ret_t UserAccess::setSpecialUserPassword(const std::string& userName, + const std::string& userPassword) +{ + if (pamUpdatePasswd(userName.c_str(), userPassword.c_str()) != PAM_SUCCESS) + { + log<level::DEBUG>("Failed to update password"); + return IPMI_CC_UNSPECIFIED_ERROR; + } + return IPMI_CC_OK; +} + ipmi_ret_t UserAccess::setUserPassword(const uint8_t userId, const char* userPassword) { std::string userName; - if (ipmiUserGetUserName(userId, userName) != IPMI_CC_OK) + if (ipmiUserGetUserName(userId, userName) != ipmi::ccSuccess) { log<level::DEBUG>("User Name not found", - entry("USER-ID:%d", (uint8_t)userId)); - return IPMI_CC_PARM_OUT_OF_RANGE; + entry("USER-ID=%d", (uint8_t)userId)); + return ipmi::ccParmOutOfRange; } std::string passwd; passwd.assign(reinterpret_cast<const char*>(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)userId)); - return IPMI_CC_INVALID_FIELD_REQUEST; - } - if (!pamUpdatePasswd(userName.c_str(), passwd.c_str())) + + int retval = pamUpdatePasswd(userName.c_str(), passwd.c_str()); + + switch (retval) { - log<level::DEBUG>("Failed to update password", - entry("USER-ID:%d", (uint8_t)userId)); - return IPMI_CC_UNSPECIFIED_ERROR; + case PAM_SUCCESS: + { + return ipmi::ccSuccess; + } + case PAM_AUTHTOK_ERR: + { + log<level::DEBUG>("Bad authentication token"); + return ipmi::ccInvalidFieldRequest; + } + default: + { + log<level::DEBUG>("Failed to update password", + entry("USER-ID=%d", (uint8_t)userId)); + return ipmi::ccUnspecifiedError; + } } - return IPMI_CC_OK; } ipmi_ret_t UserAccess::setUserEnabledState(const uint8_t userId, @@ -783,6 +797,60 @@ ipmi_ret_t UserAccess::setUserEnabledState(const uint8_t userId, return IPMI_CC_OK; } +ipmi_ret_t UserAccess::setUserPayloadAccess(const uint8_t chNum, + const uint8_t operation, + const uint8_t userId, + const PayloadAccess& payloadAccess) +{ + constexpr uint8_t enable = 0x0; + constexpr uint8_t disable = 0x1; + + if (!isValidChannel(chNum)) + { + return IPMI_CC_INVALID_FIELD_REQUEST; + } + if (!isValidUserId(userId)) + { + return IPMI_CC_PARM_OUT_OF_RANGE; + } + if (operation != enable && operation != disable) + { + return IPMI_CC_INVALID_FIELD_REQUEST; + } + // Check operation & payloadAccess if required. + boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex> + userLock{*userMutex}; + UserInfo* userInfo = getUserInfo(userId); + + if (operation == enable) + { + userInfo->payloadAccess[chNum].stdPayloadEnables1 |= + payloadAccess.stdPayloadEnables1; + + userInfo->payloadAccess[chNum].oemPayloadEnables1 |= + payloadAccess.oemPayloadEnables1; + } + else + { + userInfo->payloadAccess[chNum].stdPayloadEnables1 &= + ~(payloadAccess.stdPayloadEnables1); + + userInfo->payloadAccess[chNum].oemPayloadEnables1 &= + ~(payloadAccess.oemPayloadEnables1); + } + + try + { + writeUserData(); + } + catch (const std::exception& e) + { + log<level::ERR>("Write user data failed"); + return IPMI_CC_UNSPECIFIED_ERROR; + } + return IPMI_CC_OK; +} + ipmi_ret_t UserAccess::setUserPrivilegeAccess(const uint8_t userId, const uint8_t chNum, const UserPrivAccess& privAccess, @@ -878,6 +946,26 @@ ipmi_ret_t UserAccess::getUserName(const uint8_t userId, std::string& userName) return IPMI_CC_OK; } +bool UserAccess::isIpmiInAvailableGroupList() +{ + if (std::find(availableGroups.begin(), availableGroups.end(), + ipmiGrpName) != availableGroups.end()) + { + return true; + } + if (availableGroups.empty()) + { + // available groups shouldn't be empty, re-query + getSystemPrivAndGroups(); + if (std::find(availableGroups.begin(), availableGroups.end(), + ipmiGrpName) != availableGroups.end()) + { + return true; + } + } + return false; +} + ipmi_ret_t UserAccess::setUserName(const uint8_t userId, const char* userNameInChar) { @@ -923,6 +1011,10 @@ ipmi_ret_t UserAccess::setUserName(const uint8_t userId, { try { + if (!isIpmiInAvailableGroupList()) + { + return IPMI_CC_UNSPECIFIED_ERROR; + } // Create new user auto method = bus.new_method_call( getUserServiceName().c_str(), userMgrObjBasePath, @@ -990,6 +1082,98 @@ 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"; +static constexpr const char* payloadEnabledStr = "payload_enabled"; +static constexpr const char* stdPayloadStr = "std_payload"; +static constexpr const char* oemPayloadStr = "OEM_payload"; + +/** @brief to construct a JSON object from the given payload access details. + * + * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. (input) + * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. (input) + * + * @details Sample output JSON object format : + * "payload_enabled":{ + * "OEM_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "OEM_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "OEM_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "OEM_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "OEM_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "OEM_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "OEM_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "OEM_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "std_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "std_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "std_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "std_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "std_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "std_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "std_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * "std_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>], + * } + */ +static const Json constructJsonPayloadEnables( + const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& + stdPayload, + const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& + oemPayload) +{ + Json jsonPayloadEnabled; + + for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++) + { + std::ostringstream stdPayloadStream; + std::ostringstream oemPayloadStream; + + stdPayloadStream << stdPayloadStr << payloadNum; + oemPayloadStream << oemPayloadStr << payloadNum; + + jsonPayloadEnabled.push_back(Json::object_t::value_type( + stdPayloadStream.str(), stdPayload[payloadNum])); + + jsonPayloadEnabled.push_back(Json::object_t::value_type( + oemPayloadStream.str(), oemPayload[payloadNum])); + } + return jsonPayloadEnabled; +} + +void UserAccess::readPayloadAccessFromUserInfo( + const UserInfo& userInfo, + std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& stdPayload, + std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& oemPayload) +{ + for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++) + { + for (auto chIndex = 0; chIndex < ipmiMaxChannels; chIndex++) + { + stdPayload[payloadNum][chIndex] = + userInfo.payloadAccess[chIndex].stdPayloadEnables1[payloadNum]; + + oemPayload[payloadNum][chIndex] = + userInfo.payloadAccess[chIndex].oemPayloadEnables1[payloadNum]; + } + } +} + +void UserAccess::updatePayloadAccessInUserInfo( + const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& + stdPayload, + const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& + oemPayload, + UserInfo& userInfo) +{ + for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex) + { + // Ensure that reserved/unsupported payloads are marked to zero. + userInfo.payloadAccess[chIndex].stdPayloadEnables1.reset(); + userInfo.payloadAccess[chIndex].oemPayloadEnables1.reset(); + userInfo.payloadAccess[chIndex].stdPayloadEnables2Reserved.reset(); + userInfo.payloadAccess[chIndex].oemPayloadEnables2Reserved.reset(); + // Update SOL status as it is the only supported payload currently. + userInfo.payloadAccess[chIndex] + .stdPayloadEnables1[static_cast<uint8_t>(ipmi::PayloadType::SOL)] = + stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)][chIndex]; + } +} void UserAccess::readUserData() { @@ -1013,6 +1197,7 @@ void UserAccess::readUserData() 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) { @@ -1036,6 +1221,48 @@ void UserAccess::readUserData() userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>(); std::vector<bool> accessCallback = userInfo[jsonAccCallbk].get<std::vector<bool>>(); + + // Payload Enables Processing. + std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte> + stdPayload = {}; + std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte> + oemPayload = {}; + try + { + const auto jsonPayloadEnabled = userInfo.at(payloadEnabledStr); + for (auto payloadNum = 0; payloadNum < payloadsPerByte; + payloadNum++) + { + std::ostringstream stdPayloadStream; + std::ostringstream oemPayloadStream; + + stdPayloadStream << stdPayloadStr << payloadNum; + oemPayloadStream << oemPayloadStr << payloadNum; + + stdPayload[payloadNum] = + jsonPayloadEnabled[stdPayloadStream.str()] + .get<std::array<bool, ipmiMaxChannels>>(); + oemPayload[payloadNum] = + jsonPayloadEnabled[oemPayloadStream.str()] + .get<std::array<bool, ipmiMaxChannels>>(); + + if (stdPayload[payloadNum].size() != ipmiMaxChannels || + oemPayload[payloadNum].size() != ipmiMaxChannels) + { + log<level::ERR>("Error in reading IPMI user data file - " + "payload properties corrupted"); + throw std::runtime_error( + "Corrupted IPMI user data file - payload properties"); + } + } + } + catch (Json::out_of_range& e) + { + // Key not found in 'userInfo'; possibly an old JSON file. Use + // default values for all payloads, and SOL payload default is true. + stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)].fill(true); + } + if (privilege.size() != ipmiMaxChannels || ipmiEnabled.size() != ipmiMaxChannels || linkAuthEnabled.size() != ipmiMaxChannels || @@ -1058,6 +1285,8 @@ void UserAccess::readUserData() usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback = accessCallback[chIndex]; } + updatePayloadAccessInUserInfo(stdPayload, oemPayload, + usersTbl.user[usrIndex]); usersTbl.user[usrIndex].userEnabled = userInfo[jsonUserEnabled].get<bool>(); usersTbl.user[usrIndex].userInSystem = @@ -1078,15 +1307,6 @@ 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) @@ -1099,6 +1319,12 @@ void UserAccess::writeUserData() std::vector<bool> ipmiEnabled(ipmiMaxChannels); std::vector<bool> linkAuthEnabled(ipmiMaxChannels); std::vector<bool> accessCallback(ipmiMaxChannels); + + std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte> + stdPayload; + std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte> + oemPayload; + for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++) { privilege[chIndex] = @@ -1118,12 +1344,35 @@ void UserAccess::writeUserData() jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled; jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem; jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName; + + readPayloadAccessFromUserInfo(usersTbl.user[usrIndex], stdPayload, + oemPayload); + Json jsonPayloadEnabledInfo = + constructJsonPayloadEnables(stdPayload, oemPayload); + jsonUserInfo[payloadEnabledStr] = jsonPayloadEnabledInfo; + jsonUsersTbl.push_back(jsonUserInfo); } - oUsrData << jsonUsersTbl; - oUsrData.flush(); - oUsrData.close(); + static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"}; + int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) + { + log<level::ERR>("Error in creating temporary IPMI user data file"); + throw std::ios_base::failure( + "Error in creating temporary IPMI user data file"); + } + const auto& writeStr = jsonUsersTbl.dump(); + if (write(fd, writeStr.c_str(), writeStr.size()) != + static_cast<ssize_t>(writeStr.size())) + { + close(fd); + log<level::ERR>("Error in writing temporary IPMI user data file"); + throw std::ios_base::failure( + "Error in writing temporary IPMI user data file"); + } + close(fd); if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0) { @@ -1252,13 +1501,11 @@ void UserAccess::getSystemPrivAndGroups() auto key = t.first; if (key == allPrivProperty) { - availablePrivileges = - variant_ns::get<std::vector<std::string>>(t.second); + availablePrivileges = std::get<std::vector<std::string>>(t.second); } else if (key == allGrpProperty) { - availableGroups = - variant_ns::get<std::vector<std::string>>(t.second); + availableGroups = std::get<std::vector<std::string>>(t.second); } } // TODO: Implement Supported Privilege & Groups verification logic @@ -1285,15 +1532,15 @@ void UserAccess::getUserProperties(const DbusUserObjProperties& properties, std::string key = t.first; if (key == userPrivProperty) { - usrPriv = variant_ns::get<std::string>(t.second); + usrPriv = std::get<std::string>(t.second); } else if (key == userGrpProperty) { - usrGrps = variant_ns::get<std::vector<std::string>>(t.second); + usrGrps = std::get<std::vector<std::string>>(t.second); } else if (key == userEnabledProperty) { - usrEnabled = variant_ns::get<bool>(t.second); + usrEnabled = std::get<bool>(t.second); } } return; @@ -1312,7 +1559,7 @@ int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs, return -EIO; } -void UserAccess::initUserDataFile() +void UserAccess::cacheUserDataFile() { boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex> userLock{*userMutex}; @@ -1331,10 +1578,49 @@ void UserAccess::initUserDataFile() { usersTbl.user[userIndex].userPrivAccess[chIndex].privilege = privNoAccess; + usersTbl.user[userIndex] + .payloadAccess[chIndex] + .stdPayloadEnables1[static_cast<uint8_t>( + ipmi::PayloadType::SOL)] = true; } } writeUserData(); } + 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; + } std::map<DbusUserObjPath, DbusUserObjValue> managedObjs; try { @@ -1351,7 +1637,7 @@ void UserAccess::initUserDataFile() entry("PATH=%s", userMgrObjBasePath)); return; } - + bool updateRequired = false; UsersTbl* userData = &usersTbl; // user index 0 is reserved, starts with 1 for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx) @@ -1361,7 +1647,6 @@ void UserAccess::initUserDataFile() { std::vector<std::string> usrGrps; std::string usrPriv; - bool usrEnabled; std::string userName( reinterpret_cast<char*>(userData->user[usrIdx].userName), 0, @@ -1372,12 +1657,15 @@ void UserAccess::initUserDataFile() auto usrObj = managedObjs.find(usersPath); if (usrObj != managedObjs.end()) { + bool usrEnabled = false; + // User exist. Lets check and update other fileds getUserObjProperties(usrObj->second, usrGrps, usrPriv, usrEnabled); if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) == usrGrps.end()) { + updateRequired = true; // Group "ipmi" is removed so lets remove user in IPMI deleteUserIndex(usrIdx); } @@ -1393,6 +1681,7 @@ void UserAccess::initUserDataFile() .userPrivAccess[getUsrMgmtSyncIndex()] .privilege != priv) { + updateRequired = true; for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex) { @@ -1403,6 +1692,7 @@ void UserAccess::initUserDataFile() } if (userData->user[usrIdx].userEnabled != usrEnabled) { + updateRequired = true; userData->user[usrIdx].userEnabled = usrEnabled; } } @@ -1412,6 +1702,7 @@ void UserAccess::initUserDataFile() } else { + updateRequired = true; deleteUserIndex(usrIdx); } } @@ -1423,7 +1714,7 @@ void UserAccess::initUserDataFile() { std::vector<std::string> usrGrps; std::string usrPriv, userName; - bool usrEnabled; + bool usrEnabled = false; std::string usrObjPath = std::string(usrObj.first); if (getUserNameFromPath(usrObj.first.str, userName) != 0) { @@ -1435,6 +1726,7 @@ void UserAccess::initUserDataFile() if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) != usrGrps.end()) { + updateRequired = true; // CREATE NEW USER if (true != addUserEntry(userName, usrPriv, usrEnabled)) { @@ -1443,8 +1735,11 @@ void UserAccess::initUserDataFile() } } - // All userData slots update done. Lets write the data - writeUserData(); + if (updateRequired) + { + // All userData slots update done. Lets write the data + writeUserData(); + } return; } diff --git a/user_channel/user_mgmt.hpp b/user_channel/user_mgmt.hpp index 9ea9f6b..159b15c 100644 --- a/user_channel/user_mgmt.hpp +++ b/user_channel/user_mgmt.hpp @@ -16,19 +16,19 @@ #pragma once #include "user_layer.hpp" -#include <ipmid/api.h> - #include <boost/interprocess/sync/file_lock.hpp> #include <boost/interprocess/sync/named_recursive_mutex.hpp> #include <cstdint> #include <ctime> +#include <ipmid/api.hpp> #include <sdbusplus/bus.hpp> +#include <variant> namespace ipmi { using DbusUserPropVariant = - sdbusplus::message::variant<std::vector<std::string>, std::string, bool>; + std::variant<std::vector<std::string>, std::string, bool>; using DbusUserObjPath = sdbusplus::message::object_path; @@ -74,6 +74,7 @@ struct UserInfo bool userEnabled; bool userInSystem; bool fixedUserName; + PayloadAccess payloadAccess[ipmiMaxChannels]; }; /** @struct UsersTbl @@ -86,6 +87,16 @@ struct UsersTbl UserInfo user[ipmiMaxUsers + 1]; }; +/** @brief PAM User Authentication check + * + * @param[in] username - username in string + * @param[in] password - password in string + * + * @return status + */ +bool pamUserCheckAuthenticate(std::string_view username, + std::string_view password); + class UserAccess; UserAccess& getUserAccessObject(); @@ -155,6 +166,12 @@ class UserAccess */ bool isValidUserName(const char* userNameInChar); + /** @brief determines whether ipmi is in available groups list + * + * @return true if ipmi group is present, false otherwise + */ + bool isIpmiInAvailableGroupList(); + /** @brief provides user id of the user * * @param[in] userName - user name @@ -217,6 +234,16 @@ class UserAccess */ ipmi_ret_t setUserPassword(const uint8_t userId, const char* userPassword); + /** @brief to set special user password + * + * @param[in] userName - user name + * @param[in] userPassword - new password of the user + * + * @return IPMI_CC_OK for success, others for failure. + */ + ipmi_ret_t setSpecialUserPassword(const std::string& userName, + const std::string& userPassword); + /** @brief to set user privilege and access details * * @param[in] userId - user id @@ -231,6 +258,56 @@ class UserAccess const UserPrivAccess& privAccess, const bool& otherPrivUpdates); + /** @brief to get user payload access details from userInfo entry. + * + * @param[in] userInfo - userInfo entry in usersTbl. + * @param[out] stdPayload - stdPayloadEnables1 in a 2D-array. + * @param[out] oemPayload - oemPayloadEnables1 in a 2D-array. + * + * @details Update the given 2D-arrays using the payload access details + * available in the given userInfo entry (from usersTbl). + * This 2D-array will be mapped to a JSON object (which will be written to + * a JSON file subsequently). + */ + void readPayloadAccessFromUserInfo( + const UserInfo& userInfo, + std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& + stdPayload, + std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& + oemPayload); + + /** @brief to update user payload access details in userInfo entry. + * + * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. + * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. + * @param[out] userInfo - userInfo entry in usersTbl. + * + * @details Update user payload access details of a given userInfo + * entry (in usersTbl) with the information provided in given 2D-arrays. + * This 2D-array was created out of a JSON object (which was created by + * parsing a JSON file). + */ + void updatePayloadAccessInUserInfo( + const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& + stdPayload, + const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& + oemPayload, + UserInfo& userInfo); + + /** @brief to set user payload access details + * + * @param[in] chNum - channel number + * @param[in] operation - Enable / Disable + * @param[in] userId - user id + * @param[in] payloadAccess - payload access + * + * @return IPMI_CC_OK for success, others for failure. + */ + ipmi_ret_t setUserPayloadAccess(const uint8_t chNum, + const uint8_t operation, + const uint8_t userId, + const PayloadAccess& payloadAccess); + /** @brief reads user management related data from configuration file * */ @@ -321,8 +398,9 @@ class UserAccess void getSystemPrivAndGroups(); /** @brief function to init user data from configuration & D-Bus objects + * and to register for signals * */ - void initUserDataFile(); + void cacheUserDataFile(); }; } // namespace ipmi diff --git a/user_channel/usercommands.cpp b/user_channel/usercommands.cpp index 489eeaf..3396d2d 100644 --- a/user_channel/usercommands.cpp +++ b/user_channel/usercommands.cpp @@ -37,6 +37,8 @@ static constexpr uint8_t setPassword = 0x02; static constexpr uint8_t testPassword = 0x03; static constexpr uint8_t passwordKeySize20 = 1; static constexpr uint8_t passwordKeySize16 = 0; +static constexpr uint8_t enableOperation = 0x00; +static constexpr uint8_t disableOperation = 0x01; /** @struct SetUserNameReq * @@ -130,7 +132,7 @@ ipmi::RspType<> ipmiSetUserAccess(ipmi::Context::ptr ctx, uint4_t channel, { uint8_t sessLimit = sessionLimit.value_or(0); uint8_t chNum = - convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx); + convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel); if (reserved1 != 0 || reserved2 != 0 || sessLimit != 0 || (!isValidChannel(chNum)) || (!ipmiUserIsValidPrivilege(static_cast<uint8_t>(privilege))) || @@ -199,7 +201,7 @@ ipmi::RspType<uint6_t, // max channel users uint6_t userId, uint2_t reserved2) { uint8_t chNum = - convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx); + convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel); if (reserved1 != 0 || reserved2 != 0 || (!isValidChannel(chNum)) || (EChannelSessSupported::none == getChannelSessionSupport(chNum))) { @@ -307,7 +309,7 @@ ipmi_ret_t ipmiGetUserName(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 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)); + entry("USER-ID=%d", (uint8_t)req->userId)); return IPMI_CC_PARM_OUT_OF_RANGE; } GetUserNameResp* resp = static_cast<GetUserNameResp*>(response); @@ -366,7 +368,7 @@ ipmi_ret_t ipmiSetUserPassword(ipmi_netfn_t netfn, ipmi_cmd_t cmd, if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK) { log<level::DEBUG>("User Name not found", - entry("USER-ID:%d", (uint8_t)req->userId)); + entry("USER-ID=%d", (uint8_t)req->userId)); return IPMI_CC_PARM_OUT_OF_RANGE; } if (req->operation == setPassword) @@ -391,7 +393,7 @@ ipmi_ret_t ipmiSetUserPassword(ipmi_netfn_t netfn, ipmi_cmd_t cmd, if (password != testPassword) { log<level::DEBUG>("Test password failed", - entry("USER-ID:%d", (uint8_t)req->userId)); + entry("USER-ID=%d", (uint8_t)req->userId)); return static_cast<ipmi_ret_t>( IPMISetPasswordReturnCodes::ipmiCCPasswdFailMismatch); } @@ -450,7 +452,7 @@ ipmi::RspType<uint8_t, // channel number { uint8_t channel = - convertCurrentChannelNum(static_cast<uint8_t>(chNum), ctx); + convertCurrentChannelNum(static_cast<uint8_t>(chNum), ctx->channel); if (reserved1 || reserved2 || !isValidChannel(channel) || !isValidPrivLimit(static_cast<uint8_t>(privLevel)) || @@ -485,10 +487,192 @@ ipmi::RspType<uint8_t, // channel number rmcp, rmcpp, reserved5, oemID, oemAuxillary); } +/** @brief implements the set user payload access command. + * @param ctx - IPMI context pointer (for channel) + * @param channel - channel number (4 bits) + * @param reserved1 - skip 4 bits + * @param userId - user id (6 bits) + * @param operation - access ENABLE /DISABLE. (2 bits) + * @param stdPayload0 - IPMI - reserved. (1 bit) + * @param stdPayload1 - SOL. (1 bit) + * @param stdPayload2 - (1 bit) + * @param stdPayload3 - (1 bit) + * @param stdPayload4 - (1 bit) + * @param stdPayload5 - (1 bit) + * @param stdPayload6 - (1 bit) + * @param stdPayload7 - (1 bit) + * @param stdPayloadEnables2Reserved - (8 bits) + * @param oemPayload0 - (1 bit) + * @param oemPayload1 - (1 bit) + * @param oemPayload2 - (1 bit) + * @param oemPayload3 - (1 bit) + * @param oemPayload4 - (1 bit) + * @param oemPayload5 - (1 bit) + * @param oemPayload6 - (1 bit) + * @param oemPayload7 - (1 bit) + * @param oemPayloadEnables2Reserved - (8 bits) + * + * @returns IPMI completion code + */ +ipmi::RspType<> ipmiSetUserPayloadAccess( + ipmi::Context::ptr ctx, + + uint4_t channel, uint4_t reserved, + + uint6_t userId, uint2_t operation, + + bool stdPayload0ipmiReserved, bool stdPayload1SOL, bool stdPayload2, + bool stdPayload3, bool stdPayload4, bool stdPayload5, bool stdPayload6, + bool stdPayload7, + + uint8_t stdPayloadEnables2Reserved, + + bool oemPayload0, bool oemPayload1, bool oemPayload2, bool oemPayload3, + bool oemPayload4, bool oemPayload5, bool oemPayload6, bool oemPayload7, + + uint8_t oemPayloadEnables2Reserved) +{ + // Validate the reserved args. Only SOL payload is supported as on date. + if (reserved || stdPayload0ipmiReserved || stdPayload2 || stdPayload3 || + stdPayload4 || stdPayload5 || stdPayload6 || stdPayload7 || + oemPayload0 || oemPayload1 || oemPayload2 || oemPayload3 || + oemPayload4 || oemPayload5 || oemPayload6 || oemPayload7 || + stdPayloadEnables2Reserved || oemPayloadEnables2Reserved) + { + return ipmi::responseInvalidFieldRequest(); + } + + auto chNum = + convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel); + if ((operation != enableOperation && operation != disableOperation) || + (!isValidChannel(chNum)) || + (getChannelSessionSupport(chNum) == EChannelSessSupported::none)) + { + return ipmi::responseInvalidFieldRequest(); + } + + if (!ipmiUserIsValidUserId(static_cast<uint8_t>(userId))) + { + return ipmi::responseParmOutOfRange(); + } + + PayloadAccess payloadAccess = {0}; + payloadAccess.stdPayloadEnables1[1] = stdPayload1SOL; + + return ipmi::response(ipmiUserSetUserPayloadAccess( + chNum, static_cast<uint8_t>(operation), static_cast<uint8_t>(userId), + payloadAccess)); +} + +/** @brief implements the get user payload access command + * This command returns information about user payload enable settings + * that were set using the 'Set User Payload Access' Command. + * + * @param ctx - IPMI context pointer (for channel) + * @param channel - channel number + * @param reserved1 - skip 4 bits + * @param userId - user id + * @param reserved2 - skip 2 bits + * + * @returns IPMI completion code plus response data + * - stdPayload0ipmiReserved - IPMI payload (reserved). + * - stdPayload1SOL - SOL payload + * - stdPayload2 + * - stdPayload3 + * - stdPayload4 + * - stdPayload5 + * - stdPayload6 + * - stdPayload7 + + * - stdPayloadEnables2Reserved - Reserved. + + * - oemPayload0 + * - oemPayload1 + * - oemPayload2 + * - oemPayload3 + * - oemPayload4 + * - oemPayload5 + * - oemPayload6 + * - oemPayload7 + + * - oemPayloadEnables2Reserved - Reserved + */ +ipmi::RspType<bool, // stdPayload0ipmiReserved + bool, // stdPayload1SOL + bool, // stdPayload2 + bool, // stdPayload3 + bool, // stdPayload4 + bool, // stdPayload5 + bool, // stdPayload6 + bool, // stdPayload7 + + uint8_t, // stdPayloadEnables2Reserved + + bool, // oemPayload0 + bool, // oemPayload1 + bool, // oemPayload2 + bool, // oemPayload3 + bool, // oemPayload4 + bool, // oemPayload5 + bool, // oemPayload6 + bool, // oemPayload7 + + uint8_t // oemPayloadEnables2Reserved + > + ipmiGetUserPayloadAccess(ipmi::Context::ptr ctx, + + uint4_t channel, uint4_t reserved1, + + uint6_t userId, uint2_t reserved2) +{ + uint8_t chNum = + convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel); + if (reserved1 != 0 || reserved2 != 0 || (!isValidChannel(chNum)) || + (getChannelSessionSupport(chNum) == EChannelSessSupported::none)) + { + return ipmi::responseInvalidFieldRequest(); + } + if (!ipmiUserIsValidUserId(static_cast<uint8_t>(userId))) + { + return ipmi::responseParmOutOfRange(); + } + + ipmi::Cc retStatus; + PayloadAccess payloadAccess = {}; + retStatus = ipmiUserGetUserPayloadAccess( + chNum, static_cast<uint8_t>(userId), payloadAccess); + if (retStatus != IPMI_CC_OK) + { + return ipmi::response(retStatus); + } + constexpr uint8_t res8bits = 0; + return ipmi::responseSuccess(payloadAccess.stdPayloadEnables1.test(0), + payloadAccess.stdPayloadEnables1.test(1), + payloadAccess.stdPayloadEnables1.test(2), + payloadAccess.stdPayloadEnables1.test(3), + payloadAccess.stdPayloadEnables1.test(4), + payloadAccess.stdPayloadEnables1.test(5), + payloadAccess.stdPayloadEnables1.test(6), + payloadAccess.stdPayloadEnables1.test(7), + + res8bits, + + payloadAccess.oemPayloadEnables1.test(0), + payloadAccess.oemPayloadEnables1.test(1), + payloadAccess.oemPayloadEnables1.test(2), + payloadAccess.oemPayloadEnables1.test(3), + payloadAccess.oemPayloadEnables1.test(4), + payloadAccess.oemPayloadEnables1.test(5), + payloadAccess.oemPayloadEnables1.test(6), + payloadAccess.oemPayloadEnables1.test(7), + + res8bits); +} + void registerUserIpmiFunctions() __attribute__((constructor)); void registerUserIpmiFunctions() { - ipmiUserInit(); + post_work([]() { ipmiUserInit(); }); ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdSetUserAccessCommand, ipmi::Privilege::Admin, ipmiSetUserAccess); @@ -510,6 +694,15 @@ void registerUserIpmiFunctions() ipmi::app::cmdGetChannelAuthCapabilities, ipmi::Privilege::Callback, ipmiGetChannelAuthenticationCapabilities); + + ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, + ipmi::app::cmdSetUserPayloadAccess, + ipmi::Privilege::Admin, ipmiSetUserPayloadAccess); + + ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, + ipmi::app::cmdGetUserPayloadAccess, + ipmi::Privilege::Operator, ipmiGetUserPayloadAccess); + return; } } // namespace ipmi |