lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Fri, 20 Aug 2010 10:46:01 +0200
From:	Miloslav Trmač <mitr@...hat.com>
To:	Herbert Xu <herbert@...dor.hengli.com.au>
Cc:	linux-crypto@...r.kernel.org,
	Nikos Mavrogiannopoulos <n.mavrogiannopoulos@...il.com>,
	Neil Horman <nhorman@...hat.com>, linux-kernel@...r.kernel.org,
	Miloslav Trmač <mitr@...hat.com>
Subject: [PATCH 17/19] Add session operations

This includes:
- ncr_session_init
- ncr_session_update
- ncr_session_final
- ncr_session_once

The ncr_session_*_from_nla() functions are separate from the main
session code because they belong into ncr.c along with other code that
deals directly with user-space data structures and handles
CONFIG_COMPAT.
---
 crypto/userspace/ncr-sessions.c |  953 +++++++++++++++++++++++++++++++++++++++
 crypto/userspace/ncr.c          |   86 ++++
 2 files changed, 1039 insertions(+), 0 deletions(-)

diff --git a/crypto/userspace/ncr-sessions.c b/crypto/userspace/ncr-sessions.c
index e6fd995..b2adb8b 100644
--- a/crypto/userspace/ncr-sessions.c
+++ b/crypto/userspace/ncr-sessions.c
@@ -32,6 +32,104 @@
 #include <linux/scatterlist.h>
 #include <net/netlink.h>
 
+static int _ncr_session_update_key(struct ncr_lists *lists, ncr_session_t ses,
+				   struct nlattr *tb[]);
+static void _ncr_session_remove(struct ncr_lists *lst, ncr_session_t desc);
+
+static int session_list_deinit_fn(int id, void *item, void *unused)
+{
+	(void)unused;
+	_ncr_sessions_item_put(item);
+	return 0;
+}
+
+void ncr_sessions_list_deinit(struct ncr_lists *lst)
+{
+	/* The mutex is not necessary, but doesn't hurt and makes it easier to
+	   verify locking correctness. */
+	mutex_lock(&lst->session_idr_mutex);
+	idr_for_each(&lst->session_idr, session_list_deinit_fn, NULL);
+	idr_remove_all(&lst->session_idr);
+	idr_destroy(&lst->session_idr);
+	mutex_unlock(&lst->session_idr_mutex);
+}
+
+/* returns the data item corresponding to desc */
+struct session_item_st* ncr_sessions_item_get(struct ncr_lists *lst, ncr_session_t desc)
+{
+struct session_item_st* item;
+
+	mutex_lock(&lst->session_idr_mutex);
+	item = idr_find(&lst->session_idr, desc);
+	if (item != NULL) {
+		atomic_inc(&item->refcnt);
+		mutex_unlock(&lst->session_idr_mutex);
+		return item;
+	}
+	mutex_unlock(&lst->session_idr_mutex);
+
+	err();
+	return NULL;
+}
+
+void _ncr_sessions_item_put( struct session_item_st* item)
+{
+	if (atomic_dec_and_test(&item->refcnt)) {
+		cryptodev_cipher_deinit(&item->cipher);
+		ncr_pk_cipher_deinit(&item->pk);
+		cryptodev_hash_deinit(&item->hash);
+		if (item->key)
+			_ncr_key_item_put(item->key);
+		kfree(item->sg);
+		kfree(item->pages);
+		kfree(item);
+	}
+}
+
+struct session_item_st* ncr_session_new(struct ncr_lists *lst)
+{
+	struct session_item_st* sess;
+
+	sess = kzalloc(sizeof(*sess), GFP_KERNEL);
+	if (sess == NULL) {
+		err();
+		return NULL;
+	}
+
+	sess->array_size = DEFAULT_PREALLOC_PAGES;
+	sess->pages = kzalloc(sess->array_size *
+			sizeof(struct page *), GFP_KERNEL);
+	sess->sg = kzalloc(sess->array_size *
+			sizeof(struct scatterlist), GFP_KERNEL);
+	if (sess->sg == NULL || sess->pages == NULL) {
+		err();
+		goto err_sess;
+	}
+	mutex_init(&sess->mem_mutex);
+
+	atomic_set(&sess->refcnt, 2); /* One for lst->list, one for "sess" */
+
+	mutex_lock(&lst->session_idr_mutex);
+	/* idr_pre_get() should preallocate enough, and, due to
+	   session_idr_mutex, nobody else can use the preallocated data.
+	   Therefore the loop recommended in idr_get_new() documentation is not
+	   necessary. */
+	if (idr_pre_get(&lst->session_idr, GFP_KERNEL) == 0 ||
+	    idr_get_new(&lst->session_idr, sess, &sess->desc) != 0) {
+		mutex_unlock(&lst->session_idr_mutex);
+		goto err_sess;
+	}
+	mutex_unlock(&lst->session_idr_mutex);
+
+	return sess;
+
+err_sess:
+	kfree(sess->sg);
+	kfree(sess->pages);
+	kfree(sess);
+	return NULL;
+}
+
 static const struct algo_properties_st algo_properties[] = {
 #define KSTR(x) .kstr = x, .kstr_len = sizeof(x) - 1
 	{ .algo = NCR_ALG_NULL, KSTR("ecb(cipher_null)"),
@@ -142,9 +240,864 @@ const struct algo_properties_st *_ncr_nla_to_properties(const struct nlattr *nla
 	return NULL;
 }
 
+static const char *ncr_op_name(ncr_crypto_op_t op)
+{
+	static const char *const names[] = {
+		[NCR_OP_ENCRYPT] = "encrypt",
+		[NCR_OP_DECRYPT] = "decrypt",
+		[NCR_OP_SIGN] = "sign",
+		[NCR_OP_VERIFY] = "verify"
+	};
+
+	const char *res;
+
+	res = NULL;
+	if (op < ARRAY_SIZE(names))
+		res = names[op];
+	if (res == NULL)
+		res = "unknown";
+	return res;
+}
+
 const char *ncr_algorithm_name(const struct algo_properties_st *algo)
 {
 	if (algo != NULL && algo->kstr != NULL)
 		return algo->kstr;
 	return "unknown";
 }
+
+static int key_item_get_nla_read(struct key_item_st **st,
+				 struct ncr_lists *lists,
+				 const struct nlattr *nla)
+{
+	int ret;
+
+	if (nla == NULL) {
+		err();
+		return -EINVAL;
+	}
+	ret = ncr_key_item_get_read(st, lists, nla_get_u32(nla));
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+	return ret;
+}
+
+static int _ncr_session_init(struct ncr_lists *lists, ncr_crypto_op_t op,
+			     struct nlattr *tb[])
+{
+	const struct nlattr *nla;
+	struct session_item_st* ns = NULL;
+	int ret, audit_ret;
+	const struct algo_properties_st *sign_hash;
+
+	ns = ncr_session_new(lists);
+	if (ns == NULL) {
+		err();
+		return -ENOMEM;
+	}
+
+	ns->op = op;
+	ns->algorithm = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]);
+	if (ns->algorithm == NULL) {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+	
+	switch(op) {
+		case NCR_OP_ENCRYPT:
+		case NCR_OP_DECRYPT:
+			if (!ns->algorithm->can_encrypt) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			/* read key */
+			ret = key_item_get_nla_read(&ns->key, lists,
+						    tb[NCR_ATTR_KEY]);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			if (ns->key->type == NCR_KEY_TYPE_SECRET) {
+				int keysize = ns->key->key.secret.size;
+				
+				if (ns->algorithm->algo == NCR_ALG_NULL)
+				  keysize = 0;
+				
+				if (ns->algorithm->is_pk) {
+					err();
+					ret = -EINVAL;
+					goto fail;
+				}
+
+				ret = cryptodev_cipher_init(&ns->cipher, ns->algorithm->kstr,
+					ns->key->key.secret.data, keysize);
+				if (ret < 0) {
+					err();
+					goto fail;
+				}
+
+				if (ns->algorithm->needs_iv) {
+					nla = tb[NCR_ATTR_IV];
+					if (nla == NULL) {
+						err();
+						ret = -EINVAL;
+						goto fail;
+					}
+					cryptodev_cipher_set_iv(&ns->cipher,
+								nla_data(nla),
+								nla_len(nla));
+				}
+			} else if (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC) {
+				ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk, 
+							 tb, ns->key, NULL);
+				if (ret < 0) {
+					err();
+					goto fail;
+				}
+			} else {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+			break;
+
+		case NCR_OP_SIGN:
+		case NCR_OP_VERIFY:
+			if (!ns->algorithm->can_sign && !ns->algorithm->can_digest) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			if (ns->algorithm->can_digest) {
+				if (ns->algorithm->is_pk) {
+					err();
+					ret = -EINVAL;
+					goto fail;
+				}
+
+				ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 0, NULL, 0);
+				if (ret < 0) {
+					err();
+					goto fail;
+				}
+			
+			} else {
+				/* read key */
+				ret = key_item_get_nla_read(&ns->key, lists,
+							    tb[NCR_ATTR_KEY]);
+				if (ret < 0) {
+					err();
+					goto fail;
+				}
+
+				if (ns->algorithm->is_hmac && ns->key->type == NCR_KEY_TYPE_SECRET) {
+					if (ns->algorithm->is_pk) {
+						err();
+						ret = -EINVAL;
+						goto fail;
+					}
+
+					ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 1,
+						ns->key->key.secret.data, ns->key->key.secret.size);
+					if (ret < 0) {
+						err();
+						goto fail;
+					}
+
+				} else if (ns->algorithm->is_pk && (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC)) {
+					nla = tb[NCR_ATTR_SIGNATURE_HASH_ALGORITHM];
+					sign_hash = _ncr_nla_to_properties(nla);
+					if (sign_hash == NULL) {
+						err();
+						ret = -EINVAL;
+						goto fail;
+					}
+
+					if (!sign_hash->can_digest) {
+						err();
+						ret = -EINVAL;
+						goto fail;
+					}
+
+					if (sign_hash->is_pk) {
+						err();
+						ret = -EINVAL;
+						goto fail;
+					}
+
+					ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk, 
+						tb, ns->key, sign_hash);
+					if (ret < 0) {
+						err();
+						goto fail;
+					}
+
+					ret = cryptodev_hash_init(&ns->hash, sign_hash->kstr, 0, NULL, 0);
+					if (ret < 0) {
+						err();
+						goto fail;
+					}
+				} else {
+					err();
+					ret = -EINVAL;
+					goto fail;
+				}
+			}
+
+			break;
+		default:
+			err();
+			ret = -EINVAL;
+			goto fail;
+	}
+	
+	ret = ns->desc;
+
+fail:
+	// algorithm, params, op
+	audit_ret = audit_log_crypto_op(AUDIT_CRYPTO_OP_SESSION_INIT, lists->id,
+					ns->desc, ncr_op_name(ns->op),
+					ncr_algorithm_name(ns->algorithm),
+					ns->key != NULL ? ns->key->desc : -1,
+					ns->key != NULL ? ns->key->key_id : NULL,
+					ns->key != NULL ? ns->key->key_id_size : 0,
+					-1, NULL, 0);
+	if (audit_ret < 0 && ret == 0)
+		ret = audit_ret;
+	if (ret < 0) {
+		_ncr_session_remove(lists, ns->desc);
+	}
+	_ncr_sessions_item_put(ns);
+
+	return ret;
+}
+
+int ncr_session_init(struct ncr_lists *lists,
+		     const struct ncr_session_init *session,
+		     struct nlattr *tb[])
+{
+	return _ncr_session_init(lists, session->op, tb);
+}
+
+static int _ncr_session_encrypt(struct session_item_st* sess, const struct scatterlist* input, unsigned input_cnt,
+	size_t input_size, void *output, unsigned output_cnt, size_t *output_size)
+{
+int ret;
+
+	if (sess->algorithm->is_symmetric) {
+		/* read key */
+		ret = cryptodev_cipher_encrypt(&sess->cipher, input, 
+			output, input_size);
+		if (ret < 0) {
+			err();
+			return ret;
+		}
+		/* FIXME: handle ciphers that do not require that */
+		*output_size = input_size;
+	} else { /* public key */
+		ret = ncr_pk_cipher_encrypt(&sess->pk, input, input_cnt, input_size,
+			output, output_cnt, output_size);
+		
+		if (ret < 0) {
+			err();
+			return ret;
+		}
+	}
+	
+	return 0;
+}
+
+static int _ncr_session_decrypt(struct session_item_st* sess, const struct scatterlist* input, 
+	unsigned input_cnt, size_t input_size,
+	struct scatterlist *output, unsigned output_cnt, size_t *output_size)
+{
+int ret;
+
+	if (sess->algorithm->is_symmetric) {
+		/* read key */
+		ret = cryptodev_cipher_decrypt(&sess->cipher, input, 
+			output, input_size);
+		if (ret < 0) {
+			err();
+			return ret;
+		}
+		/* FIXME: handle ciphers that do not require equality */
+		*output_size = input_size;
+	} else { /* public key */
+		ret = ncr_pk_cipher_decrypt(&sess->pk, input, input_cnt, input_size,
+			output, output_cnt, output_size);
+		
+		if (ret < 0) {
+			err();
+			return ret;
+		}
+	}
+	
+	return 0;
+}
+
+static void _ncr_session_remove(struct ncr_lists *lst, ncr_session_t desc)
+{
+	struct session_item_st * item;
+
+	mutex_lock(&lst->session_idr_mutex);
+	item = idr_find(&lst->session_idr, desc);
+	if (item != NULL)
+		idr_remove(&lst->session_idr, desc); /* Steal the reference */
+	mutex_unlock(&lst->session_idr_mutex);
+
+	if (item != NULL)
+		_ncr_sessions_item_put(item);
+}
+
+static int _ncr_session_grow_pages(struct session_item_st *ses, int pagecount)
+{
+	struct scatterlist *sg;
+	struct page **pages;
+	int array_size;
+
+	if (likely(pagecount < ses->array_size))
+		return 0;
+
+	for (array_size = ses->array_size; array_size < pagecount;
+	     array_size *= 2)
+		;
+
+	dprintk(2, KERN_DEBUG, "%s: reallocating to %d elements\n",
+		__func__, array_size);
+	pages = krealloc(ses->pages, array_size * sizeof(struct page *),
+			 GFP_KERNEL);
+	if (unlikely(pages == NULL))
+		return -ENOMEM;
+	ses->pages = pages;
+	sg = krealloc(ses->sg, array_size * sizeof(struct scatterlist),
+		      GFP_KERNEL);
+	if (unlikely(sg == NULL))
+		return -ENOMEM;
+	ses->sg = sg;
+
+	ses->array_size = array_size;
+	return 0;
+}
+
+/* Make NCR_ATTR_UPDATE_INPUT_DATA and NCR_ATTR_UPDATE_OUTPUT_BUFFER available
+   in scatterlists */
+static int get_userbuf2(struct session_item_st *ses, struct nlattr *tb[],
+			struct scatterlist **src_sg, unsigned *src_cnt,
+			size_t *src_size, struct ncr_session_output_buffer *dst,
+			struct scatterlist **dst_sg, unsigned *dst_cnt,
+			int compat)
+{
+	const struct nlattr *src_nla, *dst_nla;
+	struct ncr_session_input_data src;
+	int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1, ret;
+	size_t input_size;
+
+	src_nla = tb[NCR_ATTR_UPDATE_INPUT_DATA];
+	dst_nla = tb[NCR_ATTR_UPDATE_OUTPUT_BUFFER];
+
+	ret = ncr_session_input_data_from_nla(&src, src_nla, compat);
+	if (unlikely(ret != 0)) {
+		err();
+		return ret;
+	}
+	*src_size = src.data_size;
+
+	if (dst_nla != NULL) {
+		ret = ncr_session_output_buffer_from_nla(dst, dst_nla, compat);
+		if (unlikely(ret != 0)) {
+			err();
+			return ret;
+		}
+	}
+
+	input_size = src.data_size;
+	src_pagecount = PAGECOUNT(src.data, input_size);
+
+	if (dst_nla == NULL || src.data != dst->buffer) {	/* non-in-situ transformation */
+		write_src = 0;
+		if (dst_nla != NULL) {
+			dst_pagecount = PAGECOUNT(dst->buffer,
+						  dst->buffer_size);
+		} else {
+			dst_pagecount = 0;
+		}
+	} else {
+		src_pagecount = max((int)(PAGECOUNT(dst->buffer,
+						    dst->buffer_size)),
+				    src_pagecount);
+		input_size = max(input_size, dst->buffer_size);
+	}
+
+	pagecount = src_pagecount + dst_pagecount;
+	ret = _ncr_session_grow_pages(ses, pagecount);
+	if (ret != 0) {
+		err();
+		return ret;
+	}
+
+	if (__get_userbuf((void __user *)src.data, input_size, write_src,
+			  src_pagecount, ses->pages, ses->sg)) {
+		err();
+		printk("write: %d\n", write_src);
+		return -EINVAL;
+	}
+	(*src_sg) = ses->sg;
+	*src_cnt = src_pagecount;
+
+	if (dst_pagecount) {
+		*dst_cnt = dst_pagecount;
+		(*dst_sg) = ses->sg + src_pagecount;
+
+		if (__get_userbuf(dst->buffer, dst->buffer_size, 1,
+				  dst_pagecount, ses->pages + src_pagecount,
+				  *dst_sg)) {
+			err();
+			release_user_pages(ses->pages, src_pagecount);
+			return -EINVAL;
+		}
+	} else {
+		if (dst_nla != NULL) {
+			*dst_cnt = src_pagecount;
+			(*dst_sg) = (*src_sg);
+		} else {
+			*dst_cnt = 0;
+			*dst_sg = NULL;
+		}
+	}
+	
+	ses->available_pages = pagecount;
+
+	return 0;
+}
+
+/* Called when userspace buffers are used */
+static int _ncr_session_update(struct ncr_lists *lists, ncr_session_t ses,
+			       struct nlattr *tb[], int compat)
+{
+	int ret;
+	struct session_item_st* sess;
+	struct scatterlist *isg = NULL;
+	struct scatterlist *osg = NULL;
+	unsigned osg_cnt=0, isg_cnt=0;
+	size_t isg_size = 0, osg_size;
+	struct ncr_session_output_buffer out;
+
+	sess = ncr_sessions_item_get(lists, ses);
+	if (sess == NULL) {
+		err();
+		return -EINVAL;
+	}
+
+	if (mutex_lock_interruptible(&sess->mem_mutex)) {
+		err();
+		_ncr_sessions_item_put(sess);
+		return -ERESTARTSYS;
+	}
+
+	ret = get_userbuf2(sess, tb, &isg, &isg_cnt, &isg_size, &out, &osg,
+			   &osg_cnt, compat);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+
+	switch(sess->op) {
+		case NCR_OP_ENCRYPT:
+			if (osg == NULL) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			osg_size = out.buffer_size;
+			if (osg_size < isg_size) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			ret = _ncr_session_encrypt(sess, isg, isg_cnt, isg_size, 
+				osg, osg_cnt, &osg_size);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+
+			ret = ncr_session_output_buffer_set_size(&out, osg_size,
+								 compat);
+			if (ret != 0) {
+				err();
+				goto fail;
+			}
+			break;
+		case NCR_OP_DECRYPT:
+			if (osg == NULL) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			osg_size = out.buffer_size;
+			if (osg_size < isg_size) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			ret = _ncr_session_decrypt(sess, isg, isg_cnt, isg_size, 
+				osg, osg_cnt, &osg_size);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+
+			ret = ncr_session_output_buffer_set_size(&out, osg_size,
+								 compat);
+			if (ret != 0) {
+				err();
+				goto fail;
+			}
+			break;
+
+		case NCR_OP_SIGN:
+		case NCR_OP_VERIFY:
+			ret = cryptodev_hash_update(&sess->hash, isg, isg_size);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			break;
+		default:
+			err();
+			ret = -EINVAL;
+			goto fail;
+	}
+
+	ret = 0;
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_SESSION_OP, lists->id, sess->desc,
+			    ncr_op_name(sess->op),
+			    ncr_algorithm_name(sess->algorithm), -1, NULL, 0,
+			    -1, NULL, 0);
+
+	if (sess->available_pages) {
+		release_user_pages(sess->pages, sess->available_pages);
+		sess->available_pages = 0;
+	}
+	mutex_unlock(&sess->mem_mutex);
+	_ncr_sessions_item_put(sess);
+
+	return ret;
+}
+
+static int try_session_update(struct ncr_lists *lists, ncr_session_t ses,
+			      struct nlattr *tb[], int compat)
+{
+	if (tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] != NULL)
+		return _ncr_session_update_key(lists, ses, tb);
+	else if (tb[NCR_ATTR_UPDATE_INPUT_DATA] != NULL)
+		return _ncr_session_update(lists, ses, tb, compat);
+
+	return 0;
+}
+
+static int _ncr_session_final(struct ncr_lists *lists, ncr_session_t ses,
+			      struct nlattr *tb[], int compat)
+{
+	const struct nlattr *nla;
+	int ret;
+	struct session_item_st* sess;
+	int digest_size;
+	uint8_t digest[NCR_HASH_MAX_OUTPUT_SIZE];
+	void *buffer = NULL;
+
+	sess = ncr_sessions_item_get(lists, ses);
+	if (sess == NULL) {
+		err();
+		return -EINVAL;
+	}
+
+	ret = try_session_update(lists, ses, tb, compat);
+	if (ret < 0) {
+		err();
+		_ncr_sessions_item_put(sess);
+		return ret;
+	}
+
+	if (mutex_lock_interruptible(&sess->mem_mutex)) {
+		err();
+		_ncr_sessions_item_put(sess);
+		return -ERESTARTSYS;
+	}
+
+	switch(sess->op) {
+	case NCR_OP_ENCRYPT:
+	case NCR_OP_DECRYPT:
+		break;
+	case NCR_OP_VERIFY: {
+		struct ncr_session_input_data src;
+
+		nla = tb[NCR_ATTR_FINAL_INPUT_DATA];
+		ret = ncr_session_input_data_from_nla(&src, nla, compat);
+		if (unlikely(ret != 0)) {
+			err();
+			goto fail;
+		}
+
+		buffer = kmalloc(src.data_size, GFP_KERNEL);
+		if (buffer == NULL) {
+			err();
+			ret = -ENOMEM;
+			goto fail;
+		}
+		if (unlikely(copy_from_user(buffer, src.data, src.data_size))) {
+			err();
+			ret = -EFAULT;
+			goto fail;
+		}
+
+		digest_size = sess->hash.digestsize;
+		if (digest_size == 0 || sizeof(digest) < digest_size) {
+			err();
+			ret = -EINVAL;
+			goto fail;
+		}
+		ret = cryptodev_hash_final(&sess->hash, digest);
+		if (ret < 0) {
+			err();
+			goto fail;
+		}
+
+		if (!sess->algorithm->is_pk)
+			ret = (digest_size == src.data_size
+			       && memcmp(buffer, digest, digest_size) == 0);
+		else {
+			ret = ncr_pk_cipher_verify(&sess->pk, buffer,
+						   src.data_size, digest,
+						   digest_size);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+		}
+		break;
+	}
+
+	case NCR_OP_SIGN: {
+		struct ncr_session_output_buffer dst;
+		size_t output_size;
+
+		nla = tb[NCR_ATTR_FINAL_OUTPUT_BUFFER];
+		ret = ncr_session_output_buffer_from_nla(&dst, nla, compat);
+		if (unlikely(ret != 0)) {
+			err();
+			goto fail;
+		}
+
+		digest_size = sess->hash.digestsize;
+		if (digest_size == 0) {
+			err();
+			ret = -EINVAL;
+			goto fail;
+		}
+
+		ret = cryptodev_hash_final(&sess->hash, digest);
+		if (ret < 0) {
+			err();
+			goto fail;
+		}
+
+		cryptodev_hash_deinit(&sess->hash);
+
+		if (!sess->algorithm->is_pk) {
+			if (dst.buffer_size < digest_size) {
+				err();
+				ret = -ERANGE;
+				goto fail;
+			}
+			if (unlikely(copy_to_user(dst.buffer, digest,
+						  digest_size))) {
+				err();
+				ret = -EFAULT;
+				goto fail;
+			}
+			output_size = digest_size;
+		} else {
+			output_size = dst.buffer_size;
+			buffer = kmalloc(output_size, GFP_KERNEL);
+			if (buffer == NULL) {
+				err();
+				ret = -ENOMEM;
+				goto fail;
+			}
+			ret = ncr_pk_cipher_sign(&sess->pk, digest, digest_size,
+						 buffer, &output_size);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			if (unlikely(copy_to_user(dst.buffer, buffer,
+						  output_size))) {
+				err();
+				ret = -EFAULT;
+				goto fail;
+			}
+		}
+
+		ret = ncr_session_output_buffer_set_size(&dst, output_size,
+							 compat);
+		if (ret != 0) {
+			err();
+			goto fail;
+		}
+		break;
+	}
+	default:
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_SESSION_FINAL, lists->id,
+			    sess->desc, ncr_op_name(sess->op),
+			    ncr_algorithm_name(sess->algorithm), -1, NULL, 0,
+			    -1, NULL, 0);
+
+	kfree(buffer);
+	mutex_unlock(&sess->mem_mutex);
+
+	cryptodev_hash_deinit(&sess->hash);
+	if (sess->algorithm->is_symmetric) {
+		cryptodev_cipher_deinit(&sess->cipher);
+	} else {
+		ncr_pk_cipher_deinit(&sess->pk);
+	}
+
+	_ncr_sessions_item_put(sess);
+	_ncr_session_remove(lists, ses);
+
+	return ret;
+}
+
+/* Direct with key: Allows to hash a key */
+static int _ncr_session_update_key(struct ncr_lists *lists, ncr_session_t ses,
+				   struct nlattr *tb[])
+{
+	int ret;
+	struct session_item_st* sess;
+	struct key_item_st* key = NULL;
+
+	sess = ncr_sessions_item_get(lists, ses);
+	if (sess == NULL) {
+		err();
+		return -EINVAL;
+	}
+
+	/* read key */
+	ret = key_item_get_nla_read(&key, lists,
+				    tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA]);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+	
+	if (key->type != NCR_KEY_TYPE_SECRET) {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	switch(sess->op) {
+		case NCR_OP_ENCRYPT:
+		case NCR_OP_DECRYPT:
+			err();
+			ret = -EINVAL;
+			goto fail;
+		case NCR_OP_SIGN:
+		case NCR_OP_VERIFY:
+			ret = _cryptodev_hash_update(&sess->hash, 
+				key->key.secret.data, key->key.secret.size);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			break;
+		default:
+			err();
+			ret = -EINVAL;
+			goto fail;
+	}
+
+	ret = 0;
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_SESSION_OP, lists->id, sess->desc,
+			    ncr_op_name(sess->op),
+			    ncr_algorithm_name(sess->algorithm),
+			    key != NULL ? key->desc : -1,
+			    key != NULL ? key->key_id : NULL,
+			    key != NULL ? key->key_id_size : 0, -1, NULL, 0);
+
+	if (key) _ncr_key_item_put(key);
+	_ncr_sessions_item_put(sess);
+
+	return ret;
+}
+
+int ncr_session_update(struct ncr_lists *lists,
+		       const struct ncr_session_update *op, struct nlattr *tb[],
+		       int compat)
+{
+	int ret;
+
+	if (tb[NCR_ATTR_UPDATE_INPUT_DATA] != NULL)
+		ret = _ncr_session_update(lists, op->ses, tb, compat);
+	else if (tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] != NULL)
+		ret = _ncr_session_update_key(lists, op->ses, tb);
+	else
+		ret = -EINVAL;
+
+	if (unlikely(ret)) {
+		err();
+		return ret;
+	}
+
+	return 0;
+}
+
+int ncr_session_final(struct ncr_lists *lists,
+		      const struct ncr_session_final *op, struct nlattr *tb[],
+		      int compat)
+{
+	return _ncr_session_final(lists, op->ses, tb, compat);
+}
+
+int ncr_session_once(struct ncr_lists *lists,
+		     const struct ncr_session_once *once, struct nlattr *tb[],
+		     int compat)
+{
+	int ret;
+
+	ret = _ncr_session_init(lists, once->op, tb);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	ret = _ncr_session_final(lists, ret, tb, compat);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	return ret;
+}
diff --git a/crypto/userspace/ncr.c b/crypto/userspace/ncr.c
index 1838aab..9fb56ad 100644
--- a/crypto/userspace/ncr.c
+++ b/crypto/userspace/ncr.c
@@ -22,8 +22,94 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
+#include <linux/audit.h>
+#include <linux/compat.h>
+#include <linux/crypto.h>
+#include <linux/ioctl.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/highmem.h>
+#include <linux/random.h>
+#include <linux/uaccess.h>
+#include <linux/scatterlist.h>
+#include <linux/cred.h>  
+#include <linux/capability.h>
+#include <linux/ncr.h>
+#include <net/netlink.h>
 #include "ncr-int.h"
+#include "utils.h"
+#include <linux/workqueue.h>
 
 /* This is the master wrapping key for storage of keys
  */
 struct key_item_st master_key;
+
+int ncr_session_input_data_from_nla(struct ncr_session_input_data *dest,
+				    const struct nlattr *nla, int compat)
+{
+	if (unlikely(nla == NULL))
+		return -EINVAL;
+#ifdef CONFIG_COMPAT
+	if (!compat) {
+#endif
+		if (unlikely(nla_len(nla) < sizeof(dest)))
+			return -ERANGE; /* nla_validate would return -ERANGE. */
+		memcpy(dest, nla_data(nla), sizeof(*dest));
+#ifdef CONFIG_COMPAT
+	} else {
+		struct compat_ncr_session_input_data old;
+
+		if (unlikely(nla_len(nla) < sizeof(old)))
+			return -ERANGE;
+		memcpy(&old, nla_data(nla), sizeof(old));
+		dest->data = compat_ptr(old.data);
+		dest->data_size = old.data_size;
+	}
+#endif
+	return 0;
+}
+
+int ncr_session_output_buffer_from_nla(struct ncr_session_output_buffer *dest,
+				       const struct nlattr *nla, int compat)
+{
+	if (unlikely(nla == NULL))
+		return -EINVAL;
+#ifdef CONFIG_COMPAT
+	if (!compat) {
+#endif
+		if (unlikely(nla_len(nla) < sizeof(dest)))
+			return -ERANGE; /* nla_validate would return -ERANGE. */
+		memcpy(dest, nla_data(nla), sizeof(*dest));
+#ifdef CONFIG_COMPAT
+	} else {
+		struct compat_ncr_session_output_buffer old;
+
+		if (unlikely(nla_len(nla) < sizeof(old)))
+			return -ERANGE;
+		memcpy(&old, nla_data(nla), sizeof(old));
+		dest->buffer = compat_ptr(old.buffer);
+		dest->buffer_size = old.buffer_size;
+		dest->result_size_ptr = compat_ptr(old.result_size_ptr);
+	}
+#endif
+	return 0;
+}
+
+
+int ncr_session_output_buffer_set_size(const struct ncr_session_output_buffer *dest,
+				       size_t size, int compat)
+{
+#ifdef CONFIG_COMPAT
+	if (!compat)
+#endif
+		return put_user(size, dest->result_size_ptr);
+#ifdef CONFIG_COMPAT
+	else {
+		compat_size_t old;
+
+		old = size;
+		return put_user(old,
+				(compat_size_t __user *)dest->result_size_ptr);
+	}
+#endif
+}
-- 
1.7.2.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ