From 77381f15bdee2bf867337a3ad6c7e2aaed93b2f5 Mon Sep 17 00:00:00 2001 From: Saravanan Palanisamy Date: Wed, 15 May 2019 22:33:17 +0000 Subject: user_layer: Add get/set user payload access. IPMI Spec reference: Section 24.6, 24.7. Support is added to get/set user access details for the unreserved, supported payload types defined by Spec. SOL is the only unreserved, supported payload currently. If support is needed for unreserved std/oem payload types in future, they can be enabled with minor source code changes to this implementation. All payload types are packed in a JSON object "payload_enabled" in ipmi_user.json file. Tested-by: 1. For user 8 in channel 3, Enable SOL payload. // Command - (channel 3 is of LAN channel type) ipmitool -I lanplus...raw 0x06 0x4C 3 0x8 0x02 0 0 0 // Verify it with Get User Payload Access Command ipmitool -I lanplus...raw 0x06 0x4D 3 8 02 00 00 00 // Response 2. Disable SOL payload. // Command ipmitool -I lanplus...raw 0x06 0x4C 3 0x48 0x02 0 0x00 0 // Verify it with Get User Payload Access Command ipmitool -I lanplus...raw 0x06 0x4D 3 8 00 00 00 00 // Response 3. Enable unsupported payload stdPayload7. // Command ipmitool -I lanplus...raw 0x06 0x4C 3 0x8 0x80 0 0 0 Error: Invalid data field in request // Response Change-Id: Idc57b04a747e55666407d928d8b2169223501e5b Signed-off-by: Saravanan Palanisamy --- host-ipmid-whitelist.conf | 1 + user_channel/user_layer.cpp | 47 ++++++++++ user_channel/user_layer.hpp | 41 +++++++++ user_channel/user_mgmt.cpp | 201 ++++++++++++++++++++++++++++++++++++++++++ user_channel/user_mgmt.hpp | 51 +++++++++++ user_channel/usercommands.cpp | 193 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 534 insertions(+) diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf index 038db34..5397115 100644 --- a/host-ipmid-whitelist.conf +++ b/host-ipmid-whitelist.conf @@ -26,6 +26,7 @@ 0x06:0x36 //: 0x06:0x37 //: 0x06:0x42 //: +0x06:0x4D //: 0x06:0x4E //: 0x06:0x4F //: 0x06:0x54 //: diff --git a/user_channel/user_layer.cpp b/user_channel/user_layer.cpp index 00f6a7f..b309e86 100644 --- a/user_channel/user_layer.cpp +++ b/user_channel/user_layer.cpp @@ -176,4 +176,51 @@ bool ipmiUserPamAuthenticate(std::string_view userName, 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 7926c59..450d878 100644 --- a/user_channel/user_layer.hpp +++ b/user_channel/user_layer.hpp @@ -16,6 +16,7 @@ #pragma once #include +#include #include 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 stdPayloadEnables1; + std::bitset stdPayloadEnables2Reserved; + std::bitset oemPayloadEnables1; + std::bitset oemPayloadEnables2Reserved; +}; + /** @brief initializes user management * * @return IPMI_CC_OK for success, others for failure. @@ -221,4 +236,30 @@ ipmi_ret_t ipmiUserSetPrivilegeAccess(const uint8_t userId, const uint8_t chNum, 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 9b40f6c..f877981 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 #include @@ -833,6 +834,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 + 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("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, @@ -1040,6 +1095,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,...], + * "OEM_payload1":[false,...], + * "OEM_payload2":[false,...], + * "OEM_payload3":[false,...], + * "OEM_payload4":[false,...], + * "OEM_payload5":[false,...], + * "OEM_payload6":[false,...], + * "OEM_payload7":[false,...], + * "std_payload0":[false,...], + * "std_payload1":[false,...], + * "std_payload2":[false,...], + * "std_payload3":[false,...], + * "std_payload4":[false,...], + * "std_payload5":[false,...], + * "std_payload6":[false,...], + * "std_payload7":[false,...], + * } + */ +static const Json constructJsonPayloadEnables( + const std::array, payloadsPerByte>& + stdPayload, + const std::array, 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, payloadsPerByte>& stdPayload, + std::array, 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, payloadsPerByte>& + stdPayload, + const std::array, 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(ipmi::PayloadType::SOL)] = + stdPayload[static_cast(ipmi::PayloadType::SOL)][chIndex]; + } +} void UserAccess::readUserData() { @@ -1063,6 +1210,15 @@ void UserAccess::readUserData() throw std::runtime_error( "Corrupted IPMI user data file - invalid user count"); } + + // Construct a JSON object with default payload access values. + std::array, payloadsPerByte> stdPayload = + {}; + std::array, payloadsPerByte> oemPayload = + {}; + static const Json jsonPayloadEnabledDefault = + constructJsonPayloadEnables(stdPayload, oemPayload); + // user index 0 is reserved, starts with 1 for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex) { @@ -1086,6 +1242,36 @@ void UserAccess::readUserData() userInfo[jsonLinkAuthEnabled].get>(); std::vector accessCallback = userInfo[jsonAccCallbk].get>(); + + // Payload Enables Processing. + auto jsonPayloadEnabled = + userInfo.value(payloadEnabledStr, jsonPayloadEnabledDefault); + + 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>(); + oemPayload[payloadNum] = + jsonPayloadEnabled[oemPayloadStream.str()] + .get>(); + + if (stdPayload[payloadNum].size() != ipmiMaxChannels || + oemPayload[payloadNum].size() != ipmiMaxChannels) + { + log("Error in reading IPMI user data file - " + "payload properties corrupted"); + throw std::runtime_error( + "Corrupted IPMI user data file - payload properties"); + } + } + if (privilege.size() != ipmiMaxChannels || ipmiEnabled.size() != ipmiMaxChannels || linkAuthEnabled.size() != ipmiMaxChannels || @@ -1108,6 +1294,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(); usersTbl.user[usrIndex].userInSystem = @@ -1140,6 +1328,12 @@ void UserAccess::writeUserData() std::vector ipmiEnabled(ipmiMaxChannels); std::vector linkAuthEnabled(ipmiMaxChannels); std::vector accessCallback(ipmiMaxChannels); + + std::array, payloadsPerByte> + stdPayload; + std::array, payloadsPerByte> + oemPayload; + for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++) { privilege[chIndex] = @@ -1159,6 +1353,13 @@ 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); } diff --git a/user_channel/user_mgmt.hpp b/user_channel/user_mgmt.hpp index 8b650c8..773b18d 100644 --- a/user_channel/user_mgmt.hpp +++ b/user_channel/user_mgmt.hpp @@ -75,6 +75,7 @@ struct UserInfo bool userEnabled; bool userInSystem; bool fixedUserName; + PayloadAccess payloadAccess[ipmiMaxChannels]; }; /** @struct UsersTbl @@ -252,6 +253,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, payloadsPerByte>& + stdPayload, + std::array, 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, payloadsPerByte>& + stdPayload, + const std::array, 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 * */ diff --git a/user_channel/usercommands.cpp b/user_channel/usercommands.cpp index 5bdbbd3..50fb52b 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 * @@ -485,6 +487,188 @@ 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(channel), ctx->channel); + if ((operation != enableOperation && operation != disableOperation) || + (!isValidChannel(chNum)) || + (getChannelSessionSupport(chNum) == EChannelSessSupported::none)) + { + return ipmi::responseInvalidFieldRequest(); + } + + if (!ipmiUserIsValidUserId(static_cast(userId))) + { + return ipmi::responseParmOutOfRange(); + } + + PayloadAccess payloadAccess = {0}; + payloadAccess.stdPayloadEnables1[1] = stdPayload1SOL; + + return ipmi::response(ipmiUserSetUserPayloadAccess( + chNum, static_cast(operation), static_cast(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 + ipmiGetUserPayloadAccess(ipmi::Context::ptr ctx, + + uint4_t channel, uint4_t reserved1, + + uint6_t userId, uint2_t reserved2) +{ + uint8_t chNum = + convertCurrentChannelNum(static_cast(channel), ctx->channel); + if (reserved1 != 0 || reserved2 != 0 || (!isValidChannel(chNum)) || + (getChannelSessionSupport(chNum) == EChannelSessSupported::none)) + { + return ipmi::responseInvalidFieldRequest(); + } + if (!ipmiUserIsValidUserId(static_cast(userId))) + { + return ipmi::responseParmOutOfRange(); + } + + ipmi::Cc retStatus; + PayloadAccess payloadAccess = {}; + retStatus = ipmiUserGetUserPayloadAccess( + chNum, static_cast(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() { @@ -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 -- cgit v1.2.1