diff --git a/include/linux/audit.h b/include/linux/audit.h index c4a755ae0d61ad..304fbb7c3c5bda 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -128,6 +128,13 @@ struct audit_task_info { extern struct audit_task_info init_struct_audit; +struct audit_contobj_netns { + struct list_head list; + struct audit_contobj *obj; + int count; + struct rcu_head rcu; +}; + extern int is_audit_feature_set(int which); extern int __init audit_register_class(int class, unsigned *list); @@ -233,6 +240,11 @@ static inline u64 audit_get_contid(struct task_struct *tsk) extern void audit_log_container_id(struct audit_context *context, struct audit_contobj *cont); +extern void audit_copy_namespaces(struct net *net, struct task_struct *tsk); +extern void audit_switch_task_namespaces(struct nsproxy *ns, + struct task_struct *p); +extern void audit_log_netns_contid_list(struct net *net, + struct audit_context *context); extern u32 audit_enabled; @@ -306,6 +318,14 @@ static inline u64 audit_get_contid(struct task_struct *tsk) static inline void audit_log_container_id(struct audit_context *context, struct audit_contobj *cont) { } +static inline void audit_copy_namespaces(struct net *net, struct task_struct *tsk) +{ } +static inline void audit_switch_task_namespaces(struct nsproxy *ns, + struct task_struct *p) +{ } +static inline void audit_log_netns_contid_list(struct net *net, + struct audit_context *context) +{ } #define audit_enabled AUDIT_OFF diff --git a/kernel/audit.c b/kernel/audit.c index 997c34178ee875..a862721dfd9b5f 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -59,6 +59,7 @@ #include #include #include +#include #include "audit.h" @@ -86,9 +87,13 @@ static unsigned int audit_net_id; /** * struct audit_net - audit private network namespace data * @sk: communication socket + * @contobj_list: audit container identifier list + * @contobj_list_lock audit container identifier list lock */ struct audit_net { struct sock *sk; + struct list_head contobj_list; + spinlock_t contobj_list_lock; }; /** @@ -214,6 +219,9 @@ struct audit_reply { static struct kmem_cache *audit_task_cache; +void audit_netns_contid_add(struct net *net, struct audit_contobj *cont); +void audit_netns_contid_del(struct net *net, struct audit_contobj *cont); + void __init audit_task_init(void) { audit_task_cache = kmem_cache_create("audit_task", @@ -326,10 +334,17 @@ struct audit_task_info init_struct_audit = { void audit_free(struct task_struct *tsk) { struct audit_task_info *info = tsk->audit; + struct nsproxy *ns = tsk->nsproxy; + struct audit_contobj *cont; audit_free_syscall(tsk); rcu_read_lock(); - _audit_contobj_put(tsk->audit->cont); + cont = _audit_contobj_get(tsk); + if (ns) { + audit_netns_contid_del(ns->net_ns, cont); + _audit_contobj_put(cont); + } + _audit_contobj_put(cont); rcu_read_unlock(); /* Freeing the audit_task_info struct must be performed after * audit_log_exit() due to need for loginuid and sessionid. @@ -437,6 +452,136 @@ static struct sock *audit_get_sk(const struct net *net) return aunet->sk; } +void audit_netns_contid_add(struct net *net, struct audit_contobj *cont) +{ + struct audit_net *aunet; + struct list_head *contobj_list; + struct audit_contobj_netns *contns; + + if (!net) + return; + if (!cont) + return; + aunet = net_generic(net, audit_net_id); + if (!aunet) + return; + contobj_list = &aunet->contobj_list; + rcu_read_lock(); + spin_lock(&aunet->contobj_list_lock); + list_for_each_entry_rcu(contns, contobj_list, list) + if (contns->obj == cont) { + contns->count++; + goto out; + } + contns = kmalloc(sizeof(*contns), GFP_ATOMIC); + if (contns) { + INIT_LIST_HEAD(&contns->list); + contns->obj = cont; + contns->count = 1; + list_add_rcu(&contns->list, contobj_list); + } +out: + spin_unlock(&aunet->contobj_list_lock); + rcu_read_unlock(); +} + +void audit_netns_contid_del(struct net *net, struct audit_contobj *cont) +{ + struct audit_net *aunet; + struct list_head *contobj_list; + struct audit_contobj_netns *contns = NULL; + + if (!net) + return; + if (!cont) + return; + aunet = net_generic(net, audit_net_id); + if (!aunet) + return; + contobj_list = &aunet->contobj_list; + rcu_read_lock(); + spin_lock(&aunet->contobj_list_lock); + list_for_each_entry_rcu(contns, contobj_list, list) + if (contns->obj == cont) { + contns->count--; + if (contns->count < 1) { + list_del_rcu(&contns->list); + kfree_rcu(contns, rcu); + } + break; + } + spin_unlock(&aunet->contobj_list_lock); + rcu_read_unlock(); +} + +void audit_copy_namespaces(struct net *net, struct task_struct *tsk) +{ + struct audit_contobj *cont; + + rcu_read_lock(); + cont = _audit_contobj_get(tsk); + audit_netns_contid_add(net, cont); + rcu_read_unlock(); +} + +void audit_switch_task_namespaces(struct nsproxy *ns, struct task_struct *p) +{ + struct audit_contobj *cont; + struct nsproxy *new = p->nsproxy; + + rcu_read_lock(); + cont = _audit_contobj_get(p); + if (!cont) + goto out; + audit_netns_contid_del(ns->net_ns, cont); + if (new) + audit_netns_contid_add(new->net_ns, cont); + else + _audit_contobj_put(cont); + _audit_contobj_put(cont); +out: + rcu_read_unlock(); +} + +/** + * audit_log_netns_contid_list - List contids for the given network namespace + * @net: the network namespace of interest + * @context: the audit context to use + * + * Description: + * Issues a CONTAINER_ID record with a CSV list of contids associated + * with a network namespace to accompany a NETFILTER_PKT record. + */ +void audit_log_netns_contid_list(struct net *net, struct audit_context *context) +{ + struct audit_buffer *ab = NULL; + struct audit_contobj_netns *cont; + struct audit_net *aunet; + + /* Generate AUDIT_CONTAINER_ID record with container ID CSV list */ + rcu_read_lock(); + aunet = net_generic(net, audit_net_id); + if (!aunet) + goto out; + list_for_each_entry_rcu(cont, &aunet->contobj_list, list) { + if (!ab) { + ab = audit_log_start(context, GFP_ATOMIC, + AUDIT_CONTAINER_ID); + if (!ab) { + audit_log_lost("out of memory in audit_log_netns_contid_list"); + goto out; + } + audit_log_format(ab, "contid="); + } else + audit_log_format(ab, ","); + audit_log_format(ab, "%llu", cont->obj->id); + } + audit_log_end(ab); +out: + rcu_read_unlock(); +} +EXPORT_SYMBOL(audit_log_netns_contid_list); + void audit_panic(const char *message) { switch (audit_failure) { @@ -1786,7 +1931,6 @@ static int __net_init audit_net_init(struct net *net) .flags = NL_CFG_F_NONROOT_RECV, .groups = AUDIT_NLGRP_MAX, }; - struct audit_net *aunet = net_generic(net, audit_net_id); aunet->sk = netlink_kernel_create(net, NETLINK_AUDIT, &cfg); @@ -1795,7 +1939,8 @@ static int __net_init audit_net_init(struct net *net) return -ENOMEM; } aunet->sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; - + INIT_LIST_HEAD(&aunet->contobj_list); + spin_lock_init(&aunet->contobj_list_lock); return 0; } @@ -2585,6 +2730,7 @@ int audit_set_contid(struct task_struct *task, u64 contid) int rc = 0; struct audit_buffer *ab; struct audit_contobj *oldcont = NULL; + struct net *net = task->nsproxy->net_ns; task_lock(task); /* Can't set if audit disabled */ @@ -2657,6 +2803,10 @@ int audit_set_contid(struct task_struct *task, u64 contid) spin_unlock(&audit_contobj_list_lock); task->audit->cont = newcont; _audit_contobj_put(oldcont); + audit_netns_contid_del(net, oldcont); + _audit_contobj_put(oldcont); + _audit_contobj_hold(newcont); + audit_netns_contid_add(net, newcont); } conterror: task_unlock(task); diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index b03df67621d01f..5eddb3377049fb 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -26,6 +26,7 @@ #include #include #include +#include static struct kmem_cache *nsproxy_cachep; @@ -187,6 +188,8 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) } tsk->nsproxy = new_ns; + if (flags & CLONE_NEWNET) + audit_copy_namespaces(new_ns->net_ns, tsk); return 0; } @@ -249,6 +252,7 @@ void switch_task_namespaces(struct task_struct *p, struct nsproxy *new) ns = p->nsproxy; p->nsproxy = new; task_unlock(p); + audit_switch_task_namespaces(ns, p); if (ns && atomic_dec_and_test(&ns->count)) free_nsproxy(ns); diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c index fe4831f2258f34..98d1e7e1a83c8d 100644 --- a/net/netfilter/nft_log.c +++ b/net/netfilter/nft_log.c @@ -66,13 +66,16 @@ static void nft_log_eval_audit(const struct nft_pktinfo *pkt) struct sk_buff *skb = pkt->skb; struct audit_buffer *ab; int fam = -1; + struct audit_context *context; + struct net *net; if (!audit_enabled) return; - ab = audit_log_start(NULL, GFP_ATOMIC, AUDIT_NETFILTER_PKT); + context = audit_alloc_local(GFP_ATOMIC); + ab = audit_log_start(context, GFP_ATOMIC, AUDIT_NETFILTER_PKT); if (!ab) - return; + goto errout; audit_log_format(ab, "mark=%#x", skb->mark); @@ -99,6 +102,10 @@ static void nft_log_eval_audit(const struct nft_pktinfo *pkt) audit_log_format(ab, " saddr=? daddr=? proto=-1"); audit_log_end(ab); + net = xt_net(&pkt->xt); + audit_log_netns_contid_list(net, context); +errout: + audit_free_context(context); } static void nft_log_eval(const struct nft_expr *expr, diff --git a/net/netfilter/xt_AUDIT.c b/net/netfilter/xt_AUDIT.c index 9cdc16b0d0d814..ecf868a1abde08 100644 --- a/net/netfilter/xt_AUDIT.c +++ b/net/netfilter/xt_AUDIT.c @@ -68,10 +68,13 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par) { struct audit_buffer *ab; int fam = -1; + struct audit_context *context; + struct net *net; if (audit_enabled == AUDIT_OFF) - goto errout; - ab = audit_log_start(NULL, GFP_ATOMIC, AUDIT_NETFILTER_PKT); + goto out; + context = audit_alloc_local(GFP_ATOMIC); + ab = audit_log_start(context, GFP_ATOMIC, AUDIT_NETFILTER_PKT); if (ab == NULL) goto errout; @@ -101,7 +104,11 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par) audit_log_end(ab); + net = xt_net(par); + audit_log_netns_contid_list(net, context); errout: + audit_free_context(context); +out: return XT_CONTINUE; }