FAQ Search Today's Posts Mark Forums Read
» Video Reviews

» Linux Archive

Linux-archive is a website aiming to archive linux email lists and to make them easily accessible for linux users/developers.


» Sponsor

» Partners

» Sponsor

Go Back   Linux Archive > Redhat > Device-mapper Development

 
 
LinkBack Thread Tools
 
Old 05-02-2012, 07:13 PM
 
Default Adds support for user-submitted ioctl commands

From: Ondrej Kozina <okozina@redhat.com>

The core of changes in DM ioctl operations

---
drivers/md/dm-ioctl.c | 138 +++++++++++++++++++++++++++++++++++------
drivers/md/dm-sysfs.c | 30 +++++++++
drivers/md/dm-table.c | 16 +++++
drivers/md/dm.c | 86 +++++++++++++++++++++++++
drivers/md/dm.h | 14 ++++
include/linux/device-mapper.h | 3 +
6 files changed, 267 insertions(+), 20 deletions(-)

diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index a1a3e6d..f3de6fc 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -351,6 +351,7 @@ static char *__change_cell_name(struct hash_cell *hc, char *new_name)
static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
const char *new)
{
+ int r;
char *new_data, *old_name = NULL;
struct hash_cell *hc;
struct dm_table *table;
@@ -397,6 +398,14 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
return ERR_PTR(-ENXIO);
}

+ 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;
+ }
}
}

@@ -515,16 +562,19 @@ 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) {
- if (old_nl)
- old_nl->next = (uint32_t) ((void *) nl -
- (void *) old_nl);
- disk = dm_disk(hc->md);
- nl->dev = huge_encode_dev(disk_devt(disk));
- nl->next = 0;
- strcpy(nl->name, hc->name);
-
- old_nl = nl;
- nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1);
+ BUG_ON(!hc->md);
+ if (!dm_check_dev_permission(hc->md, 0, NULL)) {
+ if (old_nl)
+ old_nl->next = (uint32_t) ((void *) nl -
+ (void *) old_nl);
+ disk = dm_disk(hc->md);
+ nl->dev = huge_encode_dev(disk_devt(disk));
+ nl->next = 0;
+ strcpy(nl->name, hc->name);
+
+ old_nl = nl;
+ nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1);
+ }
}
}

@@ -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)

md = hc->md;

+ if (dm_check_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
new_map = hc->new_map;
hc->new_map = NULL;
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
@@ -1008,7 +1077,7 @@ static int do_resume(struct dm_ioctl *param)

if (!r)
__dev_status(md, param);
-
+out:
dm_put(md);
return r;
}
@@ -1123,6 +1192,11 @@ static int dev_wait(struct dm_ioctl *param, size_t param_size)
if (!md)
return -ENXIO;

+ if (dm_check_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
/*
* Wait for a notification event
*/
@@ -1222,6 +1296,11 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
if (!md)
return -ENXIO;

+ if (dm_check_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
r = dm_table_create(&t, get_mode(param), param->target_count, md);
if (r)
goto out;
@@ -1292,6 +1371,7 @@ out:

static int table_clear(struct dm_ioctl *param, size_t param_size)
{
+ int r = 0;
struct hash_cell *hc;
struct mapped_device *md;

@@ -1304,6 +1384,11 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)
return -ENXIO;
}

+ if (dm_check_dev_permission(hc->md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
if (hc->new_map) {
dm_table_destroy(hc->new_map);
hc->new_map = NULL;
@@ -1313,10 +1398,12 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)

__dev_status(hc->md, param);
md = hc->md;
+
+out:
up_write(&_hash_lock);
dm_put(md);

- return 0;
+ return r;
}

/*
@@ -1387,6 +1474,7 @@ static int table_deps(struct dm_ioctl *param, size_t param_size)
*/
static int table_status(struct dm_ioctl *param, size_t param_size)
{
+ int r = 0;
struct mapped_device *md;
struct dm_table *table;

@@ -1394,6 +1482,11 @@ static int table_status(struct dm_ioctl *param, size_t param_size)
if (!md)
return -ENXIO;

+ if (dm_check_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
__dev_status(md, param);

table = dm_get_live_or_inactive_table(md, param);
@@ -1402,9 +1495,10 @@ static int table_status(struct dm_ioctl *param, size_t param_size)
dm_table_put(table);
}

+out:
dm_put(md);

- return 0;
+ return r;
}

/*
@@ -1423,6 +1517,11 @@ static int target_message(struct dm_ioctl *param, size_t param_size)
if (!md)
return -ENXIO;

+ if (dm_check_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
if (tmsg < (struct dm_target_msg *) param->data ||
invalid_str(tmsg->message, (void *) param + param_size)) {
DMWARN("Invalid target message parameters.");
@@ -1616,13 +1715,12 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user)
ioctl_fn fn = NULL;
size_t input_param_size;

- /* only root can play with this */
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
-
if (_IOC_TYPE(command) != DM_IOCTL)
return -ENOTTY;

+ if (initial_permission_check(command))
+ return -EACCES;
+
cmd = _IOC_NR(command);

/*
diff --git a/drivers/md/dm-sysfs.c b/drivers/md/dm-sysfs.c
index 84d2b91..a0bde93 100644
--- a/drivers/md/dm-sysfs.c
+++ b/drivers/md/dm-sysfs.c
@@ -64,14 +64,44 @@ static ssize_t dm_attr_suspended_show(struct mapped_device *md, char *buf)
return strlen(buf);
}

+static ssize_t dm_attr_owner_uid_show(struct mapped_device *md, char *buf)
+{
+ struct block_device *bdev = bdget_disk(dm_disk(md), 0);
+
+ if (bdev) {
+ sprintf(buf, "%d
", bdev->bd_inode->i_uid);
+ bdput(bdev);
+ return strlen(buf);
+ }
+
+ return -EIO;
+}
+
+static ssize_t dm_attr_owner_gid_show(struct mapped_device *md, char *buf)
+{
+ struct block_device *bdev = bdget_disk(dm_disk(md), 0);
+
+ if (bdev) {
+ sprintf(buf, "%d
", bdev->bd_inode->i_gid);
+ bdput(bdev);
+ return strlen(buf);
+ }
+
+ return -EIO;
+}
+
static DM_ATTR_RO(name);
static DM_ATTR_RO(uuid);
static DM_ATTR_RO(suspended);
+static DM_ATTR_RO(owner_uid);
+static DM_ATTR_RO(owner_gid);

static struct attribute *dm_attrs[] = {
&dm_attr_name.attr,
&dm_attr_uuid.attr,
&dm_attr_suspended.attr,
+ &dm_attr_owner_uid.attr,
+ &dm_attr_owner_gid.attr,
NULL,
};

diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 2e227fb..1b41e33 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -816,6 +816,20 @@ int dm_table_add_target(struct dm_table *t, const char *type,
goto bad;
}

+ /* THIS IS WIP */
+ DMDEBUG("taget-type: %s, security-check %s defined", tgt->type->name, tgt->type->security ? "" : "not");
+ if (tgt->type->security) {
+ r = tgt->type->security(tgt, argc, argv);
+ if (r)
+ goto sec_bad;
+ } else {
+ if (!capable(CAP_SYS_ADMIN)) {
+ tgt->error = "target does not support security checks!";
+ r = -ENOTTY;
+ goto sec_bad;
+ }
+ }
+
r = tgt->type->ctr(tgt, argc, argv);
kfree(argv);
if (r)
@@ -829,6 +843,8 @@ int dm_table_add_target(struct dm_table *t, const char *type,

return 0;

+ sec_bad:
+ kfree(argv);
bad:
DMERR("%s: %s: %s", dm_device_name(t->md), type, tgt->error);
dm_put_target_type(tgt->type);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index e24143c..db6e547 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -333,6 +333,78 @@ static void __exit dm_exit(void)
}

/*
+ * 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>

/*
* Suspend feature flags
@@ -29,6 +31,8 @@
#define DM_TYPE_BIO_BASED 1
#define DM_TYPE_REQUEST_BASED 2

+#define DEV_PATH "/dev/"
+
/*
* List of devices that a metadevice uses and should open/close.
*/
@@ -156,4 +160,14 @@ 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);

+/*
+ * Check device permission
+ */
+int dm_check_dev_permission(struct mapped_device *md, int warn, void *attr);
+
+/*
+ * Lookup device node according to major:minor or path
+ */
+struct dentry* dm_lookup_bdev_dentry(char *device_param);
+
#endif
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 98f34b8..a9c1d87 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -95,6 +95,8 @@ typedef int (*dm_iterate_devices_fn) (struct dm_target *ti,
typedef void (*dm_io_hints_fn) (struct dm_target *ti,
struct queue_limits *limits);

+typedef int (*dm_security_fn) (struct dm_target *ti, unsigned int argc, char **argv);
+
/*
* Returns:
* 0: The target can handle the next I/O immediately.
@@ -151,6 +153,7 @@ struct target_type {
dm_busy_fn busy;
dm_iterate_devices_fn iterate_devices;
dm_io_hints_fn io_hints;
+ dm_security_fn security;

/* For internal device-mapper use. */
struct list_head list;
--
1.7.8.5

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel
 
Old 05-25-2012, 01:22 PM
Ondrej Kozina
 
Default Adds support for user-submitted ioctl commands

The core of changes in DM ioctl operations

Changes in version No. 2:
^^^^^^^^^^^^^^^^^^^^^^^^

(*) With security enabled, open file descriptor for backing device has to be
passed in target parameters (e.g. "fd:9" instead of "8:2")

(*) Load time parameter for dm-mod "enable_security"

(*) Removed owner_gid from sysfs attributes

---
drivers/md/Makefile | 2 +-
drivers/md/dm-ioctl.c | 141 +++++++++++++++++++++++++++++++++++------
drivers/md/dm-security.c | 95 +++++++++++++++++++++++++++
drivers/md/dm-sysfs.c | 15 +++++
drivers/md/dm-table.c | 23 +++++++-
drivers/md/dm.c | 87 +++++++++++++++++++++++++
drivers/md/dm.h | 12 ++++
include/linux/device-mapper.h | 5 ++
8 files changed, 358 insertions(+), 22 deletions(-)
create mode 100644 drivers/md/dm-security.c

diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 8b2e0df..28d0808 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -3,7 +3,7 @@
#

dm-mod-y += dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o
- dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o
+ dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o dm-security.o
dm-multipath-y += dm-path-selector.o dm-mpath.o
dm-snapshot-y += dm-snap.o dm-exception-store.o dm-snap-transient.o
dm-snap-persistent.o
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index a1a3e6d..e94a883 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -351,6 +351,7 @@ static char *__change_cell_name(struct hash_cell *hc, char *new_name)
static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
const char *new)
{
+ int r;
char *new_data, *old_name = NULL;
struct hash_cell *hc;
struct dm_table *table;
@@ -397,6 +398,14 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
return ERR_PTR(-ENXIO);
}

+ 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;
+ }
}
}

@@ -515,16 +565,19 @@ 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) {
- if (old_nl)
- old_nl->next = (uint32_t) ((void *) nl -
- (void *) old_nl);
- disk = dm_disk(hc->md);
- nl->dev = huge_encode_dev(disk_devt(disk));
- nl->next = 0;
- strcpy(nl->name, hc->name);
-
- old_nl = nl;
- nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1);
+ BUG_ON(!hc->md);
+ if (!dm_check_dm_dev_permission(hc->md, 0, NULL)) {
+ if (old_nl)
+ old_nl->next = (uint32_t) ((void *) nl -
+ (void *) old_nl);
+ disk = dm_disk(hc->md);
+ nl->dev = huge_encode_dev(disk_devt(disk));
+ nl->next = 0;
+ strcpy(nl->name, hc->name);
+
+ old_nl = nl;
+ nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1);
+ }
}
}

@@ -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)

md = hc->md;

+ if (dm_check_dm_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
new_map = hc->new_map;
hc->new_map = NULL;
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
@@ -1008,7 +1080,7 @@ static int do_resume(struct dm_ioctl *param)

if (!r)
__dev_status(md, param);
-
+out:
dm_put(md);
return r;
}
@@ -1123,6 +1195,11 @@ static int dev_wait(struct dm_ioctl *param, size_t param_size)
if (!md)
return -ENXIO;

+ if (dm_check_dm_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
/*
* Wait for a notification event
*/
@@ -1222,6 +1299,11 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
if (!md)
return -ENXIO;

+ if (dm_check_dm_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
r = dm_table_create(&t, get_mode(param), param->target_count, md);
if (r)
goto out;
@@ -1292,6 +1374,7 @@ out:

static int table_clear(struct dm_ioctl *param, size_t param_size)
{
+ int r = 0;
struct hash_cell *hc;
struct mapped_device *md;

@@ -1304,6 +1387,11 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)
return -ENXIO;
}

+ if (dm_check_dm_dev_permission(hc->md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
if (hc->new_map) {
dm_table_destroy(hc->new_map);
hc->new_map = NULL;
@@ -1313,10 +1401,12 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)

__dev_status(hc->md, param);
md = hc->md;
+
+out:
up_write(&_hash_lock);
dm_put(md);

- return 0;
+ return r;
}

/*
@@ -1387,6 +1477,7 @@ static int table_deps(struct dm_ioctl *param, size_t param_size)
*/
static int table_status(struct dm_ioctl *param, size_t param_size)
{
+ int r = 0;
struct mapped_device *md;
struct dm_table *table;

@@ -1394,6 +1485,11 @@ static int table_status(struct dm_ioctl *param, size_t param_size)
if (!md)
return -ENXIO;

+ if (dm_check_dm_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
__dev_status(md, param);

table = dm_get_live_or_inactive_table(md, param);
@@ -1402,9 +1498,10 @@ static int table_status(struct dm_ioctl *param, size_t param_size)
dm_table_put(table);
}

+out:
dm_put(md);

- return 0;
+ return r;
}

/*
@@ -1423,6 +1520,11 @@ static int target_message(struct dm_ioctl *param, size_t param_size)
if (!md)
return -ENXIO;

+ if (dm_check_dm_dev_permission(md, 1, NULL)) {
+ r = -EACCES;
+ goto out;
+ }
+
if (tmsg < (struct dm_target_msg *) param->data ||
invalid_str(tmsg->message, (void *) param + param_size)) {
DMWARN("Invalid target message parameters.");
@@ -1616,13 +1718,12 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user)
ioctl_fn fn = NULL;
size_t input_param_size;

- /* only root can play with this */
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
-
if (_IOC_TYPE(command) != DM_IOCTL)
return -ENOTTY;

+ if (initial_permission_check(command))
+ return -EACCES;
+
cmd = _IOC_NR(command);

/*
diff --git a/drivers/md/dm-security.c b/drivers/md/dm-security.c
new file mode 100644
index 0000000..5ce80cf
--- /dev/null
+++ b/drivers/md/dm-security.c
@@ -0,0 +1,95 @@
+#include "dm.h"
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/mount.h>
+
+static struct file *get_device_file(unsigned int fd)
+{
+ int r;
+ struct file *file;
+
+ file = fget(fd);
+ if (!file)
+ return file;
+
+ r = -ENOTBLK;
+ if (!S_ISBLK(file->f_dentry->d_inode->i_mode))
+ goto err;
+
+ r = -EACCES;
+ if (file->f_vfsmnt->mnt_flags & MNT_NODEV)
+ goto err;
+
+ return file;
+err:
+ fput(file);
+ return ERR_PTR(r);
+}
+
+
+static struct inode *get_bdev_inode(const char *fd_string)
+{
+ char dummy;
+ struct file *dev_file;
+ struct inode *inode;
+ unsigned int fd;
+
+ if (sscanf(fd_string, "fd:%u%c", &fd, &dummy) != 1)
+ return ERR_PTR(-EINVAL);
+
+ dev_file = get_device_file(fd);
+ if (!dev_file)
+ return ERR_PTR(-EINVAL);
+ if (IS_ERR(dev_file))
+ return (void *)dev_file;
+
+ /*
+ * fd is open by process under it's context
+ * we're in kernel space
+ */
+ ihold(dev_file->f_dentry->d_inode);
+
+ inode = dev_file->f_dentry->d_inode;
+ fput(dev_file);
+ return inode;
+}
+
+int dm_check_backing_dev_permission(struct dm_target *ti, const char *fd_string)
+{
+ int perm = 0, r;
+ struct inode *inode;
+
+ inode = get_bdev_inode(fd_string);
+ if (!inode)
+ return -EINVAL;
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+
+ if (dm_table_get_mode(ti->table) & FMODE_READ)
+ perm |= MAY_READ;
+ if (dm_table_get_mode(ti->table) & FMODE_WRITE)
+ perm |= MAY_WRITE;
+
+ r = inode_permission(inode, perm);
+
+ iput(inode);
+
+ return r;
+}
+
+/* fget_light would be better */
+int dm_get_device_from_fd(unsigned int fd, dev_t *devid)
+{
+ struct file *file;
+
+ file = get_device_file(fd);
+ if (!file)
+ return -EINVAL;
+ if (IS_ERR(file))
+ return PTR_ERR(file);
+
+ *devid = file->f_dentry->d_inode->i_rdev;
+
+ fput(file);
+ return 0;
+}
diff --git a/drivers/md/dm-sysfs.c b/drivers/md/dm-sysfs.c
index 84d2b91..0efdb21 100644
--- a/drivers/md/dm-sysfs.c
+++ b/drivers/md/dm-sysfs.c
@@ -64,14 +64,29 @@ static ssize_t dm_attr_suspended_show(struct mapped_device *md, char *buf)
return strlen(buf);
}

+static ssize_t dm_attr_owner_uid_show(struct mapped_device *md, char *buf)
+{
+ struct block_device *bdev = bdget_disk(dm_disk(md), 0);
+
+ if (bdev) {
+ sprintf(buf, "%d
", bdev->bd_inode->i_uid);
+ bdput(bdev);
+ return strlen(buf);
+ }
+
+ return -EIO;
+}
+
static DM_ATTR_RO(name);
static DM_ATTR_RO(uuid);
static DM_ATTR_RO(suspended);
+static DM_ATTR_RO(owner_uid);

static struct attribute *dm_attrs[] = {
&dm_attr_name.attr,
&dm_attr_uuid.attr,
&dm_attr_suspended.attr,
+ &dm_attr_owner_uid.attr,
NULL,
};

diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 2e227fb..f29cb19 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -461,7 +461,7 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
int r;
dev_t uninitialized_var(dev);
struct dm_dev_internal *dd;
- unsigned int major, minor;
+ unsigned int fd, major, minor;
struct dm_table *t = ti->table;
char dummy;

@@ -472,6 +472,10 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
dev = MKDEV(major, minor);
if (MAJOR(dev) != major || MINOR(dev) != minor)
return -EOVERFLOW;
+ } else if (sscanf(path, "fd:%u%c", &fd, &dummy) == 1) {
+ r = dm_get_device_from_fd(fd, &dev);
+ if (r)
+ return r;
} else {
/* convert the path to a device */
struct block_device *bdev = lookup_bdev(path);
@@ -816,6 +820,21 @@ int dm_table_add_target(struct dm_table *t, const char *type,
goto bad;
}

+ DMDEBUG("taget-type: %s, security-check %s defined", tgt->type->name, tgt->type->security ? "" : "not");
+
+ if (sec_enabled) {
+ if (tgt->type->security) {
+ r = tgt->type->security(tgt, argc, argv);
+ if (r)
+ goto sec_bad;
+ } else {
+ tgt->error = "target does not support security checks!";
+ /* NOTE: think about suitable error number */
+ r = -ENOTTY;
+ goto sec_bad;
+ }
+ }
+
r = tgt->type->ctr(tgt, argc, argv);
kfree(argv);
if (r)
@@ -829,6 +848,8 @@ int dm_table_add_target(struct dm_table *t, const char *type,

return 0;

+ sec_bad:
+ kfree(argv);
bad:
DMERR("%s: %s: %s", dm_device_name(t->md), type, tgt->error);
dm_put_target_type(tgt->type);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index e24143c..a333ea7 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -46,6 +46,8 @@ static const char *_name = DM_NAME;
static unsigned int major = 0;
static unsigned int _major = 0;

+unsigned short sec_enabled = 0;
+
static DEFINE_IDR(_minor_idr);

static DEFINE_SPINLOCK(_minor_lock);
@@ -333,6 +335,76 @@ static void __exit dm_exit(void)
}

/*
+ * 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);

+/*
+ * Check device permission
+ */
+int dm_check_dm_dev_permission(struct mapped_device *md, int warn, void *attr);
+
+/*
+ * Security related functions
+ */
+
+int dm_get_device_from_fd(unsigned int fd, dev_t *devid);
+int dm_check_backing_dev_permission(struct dm_target *ti, const char *fd_string);
+
#endif
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 98f34b8..7ddca63 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -12,6 +12,8 @@
#include <linux/blkdev.h>
#include <linux/ratelimit.h>

+extern unsigned short sec_enabled;
+
struct dm_dev;
struct dm_target;
struct dm_table;
@@ -95,6 +97,8 @@ typedef int (*dm_iterate_devices_fn) (struct dm_target *ti,
typedef void (*dm_io_hints_fn) (struct dm_target *ti,
struct queue_limits *limits);

+typedef int (*dm_security_fn) (struct dm_target *ti, unsigned int argc, char **argv);
+
/*
* Returns:
* 0: The target can handle the next I/O immediately.
@@ -151,6 +155,7 @@ struct target_type {
dm_busy_fn busy;
dm_iterate_devices_fn iterate_devices;
dm_io_hints_fn io_hints;
+ dm_security_fn security;

/* For internal device-mapper use. */
struct list_head list;
--
1.7.8.6

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel
 

Thread Tools




All times are GMT. The time now is 06:13 AM.

VBulletin, Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO ©2007, Crawlability, Inc.
Copyright 2007 - 2008, www.linux-archive.org