From 9a61b4f4e9213608653852ba2388fa89663029c3 Mon Sep 17 00:00:00 2001 From: Tom Joseph Date: Mon, 11 Jul 2016 06:56:11 -0500 Subject: Whitelist IPMI commands based on Restricted mode Whitelisting of IPMI commands is done to ensure that in restricted mode only whitelisted commands are executed. Commands that are not whitelisted is restricted and insufficient privilege is returned as the completion code. When the server is deployed it would be set to restricted mode. In this scenario certain IPMI commands need to be restricted which would not be added to the whitelist. Change-Id: I90b8124e34263c4ffc5bcf06a28a7e88231aaf40 Signed-off-by: Tom Joseph --- Makefile | 14 ++++++-- generate_whitelist.sh | 27 +++++++++++++++ host-ipmid-whitelist.conf | 24 +++++++++++++ ipmid-api.h | 1 + ipmid.C | 86 ++++++++++++++++++++++++++++++++++++++++++++++- ipmiwhitelist.H | 11 ++++++ 6 files changed, 159 insertions(+), 4 deletions(-) create mode 100755 generate_whitelist.sh create mode 100644 host-ipmid-whitelist.conf create mode 100644 ipmiwhitelist.H diff --git a/Makefile b/Makefile index 08900ca..447b129 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,8 @@ TESTER = testit TESTADDSEL = testaddsel DAEMON = ipmid -DAEMON_OBJ = ipmid.o +DAEMON_OBJ = ipmid.o \ + ipmiwhitelist.o LIB_APP_OBJ = apphandler.o \ sensorhandler.o \ @@ -44,8 +45,14 @@ SBINDIR ?= /usr/sbin INCLUDEDIR ?= /usr/include LIBDIR ?= /usr/lib +WHITELIST_SRC = ipmiwhitelist.C +WHITELIST_CONF ?= host-ipmid-whitelist.conf + all: $(DAEMON) $(LIB_APP) $(LIB_HOST_SRV) $(TESTER) +$(WHITELIST_SRC) : generate_whitelist.sh $(WHITELIST_CONF) + ./$^ > $@ + %.o: %.C $(CXX) -std=c++14 -fpic -c $< $(CXXFLAGS) $(INC_FLAG) $(IPMID_PATH) -o $@ @@ -62,11 +69,11 @@ $(TESTER): $(TESTER_OBJ) $(CXX) $^ $(LDFLAGS) $(LIB_FLAG) -o $@ -ldl clean: - rm -f $(DAEMON) $(TESTER) *.o *.so + rm -f $(DAEMON) $(TESTER) *.o *.so $(WHITELIST_SRC) $(TESTADDSEL): $(TESTADDSEL_OBJ) $(CXX) $^ $(LDFLAGS) $(LIB_FLAG) -o $@ -ldl - + install: install -m 0755 -d $(DESTDIR)$(SBINDIR) install -m 0755 ipmid $(DESTDIR)$(SBINDIR) @@ -75,3 +82,4 @@ install: install -m 0755 -d $(DESTDIR)$(INCLUDEDIR)/host-ipmid install -m 0644 $(INSTALLED_HEADERS) $(DESTDIR)$(INCLUDEDIR)/host-ipmid + diff --git a/generate_whitelist.sh b/generate_whitelist.sh new file mode 100755 index 0000000..e0dded3 --- /dev/null +++ b/generate_whitelist.sh @@ -0,0 +1,27 @@ +#/bin/sh + +# Ensure some files have been passed. +if [ "x$*" == "x" ]; then + echo "Usage: $0 [whitelist_files+]" >&2 + exit -1 +fi + +cat << EOF +#include + +const std::vector whitelist = { + +EOF + +# Output each row of whitelist vector. +# Concatenate all the passed files. +# Remove comments and empty lines. +# Sort the list [numerically]. +# Remove any duplicates. +# Turn "a:b //:" -> "{ a, b }, //:" +cat $* | sed "s/#.*//" | sed '/^$/d' | sort -n | uniq | sed "s/^/ { /" | \ + sed "s/\:\(....\)\(.*\)/ , \1 }, \2/" + +cat << EOF +}; +EOF diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf new file mode 100644 index 0000000..bd02898 --- /dev/null +++ b/host-ipmid-whitelist.conf @@ -0,0 +1,24 @@ +#: +0x00:0x02 //: +0x00:0x08 //: +0x00:0x09 //: +0x04:0x2D //: +0x04:0x2F //: +0x04:0x30 //: +0x06:0x01 //: +0x06:0x08 //: +0x06:0x22 //: +0x06:0x24 //: +0x06:0x2E //: +0x06:0x31 //: +0x06:0x35 //: +0x06:0x36 //: +0x06:0x42 //: +0x0A:0x40 //: +0x0A:0x42 //: +0x0A:0x44 //: +0x0A:0x48 //: +0x0A:0x49 //: +0x0C:0x02 //: +0x2C:0x00 //: +0x2C:0x03 //: diff --git a/ipmid-api.h b/ipmid-api.h index cf3eaab..0c92069 100644 --- a/ipmid-api.h +++ b/ipmid-api.h @@ -97,6 +97,7 @@ enum ipmi_return_codes IPMI_CC_PARM_OUT_OF_RANGE = 0xC9, IPMI_CC_SENSOR_INVALID = 0xCB, IPMI_CC_RESPONSE_ERROR = 0xCE, + IPMI_CC_INSUFFICIENT_PRIVILEGE = 0xD4, IPMI_CC_UNSPECIFIED_ERROR = 0xFF, }; diff --git a/ipmid.C b/ipmid.C index 9590448..7848afe 100644 --- a/ipmid.C +++ b/ipmid.C @@ -13,10 +13,17 @@ #include #include #include "sensorhandler.h" +#include +#include +#include +#include sd_bus *bus = NULL; sd_bus_slot *ipmid_slot = NULL; +// Initialise restricted mode to true +bool restricted_mode = true; + FILE *ipmiio, *ipmidbus, *ipmicmddetails; void print_usage(void) { @@ -27,10 +34,15 @@ void print_usage(void) { fprintf(stderr, " mask : 0xFF - Print all trace\n"); } +// Host settings in DBUS +constexpr char settings_host_bus[] = "org.openbmc.settings.Host"; +constexpr char settings_host_object[] = "/org/openbmc/settings/host0"; +constexpr char settings_host_intf[] = "org.freedesktop.DBus.Properties"; + const char * DBUS_INTF = "org.openbmc.HostIpmi"; const char * FILTER = "type='signal',interface='org.openbmc.HostIpmi',member='ReceivedMessage'"; - +constexpr char RESTRICTED_MODE_FILTER[] = "type='signal',interface='org.freedesktop.DBus.Properties',path='/org/openbmc/settings/host0'"; typedef std::pair ipmi_fn_cmd_t; typedef std::pair ipmi_fn_context_t; @@ -128,6 +140,22 @@ ipmi_ret_t ipmi_netfn_router(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t // return from the Command handlers. ipmi_ret_t rc = IPMI_CC_INVALID; + // If restricted mode is true and command is not whitelisted, don't + // execute the command + if(restricted_mode) + { + if (!std::binary_search(whitelist.cbegin(), whitelist.cend(), + std::make_pair(netfn, cmd))) + { + printf("Net function:[0x%X], Command:[0x%X] is not whitelisted\n", + netfn, cmd); + rc = IPMI_CC_INSUFFICIENT_PRIVILEGE; + memcpy(response, &rc, IPMI_CC_LEN); + *data_len = IPMI_CC_LEN; + return rc; + } + } + // Walk the map that has the registered handlers and invoke the approprite // handlers for matching commands. auto iter = g_ipmid_router_map.find(std::make_pair(netfn, cmd)); @@ -237,6 +265,53 @@ final: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } +void cache_restricted_mode() +{ + sd_bus *bus = ipmid_get_sd_bus_connection(); + sd_bus_message *reply = NULL; + sd_bus_error error = SD_BUS_ERROR_NULL; + int rc = 0; + + rc = sd_bus_call_method(bus, + settings_host_bus, + settings_host_object, + settings_host_intf, + "Get", + &error, + &reply, + "ss", + settings_host_bus, + "restricted_mode"); + if(rc < 0) + { + fprintf(stderr, "Failed sd_bus_call_method method for restricted mode: %s\n", + strerror(-rc)); + goto cleanup; + } + + rc = sd_bus_message_read(reply, "v", "b", &restricted_mode); + if(rc < 0) + { + fprintf(stderr, "Failed to parse response message for restricted mode: %s\n", + strerror(-rc)); + // Fail-safe to restricted mode + restricted_mode = true; + } + + printf("Restricted mode = %d\n", restricted_mode); + +cleanup: + sd_bus_error_free(&error); + reply = sd_bus_message_unref(reply); +} + +static int handle_restricted_mode_change(sd_bus_message *m, void *user_data, + sd_bus_error *ret_error) +{ + cache_restricted_mode(); + return 0; +} + static int handle_ipmi_command(sd_bus_message *m, void *user_data, sd_bus_error *ret_error) { int r = 0; @@ -444,6 +519,15 @@ int main(int argc, char *argv[]) goto finish; } + // Wait for changes on Restricted mode + r = sd_bus_add_match(bus, &ipmid_slot, RESTRICTED_MODE_FILTER, handle_restricted_mode_change, NULL); + if (r < 0) { + fprintf(stderr, "Failed: sd_bus_add_match: %s : %s\n", strerror(-r), RESTRICTED_MODE_FILTER); + goto finish; + } + + // Initialise restricted mode + cache_restricted_mode(); for (;;) { /* Process requests */ diff --git a/ipmiwhitelist.H b/ipmiwhitelist.H new file mode 100644 index 0000000..b07451a --- /dev/null +++ b/ipmiwhitelist.H @@ -0,0 +1,11 @@ +#ifndef __HOST_IPMID_IPMI_WHITELIST_H__ +#define __HOST_IPMID_IPMI_WHITELIST_H_ + +#include +#include + +using netfncmd_pair = std::pair; + +extern const std::vector whitelist; + +#endif -- cgit v1.2.1