TOMOYO: Add ACL group support.
Tetsuo Handa [Sun, 26 Jun 2011 14:19:28 +0000 (23:19 +0900)]
ACL group allows administrator to globally grant not only "file read"
permission but also other permissions.

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

security/tomoyo/common.c
security/tomoyo/common.h
security/tomoyo/domain.c
security/tomoyo/gc.c
security/tomoyo/memory.c

index 6580ef3..507ebf0 100644 (file)
@@ -896,6 +896,12 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
                        domain->profile = (u8) profile;
                return 0;
        }
+       if (sscanf(data, "use_group %u\n", &profile) == 1
+           && profile < TOMOYO_MAX_ACL_GROUPS) {
+               if (!is_delete)
+                       domain->group = (u8) profile;
+               return 0;
+       }
        if (!strcmp(data, "quota_exceeded")) {
                domain->quota_warned = !is_delete;
                return 0;
@@ -908,7 +914,7 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
 }
 
 /**
- * tomoyo_set_group - Print category name.
+ * tomoyo_set_group - Print "acl_group " header keyword and category name.
  *
  * @head:     Pointer to "struct tomoyo_io_buffer".
  * @category: Category name.
@@ -918,6 +924,9 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
 static void tomoyo_set_group(struct tomoyo_io_buffer *head,
                             const char *category)
 {
+       if (head->type == TOMOYO_EXCEPTIONPOLICY)
+               tomoyo_io_printf(head, "acl_group %u ",
+                                head->r.acl_group_index);
        tomoyo_set_string(head, category);
 }
 
@@ -1041,17 +1050,17 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
 /**
  * tomoyo_read_domain2 - Read domain policy.
  *
- * @head:   Pointer to "struct tomoyo_io_buffer".
- * @domain: Pointer to "struct tomoyo_domain_info".
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @list: Pointer to "struct list_head".
  *
  * Caller holds tomoyo_read_lock().
  *
  * Returns true on success, false otherwise.
  */
 static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head,
-                               struct tomoyo_domain_info *domain)
+                               struct list_head *list)
 {
-       list_for_each_cookie(head->r.acl, &domain->acl_info_list) {
+       list_for_each_cookie(head->r.acl, list) {
                struct tomoyo_acl_info *ptr =
                        list_entry(head->r.acl, typeof(*ptr), list);
                if (!tomoyo_print_entry(head, ptr))
@@ -1085,6 +1094,8 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
                        tomoyo_set_lf(head);
                        tomoyo_io_printf(head, "use_profile %u\n",
                                         domain->profile);
+                       tomoyo_io_printf(head, "use_group %u\n",
+                                        domain->group);
                        if (domain->quota_warned)
                                tomoyo_set_string(head, "quota_exceeded\n");
                        if (domain->transition_failed)
@@ -1093,7 +1104,7 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
                        tomoyo_set_lf(head);
                        /* fall through */
                case 1:
-                       if (!tomoyo_read_domain2(head, domain))
+                       if (!tomoyo_read_domain2(head, &domain->acl_info_list))
                                return;
                        head->r.step++;
                        if (!tomoyo_set_lf(head))
@@ -1262,6 +1273,14 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
        };
        u8 i;
        param.is_delete = tomoyo_str_starts(&param.data, "delete ");
+       if (!param.is_delete && tomoyo_str_starts(&param.data, "select ") &&
+           !strcmp(param.data, "execute_only")) {
+               head->r.print_execute_only = true;
+               return 0;
+       }
+       /* Don't allow updating policies by non manager programs. */
+       if (!tomoyo_manager())
+               return -EPERM;
        if (tomoyo_str_starts(&param.data, "aggregator "))
                return tomoyo_write_aggregator(&param);
        for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++)
@@ -1270,6 +1289,14 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
        for (i = 0; i < TOMOYO_MAX_GROUP; i++)
                if (tomoyo_str_starts(&param.data, tomoyo_group_name[i]))
                        return tomoyo_write_group(&param, i);
+       if (tomoyo_str_starts(&param.data, "acl_group ")) {
+               unsigned int group;
+               char *data;
+               group = simple_strtoul(param.data, &data, 10);
+               if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ')
+                       return tomoyo_write_domain2(&tomoyo_acl_group[group],
+                                                   data, param.is_delete);
+       }
        return -EINVAL;
 }
 
@@ -1392,6 +1419,15 @@ static void tomoyo_read_exception(struct tomoyo_io_buffer *head)
                head->r.step++;
        if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP)
                return;
+       while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP
+              + TOMOYO_MAX_ACL_GROUPS) {
+               head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY
+                       - TOMOYO_MAX_GROUP;
+               if (!tomoyo_read_domain2(head, &tomoyo_acl_group
+                                        [head->r.acl_group_index]))
+                       return;
+               head->r.step++;
+       }
        head->r.eof = true;
 }
 
@@ -1914,7 +1950,8 @@ int tomoyo_write_control(struct tomoyo_io_buffer *head,
                return -EFAULT;
        /* Don't allow updating policies by non manager programs. */
        if (head->write != tomoyo_write_pid &&
-           head->write != tomoyo_write_domain && !tomoyo_manager())
+           head->write != tomoyo_write_domain &&
+           head->write != tomoyo_write_exception && !tomoyo_manager())
                return -EPERM;
        if (mutex_lock_interruptible(&head->io_sem))
                return -EINTR;
index f40ec1f..4bc3975 100644 (file)
@@ -38,6 +38,9 @@ struct linux_binprm;
 /* Profile number is an integer between 0 and 255. */
 #define TOMOYO_MAX_PROFILES 256
 
+/* Group number is an integer between 0 and 255. */
+#define TOMOYO_MAX_ACL_GROUPS 256
+
 /* Index numbers for operation mode. */
 enum tomoyo_mode_index {
        TOMOYO_CONFIG_DISABLED,
@@ -357,6 +360,7 @@ struct tomoyo_domain_info {
        /* Name of this domain. Never NULL.          */
        const struct tomoyo_path_info *domainname;
        u8 profile;        /* Profile number to use. */
+       u8 group;          /* Group number to use.   */
        bool is_deleted;   /* Delete flag.           */
        bool quota_warned; /* Quota warnning flag.   */
        bool transition_failed; /* Domain transition failed flag. */
@@ -446,6 +450,7 @@ struct tomoyo_io_buffer {
                int step;
                int query_index;
                u16 index;
+               u8 acl_group_index;
                u8 bit;
                u8 w_pos;
                bool eof;
@@ -666,6 +671,8 @@ extern struct mutex tomoyo_policy_lock;
 /* Has /sbin/init started? */
 extern bool tomoyo_policy_loaded;
 
+extern struct list_head tomoyo_acl_group[TOMOYO_MAX_ACL_GROUPS];
+
 /* The kernel's domain. */
 extern struct tomoyo_domain_info tomoyo_kernel_domain;
 
index cb5d2b0..af5f325 100644 (file)
@@ -12,6 +12,9 @@
 
 /* Variables definitions.*/
 
+/* The global ACL referred by "use_group" keyword. */
+struct list_head tomoyo_acl_group[TOMOYO_MAX_ACL_GROUPS];
+
 /* The initial domain. */
 struct tomoyo_domain_info tomoyo_kernel_domain;
 
@@ -125,14 +128,27 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
        return error;
 }
 
+/**
+ * tomoyo_check_acl - Do permission check.
+ *
+ * @r:           Pointer to "struct tomoyo_request_info".
+ * @check_entry: Callback function to check type specific parameters.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
 void tomoyo_check_acl(struct tomoyo_request_info *r,
                      bool (*check_entry) (struct tomoyo_request_info *,
                                           const struct tomoyo_acl_info *))
 {
        const struct tomoyo_domain_info *domain = r->domain;
        struct tomoyo_acl_info *ptr;
+       bool retried = false;
+       const struct list_head *list = &domain->acl_info_list;
 
-       list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+retry:
+       list_for_each_entry_rcu(ptr, list, list) {
                if (ptr->is_deleted || ptr->type != r->param_type)
                        continue;
                if (check_entry(r, ptr)) {
@@ -140,6 +156,11 @@ void tomoyo_check_acl(struct tomoyo_request_info *r,
                        return;
                }
        }
+       if (!retried) {
+               retried = true;
+               list = &tomoyo_acl_group[domain->group];
+               goto retry;
+       }
        r->granted = false;
 }
 
index de14030..412ee83 100644 (file)
@@ -265,10 +265,17 @@ static bool tomoyo_collect_member(const enum tomoyo_policy_id id,
         return true;
 }
 
-static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain)
+/**
+ * tomoyo_collect_acl - Delete elements in "struct tomoyo_domain_info".
+ *
+ * @list: Pointer to "struct list_head".
+ *
+ * Returns true if some elements are deleted, false otherwise.
+ */
+static bool tomoyo_collect_acl(struct list_head *list)
 {
        struct tomoyo_acl_info *acl;
-       list_for_each_entry(acl, &domain->acl_info_list, list) {
+       list_for_each_entry(acl, list, list) {
                if (!acl->is_deleted)
                        continue;
                if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list))
@@ -291,10 +298,13 @@ static void tomoyo_collect_entry(void)
                if (!tomoyo_collect_member(i, &tomoyo_policy_list[i]))
                        goto unlock;
        }
+       for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
+               if (!tomoyo_collect_acl(&tomoyo_acl_group[i]))
+                       goto unlock;
        {
                struct tomoyo_domain_info *domain;
                list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
-                       if (!tomoyo_collect_acl(domain))
+                       if (!tomoyo_collect_acl(&domain->acl_info_list))
                                goto unlock;
                        if (!domain->is_deleted || atomic_read(&domain->users))
                                continue;
index 598282c..7a04939 100644 (file)
@@ -213,6 +213,8 @@ void __init tomoyo_mm_init(void)
        for (idx = 0; idx < TOMOYO_MAX_HASH; idx++)
                INIT_LIST_HEAD(&tomoyo_name_list[idx]);
        INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
+       for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++)
+               INIT_LIST_HEAD(&tomoyo_acl_group[idx]);
        tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
        list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
        idx = tomoyo_read_lock();