Commit 7f405ee4 authored by Seth Forshee's avatar Seth Forshee

loop: Permit priveleged operations within user namespaces

Priveleged operations should be allowed on loop devices within a
devloop mount by root within the user namespace which owns the
mount. Stash away the namespace at mount time and allow
CAP_SYS_ADMIN within this namespace to perform priveleged
operations on loop devices.
Signed-off-by: default avatarSeth Forshee <seth.forshee@canonical.com>
parent 1d5ab0e9
...@@ -86,6 +86,17 @@ static DEFINE_MUTEX(loop_index_mutex); ...@@ -86,6 +86,17 @@ static DEFINE_MUTEX(loop_index_mutex);
static int max_part; static int max_part;
static int part_shift; static int part_shift;
static bool lo_capable(struct loop_device *lo, int cap)
{
struct user_namespace *ns;
if (lo->loopfs_inode) {
ns = loopfs_user_ns(loopfs_sb_from_inode(lo->loopfs_inode));
if (ns)
return ns_capable(ns, cap);
}
return capable(cap);
}
static int transfer_xor(struct loop_device *lo, int cmd, static int transfer_xor(struct loop_device *lo, int cmd,
struct page *raw_page, unsigned raw_off, struct page *raw_page, unsigned raw_off,
struct page *loop_page, unsigned loop_off, struct page *loop_page, unsigned loop_off,
...@@ -925,7 +936,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) ...@@ -925,7 +936,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
if (lo->lo_encrypt_key_size && if (lo->lo_encrypt_key_size &&
!uid_eq(lo->lo_key_owner, uid) && !uid_eq(lo->lo_key_owner, uid) &&
!capable(CAP_SYS_ADMIN)) !lo_capable(lo, CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
if (lo->lo_state != Lo_bound) if (lo->lo_state != Lo_bound)
return -ENXIO; return -ENXIO;
...@@ -1015,7 +1026,8 @@ loop_get_status(struct loop_device *lo, struct loop_info64 *info) ...@@ -1015,7 +1026,8 @@ loop_get_status(struct loop_device *lo, struct loop_info64 *info)
memcpy(info->lo_crypt_name, lo->lo_crypt_name, LO_NAME_SIZE); memcpy(info->lo_crypt_name, lo->lo_crypt_name, LO_NAME_SIZE);
info->lo_encrypt_type = info->lo_encrypt_type =
lo->lo_encryption ? lo->lo_encryption->number : 0; lo->lo_encryption ? lo->lo_encryption->number : 0;
if (lo->lo_encrypt_key_size && capable(CAP_SYS_ADMIN)) { if (lo->lo_encrypt_key_size &&
lo_capable(lo, CAP_SYS_ADMIN)) {
info->lo_encrypt_key_size = lo->lo_encrypt_key_size; info->lo_encrypt_key_size = lo->lo_encrypt_key_size;
memcpy(info->lo_encrypt_key, lo->lo_encrypt_key, memcpy(info->lo_encrypt_key, lo->lo_encrypt_key,
lo->lo_encrypt_key_size); lo->lo_encrypt_key_size);
...@@ -1160,7 +1172,8 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -1160,7 +1172,8 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
break; break;
case LOOP_SET_STATUS: case LOOP_SET_STATUS:
err = -EPERM; err = -EPERM;
if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) if ((mode & FMODE_WRITE) ||
lo_capable(lo, CAP_SYS_ADMIN))
err = loop_set_status_old(lo, err = loop_set_status_old(lo,
(struct loop_info __user *)arg); (struct loop_info __user *)arg);
break; break;
...@@ -1169,7 +1182,8 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -1169,7 +1182,8 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
break; break;
case LOOP_SET_STATUS64: case LOOP_SET_STATUS64:
err = -EPERM; err = -EPERM;
if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) if ((mode & FMODE_WRITE) ||
lo_capable(lo, CAP_SYS_ADMIN))
err = loop_set_status64(lo, err = loop_set_status64(lo,
(struct loop_info64 __user *) arg); (struct loop_info64 __user *) arg);
break; break;
...@@ -1178,7 +1192,8 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -1178,7 +1192,8 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
break; break;
case LOOP_SET_CAPACITY: case LOOP_SET_CAPACITY:
err = -EPERM; err = -EPERM;
if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) if ((mode & FMODE_WRITE) ||
lo_capable(lo, CAP_SYS_ADMIN))
err = loop_set_capacity(lo, bdev); err = loop_set_capacity(lo, bdev);
break; break;
default: default:
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/user_namespace.h>
#include <uapi/linux/loop.h> #include <uapi/linux/loop.h>
/* Possible states of device */ /* Possible states of device */
...@@ -94,6 +95,7 @@ int loop_unregister_transfer(int number); ...@@ -94,6 +95,7 @@ int loop_unregister_transfer(int number);
/* loopfs psuedo filesystem support */ /* loopfs psuedo filesystem support */
struct super_block *loopfs_sb_from_inode(struct inode *inode); struct super_block *loopfs_sb_from_inode(struct inode *inode);
struct user_namespace *loopfs_user_ns(struct super_block *sb);
struct inode *loopfs_new_dev(struct inode *ref_inode, dev_t device, struct inode *loopfs_new_dev(struct inode *ref_inode, dev_t device,
int lo_number); int lo_number);
void loopfs_kill_dev(struct inode *inode); void loopfs_kill_dev(struct inode *inode);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/parser.h> #include <linux/parser.h>
#include <linux/fsnotify.h> #include <linux/fsnotify.h>
#include <linux/user_namespace.h>
#include "loop.h" #include "loop.h"
static struct vfsmount *loopfs_mnt; static struct vfsmount *loopfs_mnt;
...@@ -31,6 +32,7 @@ struct loop_mount_opts { ...@@ -31,6 +32,7 @@ struct loop_mount_opts {
struct loop_fs_info { struct loop_fs_info {
struct dentry *control_dentry; struct dentry *control_dentry;
struct loop_mount_opts opts; struct loop_mount_opts opts;
struct user_namespace *user_ns;
kuid_t root_uid; kuid_t root_uid;
kgid_t root_gid; kgid_t root_gid;
}; };
...@@ -57,6 +59,13 @@ struct super_block *loopfs_sb_from_inode(struct inode *inode) ...@@ -57,6 +59,13 @@ struct super_block *loopfs_sb_from_inode(struct inode *inode)
return loopfs_mnt->mnt_sb; return loopfs_mnt->mnt_sb;
} }
struct user_namespace *loopfs_user_ns(struct super_block *sb)
{
if (!sb)
return NULL;
return LOOPFS_SB(sb)->user_ns;
}
static int mknod_loop_control(struct super_block *sb) static int mknod_loop_control(struct super_block *sb)
{ {
int ret = 0; int ret = 0;
...@@ -147,6 +156,7 @@ static int loopfs_fill_super(struct super_block *s, void *data, int silent) ...@@ -147,6 +156,7 @@ static int loopfs_fill_super(struct super_block *s, void *data, int silent)
return -ENOMEM; return -ENOMEM;
s->s_fs_info = fsi; s->s_fs_info = fsi;
fsi->user_ns = get_user_ns(current_user_ns());
fsi->root_uid = make_kuid(current_user_ns(), 0); fsi->root_uid = make_kuid(current_user_ns(), 0);
if (!uid_valid(fsi->root_uid)) if (!uid_valid(fsi->root_uid))
...@@ -248,6 +258,7 @@ static void loopfs_kill_sb(struct super_block *sb) ...@@ -248,6 +258,7 @@ static void loopfs_kill_sb(struct super_block *sb)
loop_release_device(inode); loop_release_device(inode);
} }
put_user_ns(LOOPFS_SB(sb)->user_ns);
kfree(LOOPFS_SB(sb)); kfree(LOOPFS_SB(sb));
kill_litter_super(sb); kill_litter_super(sb);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment