Commit 031bc9fb authored by Herbert Xu's avatar Herbert Xu Committed by Kamal Mostafa

crypto: algif_hash - Require setkey before accept(2)

commit 6de62f15 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: default avatarHerbert Xu <herbert@gondor.apana.org.au>
[bwh: Backported to 3.2:
 - Add struct kiocb * parameter to {recv,send}msg ops
 - Adjust context]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
Signed-off-by: default avatarKamal Mostafa <kamal@canonical.com>
parent a98042e0
...@@ -34,6 +34,11 @@ struct hash_ctx { ...@@ -34,6 +34,11 @@ struct hash_ctx {
struct ahash_request req; 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, static int hash_sendmsg(struct kiocb *unused, struct socket *sock,
struct msghdr *msg, size_t ignored) struct msghdr *msg, size_t ignored)
{ {
...@@ -248,22 +253,151 @@ static struct proto_ops algif_hash_ops = { ...@@ -248,22 +253,151 @@ static struct proto_ops algif_hash_ops = {
.accept = hash_accept, .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(unused, 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(unused, 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) 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) 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) 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 alg_sock *ask = alg_sk(sk);
struct hash_ctx *ctx = ask->private; struct hash_ctx *ctx = ask->private;
...@@ -271,15 +405,40 @@ static void hash_sock_destruct(struct sock *sk) ...@@ -271,15 +405,40 @@ static void hash_sock_destruct(struct sock *sk)
sock_kzfree_s(sk, ctx->result, sock_kzfree_s(sk, ctx->result,
crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req))); crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)));
sock_kfree_s(sk, ctx, ctx->len); 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); 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 hash_ctx *ctx;
struct alg_sock *ask = alg_sk(sk); struct alg_sock *ask = alg_sk(sk);
unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(private); struct algif_hash_tfm *tfm = private;
unsigned ds = crypto_ahash_digestsize(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); ctx = sock_kmalloc(sk, len, GFP_KERNEL);
if (!ctx) if (!ctx)
...@@ -299,7 +458,7 @@ static int hash_accept_parent(void *private, struct sock *sk) ...@@ -299,7 +458,7 @@ static int hash_accept_parent(void *private, struct sock *sk)
ask->private = ctx; 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, ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
af_alg_complete, &ctx->completion); af_alg_complete, &ctx->completion);
...@@ -308,12 +467,38 @@ static int hash_accept_parent(void *private, struct sock *sk) ...@@ -308,12 +467,38 @@ static int hash_accept_parent(void *private, struct sock *sk)
return 0; 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 = { static const struct af_alg_type algif_type_hash = {
.bind = hash_bind, .bind = hash_bind,
.release = hash_release, .release = hash_release,
.setkey = hash_setkey, .setkey = hash_setkey,
.accept = hash_accept_parent, .accept = hash_accept_parent,
.accept_nokey = hash_accept_parent_nokey,
.ops = &algif_hash_ops, .ops = &algif_hash_ops,
.ops_nokey = &algif_hash_ops_nokey,
.name = "hash", .name = "hash",
.owner = THIS_MODULE .owner = THIS_MODULE
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment