lkml.org 
[lkml]   [2007]   [Jun]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[TOMOYO 8/9] File access control functions.
    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 <takedakn@nttdata.co.jp>
    Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>

    ---------------
    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 majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2007-06-14 09:41    [W:3.643 / U:0.228 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site