summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'crypto')
-rw-r--r--crypto/af_alg.c110
-rw-r--r--crypto/algif_hash.c35
-rw-r--r--crypto/algif_skcipher.c28
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,