diff mbox

[2/2] crypto: algif_hash - Require setkey before accept(2)

Message ID 20160108133104.GA5838@gondor.apana.org.au (mailing list archive)
State Accepted
Delegated to: Herbert Xu
Headers show

Commit Message

Herbert Xu Jan. 8, 2016, 1:31 p.m. UTC
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>

Comments

kernel test robot Jan. 8, 2016, 1:54 p.m. UTC | #1
Hi Herbert,

[auto build test ERROR on crypto/master]
[also build test ERROR on v4.4-rc8 next-20160108]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Herbert-Xu/crypto-hash-Add-crypto_ahash_has_setkey/20160108-213436
base:   https://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6.git master
config: x86_64-randconfig-i0-201601 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):

   crypto/algif_hash.c: In function 'hash_check_key':
>> crypto/algif_hash.c:252:9: error: 'struct alg_sock' has no member named 'refcnt'
     if (ask->refcnt)
            ^
   crypto/algif_hash.c:264:11: error: 'struct alg_sock' has no member named 'refcnt'
     if (!pask->refcnt++)
              ^
   crypto/algif_hash.c:267:5: error: 'struct alg_sock' has no member named 'refcnt'
     ask->refcnt = 1;
        ^
   crypto/algif_hash.c: In function 'hash_release_parent_nokey':
   crypto/algif_hash.c:407:10: error: 'struct alg_sock' has no member named 'refcnt'
     if (!ask->refcnt) {
             ^
   crypto/algif_hash.c: At top level:
>> crypto/algif_hash.c:486:2: error: unknown field 'accept_nokey' specified in initializer
     .accept_nokey = hash_accept_parent_nokey,
     ^
>> crypto/algif_hash.c:486:18: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
     .accept_nokey = hash_accept_parent_nokey,
                     ^
   crypto/algif_hash.c:486:18: note: (near initialization for 'algif_type_hash.setauthsize')
>> crypto/algif_hash.c:488:2: error: unknown field 'ops_nokey' specified in initializer
     .ops_nokey = &algif_hash_ops_nokey,
     ^
   crypto/algif_hash.c:488:15: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
     .ops_nokey = &algif_hash_ops_nokey,
                  ^
   crypto/algif_hash.c:488:15: note: (near initialization for 'algif_type_hash.owner')

vim +252 crypto/algif_hash.c

   246		struct sock *psk;
   247		struct alg_sock *pask;
   248		struct algif_hash_tfm *tfm;
   249		struct sock *sk = sock->sk;
   250		struct alg_sock *ask = alg_sk(sk);
   251	
 > 252		if (ask->refcnt)
   253			return 0;
   254	
   255		psk = ask->parent;
   256		pask = alg_sk(ask->parent);
   257		tfm = pask->private;
   258	
   259		err = -ENOKEY;
   260		lock_sock(psk);
   261		if (!tfm->has_key)
   262			goto unlock;
   263	
   264		if (!pask->refcnt++)
   265			sock_hold(psk);
   266	
 > 267		ask->refcnt = 1;
   268		sock_put(psk);
   269	
   270		err = 0;
   271	
   272	unlock:
   273		release_sock(psk);
   274	
   275		return err;
   276	}
   277	
   278	static int hash_sendmsg_nokey(struct socket *sock, struct msghdr *msg,
   279				      size_t size)
   280	{
   281		int err;
   282	
   283		err = hash_check_key(sock);
   284		if (err)
   285			return err;
   286	
   287		return hash_sendmsg(sock, msg, size);
   288	}
   289	
   290	static ssize_t hash_sendpage_nokey(struct socket *sock, struct page *page,
   291					   int offset, size_t size, int flags)
   292	{
   293		int err;
   294	
   295		err = hash_check_key(sock);
   296		if (err)
   297			return err;
   298	
   299		return hash_sendpage(sock, page, offset, size, flags);
   300	}
   301	
   302	static int hash_recvmsg_nokey(struct socket *sock, struct msghdr *msg,
   303				      size_t ignored, int flags)
   304	{
   305		int err;
   306	
   307		err = hash_check_key(sock);
   308		if (err)
   309			return err;
   310	
   311		return hash_recvmsg(sock, msg, ignored, flags);
   312	}
   313	
   314	static int hash_accept_nokey(struct socket *sock, struct socket *newsock,
   315				     int flags)
   316	{
   317		int err;
   318	
   319		err = hash_check_key(sock);
   320		if (err)
   321			return err;
   322	
   323		return hash_accept(sock, newsock, flags);
   324	}
   325	
   326	static struct proto_ops algif_hash_ops_nokey = {
   327		.family		=	PF_ALG,
   328	
   329		.connect	=	sock_no_connect,
   330		.socketpair	=	sock_no_socketpair,
   331		.getname	=	sock_no_getname,
   332		.ioctl		=	sock_no_ioctl,
   333		.listen		=	sock_no_listen,
   334		.shutdown	=	sock_no_shutdown,
   335		.getsockopt	=	sock_no_getsockopt,
   336		.mmap		=	sock_no_mmap,
   337		.bind		=	sock_no_bind,
   338		.setsockopt	=	sock_no_setsockopt,
   339		.poll		=	sock_no_poll,
   340	
   341		.release	=	af_alg_release,
   342		.sendmsg	=	hash_sendmsg_nokey,
   343		.sendpage	=	hash_sendpage_nokey,
   344		.recvmsg	=	hash_recvmsg_nokey,
   345		.accept		=	hash_accept_nokey,
   346	};
   347	
   348	static void *hash_bind(const char *name, u32 type, u32 mask)
   349	{
   350		struct algif_hash_tfm *tfm;
   351		struct crypto_ahash *hash;
   352	
   353		tfm = kzalloc(sizeof(*tfm), GFP_KERNEL);
   354		if (!tfm)
   355			return ERR_PTR(-ENOMEM);
   356	
   357		hash = crypto_alloc_ahash(name, type, mask);
   358		if (IS_ERR(hash)) {
   359			kfree(tfm);
   360			return ERR_CAST(hash);
   361		}
   362	
   363		tfm->hash = hash;
   364	
   365		return tfm;
   366	}
   367	
   368	static void hash_release(void *private)
   369	{
   370		struct algif_hash_tfm *tfm = private;
   371	
   372		crypto_free_ahash(tfm->hash);
   373		kfree(tfm);
   374	}
   375	
   376	static int hash_setkey(void *private, const u8 *key, unsigned int keylen)
   377	{
   378		struct algif_hash_tfm *tfm = private;
   379		int err;
   380	
   381		err = crypto_ahash_setkey(tfm->hash, key, keylen);
   382		tfm->has_key = !err;
   383	
   384		return err;
   385	}
   386	
   387	static void hash_sock_destruct_common(struct sock *sk)
   388	{
   389		struct alg_sock *ask = alg_sk(sk);
   390		struct hash_ctx *ctx = ask->private;
   391	
   392		sock_kzfree_s(sk, ctx->result,
   393			      crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)));
   394		sock_kfree_s(sk, ctx, ctx->len);
   395	}
   396	
   397	static void hash_sock_destruct(struct sock *sk)
   398	{
   399		hash_sock_destruct_common(sk);
   400		af_alg_release_parent(sk);
   401	}
   402	
   403	static void hash_release_parent_nokey(struct sock *sk)
   404	{
   405		struct alg_sock *ask = alg_sk(sk);
   406	
 > 407		if (!ask->refcnt) {
   408			sock_put(ask->parent);
   409			return;
   410		}

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Milan Broz Jan. 9, 2016, 10:15 a.m. UTC | #2
On 01/08/2016 02:31 PM, Herbert Xu wrote:
> 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>

Reported-and-tested-by: Milan Broz <gmazyland@gmail.com>

(tested both patches, ditto for previous skcipher series)

Thanks,
Milan


--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c
index b4c24fe..46637be 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 socket *sock, struct msghdr *msg,
 			size_t ignored)
 {
@@ -235,22 +240,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 socket *sock, struct msghdr *msg,
+			      size_t size)
+{
+	int err;
+
+	err = hash_check_key(sock);
+	if (err)
+		return err;
+
+	return hash_sendmsg(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 socket *sock, struct msghdr *msg,
+			      size_t ignored, int flags)
+{
+	int err;
+
+	err = hash_check_key(sock);
+	if (err)
+		return err;
+
+	return hash_recvmsg(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;
@@ -258,15 +392,40 @@  static void hash_sock_destruct(struct sock *sk)
 	sock_kzfree_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)
@@ -286,7 +445,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);
 
@@ -295,12 +454,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
 };