diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | cryptodev_main.c | 10 | ||||
-rw-r--r-- | examples/Makefile | 6 | ||||
-rw-r--r-- | examples/cipher.c | 2 | ||||
-rw-r--r-- | examples/hmac.c | 2 | ||||
-rw-r--r-- | examples/new.c | 126 | ||||
-rw-r--r-- | ncr.c | 167 | ||||
-rw-r--r-- | ncr.h | 4 | ||||
-rw-r--r-- | ncr_int.h | 6 |
10 files changed, 304 insertions, 23 deletions
@@ -9,5 +9,6 @@ Module.symvers modules.order examples/cipher examples/hmac +examples/new releases scripts @@ -1,4 +1,4 @@ -KERNEL_DIR = /lib/modules/$(shell uname -r)/build +KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build VERSION = 0.1 cryptodev-objs = cryptodev_main.o cryptodev_cipher.o ncr.o @@ -12,6 +12,7 @@ install: make -C $(KERNEL_DIR) SUBDIRS=`pwd` modules_install @echo "Installing cryptodev.h in /usr/include/crypto ..." @install -D cryptodev.h /usr/include/crypto/cryptodev.h + @install -D ncr.h /usr/include/crypto/ncr.h clean: make -C $(KERNEL_DIR) SUBDIRS=`pwd` clean diff --git a/cryptodev_main.c b/cryptodev_main.c index 09aa5bf..fabda2e 100644 --- a/cryptodev_main.c +++ b/cryptodev_main.c @@ -40,6 +40,7 @@ #include <linux/scatterlist.h> #include "cryptodev_int.h" #include "ncr_int.h" +#include <linux/version.h> MODULE_AUTHOR("Nikos Mavrogiannopoulos <nmav@gnutls.org>"); MODULE_DESCRIPTION("CryptoDev driver"); @@ -561,6 +562,13 @@ 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(); @@ -596,7 +604,7 @@ cryptodev_ioctl(struct inode *inode, struct file *filp, return copy_to_user((void*)arg, &cop, sizeof(cop)); default: - return ncr_ioctl(filp->f_cred->fsuid, pcr->ncr, cmd, arg); + return ncr_ioctl(uid, pcr->ncr, cmd, arg); } } diff --git a/examples/Makefile b/examples/Makefile index 601fb07..3190c4a 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,12 +1,14 @@ KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build -hostprogs := cipher hmac +hostprogs := cipher hmac new example-cipher-objs := cipher.o example-hmac-objs := hmac.o +new-objs := new.o check: $(hostprogs) + ./new ./cipher ./hmac clean: - rm -f *.o *~ hmac cipher + rm -f *.o *~ hmac cipher new diff --git a/examples/cipher.c b/examples/cipher.c index d7982ae..c7ce2c2 100644 --- a/examples/cipher.c +++ b/examples/cipher.c @@ -10,7 +10,7 @@ #include <fcntl.h> #include <sys/ioctl.h> -#include <crypto/cryptodev.h> +#include "../cryptodev.h" #define DATA_SIZE 4096 #define BLOCK_SIZE 16 diff --git a/examples/hmac.c b/examples/hmac.c index 9975792..9757f90 100644 --- a/examples/hmac.c +++ b/examples/hmac.c @@ -10,7 +10,7 @@ #include <fcntl.h> #include <sys/ioctl.h> -#include <crypto/cryptodev.h> +#include "../cryptodev.h" #define DATA_SIZE 4096 #define BLOCK_SIZE 16 diff --git a/examples/new.c b/examples/new.c new file mode 100644 index 0000000..60f9437 --- /dev/null +++ b/examples/new.c @@ -0,0 +1,126 @@ +/* + * Demo on how to use /dev/crypto device for HMAC. + * + * Placed under public domain. + * + */ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include <sys/ioctl.h> +#include "../ncr.h" +#include <stdlib.h> + +#define DATA_SIZE 4096 + +static void randomize_data(uint8_t * data, size_t data_size) +{ +int i; + + srand(time(0)*getpid()); + for (i=0;i<data_size;i++) { + data[i] = rand() & 0xff; + } +} + +static int +test_ncr_data(int cfd) +{ + struct ncr_data_init_st init; + struct ncr_data_st kdata; + uint8_t data[DATA_SIZE]; + uint8_t data_bak[DATA_SIZE]; + int i; + + randomize_data(data, sizeof(data)); + memcpy(data_bak, data, sizeof(data)); + + init.max_object_size = DATA_SIZE; + init.flags = NCR_DATA_FLAG_EXPORTABLE; + init.initial_data = data; + init.initial_data_size = sizeof(data); + + if (ioctl(cfd, NCRIO_DATA_INIT, &init)) { + perror("ioctl(NCRIO_DATA_INIT)"); + return 1; + } + + memset(data, 0, sizeof(data)); + + kdata.desc = init.desc; + kdata.data = data; + kdata.data_size = sizeof(data); + kdata.append_flag = 0; + + if (ioctl(cfd, NCRIO_DATA_GET, &kdata)) { + perror("ioctl(NCRIO_DATA_GET)"); + return 1; + } + + if (memcmp(data, data_bak, sizeof(data))!=0) { + fprintf(stderr, "data returned but differ!\n"); + return 1; + } + + /* test set */ + memset(data, 0xf1, sizeof(data)); + + kdata.desc = init.desc; + kdata.data = data; + kdata.data_size = sizeof(data); + kdata.append_flag = 0; + + if (ioctl(cfd, NCRIO_DATA_SET, &kdata)) { + perror("ioctl(NCRIO_DATA_SET)"); + return 1; + } + + /* test get after set */ + memset(data, 0, sizeof(data)); + + kdata.desc = init.desc; + kdata.data = data; + kdata.data_size = sizeof(data); + kdata.append_flag = 0; + + if (ioctl(cfd, NCRIO_DATA_GET, &kdata)) { + perror("ioctl(NCRIO_DATA_GET)"); + return 1; + } + + for(i=0;i<kdata.data_size;i++) { + if (((uint8_t*)kdata.data)[i] != 0xf1) { + fprintf(stderr, "data returned but differ!\n"); + return 1; + } + } + + return 0; /* ok */ +} + +int +main() +{ + int fd = -1, cfd = -1; + + /* Open the crypto device */ + fd = open("/dev/crypto", O_RDWR, 0); + if (fd < 0) { + perror("open(/dev/crypto)"); + return 1; + } + + /* Run the test itself */ + if (test_ncr_data(cfd)) + return 1; + + /* Close the original descriptor */ + if (close(fd)) { + perror("close(fd)"); + return 1; + } + + return 0; +} @@ -64,7 +64,7 @@ static ncr_data_t _ncr_data_get_new_desc( struct ncr_lists* lst) struct data_item* item; int mx = 0; - list_for_each(item, &lst->data_list) { + list_for_each_entry(item, &lst->data_list, list) { mx = max(mx, item->desc); } mx++; @@ -72,54 +72,190 @@ int mx = 0; return mx; } -void* data_alloc(unsigned int uid, size_t size) +/* returns the data item corresponding to desc */ +static struct data_item* _ncr_data_item_get( struct ncr_lists* lst, ncr_data_t desc) { - /* FIXME: implement a maximum memory limit per user */ +struct data_item* item; + + down(&lst->data_sem); + list_for_each_entry(item, &lst->data_list, list) { + if (item->desc == desc) { + atomic_inc(&item->refcnt); + up(&lst->data_sem); + return item; + } + } + up(&lst->data_sem); + + return NULL; +} + +static void* data_alloc(unsigned int uid, size_t size) +{ + /* FIXME: enforce a maximum memory limit per user */ if (size > 64*1024) { return NULL; } - return kmalloc(GPF_KERNEL, size); + return kmalloc(GFP_KERNEL, size); +} + +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 ncr_lists* lst, struct data_item* item) +{ + if (atomic_dec_and_test(&item->refcnt)) { + data_free(item); + kfree(item); + } } -int ncr_data_new(unsigned int uid, struct ncr_lists* lst, void __user* arg) +static int ncr_data_new(unsigned int uid, struct ncr_lists* lst, void __user* arg) { struct ncr_data_init_st init; struct data_item* data; copy_from_user( &init, arg, sizeof(init)); - data = kmalloc(GPF_KERNEL, sizeof(*data)); + data = kmalloc(GFP_KERNEL, sizeof(*data)); if (data == NULL) { return -ENOMEM; } memset(data, 0, sizeof(*data)); - init_MUTEX(&data->sem); data->flags = init.flags; + atomic_set(&data->refcnt, 1); - data->data = data_alloc(uid, init.max_data_size); + data->data = data_alloc(uid, init.max_object_size); if (data->data == NULL) { kfree(data); return -ENOMEM; } - data->max_data_size = init.max_data_size; + data->max_data_size = init.max_object_size; - down(lst->data_sem); + down(&lst->data_sem); data->desc = _ncr_data_get_new_desc(lst); + data->uid = uid; - list_add(data, &list->data_list); + 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); - up(lst->data_sem); + up(&lst->data_sem); init.desc = data->desc; + copy_to_user(arg, &init, sizeof(init)); + + return 0; +} + + +static int ncr_data_deinit(struct ncr_lists* lst, void __user* arg) +{ + ncr_data_t desc; + struct data_item * item, *tmp; + + copy_from_user( &desc, arg, sizeof(desc)); + + down(&lst->data_sem); + + list_for_each_entry_safe(item, tmp, &lst->data_list, list) { + if(item->desc == desc) { + list_del(&item->list); + _ncr_data_item_put( lst, item); /* decrement ref count */ + break; + } + } + up(&lst->data_sem); + + return 0; +} + +static int ncr_data_get(struct ncr_lists* lst, void __user* arg) +{ + struct ncr_data_st get; + struct data_item * data; + size_t len; + + copy_from_user( &get, arg, sizeof(get)); + + data = _ncr_data_item_get( lst, get.desc); + + if (data == NULL) { + return -EINVAL; + } + + if (!(data->flags & NCR_DATA_FLAG_EXPORTABLE)) { + return -EPERM; + } + + len = min(get.data_size, data->data_size); + + /* update length */ + get.data_size = len; + copy_to_user(arg, &get, sizeof(get)); + + if (len > 0) + copy_to_user(get.data, data->data, len); + + _ncr_data_item_put( lst, data); + + return 0; +} + +static int ncr_data_set(struct ncr_lists* lst, void __user* arg) +{ + struct ncr_data_st get; + struct data_item * data; + int ret; + + copy_from_user( &get, arg, sizeof(get)); + + data = _ncr_data_item_get( lst, get.desc); + + if (data == NULL) { + return -EINVAL; + } + + if ((get.data_size > data->max_data_size) || + (get.data == NULL && get.data_size != 0)) { + ret = -EINVAL; + goto cleanup; + } + + if (!get.append_flag) { + if (get.data != NULL) + copy_from_user(data->data, get.data, get.data_size); + data->data_size = get.data_size; + } else { + if (get.data_size+data->data_size > data->max_data_size) { + ret = -EINVAL; + goto cleanup; + } + if (get.data != NULL) + copy_from_user(&data->data[data->data_size], get.data, get.data_size); + data->data_size += get.data_size; + } + ret = 0; + +cleanup: + _ncr_data_item_put( lst, data); + + return ret; } int ncr_ioctl(unsigned int uid, struct ncr_lists* lst, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long __user arg) { if (unlikely(!lst)) @@ -127,10 +263,13 @@ ncr_ioctl(unsigned int uid, struct ncr_lists* lst, switch (cmd) { case NCRIO_DATA_INIT: - return ncr_data_new(uid, lst, arg); + return ncr_data_new(uid, lst, (void*)arg); case NCRIO_DATA_GET: + return ncr_data_get(lst, (void*)arg); case NCRIO_DATA_SET: + return ncr_data_set(lst, (void*)arg); case NCRIO_DATA_DEINIT: + return ncr_data_deinit(lst, (void*)arg); default: return -EINVAL; } @@ -45,12 +45,14 @@ struct ncr_data_init_st { ncr_data_t desc; size_t max_object_size; unsigned int flags; + void* initial_data; /* can be null */ + size_t initial_data_size; }; struct ncr_data_st { ncr_data_t desc; void* data; - size_t data_size; + size_t data_size; /* rw in get */ unsigned int append_flag; /* only when used with NCRIO_DATA_SET */ }; @@ -2,14 +2,16 @@ # define NCR_INT_H #include "ncr.h" +#include <asm/atomic.h> struct data_item { struct list_head list; - void* data; + uint8_t* data; size_t data_size; size_t max_data_size; - struct semaphore sem; unsigned int flags; + atomic_t refcnt; + unsigned int uid; ncr_data_t desc; }; |