All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kentaro Takeda <takedakn@nttdata.co.jp>
To: akpm@linux-foundation.org
Cc: linux-kernel@vger.kernel.org,
	linux-security-module@vger.kernel.org,
	Kentaro Takeda <takedakn@nttdata.co.jp>,
	Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Subject: [TOMOYO #6 retry 15/21] Namespace manipulation control functions.
Date: Wed, 09 Jan 2008 09:53:35 +0900	[thread overview]
Message-ID: <20080109005425.211182127@nttdata.co.jp> (raw)
In-Reply-To: 20080109005320.323184643@nttdata.co.jp

TOMOYO Linux checks mount permission based on
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 |  908 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 908 insertions(+)

--- /dev/null
+++ linux-2.6-mm/security/tomoyo/mount.c
@@ -0,0 +1,908 @@
+/*
+ * security/tomoyo/mount.c
+ *
+ * Mount access control functions for TOMOYO Linux.
+ */
+
+#include "tomoyo.h"
+#include "realpath.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 list_head list;
+	const struct path_info *dev_name;
+	const struct path_info *dir_name;
+	const struct path_info *fs_type;
+	unsigned int flags; /* Mount flags. */
+	bool is_deleted;
+};
+
+struct no_umount_entry {
+	struct list_head list;
+	const struct path_info *dir;
+	bool is_deleted;
+};
+
+/************************  MOUNT RESTRICTION HANDLER  ************************/
+
+static LIST_HEAD(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 bool 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 DEFINE_MUTEX(mutex);
+	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, 0, 0, 0, __FUNCTION__))
+		return -EINVAL;
+
+	dev = tmy_save_name(dev_name);
+	if (!dev)
+		return -ENOMEM;
+	dir = tmy_save_name(dir_name);
+	if (!dir)
+		return -ENOMEM;
+
+	mutex_lock(&mutex);
+
+	list_for_each_entry(ptr, &mount_list, list) {
+		if (ptr->flags != flags ||
+		    tmy_pathcmp(ptr->dev_name, dev) ||
+		    tmy_pathcmp(ptr->dir_name, dir) ||
+		    tmy_pathcmp(ptr->fs_type, fs))
+			continue;
+		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->dev_name = dev;
+	new_entry->dir_name = dir;
+	new_entry->fs_type = fs;
+	new_entry->flags = flags;
+	list_add_tail_mb(&new_entry->list, &mount_list);
+	error = 0;
+out: ;
+	mutex_unlock(&mutex);
+	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 profile,
+				       const unsigned int mode)
+{
+	int error = -EPERM;
+	const char *realname1 = tmy_realpath(dev_name);
+	const char *realname2 = tmy_realpath(dir_name);
+	const char *exename = tmy_get_exe();
+	const bool is_enforce = (mode == 3);
+
+	if (!strcmp(type, MOUNT_REMOUNT_KEYWORD)) {
+		tmy_audit(KERN_WARNING "TOMOYO-%s: mount -o remount %s 0x%lX "
+			  "(pid=%d:exe=%s): Permission denied.\n",
+			  tmy_getmsg(is_enforce),
+			  realname2 ? realname2 : dir_name, flags,
+			  current->pid, exename);
+		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(KERN_WARNING "TOMOYO-%s: mount %s %s %s 0x%lX "
+			  "(pid=%d:exe=%s): Permission denied.\n",
+			  tmy_getmsg(is_enforce), type,
+			  realname1 ? realname1 : dev_name,
+			  realname2 ? realname2 : dir_name,
+			  flags, current->pid, exename);
+		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(KERN_WARNING "TOMOYO-%s: mount %s %s 0x%lX "
+			  "(pid=%d:exe=%s): Permission denied.\n",
+			  tmy_getmsg(is_enforce), type,
+			  realname2 ? realname2 : dir_name,
+			  flags, current->pid, exename);
+		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(KERN_WARNING "TOMOYO-%s: mount -t %s %s %s 0x%lX "
+			  "(pid=%d:exe=%s): Permission denied.\n",
+			  tmy_getmsg(is_enforce), type,
+			  realname1 ? realname1 : dev_name,
+			  realname2 ? realname2 : dir_name,
+			  flags, current->pid, exename);
+		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 profile = TMY_SECURITY->domain->profile;
+	const unsigned int mode = tmy_flags(TMY_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:
+		tmy_audit(KERN_WARNING "TOMOYO-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:
+		tmy_audit(KERN_WARNING "TOMOYO-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 = 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;
+		}
+
+		list_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 (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(KERN_DEBUG "TOMOYO-NOTICE: "
+					  "'mount -t %s %s %s 0x%lX' "
+					  "accepted.\n",
+					  type, requested_dev_name,
+					  requested_dir_name, flags);
+			else if (need_dev < 0)
+				tmy_audit(KERN_DEBUG "TOMOYO-NOTICE: "
+					  "'mount %s %s %s 0x%lX' accepted.\n",
+					  type, requested_dev_name,
+					  requested_dir_name, flags);
+			else if (!strcmp(type, MOUNT_REMOUNT_KEYWORD))
+				tmy_audit(KERN_DEBUG "TOMOYO-NOTICE: "
+					  "'mount -o remount %s 0x%lX' "
+					  "accepted.\n",
+					  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(KERN_DEBUG "TOMOYO-NOTICE: "
+					  "'mount %s %s 0x%lX' accepted.\n",
+					  type, requested_dir_name, flags);
+			else
+				tmy_audit(KERN_DEBUG "TOMOYO-NOTICE: "
+					  "'mount %s on %s 0x%lX' accepted.\n",
+					  type, requested_dir_name, flags);
+			break;
+		}
+
+		if (error)
+			error = tmy_mount_perm_error(dev_name, dir_name, type,
+						     flags, profile, mode);
+
+		if (error && mode == 1) {
+			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 bool 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 list_head *pos;
+	list_for_each_cookie(pos, head->read_var2, &mount_list) {
+		struct mount_entry *ptr;
+		ptr = list_entry(pos, struct mount_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		if (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))
+			return -ENOMEM;
+	}
+	return 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->path.mnt->mnt_root == vfsmnt->mnt_root &&
+			    nd->path.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;
+	const unsigned int mode = tmy_flags(TMY_DENY_CONCEAL_MOUNT);
+	const bool is_enforce = (mode == 3);
+	struct mnt_namespace *namespace = current->nsproxy->mnt_ns;
+
+	if (!mode)
+		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;
+		}
+	}
+
+
+	if (flag) {
+		int error = -EPERM;
+		char *dir;
+		dir = tmy_realpath_dentry(nd->path.dentry, nd->path.mnt);
+		if (dir) {
+			const char *exename = tmy_get_exe();
+			tmy_audit("TOMOYO-%s: mount %s (pid=%d:exe=%s): "
+				  "Permission denied.\n",
+				  tmy_getmsg(is_enforce),
+				  dir, current->pid, exename);
+			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;
+	}
+
+	return 0;
+}
+
+/************************  UMOUNT RESTRICTION HANDLER  ************************/
+
+static LIST_HEAD(no_umount_list);
+
+/* Add or remove a no-unmount entry. */
+static int tmy_add_no_umount_acl(const char *dir, const bool is_delete)
+{
+	struct no_umount_entry *new_entry;
+	struct no_umount_entry *ptr;
+	const struct path_info *saved_dir;
+	static DEFINE_MUTEX(mutex);
+	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;
+
+	mutex_lock(&mutex);
+
+	list_for_each_entry(ptr, &no_umount_list, list) {
+		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;
+	list_add_tail_mb(&new_entry->list, &no_umount_list);
+	error = 0;
+out: ;
+
+	mutex_unlock(&mutex);
+
+	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 unsigned int mode = tmy_flags(TMY_RESTRICT_UMOUNT);
+	const bool is_enforce = (mode == 3);
+
+	if (!mode)
+		return 0;
+
+	dir0 = tmy_realpath_dentry(mnt->mnt_root, mnt);
+
+	if (dir0) {
+		struct no_umount_entry *ptr;
+		struct path_info dir;
+		bool found = 0;
+
+		dir.name = dir0;
+		tmy_fill_path_info(&dir);
+
+		list_for_each_entry(ptr, &no_umount_list, list) {
+			if (ptr->is_deleted)
+				continue;
+			if (tmy_path_match(&dir, ptr->dir)) {
+				found = 1;
+				break;
+			}
+		}
+
+		if (found) {
+			const char *exename = tmy_get_exe();
+			tmy_audit("TOMOYO-%s: umount %s (pid=%d:exe=%s): "
+				  "Permission denied.\n",
+				  tmy_getmsg(is_enforce),
+				  dir0, current->pid, exename);
+			if (is_enforce &&
+			    tmy_supervisor("# %s is requesting\nunmount %s\n",
+					   exename, dir0) == 0)
+				error = 0;
+
+			tmy_free(exename);
+		} else {
+			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 bool 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 list_head *pos;
+	list_for_each_cookie(pos, head->read_var2, &no_umount_list) {
+		struct no_umount_entry *ptr;
+		ptr = list_entry(pos, struct no_umount_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		if (tmy_io_printf(head, TMY_DENY_UNMOUNT "%s\n",
+				  ptr->dir->name))
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+/***** The structure for pivot_root restrictions. *****/
+
+struct pivot_root_entry {
+	struct list_head list;
+	const struct path_info *old_root;
+	const struct path_info *new_root;
+	bool is_deleted;
+};
+
+/**********************  PIVOT_ROOT RESTRICTION HANDLER  **********************/
+
+static LIST_HEAD(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 bool 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 DEFINE_MUTEX(mutex);
+	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;
+
+	mutex_lock(&mutex);
+
+	list_for_each_entry(ptr, &pivot_root_list, list) {
+		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;
+	list_add_tail_mb(&new_entry->list, &pivot_root_list);
+	error = 0;
+out: ;
+
+	mutex_unlock(&mutex);
+
+	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;
+	const unsigned int mode = tmy_flags(TMY_RESTRICT_PIVOT_ROOT);
+	const bool is_enforce = (mode == 3);
+	char *old_root;
+	char *new_root;
+	struct path_info old_root_dir;
+	struct path_info new_root_dir;
+
+	if (!mode)
+		return 0;
+
+	old_root = tmy_realpath_dentry(old_nd->path.dentry, old_nd->path.mnt);
+	new_root = tmy_realpath_dentry(new_nd->path.dentry, new_nd->path.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;
+		list_for_each_entry(ptr, &pivot_root_list, list) {
+			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: ;
+	if (error) {
+		const char *exename = tmy_get_exe();
+		tmy_audit("TOMOYO-%s: pivot_root %s %s (pid=%d:exe=%s): "
+			  "Permission denied.\n", tmy_getmsg(is_enforce),
+			  new_root, old_root, current->pid, exename);
+		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 && mode == 1 && 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 bool 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 list_head *pos;
+	list_for_each_cookie(pos, head->read_var2, &pivot_root_list) {
+		struct pivot_root_entry *ptr;
+		ptr = list_entry(pos, struct pivot_root_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		if (tmy_io_printf(head, TMY_ALLOW_PIVOT_ROOT "%s %s\n",
+				  ptr->new_root->name, ptr->old_root->name))
+			return -ENOMEM;
+	}
+	return 0;
+}

-- 

  parent reply	other threads:[~2008-01-09  1:01 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-01-09  0:53 [TOMOYO #6 retry 00/21] TOMOYO Linux - MAC based on process invocation history Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 01/21] TOMOYO Linux documentation Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 02/21] Add struct vfsmount to struct task_struct Kentaro Takeda
2008-01-15 21:16   ` Serge E. Hallyn
2008-01-16  0:22     ` Kentaro Takeda
2008-01-16 14:39       ` Serge E. Hallyn
2008-01-17  4:55         ` Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 03/21] Add wrapper functions for VFS helper functions Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 04/21] Replace VFS with wrapper functions Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 05/21] Add packet filtering based on processs security context Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 06/21] Data structures and prototype defitions Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 07/21] Memory and pathname management functions Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 08/21] Utility functions and policy manipulation interface Kentaro Takeda
2008-01-09  4:25   ` James Morris
2008-01-09  4:29     ` James Morris
2008-01-12  2:06       ` [TOMOYO #6 retry 08/21] Utility functions and policy manipulationinterface Tetsuo Handa
2008-01-12  3:06         ` James Morris
2008-01-12  4:45         ` Greg KH
2008-01-12  7:34           ` [TOMOYO #6 retry 08/21] Utility functions and policymanipulationinterface Tetsuo Handa
2008-01-09  4:31     ` [TOMOYO #6 retry 08/21] Utility functions and policy manipulation interface Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 09/21] Domain transition functions Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 10/21] Auditing interface Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 11/21] File access control functions Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 12/21] argv0 check functions Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 13/21] environment variable name " Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 14/21] Network access control functions Kentaro Takeda
2008-01-09  0:53 ` Kentaro Takeda [this message]
2008-01-09  0:53 ` [TOMOYO #6 retry 16/21] Signal " Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 17/21] Capability access " Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 18/21] LSM adapter functions Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 19/21] Conditional permission support Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 20/21] Kconfig and Makefile Kentaro Takeda
2008-01-09  0:53 ` [TOMOYO #6 retry 21/21] Add signal hooks at sleepable location 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=20080109005425.211182127@nttdata.co.jp \
    --to=takedakn@nttdata.co.jp \
    --cc=akpm@linux-foundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=penguin-kernel@I-love.SAKURA.ne.jp \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.