All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Miloslav Trmač" <mitr@redhat.com>
To: Herbert Xu <herbert@gondor.hengli.com.au>
Cc: linux-crypto@vger.kernel.org,
	"Nikos Mavrogiannopoulos" <n.mavrogiannopoulos@gmail.com>,
	"Neil Horman" <nhorman@redhat.com>,
	linux-kernel@vger.kernel.org, "Miloslav Trmač" <mitr@redhat.com>
Subject: [PATCH 17/19] Add session operations
Date: Fri, 20 Aug 2010 10:46:01 +0200	[thread overview]
Message-ID: <1282293963-27807-19-git-send-email-mitr@redhat.com> (raw)
In-Reply-To: <1282293963-27807-1-git-send-email-mitr@redhat.com>

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

  parent reply	other threads:[~2010-08-20  8:46 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-08-20  8:45 [PATCH 00/19] RFC, v2: "New" /dev/crypto user-space interface Miloslav Trmač
2010-08-20  8:45 ` [PATCH 01/19] User-space API definition Miloslav Trmač
2010-08-20 12:48   ` Stefan Richter
2010-08-21  7:35     ` Nikos Mavrogiannopoulos
2010-08-21  9:11     ` Miloslav Trmac
2010-08-20 17:12   ` Randy Dunlap
2010-08-21 13:09   ` Kyle Moffett
2010-08-21 14:54     ` Nikos Mavrogiannopoulos
2010-08-22 10:22       ` David Howells
2010-09-03  9:18   ` Herbert Xu
2010-09-03  9:18     ` Herbert Xu
2010-09-03  9:34     ` Nikos Mavrogiannopoulos
2010-09-03  9:34       ` Nikos Mavrogiannopoulos
2010-09-03 15:20     ` Nikos Mavrogiannopoulos
2010-09-03 15:20       ` Nikos Mavrogiannopoulos
2010-08-20  8:45 ` [PATCH 02/19] Add CRYPTO_USERSPACE config option Miloslav Trmač
2010-08-20  8:45 ` [PATCH 03/19] Add libtommath headers Miloslav Trmač
2010-08-20  8:45 ` [PATCH 04/19] Add libtomcrypt headers Miloslav Trmač
2010-08-20  8:45 ` [PATCH 05/19] Add internal /dev/crypto implementation headers Miloslav Trmač
2010-08-20  8:45 ` [PATCH 06/19] Add ioctl() argument and attribute handling utils Miloslav Trmač
2010-08-20 12:59   ` Stefan Richter
2010-08-21  2:15     ` Miloslav Trmac
2010-08-21  7:15       ` Stefan Richter
2010-08-20  8:45 ` [PATCH 07/19] Add crypto API utilities Miloslav Trmač
2010-08-20  8:45 ` [PATCH 08/19] Add per-process and per-user limits Miloslav Trmač
2010-08-20  8:45 ` [PATCH 09/19] Add libtommath implementation Miloslav Trmač
2010-08-20  8:45 ` [PATCH 10/19] Add libtomcrypt implementation Miloslav Trmač
2010-08-20  8:45 ` [PATCH 10/19] Add libtommath implementation Miloslav Trmač
2010-08-20  8:45 ` [PATCH 11/19] Add algorithm properties table Miloslav Trmač
2010-08-20  8:45 ` [PATCH 12/19] Add DH implementation and pubkey abstraction layer Miloslav Trmač
2010-08-20  8:45 ` [PATCH 13/19] Add /dev/crypto auditing infrastructure Miloslav Trmač
2010-08-20  8:45 ` [PATCH 14/19] Add most operations on key objects Miloslav Trmač
2010-08-20  8:45 ` [PATCH 15/19] Add key wrapping operations Miloslav Trmač
2010-08-20  8:46 ` [PATCH 16/19] Add helpers for zero-copy userspace access Miloslav Trmač
2010-08-20  8:46 ` Miloslav Trmač [this message]
2010-08-20  8:46 ` [PATCH 18/19] Add ioctl handlers Miloslav Trmač
2010-08-20  8:46 ` [PATCH 19/19] Finally, add the /dev/crypto device Miloslav Trmač
2010-08-20 13:56 ` [PATCH 00/19] RFC, v2: "New" /dev/crypto user-space interface Ted Ts'o
2010-08-20 17:03   ` Nikos Mavrogiannopoulos
2010-08-20 17:03     ` Nikos Mavrogiannopoulos
2010-08-20 23:48     ` Ted Ts'o
2010-08-23  6:39       ` Tomas Mraz
2010-08-21 17:08 ` Arnd Bergmann
2010-08-22  7:52   ` Nikos Mavrogiannopoulos
2010-08-23  8:09     ` Arnd Bergmann
2010-08-23  9:34       ` Nikos Mavrogiannopoulos
2010-08-25  6:20 ` Pavel Machek
2010-08-25  6:44   ` Tomas Mraz
2010-08-25 15:28   ` Miloslav Trmac

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1282293963-27807-19-git-send-email-mitr@redhat.com \
    --to=mitr@redhat.com \
    --cc=herbert@gondor.hengli.com.au \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=n.mavrogiannopoulos@gmail.com \
    --cc=nhorman@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.