diff options
-rw-r--r-- | crypto/af_alg.c | 110 | ||||
-rw-r--r-- | crypto/algif_hash.c | 35 | ||||
-rw-r--r-- | crypto/algif_skcipher.c | 28 | ||||
-rw-r--r-- | include/crypto/if_alg.h | 17 | ||||
-rw-r--r-- | include/linux/audit.h | 22 | ||||
-rw-r--r-- | kernel/auditfilter.c | 2 | ||||
-rw-r--r-- | kernel/auditsc.c | 97 |
7 files changed, 296 insertions, 15 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, diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index c5813c87de0..6650ae5761f 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -31,6 +31,9 @@ struct alg_sock { const struct af_alg_type *type; void *private; +#ifdef CONFIG_AUDIT + int id; +#endif }; struct af_alg_completion { @@ -46,6 +49,7 @@ struct af_alg_control { struct af_alg_type { void *(*bind)(const char *name, u32 type, u32 mask); void (*release)(void *private); + const char *(*alg_name)(void *private); int (*setkey)(void *private, const u8 *key, unsigned int keylen); int (*accept)(void *private, struct sock *sk); @@ -62,6 +66,8 @@ struct af_alg_sgl { int af_alg_register_type(const struct af_alg_type *type); int af_alg_unregister_type(const struct af_alg_type *type); +void af_alg_sk_destruct_child(struct sock *sk); + int af_alg_release(struct socket *sock); int af_alg_accept(struct sock *sk, struct socket *newsock); @@ -74,16 +80,17 @@ int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con); int af_alg_wait_for_completion(int err, struct af_alg_completion *completion); void af_alg_complete(struct crypto_async_request *req, int err); +#ifdef CONFIG_AUDIT +int af_alg_audit_crypto_op(struct sock *sk, const char *operation, int ctx2); +#else +#define af_alg_audit_crypto_op(sk, operation, ctx2) (0) +#endif + 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); diff --git a/include/linux/audit.h b/include/linux/audit.h index f391d45c8ae..a9516daa771 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -122,6 +122,8 @@ #define AUDIT_MAC_UNLBL_STCADD 1416 /* NetLabel: add a static label */ #define AUDIT_MAC_UNLBL_STCDEL 1417 /* NetLabel: del a static label */ +#define AUDIT_CRYPTO_USERSPACE_OP 1600 /* User-space crypto operation */ + #define AUDIT_FIRST_KERN_ANOM_MSG 1700 #define AUDIT_LAST_KERN_ANOM_MSG 1799 #define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */ @@ -219,6 +221,7 @@ #define AUDIT_PERM 106 #define AUDIT_DIR 107 #define AUDIT_FILETYPE 108 +#define AUDIT_CRYPTO_OP 109 #define AUDIT_ARG0 200 #define AUDIT_ARG1 (AUDIT_ARG0+1) @@ -314,6 +317,13 @@ enum { #define AUDIT_PERM_READ 4 #define AUDIT_PERM_ATTR 8 +#define AUDIT_CRYPTO_OP_TFM_NEW 1 +#define AUDIT_CRYPTO_OP_TFM_KEY_IMPORT 2 +#define AUDIT_CRYPTO_OP_TFM_DEL 3 +#define AUDIT_CRYPTO_OP_CTX_NEW 4 +#define AUDIT_CRYPTO_OP_CTX_OP 5 +#define AUDIT_CRYPTO_OP_CTX_DEL 6 + struct audit_status { __u32 mask; /* Bit mask for valid entries */ __u32 enabled; /* 1 = enabled, 0 = disabled */ @@ -478,6 +488,8 @@ extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm, const struct cred *new, const struct cred *old); extern void __audit_log_capset(pid_t pid, const struct cred *new, const struct cred *old); +extern int __audit_log_crypto_op(int op, int tfm, int ctx, int ctx2, + const char *algorithm, const char *operation); static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp) { @@ -531,6 +543,15 @@ static inline void audit_log_capset(pid_t pid, const struct cred *new, __audit_log_capset(pid, new, old); } +static inline int audit_log_crypto_op(int op, int tfm, int ctx, int ctx2, + const char *algorithm, + const char *operation) +{ + if (unlikely(!audit_dummy_context())) + return __audit_log_crypto_op(op, tfm, ctx, ctx2, algorithm, operation); + return 0; +} + extern int audit_n_rules; extern int audit_signals; #else @@ -564,6 +585,7 @@ extern int audit_signals; #define audit_mq_getsetattr(d,s) ((void)0) #define audit_log_bprm_fcaps(b, ncr, ocr) ({ 0; }) #define audit_log_capset(pid, ncr, ocr) ((void)0) +#define audit_log_crypto_op(op, tfm, ctx, ctx2, algorithm, operation) (0) #define audit_ptrace(t) ((void)0) #define audit_n_rules 0 #define audit_signals 0 diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index ce08041f578..6f35eedcd99 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -364,6 +364,7 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) case AUDIT_DEVMINOR: case AUDIT_EXIT: case AUDIT_SUCCESS: + case AUDIT_CRYPTO_OP: /* bit ops are only useful on syscall args */ if (f->op == Audit_bitmask || f->op == Audit_bittest) goto exit_free; @@ -454,6 +455,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, case AUDIT_DEVMINOR: case AUDIT_EXIT: case AUDIT_SUCCESS: + case AUDIT_CRYPTO_OP: case AUDIT_ARG0: case AUDIT_ARG1: case AUDIT_ARG2: diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 3828ad5fb8f..e9761ad22ce 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -158,6 +158,16 @@ struct audit_aux_data_capset { struct audit_cap_data cap; }; +struct audit_aux_data_crypto_op { + struct audit_aux_data d; + int op; + int tfm; + int ctx; /* -1 means N/A */ + int ctx2; /* -1 means N/A */ + const char *algorithm; /* NULL means N/A */ + const char *operation; /* NULL means N/A */ +}; + struct audit_tree_refs { struct audit_tree_refs *next; struct audit_chunk *c[31]; @@ -633,6 +643,24 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_FILETYPE: result = audit_match_filetype(ctx, f->val); break; + case AUDIT_CRYPTO_OP: { + struct audit_aux_data *aux; + + if (!ctx) + break; + for (aux = ctx->aux; aux; aux = aux->next) { + struct audit_aux_data_crypto_op *ax; + + if (aux->type != AUDIT_CRYPTO_USERSPACE_OP) + continue; + ax = (void *)aux; + result = audit_comparator(ax->op, f->op, + f->val); + if (result) + break; + } + break; + } } if (!result) { @@ -1201,6 +1229,37 @@ static void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *c } } +static void log_crypto_op(struct audit_buffer *ab, + const struct audit_aux_data_crypto_op *crypto) +{ + static const char *const ops[] = { + [AUDIT_CRYPTO_OP_TFM_NEW] = "tfm_new", + [AUDIT_CRYPTO_OP_TFM_KEY_IMPORT] = "tfm_key_import", + [AUDIT_CRYPTO_OP_TFM_DEL] = "tfm_del", + [AUDIT_CRYPTO_OP_CTX_NEW] = "ctx_new", + [AUDIT_CRYPTO_OP_CTX_OP] = "ctx_op", + [AUDIT_CRYPTO_OP_CTX_DEL] = "ctx_del", + }; + + if (crypto->op < ARRAY_SIZE(ops) && ops[crypto->op] != NULL) + audit_log_format(ab, "crypto_op=%s", ops[crypto->op]); + else + audit_log_format(ab, "crypto_op=%d", crypto->op); + audit_log_format(ab, " tfm=%d", crypto->tfm); + if (crypto->ctx != -1) + audit_log_format(ab, " ctx=%d", crypto->ctx); + if (crypto->ctx2 != -1) + audit_log_format(ab, " ctx2=%d", crypto->ctx2); + if (crypto->algorithm != NULL) { + audit_log_format(ab, " algorithm="); + audit_log_string(ab, crypto->algorithm); + } + if (crypto->operation != NULL) { + audit_log_format(ab, " operation="); + audit_log_string(ab, crypto->operation); + } +} + static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name) { kernel_cap_t *perm = &name->fcap.permitted; @@ -1404,6 +1463,11 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts audit_log_cap(ab, "new_pe", &axs->new_pcap.effective); break; } + case AUDIT_CRYPTO_USERSPACE_OP: { + struct audit_aux_data_crypto_op *axc = (void *)aux; + log_crypto_op(ab, axc); + break; } + } audit_log_end(ab); } @@ -2464,6 +2528,39 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm, } /** + * __audit_log_crypto_op - store information about an user-space crypto op + * @op: AUDIT_CRYPTO_OP_* + * @tfm: unique transform ID + * @ctx: unique context ID, or -1 + * @ctx2: secondary context ID, or -1 + * @algorithm: algorithm (crypto API transform) name, or NULL + * @operation: more detailed operation description, or NULL + */ +int __audit_log_crypto_op(int op, int tfm, int ctx, int ctx2, + const char *algorithm, const char *operation) +{ + struct audit_aux_data_crypto_op *ax; + struct audit_context *context = current->audit_context; + + ax = kmalloc(sizeof(*ax), GFP_KERNEL); + if (!ax) + return -ENOMEM; + + ax->d.type = AUDIT_CRYPTO_USERSPACE_OP; + ax->d.next = context->aux; + context->aux = (void *)ax; + + ax->op = op; + ax->tfm = tfm; + ax->ctx = ctx; + ax->ctx2 = ctx2; + ax->algorithm = algorithm; + ax->operation = operation; + return 0; +} +EXPORT_SYMBOL_GPL(__audit_log_crypto_op); + +/** * __audit_log_capset - store information about the arguments to the capset syscall * @pid: target pid of the capset call * @new: the new credentials |