diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2010-05-28 13:55:45 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2010-06-17 20:47:38 +0200 |
commit | c3695ab5f06af533deaf1de75cdd4631a9d77d00 (patch) | |
tree | 89a2c59d982accae5760d86fc5ae4dfa6b55c087 | |
parent | bda013fbafabd0d0826441cb40c93a46d4dc2301 (diff) | |
download | kernel-crypto-c3695ab5f06af533deaf1de75cdd4631a9d77d00.tar.gz kernel-crypto-c3695ab5f06af533deaf1de75cdd4631a9d77d00.tar.xz kernel-crypto-c3695ab5f06af533deaf1de75cdd4631a9d77d00.zip |
Added some initial for of key. Added helper functions to enforce per user and per process limits.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | cryptodev_main.c | 9 | ||||
-rw-r--r-- | examples/new.c | 20 | ||||
-rw-r--r-- | ncr-data.c | 75 | ||||
-rw-r--r-- | ncr-key.c | 154 | ||||
-rw-r--r-- | ncr-limits.c | 176 | ||||
-rw-r--r-- | ncr.c | 40 | ||||
-rw-r--r-- | ncr_int.h | 70 |
8 files changed, 477 insertions, 69 deletions
@@ -2,7 +2,7 @@ KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build VERSION = 0.1 cryptodev-objs = cryptodev_main.o cryptodev_cipher.o ncr.o \ - ncr-data.o + ncr-data.o ncr-key.o ncr-limits.o obj-m += cryptodev.o diff --git a/cryptodev_main.c b/cryptodev_main.c index 50e5a2749fd..da7ef2b3c8c 100644 --- a/cryptodev_main.c +++ b/cryptodev_main.c @@ -562,13 +562,6 @@ cryptodev_ioctl(struct inode *inode, struct file *filp, struct fcrypt * fcr; uint32_t ses; int ret, fd; - unsigned int uid; - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27) - uid = filp->f_uid; -#else - uid = filp->f_cred->fsuid; -#endif if (unlikely(!pcr)) BUG(); @@ -601,7 +594,7 @@ cryptodev_ioctl(struct inode *inode, struct file *filp, return copy_to_user((void*)arg, &cop, sizeof(cop)); default: - return ncr_ioctl(uid, pcr->ncr, cmd, arg); + return ncr_ioctl(pcr->ncr, filp, cmd, arg); } } diff --git a/examples/new.c b/examples/new.c index db46927c28c..8d0adb5bee1 100644 --- a/examples/new.c +++ b/examples/new.c @@ -130,12 +130,24 @@ test_ncr_data(int cfd) if (ioctl(cfd, NCRIO_DATA_GET, &kdata)) { perror("ioctl"); fprintf(stderr, "Verified that unexportable data cannot be exported\n"); - return 0; + } else { + fprintf(stderr, "Unexportable data were exported!?\n"); + return 1; /* ok */ } - fprintf(stderr, "Unexportable data were exported!?\n"); - - return 1; /* ok */ + for (i=0;i<512;i++ ) { + init.max_object_size = DATA_SIZE; + init.flags = 0; + init.initial_data = data; + init.initial_data_size = sizeof(data); + + if (ioctl(cfd, NCRIO_DATA_INIT, &init)) { + perror("ioctl(NCRIO_DATA_INIT)"); + fprintf(stderr, "Reached maximum limit at: %d data\n", i); + } + } + + return 0; } int diff --git a/ncr-data.c b/ncr-data.c index ea10f8b10ee..90986bca21d 100644 --- a/ncr-data.c +++ b/ncr-data.c @@ -34,30 +34,30 @@ static void _ncr_data_item_put( struct data_item* item); -void ncr_data_list_deinit(struct ncr_lists *lst) +void ncr_data_list_deinit(struct list_sem_st* lst) { if(lst) { struct data_item * item, *tmp; - down(&lst->data_sem); + down(&lst->sem); - list_for_each_entry_safe(item, tmp, &lst->data_list, list) { + list_for_each_entry_safe(item, tmp, &lst->list, list) { list_del(&item->list); _ncr_data_item_put( item); /* decrement ref count */ } - up(&lst->data_sem); + up(&lst->sem); } } /* must be called with data semaphore down */ -static ncr_data_t _ncr_data_get_new_desc( struct ncr_lists* lst) +static ncr_data_t _ncr_data_get_new_desc( struct list_sem_st* lst) { struct data_item* item; int mx = 0; - list_for_each_entry(item, &lst->data_list, list) { + list_for_each_entry(item, &lst->list, list) { mx = max(mx, item->desc); } mx++; @@ -66,27 +66,27 @@ int mx = 0; } /* returns the data item corresponding to desc */ -static struct data_item* ncr_data_item_get( struct ncr_lists* lst, ncr_data_t desc) +static struct data_item* ncr_data_item_get( struct list_sem_st* lst, ncr_data_t desc) { struct data_item* item; - down(&lst->data_sem); - list_for_each_entry(item, &lst->data_list, list) { + down(&lst->sem); + list_for_each_entry(item, &lst->list, list) { if (item->desc == desc) { atomic_inc(&item->refcnt); - up(&lst->data_sem); + up(&lst->sem); return item; } } - up(&lst->data_sem); + up(&lst->sem); err(); return NULL; } -static void* data_alloc(unsigned int uid, size_t size) +static void* data_alloc(size_t size) { - /* FIXME: enforce a maximum memory limit per user */ + /* FIXME: enforce a maximum memory limit per process and per user */ if (size > 64*1024) { err(); return NULL; @@ -94,25 +94,27 @@ static void* data_alloc(unsigned int uid, size_t size) return kmalloc(size, GFP_KERNEL); } -static void data_free(struct data_item * data) -{ - /* FIXME: enforce a maximum memory limit per user */ - kfree(data->data); -} - static void _ncr_data_item_put( struct data_item* item) { if (atomic_dec_and_test(&item->refcnt)) { - data_free(item); + ncr_limits_remove(item->filp, LIMIT_TYPE_DATA); + kfree(item->data); kfree(item); } } -int ncr_data_new(unsigned int uid, struct ncr_lists* lst, void __user* arg) +int ncr_data_init(struct file *filp, struct list_sem_st* lst, void __user* arg) { struct ncr_data_init_st init; struct data_item* data; - + int ret; + + ret = ncr_limits_add_and_check(filp, LIMIT_TYPE_DATA); + if (ret < 0) { + err(); + return ret; + } + copy_from_user( &init, arg, sizeof(init)); data = kmalloc(sizeof(*data), GFP_KERNEL); @@ -124,9 +126,10 @@ int ncr_data_new(unsigned int uid, struct ncr_lists* lst, void __user* arg) memset(data, 0, sizeof(*data)); data->flags = init.flags; + data->filp = filp; atomic_set(&data->refcnt, 1); - data->data = data_alloc(uid, init.max_object_size); + data->data = data_alloc(init.max_object_size); if (data->data == NULL) { kfree(data); err(); @@ -134,19 +137,19 @@ int ncr_data_new(unsigned int uid, struct ncr_lists* lst, void __user* arg) } data->max_data_size = init.max_object_size; - down(&lst->data_sem); - - data->desc = _ncr_data_get_new_desc(lst); - data->uid = uid; - if (init.initial_data != NULL) { copy_from_user(data->data, init.initial_data, init.initial_data_size); data->data_size = init.initial_data_size; } - list_add(&data->list, &lst->data_list); + down(&lst->sem); + + data->desc = _ncr_data_get_new_desc(lst); + data->filp = filp; + + list_add(&data->list, &lst->list); - up(&lst->data_sem); + up(&lst->sem); init.desc = data->desc; copy_to_user(arg, &init, sizeof(init)); @@ -155,16 +158,16 @@ int ncr_data_new(unsigned int uid, struct ncr_lists* lst, void __user* arg) } -int ncr_data_deinit(struct ncr_lists* lst, void __user* arg) +int ncr_data_deinit(struct list_sem_st* lst, void __user* arg) { ncr_data_t desc; struct data_item * item, *tmp; copy_from_user( &desc, arg, sizeof(desc)); - down(&lst->data_sem); + down(&lst->sem); - list_for_each_entry_safe(item, tmp, &lst->data_list, list) { + list_for_each_entry_safe(item, tmp, &lst->list, list) { if(item->desc == desc) { list_del(&item->list); _ncr_data_item_put( item); /* decrement ref count */ @@ -172,12 +175,12 @@ int ncr_data_deinit(struct ncr_lists* lst, void __user* arg) } } - up(&lst->data_sem); + up(&lst->sem); return 0; } -int ncr_data_get(struct ncr_lists* lst, void __user* arg) +int ncr_data_get(struct list_sem_st* lst, void __user* arg) { struct ncr_data_st get; struct data_item * data; @@ -211,7 +214,7 @@ int ncr_data_get(struct ncr_lists* lst, void __user* arg) return 0; } -int ncr_data_set(struct ncr_lists* lst, void __user* arg) +int ncr_data_set(struct list_sem_st* lst, void __user* arg) { struct ncr_data_st get; struct data_item * data; diff --git a/ncr-key.c b/ncr-key.c new file mode 100644 index 00000000000..65a5525894b --- /dev/null +++ b/ncr-key.c @@ -0,0 +1,154 @@ +/* + * New driver for /dev/crypto device (aka CryptoDev) + + * Copyright (c) 2010 Nikos Mavrogiannopoulos <nmav@gnutls.org> + * + * This file is part of linux cryptodev. + * + * cryptodev is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * cryptodev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/crypto.h> +#include <linux/mm.h> +#include <linux/highmem.h> +#include <linux/random.h> +#include "cryptodev.h" +#include <asm/uaccess.h> +#include <asm/ioctl.h> +#include <linux/scatterlist.h> +#include "ncr.h" +#include "ncr_int.h" + +#define err() printk(KERN_DEBUG"ncr: %s: %d\n", __func__, __LINE__) + +static void _ncr_key_item_put( struct key_item* item); + +void ncr_key_list_deinit(struct list_sem_st* lst) +{ + if(lst) { + struct key_item * item, *tmp; + + down(&lst->sem); + + list_for_each_entry_safe(item, tmp, &lst->list, list) { + list_del(&item->list); + _ncr_key_item_put( item); /* decrement ref count */ + } + up(&lst->sem); + } +} + +/* must be called with data semaphore down + */ +static ncr_key_t _ncr_key_get_new_desc( struct list_sem_st* lst) +{ +struct key_item* item; +int mx = 0; + + list_for_each_entry(item, &lst->list, list) { + mx = max(mx, item->desc); + } + mx++; + + return mx; +} + +/* returns the data item corresponding to desc */ +static struct key_item* ncr_key_item_get( struct list_sem_st* lst, ncr_key_t desc) +{ +struct key_item* item; + + down(&lst->sem); + list_for_each_entry(item, &lst->list, list) { + if (item->desc == desc) { + atomic_inc(&item->refcnt); + up(&lst->sem); + return item; + } + } + up(&lst->sem); + + err(); + return NULL; +} + +static void _ncr_key_item_put( struct key_item* item) +{ + if (atomic_dec_and_test(&item->refcnt)) { + ncr_limits_remove(item->filp, LIMIT_TYPE_KEY); + kfree(item); + } +} + +int ncr_key_init(struct file *filp, struct list_sem_st* lst, void __user* arg) +{ + ncr_key_t desc; + struct key_item* key; + int ret; + + ret = ncr_limits_add_and_check(filp, LIMIT_TYPE_KEY); + if (ret < 0) { + err(); + return ret; + } + + copy_from_user( &desc, arg, sizeof(desc)); + + key = kmalloc(sizeof(*key), GFP_KERNEL); + if (key == NULL) { + err(); + return -ENOMEM; + } + + memset(key, 0, sizeof(*key)); + + atomic_set(&key->refcnt, 1); + + down(&lst->sem); + + key->desc = _ncr_key_get_new_desc(lst); + key->filp = filp; + + list_add(&key->list, &lst->list); + + up(&lst->sem); + + desc = key->desc; + copy_to_user(arg, &desc, sizeof(desc)); + + return 0; +} + + +int ncr_key_deinit(struct list_sem_st* lst, void __user* arg) +{ + ncr_key_t desc; + struct key_item * item, *tmp; + + copy_from_user( &desc, arg, sizeof(desc)); + + down(&lst->sem); + + list_for_each_entry_safe(item, tmp, &lst->list, list) { + if(item->desc == desc) { + list_del(&item->list); + _ncr_key_item_put( item); /* decrement ref count */ + break; + } + } + + up(&lst->sem); + + return 0; +} diff --git a/ncr-limits.c b/ncr-limits.c new file mode 100644 index 00000000000..c65b7b333d7 --- /dev/null +++ b/ncr-limits.c @@ -0,0 +1,176 @@ +/* + * New driver for /dev/crypto device (aka CryptoDev) + + * Copyright (c) 2010 Nikos Mavrogiannopoulos <nmav@gnutls.org> + * + * This file is part of linux cryptodev. + * + * cryptodev is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * cryptodev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/highmem.h> +#include <linux/random.h> +#include "cryptodev.h" +#include <asm/atomic.h> +#include <linux/version.h> +#include "ncr.h" +#include "ncr_int.h" + +/* arbitrary now */ +unsigned int max_per_user[] = { + [LIMIT_TYPE_KEY] = 128, + [LIMIT_TYPE_DATA] = 128, +}; + +unsigned int max_per_process[] = { + [LIMIT_TYPE_KEY] = 64, + [LIMIT_TYPE_DATA] = 64, +}; + +struct limit_user_item_st { + struct list_head list; + uid_t uid; + limits_type_t type; + atomic_t cnt; +}; + +struct limit_process_item_st { + struct list_head list; + struct pid * pid; + limits_type_t type; + atomic_t cnt; +}; + +struct limit_st { + struct list_sem_st users; + struct list_sem_st processes; +} limits; + +void ncr_limits_init(void) +{ + init_MUTEX(&limits.users.sem); + INIT_LIST_HEAD(&limits.users.list); + + init_MUTEX(&limits.processes.sem); + INIT_LIST_HEAD(&limits.processes.list); +} + +int ncr_limits_add_and_check(struct file *filp, limits_type_t type) +{ +struct limit_process_item_st* pitem; +struct limit_user_item_st* uitem; +uid_t uid; +int add = 1; + +/* FIXME: is this uid ok? + */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27) + uid = filp->f_uid; +#else + uid = filp->f_cred->fsuid; +#endif + + down(&limits.users.sem); + list_for_each_entry(uitem, &limits.users.list, list) { + if (uitem->uid == uid && uitem->type == type) { + add = 0; + if (atomic_add_unless(&uitem->cnt, 1, max_per_user[type])==0) { + err(); + up(&limits.users.sem); + return -EPERM; + } + } + } + + if (add) { + uitem = kmalloc(GFP_KERNEL, sizeof(*uitem)); + if (uitem == NULL) { + err(); + return -ENOMEM; + } + uitem->uid = uid; + uitem->type = type; + atomic_set(&uitem->cnt, 1); + + list_add(&uitem->list, &limits.users.list); + } + up(&limits.users.sem); + + add = 1; + /* check process limits */ + down(&limits.processes.sem); + list_for_each_entry(pitem, &limits.processes.list, list) { + if (pitem->pid == filp->f_owner.pid && pitem->type == type) { + add = 0; + if (atomic_add_unless(&pitem->cnt, 1, max_per_process[type])==0) { + err(); + up(&limits.processes.sem); + return -EPERM; + } + } + } + + + if (add) { + pitem = kmalloc(GFP_KERNEL, sizeof(*pitem)); + if (uitem == NULL) { + err(); + return -ENOMEM; + } + pitem->pid = filp->f_owner.pid; + pitem->type = type; + atomic_set(&pitem->cnt, 1); + + list_add(&pitem->list, &limits.processes.list); + } + up(&limits.processes.sem); + + return 0; +} + +void ncr_limits_remove(struct file *filp, limits_type_t type) +{ +struct limit_process_item_st* pitem; +struct limit_user_item_st* uitem; +uid_t uid; + +/* FIXME: is this uid ok? + */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27) + uid = filp->f_uid; +#else + uid = filp->f_cred->fsuid; +#endif + + down(&limits.users.sem); + list_for_each_entry(uitem, &limits.users.list, list) { + if (uitem->uid == uid && uitem->type == type) { + atomic_dec(&uitem->cnt); + } + } + up(&limits.users.sem); + + /* check process limits */ + down(&limits.processes.sem); + list_for_each_entry(pitem, &limits.processes.list, list) { + if (pitem->pid == filp->f_owner.pid && pitem->type == type) { + atomic_dec(&pitem->cnt); + } + } + up(&limits.processes.sem); + + return; +} @@ -44,8 +44,11 @@ void* ncr_init_lists(void) memset(lst, 0, sizeof(*lst)); - init_MUTEX(&lst->data_sem); - INIT_LIST_HEAD(&lst->data_list); + init_MUTEX(&lst->data.sem); + INIT_LIST_HEAD(&lst->data.list); + + init_MUTEX(&lst->key.sem); + INIT_LIST_HEAD(&lst->key.list); return lst; } @@ -53,15 +56,16 @@ void* ncr_init_lists(void) void ncr_deinit_lists(struct ncr_lists *lst) { if(lst) { - ncr_data_list_deinit(lst); + ncr_data_list_deinit(&lst->data); + ncr_key_list_deinit(&lst->key); kfree(lst); } } int -ncr_ioctl(unsigned int uid, struct ncr_lists* lst, - unsigned int cmd, unsigned long __user arg) +ncr_ioctl(struct ncr_lists* lst, struct file *filp, + unsigned int cmd, unsigned long arg) { if (unlikely(!lst)) @@ -69,13 +73,31 @@ ncr_ioctl(unsigned int uid, struct ncr_lists* lst, switch (cmd) { case NCRIO_DATA_INIT: - return ncr_data_new(uid, lst, (void*)arg); + return ncr_data_init(filp, &lst->data, (void*)arg); case NCRIO_DATA_GET: - return ncr_data_get(lst, (void*)arg); + return ncr_data_get(&lst->data, (void*)arg); case NCRIO_DATA_SET: - return ncr_data_set(lst, (void*)arg); + return ncr_data_set(&lst->data, (void*)arg); case NCRIO_DATA_DEINIT: - return ncr_data_deinit(lst, (void*)arg); + return ncr_data_deinit(&lst->data, (void*)arg); + case NCRIO_KEY_INIT: + return ncr_key_init(filp, &lst->key, (void*)arg); + case NCRIO_KEY_DEINIT: + return ncr_key_deinit(&lst->key, (void*)arg); +#if 0 + case NCRIO_KEY_GENERATE: + return ncr_key_generate(&lst->key, (void*)arg); + case NCRIO_KEY_GENERATE_PAIR: + return ncr_key_generate_pair(&lst->key, (void*)arg); + case NCRIO_KEY_DERIVE: + return ncr_key_derive(&lst->key, (void*)arg); + case NCRIO_KEY_EXPORT: + return ncr_key_export(&lst->key, (void*)arg); + case NCRIO_KEY_IMPORT: + return ncr_key_import(&lst->key, (void*)arg); + case NCRIO_KEY_GET_PUBLIC: + return ncr_key_get_public(&lst->key, (void*)arg); +#endif default: return -EINVAL; } diff --git a/ncr_int.h b/ncr_int.h index 7d78ac8a1e9..82141ac6112 100644 --- a/ncr_int.h +++ b/ncr_int.h @@ -4,6 +4,8 @@ #include "ncr.h" #include <asm/atomic.h> +#define err() printk(KERN_DEBUG"ncr: %s: %d\n", __func__, __LINE__) + struct data_item { struct list_head list; /* This object is not protected from concurrent access. @@ -15,33 +17,79 @@ struct data_item { size_t max_data_size; unsigned int flags; atomic_t refcnt; - unsigned int uid; + struct file *filp; /* who has it */ ncr_data_t desc; }; +#define MAX_KEY_SIZE 32 /* in bytes */ + +struct key_item { + struct list_head list; + /* This object is also not protected from concurrent access. + */ + ncr_key_type_t type; + unsigned int flags; + ncr_algorithm_t algorithm; /* valid for public/private keys */ + uint8_t key_id[MAX_KEY_ID_SIZE]; + size_t key_id_size; + + union { + struct { + uint8_t data[MAX_KEY_SIZE]; + size_t size; + } secret; + } key; + + atomic_t refcnt; + + struct file *filp; /* who has it */ + ncr_key_t desc; +}; + +struct list_sem_st { + struct list_head list; + struct semaphore sem; +}; + /* all the data associated with the open descriptor * are here. */ struct ncr_lists { - struct list_head data_list; - struct semaphore data_sem; + struct list_sem_st data; + struct list_sem_st key; /* sessions */ - /* keys */ }; void* ncr_init_lists(void); void ncr_deinit_lists(struct ncr_lists *lst); -int -ncr_ioctl(unsigned int uid, struct ncr_lists* lst, +int ncr_ioctl(struct ncr_lists*, struct file *filp, unsigned int cmd, unsigned long arg); + +int ncr_data_set(struct list_sem_st*, void __user* arg); +int ncr_data_get(struct list_sem_st*, void __user* arg); +int ncr_data_deinit(struct list_sem_st*, void __user* arg); +int ncr_data_init(struct file* filp, struct list_sem_st*, void __user* arg); +void ncr_data_list_deinit(struct list_sem_st*); + +int ncr_key_init(struct file* filp, struct list_sem_st*, void __user* arg); +int ncr_key_generate(struct list_sem_st*, void __user* arg); +int ncr_key_generate_pair(struct list_sem_st* lst, void __user* arg); +int ncr_key_derive(struct list_sem_st*, void __user* arg); +int ncr_key_export(struct list_sem_st*, void __user* arg); +int ncr_key_import(struct list_sem_st*, void __user* arg); +int ncr_key_get_public(struct list_sem_st* lst, void __user* arg); +int ncr_key_deinit(struct list_sem_st*, void __user* arg); +void ncr_key_list_deinit(struct list_sem_st* lst); + +typedef enum { + LIMIT_TYPE_KEY, + LIMIT_TYPE_DATA +} limits_type_t; -int ncr_data_set(struct ncr_lists* lst, void __user* arg); -int ncr_data_get(struct ncr_lists* lst, void __user* arg); -int ncr_data_deinit(struct ncr_lists* lst, void __user* arg); -int ncr_data_new(unsigned int uid, struct ncr_lists* lst, void __user* arg); -void ncr_data_list_deinit(struct ncr_lists *lst); +void ncr_limits_remove(struct file *filp, limits_type_t type); +int ncr_limits_add_and_check(struct file *filp, limits_type_t type); #endif |