summaryrefslogtreecommitdiffstats
path: root/crypto/userspace/ncr-sessions.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/userspace/ncr-sessions.c')
-rw-r--r--crypto/userspace/ncr-sessions.c1031
1 files changed, 648 insertions, 383 deletions
diff --git a/crypto/userspace/ncr-sessions.c b/crypto/userspace/ncr-sessions.c
index f3d3590359f..e4af261dca5 100644
--- a/crypto/userspace/ncr-sessions.c
+++ b/crypto/userspace/ncr-sessions.c
@@ -29,9 +29,42 @@
#include "ncr-int.h"
#include <linux/mm_types.h>
#include <linux/scatterlist.h>
+#include <net/netlink.h>
+
+struct session_item_st {
+ atomic_t refcnt;
+ /* Constant values throughout the life of this object follow. */
+ ncr_session_t desc;
+ const struct algo_properties_st *algorithm;
+ ncr_crypto_op_t op;
+ struct key_item_st *key;
+
+ /* Variable values, protected usually by mutex, follow. */
+ struct mutex mutex;
+
+ /* contexts for various options.
+ * simpler to have them like that than
+ * in a union.
+ */
+ struct cipher_data cipher;
+ struct ncr_pk_ctx pk;
+ struct hash_data hash;
+ /* This is a hack, ideally we'd have a hash algorithm that simply
+ outputs its input as a digest. We'd still need to distinguish
+ between the hash to identify in the signature and the hash to
+ actually use, though. */
+ void *transparent_hash;
-static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_op_st* op);
-static void _ncr_session_remove(struct ncr_lists *lst, ncr_session_t desc);
+ struct scatterlist *sg;
+ struct page **pages;
+ unsigned array_size;
+ unsigned available_pages;
+};
+
+static void _ncr_sessions_item_put(struct session_item_st *item);
+static int _ncr_session_update_key(struct ncr_lists *lists,
+ struct session_item_st *sess,
+ struct nlattr *tb[]);
static int session_list_deinit_fn(int id, void *item, void *unused)
{
@@ -51,12 +84,57 @@ void ncr_sessions_list_deinit(struct ncr_lists *lst)
mutex_unlock(&lst->session_idr_mutex);
}
+/* Allocate a descriptor without making a sesssion available to userspace. */
+static ncr_session_t session_alloc_desc(struct ncr_lists *lst)
+{
+ int ret, desc;
+
+ mutex_lock(&lst->session_idr_mutex);
+ if (idr_pre_get(&lst->session_idr, GFP_KERNEL) == 0) {
+ ret = -ENOMEM;
+ goto end;
+ }
+ /* 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. */
+ ret = idr_get_new(&lst->session_idr, NULL, &desc);
+ if (ret != 0)
+ goto end;
+ ret = desc;
+end:
+ mutex_unlock(&lst->session_idr_mutex);
+ return ret;
+}
+
+/* Drop a pre-allocated, unpublished session descriptor */
+static void session_drop_desc(struct ncr_lists *lst, ncr_session_t desc)
+{
+ mutex_lock(&lst->session_idr_mutex);
+ idr_remove(&lst->session_idr, desc);
+ mutex_unlock(&lst->session_idr_mutex);
+}
+
+/* Make a session descriptor visible in user-space, stealing the reference */
+static void session_publish_ref(struct ncr_lists *lst,
+ struct session_item_st *sess)
+{
+ void *old;
+
+ mutex_lock(&lst->session_idr_mutex);
+ old = idr_replace(&lst->session_idr, sess, sess->desc);
+ mutex_unlock(&lst->session_idr_mutex);
+ BUG_ON(old != NULL);
+}
+
/* returns the data item corresponding to desc */
-struct session_item_st* ncr_sessions_item_get(struct ncr_lists *lst, ncr_session_t desc)
+static struct session_item_st *session_get_ref(struct ncr_lists *lst,
+ ncr_session_t desc)
{
struct session_item_st* item;
mutex_lock(&lst->session_idr_mutex);
+ /* item may be NULL for pre-allocated session IDs. */
item = idr_find(&lst->session_idr, desc);
if (item != NULL) {
atomic_inc(&item->refcnt);
@@ -69,12 +147,30 @@ struct session_item_st* item;
return NULL;
}
-void _ncr_sessions_item_put( struct session_item_st* item)
+/* Find a session, stealing the reference, but keep the descriptor allocated. */
+static struct session_item_st *session_unpublish_ref(struct ncr_lists *lst,
+ ncr_session_t desc)
+{
+ struct session_item_st *sess;
+
+ mutex_lock(&lst->session_idr_mutex);
+ /* sess may be NULL for pre-allocated session IDs. */
+ sess = idr_replace(&lst->session_idr, NULL, desc);
+ mutex_unlock(&lst->session_idr_mutex);
+ if (sess != NULL && !IS_ERR(sess))
+ return sess;
+
+ err();
+ return NULL;
+}
+
+static 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);
+ kfree(item->transparent_hash);
if (item->key)
_ncr_key_item_put(item->key);
kfree(item->sg);
@@ -83,7 +179,7 @@ void _ncr_sessions_item_put( struct session_item_st* item)
}
}
-struct session_item_st* ncr_session_new(struct ncr_lists *lst)
+static struct session_item_st *ncr_session_new(ncr_session_t desc)
{
struct session_item_st* sess;
@@ -102,21 +198,10 @@ struct session_item_st* ncr_session_new(struct ncr_lists *lst)
err();
goto err_sess;
}
- mutex_init(&sess->mem_mutex);
-
- atomic_set(&sess->refcnt, 2); /* One for lst->list, one for "sess" */
+ mutex_init(&sess->mutex);
- 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);
+ atomic_set(&sess->refcnt, 1);
+ sess->desc = desc;
return sess;
@@ -128,113 +213,210 @@ err_sess:
}
static const struct algo_properties_st algo_properties[] = {
- { .algo = NCR_ALG_NULL, .kstr = "ecb(cipher_null)",
+#define KSTR(x) .kstr = x, .kstr_len = sizeof(x) - 1
+ { .algo = NCR_ALG_NULL, KSTR("ecb(cipher_null)"),
.needs_iv = 0, .is_symmetric=1, .can_encrypt=1,
.key_type = NCR_KEY_TYPE_INVALID },
- { .algo = NCR_ALG_3DES_CBC, .kstr = "cbc(des3_ede)",
+ { .algo = NCR_ALG_3DES_CBC, KSTR("cbc(des3_ede)"),
.needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_AES_CBC, .kstr = "cbc(aes)",
+ { KSTR("cbc(aes)"),
.needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_CAMELLIA_CBC, .kstr = "cbc(camelia)",
+ { KSTR("cbc(camelia)"),
.needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_AES_CTR, .kstr = "ctr(aes)",
+ { KSTR("ctr(aes)"),
.needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_CAMELLIA_CTR, .kstr = "ctr(camelia)",
+ { KSTR("ctr(camelia)"),
.needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_ARCFOUR, .kstr = NULL,
+ { KSTR("ecb(aes)"),
.needs_iv = 0, .is_symmetric=1, .can_encrypt=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_AES_ECB, .kstr = "ecb(aes)",
+ { KSTR("ecb(camelia)"),
.needs_iv = 0, .is_symmetric=1, .can_encrypt=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_CAMELLIA_ECB, .kstr = "ecb(camelia)",
- .needs_iv = 0, .is_symmetric=1, .can_encrypt=1,
- .key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_SHA1, .kstr = "sha1",
+ { .algo = NCR_ALG_SHA1, KSTR("sha1"),
.digest_size = 20, .can_digest=1,
.key_type = NCR_KEY_TYPE_INVALID },
- { .algo = NCR_ALG_MD5, .kstr = "md5",
+ { .algo = NCR_ALG_MD5, KSTR("md5"),
.digest_size = 16, .can_digest=1,
.key_type = NCR_KEY_TYPE_INVALID },
- { .algo = NCR_ALG_SHA2_224, .kstr = "sha224",
+ { .algo = NCR_ALG_SHA2_224, KSTR("sha224"),
.digest_size = 28, .can_digest=1,
.key_type = NCR_KEY_TYPE_INVALID },
- { .algo = NCR_ALG_SHA2_256, .kstr = "sha256",
+ { .algo = NCR_ALG_SHA2_256, KSTR("sha256"),
.digest_size = 32, .can_digest=1,
.key_type = NCR_KEY_TYPE_INVALID },
- { .algo = NCR_ALG_SHA2_384, .kstr = "sha384",
+ { .algo = NCR_ALG_SHA2_384, KSTR("sha384"),
.digest_size = 48, .can_digest=1,
.key_type = NCR_KEY_TYPE_INVALID },
- { .algo = NCR_ALG_SHA2_512, .kstr = "sha512",
+ { .algo = NCR_ALG_SHA2_512, KSTR("sha512"),
.digest_size = 64, .can_digest=1,
.key_type = NCR_KEY_TYPE_INVALID },
- { .algo = NCR_ALG_HMAC_SHA1, .is_hmac = 1, .kstr = "hmac(sha1)",
+ { .is_hmac = 1, KSTR("hmac(sha1)"),
.digest_size = 20, .can_sign=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_HMAC_MD5, .is_hmac = 1, .kstr = "hmac(md5)",
+ { .is_hmac = 1, KSTR("hmac(md5)"),
.digest_size = 16, .can_sign=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_HMAC_SHA2_224, .is_hmac = 1, .kstr = "hmac(sha224)",
+ { .is_hmac = 1, KSTR("hmac(sha224)"),
.digest_size = 28, .can_sign=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_HMAC_SHA2_256, .is_hmac = 1, .kstr = "hmac(sha256)",
+ { .is_hmac = 1, KSTR("hmac(sha256)"),
.digest_size = 32, .can_sign=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_HMAC_SHA2_384, .is_hmac = 1, .kstr = "hmac(sha384)",
+ { .is_hmac = 1, KSTR("hmac(sha384)"),
.digest_size = 48, .can_sign=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_HMAC_SHA2_512, .is_hmac = 1, .kstr = "hmac(sha512)",
+ { .is_hmac = 1, KSTR("hmac(sha512)"),
.digest_size = 64, .can_sign=1,
.key_type = NCR_KEY_TYPE_SECRET },
- { .algo = NCR_ALG_RSA, .kstr = NULL, .is_pk = 1,
+ /* NOTE: These algorithm names are not available through the kernel API
+ (yet). */
+ { .algo = NCR_ALG_RSA, KSTR("rsa"), .is_pk = 1,
.can_encrypt=1, .can_sign=1, .key_type = NCR_KEY_TYPE_PUBLIC },
- { .algo = NCR_ALG_DSA, .kstr = NULL, .is_pk = 1,
+ { .algo = NCR_ALG_RSA, KSTR(NCR_ALG_RSA_TRANSPARENT_HASH), .is_pk = 1,
+ .can_encrypt=1, .can_sign=1, .has_transparent_hash = 1,
+ .key_type = NCR_KEY_TYPE_PUBLIC },
+ { .algo = NCR_ALG_DSA, KSTR("dsa"), .is_pk = 1,
.can_sign=1, .key_type = NCR_KEY_TYPE_PUBLIC },
- { .algo = NCR_ALG_DH, .kstr = NULL, .is_pk = 1,
+ { .algo = NCR_ALG_DSA, KSTR(NCR_ALG_DSA_TRANSPARENT_HASH), .is_pk = 1,
+ .can_sign=1, .has_transparent_hash = 1,
+ .key_type = NCR_KEY_TYPE_PUBLIC },
+ { .algo = NCR_ALG_DH, KSTR("dh"), .is_pk = 1,
.can_kx=1, .key_type = NCR_KEY_TYPE_PUBLIC },
- { .algo = NCR_ALG_NONE }
-
+#undef KSTR
};
-const struct algo_properties_st *_ncr_algo_to_properties(ncr_algorithm_t algo)
+/* The lookups by string are inefficient - can we look up all we need from
+ crypto API? */
+const struct algo_properties_st *_ncr_algo_to_properties(const char *algo)
{
- ncr_algorithm_t a;
- int i = 0;
-
- for (i = 0; (a = algo_properties[i].algo) != NCR_ALG_NONE; i++) {
- if (a == algo)
- return &algo_properties[i];
+ const struct algo_properties_st *a;
+ size_t name_len;
+
+ name_len = strlen(algo);
+ for (a = algo_properties;
+ a < algo_properties + ARRAY_SIZE(algo_properties); a++) {
+ if (a->kstr_len == name_len
+ && memcmp(a->kstr, algo, name_len) == 0)
+ return a;
}
return NULL;
}
-static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* session)
+const struct algo_properties_st *_ncr_nla_to_properties(const struct nlattr *nla)
+{
+ const struct algo_properties_st *a;
+ size_t name_len;
+
+ if (nla == NULL)
+ return NULL;
+
+ /* nla_len() >= 1 ensured by validate_nla() case NLA_NUL_STRING */
+ name_len = nla_len(nla) - 1;
+ for (a = algo_properties;
+ a < algo_properties + ARRAY_SIZE(algo_properties); a++) {
+ if (a->kstr_len == name_len
+ && memcmp(a->kstr, nla_data(nla), name_len + 1) == 0)
+ return a;
+ }
+ return NULL;
+}
+
+static int key_item_get_nla_read(struct key_item_st **st,
+ struct ncr_lists *lists,
+ const struct nlattr *nla)
{
- struct session_item_st* ns = NULL;
int ret;
- const struct algo_properties_st *sign_hash;
- ns = ncr_session_new(lists);
- if (ns == NULL) {
+ if (nla == NULL) {
err();
- return -ENOMEM;
+ return -EINVAL;
+ }
+ ret = ncr_key_item_get_read(st, lists, nla_get_u32(nla));
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+ return ret;
+}
+
+/* The caller is responsible for locking of "session". old_session must not be
+ locked on entry. */
+static int
+init_or_clone_hash(struct session_item_st *session,
+ struct session_item_st *old_session,
+ const struct algo_properties_st *algo,
+ const void *mac_key, size_t mac_key_size)
+{
+ int ret;
+
+ if (old_session == NULL)
+ return cryptodev_hash_init(&session->hash, algo->kstr,
+ mac_key, mac_key_size);
+
+ if (mutex_lock_interruptible(&old_session->mutex)) {
+ err();
+ return -ERESTARTSYS;
}
+ ret = cryptodev_hash_clone(&session->hash, &old_session->hash, mac_key,
+ mac_key_size);
+ mutex_unlock(&old_session->mutex);
+
+ return ret;
+}
- ns->op = session->op;
- ns->algorithm = _ncr_algo_to_properties(session->algorithm);
- if (ns->algorithm == NULL) {
+static struct session_item_st *_ncr_session_init(struct ncr_lists *lists,
+ ncr_session_t desc,
+ ncr_crypto_op_t op,
+ struct nlattr *tb[])
+{
+ const struct nlattr *nla;
+ struct session_item_st *ns, *old_session = NULL;
+ int ret;
+ const struct algo_properties_st *sign_hash;
+
+ ns = ncr_session_new(desc);
+ if (ns == NULL) {
err();
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-ENOMEM);
}
+ /* ns is the only reference throughout this function, so no locking
+ is necessary. */
+
+ ns->op = op;
+ nla = tb[NCR_ATTR_SESSION_CLONE_FROM];
+ if (nla != NULL) {
+ /* "ns" is not visible to userspace, so this is safe. */
+ old_session = session_get_ref(lists, nla_get_u32(nla));
+ if (old_session == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (ns->op != old_session->op) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
+
+ if (old_session == NULL) {
+ ns->algorithm = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]);
+ if (ns->algorithm == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ } else
+ ns->algorithm = old_session->algorithm;
- switch(session->op) {
+ switch(op) {
case NCR_OP_ENCRYPT:
case NCR_OP_DECRYPT:
if (!ns->algorithm->can_encrypt) {
@@ -243,19 +425,35 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses
goto fail;
}
+ if (old_session != NULL) {
+ err();
+ ret = -EOPNOTSUPP;
+ goto fail;
+ }
+
/* read key */
- ret = ncr_key_item_get_read( &ns->key, lists, session->key);
+ ret = key_item_get_nla_read(&ns->key, lists,
+ tb[NCR_ATTR_KEY]);
if (ret < 0) {
err();
goto fail;
}
+
+ /* wrapping keys cannot be used for encryption or decryption
+ */
+ if (ns->key->flags & NCR_KEY_FLAG_WRAPPING || ns->key->flags & NCR_KEY_FLAG_UNWRAPPING) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
if (ns->key->type == NCR_KEY_TYPE_SECRET) {
int keysize = ns->key->key.secret.size;
- if (session->algorithm == NCR_ALG_NULL)
+ if (ns->algorithm->algo == NCR_ALG_NULL)
keysize = 0;
- if (ns->algorithm->kstr == NULL) {
+ if (ns->algorithm->is_pk) {
err();
ret = -EINVAL;
goto fail;
@@ -269,16 +467,19 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses
}
if (ns->algorithm->needs_iv) {
- if (session->params.params.cipher.iv_size > sizeof(session->params.params.cipher.iv)) {
+ nla = tb[NCR_ATTR_IV];
+ if (nla == NULL) {
err();
ret = -EINVAL;
goto fail;
}
- cryptodev_cipher_set_iv(&ns->cipher, session->params.params.cipher.iv, session->params.params.cipher.iv_size);
+ 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,
- &session->params, ns->key, NULL);
+ tb, ns->key, NULL);
if (ret < 0) {
err();
goto fail;
@@ -299,34 +500,51 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses
}
if (ns->algorithm->can_digest) {
- if (ns->algorithm->kstr == NULL) {
+ if (ns->algorithm->is_pk) {
err();
ret = -EINVAL;
goto fail;
}
- ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 0, NULL, 0);
+ ret = init_or_clone_hash(ns, old_session,
+ ns->algorithm, NULL,
+ 0);
if (ret < 0) {
err();
goto fail;
}
} else {
- /* read key */
- ret = ncr_key_item_get_read( &ns->key, lists, session->key);
- if (ret < 0) {
+ /* Get key */
+ if (old_session == NULL) {
+ ret = key_item_get_nla_read(&ns->key,
+ lists,
+ tb[NCR_ATTR_KEY]);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ } else {
+ atomic_inc(&old_session->key->refcnt);
+ ns->key = old_session->key;
+ }
+
+ /* wrapping keys cannot be used for anything except wrapping.
+ */
+ if (ns->key->flags & NCR_KEY_FLAG_WRAPPING || ns->key->flags & NCR_KEY_FLAG_UNWRAPPING) {
err();
+ ret = -EINVAL;
goto fail;
}
if (ns->algorithm->is_hmac && ns->key->type == NCR_KEY_TYPE_SECRET) {
- if (ns->algorithm->kstr == NULL) {
+ if (ns->algorithm->is_pk) {
err();
ret = -EINVAL;
goto fail;
}
- ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 1,
+ ret = init_or_clone_hash(ns, old_session, ns->algorithm,
ns->key->key.secret.data, ns->key->key.secret.size);
if (ret < 0) {
err();
@@ -334,10 +552,17 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses
}
} else if (ns->algorithm->is_pk && (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC)) {
- sign_hash = ncr_key_params_get_sign_hash(ns->key->algorithm, &session->params);
- if (IS_ERR(sign_hash)) {
+ if (old_session != NULL) {
+ err();
+ ret = -EOPNOTSUPP;
+ goto fail;
+ }
+
+ nla = tb[NCR_ATTR_SIGNATURE_HASH_ALGORITHM];
+ sign_hash = _ncr_nla_to_properties(nla);
+ if (sign_hash == NULL) {
err();
- ret = PTR_ERR(sign_hash);
+ ret = -EINVAL;
goto fail;
}
@@ -347,24 +572,33 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses
goto fail;
}
- if (sign_hash->kstr == NULL) {
+ if (sign_hash->is_pk) {
err();
ret = -EINVAL;
goto fail;
}
ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk,
- &session->params, ns->key, sign_hash);
+ tb, ns->key, sign_hash);
if (ret < 0) {
err();
goto fail;
}
- ret = cryptodev_hash_init(&ns->hash, sign_hash->kstr, 0, NULL, 0);
+ ret = cryptodev_hash_init(&ns->hash, sign_hash->kstr, NULL, 0);
if (ret < 0) {
err();
goto fail;
}
+
+ if (ns->algorithm->has_transparent_hash) {
+ ns->transparent_hash = kzalloc(ns->hash.digestsize, GFP_KERNEL);
+ if (ns->transparent_hash == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto fail;
+ }
+ }
} else {
err();
ret = -EINVAL;
@@ -378,44 +612,46 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses
ret = -EINVAL;
goto fail;
}
+
+ if (old_session != NULL)
+ _ncr_sessions_item_put(old_session);
- ret = 0;
- session->ses = ns->desc;
+ return ns;
fail:
- if (ret < 0) {
- _ncr_session_remove(lists, ns->desc);
- }
+ if (old_session != NULL)
+ _ncr_sessions_item_put(old_session);
_ncr_sessions_item_put(ns);
- return ret;
+ return ERR_PTR(ret);
}
-int ncr_session_init(struct ncr_lists* lists, void __user* arg)
+int ncr_session_init(struct ncr_lists *lists,
+ const struct ncr_session_init *session,
+ struct nlattr *tb[])
{
- struct ncr_session_st session;
- int ret;
+ ncr_session_t desc;
+ struct session_item_st *sess;
- if (unlikely(copy_from_user(&session, arg, sizeof(session)))) {
+ desc = session_alloc_desc(lists);
+ if (desc < 0) {
err();
- return -EFAULT;
+ return desc;
}
- ret = _ncr_session_init(lists, &session);
- if (unlikely(ret)) {
+ sess = _ncr_session_init(lists, desc, session->op, tb);
+ if (IS_ERR(sess)) {
err();
- return ret;
+ session_drop_desc(lists, desc);
+ return PTR_ERR(sess);
}
- ret = copy_to_user( arg, &session, sizeof(session));
- if (unlikely(ret)) {
- err();
- _ncr_session_remove(lists, session.ses);
- return -EFAULT;
- }
- return ret;
+ session_publish_ref(lists, sess);
+
+ return desc;
}
+/* The caller is responsible for locking of the session. */
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)
{
@@ -444,6 +680,7 @@ int ret;
return 0;
}
+/* The caller is responsible for locking of the session. */
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)
@@ -473,20 +710,7 @@ int 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);
-}
-
+/* The caller is responsible for locking of the session. */
static int _ncr_session_grow_pages(struct session_item_st *ses, int pagecount)
{
struct scatterlist *sg;
@@ -517,66 +741,65 @@ static int _ncr_session_grow_pages(struct session_item_st *ses, int pagecount)
return 0;
}
-/* Only the output buffer is given as scatterlist */
-static int get_userbuf1(struct session_item_st* ses,
- void __user * udata, size_t udata_size, struct scatterlist **dst_sg, unsigned *dst_cnt)
+/* Make NCR_ATTR_UPDATE_INPUT_DATA and NCR_ATTR_UPDATE_OUTPUT_BUFFER available
+ in scatterlists.
+ The caller is responsible for locking of the session. */
+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)
{
- int pagecount = 0;
+ 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;
- if (unlikely(udata == NULL)) {
- err();
- return -EINVAL;
- }
+ src_nla = tb[NCR_ATTR_UPDATE_INPUT_DATA];
+ dst_nla = tb[NCR_ATTR_UPDATE_OUTPUT_BUFFER];
- pagecount = PAGECOUNT(udata, udata_size);
- _ncr_session_grow_pages(ses, pagecount);
-
- if (__get_userbuf(udata, udata_size, 1,
- pagecount, ses->pages, ses->sg)) {
+ ret = ncr_session_input_data_from_nla(&src, src_nla, compat);
+ if (unlikely(ret != 0)) {
err();
- return -EINVAL;
+ return ret;
}
- (*dst_sg) = ses->sg;
- *dst_cnt = pagecount;
+ *src_size = src.data_size;
- ses->available_pages = pagecount;
-
- return 0;
-}
-
-/* make op->data.udata.input and op->data.udata.output available in scatterlists */
-static int get_userbuf2(struct session_item_st* ses,
- struct ncr_session_op_st* op, struct scatterlist **src_sg,
- unsigned *src_cnt, struct scatterlist **dst_sg, unsigned *dst_cnt)
-{
- int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1;
- size_t input_size = op->data.udata.input_size;
-
- if (unlikely(op->data.udata.input == NULL)) {
- err();
- return -EINVAL;
+ if (dst_nla != NULL) {
+ ret = ncr_session_output_buffer_from_nla(dst, dst_nla, compat);
+ if (unlikely(ret != 0)) {
+ err();
+ return ret;
+ }
}
- src_pagecount = PAGECOUNT(op->data.udata.input, input_size);
+ input_size = src.data_size;
+ src_pagecount = PAGECOUNT(src.data, input_size);
- if (op->data.udata.input != op->data.udata.output) { /* non-in-situ transformation */
+ if (dst_nla == NULL || src.data != dst->buffer) { /* non-in-situ transformation */
write_src = 0;
- if (op->data.udata.output != NULL) {
- dst_pagecount = PAGECOUNT(op->data.udata.output, op->data.udata.output_size);
+ if (dst_nla != NULL) {
+ dst_pagecount = PAGECOUNT(dst->buffer,
+ dst->buffer_size);
} else {
dst_pagecount = 0;
}
} else {
- src_pagecount = max((int)(PAGECOUNT(op->data.udata.output, op->data.udata.output_size)),
- src_pagecount);
- input_size = max(input_size, (size_t)op->data.udata.output_size);
+ 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;
- _ncr_session_grow_pages(ses, pagecount);
+ ret = _ncr_session_grow_pages(ses, pagecount);
+ if (ret != 0) {
+ err();
+ return ret;
+ }
- if (__get_userbuf(op->data.udata.input, input_size, write_src,
- src_pagecount, ses->pages, ses->sg)) {
+ 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;
@@ -588,14 +811,15 @@ static int get_userbuf2(struct session_item_st* ses,
*dst_cnt = dst_pagecount;
(*dst_sg) = ses->sg + src_pagecount;
- if (__get_userbuf(op->data.udata.output, op->data.udata.output_size, 1, dst_pagecount,
- ses->pages + src_pagecount, *dst_sg)) {
+ 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 (op->data.udata.output != NULL) {
+ if (dst_nla != NULL) {
*dst_cnt = src_pagecount;
(*dst_sg) = (*src_sg);
} else {
@@ -609,35 +833,25 @@ static int get_userbuf2(struct session_item_st* ses,
return 0;
}
-/* Called when userspace buffers are used */
-static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op)
+/* Called when userspace buffers are used.
+ The caller is responsible for locking of the session. */
+static int _ncr_session_update(struct session_item_st *sess,
+ struct nlattr *tb[], int compat)
{
+ const struct nlattr *nla;
int ret;
- struct session_item_st* sess;
- struct scatterlist *isg;
- struct scatterlist *osg;
+ struct scatterlist *isg = NULL;
+ struct scatterlist *osg = NULL;
unsigned osg_cnt=0, isg_cnt=0;
- size_t isg_size, osg_size;
-
- sess = ncr_sessions_item_get(lists, op->ses);
- if (sess == NULL) {
- err();
- return -EINVAL;
- }
-
- if (mutex_lock_interruptible(&sess->mem_mutex)) {
- err();
- _ncr_sessions_item_put(sess);
- return -ERESTARTSYS;
- }
+ size_t isg_size = 0, osg_size;
+ struct ncr_session_output_buffer out;
- ret = get_userbuf2(sess, op, &isg, &isg_cnt, &osg, &osg_cnt);
+ ret = get_userbuf2(sess, tb, &isg, &isg_cnt, &isg_size, &out, &osg,
+ &osg_cnt, compat);
if (ret < 0) {
err();
- goto fail;
+ return ret;
}
- isg_size = op->data.udata.input_size;
- osg_size = op->data.udata.output_size;
switch(sess->op) {
case NCR_OP_ENCRYPT:
@@ -647,20 +861,35 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st
goto fail;
}
+ osg_size = out.buffer_size;
if (osg_size < isg_size) {
err();
ret = -EINVAL;
goto fail;
}
+ if (sess->algorithm->is_symmetric
+ && sess->algorithm->needs_iv) {
+ nla = tb[NCR_ATTR_IV];
+ if (nla != NULL)
+ cryptodev_cipher_set_iv(&sess->cipher,
+ nla_data(nla),
+ nla_len(nla));
+ }
+
ret = _ncr_session_encrypt(sess, isg, isg_cnt, isg_size,
osg, osg_cnt, &osg_size);
if (ret < 0) {
err();
goto fail;
}
- op->data.udata.output_size = osg_size;
-
+
+ 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) {
@@ -669,28 +898,60 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st
goto fail;
}
+ osg_size = out.buffer_size;
if (osg_size < isg_size) {
err();
ret = -EINVAL;
goto fail;
}
+ if (sess->algorithm->is_symmetric
+ && sess->algorithm->needs_iv) {
+ nla = tb[NCR_ATTR_IV];
+ if (nla != NULL)
+ cryptodev_cipher_set_iv(&sess->cipher,
+ nla_data(nla),
+ nla_len(nla));
+ }
+
ret = _ncr_session_decrypt(sess, isg, isg_cnt, isg_size,
osg, osg_cnt, &osg_size);
if (ret < 0) {
err();
goto fail;
}
- op->data.udata.output_size = osg_size;
+ 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;
+ if (sess->algorithm->has_transparent_hash) {
+ if (isg_size != sess->hash.digestsize) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ ret = sg_copy_to_buffer(isg, isg_cnt,
+ sess->transparent_hash,
+ isg_size);
+ if (ret != isg_size) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ } else {
+ ret = cryptodev_hash_update(&sess->hash, isg,
+ isg_size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
}
break;
default:
@@ -706,213 +967,199 @@ fail:
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, struct ncr_session_op_st* op)
+/* The caller is responsible for locking of the session. */
+static int try_session_update(struct ncr_lists *lists,
+ struct session_item_st *sess, struct nlattr *tb[],
+ int compat)
{
- if (op->type == NCR_KEY_DATA) {
- if (op->data.kdata.input != NCR_KEY_INVALID)
- return _ncr_session_update_key(lists, op);
- } else if (op->type == NCR_DIRECT_DATA) {
- if (op->data.udata.input != NULL)
- return _ncr_session_update(lists, op);
- }
-
- return 0;
+ if (tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] != NULL)
+ return _ncr_session_update_key(lists, sess, tb);
+ else if (tb[NCR_ATTR_UPDATE_INPUT_DATA] != NULL)
+ return _ncr_session_update(sess, tb, compat);
+ else
+ return 0;
}
-static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* op)
+/* The caller is responsible for locking of the session.
+ Note that one or more _ncr_session_update()s may still be blocked on
+ sess->mutex and will execute after this function! */
+static int _ncr_session_final(struct ncr_lists *lists,
+ struct session_item_st *sess, 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];
- uint8_t vdigest[NCR_HASH_MAX_OUTPUT_SIZE];
- struct scatterlist *osg;
- unsigned osg_cnt=0;
- size_t osg_size = 0;
- size_t orig_osg_size;
- void __user * udata = NULL;
- size_t *udata_size;
-
- sess = ncr_sessions_item_get(lists, op->ses);
- if (sess == NULL) {
- err();
- return -EINVAL;
- }
+ void *buffer = NULL;
- ret = try_session_update(lists, op);
+ ret = try_session_update(lists, sess, 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;
- }
-
- if (op->type == NCR_DIRECT_DATA) {
- udata = op->data.udata.output;
- udata_size = &op->data.udata.output_size;
- } else if (op->type == NCR_KEY_DATA) {
- udata = op->data.kdata.output;
- udata_size = &op->data.kdata.output_size;
- } else {
- err();
- ret = -EINVAL;
- goto fail;
- }
-
switch(sess->op) {
- case NCR_OP_ENCRYPT:
- case NCR_OP_DECRYPT:
- break;
- case NCR_OP_VERIFY:
- ret = get_userbuf1(sess, udata, *udata_size, &osg, &osg_cnt);
+ 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;
+ }
+ if (sess->algorithm->has_transparent_hash)
+ memcpy(digest, sess->transparent_hash, digest_size);
+ else {
+ ret = cryptodev_hash_final(&sess->hash, digest);
if (ret < 0) {
err();
goto fail;
}
- orig_osg_size = osg_size = *udata_size;
+ }
- digest_size = sess->hash.digestsize;
- if (digest_size == 0 || sizeof(digest) < digest_size) {
+ 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();
- ret = -EINVAL;
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;
+ }
+
+ if (sess->algorithm->has_transparent_hash)
+ memcpy(digest, sess->transparent_hash, digest_size);
+ else {
ret = cryptodev_hash_final(&sess->hash, digest);
if (ret < 0) {
err();
goto fail;
}
+ }
- if (sess->algorithm->is_hmac) {
- ret = sg_copy_to_buffer(osg, osg_cnt, vdigest, digest_size);
- if (ret != digest_size) {
- err();
- ret = -EINVAL;
- goto fail;
- }
-
- if (digest_size != osg_size ||
- memcmp(vdigest, digest, digest_size) != 0) {
-
- op->err = NCR_VERIFICATION_FAILED;
- } else {
- op->err = NCR_SUCCESS;
- }
- } else {
- /* PK signature */
- ret = ncr_pk_cipher_verify(&sess->pk, osg, osg_cnt, osg_size,
- digest, digest_size, &op->err);
- if (ret < 0) {
- err();
- goto fail;
- }
- }
- break;
+ cryptodev_hash_deinit(&sess->hash);
- case NCR_OP_SIGN:
- ret = get_userbuf1(sess, udata, *udata_size, &osg, &osg_cnt);
- if (ret < 0) {
+ if (!sess->algorithm->is_pk) {
+ if (dst.buffer_size < digest_size) {
err();
+ ret = -ERANGE;
goto fail;
}
- orig_osg_size = osg_size = *udata_size;
-
- digest_size = sess->hash.digestsize;
- if (digest_size == 0 || osg_size < digest_size) {
+ if (unlikely(copy_to_user(dst.buffer, digest,
+ digest_size))) {
err();
- ret = -EINVAL;
+ ret = -EFAULT;
goto fail;
}
-
- ret = cryptodev_hash_final(&sess->hash, digest);
+ 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;
}
-
- ret = sg_copy_from_buffer(osg, osg_cnt, digest, digest_size);
- if (ret != digest_size) {
+ if (unlikely(copy_to_user(dst.buffer, buffer,
+ output_size))) {
err();
- ret = -EINVAL;
+ ret = -EFAULT;
goto fail;
}
- osg_size = digest_size;
-
- cryptodev_hash_deinit(&sess->hash);
+ }
- if (sess->algorithm->is_pk) {
- /* PK signature */
-
- ret = ncr_pk_cipher_sign(&sess->pk, osg, osg_cnt, osg_size,
- osg, osg_cnt, &orig_osg_size);
- if (ret < 0) {
- err();
- goto fail;
- }
- osg_size = orig_osg_size;
- }
- break;
- default:
+ ret = ncr_session_output_buffer_set_size(&dst, output_size,
+ compat);
+ if (ret != 0) {
err();
- ret = -EINVAL;
goto fail;
+ }
+ break;
}
-
- if (osg_size > 0)
- *udata_size = osg_size;
-
- ret = 0;
-
-fail:
- if (sess->available_pages) {
- release_user_pages(sess->pages, sess->available_pages);
- sess->available_pages = 0;
- }
- 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);
+ default:
+ err();
+ ret = -EINVAL;
+ goto fail;
}
- _ncr_sessions_item_put(sess);
- _ncr_session_remove(lists, op->ses);
+fail:
+ kfree(buffer);
return ret;
}
-/* Direct with key: Allows to hash a key */
-/* Called when userspace buffers are used */
-static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_op_st* op)
+/* Direct with key: Allows to hash a key.
+ The caller is responsible for locking of the session. */
+static int _ncr_session_update_key(struct ncr_lists *lists,
+ struct session_item_st* sess,
+ struct nlattr *tb[])
{
int ret;
- struct session_item_st* sess;
struct key_item_st* key = NULL;
- sess = ncr_sessions_item_get(lists, op->ses);
- if (sess == NULL) {
- err();
- return -EINVAL;
- }
-
/* read key */
- ret = ncr_key_item_get_read( &key, lists, op->data.kdata.input);
+ ret = key_item_get_nla_read(&key, lists,
+ tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA]);
if (ret < 0) {
err();
- goto fail;
+ return ret;
}
if (key->type != NCR_KEY_TYPE_SECRET) {
@@ -929,6 +1176,11 @@ static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_o
goto fail;
case NCR_OP_SIGN:
case NCR_OP_VERIFY:
+ if (sess->algorithm->has_transparent_hash) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
ret = _cryptodev_hash_update(&sess->hash,
key->key.secret.data, key->key.secret.size);
if (ret < 0) {
@@ -945,89 +1197,102 @@ static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_o
ret = 0;
fail:
- if (key) _ncr_key_item_put(key);
- _ncr_sessions_item_put(sess);
+ _ncr_key_item_put(key);
return ret;
}
-int ncr_session_update(struct ncr_lists* lists, void __user* arg)
+int ncr_session_update(struct ncr_lists *lists,
+ const struct ncr_session_update *op, struct nlattr *tb[],
+ int compat)
{
- struct ncr_session_op_st op;
+ struct session_item_st *sess;
int ret;
- if (unlikely(copy_from_user( &op, arg, sizeof(op)))) {
+ sess = session_get_ref(lists, op->ses);
+ if (sess == NULL) {
err();
- return -EFAULT;
+ return -EINVAL;
}
- if (op.type == NCR_DIRECT_DATA)
- ret = _ncr_session_update(lists, &op);
- else if (op.type == NCR_KEY_DATA)
- ret = _ncr_session_update_key(lists, &op);
+ /* Note that op->ses may be reallocated from now on, making the audit
+ information confusing. */
+
+ if (mutex_lock_interruptible(&sess->mutex)) {
+ err();
+ ret = -ERESTARTSYS;
+ goto end;
+ }
+ if (tb[NCR_ATTR_UPDATE_INPUT_DATA] != NULL)
+ ret = _ncr_session_update(sess, tb, compat);
+ else if (tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] != NULL)
+ ret = _ncr_session_update_key(lists, sess, tb);
else
ret = -EINVAL;
+ mutex_unlock(&sess->mutex);
+
+end:
+ _ncr_sessions_item_put(sess);
if (unlikely(ret)) {
err();
return ret;
}
- if (unlikely(copy_to_user(arg, &op, sizeof(op)))) {
- err();
- return -EFAULT;
- }
-
return 0;
}
-int ncr_session_final(struct ncr_lists* lists, void __user* arg)
+int ncr_session_final(struct ncr_lists *lists,
+ const struct ncr_session_final *op, struct nlattr *tb[],
+ int compat)
{
- struct ncr_session_op_st op;
+ struct session_item_st *sess;
int ret;
- if (unlikely(copy_from_user(&op, arg, sizeof(op)))) {
+ /* Make the session inaccessible atomically to avoid concurrent
+ session_final() callers, but keep the ID allocated to keep audit
+ information unambiguous. */
+ sess = session_unpublish_ref(lists, op->ses);
+ if (sess == NULL) {
err();
- return -EFAULT;
+ return -EINVAL;
}
- ret = _ncr_session_final(lists, &op);
- if (unlikely(ret)) {
+ if (mutex_lock_interruptible(&sess->mutex)) {
err();
- return ret;
+ /* Other threads may observe the session descriptor
+ disappearing and reappearing - but then they should not be
+ accessing it anyway if it is being freed.
+ session_unpublish_ref keeps the ID allocated for us. */
+ session_publish_ref(lists, sess);
+ return -ERESTARTSYS;
}
+ ret = _ncr_session_final(lists, sess, tb, compat);
+ mutex_unlock(&sess->mutex);
- if (unlikely(copy_to_user(arg, &op, sizeof(op)))) {
- err();
- return -EFAULT;
- }
- return 0;
+ _ncr_sessions_item_put(sess);
+ session_drop_desc(lists, op->ses);
+
+ return ret;
}
-int ncr_session_once(struct ncr_lists* lists, void __user* arg)
+int ncr_session_once(struct ncr_lists *lists,
+ const struct ncr_session_once *once, struct nlattr *tb[],
+ int compat)
{
- struct ncr_session_once_op_st kop;
+ struct session_item_st *sess;
int ret;
- if (unlikely(copy_from_user(&kop, arg, sizeof(kop)))) {
+ sess = _ncr_session_init(lists, -1, once->op, tb);
+ if (IS_ERR(sess)) {
err();
- return -EFAULT;
+ return PTR_ERR(sess);
}
- ret = _ncr_session_init(lists, &kop.init);
- if (ret < 0) {
- err();
- return ret;
- }
- kop.op.ses = kop.init.ses;
+ /* No locking of sess necessary, "sess" is the only reference. */
+ ret = _ncr_session_final(lists, sess, tb, compat);
- ret = _ncr_session_final(lists, &kop.op);
- if (ret < 0) {
- err();
- return ret;
- }
+ _ncr_sessions_item_put(sess);
- if (unlikely(copy_to_user(arg, &kop, sizeof(kop))))
- return -EFAULT;
- return 0;
+ return ret;
}