summaryrefslogtreecommitdiffstats
path: root/apphandler.cpp
diff options
context:
space:
mode:
authorRichard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>2019-04-26 22:59:16 +0530
committerVernon Mauery <vernon.mauery@linux.intel.com>2019-05-07 15:25:09 +0000
commit84bf9be5e1be52f8bea48abbfb8ac0d3414ddfa4 (patch)
tree25699c568c839a371318dba19cbb2b114549159b /apphandler.cpp
parente9e99d9321a4f1e2f776ef94c646ef15709e7551 (diff)
downloadphosphor-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.cpp282
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);
OpenPOWER on IntegriCloud