[PATCH v8 5/6] crypto: algif_akcipher - add ops_nokey

2016-06-23 Thread Tadeusz Struk
Similar to algif_skcipher and algif_hash, algif_akcipher needs
to prevent user space from using the interface in an improper way.
This patch adds nokey ops handlers, which do just that.

Signed-off-by: Tadeusz Struk 
---
 crypto/algif_akcipher.c |  159 +--
 1 file changed, 152 insertions(+), 7 deletions(-)

diff --git a/crypto/algif_akcipher.c b/crypto/algif_akcipher.c
index 8dd6354..2b8d37e 100644
--- a/crypto/algif_akcipher.c
+++ b/crypto/algif_akcipher.c
@@ -27,6 +27,11 @@ struct akcipher_sg_list {
struct scatterlist sg[ALG_MAX_PAGES];
 };
 
+struct akcipher_tfm {
+   struct crypto_akcipher *akcipher;
+   bool has_key;
+};
+
 struct akcipher_ctx {
struct akcipher_sg_list tsgl;
struct af_alg_sgl rsgl[ALG_MAX_PAGES];
@@ -439,25 +444,151 @@ static struct proto_ops algif_akcipher_ops = {
.poll   =   akcipher_poll,
 };
 
+static int akcipher_check_key(struct socket *sock)
+{
+   int err = 0;
+   struct sock *psk;
+   struct alg_sock *pask;
+   struct akcipher_tfm *tfm;
+   struct sock *sk = sock->sk;
+   struct alg_sock *ask = alg_sk(sk);
+
+   lock_sock(sk);
+   if (ask->refcnt)
+   goto unlock_child;
+
+   psk = ask->parent;
+   pask = alg_sk(ask->parent);
+   tfm = pask->private;
+
+   err = -ENOKEY;
+   lock_sock_nested(psk, SINGLE_DEPTH_NESTING);
+   if (!tfm->has_key)
+   goto unlock;
+
+   if (!pask->refcnt++)
+   sock_hold(psk);
+
+   ask->refcnt = 1;
+   sock_put(psk);
+
+   err = 0;
+
+unlock:
+   release_sock(psk);
+unlock_child:
+   release_sock(sk);
+
+   return err;
+}
+
+static int akcipher_sendmsg_nokey(struct socket *sock, struct msghdr *msg,
+ size_t size)
+{
+   int err;
+
+   err = akcipher_check_key(sock);
+   if (err)
+   return err;
+
+   return akcipher_sendmsg(sock, msg, size);
+}
+
+static ssize_t akcipher_sendpage_nokey(struct socket *sock, struct page *page,
+  int offset, size_t size, int flags)
+{
+   int err;
+
+   err = akcipher_check_key(sock);
+   if (err)
+   return err;
+
+   return akcipher_sendpage(sock, page, offset, size, flags);
+}
+
+static int akcipher_recvmsg_nokey(struct socket *sock, struct msghdr *msg,
+ size_t ignored, int flags)
+{
+   int err;
+
+   err = akcipher_check_key(sock);
+   if (err)
+   return err;
+
+   return akcipher_recvmsg(sock, msg, ignored, flags);
+}
+
+static struct proto_ops algif_akcipher_ops_nokey = {
+   .family =   PF_ALG,
+
+   .connect=   sock_no_connect,
+   .socketpair =   sock_no_socketpair,
+   .getname=   sock_no_getname,
+   .ioctl  =   sock_no_ioctl,
+   .listen =   sock_no_listen,
+   .shutdown   =   sock_no_shutdown,
+   .getsockopt =   sock_no_getsockopt,
+   .mmap   =   sock_no_mmap,
+   .bind   =   sock_no_bind,
+   .accept =   sock_no_accept,
+   .setsockopt =   sock_no_setsockopt,
+
+   .release=   af_alg_release,
+   .sendmsg=   akcipher_sendmsg_nokey,
+   .sendpage   =   akcipher_sendpage_nokey,
+   .recvmsg=   akcipher_recvmsg_nokey,
+   .poll   =   akcipher_poll,
+};
+
 static void *akcipher_bind(const char *name, u32 type, u32 mask)
 {
-   return crypto_alloc_akcipher(name, type, mask);
+   struct akcipher_tfm *tfm;
+   struct crypto_akcipher *akcipher;
+
+   tfm = kzalloc(sizeof(*tfm), GFP_KERNEL);
+   if (!tfm)
+   return ERR_PTR(-ENOMEM);
+
+   akcipher = crypto_alloc_akcipher(name, type, mask);
+   if (IS_ERR(akcipher)) {
+   kfree(tfm);
+   return ERR_CAST(akcipher);
+   }
+
+   tfm->akcipher = akcipher;
+   return tfm;
 }
 
 static void akcipher_release(void *private)
 {
-   crypto_free_akcipher(private);
+   struct akcipher_tfm *tfm = private;
+   struct crypto_akcipher *akcipher = tfm->akcipher;
+
+   crypto_free_akcipher(akcipher);
+   kfree(tfm);
 }
 
 static int akcipher_setprivkey(void *private, const u8 *key,
   unsigned int keylen)
 {
-   return crypto_akcipher_set_priv_key(private, key, keylen);
+   struct akcipher_tfm *tfm = private;
+   struct crypto_akcipher *akcipher = tfm->akcipher;
+   int err;
+
+   err = crypto_akcipher_set_priv_key(akcipher, key, keylen);
+   tfm->has_key = !err;
+   return err;
 }
 
 static int akcipher_setpubkey(void *private, const u8 *key, unsigned int 
keylen)
 {
-   return crypto_akcipher_set_pub_key(private, key, keylen);
+   struct akcipher_tfm 

[PATCH v8 5/6] crypto: algif_akcipher - add ops_nokey

2016-06-23 Thread Tadeusz Struk
Similar to algif_skcipher and algif_hash, algif_akcipher needs
to prevent user space from using the interface in an improper way.
This patch adds nokey ops handlers, which do just that.

Signed-off-by: Tadeusz Struk 
---
 crypto/algif_akcipher.c |  159 +--
 1 file changed, 152 insertions(+), 7 deletions(-)

diff --git a/crypto/algif_akcipher.c b/crypto/algif_akcipher.c
index 8dd6354..2b8d37e 100644
--- a/crypto/algif_akcipher.c
+++ b/crypto/algif_akcipher.c
@@ -27,6 +27,11 @@ struct akcipher_sg_list {
struct scatterlist sg[ALG_MAX_PAGES];
 };
 
+struct akcipher_tfm {
+   struct crypto_akcipher *akcipher;
+   bool has_key;
+};
+
 struct akcipher_ctx {
struct akcipher_sg_list tsgl;
struct af_alg_sgl rsgl[ALG_MAX_PAGES];
@@ -439,25 +444,151 @@ static struct proto_ops algif_akcipher_ops = {
.poll   =   akcipher_poll,
 };
 
+static int akcipher_check_key(struct socket *sock)
+{
+   int err = 0;
+   struct sock *psk;
+   struct alg_sock *pask;
+   struct akcipher_tfm *tfm;
+   struct sock *sk = sock->sk;
+   struct alg_sock *ask = alg_sk(sk);
+
+   lock_sock(sk);
+   if (ask->refcnt)
+   goto unlock_child;
+
+   psk = ask->parent;
+   pask = alg_sk(ask->parent);
+   tfm = pask->private;
+
+   err = -ENOKEY;
+   lock_sock_nested(psk, SINGLE_DEPTH_NESTING);
+   if (!tfm->has_key)
+   goto unlock;
+
+   if (!pask->refcnt++)
+   sock_hold(psk);
+
+   ask->refcnt = 1;
+   sock_put(psk);
+
+   err = 0;
+
+unlock:
+   release_sock(psk);
+unlock_child:
+   release_sock(sk);
+
+   return err;
+}
+
+static int akcipher_sendmsg_nokey(struct socket *sock, struct msghdr *msg,
+ size_t size)
+{
+   int err;
+
+   err = akcipher_check_key(sock);
+   if (err)
+   return err;
+
+   return akcipher_sendmsg(sock, msg, size);
+}
+
+static ssize_t akcipher_sendpage_nokey(struct socket *sock, struct page *page,
+  int offset, size_t size, int flags)
+{
+   int err;
+
+   err = akcipher_check_key(sock);
+   if (err)
+   return err;
+
+   return akcipher_sendpage(sock, page, offset, size, flags);
+}
+
+static int akcipher_recvmsg_nokey(struct socket *sock, struct msghdr *msg,
+ size_t ignored, int flags)
+{
+   int err;
+
+   err = akcipher_check_key(sock);
+   if (err)
+   return err;
+
+   return akcipher_recvmsg(sock, msg, ignored, flags);
+}
+
+static struct proto_ops algif_akcipher_ops_nokey = {
+   .family =   PF_ALG,
+
+   .connect=   sock_no_connect,
+   .socketpair =   sock_no_socketpair,
+   .getname=   sock_no_getname,
+   .ioctl  =   sock_no_ioctl,
+   .listen =   sock_no_listen,
+   .shutdown   =   sock_no_shutdown,
+   .getsockopt =   sock_no_getsockopt,
+   .mmap   =   sock_no_mmap,
+   .bind   =   sock_no_bind,
+   .accept =   sock_no_accept,
+   .setsockopt =   sock_no_setsockopt,
+
+   .release=   af_alg_release,
+   .sendmsg=   akcipher_sendmsg_nokey,
+   .sendpage   =   akcipher_sendpage_nokey,
+   .recvmsg=   akcipher_recvmsg_nokey,
+   .poll   =   akcipher_poll,
+};
+
 static void *akcipher_bind(const char *name, u32 type, u32 mask)
 {
-   return crypto_alloc_akcipher(name, type, mask);
+   struct akcipher_tfm *tfm;
+   struct crypto_akcipher *akcipher;
+
+   tfm = kzalloc(sizeof(*tfm), GFP_KERNEL);
+   if (!tfm)
+   return ERR_PTR(-ENOMEM);
+
+   akcipher = crypto_alloc_akcipher(name, type, mask);
+   if (IS_ERR(akcipher)) {
+   kfree(tfm);
+   return ERR_CAST(akcipher);
+   }
+
+   tfm->akcipher = akcipher;
+   return tfm;
 }
 
 static void akcipher_release(void *private)
 {
-   crypto_free_akcipher(private);
+   struct akcipher_tfm *tfm = private;
+   struct crypto_akcipher *akcipher = tfm->akcipher;
+
+   crypto_free_akcipher(akcipher);
+   kfree(tfm);
 }
 
 static int akcipher_setprivkey(void *private, const u8 *key,
   unsigned int keylen)
 {
-   return crypto_akcipher_set_priv_key(private, key, keylen);
+   struct akcipher_tfm *tfm = private;
+   struct crypto_akcipher *akcipher = tfm->akcipher;
+   int err;
+
+   err = crypto_akcipher_set_priv_key(akcipher, key, keylen);
+   tfm->has_key = !err;
+   return err;
 }
 
 static int akcipher_setpubkey(void *private, const u8 *key, unsigned int 
keylen)
 {
-   return crypto_akcipher_set_pub_key(private, key, keylen);
+   struct akcipher_tfm *tfm = private;
+