This is the main part for profiling and controlling file access.
We thought checking old pathname and new pathname separately
for rename() and link() operation is a too rough access control
and we are checking both pathnames using tomoyo_check_double_write_acl().

Signed-off-by: Kentaro Takeda <[EMAIL PROTECTED]>
Signed-off-by: Tetsuo Handa <[EMAIL PROTECTED]>

---------------
security/tomoyo/file.c | 1126 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1126 insertions(+)

diff -ubBpErN linux-2.6.21.5/security/tomoyo/file.c 
linux-2.6.21.5-tomoyo/security/tomoyo/file.c
--- linux-2.6.21.5/security/tomoyo/file.c       1970-01-01 09:00:00.000000000 
+0900
+++ linux-2.6.21.5-tomoyo/security/tomoyo/file.c        2007-06-05 
00:00:00.000000000 +0900
@@ -0,0 +1,1126 @@
+/*
+ * security/tomoyo/file.c
+ *
+ * File access control functions for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2007  NTT DATA CORPORATION
+ *
+ * Version: 2.0   2007/06/05
+ */
+
+#include "tomoyo.h"
+#include "realpath.h"
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+
+/*************************  VARIABLES  *************************/
+
+extern struct semaphore domain_acl_lock;
+
+/***** The structure for globally readable files. *****/
+
+struct globally_readable_file_entry {
+       struct globally_readable_file_entry *next;
+       const struct path_info *filename;
+       int is_deleted;
+};
+
+/***** The structure for filename patterns. *****/
+
+struct pattern_entry {
+       struct pattern_entry *next;
+       const struct path_info *pattern;
+       int is_deleted;
+};
+
+/***** The structure for non-rewritable-by-default file patterns. *****/
+
+struct no_rewrite_entry {
+       struct no_rewrite_entry *next;
+       const struct path_info *pattern;
+       int is_deleted;
+};
+
+/***** The structure for detailed write operations. *****/
+
+static struct {
+       const char *keyword;
+       const int paths;
+       int check_type;
+} acl_type_array[] = { /* mapping.txt */
+       { "create",   1, 1 }, /* TOMOYO_TYPE_CREATE_ACL */
+       { "unlink",   1, 1 }, /* TOMOYO_TYPE_UNLINK_ACL */
+       { "mkdir",    1, 1 }, /* TOMOYO_TYPE_MKDIR_ACL */
+       { "rmdir",    1, 1 }, /* TOMOYO_TYPE_RMDIR_ACL */
+       { "mkfifo",   1, 1 }, /* TOMOYO_TYPE_MKFIFO_ACL */
+       { "mksock",   1, 1 }, /* TOMOYO_TYPE_MKSOCK_ACL */
+       { "mkblock",  1, 1 }, /* TOMOYO_TYPE_MKBLOCK_ACL */
+       { "mkchar",   1, 1 }, /* TOMOYO_TYPE_MKCHAR_ACL */
+       { "truncate", 1, 1 }, /* TOMOYO_TYPE_TRUNCATE_ACL */
+       { "symlink",  1, 1 }, /* TOMOYO_TYPE_SYMLINK_ACL */
+       { "link",     2, 1 }, /* TOMOYO_TYPE_LINK_ACL */
+       { "rename",   2, 1 }, /* TOMOYO_TYPE_RENAME_ACL */
+       { "rewrite",  1, 1 }, /* TOMOYO_TYPE_REWRITE_ACL */
+       { NULL, 0, 0 }
+};
+
+/*************************  UTILITY FUNCTIONS  *************************/
+
+const char *tomoyo_acltype2keyword(const unsigned int acl_type)
+{
+       return (acl_type < sizeof(acl_type_array) / sizeof(acl_type_array[0]))
+               ? acl_type_array[acl_type].keyword : NULL;
+}
+
+int tomoyo_acltype2paths(const unsigned int acl_type)
+{
+       return (acl_type < sizeof(acl_type_array) / sizeof(acl_type_array[0]))
+               ? acl_type_array[acl_type].paths : 0;
+}
+
+static unsigned int tomoyo_check_acl_flags(const unsigned int index)
+{
+       if (index < (sizeof(acl_type_array) / sizeof(acl_type_array[0])) - 1)
+               return acl_type_array[index].check_type;
+       printk("%s: Index %u is out of range. Fix the kernel source.\n", 
__FUNCTION__, index);
+       return 0;
+}
+
+static int tomoyo_strendswith(const char *name, const char *tail)
+{
+       int len;
+       if (!name || !tail) return 0;
+       len = strlen(name) - strlen(tail);
+       return len >= 0 && strcmp(name + len, tail) == 0;
+}
+
+static struct path_info *tomoyo_get_path(struct dentry *dentry, struct 
vfsmount *mnt)
+{
+       struct path_info_with_data { /* sizeof(struct path_info_with_data) <= 
PAGE_SIZE */
+               struct path_info head; /* Keep this first, this pointer is 
passed to tomoyo_free(). */
+               char bariier1[16];
+               char body[TOMOYO_MAX_PATHNAME_LEN];
+               char barrier2[16];
+       } *buf = tomoyo_alloc(sizeof(*buf));
+       if (buf) {
+               int error;
+               if ((error = tomoyo_realpath_from_dentry2(dentry,
+                                                         mnt,
+                                                         buf->body,
+                                                         sizeof(buf->body) - 
1)) == 0) {
+                       buf->head.name = buf->body;
+                       tomoyo_fill_path_info(&buf->head);
+                       return &buf->head;
+               }
+               tomoyo_free(buf);
+               buf = NULL;
+               printk("tomoyo_realpath_from_dentry = %d\n", error);
+       }
+       return NULL;
+}
+
+/*************************  PROTOTYPES  *************************/
+
+static int tomoyo_add_double_write_acl(const u8 type,
+                                       const char *filename1,
+                                       const char *filename2,
+                                       struct domain_info * const domain,
+                                       const u8 is_add);
+static int tomoyo_add_single_write_acl(const u8 type,
+                                       const char *filename,
+                                       struct domain_info * const domain,
+                                       const u8 is_add);
+
+/*************************  AUDIT FUNCTIONS  *************************/
+
+static int tomoyo_audit_file_log(const struct path_info *filename,
+                                 const u8 perm,
+                                 const int is_granted)
+{
+       char *buf;
+       int len;
+       len = filename->total_len + 8;
+       if ((buf = tomoyo_init_audit_log(&len)) == NULL) return -ENOMEM;
+       snprintf(buf + strlen(buf), len - strlen(buf) - 1, "%d %s\n", perm, 
filename->name);
+       return tomoyo_write_audit_log(buf, is_granted);
+}
+
+static int tomoyo_audit_write_log(const char *operation,
+                                  const struct path_info *filename1,
+                                  const struct path_info *filename2,
+                                  const int is_granted)
+{
+       char *buf;
+       int len;
+       len = strlen(operation) + filename1->total_len + (filename2 ? 
filename2->total_len : 0) + 16;
+       if ((buf = tomoyo_init_audit_log(&len)) == NULL) return -ENOMEM;
+       snprintf(buf + strlen(buf), len - strlen(buf) - 1, "allow_%s %s %s\n",
+                operation, filename1->name, filename2 ? filename2->name : "");
+       return tomoyo_write_audit_log(buf, is_granted);
+}
+
+/*************************  PERMISSION MAP HANDLER  *************************/
+
+int tomoyo_set_permission_mapping(struct io_buffer *head)
+{
+       int i;
+       char *data = head->write_buf;
+       char *cp = NULL;
+       if ((cp = strchr(data, '=')) == NULL) {
+       out: ;
+               printk("ERROR: Invalid line '%s=%s'\n", data, cp);
+               printk("This line must be one of the following. The first is the 
default.\n");
+               printk("%s=%s if you want to check this permission using this 
permission.\n",
+                      data, data);
+               printk("%s=generic-write if you want to check this permission "
+                      "using generic-write permission.\n", data);
+               printk("%s=no-check if you don't want to check this 
permission.\n", data);
+               return -EINVAL;
+       }
+       *cp++ = '\0';
+       for (i = 0; acl_type_array[i].keyword; i++) {
+               if (strcmp(acl_type_array[i].keyword, data)) continue;
+               if (strcmp(cp, acl_type_array[i].keyword) == 0) 
acl_type_array[i].check_type = 1;
+               else if (strcmp(cp, "generic-write") == 0) 
acl_type_array[i].check_type = 0;
+               else if (strcmp(cp, "no-check") == 0) 
acl_type_array[i].check_type = -1;
+               else goto out;
+               return 0;
+       }
+       printk("WARNING: Unprocessed line '%s=%s'\n", data, cp);
+       return -EINVAL;
+}
+
+int tomoyo_read_permission_mapping(struct io_buffer *head)
+{
+       if (!head->read_eof) {
+               int i;
+               for (i = 0; acl_type_array[i].keyword; i++) {
+                       tomoyo_io_printf(head,
+                                        "%s=%s\n",
+                                        acl_type_array[i].keyword,
+                                        acl_type_array[i].check_type > 0 ?
+                                               acl_type_array[i].keyword :
+                                               acl_type_array[i].check_type == 
0 ?
+                                                       "generic-write" : 
"no-check");
+               }
+               head->read_eof = 1;
+       }
+       return 0;
+}
+
+/*************************  GLOBALLY READABLE FILE HANDLER  
*************************/
+
+static struct globally_readable_file_entry *globally_readable_list = NULL;
+
+static int tomoyo_add_globally_readable_entry(const char *filename, const int 
is_delete)
+{
+       struct globally_readable_file_entry *new_entry, *ptr;
+       static DECLARE_MUTEX(lock);
+       const struct path_info *saved_filename;
+       int error = -ENOMEM;
+       if (!tomoyo_is_correct_path(filename, 1, -1, -1, __FUNCTION__))
+               return -EINVAL; /* No patterns allowed. */
+       if ((saved_filename = tomoyo_save_name(filename)) == NULL) return 
-ENOMEM;
+       down(&lock);
+       for (ptr = globally_readable_list; ptr; ptr = ptr->next) {
+               if (ptr->filename == saved_filename) {
+                       ptr->is_deleted = is_delete;
+                       error = 0;
+                       goto out;
+               }
+       }
+       if (is_delete) {
+               error = -ENOENT;
+               goto out;
+       }
+       if ((new_entry = tomoyo_alloc_element(sizeof(*new_entry))) == NULL) 
goto out;
+       new_entry->filename = saved_filename;
+       mb(); /* Instead of using spinlock. */
+       if ((ptr = globally_readable_list) != NULL) {
+               while (ptr->next)
+                       ptr = ptr->next;
+               ptr->next = new_entry;
+       } else {
+               globally_readable_list = new_entry;
+       }
+       error = 0;
+ out: ;
+       up(&lock);
+       return error;
+}
+
+static int tomoyo_is_globally_readable_file(const struct path_info *filename)
+{
+       struct globally_readable_file_entry *ptr;
+       for (ptr = globally_readable_list; ptr; ptr = ptr->next) {
+               if (!ptr->is_deleted && !tomoyo_pathcmp(filename, 
ptr->filename)) return 1;
+       }
+       return 0;
+}
+
+int tomoyo_add_globally_readable_policy(char *filename, const int is_delete)
+{
+       return tomoyo_add_globally_readable_entry(filename, is_delete);
+}
+
+int tomoyo_read_globally_readable_policy(struct io_buffer *head)
+{
+       struct globally_readable_file_entry *ptr = head->read_var2;
+       if (!ptr) ptr = globally_readable_list;
+       while (ptr) {
+               head->read_var2 = ptr;
+               if (!ptr->is_deleted && tomoyo_io_printf(head,
+                                                        TOMOYO_KEYWORD_ALLOW_READ 
"%s\n",
+                                                        ptr->filename->name))
+                       break;
+               ptr = ptr->next;
+       }
+       return ptr ? -ENOMEM : 0;
+}
+
+/*************************  FILE GROUP HANDLER  *************************/
+
+static struct group_entry *group_list = NULL;
+
+static int tomoyo_add_group_entry(const char *group_name,
+                                  const char *member_name,
+                                  const int is_delete)
+{
+       static DECLARE_MUTEX(lock);
+       struct group_entry *new_group, *group;
+       struct group_member *new_member, *member;
+       const struct path_info *saved_group_name, *saved_member_name;
+       int error = -ENOMEM;
+       if (!tomoyo_is_correct_path(group_name, 0, 0, 0, __FUNCTION__) || 
!group_name[0] ||
+               !tomoyo_is_correct_path(member_name, 0, 0, 0, __FUNCTION__) || 
!member_name[0])
+                       return -EINVAL;
+       if ((saved_group_name = tomoyo_save_name(group_name)) == NULL ||
+               (saved_member_name = tomoyo_save_name(member_name)) == NULL) 
return -ENOMEM;
+       down(&lock);
+       for (group = group_list; group; group = group->next) {
+               if (saved_group_name != group->group_name) continue;
+               for (member = group->first_member; member; member = 
member->next) {
+                       if (member->member_name == saved_member_name) {
+                               member->is_deleted = is_delete;
+                               error = 0;
+                               goto out;
+                       }
+               }
+               break;
+       }
+       if (is_delete) {
+               error = -ENOENT;
+               goto out;
+       }
+       if (!group) {
+               if ((new_group = tomoyo_alloc_element(sizeof(*new_group))) == 
NULL) goto out;
+               new_group->group_name = saved_group_name;
+               mb(); /* Instead of using spinlock. */
+               if ((group = group_list) != NULL) {
+                       while (group->next)
+                               group = group->next;
+                       group->next = new_group;
+               } else {
+                       group_list= new_group;
+               }
+               group = new_group;
+       }
+       if ((new_member = tomoyo_alloc_element(sizeof(*new_member))) == NULL) 
goto out;
+       new_member->member_name = saved_member_name;
+       mb(); /* Instead of using spinlock. */
+       if ((member = group->first_member) != NULL) {
+               while (member->next)
+                       member = member->next;
+               member->next = new_member;
+       } else {
+               group->first_member = new_member;
+       }
+       error = 0;
+ out:
+       up(&lock);
+       return error;
+}
+
+int tomoyo_add_group_policy(char *data, const int is_delete)
+{
+       char *cp = strchr(data, ' ');
+       if (!cp) return -EINVAL;
+       *cp++ = '\0';
+       return tomoyo_add_group_entry(data, cp, is_delete);
+}
+
+static struct group_entry *tomoyo_find_or_assign_new_group(const char 
*group_name)
+{
+       int i;
+       struct group_entry *group;
+       for (i = 0; i <= 1; i++) {
+               for (group = group_list; group; group = group->next) {
+                       if (strcmp(group_name, group->group_name->name) == 0) 
return group;
+               }
+               if (i == 0) {
+                       tomoyo_add_group_entry(group_name, "/", 0);
+                       tomoyo_add_group_entry(group_name, "/", 1);
+               }
+       }
+       return NULL;
+}
+
+static int tomoyo_path_matchies_to_group(const struct path_info *pathname,
+                                         const struct group_entry *group,
+                                         const int may_use_pattern)
+{
+       struct group_member *member;
+       for (member = group->first_member; member; member = member->next) {
+               if (member->is_deleted) continue;
+               if (!member->member_name->is_patterned) {
+                       if (!tomoyo_pathcmp(pathname, member->member_name)) 
return 1;
+               } else if (may_use_pattern) {
+                       if (tomoyo_path_matches_to_pattern(pathname, 
member->member_name)) return 1;
+               }
+       }
+       return 0;
+}
+
+int tomoyo_read_group_policy(struct io_buffer *head)
+{
+       struct group_entry *group = head->read_var1;
+       struct group_member *member = head->read_var2;
+       if (!group) group = group_list;
+       while (group) {
+               head->read_var1 = group;
+               if (!member) member = group->first_member;
+               while (member) {
+                       head->read_var2 = member;
+                       if (!member->is_deleted &&
+                           tomoyo_io_printf(head,
+                                            TOMOYO_KEYWORD_PATH_GROUP "%s 
%s\n",
+                                            group->group_name->name,
+                                            member->member_name->name))
+                               break;
+                       member = member->next;
+               }
+               if (member) break;
+               head->read_var2 = NULL;
+               group = group->next;
+       }
+       return group ? -ENOMEM : 0;
+}
+
+/*************************  FILE PATTERN HANDLER  *************************/
+
+static struct pattern_entry *pattern_list = NULL;
+
+static int tomoyo_add_pattern_entry(const char *pattern, const int is_delete)
+{
+       struct pattern_entry *new_entry, *ptr;
+       static DECLARE_MUTEX(lock);
+       const struct path_info *saved_pattern;
+       int error = -ENOMEM;
+       if (!tomoyo_is_correct_path(pattern, 0, 1, 0, __FUNCTION__)) return 
-EINVAL;
+       if ((saved_pattern = tomoyo_save_name(pattern)) == NULL) return -ENOMEM;
+       down(&lock);
+       for (ptr = pattern_list; ptr; ptr = ptr->next) {
+               if (saved_pattern == ptr->pattern) {
+                       ptr->is_deleted = is_delete;
+                       error = 0;
+                       goto out;
+               }
+       }
+       if (is_delete) {
+               error = -ENOENT;
+               goto out;
+       }
+       if ((new_entry = tomoyo_alloc_element(sizeof(*new_entry))) == NULL) 
goto out;
+       new_entry->pattern = saved_pattern;
+       mb(); /* Instead of using spinlock. */
+       if ((ptr = pattern_list) != NULL) {
+               while (ptr->next)
+                       ptr = ptr->next;
+               ptr->next = new_entry;
+       } else {
+               pattern_list = new_entry;
+       }
+       error = 0;
+ out:
+       up(&lock);
+       return error;
+}
+
+static const struct path_info *tomoyo_get_pattern(const struct path_info 
*filename)
+{
+       struct pattern_entry *ptr;
+       const struct path_info *pattern = NULL;
+       for (ptr = pattern_list; ptr; ptr = ptr->next) {
+               if (ptr->is_deleted) continue;
+               if (!tomoyo_path_matches_to_pattern(filename, ptr->pattern)) 
continue;
+               pattern = ptr->pattern;
+               if (tomoyo_strendswith(pattern->name, "/\\*")) {
+                       /* Do nothing. Try to find the better match. */
+               } else {
+                       /* This would be the better match. Use this. */
+                       break;
+               }
+       }
+       if (pattern) filename = pattern;
+       return filename;
+}
+
+int tomoyo_add_pattern_policy(char *pattern, const int is_delete)
+{
+       return tomoyo_add_pattern_entry(pattern, is_delete);
+}
+
+int tomoyo_read_pattern_policy(struct io_buffer *head)
+{
+       struct pattern_entry *ptr = head->read_var2;
+       if (!ptr) ptr = pattern_list;
+       while (ptr) {
+               head->read_var2 = ptr;
+               if (!ptr->is_deleted &&
+                   tomoyo_io_printf(head,
+                                    TOMOYO_KEYWORD_FILE_PATTERN "%s\n",
+                                    ptr->pattern->name))
+                       break;
+               ptr = ptr->next;
+       }
+       return ptr ? -ENOMEM : 0;
+}
+
+/*************************  NON REWRITABLE FILE HANDLER  
*************************/
+
+static struct no_rewrite_entry *no_rewrite_list = NULL;
+
+static int tomoyo_add_no_rewrite_entry(const char *pattern, const int 
is_delete)
+{
+       struct no_rewrite_entry *new_entry, *ptr;
+       static DECLARE_MUTEX(lock);
+       const struct path_info *saved_pattern;
+       int error = -ENOMEM;
+       if (!tomoyo_is_correct_path(pattern, 0, 0, 0, __FUNCTION__)) return 
-EINVAL;
+       if ((saved_pattern = tomoyo_save_name(pattern)) == NULL) return -ENOMEM;
+       down(&lock);
+       for (ptr = no_rewrite_list; ptr; ptr = ptr->next) {
+               if (ptr->pattern == saved_pattern) {
+                       ptr->is_deleted = is_delete;
+                       error = 0;
+                       goto out;
+               }
+       }
+       if (is_delete) {
+               error = -ENOENT;
+               goto out;
+       }
+       if ((new_entry = tomoyo_alloc_element(sizeof(*new_entry))) == NULL) 
goto out;
+       new_entry->pattern = saved_pattern;
+       mb(); /* Instead of using spinlock. */
+       if ((ptr = no_rewrite_list) != NULL) {
+               while (ptr->next)
+                       ptr = ptr->next;
+               ptr->next = new_entry;
+       } else {
+               no_rewrite_list = new_entry;
+       }
+       error = 0;
+ out:
+       up(&lock);
+       return error;
+}
+
+static int tomoyo_is_no_rewrite_file(const struct path_info *filename)
+{
+       struct no_rewrite_entry *ptr;
+       for (ptr = no_rewrite_list; ptr; ptr = ptr->next) {
+               if (ptr->is_deleted) continue;
+               if (!tomoyo_path_matches_to_pattern(filename, ptr->pattern)) 
continue;
+               return 1;
+       }
+       return 0;
+}
+
+int tomoyo_add_no_rewrite_policy(char *pattern, const int is_delete)
+{
+       return tomoyo_add_no_rewrite_entry(pattern, is_delete);
+}
+
+int tomoyo_read_no_rewrite_policy(struct io_buffer *head)
+{
+       struct no_rewrite_entry *ptr = head->read_var2;
+       if (!ptr) ptr = no_rewrite_list;
+       while (ptr) {
+               head->read_var2 = ptr;
+               if (!ptr->is_deleted &&
+                   tomoyo_io_printf(head,
+                                    TOMOYO_KEYWORD_DENY_REWRITE "%s\n",
+                                    ptr->pattern->name))
+                       break;
+               ptr = ptr->next;
+       }
+       return ptr ? -ENOMEM : 0;
+}
+
+/*************************  FILE ACL HANDLER  *************************/
+
+static int tomoyo_add_file_acl(const char *filename,
+                               u8 perm,
+                               struct domain_info * const domain,
+                               const u8 is_add)
+{
+       const struct path_info *saved_filename;
+       struct acl_info *ptr;
+       int error = -ENOMEM;
+       u8 is_group = 0;
+       if (!domain) return -EINVAL;
+       if (perm > 7 || !perm) {
+               printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n", 
__FUNCTION__, perm, filename);
+               return -EINVAL;
+       }
+       if (!tomoyo_is_correct_path(filename, 0, 0, 0, __FUNCTION__)) return 
-EINVAL;
+       if (filename[0] == '@') {
+               /* This cast is OK because I don't dereference in this 
function. */
+               if ((saved_filename =
+                       (struct path_info *) 
tomoyo_find_or_assign_new_group(filename + 1)) == NULL)
+                       return -ENOMEM;
+               is_group = 1;
+       } else {
+               if ((saved_filename = tomoyo_save_name(filename)) == NULL) 
return -ENOMEM;
+               if (!saved_filename->is_dir) {
+                       if (perm == 4 &&
+                           tomoyo_is_globally_readable_file(saved_filename) &&
+                           is_add) {
+                               return 0;   /* Don't add if the file is 
globally readable files. */
+                       }
+               } else {
+                       if ((perm & 2) == 0)
+                               return 0; /* Don't add if the directory doesn't 
have write permission. */
+               }
+       }
+       down(&domain_acl_lock);
+       if (is_add) {
+               if ((ptr = domain->first_acl_ptr) == NULL) goto first_entry;
+               while (1) {
+                       struct file_acl_record *new_ptr;
+                       if (ptr->type == TOMOYO_TYPE_FILE_ACL) {
+                               if (((struct file_acl_record *) 
ptr)->u.filename == saved_filename) {
+                                       if (ptr->is_deleted) {
+                                               ptr->u.b[0] = 0;
+                                               mb();
+                                               ptr->is_deleted = 0;
+                                       }
+                                       /* Found. Just 'OR' the permission 
bits. */
+                                       ptr->u.b[0] |= perm;
+                                       error = 0;
+                                       
tomoyo_update_counter(TOMOYO_UPDATES_COUNTER_DOMAIN_POLICY);
+                                       break;
+                               }
+                       }
+                       if (ptr->next) {
+                               ptr = ptr->next;
+                               continue;
+                       }
+       first_entry: ;
+                       if (is_add == 1 && tomoyo_too_many_domain_acl(domain)) 
break;
+                       /* Not found. Append it to the tail. */
+                       if ((new_ptr = tomoyo_alloc_element(sizeof(*new_ptr))) 
== NULL) break;
+                       new_ptr->head.type = TOMOYO_TYPE_FILE_ACL;
+                       new_ptr->head.u.b[0] = perm;
+                       new_ptr->head.u.b[1] = is_group;
+                       new_ptr->u.filename = saved_filename;
+                       error = tomoyo_add_domain_acl(ptr, domain, (struct 
acl_info *) new_ptr);
+                       break;
+               }
+       } else {
+               for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+                       if (ptr->type != TOMOYO_TYPE_FILE_ACL ||
+                           ptr->is_deleted ||
+                           ptr->u.b[0] != perm)
+                               continue;
+                       if (((struct file_acl_record *) ptr)->u.filename != 
saved_filename) continue;
+                       error = tomoyo_del_domain_acl(ptr);
+                       break;
+               }
+       }
+       up(&domain_acl_lock);
+       return error;
+}
+
+static int tomoyo_check_file_acl(const struct path_info *filename, const u8 
perm)
+{
+       const struct domain_info *domain =
+               ((struct tomoyo_security *) current->security)->domain_info;
+       struct acl_info *ptr;
+       const int may_use_pattern = ((perm & 1) == 0);
+       if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+       if (!filename->is_dir) {
+               if (perm == 4 && tomoyo_is_globally_readable_file(filename)) 
return 0;
+       }
+       for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+               if (ptr->type != TOMOYO_TYPE_FILE_ACL ||
+                   ptr->is_deleted ||
+                   (ptr->u.b[0] & perm) != perm)
+                       continue;
+               if (ptr->u.b[1]) {
+                       if (tomoyo_path_matchies_to_group(filename,
+                                                         ((struct file_acl_record 
*) ptr)->u.group,
+                                                         may_use_pattern))
+                               return 0;
+               } else if (may_use_pattern ||
+                          !((struct file_acl_record *) 
ptr)->u.filename->is_patterned) {
+                       if (tomoyo_path_matches_to_pattern(filename,
+                                                          ((struct 
file_acl_record *) ptr)->u.filename))
+                               return 0;
+               }
+       }
+       return -EPERM;
+}
+
+static int tomoyo_check_file_perm2(const struct path_info *filename, const u8 
perm, const char *operation)
+{
+       int error = 0;
+       if (!filename) return 0;
+       error = tomoyo_check_file_acl(filename, perm);
+       tomoyo_audit_file_log(filename, perm, !error);
+       if (error) {
+               struct domain_info * const domain =
+                       ((struct tomoyo_security *) 
current->security)->domain_info;
+               const int is_enforce = 
tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+               if (tomoyo_verbose_mode()) {
+                       printk("TOMOYO-%s: Access %d(%s) to %s denied for %s\n",
+                              tomoyo_get_msg(is_enforce), perm, operation,
+                              filename->name, tomoyo_get_last_name(domain));
+               }
+               if (is_enforce) error =
+                       tomoyo_check_supervisor("%s\n%d %s\n",
+                                               domain->domainname->name, perm, 
filename->name);
+               else if (tomoyo_check_accept(TOMOYO_MAC_FOR_FILE)) {
+                       /* Don't use patterns if execution bit is on. */
+                       const struct path_info *patterned_file =
+                               ((perm & 1) == 0) ? 
tomoyo_get_pattern(filename) : filename;
+                       tomoyo_add_file_acl(patterned_file->name, perm, domain, 
1);
+               }
+               if (!is_enforce) error = 0;
+       }
+       return error;
+}
+
+int tomoyo_add_file_policy(char *data, struct domain_info *domain, const int 
is_delete)
+{
+       char *filename = strchr(data, ' ');
+       unsigned int perm;
+       u8 type;
+       if (!filename) return -EINVAL;
+       *filename++ = '\0';
+       if (sscanf(data, "%u", &perm) == 1) {
+               return tomoyo_add_file_acl(filename, (u8) perm, domain, 
is_delete ? 0 : -1);
+       }
+       if (strncmp(data, "allow_", 6)) goto out;
+       data += 6;
+       for (type = 0; acl_type_array[type].keyword; type++) {
+               if (strcmp(data, acl_type_array[type].keyword)) continue;
+               if (acl_type_array[type].paths == 2) {
+                       char *filename2 = strchr(filename, ' ');
+                       if (!filename2) break;
+                       *filename2++ = '\0';
+                       return tomoyo_add_double_write_acl(type,
+                                                          filename,
+                                                          filename2,
+                                                          domain,
+                                                          is_delete ? 0 : -1);
+               } else {
+                       return tomoyo_add_single_write_acl(type,
+                                                          filename,
+                                                          domain,
+                                                          is_delete ? 0 : -1);
+               }
+               break;
+       }
+ out:
+       return -EINVAL;
+}
+
+static int tomoyo_add_single_write_acl(const u8 type,
+                                       const char *filename,
+                                       struct domain_info * const domain,
+                                       const u8 is_add)
+{
+       const struct path_info *saved_filename;
+       struct acl_info *ptr;
+       int error = -ENOMEM;
+       u8 is_group = 0;
+       if (!domain) return -EINVAL;
+       if (!tomoyo_is_correct_path(filename, 0, 0, 0, __FUNCTION__)) return 
-EINVAL;
+       if (filename[0] == '@') {
+               /* This cast is OK because I don't dereference in this 
function. */
+               if ((saved_filename =
+                       (struct path_info *) 
tomoyo_find_or_assign_new_group(filename + 1)) == NULL)
+                       return -ENOMEM;
+               is_group = 1;
+       } else {
+               if ((saved_filename = tomoyo_save_name(filename)) == NULL) 
return -ENOMEM;
+       }
+       down(&domain_acl_lock);
+       if (is_add) {
+               if ((ptr = domain->first_acl_ptr) == NULL) goto first_entry;
+               while (1) {
+                       struct single_acl_record *new_ptr;
+                       if (ptr->type == type) {
+                               if (((struct single_acl_record *) 
ptr)->u.filename ==
+                                   saved_filename) {
+                                       ptr->is_deleted = 0;
+                                       /* Found. Nothing to do. */
+                                       error = 0;
+                                       break;
+                               }
+                       }
+                       if (ptr->next) {
+                               ptr = ptr->next;
+                               continue;
+                       }
+               first_entry: ;
+                       if (is_add == 1 && tomoyo_too_many_domain_acl(domain)) 
break;
+                       /* Not found. Append it to the tail. */
+                       if ((new_ptr = tomoyo_alloc_element(sizeof(*new_ptr))) 
== NULL) break;
+                       new_ptr->head.type = type;
+                       new_ptr->head.u.w = is_group;
+                       new_ptr->u.filename = saved_filename;
+                       error = tomoyo_add_domain_acl(ptr, domain, (struct 
acl_info *) new_ptr);
+                       break;
+               }
+       } else {
+               error = -ENOENT;
+               for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+                       if (ptr->type != type || ptr->is_deleted) continue;
+                       if (((struct single_acl_record *) ptr)->u.filename != 
saved_filename)
+                               continue;
+                       error = tomoyo_del_domain_acl(ptr);
+                       break;
+               }
+       }
+       up(&domain_acl_lock);
+       return error;
+}
+
+static int tomoyo_add_double_write_acl(const u8 type,
+                                       const char *filename1,
+                                       const char *filename2,
+                                       struct domain_info * const domain,
+                                       const u8 is_add)
+{
+       const struct path_info *saved_filename1, *saved_filename2;
+       struct acl_info *ptr;
+       int error = -ENOMEM;
+       u8 is_group1 = 0, is_group2 = 0;
+       if (!domain) return -EINVAL;
+       if (!tomoyo_is_correct_path(filename1, 0, 0, 0, __FUNCTION__) ||
+           !tomoyo_is_correct_path(filename2, 0, 0, 0, __FUNCTION__))
+               return -EINVAL;
+       if (filename1[0] == '@') {
+               /* This cast is OK because I don't dereference in this 
function. */
+               if ((saved_filename1 =
+                       (struct path_info *) 
tomoyo_find_or_assign_new_group(filename1 + 1)) == NULL)
+                       return -ENOMEM;
+               is_group1 = 1;
+       } else {
+               if ((saved_filename1 = tomoyo_save_name(filename1)) == NULL) 
return -ENOMEM;
+       }
+       if (filename2[0] == '@') {
+               /* This cast is OK because I don't dereference in this 
function. */
+               if ((saved_filename2 =
+                       (struct path_info *) 
tomoyo_find_or_assign_new_group(filename2 + 1)) == NULL)
+                       return -ENOMEM;
+               is_group2 = 1;
+       } else {
+               if ((saved_filename2 = tomoyo_save_name(filename2)) == NULL) 
return -ENOMEM;
+       }
+       down(&domain_acl_lock);
+       if (is_add) {
+               if ((ptr = domain->first_acl_ptr) == NULL) goto first_entry;
+               while (1) {
+                       struct double_acl_record *new_ptr;
+                       if (ptr->type == type) {
+                               if (((struct double_acl_record *) 
ptr)->u1.filename1 ==
+                                       saved_filename1 &&
+                                   ((struct double_acl_record *) 
ptr)->u2.filename2 ==
+                                       saved_filename2) {
+                                       ptr->is_deleted = 0;
+                                       /* Found. Nothing to do. */
+                                       error = 0;
+                                       break;
+                               }
+                       }
+                       if (ptr->next) {
+                               ptr = ptr->next;
+                               continue;
+                       }
+               first_entry: ;
+                       if (is_add == 1 && tomoyo_too_many_domain_acl(domain)) 
break;
+                       /* Not found. Append it to the tail. */
+                       if ((new_ptr = tomoyo_alloc_element(sizeof(*new_ptr))) 
== NULL) break;
+                       new_ptr->head.type = type;
+                       new_ptr->head.u.b[0] = is_group1;
+                       new_ptr->head.u.b[1] = is_group2;
+                       new_ptr->u1.filename1 = saved_filename1;
+                       new_ptr->u2.filename2 = saved_filename2;
+                       error = tomoyo_add_domain_acl(ptr, domain, (struct 
acl_info *) new_ptr);
+                       break;
+               }
+       } else {
+               error = -ENOENT;
+               for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+                       if (ptr->type != type || ptr->is_deleted) continue;
+                       if (((struct double_acl_record *) ptr)->u1.filename1 != 
saved_filename1 ||
+                               ((struct double_acl_record *) 
ptr)->u2.filename2 != saved_filename2)
+                                       continue;
+                       error = tomoyo_del_domain_acl(ptr);
+                       break;
+               }
+       }
+       up(&domain_acl_lock);
+       return error;
+}
+
+static int tomoyo_check_single_write_acl(const u8 type, const struct path_info 
*filename)
+{
+       const struct domain_info *domain =
+               ((struct tomoyo_security *) current->security)->domain_info;
+       struct acl_info *ptr;
+       if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+       for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+               if (ptr->type != type || ptr->is_deleted) continue;
+               if (ptr->u.w) {
+                       if (!tomoyo_path_matchies_to_group(filename,
+                                                          ((struct 
single_acl_record *) ptr)->u.group,
+                                                          1))
+                               continue;
+               } else {
+                       if (!tomoyo_path_matches_to_pattern(filename,
+                                                           ((struct 
single_acl_record *) ptr)->u.filename))
+                               continue;
+               }
+               return 0;
+       }
+       return -EPERM;
+}
+
+static int tomoyo_check_double_write_acl(const u8 type,
+                                         const struct path_info *filename1,
+                                         const struct path_info *filename2)
+{
+       const struct domain_info *domain =
+               ((struct tomoyo_security *) current->security)->domain_info;
+       struct acl_info *ptr;
+       if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+       for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+               if (ptr->type != type || ptr->is_deleted) continue;
+               if (ptr->u.b[0]) {
+                       if (!tomoyo_path_matchies_to_group(filename1,
+                                                          ((struct 
double_acl_record *) ptr)->u1.group1,
+                                                          1))
+                               continue;
+               } else {
+                       if (!tomoyo_path_matches_to_pattern(filename1,
+                                                           ((struct 
double_acl_record *) ptr)->u1.filename1))
+                               continue;
+               }
+               if (ptr->u.b[1]) {
+                       if (!tomoyo_path_matchies_to_group(filename2,
+                                                          ((struct 
double_acl_record *) ptr)->u2.group2,
+                                                          1))
+                               continue;
+               } else {
+                       if (!tomoyo_path_matches_to_pattern(filename2,
+                                                           ((struct 
double_acl_record *) ptr)->u2.filename2))
+                               continue;
+               }
+               return 0;
+       }
+       return -EPERM;
+}
+
+static int tomoyo_check_single_write_permission2(const unsigned int operation,
+                                                 const struct path_info 
*filename)
+{
+       int error;
+       struct domain_info * const domain =
+               ((struct tomoyo_security *) current->security)->domain_info;
+       const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+       if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+       if (tomoyo_check_acl_flags(operation) < 0) return 0;
+       if (tomoyo_check_acl_flags(operation) > 0) {
+               error = tomoyo_check_single_write_acl(operation, filename);
+               tomoyo_audit_write_log(tomoyo_acltype2keyword(operation), 
filename, NULL, !error);
+               if (error) {
+                       if (tomoyo_verbose_mode()) {
+                               printk("TOMOYO-%s: Access '%s %s' denied for 
%s\n",
+                                      tomoyo_get_msg(is_enforce),
+                                      tomoyo_acltype2keyword(operation),
+                                      filename->name,
+                                      tomoyo_get_last_name(domain));
+                       }
+                       if (is_enforce)
+                               error = tomoyo_check_supervisor("%s\nallow_%s 
%s\n",
+                                                               
domain->domainname->name,
+                                                               
tomoyo_acltype2keyword(operation),
+                                                               filename->name);
+                       else if (tomoyo_check_accept(TOMOYO_MAC_FOR_FILE))
+                               tomoyo_add_single_write_acl(operation,
+                                                           
tomoyo_get_pattern(filename)->name,
+                                                           domain,
+                                                           1);
+                       if (!is_enforce)
+                               error = 0;
+               }
+       } else {
+               error = tomoyo_check_file_perm2(filename, 2, 
tomoyo_acltype2keyword(operation));
+       }
+       if (!error && operation == TOMOYO_TYPE_TRUNCATE_ACL && 
tomoyo_is_no_rewrite_file(filename)) {
+               error = 
tomoyo_check_single_write_permission2(TOMOYO_TYPE_REWRITE_ACL, filename);
+       }
+       return error;
+}
+
+int tomoyo_check_exec_perm(const struct path_info *filename, struct file *filp)
+{
+       if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+       return tomoyo_check_file_perm2(filename, 1, "do_execve");
+}
+
+int tomoyo_check_open_permission(struct dentry *dentry, struct vfsmount *mnt, 
const int flag)
+{
+       const int acc_mode = ACC_MODE(flag);
+       int error = -ENOMEM;
+       struct path_info *buf;
+       const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+       if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+       if (acc_mode == 0) return 0;
+       if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
+               /* I don't check directories here because mkdir() and rmdir() 
don't call me. */
+               return 0;
+       }
+       buf = tomoyo_get_path(dentry, mnt);
+       if (buf) {
+               error = 0;
+               if ((acc_mode & MAY_WRITE)) {
+                       if ((flag & O_TRUNC) || !(flag & O_APPEND)) {
+                               if (tomoyo_is_no_rewrite_file(buf)) {
+                                       error = 
tomoyo_check_single_write_permission2(TOMOYO_TYPE_REWRITE_ACL,
+                                                                               
      buf);
+                               }
+                       }
+               }
+               if (error == 0) error = tomoyo_check_file_perm2(buf, acc_mode, 
"open");
+               if (error == 0 && (flag & O_TRUNC))
+                       error = 
tomoyo_check_single_write_permission2(TOMOYO_TYPE_TRUNCATE_ACL, buf);
+               tomoyo_free(buf);
+       }
+       if (!is_enforce) error = 0;
+       return error;
+}
+
+int tomoyo_check_single_write_permission(const unsigned int operation,
+                                         struct dentry *dentry,
+                                         struct vfsmount *mnt)
+{
+       int error = -ENOMEM;
+       struct path_info *buf;
+       const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+       if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+       buf = tomoyo_get_path(dentry, mnt);
+       if (buf) {
+               switch (operation) {
+               case TOMOYO_TYPE_MKDIR_ACL:
+               case TOMOYO_TYPE_RMDIR_ACL:
+                       if (!buf->is_dir) {
+                               strcat((char *) buf->name, "/");
+                               tomoyo_fill_path_info(buf);
+                       }
+               }
+               error = tomoyo_check_single_write_permission2(operation, buf);
+               tomoyo_free(buf);
+       }
+       if (!is_enforce) error = 0;
+       return error;
+}
+
+int tomoyo_check_rewrite_permission(struct file *filp)
+{
+       int error = -ENOMEM;
+       const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+       struct path_info *buf = tomoyo_get_path(filp->f_dentry, filp->f_vfsmnt);
+       if (buf) {
+               if (tomoyo_is_no_rewrite_file(buf)) {
+                       error = 
tomoyo_check_single_write_permission2(TOMOYO_TYPE_REWRITE_ACL, buf);
+               } else {
+                       error = 0;
+               }
+               tomoyo_free(buf);
+       }
+       if (!is_enforce) error = 0;
+       return error;
+}
+
+int tomoyo_check_double_write_permission(const unsigned int operation,
+                                         struct dentry *dentry1,
+                                         struct vfsmount *mnt1,
+                                         struct dentry *dentry2,
+                                         struct vfsmount *mnt2)
+{
+       int error = -ENOMEM;
+       struct path_info *buf1, *buf2;
+       struct domain_info * const domain =
+               ((struct tomoyo_security *) current->security)->domain_info;
+       const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+       if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+       if (tomoyo_check_acl_flags(operation) < 0) return 0;         
+       buf1 = tomoyo_get_path(dentry1, mnt1);
+       buf2 = tomoyo_get_path(dentry2, mnt2);
+       if (buf1 && buf2) {
+               if (operation == TOMOYO_TYPE_RENAME_ACL) {
+                       /* TOMOYO_TYPE_LINK_ACL can't reach here for directory. 
*/
+                       if (dentry1->d_inode && 
S_ISDIR(dentry1->d_inode->i_mode)) {
+                               if (!buf1->is_dir) {
+                                       strcat((char *) buf1->name, "/");
+                                       tomoyo_fill_path_info(buf1);
+                               }
+                               if (!buf2->is_dir) {
+                                       strcat((char *) buf2->name, "/");
+                                       tomoyo_fill_path_info(buf2);
+                               }
+                       }
+               }
+               if (tomoyo_check_acl_flags(operation) > 0) {
+                       error = tomoyo_check_double_write_acl(operation, buf1, 
buf2);
+                       
tomoyo_audit_write_log(tomoyo_acltype2keyword(operation), buf1, buf2, !error);
+                       if (error) {
+                               if (tomoyo_verbose_mode()) {
+                                       printk("TOMOYO-%s: Access '%s %s %s' denied 
for %s\n",
+                                              tomoyo_get_msg(is_enforce),
+                                              
tomoyo_acltype2keyword(operation),
+                                              buf1->name,
+                                              buf2->name,
+                                              tomoyo_get_last_name(domain));
+                               }
+                               if (is_enforce)
+                                       error = 
tomoyo_check_supervisor("%s\nallow_%s %s %s\n",
+                                                                       
domain->domainname->name,
+                                                                       
tomoyo_acltype2keyword(operation),
+                                                                       
buf1->name,
+                                                                       
buf2->name);
+                               else if 
(tomoyo_check_accept(TOMOYO_MAC_FOR_FILE))
+                                       tomoyo_add_double_write_acl(operation,
+                                                                   
tomoyo_get_pattern(buf1)->name,
+                                                                   
tomoyo_get_pattern(buf2)->name,
+                                                                   domain,
+                                                                   1);
+                       }
+               } else {
+                       error = tomoyo_check_file_perm2(buf1, 2, 
tomoyo_acltype2keyword(operation));
+                       if (!error)
+                               error = tomoyo_check_file_perm2(buf2,
+                                                               2,
+                                                               
tomoyo_acltype2keyword(operation));
+               }
+       }
+       tomoyo_free(buf1);
+       tomoyo_free(buf2);
+       if (!is_enforce) error = 0;
+       return error;
+}
---------------

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to