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 <[EMAIL PROTECTED]>
Signed-off-by: Tetsuo Handa <[EMAIL PROTECTED]>
 security/tomoyo/mount.c |  908 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 908 insertions(+)

--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6-mm/security/tomoyo/mount.c        2007-11-14 15:59:44.000000000 
+0900
@@ -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, 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;
+
+       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;
+}

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

Reply via email to