Enforce smack policy from conf file 09/5609/1
authorBrian McGillion <brian.mcgillion@intel.com>
Mon, 6 Feb 2012 16:48:30 +0000 (18:48 +0200)
committerMichael Leibowitz <michael.leibowitz@intel.com>
Wed, 10 Jul 2013 17:31:28 +0000 (10:31 -0700)
bus/config-parser.c
bus/policy.c
bus/policy.h
bus/smack.c
bus/smack.h

index 07e8fbb..d7ba549 100644 (file)
@@ -43,6 +43,7 @@ typedef enum
   POLICY_MANDATORY,
   POLICY_USER,
   POLICY_GROUP,
+  POLICY_SMACK,
   POLICY_CONSOLE
 } PolicyType;
 
@@ -64,7 +65,11 @@ typedef struct
     struct
     {
       PolicyType type;
-      unsigned long gid_uid_or_at_console;      
+      union
+      {
+        unsigned long gid_uid_or_at_console;
+        char *smack_label;
+      };
     } policy;
 
     struct
@@ -150,6 +155,8 @@ element_free (Element *e)
 {
   if (e->type == ELEMENT_LIMIT)
     dbus_free (e->d.limit.name);
+  else if (e->type == ELEMENT_POLICY && e->d.policy.type == POLICY_SMACK)
+      dbus_free (e->d.policy.smack_label);
   
   dbus_free (e);
 }
@@ -972,6 +979,7 @@ start_busconfig_child (BusConfigParser   *parser,
       const char *user;
       const char *group;
       const char *at_console;
+      const char *smack;
 
       if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
         {
@@ -988,20 +996,16 @@ start_busconfig_child (BusConfigParser   *parser,
                               "context", &context,
                               "user", &user,
                               "group", &group,
+                              "smack", &smack,
                               "at_console", &at_console,
                               NULL))
         return FALSE;
 
-      if (((context && user) ||
-           (context && group) ||
-           (context && at_console)) ||
-           ((user && group) ||
-           (user && at_console)) ||
-           (group && at_console) ||
-          !(context || user || group || at_console))
+      if (((context != NULL) + (user != NULL) + (group != NULL) +
+          (smack != NULL) + (at_console != NULL)) != 1)
         {
           dbus_set_error (error, DBUS_ERROR_FAILED,
-                          "<policy> element must have exactly one of (context|user|group|at_console) attributes");
+                          "<policy> element must have exactly one of (context|user|group|smack|at_console) attributes");
           return FALSE;
         }
 
@@ -1047,6 +1051,16 @@ start_busconfig_child (BusConfigParser   *parser,
             _dbus_warn ("Unknown group \"%s\" in message bus configuration file\n",
                         group);          
         }
+      else if (smack != NULL)
+        {
+          e->d.policy.type = POLICY_SMACK;
+          e->d.policy.smack_label = _dbus_strdup (smack);
+          if (e->d.policy.smack_label == NULL)
+            {
+              BUS_SET_OOM (error);
+              return FALSE;
+            }
+        }
       else if (at_console != NULL)
         {
            dbus_bool_t t;
@@ -1631,8 +1645,10 @@ append_rule_from_element (BusConfigParser   *parser,
                                              rule))
             goto nomem;
           break;
-        
-
+        case POLICY_SMACK:
+          if (!bus_policy_append_smack_rule (parser->policy, pe->d.policy.smack_label, rule))
+            goto nomem;
+          break;
         case POLICY_CONSOLE:
           if (!bus_policy_append_console_rule (parser->policy, pe->d.policy.gid_uid_or_at_console,
                                                rule))
index 379cea9..836354a 100644 (file)
@@ -26,6 +26,7 @@
 #include "services.h"
 #include "test.h"
 #include "utils.h"
+#include "smack.h"
 #include <dbus/dbus-list.h>
 #include <dbus/dbus-hash.h>
 #include <dbus/dbus-internals.h>
@@ -126,12 +127,13 @@ struct BusPolicy
 {
   int refcount;
 
-  DBusList *default_rules;         /**< Default policy rules */
-  DBusList *mandatory_rules;       /**< Mandatory policy rules */
-  DBusHashTable *rules_by_uid;     /**< per-UID policy rules */
-  DBusHashTable *rules_by_gid;     /**< per-GID policy rules */
-  DBusList *at_console_true_rules; /**< console user policy rules where at_console="true"*/
-  DBusList *at_console_false_rules; /**< console user policy rules where at_console="false"*/
+  DBusList *default_rules;             /**< Default policy rules */
+  DBusList *mandatory_rules;           /**< Mandatory policy rules */
+  DBusHashTable *rules_by_uid;         /**< per-UID policy rules */
+  DBusHashTable *rules_by_gid;         /**< per-GID policy rules */
+  DBusHashTable *rules_by_smack_label; /**< per-SMACK label policy rules */
+  DBusList *at_console_true_rules;     /**< console user policy rules where at_console="true"*/
+  DBusList *at_console_false_rules;    /**< console user policy rules where at_console="false"*/
 };
 
 static void
@@ -181,6 +183,14 @@ bus_policy_new (void)
   if (policy->rules_by_gid == NULL)
     goto failed;
 
+#ifdef DBUS_ENABLE_SMACK
+  policy->rules_by_smack_label = _dbus_hash_table_new (DBUS_HASH_STRING,
+                                                       (DBusFreeFunction) dbus_free,
+                                                       free_rule_list_func);
+  if (policy->rules_by_smack_label == NULL)
+    goto failed;
+#endif
+
   return policy;
   
  failed:
@@ -230,7 +240,13 @@ bus_policy_unref (BusPolicy *policy)
           _dbus_hash_table_unref (policy->rules_by_gid);
           policy->rules_by_gid = NULL;
         }
-      
+
+      if (policy->rules_by_smack_label)
+        {
+          _dbus_hash_table_unref (policy->rules_by_smack_label);
+          policy->rules_by_smack_label = NULL;
+        }
+
       dbus_free (policy);
     }
 }
@@ -356,6 +372,24 @@ bus_policy_create_client_policy (BusPolicy      *policy,
         }
     }
 
+  if (policy->rules_by_smack_label &&
+      _dbus_hash_table_get_n_entries (policy->rules_by_smack_label) > 0)
+    {
+      DBusList **list;
+      dbus_bool_t nomem_err = FALSE;
+
+      list = bus_smack_generate_allowed_list(connection, policy->rules_by_smack_label, &nomem_err);
+
+      if (list != NULL)
+        {
+          nomem_err = !add_list_to_client (list, client);
+          _dbus_list_clear (list);
+        }
+
+      if (nomem_err)
+        goto nomem;
+    }
+
   if (!add_list_to_client (&policy->mandatory_rules,
                            client))
     goto nomem;
@@ -576,6 +610,66 @@ bus_policy_append_group_rule (BusPolicy      *policy,
   return TRUE;
 }
 
+#ifdef DBUS_ENABLE_SMACK
+static DBusList **
+get_list_string (DBusHashTable *table,
+                 const char *key)
+{
+  DBusList **list;
+
+  if (key == NULL)
+      return NULL;
+
+  list = _dbus_hash_table_lookup_string (table, key);
+
+  if (list == NULL)
+    {
+      char *new_key;
+
+      list = dbus_new0 (DBusList*, 1);
+      if (list == NULL)
+        return NULL;
+
+      new_key = _dbus_strdup (key);
+      if (new_key == NULL)
+        {
+          dbus_free (list);
+          return NULL;
+        }
+
+      if (!_dbus_hash_table_insert_string (table, new_key, list))
+        {
+          dbus_free (list);
+          dbus_free (new_key);
+          return NULL;
+        }
+    }
+
+  return list;
+}
+#endif
+
+dbus_bool_t
+bus_policy_append_smack_rule (BusPolicy      *policy,
+                              const char     *label,
+                              BusPolicyRule  *rule)
+{
+#ifdef DBUS_ENABLE_SMACK
+  DBusList **list;
+
+  list = get_list_string (policy->rules_by_smack_label, label);
+  if (list == NULL)
+    return FALSE;
+
+  if (!_dbus_list_append (list, rule))
+    return FALSE;
+
+  bus_policy_rule_ref (rule);
+#endif
+
+  return TRUE;
+}
+
 dbus_bool_t
 bus_policy_append_console_rule (BusPolicy      *policy,
                                 dbus_bool_t     at_console,
@@ -653,6 +747,31 @@ merge_id_hash (DBusHashTable *dest,
   return TRUE;
 }
 
+#ifdef DBUS_ENABLE_SMACK
+static dbus_bool_t
+merge_string_hash (DBusHashTable *dest,
+                   DBusHashTable *to_absorb)
+{
+  DBusHashIter iter;
+
+  _dbus_hash_iter_init (to_absorb, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      const char *absorb_label = _dbus_hash_iter_get_string_key(&iter);
+      DBusList **list = _dbus_hash_iter_get_value (&iter);
+      DBusList **target = get_list_string (dest, absorb_label);
+
+      if (target == NULL)
+        return FALSE;
+
+      if (!append_copy_of_policy_list (target, list))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+#endif
+
 dbus_bool_t
 bus_policy_merge (BusPolicy *policy,
                   BusPolicy *to_absorb)
@@ -685,6 +804,12 @@ bus_policy_merge (BusPolicy *policy,
                       to_absorb->rules_by_gid))
     return FALSE;
 
+#ifdef DBUS_ENABLE_SMACK
+  if (!merge_string_hash (policy->rules_by_smack_label,
+                          to_absorb->rules_by_smack_label))
+    return FALSE;
+#endif
+
   return TRUE;
 }
 
@@ -873,7 +998,7 @@ bus_client_policy_check_can_send (BusClientPolicy *policy,
 {
   DBusList *link;
   dbus_bool_t allowed;
-  
+
   /* policy->rules is in the order the rules appeared
    * in the config file, i.e. last rule that applies wins
    */
index 3ff6f48..20d0a39 100644 (file)
@@ -130,6 +130,9 @@ dbus_bool_t      bus_policy_append_user_rule      (BusPolicy        *policy,
 dbus_bool_t      bus_policy_append_group_rule     (BusPolicy        *policy,
                                                    dbus_gid_t        gid,
                                                    BusPolicyRule    *rule);
+dbus_bool_t      bus_policy_append_smack_rule     (BusPolicy        *policy,
+                                                   const char       *label,
+                                                   BusPolicyRule    *rule);
 dbus_bool_t      bus_policy_append_console_rule   (BusPolicy        *policy,
                                                    dbus_bool_t        at_console,
                                                    BusPolicyRule    *rule);
index b8542c2..d4546a3 100644 (file)
 #include "connection.h"
 #include "services.h"
 #include "utils.h"
+#include "policy.h"
 
 #ifdef DBUS_ENABLE_SMACK
 #include <sys/smack.h>
 #endif
 
+#define SMACK_WRITE "W"
+#define SMACK_READ "R"
+#define SMACK_READ_WRITE "RW"
+
+
 #ifdef DBUS_ENABLE_SMACK
 static char *
 bus_smack_get_label (DBusConnection *connection)
@@ -130,3 +136,108 @@ err:
   return FALSE;
 #endif
 }
+
+#ifdef DBUS_ENABLE_SMACK
+static dbus_bool_t
+bus_smack_has_access (const char *subject, const char *object,
+                      const char *access)
+{
+  return (smack_have_access (subject, object, access) == 1 ? TRUE : FALSE);
+}
+#endif
+
+
+/**
+ * Calculate the list of rules that apply to a connection.
+ *
+ * @param connection The inbound conenction
+ * @param rules_by_smack_label The table of object labels -> rules mapping
+ * @param nomem_err (out) If a nomem situation is encountered this value is set to TRUE.
+ * @returns the list of permitted rules if it exists and no errors were encountered otherwise NULL.
+ */
+DBusList**
+bus_smack_generate_allowed_list (DBusConnection *connection,
+                                 DBusHashTable  *rules_by_smack_label,
+                                 dbus_bool_t *nomem_err)
+{
+#ifdef DBUS_ENABLE_SMACK
+  char *subject_label;
+  DBusHashIter iter;
+  dbus_bool_t is_allowed;
+  DBusList **allowed_list;
+
+  /* the label of the subject, is the label on the new connection,
+     either the service itself or one of its clients */
+  subject_label = bus_smack_get_label (connection);
+  if (subject_label == NULL)
+    return NULL;
+
+  allowed_list = dbus_new0 (DBusList*, 1);
+  if (allowed_list == NULL)
+    goto nomem;
+
+  /* Iterate over all the smack labels we have parsed from the .conf files */
+  _dbus_hash_iter_init (rules_by_smack_label, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      DBusList *link;
+      const char *object_label = _dbus_hash_iter_get_string_key (&iter);
+      /* the list here is all the rules that are 'protected'
+         by the SMACK label named $object_label */
+      DBusList **list = _dbus_hash_iter_get_value (&iter);
+
+      link = _dbus_list_get_first_link (list);
+      while (link != NULL)
+        {
+          BusPolicyRule *rule = link->data;
+          link = _dbus_list_get_next_link (list, link);
+          is_allowed = FALSE;
+
+          switch (rule->type)
+            {
+            case BUS_POLICY_RULE_OWN:
+              is_allowed = bus_smack_has_access (subject_label,
+                                                 object_label,
+                                                 SMACK_READ_WRITE);
+              break;
+            case BUS_POLICY_RULE_SEND:
+              is_allowed = bus_smack_has_access (subject_label,
+                                                 object_label,
+                                                 SMACK_WRITE);
+              break;
+            case BUS_POLICY_RULE_RECEIVE:
+              is_allowed = bus_smack_has_access (subject_label,
+                                                 object_label,
+                                                 SMACK_READ);
+              break;
+            default:
+              continue;
+            }
+
+          if (is_allowed)
+            {
+              if (!_dbus_list_append (allowed_list, rule))
+                goto nomem;
+
+              bus_policy_rule_ref (rule);
+            }
+
+          _dbus_verbose ("permission request subject (%s) -> object (%s) : %s", subject_label, object_label, (is_allowed ? "GRANTED" : "REJECTED"));
+        }
+    }
+
+  dbus_free(subject_label);
+  return allowed_list;
+
+nomem:
+  if (allowed_list != NULL)
+    _dbus_list_clear (allowed_list);
+
+  dbus_free(subject_label);
+  *nomem_err = TRUE;
+  return NULL;
+
+#else
+  return NULL;
+#endif
+}
index 04a4a2a..6b1dfad 100644 (file)
 #define SMACK_H
 
 #include "bus.h"
+#include <dbus/dbus-hash.h>
 
 dbus_bool_t bus_smack_handle_get_connection_context (DBusConnection *connection,
                                                      BusTransaction *transaction,
                                                      DBusMessage    *message,
                                                      DBusError      *error);
 
+DBusList **bus_smack_generate_allowed_list (DBusConnection *connection,
+                                            DBusHashTable *label_rules,
+                                            dbus_bool_t *error);
 #endif // SMACK_H