lkml.org 
[lkml]   [2016]   [Feb]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 3.12 065/142] crypto: algif_hash - Require setkey before accept(2)
    Date
    From: Herbert Xu <herbert@gondor.apana.org.au>

    3.12-stable review patch. If anyone has any objections, please let me know.

    ===============

    commit 6de62f15b581f920ade22d758f4c338311c2f0d4 upstream.

    Hash implementations that require a key may crash if you use
    them without setting a key. This patch adds the necessary checks
    so that if you do attempt to use them without a key that we return
    -ENOKEY instead of proceeding.

    This patch also adds a compatibility path to support old applications
    that do acept(2) before setkey.

    Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
    Signed-off-by: Jiri Slaby <jslaby@suse.cz>
    ---
    crypto/algif_hash.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++---
    1 file changed, 193 insertions(+), 8 deletions(-)

    diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c
    index a68b56a368a8..402dae150ec0 100644
    --- a/crypto/algif_hash.c
    +++ b/crypto/algif_hash.c
    @@ -34,6 +34,11 @@ struct hash_ctx {
    struct ahash_request req;
    };

    +struct algif_hash_tfm {
    + struct crypto_ahash *hash;
    + bool has_key;
    +};
    +
    static int hash_sendmsg(struct kiocb *unused, struct socket *sock,
    struct msghdr *msg, size_t ignored)
    {
    @@ -246,22 +251,151 @@ static struct proto_ops algif_hash_ops = {
    .accept = hash_accept,
    };

    +static int hash_check_key(struct socket *sock)
    +{
    + int err;
    + struct sock *psk;
    + struct alg_sock *pask;
    + struct algif_hash_tfm *tfm;
    + struct sock *sk = sock->sk;
    + struct alg_sock *ask = alg_sk(sk);
    +
    + if (ask->refcnt)
    + return 0;
    +
    + psk = ask->parent;
    + pask = alg_sk(ask->parent);
    + tfm = pask->private;
    +
    + err = -ENOKEY;
    + lock_sock(psk);
    + if (!tfm->has_key)
    + goto unlock;
    +
    + if (!pask->refcnt++)
    + sock_hold(psk);
    +
    + ask->refcnt = 1;
    + sock_put(psk);
    +
    + err = 0;
    +
    +unlock:
    + release_sock(psk);
    +
    + return err;
    +}
    +
    +static int hash_sendmsg_nokey(struct kiocb *unused, struct socket *sock,
    + struct msghdr *msg, size_t size)
    +{
    + int err;
    +
    + err = hash_check_key(sock);
    + if (err)
    + return err;
    +
    + return hash_sendmsg(NULL, sock, msg, size);
    +}
    +
    +static ssize_t hash_sendpage_nokey(struct socket *sock, struct page *page,
    + int offset, size_t size, int flags)
    +{
    + int err;
    +
    + err = hash_check_key(sock);
    + if (err)
    + return err;
    +
    + return hash_sendpage(sock, page, offset, size, flags);
    +}
    +
    +static int hash_recvmsg_nokey(struct kiocb *unused, struct socket *sock,
    + struct msghdr *msg, size_t ignored, int flags)
    +{
    + int err;
    +
    + err = hash_check_key(sock);
    + if (err)
    + return err;
    +
    + return hash_recvmsg(NULL, sock, msg, ignored, flags);
    +}
    +
    +static int hash_accept_nokey(struct socket *sock, struct socket *newsock,
    + int flags)
    +{
    + int err;
    +
    + err = hash_check_key(sock);
    + if (err)
    + return err;
    +
    + return hash_accept(sock, newsock, flags);
    +}
    +
    +static struct proto_ops algif_hash_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,
    + .setsockopt = sock_no_setsockopt,
    + .poll = sock_no_poll,
    +
    + .release = af_alg_release,
    + .sendmsg = hash_sendmsg_nokey,
    + .sendpage = hash_sendpage_nokey,
    + .recvmsg = hash_recvmsg_nokey,
    + .accept = hash_accept_nokey,
    +};
    +
    static void *hash_bind(const char *name, u32 type, u32 mask)
    {
    - return crypto_alloc_ahash(name, type, mask);
    + struct algif_hash_tfm *tfm;
    + struct crypto_ahash *hash;
    +
    + tfm = kzalloc(sizeof(*tfm), GFP_KERNEL);
    + if (!tfm)
    + return ERR_PTR(-ENOMEM);
    +
    + hash = crypto_alloc_ahash(name, type, mask);
    + if (IS_ERR(hash)) {
    + kfree(tfm);
    + return ERR_CAST(hash);
    + }
    +
    + tfm->hash = hash;
    +
    + return tfm;
    }

    static void hash_release(void *private)
    {
    - crypto_free_ahash(private);
    + struct algif_hash_tfm *tfm = private;
    +
    + crypto_free_ahash(tfm->hash);
    + kfree(tfm);
    }

    static int hash_setkey(void *private, const u8 *key, unsigned int keylen)
    {
    - return crypto_ahash_setkey(private, key, keylen);
    + struct algif_hash_tfm *tfm = private;
    + int err;
    +
    + err = crypto_ahash_setkey(tfm->hash, key, keylen);
    + tfm->has_key = !err;
    +
    + return err;
    }

    -static void hash_sock_destruct(struct sock *sk)
    +static void hash_sock_destruct_common(struct sock *sk)
    {
    struct alg_sock *ask = alg_sk(sk);
    struct hash_ctx *ctx = ask->private;
    @@ -269,15 +403,40 @@ static void hash_sock_destruct(struct sock *sk)
    sock_kfree_s(sk, ctx->result,
    crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)));
    sock_kfree_s(sk, ctx, ctx->len);
    +}
    +
    +static void hash_sock_destruct(struct sock *sk)
    +{
    + hash_sock_destruct_common(sk);
    af_alg_release_parent(sk);
    }

    -static int hash_accept_parent(void *private, struct sock *sk)
    +static void hash_release_parent_nokey(struct sock *sk)
    +{
    + struct alg_sock *ask = alg_sk(sk);
    +
    + if (!ask->refcnt) {
    + sock_put(ask->parent);
    + return;
    + }
    +
    + af_alg_release_parent(sk);
    +}
    +
    +static void hash_sock_destruct_nokey(struct sock *sk)
    +{
    + hash_sock_destruct_common(sk);
    + hash_release_parent_nokey(sk);
    +}
    +
    +static int hash_accept_parent_common(void *private, struct sock *sk)
    {
    struct hash_ctx *ctx;
    struct alg_sock *ask = alg_sk(sk);
    - unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(private);
    - unsigned ds = crypto_ahash_digestsize(private);
    + struct algif_hash_tfm *tfm = private;
    + struct crypto_ahash *hash = tfm->hash;
    + unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(hash);
    + unsigned ds = crypto_ahash_digestsize(hash);

    ctx = sock_kmalloc(sk, len, GFP_KERNEL);
    if (!ctx)
    @@ -297,7 +456,7 @@ static int hash_accept_parent(void *private, struct sock *sk)

    ask->private = ctx;

    - ahash_request_set_tfm(&ctx->req, private);
    + ahash_request_set_tfm(&ctx->req, hash);
    ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
    af_alg_complete, &ctx->completion);

    @@ -306,12 +465,38 @@ static int hash_accept_parent(void *private, struct sock *sk)
    return 0;
    }

    +static int hash_accept_parent(void *private, struct sock *sk)
    +{
    + struct algif_hash_tfm *tfm = private;
    +
    + if (!tfm->has_key && crypto_ahash_has_setkey(tfm->hash))
    + return -ENOKEY;
    +
    + return hash_accept_parent_common(private, sk);
    +}
    +
    +static int hash_accept_parent_nokey(void *private, struct sock *sk)
    +{
    + int err;
    +
    + err = hash_accept_parent_common(private, sk);
    + if (err)
    + goto out;
    +
    + sk->sk_destruct = hash_sock_destruct_nokey;
    +
    +out:
    + return err;
    +}
    +
    static const struct af_alg_type algif_type_hash = {
    .bind = hash_bind,
    .release = hash_release,
    .setkey = hash_setkey,
    .accept = hash_accept_parent,
    + .accept_nokey = hash_accept_parent_nokey,
    .ops = &algif_hash_ops,
    + .ops_nokey = &algif_hash_ops_nokey,
    .name = "hash",
    .owner = THIS_MODULE
    };
    --
    2.7.1
    \
     
     \ /
      Last update: 2016-02-24 11:41    [W:3.224 / U:0.020 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site