diff options
author | Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> | 2019-04-26 22:59:16 +0530 |
---|---|---|
committer | Vernon Mauery <vernon.mauery@linux.intel.com> | 2019-05-07 15:25:09 +0000 |
commit | 84bf9be5e1be52f8bea48abbfb8ac0d3414ddfa4 (patch) | |
tree | 25699c568c839a371318dba19cbb2b114549159b /apphandler.cpp | |
parent | e9e99d9321a4f1e2f776ef94c646ef15709e7551 (diff) | |
download | phosphor-host-ipmid-84bf9be5e1be52f8bea48abbfb8ac0d3414ddfa4.tar.gz phosphor-host-ipmid-84bf9be5e1be52f8bea48abbfb8ac0d3414ddfa4.zip |
apphandler: Master write read - whitelist filter
Support added for master write read IPMI command with
whitelist filter support. Allows commands which are
listed in the whitelist filter.
Tested:
1. Verified with filter list and able to execute whitelisted
commands as per the mask
2. verified command throws invalid field in request for non
whitelisted filters.
Change-Id: I8f20808a8a2d9afc10747485a1303492329a6996
Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>
Diffstat (limited to 'apphandler.cpp')
-rw-r--r-- | apphandler.cpp | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/apphandler.cpp b/apphandler.cpp index 87f45d2..648a390 100644 --- a/apphandler.cpp +++ b/apphandler.cpp @@ -1,6 +1,12 @@ #include <arpa/inet.h> +#include <fcntl.h> #include <limits.h> +#include <linux/i2c-dev.h> +#include <linux/i2c.h> #include <mapper.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> #include <systemd/sd-bus.h> #include <unistd.h> @@ -58,6 +64,33 @@ using Activation = using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC; namespace fs = std::filesystem; +typedef struct +{ + uint8_t busId; + uint8_t slaveAddr; + uint8_t slaveAddrMask; + std::vector<uint8_t> data; + std::vector<uint8_t> dataMask; +} i2cMasterWRWhitelist; + +static std::vector<i2cMasterWRWhitelist>& getWRWhitelist() +{ + static std::vector<i2cMasterWRWhitelist> wrWhitelist; + return wrWhitelist; +} + +static constexpr const char* i2cMasterWRWhitelistFile = + "/usr/share/ipmi-providers/master_write_read_white_list.json"; + +static constexpr uint8_t maxIPMIWriteReadSize = 144; +static constexpr const char* filtersStr = "filters"; +static constexpr const char* busIdStr = "busId"; +static constexpr const char* slaveAddrStr = "slaveAddr"; +static constexpr const char* slaveAddrMaskStr = "slaveAddrMask"; +static constexpr const char* cmdStr = "command"; +static constexpr const char* cmdMaskStr = "commandMask"; +static constexpr int base_16 = 16; + /** * @brief Returns the Version info from primary s/w object * @@ -1021,6 +1054,243 @@ writeResponse: return IPMI_CC_OK; } +inline std::vector<uint8_t> convertStringToData(const std::string& command) +{ + std::istringstream iss(command); + std::string token; + std::vector<uint8_t> dataValue; + while (std::getline(iss, token, ' ')) + { + dataValue.emplace_back( + static_cast<uint8_t>(std::stoul(token, nullptr, base_16))); + } + return dataValue; +} + +static bool populateI2CMasterWRWhitelist() +{ + nlohmann::json data = nullptr; + std::ifstream jsonFile(i2cMasterWRWhitelistFile); + + if (!jsonFile.good()) + { + log<level::WARNING>("i2c white list file not found!", + entry("FILE_NAME: %s", i2cMasterWRWhitelistFile)); + return false; + } + + try + { + data = nlohmann::json::parse(jsonFile, nullptr, false); + } + catch (nlohmann::json::parse_error& e) + { + log<level::ERR>("Corrupted i2c white list config file", + entry("FILE_NAME: %s", i2cMasterWRWhitelistFile), + entry("MSG: %s", e.what())); + return false; + } + + try + { + // Example JSON Structure format + // "filters": [ + // { + // "Description": "Allow full read - ignore first byte write value + // for 0x40 to 0x4F", + // "busId": "0x01", + // "slaveAddr": "0x40", + // "slaveAddrMask": "0x0F", + // "command": "0x00", + // "commandMask": "0xFF" + // }, + // { + // "Description": "Allow full read - first byte match 0x05 and + // ignore second byte", + // "busId": "0x01", + // "slaveAddr": "0x57", + // "slaveAddrMask": "0x00", + // "command": "0x05 0x00", + // "commandMask": "0x00 0xFF" + // },] + + nlohmann::json filters = data[filtersStr].get<nlohmann::json>(); + std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist(); + for (const auto& it : filters.items()) + { + nlohmann::json filter = it.value(); + if (filter.is_null()) + { + log<level::ERR>( + "Corrupted I2C master write read whitelist config file", + entry("FILE_NAME: %s", i2cMasterWRWhitelistFile)); + return false; + } + const std::vector<uint8_t>& writeData = + convertStringToData(filter[cmdStr].get<std::string>()); + const std::vector<uint8_t>& writeDataMask = + convertStringToData(filter[cmdMaskStr].get<std::string>()); + if (writeDataMask.size() != writeData.size()) + { + log<level::ERR>("I2C master write read whitelist filter " + "mismatch for command & mask size"); + return false; + } + whitelist.push_back( + {static_cast<uint8_t>(std::stoul( + filter[busIdStr].get<std::string>(), nullptr, base_16)), + static_cast<uint8_t>( + std::stoul(filter[slaveAddrStr].get<std::string>(), + nullptr, base_16)), + static_cast<uint8_t>( + std::stoul(filter[slaveAddrMaskStr].get<std::string>(), + nullptr, base_16)), + writeData, writeDataMask}); + } + if (whitelist.size() != filters.size()) + { + log<level::ERR>( + "I2C master write read whitelist filter size mismatch"); + return false; + } + } + catch (std::exception& e) + { + log<level::ERR>("I2C master write read whitelist unexpected exception", + entry("ERROR=%s", e.what())); + return false; + } + return true; +} + +static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data, + const std::vector<uint8_t>& dataMask, + const std::vector<uint8_t>& writeData) +{ + std::vector<uint8_t> processedDataBuf(data.size()); + std::vector<uint8_t> processedReqBuf(dataMask.size()); + std::transform(writeData.begin(), writeData.end(), dataMask.begin(), + processedReqBuf.begin(), std::bit_or<uint8_t>()); + std::transform(data.begin(), data.end(), dataMask.begin(), + processedDataBuf.begin(), std::bit_or<uint8_t>()); + + return (processedDataBuf == processedReqBuf); +} + +static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr, + std::vector<uint8_t>& writeData) +{ + std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist(); + for (const auto& wlEntry : whiteList) + { + if ((busId == wlEntry.busId) && + ((slaveAddr | wlEntry.slaveAddrMask) == + (wlEntry.slaveAddr | wlEntry.slaveAddrMask))) + { + const std::vector<uint8_t>& dataMask = wlEntry.dataMask; + // Skip as no-match, if requested write data is more than the + // write data mask size + if (writeData.size() > dataMask.size()) + { + continue; + } + if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData)) + { + return true; + } + } + } + return false; +} + +/** @brief implements master write read IPMI command which can be used for + * low-level I2C/SMBus write, read or write-read access + * @param isPrivateBus -to indicate private bus usage + * @param busId - bus id + * @param channelNum - channel number + * @param reserved - skip 1 bit + * @param slaveAddr - slave address + * @param read count - number of bytes to be read + * @param writeData - data to be written + * + * @returns IPMI completion code plus response data + * - readData - i2c response data + */ +ipmi::RspType<std::vector<uint8_t>> + ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum, + bool reserved, uint7_t slaveAddr, uint8_t readCount, + std::vector<uint8_t> writeData) +{ + i2c_rdwr_ioctl_data msgReadWrite = {0}; + i2c_msg i2cmsg[2] = {0}; + + if (readCount > maxIPMIWriteReadSize) + { + log<level::ERR>("Master write read command: Read count exceeds limit"); + return ipmi::responseParmOutOfRange(); + } + const size_t writeCount = writeData.size(); + if (!readCount && !writeCount) + { + log<level::ERR>("Master write read command: Read & write count are 0"); + return ipmi::responseInvalidFieldRequest(); + } + if (!isCmdWhitelisted(static_cast<uint8_t>(busId), + static_cast<uint8_t>(slaveAddr), writeData)) + { + log<level::ERR>("Master write read request blocked!", + entry("BUS=%d", static_cast<uint8_t>(busId)), + entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr))); + return ipmi::responseInvalidFieldRequest(); + } + std::vector<uint8_t> readBuf(readCount); + std::string i2cBus = + "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId)); + + int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC); + if (i2cDev < 0) + { + log<level::ERR>("Failed to open i2c bus", + entry("BUS=%s", i2cBus.c_str())); + return ipmi::responseInvalidFieldRequest(); + } + + int msgCount = 0; + if (writeCount) + { + i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr); + i2cmsg[msgCount].flags = 0x00; + i2cmsg[msgCount].len = writeCount; + i2cmsg[msgCount].buf = writeData.data(); + msgCount++; + } + if (readCount) + { + i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr); + i2cmsg[msgCount].flags = I2C_M_RD; + i2cmsg[msgCount].len = readCount; + i2cmsg[msgCount].buf = readBuf.data(); + msgCount++; + } + + msgReadWrite.msgs = i2cmsg; + msgReadWrite.nmsgs = msgCount; + + int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite); + ::close(i2cDev); + + if (ret < 0) + { + log<level::ERR>("Master write read: Failed", entry("RET=%d", ret)); + return ipmi::responseUnspecifiedError(); + } + if (readCount) + { + readBuf.resize(msgReadWrite.msgs[msgCount - 1].len); + } + return ipmi::responseSuccess(readBuf); +} + void register_netfn_app_functions() { // <Get Device ID> @@ -1063,6 +1333,18 @@ void register_netfn_app_functions() ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_ACPI, NULL, ipmi_app_get_acpi_power_state, PRIVILEGE_ADMIN); + // Note: For security reason, this command will be registered only when + // there are proper I2C Master write read whitelist + if (populateI2CMasterWRWhitelist()) + { + // Note: For security reasons, registering master write read as admin + // privilege command, even though IPMI 2.0 specification allows it as + // operator privilege. + ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, + ipmi::app::cmdMasterWriteRead, + ipmi::Privilege::Admin, ipmiMasterWriteRead); + } + // <Get System GUID Command> ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL, ipmi_app_get_sys_guid, PRIVILEGE_USER); |