+ r = dm_check_dev_permission(hc->md, 1, NULL);
+ if (r) {
+ dm_put(hc->md);
+ up_write(&_hash_lock);
+ kfree(new_data);
+ return ERR_PTR(-EACCES);
+ }
+
/*
* Does this device already have a uuid?
*/
@@ -434,6 +443,41 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
return md;
}
+/*
+ * non-root submited ioctl helper function
+ */
+static int initial_permission_check(unsigned int cmd)
+{
+ if (capable(CAP_SYS_ADMIN))
+ return 0;
+
+ /* FIXME: This is only an example */
+ switch(_IOC_NR(cmd)) {
+ case DM_REMOVE_ALL_CMD:
+ case DM_DEV_SET_GEOMETRY_CMD:
+ DMDEBUG("non-root user cant't call this ioctl: 0x%x", cmd);
+ return 1;
+ }
+ return 0;
+}
+
+static int deny_create(void)
+{
+ if (capable(CAP_SYS_ADMIN))
+ return 0;
+
+ /*
+ * FIXME: Add some function to limit user to create
+ * only reasonable number of DM_DEVS
+ *
+ * sugestions:
+ * a) only limited number of devices per second
+ * b) limited number in total per specific user
+ * c) limited number in total per non-root user
+ */
+ return 0;
+}
+
/*-----------------------------------------------------------------
* Implementation of the ioctl commands
*---------------------------------------------------------------*/
@@ -492,9 +536,12 @@ static int list_devices(struct dm_ioctl *param, size_t param_size)
*/
for (i = 0; i < NUM_BUCKETS; i++) {
list_for_each_entry (hc, _name_buckets + i, name_list) {
- needed += sizeof(struct dm_name_list);
- needed += strlen(hc->name) + 1;
- needed += ALIGN_MASK;
+ BUG_ON(!hc->md);
+ if (!dm_check_dev_permission(hc->md, 0, NULL)) {
+ needed += sizeof(struct dm_name_list);
+ needed += strlen(hc->name) + 1;
+ needed += ALIGN_MASK;
+ }
}
}
@@ -704,6 +754,9 @@ static int dev_create(struct dm_ioctl *param, size_t param_size)
int r, m = DM_ANY_MINOR;
struct mapped_device *md;
+ if (deny_create())
+ return -EACCES;
+
r = check_name(param->name);
if (r)
return r;
@@ -808,6 +861,12 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
md = hc->md;
+ if (dm_check_dev_permission(md, 1, NULL)) {
+ up_write(&_hash_lock);
+ dm_put(md);
+ return -EACCES;
+ }
+
/*
* Ensure the device is not open and nothing further can open it.
*/
@@ -930,6 +989,11 @@ static int do_suspend(struct dm_ioctl *param)
if (!md)
return -ENXIO;
+ if (dm_check_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
if (param->flags & DM_SKIP_LOCKFS_FLAG)
suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
if (param->flags & DM_NOFLUSH_FLAG)
@@ -968,6 +1032,11 @@ static int do_resume(struct dm_ioctl *param)
/*
+ * okozina: permissions
+ */
+
+/* FIXME: just simple test */
+/*
+ * Supposed to call during device initialization.
+ * Otherwise inode counter should be incremented.
+ */
+static int set_bdev_owner(struct block_device *bdev)
+{
+ int r = 0;
+ struct inode *inode;
+
+ /*
+ * ATTR_FORCE is compulsory. otherwise perm dennied for
+ * user w/o CAP_FOWNER.
+ *
+ * Note that user that has no right to create dm device
+ * needs to be stopped before ioct() syscall on control
+ * nod.
+ */
+ struct iattr attr = {
+ .ia_valid = ATTR_UID | ATTR_GID | ATTR_FORCE,
+ .ia_uid = current_fsuid(),
+ .ia_gid = current_fsgid()
+ };
+
+ inode = bdev->bd_inode;
+
+ mutex_lock(&inode->i_mutex);
+
+ /* for info only */
+ BUG_ON(!inode->i_sb);
+ BUG_ON(!inode->i_sb->s_root);
+
+ r = inode_change_ok(inode, &attr);
+ if (r)
+ goto out;
+
+ setattr_copy(inode, &attr);
+ mark_inode_dirty(inode);
+
+out:
+ mutex_unlock(&inode->i_mutex);
+ return r;
+}
+
+/*
+ * This is only suggestion how to define ownership
+ * of block device in kernel
+ */
+int dm_check_dev_permission(struct mapped_device *md, int warn, void *attr __attribute__((unused)))
+{
+ int r;
+
+ if (!md)
+ return -EINVAL;
+
+ BUG_ON(!md->bdev);
+ BUG_ON(!md->bdev->bd_inode);
+
+ r = !inode_owner_or_capable(md->bdev->bd_inode);
+
+ if (r && warn)
+ DMWARN("uid: %d, git: %d is not owner (or capable) of the "
+ "device %s", current_uid(), current_gid(),
+ dm_device_name(md));
+
+ return r;
+}
+
+/*
* Block device functions
*/
int dm_deleting_md(struct mapped_device *md)
@@ -421,6 +493,11 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
if (!map || !dm_table_get_size(map))
goto out;
+ if (!capable(CAP_SYS_ADMIN)) {
+ r = -EACCES;
+ goto out;
+ }
+
/* We only support devices that have a single target */
if (dm_table_get_num_targets(map) != 1)
goto out;
@@ -1884,6 +1961,15 @@ static struct mapped_device *alloc_dev(int minor)
if (!md->bdev)
goto bad_bdev;
+ /* It's just a test. This should be
+ * in block layer */
+ if (set_bdev_owner(md->bdev)) {
+ DMDEBUG("set device ownership failed");
+ goto bad_bdev;
+ }
+ DMDEBUG("Device %s has got owner uid: %d, gid %d",
+ md->name, md->bdev->bd_inode->i_uid, md->bdev->bd_inode->i_gid);
+
bio_init(&md->flush_bio);
md->flush_bio.bi_bdev = md->bdev;
md->flush_bio.bi_rw = WRITE_FLUSH;
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index b7dacd5..93d8869 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -15,6 +15,8 @@
#include <linux/list.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+ r = dm_check_dm_dev_permission(hc->md, 1, NULL);
+ if (r) {
+ dm_put(hc->md);
+ up_write(&_hash_lock);
+ kfree(new_data);
+ return ERR_PTR(-EACCES);
+ }
+
/*
* Does this device already have a uuid?
*/
@@ -434,6 +443,44 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
return md;
}
+/*
+ * non-root submited ioctl helper function
+ */
+static int initial_permission_check(unsigned int cmd)
+{
+ if (capable(CAP_SYS_ADMIN))
+ return 0;
+
+ if (!sec_enabled)
+ return 1;
+
+ /* FIXME: This is only an example */
+ switch(_IOC_NR(cmd)) {
+ case DM_REMOVE_ALL_CMD:
+ case DM_DEV_SET_GEOMETRY_CMD:
+ DMDEBUG("non-root user cant't call this ioctl: 0x%x", cmd);
+ return 1;
+ }
+ return 0;
+}
+
+static int deny_create(void)
+{
+ if (capable(CAP_SYS_ADMIN))
+ return 0;
+
+ /*
+ * FIXME: Add some function to limit user to create
+ * only reasonable number of DM_DEVS
+ *
+ * sugestions:
+ * a) only limited number of devices per second
+ * b) limited number in total per specific user
+ * c) limited number in total per non-root user
+ */
+ return 0;
+}
+
/*-----------------------------------------------------------------
* Implementation of the ioctl commands
*---------------------------------------------------------------*/
@@ -492,9 +539,12 @@ static int list_devices(struct dm_ioctl *param, size_t param_size)
*/
for (i = 0; i < NUM_BUCKETS; i++) {
list_for_each_entry (hc, _name_buckets + i, name_list) {
- needed += sizeof(struct dm_name_list);
- needed += strlen(hc->name) + 1;
- needed += ALIGN_MASK;
+ BUG_ON(!hc->md);
+ if (!dm_check_dm_dev_permission(hc->md, 0, NULL)) {
+ needed += sizeof(struct dm_name_list);
+ needed += strlen(hc->name) + 1;
+ needed += ALIGN_MASK;
+ }
}
}
@@ -704,6 +757,9 @@ static int dev_create(struct dm_ioctl *param, size_t param_size)
int r, m = DM_ANY_MINOR;
struct mapped_device *md;
+ if (deny_create())
+ return -EACCES;
+
r = check_name(param->name);
if (r)
return r;
@@ -808,6 +864,12 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
md = hc->md;
+ if (dm_check_dm_dev_permission(md, 1, NULL)) {
+ up_write(&_hash_lock);
+ dm_put(md);
+ return -EACCES;
+ }
+
/*
* Ensure the device is not open and nothing further can open it.
*/
@@ -930,6 +992,11 @@ static int do_suspend(struct dm_ioctl *param)
if (!md)
return -ENXIO;
+ if (dm_check_dm_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
if (param->flags & DM_SKIP_LOCKFS_FLAG)
suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
if (param->flags & DM_NOFLUSH_FLAG)
@@ -968,6 +1035,11 @@ static int do_resume(struct dm_ioctl *param)
/*
+ * okozina: permissions
+ */
+
+/* FIXME: just simple test */
+/*
+ * Supposed to call during device initialization.
+ * Otherwise inode counter should be incremented.
+ */
+static int set_bdev_owner(struct block_device *bdev)
+{
+ int r = 0;
+ struct inode *inode;
+
+ /*
+ * ATTR_FORCE is compulsory. otherwise perm dennied for
+ * user w/o CAP_FOWNER.
+ *
+ * Note that user that has no right to create dm device
+ * needs to be stopped before ioct() syscall on control
+ * nod.
+ */
+ struct iattr attr = {
+ .ia_valid = ATTR_UID | ATTR_FORCE,
+ .ia_uid = current_fsuid()
+ };
+
+ inode = bdev->bd_inode;
+
+ mutex_lock(&inode->i_mutex);
+
+ /* for info only */
+ BUG_ON(!inode->i_sb);
+ BUG_ON(!inode->i_sb->s_root);
+
+ r = inode_change_ok(inode, &attr);
+ if (r)
+ goto out;
+
+ setattr_copy(inode, &attr);
+ mark_inode_dirty(inode);
+
+out:
+ mutex_unlock(&inode->i_mutex);
+ return r;
+}
+
+/*
+ * This is only suggestion how to define ownership
+ * of block device in kernel
+ */
+int dm_check_dm_dev_permission(struct mapped_device *md, int warn, void *attr __attribute__((unused)))
+{
+ int r;
+
+ if (!md)
+ return -EINVAL;
+
+ BUG_ON(!md->bdev);
+ BUG_ON(!md->bdev->bd_inode);
+
+ r = !inode_owner_or_capable(md->bdev->bd_inode);
+
+ if (r && warn)
+ DMWARN("uid: %d is not owner (or capable) of the "
+ "device %s", current_uid(), dm_device_name(md));
+
+ return r;
+}
+
+/*
* Block device functions
*/
int dm_deleting_md(struct mapped_device *md)
@@ -421,6 +493,11 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
if (!map || !dm_table_get_size(map))
goto out;
+ if (!capable(CAP_SYS_ADMIN)) {
+ r = -EACCES;
+ goto out;
+ }
+
/* We only support devices that have a single target */
if (dm_table_get_num_targets(map) != 1)
goto out;
@@ -1884,6 +1961,15 @@ static struct mapped_device *alloc_dev(int minor)
if (!md->bdev)
goto bad_bdev;
+ /* It's just a test. This should be
+ * in block layer */
+ if (set_bdev_owner(md->bdev)) {
+ DMDEBUG("set device ownership failed");
+ goto bad_bdev;
+ }
+ DMDEBUG("Device %s has got owner uid: %d",
+ md->name, md->bdev->bd_inode->i_uid);
+
bio_init(&md->flush_bio);
md->flush_bio.bi_bdev = md->bdev;
md->flush_bio.bi_rw = WRITE_FLUSH;
@@ -2774,6 +2860,7 @@ module_init(dm_init);
module_exit(dm_exit);
module_param(major, uint, 0);
+module_param_named(enable_security, sec_enabled, ushort, 0444);
MODULE_PARM_DESC(major, "The major number of the device mapper");
MODULE_DESCRIPTION(DM_NAME " driver");
MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index b7dacd5..82e98bd 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -156,4 +156,16 @@ void dm_kcopyd_exit(void);
struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity);
void dm_free_md_mempools(struct dm_md_mempools *pools);