diff options
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/af_alg.c | 110 | ||||
-rw-r--r-- | crypto/algif_hash.c | 35 | ||||
-rw-r--r-- | crypto/algif_skcipher.c | 28 |
3 files changed, 163 insertions, 10 deletions
diff --git a/crypto/af_alg.c b/crypto/af_alg.c index bd9e53c7802..31f2b224ae4 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -14,11 +14,14 @@ #include <asm/atomic.h> #include <crypto/if_alg.h> +#include <linux/audit.h> #include <linux/crypto.h> +#include <linux/idr.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/net.h> #include <linux/rwsem.h> @@ -38,6 +41,10 @@ static struct proto alg_proto = { static LIST_HEAD(alg_types); static DECLARE_RWSEM(alg_types_sem); +#ifdef CONFIG_AUDIT +static DEFINE_MUTEX(alg_ida_mutex); +static DEFINE_IDA(alg_ida); +#endif static const struct af_alg_type *alg_get_type(const char *name) { @@ -107,6 +114,64 @@ int af_alg_unregister_type(const struct af_alg_type *type) } EXPORT_SYMBOL_GPL(af_alg_unregister_type); +static struct sock *alg_sk_alloc(struct net *net) +{ + struct sock *sk; + + sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto); + if (!sk) + goto out; + +#ifdef CONFIG_AUDIT + mutex_lock(&alg_ida_mutex); + /* ida_pre_get() should preallocate enough, and, due to + lists_ida_mutex, nobody else can use the preallocated data. + Therefore the loop recommended in idr_get_new() documentation is not + necessary. */ + if (ida_pre_get(&alg_ida, GFP_KERNEL) == 0 || + ida_get_new(&alg_ida, &alg_sk(sk)->id) != 0) { + mutex_unlock(&alg_ida_mutex); + alg_sk(sk)->id = -1; + sk_free(sk); + sk = NULL; + goto out; + } + mutex_unlock(&alg_ida_mutex); +#endif + +out: + return sk; +} + +#ifdef CONFIG_AUDIT +static void alg_sk_destruct(struct sock *sk) +{ + struct alg_sock *ask = alg_sk(sk); + + if (ask->id != -1) { + mutex_lock(&alg_ida_mutex); + ida_remove(&alg_ida, ask->id); + mutex_unlock(&alg_ida_mutex); + } +} +#else +static void alg_sk_destruct(struct sock *sk) {} +#endif + +void af_alg_sk_destruct_child(struct sock *sk) +{ + struct alg_sock *ask = alg_sk(sk); + struct alg_sock *parent_ask = alg_sk(ask->parent); + + audit_log_crypto_op(AUDIT_CRYPTO_OP_CTX_DEL, parent_ask->id, + ask->id, -1, + ask->type->alg_name(parent_ask->private), NULL); + + sock_put(ask->parent); + alg_sk_destruct(sk); +} +EXPORT_SYMBOL_GPL(af_alg_sk_destruct_child); + static void alg_do_release(const struct af_alg_type *type, void *private) { if (!type) @@ -176,6 +241,11 @@ static int alg_setkey(struct sock *sk, char __user *ukey, u8 *key; int err; + err = audit_log_crypto_op(AUDIT_CRYPTO_OP_TFM_KEY_IMPORT, ask->id, -1, + -1, type->alg_name(ask->private), NULL); + if (err) + return err; + key = sock_kmalloc(sk, keylen, GFP_KERNEL); if (!key) return -ENOMEM; @@ -236,7 +306,7 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) if (!type) goto unlock; - sk2 = sk_alloc(sock_net(sk), PF_ALG, GFP_KERNEL, &alg_proto); + sk2 = alg_sk_alloc(sock_net(sk)); err = -ENOMEM; if (!sk2) goto unlock; @@ -246,6 +316,7 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) err = type->accept(ask->private, sk2); if (err) { + alg_sk_destruct(sk2); sk_free(sk2); goto unlock; } @@ -256,6 +327,14 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) alg_sk(sk2)->parent = sk; alg_sk(sk2)->type = type; + err = audit_log_crypto_op(AUDIT_CRYPTO_OP_CTX_NEW, ask->id, + alg_sk(sk2)->id, -1, + type->alg_name(ask->private), NULL); + if (err) { + sk_free(sk2); + return err; + } + newsock->ops = type->ops; newsock->state = SS_CONNECTED; @@ -300,7 +379,11 @@ static void alg_sock_destruct(struct sock *sk) { struct alg_sock *ask = alg_sk(sk); + audit_log_crypto_op(AUDIT_CRYPTO_OP_TFM_DEL, ask->id, -1, -1, NULL, + NULL); + alg_do_release(ask->type, ask->private); + alg_sk_destruct(sk); } static int alg_create(struct net *net, struct socket *sock, int protocol, @@ -315,10 +398,18 @@ static int alg_create(struct net *net, struct socket *sock, int protocol, return -EPROTONOSUPPORT; err = -ENOMEM; - sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto); + sk = alg_sk_alloc(net); if (!sk) goto out; + err = audit_log_crypto_op(AUDIT_CRYPTO_OP_TFM_NEW, alg_sk(sk)->id, -1, + -1, NULL, NULL); + if (err) { + alg_sk_destruct(sk); + sk_free(sk); + goto out; + } + sock->ops = &alg_proto_ops; sock_init_data(sock, sk); @@ -452,6 +543,20 @@ void af_alg_complete(struct crypto_async_request *req, int err) } EXPORT_SYMBOL_GPL(af_alg_complete); +#ifdef CONFIG_AUDIT +int af_alg_audit_crypto_op(struct sock *sk, const char *operation, int ctx2) +{ + struct alg_sock *ask = alg_sk(sk); + struct alg_sock *parent_ask = alg_sk(ask->parent); + const char *alg_name; + + alg_name = parent_ask->type->alg_name(parent_ask->private); + return audit_log_crypto_op(AUDIT_CRYPTO_OP_CTX_OP, parent_ask->id, + ask->id, ctx2, alg_name, operation); +} +EXPORT_SYMBOL_GPL(af_alg_audit_crypto_op); +#endif + static int __init af_alg_init(void) { int err = proto_register(&alg_proto, 0); @@ -475,6 +580,7 @@ static void __exit af_alg_exit(void) { sock_unregister(PF_ALG); proto_unregister(&alg_proto); + ida_destroy(&alg_ida); } module_init(af_alg_init); diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c index 62122a1a2f7..7e3ffd16e0a 100644 --- a/crypto/algif_hash.c +++ b/crypto/algif_hash.c @@ -46,6 +46,10 @@ static int hash_sendmsg(struct kiocb *unused, struct socket *sock, long copied = 0; int err; + err = af_alg_audit_crypto_op(sk, "hash-input", -1); + if (err) + return err; + if (limit > sk->sk_sndbuf) limit = sk->sk_sndbuf; @@ -112,6 +116,10 @@ static ssize_t hash_sendpage(struct socket *sock, struct page *page, struct hash_ctx *ctx = ask->private; int err; + err = af_alg_audit_crypto_op(sk, "hash-input", -1); + if (err) + return err; + lock_sock(sk); sg_init_table(ctx->sgl.sg, 1); sg_set_page(ctx->sgl.sg, page, size, offset); @@ -154,6 +162,10 @@ static int hash_recvmsg(struct kiocb *unused, struct socket *sock, unsigned ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)); int err; + err = af_alg_audit_crypto_op(sk, "hash-output", -1); + if (err) + return err; + if (len > ds) len = ds; else if (len < ds) @@ -202,13 +214,20 @@ static int hash_accept(struct socket *sock, struct socket *newsock, int flags) ctx2 = ask2->private; ctx2->more = 1; + err = af_alg_audit_crypto_op(sk, "hash-clone", ask2->id); + if (err) + goto error_sk2; + err = crypto_ahash_import(&ctx2->req, state); - if (err) { - sock_orphan(sk2); - sock_put(sk2); - } + if (err) + goto error_sk2; return err; + +error_sk2: + sock_orphan(sk2); + sock_put(sk2); + return err; } static struct proto_ops algif_hash_ops = { @@ -243,6 +262,11 @@ static void hash_release(void *private) crypto_free_ahash(private); } +static const char *hash_alg_name(void *private) +{ + return crypto_tfm_alg_name(crypto_ahash_tfm(private)); +} + static int hash_setkey(void *private, const u8 *key, unsigned int keylen) { return crypto_ahash_setkey(private, key, keylen); @@ -256,7 +280,7 @@ 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); - af_alg_release_parent(sk); + af_alg_sk_destruct_child(sk); } static int hash_accept_parent(void *private, struct sock *sk) @@ -296,6 +320,7 @@ static int hash_accept_parent(void *private, struct sock *sk) static const struct af_alg_type algif_type_hash = { .bind = hash_bind, .release = hash_release, + .alg_name = hash_alg_name, .setkey = hash_setkey, .accept = hash_accept_parent, .ops = &algif_hash_ops, diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 6a6dfc062d2..1d8fb210449 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -286,6 +286,11 @@ static int skcipher_sendmsg(struct kiocb *unused, struct socket *sock, memcpy(ctx->iv, con.iv->iv, ivsize); } + err = af_alg_audit_crypto_op(sk, ctx->enc ? "encrypt-input" + : "decrypt-input", -1); + if (err) + goto unlock; + while (size) { struct scatterlist *sg; unsigned long len = size; @@ -376,8 +381,14 @@ static ssize_t skcipher_sendpage(struct socket *sock, struct page *page, struct alg_sock *ask = alg_sk(sk); struct skcipher_ctx *ctx = ask->private; struct skcipher_sg_list *sgl; - int err = -EINVAL; + int err; + err = af_alg_audit_crypto_op(sk, ctx->enc ? "encrypt-input" + : "decrypt-input", -1); + if (err) + return err; + + err = -EINVAL; lock_sock(sk); if (!ctx->more && ctx->used) goto unlock; @@ -427,10 +438,15 @@ static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock, struct scatterlist *sg; unsigned long iovlen; struct iovec *iov; - int err = -EAGAIN; + int err; int used; long copied = 0; + err = af_alg_audit_crypto_op(sk, ctx->enc ? "encrypt-output" + : "decrypt-output", -1); + if (err) + return err; + lock_sock(sk); for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0; iovlen--, iov++) { @@ -551,6 +567,11 @@ static void skcipher_release(void *private) crypto_free_ablkcipher(private); } +static const char *skcipher_alg_name(void *private) +{ + return crypto_tfm_alg_name(crypto_ablkcipher_tfm(private)); +} + static int skcipher_setkey(void *private, const u8 *key, unsigned int keylen) { return crypto_ablkcipher_setkey(private, key, keylen); @@ -565,7 +586,7 @@ static void skcipher_sock_destruct(struct sock *sk) skcipher_free_sgl(sk); sock_kfree_s(sk, ctx->iv, crypto_ablkcipher_ivsize(tfm)); sock_kfree_s(sk, ctx, ctx->len); - af_alg_release_parent(sk); + af_alg_sk_destruct_child(sk); } static int skcipher_accept_parent(void *private, struct sock *sk) @@ -609,6 +630,7 @@ static int skcipher_accept_parent(void *private, struct sock *sk) static const struct af_alg_type algif_type_skcipher = { .bind = skcipher_bind, .release = skcipher_release, + .alg_name = skcipher_alg_name, .setkey = skcipher_setkey, .accept = skcipher_accept_parent, .ops = &algif_skcipher_ops, |