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 <tomjoseph@in.ibm.com>
diff --git a/ipmid.C b/ipmid.C
index 9590448..7848afe 100644
--- a/ipmid.C
+++ b/ipmid.C
@@ -13,10 +13,17 @@
 #include <errno.h>
 #include <mapper.h>
 #include "sensorhandler.h"
+#include <vector>
+#include <algorithm>
+#include <iterator>
+#include <ipmiwhitelist.H>
 
 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 @@
   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_netfn_t, ipmi_cmd_t> ipmi_fn_cmd_t;
 typedef std::pair<ipmid_callback_t, ipmi_context_t> ipmi_fn_context_t;
@@ -128,6 +140,22 @@
     // 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 @@
     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 @@
         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 */