lkml.org 
[lkml]   [2008]   [Apr]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[TOMOYO #7 13/30] mount restriction part.
    This file controls mount() requests.

    Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
    Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
    Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
    ---
    fs/sakura_mount.c | 544 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
    1 file changed, 544 insertions(+)

    --- /dev/null
    +++ linux-2.6.25-rc8-mm1/fs/sakura_mount.c
    @@ -0,0 +1,544 @@
    +/*
    + * fs/sakura_mount.c
    + *
    + * Implementation of the Domain-Free Mandatory Access Control.
    + *
    + * Copyright (C) 2005-2008 NTT DATA CORPORATION
    + *
    + * Version: 1.6.0 2008/04/01
    + *
    + */
    +
    +#include <linux/ccs_common.h>
    +#include <linux/sakura.h>
    +#include <linux/realpath.h>
    +#include <linux/namei.h>
    +
    +/* Keywords for mount restrictions. */
    +
    +/* Allow to call 'mount --bind /source_dir /dest_dir' */
    +#define MOUNT_BIND_KEYWORD "--bind"
    +/* Allow to call 'mount --move /old_dir /new_dir ' */
    +#define MOUNT_MOVE_KEYWORD "--move"
    +/* Allow to call 'mount -o remount /dir ' */
    +#define MOUNT_REMOUNT_KEYWORD "--remount"
    +/* Allow to call 'mount --make-unbindable /dir' */
    +#define MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable"
    +/* Allow to call 'mount --make-private /dir' */
    +#define MOUNT_MAKE_PRIVATE_KEYWORD "--make-private"
    +/* Allow to call 'mount --make-slave /dir' */
    +#define MOUNT_MAKE_SLAVE_KEYWORD "--make-slave"
    +/* Allow to call 'mount --make-shared /dir' */
    +#define MOUNT_MAKE_SHARED_KEYWORD "--make-shared"
    +
    +/* Structure for "allow_mount" keyword. */
    +struct mount_entry {
    + struct list1_head list;
    + const struct path_info *dev_name;
    + const struct path_info *dir_name;
    + const struct path_info *fs_type;
    + unsigned long flags;
    + bool is_deleted;
    +};
    +
    +/* The list for "struct mount_entry". */
    +static LIST1_HEAD(mount_list);
    +
    +/**
    + * update_mount_acl - Update "struct mount_entry" list.
    + *
    + * @dev_name: Name of device file.
    + * @dir_name: Name of mount point.
    + * @fs_type: Name of filesystem.
    + * @flags: Mount options.
    + * @is_delete: True if it is a delete request.
    + *
    + * Returns 0 on success, negative value otherwise.
    + */
    +static int update_mount_acl(const char *dev_name, const char *dir_name,
    + const char *fs_type, const unsigned long flags,
    + const bool is_delete)
    +{
    + struct file_system_type *type = NULL;
    + struct mount_entry *new_entry;
    + struct mount_entry *ptr;
    + const struct path_info *fs;
    + const struct path_info *dev;
    + const struct path_info *dir;
    + static DEFINE_MUTEX(lock);
    + int error = -ENOMEM;
    + fs = ccs_save_name(fs_type);
    + if (!fs)
    + return -EINVAL;
    + if (!dev_name)
    + /* Map dev_name to "<NULL>" for if no dev_name given. */
    + dev_name = "<NULL>";
    + if (!strcmp(fs->name, MOUNT_REMOUNT_KEYWORD))
    + /* Fix dev_name to "any" for remount permission. */
    + dev_name = "any";
    + if (!strcmp(fs->name, MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
    + !strcmp(fs->name, MOUNT_MAKE_PRIVATE_KEYWORD) ||
    + !strcmp(fs->name, MOUNT_MAKE_SLAVE_KEYWORD) ||
    + !strcmp(fs->name, MOUNT_MAKE_SHARED_KEYWORD))
    + dev_name = "any";
    + if (!ccs_is_correct_path(dev_name, 0, 0, 0, __func__) ||
    + !ccs_is_correct_path(dir_name, 0, 0, 0, __func__))
    + return -EINVAL;
    + dev = ccs_save_name(dev_name);
    + dir = ccs_save_name(dir_name);
    + if (!dev || !dir)
    + return -ENOMEM;
    + mutex_lock(&lock);
    + list1_for_each_entry(ptr, &mount_list, list) {
    + if (ptr->flags != flags ||
    + ccs_pathcmp(ptr->dev_name, dev) ||
    + ccs_pathcmp(ptr->dir_name, dir) ||
    + ccs_pathcmp(ptr->fs_type, fs))
    + continue;
    + error = 0;
    + if (is_delete) {
    + ptr->is_deleted = true;
    + goto out;
    + } else {
    + if (ptr->is_deleted) {
    + ptr->is_deleted = false;
    + goto update;
    + }
    + goto out; /* No changes. */
    + }
    + }
    + if (is_delete) {
    + error = -ENOENT;
    + goto out;
    + }
    + new_entry = ccs_alloc_element(sizeof(*new_entry));
    + if (!new_entry)
    + goto out;
    + new_entry->dev_name = dev;
    + new_entry->dir_name = dir;
    + new_entry->fs_type = fs;
    + new_entry->flags = flags;
    + list1_add_tail_mb(&new_entry->list, &mount_list);
    + error = 0;
    + ptr = new_entry;
    + update:
    + if (!strcmp(fs->name, MOUNT_REMOUNT_KEYWORD)) {
    + printk(KERN_CONT "%sAllow remount %s with options 0x%lX.\n",
    + ccs_log_level, dir->name, ptr->flags);
    + } else if (!strcmp(fs->name, MOUNT_BIND_KEYWORD)
    + || !strcmp(fs->name, MOUNT_MOVE_KEYWORD)) {
    + printk(KERN_CONT "%sAllow mount %s %s %s with options 0x%lX\n",
    + ccs_log_level, fs->name, dev->name, dir->name,
    + ptr->flags);
    + } else if (!strcmp(fs->name, MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
    + !strcmp(fs->name, MOUNT_MAKE_PRIVATE_KEYWORD) ||
    + !strcmp(fs->name, MOUNT_MAKE_SLAVE_KEYWORD) ||
    + !strcmp(fs->name, MOUNT_MAKE_SHARED_KEYWORD)) {
    + printk(KERN_CONT "%sAllow mount %s %s with options 0x%lX.\n",
    + ccs_log_level, fs->name, dir->name, ptr->flags);
    + } else {
    + type = get_fs_type(fs->name);
    + if (type && (type->fs_flags & FS_REQUIRES_DEV) != 0)
    + printk(KERN_CONT "%sAllow mount -t %s %s %s "
    + "with options 0x%lX.\n", ccs_log_level,
    + fs->name, dev->name, dir->name, ptr->flags);
    + else
    + printk(KERN_CONT "%sAllow mount %s on %s "
    + "with options 0x%lX.\n", ccs_log_level,
    + fs->name, dir->name, ptr->flags);
    + }
    + if (type)
    + put_filesystem(type);
    + out:
    + mutex_unlock(&lock);
    + ccs_update_counter(CCS_UPDATES_COUNTER_SYSTEM_POLICY);
    + return error;
    +}
    +
    +/**
    + * print_success - Print success messages.
    + *
    + * @dev_name: Name of device file.
    + * @dir_name: Name of mount point.
    + * @type: Name of filesystem type.
    + * @flags: Mount options.
    + * @need_dev: Type of @dev_name.
    + *
    + * Returns nothing.
    + */
    +static void print_success(const char *dev_name, const char *dir_name,
    + const char *type, const unsigned long flags,
    + const int need_dev)
    +{
    + if (need_dev > 0) {
    + printk(KERN_DEBUG "SAKURA-NOTICE: "
    + "'mount -t %s %s %s 0x%lX' accepted.\n",
    + type, dev_name, dir_name, flags);
    + } else if (need_dev < 0) {
    + printk(KERN_DEBUG "SAKURA-NOTICE: "
    + "'mount %s %s %s 0x%lX' accepted.\n",
    + type, dev_name, dir_name, flags);
    + } else if (!strcmp(type, MOUNT_REMOUNT_KEYWORD)) {
    + printk(KERN_DEBUG "SAKURA-NOTICE: "
    + "'mount -o remount %s 0x%lX' accepted.\n",
    + dir_name, flags);
    + } else if (!strcmp(type, MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
    + !strcmp(type, MOUNT_MAKE_PRIVATE_KEYWORD) ||
    + !strcmp(type, MOUNT_MAKE_SLAVE_KEYWORD) ||
    + !strcmp(type, MOUNT_MAKE_SHARED_KEYWORD)) {
    + printk(KERN_DEBUG "SAKURA-NOTICE: "
    + "'mount %s %s 0x%lX' accepted.\n",
    + type, dir_name, flags);
    + } else {
    + printk(KERN_DEBUG "SAKURA-NOTICE: "
    + "'mount %s on %s 0x%lX' accepted.\n",
    + type, dir_name, flags);
    + }
    +}
    +
    +/**
    + * print_error - Print error messages.
    + *
    + * @dev_name: Name of device file.
    + * @dir_name: Name of mount point.
    + * @type: Name of filesystem type.
    + * @flags: Mount options.
    + * @is_enforce: True if it is enforcing mode.
    + * @error: Error value.
    + *
    + * Returns 0 if permitted by the administrator's decision, negative value
    + * otherwise.
    + */
    +static int print_error(const char *dev_name, const char *dir_name,
    + const char *type, const unsigned long flags,
    + const bool is_enforce, int error)
    +{
    + const char *realname1 = ccs_realpath(dev_name);
    + const char *realname2 = ccs_realpath(dir_name);
    + const char *exename = ccs_get_exe();
    + if (!strcmp(type, MOUNT_REMOUNT_KEYWORD)) {
    + printk(KERN_WARNING "SAKURA-%s: mount -o remount %s 0x%lX "
    + "(pid=%d:exe=%s): Permission denied.\n",
    + ccs_get_msg(is_enforce),
    + realname2 ? realname2 : dir_name,
    + flags, current->pid, exename);
    + if (is_enforce)
    + error = ccs_check_supervisor("# %s is requesting\n"
    + "mount -o remount %s "
    + "0x%lX\n", exename,
    + realname2 ? realname2
    + : dir_name, flags);
    + } else if (!strcmp(type, MOUNT_BIND_KEYWORD)
    + || !strcmp(type, MOUNT_MOVE_KEYWORD)) {
    + printk(KERN_WARNING "SAKURA-%s: mount %s %s %s 0x%lX "
    + "(pid=%d:exe=%s): Permission denied.\n",
    + ccs_get_msg(is_enforce), type,
    + realname1 ? realname1 : dev_name,
    + realname2 ? realname2 : dir_name,
    + flags, current->pid, exename);
    + if (is_enforce)
    + error = ccs_check_supervisor("# %s is requesting\n"
    + "mount %s %s %s 0x%lX\n",
    + exename, type,
    + realname1 ? realname1 :
    + dev_name,
    + realname2 ? realname2 :
    + dir_name, flags);
    + } else if (!strcmp(type, MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
    + !strcmp(type, MOUNT_MAKE_PRIVATE_KEYWORD) ||
    + !strcmp(type, MOUNT_MAKE_SLAVE_KEYWORD) ||
    + !strcmp(type, MOUNT_MAKE_SHARED_KEYWORD)) {
    + printk(KERN_WARNING "SAKURA-%s: mount %s %s 0x%lX "
    + "(pid=%d:exe=%s): Permission denied.\n",
    + ccs_get_msg(is_enforce), type,
    + realname2 ? realname2 : dir_name,
    + flags, current->pid, exename);
    + if (is_enforce)
    + error = ccs_check_supervisor("# %s is requesting\n"
    + "mount %s %s 0x%lX",
    + exename, type,
    + realname2 ? realname2 :
    + dir_name, flags);
    + } else {
    + printk(KERN_WARNING "SAKURA-%s: mount -t %s %s %s 0x%lX "
    + "(pid=%d:exe=%s): Permission denied.\n",
    + ccs_get_msg(is_enforce), type,
    + realname1 ? realname1 : dev_name,
    + realname2 ? realname2 : dir_name,
    + flags, current->pid, exename);
    + if (is_enforce)
    + error = ccs_check_supervisor("# %s is requesting\n"
    + "mount -t %s %s %s "
    + "0x%lX\n", exename, type,
    + realname1 ? realname1 :
    + dev_name,
    + realname2 ? realname2 :
    + dir_name, flags);
    + }
    + ccs_free(exename);
    + ccs_free(realname2);
    + ccs_free(realname1);
    + return error;
    +}
    +
    +/**
    + * check_mount_permission2 - Check permission for mount() operation.
    + *
    + * @dev_name: Name of device file.
    + * @dir_name: Name of mount point.
    + * @type: Name of filesystem type. May be NULL.
    + * @flags: Mount options.
    + *
    + * Returns 0 on success, negative value otherwise.
    + */
    +static int check_mount_permission2(char *dev_name, char *dir_name, char *type,
    + unsigned long flags)
    +{
    + const u8 mode = ccs_check_flags(CCS_SAKURA_RESTRICT_MOUNT);
    + const bool is_enforce = (mode == 3);
    + int error = -EPERM;
    + if (!mode)
    + return 0;
    + if (!type)
    + type = "<NULL>";
    + if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
    + flags &= ~MS_MGC_MSK;
    + switch (flags & (MS_REMOUNT | MS_MOVE | MS_BIND)) {
    + case MS_REMOUNT:
    + case MS_MOVE:
    + case MS_BIND:
    + case 0:
    + break;
    + default:
    + printk(KERN_WARNING "SAKURA-ERROR: "
    + "%s%s%sare given for single mount operation.\n",
    + flags & MS_REMOUNT ? "'remount' " : "",
    + flags & MS_MOVE ? "'move' " : "",
    + flags & MS_BIND ? "'bind' " : "");
    + return -EINVAL;
    + }
    + switch (flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED)) {
    + case MS_UNBINDABLE:
    + case MS_PRIVATE:
    + case MS_SLAVE:
    + case MS_SHARED:
    + case 0:
    + break;
    + default:
    + printk(KERN_WARNING "SAKURA-ERROR: "
    + "%s%s%s%sare given for single mount operation.\n",
    + flags & MS_UNBINDABLE ? "'unbindable' " : "",
    + flags & MS_PRIVATE ? "'private' " : "",
    + flags & MS_SLAVE ? "'slave' " : "",
    + flags & MS_SHARED ? "'shared' " : "");
    + return -EINVAL;
    + }
    + if (flags & MS_REMOUNT) {
    + error = check_mount_permission2(dev_name, dir_name,
    + MOUNT_REMOUNT_KEYWORD,
    + flags & ~MS_REMOUNT);
    + } else if (flags & MS_MOVE) {
    + error = check_mount_permission2(dev_name, dir_name,
    + MOUNT_MOVE_KEYWORD,
    + flags & ~MS_MOVE);
    + } else if (flags & MS_BIND) {
    + error = check_mount_permission2(dev_name, dir_name,
    + MOUNT_BIND_KEYWORD,
    + flags & ~MS_BIND);
    + } else if (flags & MS_UNBINDABLE) {
    + error = check_mount_permission2(dev_name, dir_name,
    + MOUNT_MAKE_UNBINDABLE_KEYWORD,
    + flags & ~MS_UNBINDABLE);
    + } else if (flags & MS_PRIVATE) {
    + error = check_mount_permission2(dev_name, dir_name,
    + MOUNT_MAKE_PRIVATE_KEYWORD,
    + flags & ~MS_PRIVATE);
    + } else if (flags & MS_SLAVE) {
    + error = check_mount_permission2(dev_name, dir_name,
    + MOUNT_MAKE_SLAVE_KEYWORD,
    + flags & ~MS_SLAVE);
    + } else if (flags & MS_SHARED) {
    + error = check_mount_permission2(dev_name, dir_name,
    + MOUNT_MAKE_SHARED_KEYWORD,
    + flags & ~MS_SHARED);
    + } else {
    + struct mount_entry *ptr;
    + struct file_system_type *fstype = NULL;
    + const char *requested_dir_name = NULL;
    + const char *requested_dev_name = NULL;
    + struct path_info rdev;
    + struct path_info rdir;
    + int need_dev = 0;
    +
    + requested_dir_name = ccs_realpath(dir_name);
    + if (!requested_dir_name) {
    + error = -ENOENT;
    + goto cleanup;
    + }
    + rdir.name = requested_dir_name;
    + ccs_fill_path_info(&rdir);
    +
    + /* Compare fs name. */
    + if (!strcmp(type, MOUNT_REMOUNT_KEYWORD)) {
    + /* Needn't to resolve dev_name */
    + } else if (!strcmp(type, MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
    + !strcmp(type, MOUNT_MAKE_PRIVATE_KEYWORD) ||
    + !strcmp(type, MOUNT_MAKE_SLAVE_KEYWORD) ||
    + !strcmp(type, MOUNT_MAKE_SHARED_KEYWORD)) {
    + /* Needn't to resolve dev_name */
    + } else if (!strcmp(type, MOUNT_BIND_KEYWORD) ||
    + !strcmp(type, MOUNT_MOVE_KEYWORD)) {
    + requested_dev_name = ccs_realpath(dev_name);
    + if (!requested_dev_name) {
    + error = -ENOENT;
    + goto cleanup;
    + }
    + rdev.name = requested_dev_name;
    + ccs_fill_path_info(&rdev);
    + /* dev_name is a directory */
    + need_dev = -1;
    + } else {
    + fstype = get_fs_type(type);
    + if (fstype) {
    + if (fstype->fs_flags & FS_REQUIRES_DEV) {
    + requested_dev_name
    + = ccs_realpath(dev_name);
    + if (!requested_dev_name) {
    + error = -ENOENT;
    + goto cleanup;
    + }
    + rdev.name = requested_dev_name;
    + ccs_fill_path_info(&rdev);
    + /* dev_name is a block device file */
    + need_dev = 1;
    + }
    + } else {
    + error = -ENODEV;
    + goto cleanup;
    + }
    + }
    + list1_for_each_entry(ptr, &mount_list, list) {
    + if (ptr->is_deleted)
    + continue;
    +
    + /* Compare options */
    + if (ptr->flags != flags)
    + continue;
    +
    + /* Compare fs name. */
    + if (strcmp(type, ptr->fs_type->name))
    + continue;
    +
    + /* Compare mount point. */
    + if (ccs_path_matches_pattern(&rdir, ptr->dir_name) == 0)
    + continue;
    +
    + /* Compare device name. */
    + if (requested_dev_name &&
    + ccs_path_matches_pattern(&rdev, ptr->dev_name) == 0)
    + continue;
    +
    + /* OK. */
    + error = 0;
    + print_success(requested_dev_name, requested_dir_name,
    + type, flags, need_dev);
    + break;
    + }
    + if (error)
    + error = print_error(dev_name, dir_name, type, flags,
    + is_enforce, error);
    + if (error && mode == 1)
    + update_mount_acl(need_dev ?
    + requested_dev_name : dev_name,
    + requested_dir_name, type, flags, 0);
    + cleanup:
    + ccs_free(requested_dev_name);
    + ccs_free(requested_dir_name);
    + if (fstype)
    + put_filesystem(fstype);
    + }
    + if (!is_enforce)
    + error = 0;
    + return error;
    +}
    +
    +/**
    + * ccs_check_mount_permission - Check permission for mount() operation.
    + *
    + * @dev_name: Name of device file.
    + * @dir_name: Name of mount point.
    + * @type: Name of filesystem type. May be NULL.
    + * @flags: Mount options.
    + *
    + * Returns 0 on success, negative value otherwise.
    + *
    + * This is a wrapper to allow use of 1.4.x patch for 1.5.x.
    + */
    +int ccs_check_mount_permission(char *dev_name, char *dir_name, char *type,
    + const unsigned long *flags)
    +{
    + return check_mount_permission2(dev_name, dir_name, type, *flags);
    +}
    +
    +/**
    + * ccs_write_mount_policy - Write "struct mount_entry" list.
    + *
    + * @data: String to parse.
    + * @is_delete: True if it is a delete request.
    + *
    + * Returns 0 on success, negative value otherwise.
    + */
    +int ccs_write_mount_policy(char *data, const bool is_delete)
    +{
    + char *cp;
    + char *cp2;
    + const char *fs;
    + const char *dev;
    + const char *dir;
    + unsigned long flags = 0;
    + cp2 = data;
    + cp = strchr(cp2, ' ');
    + if (!cp)
    + return -EINVAL;
    + *cp = '\0';
    + dev = cp2;
    + cp2 = cp + 1;
    + cp = strchr(cp2, ' ');
    + if (!cp)
    + return -EINVAL;
    + *cp = '\0';
    + dir = cp2;
    + cp2 = cp + 1;
    + cp = strchr(cp2, ' ');
    + if (!cp)
    + return -EINVAL;
    + *cp = '\0';
    + fs = cp2;
    + flags = simple_strtoul(cp + 1, NULL, 0);
    + return update_mount_acl(dev, dir, fs, flags, is_delete);
    +}
    +
    +/**
    + * ccs_read_mount_policy - Read "struct mount_entry" list.
    + *
    + * @head: Pointer to "struct ccs_io_buffer".
    + *
    + * Returns true on success, false otherwise.
    + */
    +bool ccs_read_mount_policy(struct ccs_io_buffer *head)
    +{
    + struct list1_head *pos;
    + list1_for_each_cookie(pos, head->read_var2, &mount_list) {
    + struct mount_entry *ptr;
    + ptr = list1_entry(pos, struct mount_entry, list);
    + if (ptr->is_deleted)
    + continue;
    + if (!ccs_io_printf(head, KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n",
    + ptr->dev_name->name, ptr->dir_name->name,
    + ptr->fs_type->name, ptr->flags))
    + goto out;
    + }
    + return true;
    + out:
    + return false;
    +}
    --


    \
     
     \ /
      Last update: 2008-04-04 15:15    [W:4.428 / U:1.072 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site