[TOMOYO #7 13/30] mount restriction part.

From: Tetsuo Handa
Date: Fri Apr 04 2008 - 09:12:12 EST


This file controls mount() requests.

Signed-off-by: Kentaro Takeda <takedakn@xxxxxxxxxxxxx>
Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
Signed-off-by: Toshiharu Harada <haradats@xxxxxxxxxxxxx>
---
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;
+}

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