[patch 06/10] unprivileged mounts: allow unprivileged mounts

2008-02-05 Thread Miklos Szeredi
From: Miklos Szeredi <[EMAIL PROTECTED]>

For "safe" filesystems allow unprivileged mounting and forced
unmounting.

A filesystem type is considered "safe", if mounting it by an
unprivileged user may not cause a security problem.  This is somewhat
subjective, so setting this property is left to userspace (implemented
in the next patch).

Since most filesystems haven't been designed with unprivileged
mounting in mind, a thorough audit is recommended before setting this
property.

Make this a separate integer member in 'struct file_system_type'
instead of a flag, since that is easier to handle by sysctl code.

Move subtype handling from do_kern_mount() into do_new_mount().  All
other callers are kernel-internal and do not need subtype support.

Signed-off-by: Miklos Szeredi <[EMAIL PROTECTED]>
Acked-by: Serge Hallyn <[EMAIL PROTECTED]>
---

Index: linux/fs/namespace.c
===
--- linux.orig/fs/namespace.c   2008-02-04 23:48:00.0 +0100
+++ linux/fs/namespace.c2008-02-04 23:48:02.0 +0100
@@ -1105,14 +1105,16 @@ static bool is_mount_owner(struct vfsmou
 /*
  * umount is permitted for
  *  - sysadmin
- *  - mount owner, if not forced umount
+ *  - mount owner
+ *o if not forced umount,
+ *o if forced umount, and filesystem is "safe"
  */
 static bool permit_umount(struct vfsmount *mnt, int flags)
 {
if (capable(CAP_SYS_ADMIN))
return true;
 
-   if (flags & MNT_FORCE)
+   if ((flags & MNT_FORCE) && !(mnt->mnt_sb->s_type->fs_safe))
return false;
 
return is_mount_owner(mnt, current->fsuid);
@@ -1170,13 +1172,17 @@ asmlinkage long sys_oldumount(char __use
  * - mountpoint is not a symlink
  * - mountpoint is in a mount owned by the user
  */
-static bool permit_mount(struct nameidata *nd, int *flags)
+static bool permit_mount(struct nameidata *nd, struct file_system_type *type,
+int *flags)
 {
struct inode *inode = nd->path.dentry->d_inode;
 
if (capable(CAP_SYS_ADMIN))
return true;
 
+   if (type && !type->fs_safe)
+   return false;
+
if (S_ISLNK(inode->i_mode))
return false;
 
@@ -1430,7 +1436,7 @@ static int do_loopback(struct nameidata 
struct vfsmount *mnt = NULL;
int err;
 
-   if (!permit_mount(nd, ))
+   if (!permit_mount(nd, NULL, ))
return -EPERM;
if (!old_name || !*old_name)
return -EINVAL;
@@ -1611,30 +1617,76 @@ out:
return err;
 }
 
+static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char 
*fstype)
+{
+   int err;
+   const char *subtype = strchr(fstype, '.');
+   if (subtype) {
+   subtype++;
+   err = -EINVAL;
+   if (!subtype[0])
+   goto err;
+   } else
+   subtype = "";
+
+   mnt->mnt_sb->s_subtype = kstrdup(subtype, GFP_KERNEL);
+   err = -ENOMEM;
+   if (!mnt->mnt_sb->s_subtype)
+   goto err;
+   return mnt;
+
+ err:
+   mntput(mnt);
+   return ERR_PTR(err);
+}
+
 /*
  * create a new mount for userspace and request it to be added into the
  * namespace's tree
  */
-static int do_new_mount(struct nameidata *nd, char *type, int flags,
+static int do_new_mount(struct nameidata *nd, char *fstype, int flags,
int mnt_flags, char *name, void *data)
 {
+   int err;
struct vfsmount *mnt;
+   struct file_system_type *type;
 
-   if (!type || !memchr(type, 0, PAGE_SIZE))
+   if (!fstype || !memchr(fstype, 0, PAGE_SIZE))
return -EINVAL;
 
-   /* we need capabilities... */
-   if (!capable(CAP_SYS_ADMIN))
-   return -EPERM;
-
-   mnt = do_kern_mount(type, flags & ~MS_SETUSER, name, data);
-   if (IS_ERR(mnt))
+   type = get_fs_type(fstype);
+   if (!type)
+   return -ENODEV;
+
+   err = -EPERM;
+   if (!permit_mount(nd, type, ))
+   goto out_put_filesystem;
+
+   if (flags & MS_SETUSER) {
+   err = reserve_user_mount();
+   if (err)
+   goto out_put_filesystem;
+   }
+
+   mnt = vfs_kern_mount(type, flags & ~MS_SETUSER, name, data);
+   if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
+   !mnt->mnt_sb->s_subtype)
+   mnt = fs_set_subtype(mnt, fstype);
+   put_filesystem(type);
+   if (IS_ERR(mnt)) {
+   if (flags & MS_SETUSER)
+   dec_nr_user_mounts();
return PTR_ERR(mnt);
+   }
 
if (flags & MS_SETUSER)
-   set_mnt_user(mnt);
+   __set_mnt_user(mnt);
 
return do_add_mount(mnt, nd, mnt_flags, NULL);
+
+ out_put_filesystem:
+   put_filesystem(type);
+   return err;
 }
 
 /*
@@ -1665,7 +1717,7 @@ int do_add_mount(struct vfsmount *newmnt
  

[patch 06/10] unprivileged mounts: allow unprivileged mounts

2008-02-05 Thread Miklos Szeredi
From: Miklos Szeredi [EMAIL PROTECTED]

For safe filesystems allow unprivileged mounting and forced
unmounting.

A filesystem type is considered safe, if mounting it by an
unprivileged user may not cause a security problem.  This is somewhat
subjective, so setting this property is left to userspace (implemented
in the next patch).

Since most filesystems haven't been designed with unprivileged
mounting in mind, a thorough audit is recommended before setting this
property.

Make this a separate integer member in 'struct file_system_type'
instead of a flag, since that is easier to handle by sysctl code.

Move subtype handling from do_kern_mount() into do_new_mount().  All
other callers are kernel-internal and do not need subtype support.

Signed-off-by: Miklos Szeredi [EMAIL PROTECTED]
Acked-by: Serge Hallyn [EMAIL PROTECTED]
---

Index: linux/fs/namespace.c
===
--- linux.orig/fs/namespace.c   2008-02-04 23:48:00.0 +0100
+++ linux/fs/namespace.c2008-02-04 23:48:02.0 +0100
@@ -1105,14 +1105,16 @@ static bool is_mount_owner(struct vfsmou
 /*
  * umount is permitted for
  *  - sysadmin
- *  - mount owner, if not forced umount
+ *  - mount owner
+ *o if not forced umount,
+ *o if forced umount, and filesystem is safe
  */
 static bool permit_umount(struct vfsmount *mnt, int flags)
 {
if (capable(CAP_SYS_ADMIN))
return true;
 
-   if (flags  MNT_FORCE)
+   if ((flags  MNT_FORCE)  !(mnt-mnt_sb-s_type-fs_safe))
return false;
 
return is_mount_owner(mnt, current-fsuid);
@@ -1170,13 +1172,17 @@ asmlinkage long sys_oldumount(char __use
  * - mountpoint is not a symlink
  * - mountpoint is in a mount owned by the user
  */
-static bool permit_mount(struct nameidata *nd, int *flags)
+static bool permit_mount(struct nameidata *nd, struct file_system_type *type,
+int *flags)
 {
struct inode *inode = nd-path.dentry-d_inode;
 
if (capable(CAP_SYS_ADMIN))
return true;
 
+   if (type  !type-fs_safe)
+   return false;
+
if (S_ISLNK(inode-i_mode))
return false;
 
@@ -1430,7 +1436,7 @@ static int do_loopback(struct nameidata 
struct vfsmount *mnt = NULL;
int err;
 
-   if (!permit_mount(nd, flags))
+   if (!permit_mount(nd, NULL, flags))
return -EPERM;
if (!old_name || !*old_name)
return -EINVAL;
@@ -1611,30 +1617,76 @@ out:
return err;
 }
 
+static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char 
*fstype)
+{
+   int err;
+   const char *subtype = strchr(fstype, '.');
+   if (subtype) {
+   subtype++;
+   err = -EINVAL;
+   if (!subtype[0])
+   goto err;
+   } else
+   subtype = ;
+
+   mnt-mnt_sb-s_subtype = kstrdup(subtype, GFP_KERNEL);
+   err = -ENOMEM;
+   if (!mnt-mnt_sb-s_subtype)
+   goto err;
+   return mnt;
+
+ err:
+   mntput(mnt);
+   return ERR_PTR(err);
+}
+
 /*
  * create a new mount for userspace and request it to be added into the
  * namespace's tree
  */
-static int do_new_mount(struct nameidata *nd, char *type, int flags,
+static int do_new_mount(struct nameidata *nd, char *fstype, int flags,
int mnt_flags, char *name, void *data)
 {
+   int err;
struct vfsmount *mnt;
+   struct file_system_type *type;
 
-   if (!type || !memchr(type, 0, PAGE_SIZE))
+   if (!fstype || !memchr(fstype, 0, PAGE_SIZE))
return -EINVAL;
 
-   /* we need capabilities... */
-   if (!capable(CAP_SYS_ADMIN))
-   return -EPERM;
-
-   mnt = do_kern_mount(type, flags  ~MS_SETUSER, name, data);
-   if (IS_ERR(mnt))
+   type = get_fs_type(fstype);
+   if (!type)
+   return -ENODEV;
+
+   err = -EPERM;
+   if (!permit_mount(nd, type, flags))
+   goto out_put_filesystem;
+
+   if (flags  MS_SETUSER) {
+   err = reserve_user_mount();
+   if (err)
+   goto out_put_filesystem;
+   }
+
+   mnt = vfs_kern_mount(type, flags  ~MS_SETUSER, name, data);
+   if (!IS_ERR(mnt)  (type-fs_flags  FS_HAS_SUBTYPE) 
+   !mnt-mnt_sb-s_subtype)
+   mnt = fs_set_subtype(mnt, fstype);
+   put_filesystem(type);
+   if (IS_ERR(mnt)) {
+   if (flags  MS_SETUSER)
+   dec_nr_user_mounts();
return PTR_ERR(mnt);
+   }
 
if (flags  MS_SETUSER)
-   set_mnt_user(mnt);
+   __set_mnt_user(mnt);
 
return do_add_mount(mnt, nd, mnt_flags, NULL);
+
+ out_put_filesystem:
+   put_filesystem(type);
+   return err;
 }
 
 /*
@@ -1665,7 +1717,7 @@ int do_add_mount(struct vfsmount *newmnt
if 

[patch 06/10] unprivileged mounts: allow unprivileged mounts

2008-01-16 Thread Miklos Szeredi
From: Miklos Szeredi <[EMAIL PROTECTED]>

For "safe" filesystems allow unprivileged mounting and forced
unmounting.

A filesystem type is considered "safe", if mounting it by an
unprivileged user may not cause a security problem.  This is somewhat
subjective, so setting this property is left to userspace (implemented
in the next patch).

Since most filesystems haven't been designed with unprivileged
mounting in mind, a thorough audit is recommended before setting this
property.

Make this a separate integer member in 'struct file_system_type'
instead of a flag, since that is easier to handle by sysctl code.

Move subtype handling from do_kern_mount() into do_new_mount().  All
other callers are kernel-internal and do not need subtype support.

Signed-off-by: Miklos Szeredi <[EMAIL PROTECTED]>
Acked-by: Serge Hallyn <[EMAIL PROTECTED]>
---

Index: linux/fs/namespace.c
===
--- linux.orig/fs/namespace.c   2008-01-16 13:25:08.0 +0100
+++ linux/fs/namespace.c2008-01-16 13:25:09.0 +0100
@@ -966,14 +966,16 @@ static bool is_mount_owner(struct vfsmou
 /*
  * umount is permitted for
  *  - sysadmin
- *  - mount owner, if not forced umount
+ *  - mount owner
+ *o if not forced umount,
+ *o if forced umount, and filesystem is "safe"
  */
 static bool permit_umount(struct vfsmount *mnt, int flags)
 {
if (capable(CAP_SYS_ADMIN))
return true;
 
-   if (flags & MNT_FORCE)
+   if ((flags & MNT_FORCE) && !(mnt->mnt_sb->s_type->fs_safe))
return false;
 
return is_mount_owner(mnt, current->fsuid);
@@ -1031,13 +1033,17 @@ asmlinkage long sys_oldumount(char __use
  * - mountpoint is not a symlink
  * - mountpoint is in a mount owned by the user
  */
-static bool permit_mount(struct nameidata *nd, int *flags)
+static bool permit_mount(struct nameidata *nd, struct file_system_type *type,
+int *flags)
 {
struct inode *inode = nd->path.dentry->d_inode;
 
if (capable(CAP_SYS_ADMIN))
return true;
 
+   if (type && !type->fs_safe)
+   return false;
+
if (S_ISLNK(inode->i_mode))
return false;
 
@@ -1291,7 +1297,7 @@ static int do_loopback(struct nameidata 
struct vfsmount *mnt = NULL;
int err;
 
-   if (!permit_mount(nd, ))
+   if (!permit_mount(nd, NULL, ))
return -EPERM;
if (!old_name || !*old_name)
return -EINVAL;
@@ -1472,30 +1478,76 @@ out:
return err;
 }
 
+static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char 
*fstype)
+{
+   int err;
+   const char *subtype = strchr(fstype, '.');
+   if (subtype) {
+   subtype++;
+   err = -EINVAL;
+   if (!subtype[0])
+   goto err;
+   } else
+   subtype = "";
+
+   mnt->mnt_sb->s_subtype = kstrdup(subtype, GFP_KERNEL);
+   err = -ENOMEM;
+   if (!mnt->mnt_sb->s_subtype)
+   goto err;
+   return mnt;
+
+ err:
+   mntput(mnt);
+   return ERR_PTR(err);
+}
+
 /*
  * create a new mount for userspace and request it to be added into the
  * namespace's tree
  */
-static int do_new_mount(struct nameidata *nd, char *type, int flags,
+static int do_new_mount(struct nameidata *nd, char *fstype, int flags,
int mnt_flags, char *name, void *data)
 {
+   int err;
struct vfsmount *mnt;
+   struct file_system_type *type;
 
-   if (!type || !memchr(type, 0, PAGE_SIZE))
+   if (!fstype || !memchr(fstype, 0, PAGE_SIZE))
return -EINVAL;
 
-   /* we need capabilities... */
-   if (!capable(CAP_SYS_ADMIN))
-   return -EPERM;
-
-   mnt = do_kern_mount(type, flags & ~MS_SETUSER, name, data);
-   if (IS_ERR(mnt))
+   type = get_fs_type(fstype);
+   if (!type)
+   return -ENODEV;
+
+   err = -EPERM;
+   if (!permit_mount(nd, type, ))
+   goto out_put_filesystem;
+
+   if (flags & MS_SETUSER) {
+   err = reserve_user_mount();
+   if (err)
+   goto out_put_filesystem;
+   }
+
+   mnt = vfs_kern_mount(type, flags & ~MS_SETUSER, name, data);
+   if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
+   !mnt->mnt_sb->s_subtype)
+   mnt = fs_set_subtype(mnt, fstype);
+   put_filesystem(type);
+   if (IS_ERR(mnt)) {
+   if (flags & MS_SETUSER)
+   dec_nr_user_mounts();
return PTR_ERR(mnt);
+   }
 
if (flags & MS_SETUSER)
-   set_mnt_user(mnt);
+   __set_mnt_user(mnt);
 
return do_add_mount(mnt, nd, mnt_flags, NULL);
+
+ out_put_filesystem:
+   put_filesystem(type);
+   return err;
 }
 
 /*
@@ -1526,7 +1578,7 @@ int do_add_mount(struct vfsmount *newmnt

[patch 06/10] unprivileged mounts: allow unprivileged mounts

2008-01-16 Thread Miklos Szeredi
From: Miklos Szeredi [EMAIL PROTECTED]

For safe filesystems allow unprivileged mounting and forced
unmounting.

A filesystem type is considered safe, if mounting it by an
unprivileged user may not cause a security problem.  This is somewhat
subjective, so setting this property is left to userspace (implemented
in the next patch).

Since most filesystems haven't been designed with unprivileged
mounting in mind, a thorough audit is recommended before setting this
property.

Make this a separate integer member in 'struct file_system_type'
instead of a flag, since that is easier to handle by sysctl code.

Move subtype handling from do_kern_mount() into do_new_mount().  All
other callers are kernel-internal and do not need subtype support.

Signed-off-by: Miklos Szeredi [EMAIL PROTECTED]
Acked-by: Serge Hallyn [EMAIL PROTECTED]
---

Index: linux/fs/namespace.c
===
--- linux.orig/fs/namespace.c   2008-01-16 13:25:08.0 +0100
+++ linux/fs/namespace.c2008-01-16 13:25:09.0 +0100
@@ -966,14 +966,16 @@ static bool is_mount_owner(struct vfsmou
 /*
  * umount is permitted for
  *  - sysadmin
- *  - mount owner, if not forced umount
+ *  - mount owner
+ *o if not forced umount,
+ *o if forced umount, and filesystem is safe
  */
 static bool permit_umount(struct vfsmount *mnt, int flags)
 {
if (capable(CAP_SYS_ADMIN))
return true;
 
-   if (flags  MNT_FORCE)
+   if ((flags  MNT_FORCE)  !(mnt-mnt_sb-s_type-fs_safe))
return false;
 
return is_mount_owner(mnt, current-fsuid);
@@ -1031,13 +1033,17 @@ asmlinkage long sys_oldumount(char __use
  * - mountpoint is not a symlink
  * - mountpoint is in a mount owned by the user
  */
-static bool permit_mount(struct nameidata *nd, int *flags)
+static bool permit_mount(struct nameidata *nd, struct file_system_type *type,
+int *flags)
 {
struct inode *inode = nd-path.dentry-d_inode;
 
if (capable(CAP_SYS_ADMIN))
return true;
 
+   if (type  !type-fs_safe)
+   return false;
+
if (S_ISLNK(inode-i_mode))
return false;
 
@@ -1291,7 +1297,7 @@ static int do_loopback(struct nameidata 
struct vfsmount *mnt = NULL;
int err;
 
-   if (!permit_mount(nd, flags))
+   if (!permit_mount(nd, NULL, flags))
return -EPERM;
if (!old_name || !*old_name)
return -EINVAL;
@@ -1472,30 +1478,76 @@ out:
return err;
 }
 
+static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char 
*fstype)
+{
+   int err;
+   const char *subtype = strchr(fstype, '.');
+   if (subtype) {
+   subtype++;
+   err = -EINVAL;
+   if (!subtype[0])
+   goto err;
+   } else
+   subtype = ;
+
+   mnt-mnt_sb-s_subtype = kstrdup(subtype, GFP_KERNEL);
+   err = -ENOMEM;
+   if (!mnt-mnt_sb-s_subtype)
+   goto err;
+   return mnt;
+
+ err:
+   mntput(mnt);
+   return ERR_PTR(err);
+}
+
 /*
  * create a new mount for userspace and request it to be added into the
  * namespace's tree
  */
-static int do_new_mount(struct nameidata *nd, char *type, int flags,
+static int do_new_mount(struct nameidata *nd, char *fstype, int flags,
int mnt_flags, char *name, void *data)
 {
+   int err;
struct vfsmount *mnt;
+   struct file_system_type *type;
 
-   if (!type || !memchr(type, 0, PAGE_SIZE))
+   if (!fstype || !memchr(fstype, 0, PAGE_SIZE))
return -EINVAL;
 
-   /* we need capabilities... */
-   if (!capable(CAP_SYS_ADMIN))
-   return -EPERM;
-
-   mnt = do_kern_mount(type, flags  ~MS_SETUSER, name, data);
-   if (IS_ERR(mnt))
+   type = get_fs_type(fstype);
+   if (!type)
+   return -ENODEV;
+
+   err = -EPERM;
+   if (!permit_mount(nd, type, flags))
+   goto out_put_filesystem;
+
+   if (flags  MS_SETUSER) {
+   err = reserve_user_mount();
+   if (err)
+   goto out_put_filesystem;
+   }
+
+   mnt = vfs_kern_mount(type, flags  ~MS_SETUSER, name, data);
+   if (!IS_ERR(mnt)  (type-fs_flags  FS_HAS_SUBTYPE) 
+   !mnt-mnt_sb-s_subtype)
+   mnt = fs_set_subtype(mnt, fstype);
+   put_filesystem(type);
+   if (IS_ERR(mnt)) {
+   if (flags  MS_SETUSER)
+   dec_nr_user_mounts();
return PTR_ERR(mnt);
+   }
 
if (flags  MS_SETUSER)
-   set_mnt_user(mnt);
+   __set_mnt_user(mnt);
 
return do_add_mount(mnt, nd, mnt_flags, NULL);
+
+ out_put_filesystem:
+   put_filesystem(type);
+   return err;
 }
 
 /*
@@ -1526,7 +1578,7 @@ int do_add_mount(struct vfsmount *newmnt
if