public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Kentaro Takeda <takedakn@nttdata.co.jp>
To: linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org
Cc: chrisw@sous-sol.org
Subject: [TOMOYO 10/15](repost) Namespace manipulation control functions.
Date: Tue, 02 Oct 2007 16:37:05 +0900	[thread overview]
Message-ID: <4701F521.4060204@nttdata.co.jp> (raw)
In-Reply-To: <4701F285.5000206@nttdata.co.jp>

Mount access control functions for TOMOYO Linux.
TOMOYO Linux checks permission according to
device name, mount point, filesystem type and optional flags.
TOMOYO Linux also checks permission in umount and pivot_root.

Each permission can be automatically accumulated into
the policy using 'learning mode'.

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/tomoyo/mount.c | 1019 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1019 insertions(+)

--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/security/tomoyo/mount.c	2007-10-02 11:26:22.000000000 +0900
@@ -0,0 +1,1019 @@
+/*
+ * security/tomoyo/mount.c
+ *
+ * Mount access control functions for TOMOYO Linux.
+ */
+
+#include "tomoyo.h"
+#include "realpath.h"
+#include <linux/namei.h>
+#include <linux/mnt_namespace.h>
+
+/***** KEYWORDS for mount restrictions. *****/
+
+#define MOUNT_BIND_KEYWORD    	      "--bind"
+#define MOUNT_MOVE_KEYWORD    	      "--move"
+#define MOUNT_REMOUNT_KEYWORD 	      "--remount"
+#define MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable"
+#define MOUNT_MAKE_PRIVATE_KEYWORD    "--make-private"
+#define MOUNT_MAKE_SLAVE_KEYWORD      "--make-slave"
+#define MOUNT_MAKE_SHARED_KEYWORD     "--make-shared"
+
+/***** The structure for mount restrictions. *****/
+
+struct mount_entry {
+	struct mount_entry *next; /* NULL if none. */
+	const struct path_info *dev_name;
+	const struct path_info *dir_name;
+	const struct path_info *fs_type;
+	unsigned int flags; /* Mount flags. */
+	u8 is_deleted;
+};
+
+struct no_umount_entry {
+	struct no_umount_entry *next; /* NULL if none. */
+	const struct path_info *dir;
+	u8 is_deleted;
+};
+
+/*************************  AUDIT FUNCTIONS  *************************/
+
+static int tmy_audit_mount_log(const u8 is_granted,
+			       const u8 is_enforce,
+			       const char *fmt, ...)
+	__attribute__((format(printf, 3, 4)));
+
+static int tmy_audit_mount_log(const u8 is_granted,
+			       const u8 is_enforce,
+			       const char *fmt, ...)
+{
+	char *buf1;
+	char *buf2;
+	unsigned int len;
+	va_list args;
+
+	if (is_granted) {
+		if (!tmy_audit_grant())
+			return 0;
+	} else {
+		if (!tmy_audit_reject())
+			return 0;
+	}
+
+	buf1 = tmy_alloc(PAGE_SIZE);
+
+	if (!buf1)
+		return -ENOMEM;
+
+	va_start(args, fmt);
+	len = vsnprintf(buf1, PAGE_SIZE, fmt, args);
+	va_end(args);
+
+	if (len >= PAGE_SIZE) {
+		tmy_free(buf1);
+		return -ENOMEM;
+	}
+
+	buf2 = tmy_init_audit_log(&len);
+
+	if (!buf2) {
+		tmy_free(buf1);
+		return -ENOMEM;
+	}
+
+	snprintf(buf2 + strlen(buf2),
+		 len - strlen(buf2),
+		 "%s", buf1);
+
+	tmy_free(buf1);
+
+	return tmy_write_audit_log(buf2, is_granted, is_enforce);
+}
+
+/************************  MOUNT RESTRICTION HANDLER  ************************/
+
+static void put_filesystem(struct file_system_type *fs)
+{
+	module_put(fs->owner);
+}
+
+static struct mount_entry *mount_list;
+
+/* Add or remove a mount entry. */
+static int tmy_add_mount_acl(const char *dev_name,
+			     const char *dir_name,
+			     const char *fs_type,
+			     const unsigned int flags,
+			     const u8 is_delete)
+{
+	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 DECLARE_MUTEX(lock);
+	int error = -ENOMEM;
+
+	fs = tmy_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) == 0)
+		/* Fix dev_name to "any" for remount permission. */
+		dev_name = "any";
+	if (strcmp(fs->name, MOUNT_MAKE_UNBINDABLE_KEYWORD) == 0 ||
+	    strcmp(fs->name, MOUNT_MAKE_PRIVATE_KEYWORD) == 0 ||
+	    strcmp(fs->name, MOUNT_MAKE_SLAVE_KEYWORD) == 0 ||
+	    strcmp(fs->name, MOUNT_MAKE_SHARED_KEYWORD) == 0)
+		dev_name = "any";
+
+	if (!tmy_correct_path(dev_name, 0, 0, 0, __FUNCTION__) ||
+	    !tmy_correct_path(dir_name, 1, 0, 1, __FUNCTION__))
+		return -EINVAL;
+
+	dev = tmy_save_name(dev_name);
+	if (!dev)
+		return -ENOMEM;
+	dir = tmy_save_name(dir_name);
+	if (!dir)
+		return -ENOMEM;
+
+	down(&lock);
+
+	for (ptr = mount_list; ptr; ptr = ptr->next) {
+		if (ptr->flags != flags ||
+		    tmy_pathcmp(ptr->dev_name, dev) ||
+		    tmy_pathcmp(ptr->dir_name, dir) ||
+		    tmy_pathcmp(ptr->fs_type, fs))
+			continue;
+
+		if (is_delete) {
+			ptr->is_deleted = 1;
+			error = 0;
+			goto out;
+		} else {
+			if (ptr->is_deleted) {
+				ptr->flags = flags;
+				ptr->is_deleted = 0;
+			} else {
+				if (ptr->flags == flags) {
+					error = 0;
+					goto out; /* No changes. */
+				}
+				ptr->flags = flags;
+			}
+			error = 0;
+			goto out;
+		}
+	}
+
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+
+	new_entry = tmy_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;
+	mb(); /* Instead of using spinlock. */
+
+	ptr = mount_list;
+	if (ptr) {
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = new_entry;
+	} else
+		mount_list = new_entry;
+
+	error = 0;
+	ptr = new_entry;
+
+out: ;
+	up(&lock);
+
+	return error;
+}
+
+/* Print error message for mount request. */
+static inline int tmy_mount_perm_error(char *dev_name,
+				       char *dir_name,
+				       char *type,
+				       unsigned long flags,
+				       const u8 is_enforce)
+{
+	int error = -EPERM;
+	const char *realname1 = tmy_realpath(dev_name);
+	const char *realname2 = tmy_realpath(dir_name);
+	const char *exename = tmy_get_exe();
+
+	if (!strcmp(type, MOUNT_REMOUNT_KEYWORD)) {
+
+		tmy_audit_mount_log(0, is_enforce,
+				    "mount -o remount %s 0x%lX",
+				    realname2 ? realname2 : dir_name,
+				    flags);
+		if (is_enforce &&
+		    !tmy_supervisor("# %s is requesting\nmount -o remount %s\n",
+				    exename, realname2 ? realname2 : dir_name))
+			error = 0;
+
+	} else if (!strcmp(type, MOUNT_BIND_KEYWORD) ||
+		   !strcmp(type, MOUNT_MOVE_KEYWORD)) {
+
+		tmy_audit_mount_log(0, is_enforce, "mount %s %s %s 0x%lX",
+				    type, realname1 ? realname1 : dev_name,
+				    realname2 ? realname2 : dir_name, flags);
+		if (is_enforce &&
+		    tmy_supervisor("# %s is requesting\nmount %s %s %s 0x%lX\n",
+				   exename, type,
+				   realname1 ? realname1 : dev_name,
+				   realname2 ? realname2 : dir_name,
+				   flags) == 0)
+			error = 0;
+
+	} 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)) {
+
+		tmy_audit_mount_log(0, is_enforce, "mount %s %s 0x%lX", type,
+				    realname2 ? realname2 : dir_name, flags);
+		if (is_enforce &&
+		    tmy_supervisor("# %s is requesting\nmount %s %s 0x%lX",
+				   exename, type,
+				   realname2 ? realname2 : dir_name,
+				   flags) == 0)
+			error = 0;
+
+	} else {
+
+		tmy_audit_mount_log(0, is_enforce, "mount -t %s %s %s 0x%lX",
+				    type, realname1 ? realname1 : dev_name,
+				    realname2 ? realname2 : dir_name, flags);
+		if (is_enforce &&
+		    tmy_supervisor("# %s is requesting\n"
+				   "mount -t %s %s %s 0x%lX\n",
+				   exename, type,
+				   realname1 ? realname1 : dev_name,
+				   realname2 ? realname2 : dir_name,
+				   flags) == 0)
+			error = 0;
+
+	}
+
+	tmy_free(exename);
+	tmy_free(realname2);
+	tmy_free(realname1);
+	return error;
+}
+
+/**
+ * tmy_mount_perm - check for mount permission.
+ * @dev_name: pointer to device name. May be NULL.
+ * @dir_name: pointer to mount point.
+ * @type:     pointer to filesystem. May be NULL.
+ * @flags:    mount flags.
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ */
+int tmy_mount_perm(char *dev_name,
+		   char *dir_name,
+		   char *type,
+		   unsigned long flags)
+{
+	const u8 is_enforce = tmy_enforce(TMY_RESTRICT_MOUNT);
+	int error = -EPERM;
+
+	if (!tmy_flags(TMY_RESTRICT_MOUNT))
+		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:
+		tmy_audit_mount_log(0, 1, "%s%s%sare given "
+				    "for single mount operation",
+				    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:
+		tmy_audit_mount_log(0, 1, "%s%s%s%sare given "
+				    "for single mount operation",
+				    flags & MS_UNBINDABLE ? "unbindable " : "",
+				    flags & MS_PRIVATE    ? "private " : "",
+				    flags & MS_SLAVE      ? "slave " : "",
+				    flags & MS_SHARED     ? "shared " : "");
+		return -EINVAL;
+	}
+
+	if (flags & MS_REMOUNT)
+		error = tmy_mount_perm(dev_name, dir_name,
+				       MOUNT_REMOUNT_KEYWORD,
+				       flags & ~MS_REMOUNT);
+	else if (flags & MS_MOVE)
+		error = tmy_mount_perm(dev_name, dir_name,
+				       MOUNT_MOVE_KEYWORD,
+				       flags & ~MS_MOVE);
+	else if (flags & MS_BIND)
+		error = tmy_mount_perm(dev_name, dir_name,
+				       MOUNT_BIND_KEYWORD,
+				       flags & ~MS_BIND);
+	else if (flags & MS_UNBINDABLE)
+		error = tmy_mount_perm(dev_name, dir_name,
+				       MOUNT_MAKE_UNBINDABLE_KEYWORD,
+				       flags & ~MS_UNBINDABLE);
+	else if (flags & MS_PRIVATE)
+		error = tmy_mount_perm(dev_name, dir_name,
+				       MOUNT_MAKE_PRIVATE_KEYWORD,
+				       flags & ~MS_PRIVATE);
+	else if (flags & MS_SLAVE)
+		error = tmy_mount_perm(dev_name, dir_name,
+				       MOUNT_MAKE_SLAVE_KEYWORD,
+				       flags & ~MS_SLAVE);
+	else if (flags & MS_SHARED)
+		error = tmy_mount_perm(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 = tmy_realpath(dir_name);
+		if (!requested_dir_name) {
+			error = -ENOENT;
+			goto cleanup;
+		}
+		rdir.name = requested_dir_name;
+		tmy_fill_path_info(&rdir);
+
+		/* Compare fs name. */
+		fstype = get_fs_type(type);
+		if (strcmp(type, MOUNT_REMOUNT_KEYWORD) == 0)
+			/* Needn't to resolve dev_name */;
+		else if (strcmp(type, MOUNT_MAKE_UNBINDABLE_KEYWORD) == 0 ||
+			 strcmp(type, MOUNT_MAKE_PRIVATE_KEYWORD) == 0 ||
+			 strcmp(type, MOUNT_MAKE_SLAVE_KEYWORD) == 0 ||
+			 strcmp(type, MOUNT_MAKE_SHARED_KEYWORD) == 0)
+			/* Needn't to resolve dev_name */;
+		else if (strcmp(type, MOUNT_BIND_KEYWORD) == 0 ||
+			 strcmp(type, MOUNT_MOVE_KEYWORD) == 0) {
+			requested_dev_name = tmy_realpath(dev_name);
+			if (!requested_dev_name) {
+				error = -ENOENT;
+				goto cleanup;
+			}
+			rdev.name = requested_dev_name;
+			tmy_fill_path_info(&rdev);
+			need_dev = -1;
+		} else if (fstype) {
+			if (fstype->fs_flags & FS_REQUIRES_DEV) {
+				requested_dev_name = tmy_realpath(dev_name);
+				if (!requested_dev_name) {
+					error = -ENOENT;
+					goto cleanup;
+				}
+				rdev.name = requested_dev_name;
+				tmy_fill_path_info(&rdev);
+				need_dev = 1;
+			}
+		} else {
+			error = -ENODEV;
+			goto cleanup;
+		}
+
+		for (ptr = mount_list; ptr; ptr = ptr->next) {
+			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 (tmy_path_match(&rdir, ptr->dir_name) == 0)
+				continue;
+
+			/* Compare device name. */
+			if (requested_dev_name &&
+			    tmy_path_match(&rdev, ptr->dev_name) == 0)
+				continue;
+
+			/* OK. */
+			error = 0;
+
+			if (need_dev > 0)
+				tmy_audit_mount_log(1, is_enforce,
+						    "mount -t %s %s %s 0x%lX",
+						    type,
+						    requested_dev_name,
+						    requested_dir_name, flags);
+			else if (need_dev < 0)
+				tmy_audit_mount_log(1, is_enforce,
+						    "mount %s %s %s 0x%lX",
+						    type, requested_dev_name,
+						    requested_dir_name, flags);
+			else if (!strcmp(type, MOUNT_REMOUNT_KEYWORD))
+				tmy_audit_mount_log(1, is_enforce,
+						    "mount -o remount %s 0x%lX",
+						    requested_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))
+				tmy_audit_mount_log(1, is_enforce,
+						    "mount %s %s 0x%lX",
+						    type, requested_dir_name,
+						    flags);
+			else
+				tmy_audit_mount_log(1, is_enforce,
+						    "mount %s on %s 0x%lX",
+						    type, requested_dir_name,
+						    flags);
+
+			break;
+		}
+
+		if (error)
+			error = tmy_mount_perm_error(dev_name, dir_name,
+						     type, flags, is_enforce);
+
+		if (error && tmy_accept(TMY_RESTRICT_MOUNT, NULL)) {
+			tmy_add_mount_acl(need_dev ?
+					  requested_dev_name : dev_name,
+					  requested_dir_name, type, flags, 0);
+			tmy_update_counter(TMY_UPDATE_SYSTEMPOLICY);
+		}
+
+cleanup:
+		tmy_free(requested_dev_name);
+		tmy_free(requested_dir_name);
+		if (fstype)
+			put_filesystem(fstype);
+
+	}
+
+	if (!is_enforce)
+		error = 0;
+	return error;
+
+}
+
+/**
+ * tmy_add_mount_policy - add or delete mount policy.
+ * @data:      a line to parse.
+ * @is_delete: is this delete request?
+ *
+ * Returns zero on success.
+ * Returns nonzero on failure.
+ */
+int tmy_add_mount_policy(char *data, const u8 is_delete)
+{
+	char *cp;
+	char *cp2;
+	const char *fs;
+	const char *dev;
+	const char *dir;
+	unsigned int 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 tmy_add_mount_acl(dev, dir, fs, flags, is_delete);
+}
+
+/**
+ * tmy_read_mount_policy - read mount policy.
+ * @head: pointer to "struct io_buffer".
+ *
+ * Returns nonzero if reading incomplete.
+ * Returns zero otherwise.
+ */
+int tmy_read_mount_policy(struct io_buffer *head)
+{
+	struct mount_entry *ptr = head->read_var2;
+
+	if (!ptr)
+		ptr = mount_list;
+
+	while (ptr) {
+		head->read_var2 = ptr;
+		if (ptr->is_deleted == 0 &&
+		    tmy_io_printf(head, TMY_ALLOW_MOUNT "%s %s %s 0x%x\n",
+				  ptr->dev_name->name, ptr->dir_name->name,
+				  ptr->fs_type->name, ptr->flags))
+			break;
+		ptr = ptr->next;
+	}
+
+	return ptr ? -ENOMEM : 0;
+}
+
+static int tmy_find_conceal(struct nameidata *nd,
+			    struct vfsmount *vfsmnt,
+			    struct dentry *dentry)
+{
+	int flag = 0;
+
+	if (IS_ROOT(dentry) || !d_unhashed(dentry)) {
+		while (1) {
+
+			if (nd->mnt->mnt_root == vfsmnt->mnt_root &&
+			    nd->dentry == dentry) {
+				flag = 1;
+				break;
+			}
+
+			if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+
+				spin_lock(&vfsmount_lock);
+
+				if (vfsmnt->mnt_parent == vfsmnt) {
+					spin_unlock(&vfsmount_lock);
+					break;
+				}
+				dentry = vfsmnt->mnt_mountpoint;
+				vfsmnt = vfsmnt->mnt_parent;
+
+				spin_unlock(&vfsmount_lock);
+
+				continue;
+			}
+			dentry = dentry->d_parent;
+
+		}
+	}
+
+	return flag;
+}
+
+/**
+ * tmy_conceal_mount - check for conceal mount permission.
+ * @nd: pointer to "struct nameidata".
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ *
+ * People seldom mount on directries that have submounts.
+ * For example, you don't mount on /usr/ directory if /usr/local/ directory
+ * is already mounted, do you?
+ * This function forbids such mount requests.
+ */
+int tmy_conceal_mount(struct nameidata *nd)
+{
+	int flag = 0;
+	struct mnt_namespace *namespace = current->nsproxy->mnt_ns;
+	u8 is_enforce;
+	char *dir;
+
+	if (!tmy_flags(TMY_DENY_CONCEAL_MOUNT))
+		return 0;
+
+	if (namespace) {
+		struct list_head *p;
+
+		list_for_each(p, &namespace->list) {
+			struct vfsmount *vfsmnt =
+				list_entry(p, struct vfsmount, mnt_list);
+			struct dentry *dentry = vfsmnt->mnt_root;
+
+			spin_lock(&dcache_lock);
+
+			flag = tmy_find_conceal(nd, vfsmnt, dentry);
+
+			spin_unlock(&dcache_lock);
+
+			if (flag)
+				break;
+		}
+	}
+
+	is_enforce = tmy_enforce(TMY_DENY_CONCEAL_MOUNT);
+	dir = tmy_realpath_dentry(nd->dentry, nd->mnt);
+
+	if (flag) {
+		int error = -EPERM;
+		if (dir) {
+			const char *exename = tmy_get_exe();
+
+			tmy_audit_mount_log(0, is_enforce, "mount %s", dir);
+			if (is_enforce &&
+			    tmy_supervisor("# %s is requesting\nmount on %s\n",
+					   exename, dir) == 0)
+				error = 0;
+
+			tmy_free(exename);
+		}
+		tmy_free(dir);
+
+		if (is_enforce)
+			return error;
+	} else {
+		if (dir)
+			tmy_audit_mount_log(1, is_enforce, "mount %s", dir);
+		tmy_free(dir);
+	}
+
+	return 0;
+}
+
+/************************  UMOUNT RESTRICTION HANDLER  ************************/
+
+static struct no_umount_entry *no_umount_list;
+
+/* Add or remove a no-unmount entry. */
+static int tmy_add_no_umount_acl(const char *dir, const u8 is_delete)
+{
+	struct no_umount_entry *new_entry;
+	struct no_umount_entry *ptr;
+	const struct path_info *saved_dir;
+	static DECLARE_MUTEX(lock);
+	int error = -ENOMEM;
+
+	if (!tmy_correct_path(dir, 1, 0, 1, __FUNCTION__))
+		return -EINVAL;
+	saved_dir = tmy_save_name(dir);
+	if (!saved_dir)
+		return -ENOMEM;
+
+	down(&lock);
+
+	for (ptr = no_umount_list; ptr; ptr = ptr->next) {
+		if (ptr->dir == saved_dir) {
+			ptr->is_deleted = is_delete;
+			error = 0;
+			goto out;
+		}
+	}
+
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+
+	new_entry = tmy_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+
+	new_entry->dir = saved_dir;
+	mb(); /* Instead of using spinlock. */
+
+	ptr = no_umount_list;
+	if (ptr) {
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = new_entry;
+	} else
+		no_umount_list = new_entry;
+
+	error = 0;
+out: ;
+
+	up(&lock);
+
+	return error;
+}
+
+/**
+ * tmy_umount_perm - check for no-unmount permission.
+ * @mnt: pointer to "struct vfsmount".
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ */
+int tmy_umount_perm(struct vfsmount *mnt)
+{
+	int error = -EPERM;
+	const char *dir0;
+	const u8 is_enforce = tmy_enforce(TMY_RESTRICT_UMOUNT);
+
+	if (!tmy_flags(TMY_RESTRICT_UMOUNT))
+		return 0;
+
+	dir0 = tmy_realpath_dentry(mnt->mnt_root, mnt);
+
+	if (dir0) {
+		struct no_umount_entry *ptr;
+		struct path_info dir;
+
+		dir.name = dir0;
+		tmy_fill_path_info(&dir);
+
+		for (ptr = no_umount_list; ptr; ptr = ptr->next) {
+			if (ptr->is_deleted)
+				continue;
+			if (tmy_path_match(&dir, ptr->dir))
+				break;
+		}
+
+		if (ptr) {
+			const char *exename = tmy_get_exe();
+
+			tmy_audit_mount_log(0, is_enforce, "umount %s", dir0);
+			if (is_enforce &&
+			    tmy_supervisor("# %s is requesting\nunmount %s\n",
+					   exename, dir0) == 0)
+				error = 0;
+
+			tmy_free(exename);
+		} else {
+			tmy_audit_mount_log(1, is_enforce, "umount %s", dir0);
+			error = 0;
+		}
+
+		tmy_free(dir0);
+	}
+
+	if (!is_enforce)
+		error = 0;
+	return error;
+}
+
+/**
+ * tmy_add_no_umount_policy - add or delete no-unmount policy.
+ * @data:      a line to parse.
+ * @is_delete: is this delete request?
+ *
+ * Returns zero on success.
+ * Returns nonzero on failure.
+ */
+int tmy_add_no_umount_policy(char *data, const u8 is_delete)
+{
+	return tmy_add_no_umount_acl(data, is_delete);
+}
+
+/**
+ * tmy_read_no_umount_policy - read no-unmount policy.
+ * @head: pointer to "struct io_buffer".
+ *
+ * Returns nonzero if reading incomplete.
+ * Returns zero otherwise.
+ */
+int tmy_read_no_umount_policy(struct io_buffer *head)
+{
+	struct no_umount_entry *ptr = head->read_var2;
+
+	if (!ptr)
+		ptr = no_umount_list;
+
+	while (ptr) {
+		head->read_var2 = ptr;
+		if (ptr->is_deleted == 0 &&
+		    tmy_io_printf(head, TMY_DENY_UNMOUNT "%s\n",
+				  ptr->dir->name))
+			break;
+		ptr = ptr->next;
+	}
+
+	return ptr ? -ENOMEM : 0;
+}
+
+/***** The structure for pivot_root restrictions. *****/
+
+struct pivot_root_entry {
+	struct pivot_root_entry *next;
+	const struct path_info *old_root;
+	const struct path_info *new_root;
+	u8 is_deleted;
+};
+
+/**********************  PIVOT_ROOT RESTRICTION HANDLER  **********************/
+
+static struct pivot_root_entry *pivot_root_list;
+
+/* Add or remove a pivot_root entry. */
+static int tmy_add_pivot_root_acl(const char *old_root,
+				  const char *new_root,
+				  const u8 is_delete)
+{
+	struct pivot_root_entry *new_entry;
+	struct pivot_root_entry *ptr;
+	const struct path_info *saved_old_root;
+	const struct path_info *saved_new_root;
+	static DECLARE_MUTEX(lock);
+	int error = -ENOMEM;
+
+	if (!tmy_correct_path(old_root, 1, 0, 1, __FUNCTION__) ||
+	    !tmy_correct_path(new_root, 1, 0, 1, __FUNCTION__))
+		return -EINVAL;
+
+	saved_old_root = tmy_save_name(old_root);
+	if (!saved_old_root)
+		return -ENOMEM;
+	saved_new_root = tmy_save_name(new_root);
+	if (!saved_new_root)
+		return -ENOMEM;
+
+	down(&lock);
+
+	for (ptr = pivot_root_list; ptr; ptr = ptr->next) {
+		if (ptr->old_root == saved_old_root &&
+		    ptr->new_root == saved_new_root) {
+			ptr->is_deleted = is_delete;
+			error = 0;
+			goto out;
+		}
+	}
+
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+
+	new_entry = tmy_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+
+	new_entry->old_root = saved_old_root;
+	new_entry->new_root = saved_new_root;
+	mb(); /* Instead of using spinlock. */
+
+	ptr = pivot_root_list;
+	if (ptr) {
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = new_entry;
+	} else
+		pivot_root_list = new_entry;
+
+	error = 0;
+out: ;
+
+	up(&lock);
+
+	return error;
+}
+
+/**
+ * tmy_pivot_root_perm - check for pivot_root permission.
+ * @old_nd:  pointer to "struct nameidata".
+ * @new_nd:  pointer to "struct nameidata".
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ */
+int tmy_pivot_root_perm(struct nameidata *old_nd, struct nameidata *new_nd)
+{
+	int error = -EPERM;
+	char *old_root;
+	char *new_root;
+	u8 is_enforce;
+	struct path_info old_root_dir;
+	struct path_info new_root_dir;
+
+	if (!tmy_flags(TMY_RESTRICT_PIVOT_ROOT))
+		return 0;
+
+	old_root = tmy_realpath_dentry(old_nd->dentry, old_nd->mnt);
+	new_root = tmy_realpath_dentry(new_nd->dentry, new_nd->mnt);
+
+	if (!old_root || !new_root)
+		goto out;
+
+	old_root_dir.name = old_root;
+	tmy_fill_path_info(&old_root_dir);
+	new_root_dir.name = new_root;
+	tmy_fill_path_info(&new_root_dir);
+
+	if (old_root_dir.is_dir && new_root_dir.is_dir) {
+		struct pivot_root_entry *ptr;
+
+		for (ptr = pivot_root_list; ptr; ptr = ptr->next) {
+			if (ptr->is_deleted)
+				continue;
+			if (tmy_path_match(&old_root_dir, ptr->old_root) &&
+			    tmy_path_match(&new_root_dir, ptr->new_root)) {
+				error = 0;
+				break;
+			}
+		}
+	}
+
+out: ;
+
+	is_enforce = tmy_enforce(TMY_RESTRICT_PIVOT_ROOT);
+	tmy_audit_mount_log(!error, is_enforce, "pivot_root %s %s",
+			    new_root, old_root);
+
+	if (error) {
+		const char *exename = tmy_get_exe();
+
+		if (is_enforce &&
+		    tmy_supervisor("# %s is requesting\npivot_root %s %s\n",
+				   exename, new_root, old_root) == 0)
+			error = 0;
+
+		if (exename)
+			tmy_free(exename);
+
+		if (!is_enforce && tmy_accept(TMY_RESTRICT_PIVOT_ROOT, NULL)
+		    && old_root && new_root) {
+			tmy_add_pivot_root_acl(old_root, new_root, 0);
+			tmy_update_counter(TMY_UPDATE_SYSTEMPOLICY);
+		}
+
+		if (!is_enforce)
+			error = 0;
+	}
+
+	tmy_free(old_root);
+	tmy_free(new_root);
+	return error;
+}
+
+/**
+ * tmy_add_pivot_root_policy - add or delete pivot_root policy.
+ * @data:      a line to parse.
+ * @is_delete: is this delete request?
+ *
+ * Returns zero on success.
+ * Returns nonzero on failure.
+ */
+int tmy_add_pivot_root_policy(char *data, const u8 is_delete)
+{
+	char *cp = strchr(data, ' ');
+
+	if (!cp)
+		return -EINVAL;
+	*cp++ = '\0';
+
+	return tmy_add_pivot_root_acl(cp, data, is_delete);
+}
+
+/**
+ * tmy_read_pivot_root_policy - read pivot_root policy.
+ * @head: pointer to "struct io_buffer".
+ *
+ * Returns nonzero if reading incomplete.
+ * Returns zero otherwise.
+ */
+int tmy_read_pivot_root_policy(struct io_buffer *head)
+{
+	struct pivot_root_entry *ptr = head->read_var2;
+	if (!ptr)
+		ptr = pivot_root_list;
+
+	while (ptr) {
+		head->read_var2 = ptr;
+		if (ptr->is_deleted == 0 &&
+		    tmy_io_printf(head, TMY_ALLOW_PIVOT_ROOT "%s %s\n",
+				  ptr->new_root->name, ptr->old_root->name))
+			break;
+		ptr = ptr->next;
+	}
+
+	return ptr ? -ENOMEM : 0;
+}



  parent reply	other threads:[~2007-10-02  7:37 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-10-02  7:25 [TOMOYO 00/15](repost) TOMOYO Linux - MAC based on process invocation history Kentaro Takeda
2007-10-02  7:28 ` [TOMOYO 01/15](repost) Allow use of namespace_sem from LSM module Kentaro Takeda
2007-10-02  7:29 ` [TOMOYO 02/15](repost) Data structures and prototypes definition Kentaro Takeda
2007-10-02  7:30 ` [TOMOYO 03/15](repost) Memory and pathname management functions Kentaro Takeda
2007-10-03  7:39   ` James Morris
2007-10-03 11:12     ` Tetsuo Handa
2007-10-02  7:31 ` [TOMOYO 04/15](repost) Utility functions and securityfs interface for policy manipulation Kentaro Takeda
2007-10-02  8:05   ` Paul Mundt
2007-10-02 14:15     ` Greg KH
2007-10-02  7:32 ` [TOMOYO 05/15](repost) Domain transition handler functions Kentaro Takeda
2007-10-02 11:15   ` James Morris
2007-10-02 12:44     ` Tetsuo Handa
2007-10-02 13:00       ` YOSHIFUJI Hideaki / 吉藤英明
2007-10-02 13:07       ` James Morris
2007-10-02 14:50         ` Andi Kleen
2007-10-03 11:24         ` Tetsuo Handa
2007-10-03 11:43           ` YOSHIFUJI Hideaki / 吉藤英明
2007-10-03 12:37             ` James Morris
2007-10-03 13:04               ` Tetsuo Handa
2007-10-03 13:11                 ` YOSHIFUJI Hideaki / 吉藤英明
2007-10-03 13:14                 ` KaiGai Kohei
2007-10-03 13:59                   ` Tetsuo Handa
2007-10-03 14:07                     ` Peter Zijlstra
2007-10-03 14:26                       ` Tetsuo Handa
2007-10-03 14:26                         ` Peter Zijlstra
2007-10-03 14:32                         ` YOSHIFUJI Hideaki / 吉藤英明
2007-10-03 14:39                           ` James Morris
2007-10-03 14:56                           ` Tetsuo Handa
2007-10-04 12:57                             ` Tetsuo Handa
2007-10-03 14:37                         ` Jiri Kosina
2007-10-07 10:38                       ` Sleeping in RCU list traversal Tetsuo Handa
2007-10-03 13:24                 ` [TOMOYO 05/15](repost) Domain transition handler functions Peter Zijlstra
2007-10-03 14:19                   ` Tetsuo Handa
2007-10-03 14:28                     ` Peter Zijlstra
2007-10-15 11:46                       ` Tetsuo Handa
2007-10-03 14:35                     ` David P. Quigley
2007-10-15 12:09               ` Tetsuo Handa
2007-10-02  7:33 ` [TOMOYO 06/15](repost) Auditing interface Kentaro Takeda
2007-10-02  7:34 ` [TOMOYO 07/15](repost) File access control functions Kentaro Takeda
2007-10-02  7:35 ` [TOMOYO 08/15](repost) Argv[0] " Kentaro Takeda
2007-10-02  7:36 ` [TOMOYO 09/15](repost) Networking " Kentaro Takeda
2007-10-02  7:37 ` Kentaro Takeda [this message]
2007-10-02  7:37 ` [TOMOYO 11/15](repost) Signal transmission " Kentaro Takeda
2007-10-02  7:38 ` [TOMOYO 12/15](repost) LSM adapter for TOMOYO Kentaro Takeda
2007-10-02  7:39 ` [TOMOYO 13/15](repost) Conditional permission support Kentaro Takeda
2007-10-02  7:39 ` [TOMOYO 14/15](repost) LSM expansion for TOMOYO Linux Kentaro Takeda
2007-10-02 12:48   ` James Morris
2007-10-02 13:33     ` Tetsuo Handa
2007-10-02 14:36   ` James Morris
2007-10-02 21:49     ` Tetsuo Handa
2007-10-02  7:40 ` [TOMOYO 15/15](repost) Kconfig and Makefile " Kentaro Takeda
2007-10-02  7:42 ` [TOMOYO 00/15](repost) TOMOYO Linux - MAC based on process invocation history Kentaro Takeda
2007-10-02 10:37   ` James Morris
2007-10-02 10:58     ` Kentaro Takeda

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4701F521.4060204@nttdata.co.jp \
    --to=takedakn@nttdata.co.jp \
    --cc=chrisw@sous-sol.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox