Created
January 12, 2016 09:26
-
-
Save dvyukov/fad55a75c516033c50a9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/crypto/af_alg.c b/crypto/af_alg.c | |
index a8e7aa3..b8fb45a 100644 | |
--- a/crypto/af_alg.c | |
+++ b/crypto/af_alg.c | |
@@ -125,6 +125,23 @@ int af_alg_release(struct socket *sock) | |
} | |
EXPORT_SYMBOL_GPL(af_alg_release); | |
+void af_alg_release_parent(struct sock *sk) | |
+{ | |
+ struct alg_sock *ask = alg_sk(sk); | |
+ bool last; | |
+ | |
+ sk = ask->parent; | |
+ ask = alg_sk(sk); | |
+ | |
+ lock_sock(sk); | |
+ last = !--ask->refcnt; | |
+ release_sock(sk); | |
+ | |
+ if (last) | |
+ sock_put(sk); | |
+} | |
+EXPORT_SYMBOL_GPL(af_alg_release_parent); | |
+ | |
static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |
{ | |
const u32 forbidden = CRYPTO_ALG_INTERNAL; | |
@@ -133,6 +150,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |
struct sockaddr_alg *sa = (void *)uaddr; | |
const struct af_alg_type *type; | |
void *private; | |
+ int err; | |
if (sock->state == SS_CONNECTED) | |
return -EINVAL; | |
@@ -160,16 +178,22 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |
return PTR_ERR(private); | |
} | |
+ err = -EBUSY; | |
lock_sock(sk); | |
+ if (ask->refcnt) | |
+ goto unlock; | |
swap(ask->type, type); | |
swap(ask->private, private); | |
+ err = 0; | |
+ | |
+unlock: | |
release_sock(sk); | |
alg_do_release(type, private); | |
- return 0; | |
+ return err; | |
} | |
static int alg_setkey(struct sock *sk, char __user *ukey, | |
@@ -196,6 +220,16 @@ out: | |
return err; | |
} | |
+static int alg_setauthsize(struct sock *sk, unsigned int size) | |
+{ | |
+ int err; | |
+ struct alg_sock *ask = alg_sk(sk); | |
+ const struct af_alg_type *type = ask->type; | |
+ | |
+ err = type->setauthsize(ask->private, size); | |
+ return err; | |
+} | |
+ | |
static int alg_setsockopt(struct socket *sock, int level, int optname, | |
char __user *optval, unsigned int optlen) | |
{ | |
@@ -210,6 +244,11 @@ static int alg_setsockopt(struct socket *sock, int level, int optname, | |
if (level != SOL_ALG || !type) | |
goto unlock; | |
+ if (ask->refcnt) { | |
+ err = -EBUSY; | |
+ goto unlock; | |
+ } | |
+ | |
switch (optname) { | |
case ALG_SET_KEY: | |
if (sock->state == SS_CONNECTED) | |
@@ -224,7 +263,7 @@ static int alg_setsockopt(struct socket *sock, int level, int optname, | |
goto unlock; | |
if (!type->setauthsize) | |
goto unlock; | |
- err = type->setauthsize(ask->private, optlen); | |
+ err = alg_setauthsize(sk, optlen); | |
} | |
unlock: | |
@@ -257,14 +296,13 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) | |
security_sk_clone(sk, sk2); | |
err = type->accept(ask->private, sk2); | |
- if (err) { | |
- sk_free(sk2); | |
+ if (err) | |
goto unlock; | |
- } | |
sk2->sk_family = PF_ALG; | |
- sock_hold(sk); | |
+ if (!ask->refcnt++) | |
+ sock_hold(sk); | |
alg_sk(sk2)->parent = sk; | |
alg_sk(sk2)->type = type; | |
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c | |
index 634b4d1..df483f9 100644 | |
--- a/crypto/algif_skcipher.c | |
+++ b/crypto/algif_skcipher.c | |
@@ -31,6 +31,11 @@ struct skcipher_sg_list { | |
struct scatterlist sg[0]; | |
}; | |
+struct skcipher_tfm { | |
+ struct crypto_skcipher *skcipher; | |
+ bool has_key; | |
+}; | |
+ | |
struct skcipher_ctx { | |
struct list_head tsgl; | |
struct af_alg_sgl rsgl; | |
@@ -750,17 +755,41 @@ static struct proto_ops algif_skcipher_ops = { | |
static void *skcipher_bind(const char *name, u32 type, u32 mask) | |
{ | |
- return crypto_alloc_skcipher(name, type, mask); | |
+ struct skcipher_tfm *tfm; | |
+ struct crypto_skcipher *skcipher; | |
+ | |
+ tfm = kzalloc(sizeof(*tfm), GFP_KERNEL); | |
+ if (!tfm) | |
+ return ERR_PTR(-ENOMEM); | |
+ | |
+ skcipher = crypto_alloc_skcipher(name, type, mask); | |
+ if (IS_ERR(skcipher)) { | |
+ kfree(tfm); | |
+ return ERR_CAST(skcipher); | |
+ } | |
+ | |
+ tfm->skcipher = skcipher; | |
+ | |
+ return tfm; | |
} | |
static void skcipher_release(void *private) | |
{ | |
- crypto_free_skcipher(private); | |
+ struct skcipher_tfm *tfm = private; | |
+ | |
+ crypto_free_skcipher(tfm->skcipher); | |
+ kfree(tfm); | |
} | |
static int skcipher_setkey(void *private, const u8 *key, unsigned int keylen) | |
{ | |
- return crypto_skcipher_setkey(private, key, keylen); | |
+ struct skcipher_tfm *tfm = private; | |
+ int err; | |
+ | |
+ err = crypto_skcipher_setkey(tfm->skcipher, key, keylen); | |
+ tfm->has_key = !err; | |
+ | |
+ return err; | |
} | |
static void skcipher_wait(struct sock *sk) | |
@@ -792,20 +821,25 @@ static int skcipher_accept_parent(void *private, struct sock *sk) | |
{ | |
struct skcipher_ctx *ctx; | |
struct alg_sock *ask = alg_sk(sk); | |
- unsigned int len = sizeof(*ctx) + crypto_skcipher_reqsize(private); | |
+ struct skcipher_tfm *tfm = private; | |
+ struct crypto_skcipher *skcipher = tfm->skcipher; | |
+ unsigned int len = sizeof(*ctx) + crypto_skcipher_reqsize(skcipher); | |
+ | |
+ if (!tfm->has_key) | |
+ return -ENOKEY; | |
ctx = sock_kmalloc(sk, len, GFP_KERNEL); | |
if (!ctx) | |
return -ENOMEM; | |
- ctx->iv = sock_kmalloc(sk, crypto_skcipher_ivsize(private), | |
+ ctx->iv = sock_kmalloc(sk, crypto_skcipher_ivsize(skcipher), | |
GFP_KERNEL); | |
if (!ctx->iv) { | |
sock_kfree_s(sk, ctx, len); | |
return -ENOMEM; | |
} | |
- memset(ctx->iv, 0, crypto_skcipher_ivsize(private)); | |
+ memset(ctx->iv, 0, crypto_skcipher_ivsize(skcipher)); | |
INIT_LIST_HEAD(&ctx->tsgl); | |
ctx->len = len; | |
@@ -818,7 +852,7 @@ static int skcipher_accept_parent(void *private, struct sock *sk) | |
ask->private = ctx; | |
- skcipher_request_set_tfm(&ctx->req, private); | |
+ skcipher_request_set_tfm(&ctx->req, skcipher); | |
skcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG, | |
af_alg_complete, &ctx->completion); | |
diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h | |
index 018afb2..589716f 100644 | |
--- a/include/crypto/if_alg.h | |
+++ b/include/crypto/if_alg.h | |
@@ -30,6 +30,8 @@ struct alg_sock { | |
struct sock *parent; | |
+ unsigned int refcnt; | |
+ | |
const struct af_alg_type *type; | |
void *private; | |
}; | |
@@ -67,6 +69,7 @@ int af_alg_register_type(const struct af_alg_type *type); | |
int af_alg_unregister_type(const struct af_alg_type *type); | |
int af_alg_release(struct socket *sock); | |
+void af_alg_release_parent(struct sock *sk); | |
int af_alg_accept(struct sock *sk, struct socket *newsock); | |
int af_alg_make_sg(struct af_alg_sgl *sgl, struct iov_iter *iter, int len); | |
@@ -83,11 +86,6 @@ static inline struct alg_sock *alg_sk(struct sock *sk) | |
return (struct alg_sock *)sk; | |
} | |
-static inline void af_alg_release_parent(struct sock *sk) | |
-{ | |
- sock_put(alg_sk(sk)->parent); | |
-} | |
- | |
static inline void af_alg_init_completion(struct af_alg_completion *completion) | |
{ | |
init_completion(&completion->completion); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment