TOMOYO: Add numeric values grouping support.
Tetsuo Handa [Mon, 17 May 2010 01:06:58 +0000 (10:06 +0900)]
This patch adds numeric values grouping support, which is useful for grouping
numeric values such as file's UID, DAC's mode, ioctl()'s cmd number.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>

security/tomoyo/Makefile
security/tomoyo/common.c
security/tomoyo/common.h
security/tomoyo/file.c
security/tomoyo/gc.c
security/tomoyo/number_group.c [new file with mode: 0644]

index 4fb3903..4d1b5af 100644 (file)
@@ -1 +1 @@
-obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o
+obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o number_group.o
index b5dbdc9..d82c297 100644 (file)
@@ -119,6 +119,159 @@ static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head,
 }
 
 /**
+ * tomoyo_parse_ulong - Parse an "unsigned long" value.
+ *
+ * @result: Pointer to "unsigned long".
+ * @str:    Pointer to string to parse.
+ *
+ * Returns value type on success, 0 otherwise.
+ *
+ * The @src is updated to point the first character after the value
+ * on success.
+ */
+u8 tomoyo_parse_ulong(unsigned long *result, char **str)
+{
+       const char *cp = *str;
+       char *ep;
+       int base = 10;
+       if (*cp == '0') {
+               char c = *(cp + 1);
+               if (c == 'x' || c == 'X') {
+                       base = 16;
+                       cp += 2;
+               } else if (c >= '0' && c <= '7') {
+                       base = 8;
+                       cp++;
+               }
+       }
+       *result = simple_strtoul(cp, &ep, base);
+       if (cp == ep)
+               return 0;
+       *str = ep;
+       switch (base) {
+       case 16:
+               return TOMOYO_VALUE_TYPE_HEXADECIMAL;
+       case 8:
+               return TOMOYO_VALUE_TYPE_OCTAL;
+       default:
+               return TOMOYO_VALUE_TYPE_DECIMAL;
+       }
+}
+
+/**
+ * tomoyo_print_ulong - Print an "unsigned long" value.
+ *
+ * @buffer:     Pointer to buffer.
+ * @buffer_len: Size of @buffer.
+ * @value:      An "unsigned long" value.
+ * @type:       Type of @value.
+ *
+ * Returns nothing.
+ */
+void tomoyo_print_ulong(char *buffer, const int buffer_len,
+                       const unsigned long value, const u8 type)
+{
+       if (type == TOMOYO_VALUE_TYPE_DECIMAL)
+               snprintf(buffer, buffer_len, "%lu", value);
+       else if (type == TOMOYO_VALUE_TYPE_OCTAL)
+               snprintf(buffer, buffer_len, "0%lo", value);
+       else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL)
+               snprintf(buffer, buffer_len, "0x%lX", value);
+       else
+               snprintf(buffer, buffer_len, "type(%u)", type);
+}
+
+/**
+ * tomoyo_print_number_union - Print a tomoyo_number_union.
+ *
+ * @head:       Pointer to "struct tomoyo_io_buffer".
+ * @ptr:        Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_print_number_union(struct tomoyo_io_buffer *head,
+                              const struct tomoyo_number_union *ptr)
+{
+       unsigned long min;
+       unsigned long max;
+       u8 min_type;
+       u8 max_type;
+       if (!tomoyo_io_printf(head, " "))
+               return false;
+       if (ptr->is_group)
+               return tomoyo_io_printf(head, "@%s",
+                                       ptr->group->group_name->name);
+       min_type = ptr->min_type;
+       max_type = ptr->max_type;
+       min = ptr->values[0];
+       max = ptr->values[1];
+       switch (min_type) {
+       case TOMOYO_VALUE_TYPE_HEXADECIMAL:
+               if (!tomoyo_io_printf(head, "0x%lX", min))
+                       return false;
+               break;
+       case TOMOYO_VALUE_TYPE_OCTAL:
+               if (!tomoyo_io_printf(head, "0%lo", min))
+                       return false;
+               break;
+       default:
+               if (!tomoyo_io_printf(head, "%lu", min))
+                       return false;
+               break;
+       }
+       if (min == max && min_type == max_type)
+               return true;
+       switch (max_type) {
+       case TOMOYO_VALUE_TYPE_HEXADECIMAL:
+               return tomoyo_io_printf(head, "-0x%lX", max);
+       case TOMOYO_VALUE_TYPE_OCTAL:
+               return tomoyo_io_printf(head, "-0%lo", max);
+       default:
+               return tomoyo_io_printf(head, "-%lu", max);
+       }
+}
+
+/**
+ * tomoyo_parse_number_union - Parse a tomoyo_number_union.
+ *
+ * @data: Number or number range or number group.
+ * @ptr:  Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num)
+{
+       u8 type;
+       unsigned long v;
+       memset(num, 0, sizeof(*num));
+       if (data[0] == '@') {
+               if (!tomoyo_is_correct_path(data, 0, 0, 0))
+                       return false;
+               num->group = tomoyo_get_number_group(data + 1);
+               num->is_group = true;
+               return num->group != NULL;
+       }
+       type = tomoyo_parse_ulong(&v, &data);
+       if (!type)
+               return false;
+       num->values[0] = v;
+       num->min_type = type;
+       if (!*data) {
+               num->values[1] = v;
+               num->max_type = type;
+               return true;
+       }
+       if (*data++ != '-')
+               return false;
+       type = tomoyo_parse_ulong(&v, &data);
+       if (!type || *data)
+               return false;
+       num->values[1] = v;
+       num->max_type = type;
+       return true;
+}
+
+/**
  * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.
  *
  * @str: Pointer to the string.
@@ -1750,6 +1903,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
                return tomoyo_write_no_rewrite_policy(data, is_delete);
        if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP))
                return tomoyo_write_path_group_policy(data, is_delete);
+       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NUMBER_GROUP))
+               return tomoyo_write_number_group_policy(data, is_delete);
        return -EINVAL;
 }
 
@@ -1812,6 +1967,12 @@ static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
                        head->read_var2 = NULL;
                        head->read_step = 10;
                case 10:
+                       if (!tomoyo_read_number_group_policy(head))
+                               break;
+                       head->read_var1 = NULL;
+                       head->read_var2 = NULL;
+                       head->read_step = 11;
+               case 11:
                        head->read_eof = true;
                        break;
                default:
index 9f1ae5e..33d3072 100644 (file)
@@ -55,6 +55,7 @@ struct linux_binprm;
 #define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN      "no_initialize_domain "
 #define TOMOYO_KEYWORD_NO_KEEP_DOMAIN            "no_keep_domain "
 #define TOMOYO_KEYWORD_PATH_GROUP                "path_group "
+#define TOMOYO_KEYWORD_NUMBER_GROUP              "number_group "
 #define TOMOYO_KEYWORD_SELECT                    "select "
 #define TOMOYO_KEYWORD_USE_PROFILE               "use_profile "
 #define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ  "ignore_global_allow_read"
@@ -62,6 +63,12 @@ struct linux_binprm;
 #define TOMOYO_ROOT_NAME                         "<kernel>"
 #define TOMOYO_ROOT_NAME_LEN                     (sizeof(TOMOYO_ROOT_NAME) - 1)
 
+/* Value type definition. */
+#define TOMOYO_VALUE_TYPE_INVALID     0
+#define TOMOYO_VALUE_TYPE_DECIMAL     1
+#define TOMOYO_VALUE_TYPE_OCTAL       2
+#define TOMOYO_VALUE_TYPE_HEXADECIMAL 3
+
 /* Index numbers for Access Controls. */
 enum tomoyo_mac_index {
        TOMOYO_MAC_FOR_FILE,  /* domain_policy.conf */
@@ -211,6 +218,14 @@ struct tomoyo_name_union {
        u8 is_group;
 };
 
+struct tomoyo_number_union {
+       unsigned long values[2];
+       struct tomoyo_number_group *group;
+       u8 min_type;
+       u8 max_type;
+       u8 is_group;
+};
+
 /* Structure for "path_group" directive. */
 struct tomoyo_path_group {
        struct list_head list;
@@ -219,6 +234,14 @@ struct tomoyo_path_group {
        atomic_t users;
 };
 
+/* Structure for "number_group" directive. */
+struct tomoyo_number_group {
+       struct list_head list;
+       const struct tomoyo_path_info *group_name;
+       struct list_head member_list;
+       atomic_t users;
+};
+
 /* Structure for "path_group" directive. */
 struct tomoyo_path_group_member {
        struct list_head list;
@@ -226,6 +249,13 @@ struct tomoyo_path_group_member {
        const struct tomoyo_path_info *member_name;
 };
 
+/* Structure for "number_group" directive. */
+struct tomoyo_number_group_member {
+       struct list_head list;
+       bool is_deleted;
+       struct tomoyo_number_union number;
+};
+
 /*
  * tomoyo_acl_info is a structure which is used for holding
  *
@@ -554,9 +584,18 @@ bool tomoyo_parse_name_union(const char *filename,
 bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
                               const struct tomoyo_path_group *group,
                               const bool may_use_pattern);
+/* Check whether the given value matches the given number_group. */
+bool tomoyo_number_matches_group(const unsigned long min,
+                                const unsigned long max,
+                                const struct tomoyo_number_group *group);
 /* Check whether the given filename matches the given pattern. */
 bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
                                 const struct tomoyo_path_info *pattern);
+
+bool tomoyo_print_number_union(struct tomoyo_io_buffer *head,
+                              const struct tomoyo_number_union *ptr);
+bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num);
+
 /* Read "alias" entry in exception policy. */
 bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head);
 /*
@@ -570,6 +609,8 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head);
 bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head);
 /* Read "path_group" entry in exception policy. */
 bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head);
+/* Read "number_group" entry in exception policy. */
+bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head);
 /* Read "allow_read" entry in exception policy. */
 bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head);
 /* Read "deny_rewrite" entry in exception policy. */
@@ -614,6 +655,8 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete);
 int tomoyo_write_pattern_policy(char *data, const bool is_delete);
 /* Create "path_group" entry in exception policy. */
 int tomoyo_write_path_group_policy(char *data, const bool is_delete);
+/* Create "number_group" entry in exception policy. */
+int tomoyo_write_number_group_policy(char *data, const bool is_delete);
 /* Find a domain by the given name. */
 struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
 /* Find or create a domain by the given name. */
@@ -623,6 +666,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
 
 /* Allocate memory for "struct tomoyo_path_group". */
 struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name);
+struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name);
 
 /* Check mode for specified functionality. */
 unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
@@ -632,6 +676,8 @@ void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
 /* Run policy loader when /sbin/init starts. */
 void tomoyo_load_policy(const char *filename);
 
+void tomoyo_put_number_union(struct tomoyo_number_union *ptr);
+
 /* Convert binary string to ascii string. */
 int tomoyo_encode(char *buffer, int buflen, const char *str);
 
@@ -697,6 +743,7 @@ extern struct srcu_struct tomoyo_ss;
 extern struct list_head tomoyo_domain_list;
 
 extern struct list_head tomoyo_path_group_list;
+extern struct list_head tomoyo_number_group_list;
 extern struct list_head tomoyo_domain_initializer_list;
 extern struct list_head tomoyo_domain_keeper_list;
 extern struct list_head tomoyo_alias_list;
@@ -773,6 +820,12 @@ static inline void tomoyo_put_path_group(struct tomoyo_path_group *group)
                atomic_dec(&group->users);
 }
 
+static inline void tomoyo_put_number_group(struct tomoyo_number_group *group)
+{
+       if (group)
+               atomic_dec(&group->users);
+}
+
 static inline struct tomoyo_domain_info *tomoyo_domain(void)
 {
        return current_cred()->security;
@@ -797,6 +850,14 @@ static inline bool tomoyo_is_same_name_union
                p1->is_group == p2->is_group;
 }
 
+static inline bool tomoyo_is_same_number_union
+(const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2)
+{
+       return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1]
+               && p1->group == p2->group && p1->min_type == p2->min_type &&
+               p1->max_type == p2->max_type && p1->is_group == p2->is_group;
+}
+
 static inline bool tomoyo_is_same_path_acl(const struct tomoyo_path_acl *p1,
                                           const struct tomoyo_path_acl *p2)
 {
index 1c6f823..2dffe07 100644 (file)
@@ -76,6 +76,20 @@ static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info
        return false;
 }
 
+void tomoyo_put_number_union(struct tomoyo_number_union *ptr)
+{
+       if (ptr && ptr->is_group)
+               tomoyo_put_number_group(ptr->group);
+}
+
+bool tomoyo_compare_number_union(const unsigned long value,
+                                const struct tomoyo_number_union *ptr)
+{
+       if (ptr->is_group)
+               return tomoyo_number_matches_group(value, value, ptr->group);
+       return value >= ptr->values[0] && value <= ptr->values[1];
+}
+
 /**
  * tomoyo_path2keyword - Get the name of single path operation.
  *
index b9cc71b..6a48197 100644 (file)
@@ -14,6 +14,8 @@
 enum tomoyo_gc_id {
        TOMOYO_ID_PATH_GROUP,
        TOMOYO_ID_PATH_GROUP_MEMBER,
+       TOMOYO_ID_NUMBER_GROUP,
+       TOMOYO_ID_NUMBER_GROUP_MEMBER,
        TOMOYO_ID_DOMAIN_INITIALIZER,
        TOMOYO_ID_DOMAIN_KEEPER,
        TOMOYO_ID_ALIAS,
@@ -162,6 +164,16 @@ static void tomoyo_del_path_group(struct tomoyo_path_group *group)
        tomoyo_put_name(group->group_name);
 }
 
+static void tomoyo_del_number_group_member(struct tomoyo_number_group_member
+                                          *member)
+{
+}
+
+static void tomoyo_del_number_group(struct tomoyo_number_group *group)
+{
+       tomoyo_put_name(group->group_name);
+}
+
 static void tomoyo_collect_entry(void)
 {
        if (mutex_lock_interruptible(&tomoyo_policy_lock))
@@ -329,6 +341,29 @@ static void tomoyo_collect_entry(void)
                                break;
                }
        }
+       {
+               struct tomoyo_number_group *group;
+               list_for_each_entry_rcu(group, &tomoyo_number_group_list, list) {
+                       struct tomoyo_number_group_member *member;
+                       list_for_each_entry_rcu(member, &group->member_list,
+                                               list) {
+                               if (!member->is_deleted)
+                                       continue;
+                               if (tomoyo_add_to_gc(TOMOYO_ID_NUMBER_GROUP_MEMBER,
+                                                    member))
+                                       list_del_rcu(&member->list);
+                               else
+                                       break;
+                       }
+                       if (!list_empty(&group->member_list) ||
+                           atomic_read(&group->users))
+                               continue;
+                       if (tomoyo_add_to_gc(TOMOYO_ID_NUMBER_GROUP, group))
+                               list_del_rcu(&group->list);
+                       else
+                               break;
+               }
+       }
        mutex_unlock(&tomoyo_policy_lock);
 }
 
@@ -376,6 +411,12 @@ static void tomoyo_kfree_entry(void)
                case TOMOYO_ID_PATH_GROUP:
                        tomoyo_del_path_group(p->element);
                        break;
+               case TOMOYO_ID_NUMBER_GROUP_MEMBER:
+                       tomoyo_del_number_group_member(p->element);
+                       break;
+               case TOMOYO_ID_NUMBER_GROUP:
+                       tomoyo_del_number_group(p->element);
+                       break;
                default:
                        printk(KERN_WARNING "Unknown type\n");
                        break;
diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c
new file mode 100644 (file)
index 0000000..c49792e
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * security/tomoyo/number_group.c
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ */
+
+#include <linux/slab.h>
+#include "common.h"
+
+/* The list for "struct tomoyo_number_group". */
+LIST_HEAD(tomoyo_number_group_list);
+
+/**
+ * tomoyo_get_number_group - Allocate memory for "struct tomoyo_number_group".
+ *
+ * @group_name: The name of number group.
+ *
+ * Returns pointer to "struct tomoyo_number_group" on success,
+ * NULL otherwise.
+ */
+struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name)
+{
+       struct tomoyo_number_group *entry = NULL;
+       struct tomoyo_number_group *group = NULL;
+       const struct tomoyo_path_info *saved_group_name;
+       int error = -ENOMEM;
+       if (!tomoyo_is_correct_path(group_name, 0, 0, 0) ||
+           !group_name[0])
+               return NULL;
+       saved_group_name = tomoyo_get_name(group_name);
+       if (!saved_group_name)
+               return NULL;
+       entry = kzalloc(sizeof(*entry), GFP_NOFS);
+       if (mutex_lock_interruptible(&tomoyo_policy_lock))
+               goto out;
+       list_for_each_entry_rcu(group, &tomoyo_number_group_list, list) {
+               if (saved_group_name != group->group_name)
+                       continue;
+               atomic_inc(&group->users);
+               error = 0;
+               break;
+       }
+       if (error && tomoyo_memory_ok(entry)) {
+               INIT_LIST_HEAD(&entry->member_list);
+               entry->group_name = saved_group_name;
+               saved_group_name = NULL;
+               atomic_set(&entry->users, 1);
+               list_add_tail_rcu(&entry->list, &tomoyo_number_group_list);
+               group = entry;
+               entry = NULL;
+               error = 0;
+       }
+       mutex_unlock(&tomoyo_policy_lock);
+ out:
+       tomoyo_put_name(saved_group_name);
+       kfree(entry);
+       return !error ? group : NULL;
+}
+
+/**
+ * tomoyo_write_number_group_policy - Write "struct tomoyo_number_group" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, nagative value otherwise.
+ */
+int tomoyo_write_number_group_policy(char *data, const bool is_delete)
+{
+       struct tomoyo_number_group *group;
+       struct tomoyo_number_group_member e = { };
+       struct tomoyo_number_group_member *member;
+       int error = is_delete ? -ENOENT : -ENOMEM;
+       char *w[2];
+       if (!tomoyo_tokenize(data, w, sizeof(w)))
+               return -EINVAL;
+       if (!tomoyo_parse_number_union(w[1], &e.number))
+               return -EINVAL;
+       if (e.number.is_group || e.number.values[0] > e.number.values[1]) {
+               tomoyo_put_number_union(&e.number);
+               return -EINVAL;
+       }
+       group = tomoyo_get_number_group(w[0]);
+       if (!group)
+               return -ENOMEM;
+       if (mutex_lock_interruptible(&tomoyo_policy_lock))
+               goto out;
+       list_for_each_entry_rcu(member, &group->member_list, list) {
+               if (memcmp(&member->number, &e.number, sizeof(e.number)))
+                       continue;
+               member->is_deleted = is_delete;
+               error = 0;
+               break;
+       }
+       if (!is_delete && error) {
+               struct tomoyo_number_group_member *entry =
+                       tomoyo_commit_ok(&e, sizeof(e));
+               if (entry) {
+                       list_add_tail_rcu(&entry->list, &group->member_list);
+                       error = 0;
+               }
+       }
+       mutex_unlock(&tomoyo_policy_lock);
+ out:
+       tomoyo_put_number_group(group);
+       return error;
+}
+
+/**
+ * tomoyo_read_number_group_policy - Read "struct tomoyo_number_group" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head)
+{
+       struct list_head *gpos;
+       struct list_head *mpos;
+       list_for_each_cookie(gpos, head->read_var1, &tomoyo_number_group_list) {
+               struct tomoyo_number_group *group;
+               const char *name;
+               group = list_entry(gpos, struct tomoyo_number_group, list);
+               name = group->group_name->name;
+               list_for_each_cookie(mpos, head->read_var2,
+                                    &group->member_list) {
+                       int pos;
+                       const struct tomoyo_number_group_member *member
+                               = list_entry(mpos,
+                                            struct tomoyo_number_group_member,
+                                            list);
+                       if (member->is_deleted)
+                               continue;
+                       pos = head->read_avail;
+                       if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_NUMBER_GROUP
+                                             "%s", name) ||
+                           !tomoyo_print_number_union(head, &member->number) ||
+                           !tomoyo_io_printf(head, "\n")) {
+                               head->read_avail = pos;
+                               return false;
+                       }
+               }
+       }
+       return true;
+}
+
+/**
+ * tomoyo_number_matches_group - Check whether the given number matches members of the given number group.
+ *
+ * @min:   Min number.
+ * @max:   Max number.
+ * @group: Pointer to "struct tomoyo_number_group".
+ *
+ * Returns true if @min and @max partially overlaps @group, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_number_matches_group(const unsigned long min,
+                                const unsigned long max,
+                                const struct tomoyo_number_group *group)
+{
+       struct tomoyo_number_group_member *member;
+       bool matched = false;
+       list_for_each_entry_rcu(member, &group->member_list, list) {
+               if (member->is_deleted)
+                       continue;
+               if (min > member->number.values[1] ||
+                   max < member->number.values[0])
+                       continue;
+               matched = true;
+               break;
+       }
+       return matched;
+}