From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.1 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH, MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1D4B8C433E0 for ; Sat, 27 Jun 2020 13:23:27 +0000 (UTC) Received: from us-smtp-delivery-1.mimecast.com (us-smtp-2.mimecast.com [207.211.31.81]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id AD825208B6 for ; Sat, 27 Jun 2020 13:23:26 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="gDjn3dKT" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org AD825208B6 Authentication-Results: mail.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=linux-audit-bounces@redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1593264205; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:in-reply-to:references:references:references: list-id:list-help:list-unsubscribe:list-subscribe:list-post; bh=0VsWsbe/x0gubIbbdOq4RmyKRsm8mEQ5bbOdW8uxul4=; b=gDjn3dKTTy2AbDrOOHeQw2dcdKGQSiD4L+tjAcHJV3vjEhYNNNjc+fM2jKDXDeP75VxftM uk6PMWwJkq/lmwp2nCm4Q0K3OGUivHFPNQf4fi+aKBBsZwWTFMmff2+QmHdN+q8njvQbjv LAN5VV3pyG1YjjcoxZCqMyZfa78+CIs= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-133-j70R5aBhO4SnrLNCeJ4GRQ-1; Sat, 27 Jun 2020 09:23:23 -0400 X-MC-Unique: j70R5aBhO4SnrLNCeJ4GRQ-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7E6B1800C60; Sat, 27 Jun 2020 13:23:20 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 67EC61C8; Sat, 27 Jun 2020 13:23:20 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 4A98787861; Sat, 27 Jun 2020 13:23:20 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 05RDNJc2007726 for ; Sat, 27 Jun 2020 09:23:19 -0400 Received: by smtp.corp.redhat.com (Postfix) id 2992E8E7FB; Sat, 27 Jun 2020 13:23:19 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.28]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8A09D71662; Sat, 27 Jun 2020 13:23:14 +0000 (UTC) From: Richard Guy Briggs To: containers@lists.linux-foundation.org, linux-api@vger.kernel.org, Linux-Audit Mailing List , linux-fsdevel@vger.kernel.org, LKML , netdev@vger.kernel.org, netfilter-devel@vger.kernel.org Subject: [PATCH ghak90 V9 10/13] audit: add support for containerid to network namespaces Date: Sat, 27 Jun 2020 09:20:43 -0400 Message-Id: In-Reply-To: References: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-loop: linux-audit@redhat.com Cc: nhorman@tuxdriver.com, Richard Guy Briggs , dhowells@redhat.com, ebiederm@xmission.com, simo@redhat.com, eparis@parisplace.org, mpatel@redhat.com, serge@hallyn.com X-BeenThere: linux-audit@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Linux Audit Discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-audit-bounces@redhat.com Errors-To: linux-audit-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit This also adds support to qualify NETFILTER_PKT records. Audit events could happen in a network namespace outside of a task context due to packets received from the net that trigger an auditing rule prior to being associated with a running task. The network namespace could be in use by multiple containers by association to the tasks in that network namespace. We still want a way to attribute these events to any potential containers. Keep a list per network namespace to track these audit container identifiiers. Add/increment the audit container identifier on: - initial setting of the audit container identifier via /proc - clone/fork call that inherits an audit container identifier - unshare call that inherits an audit container identifier - setns call that inherits an audit container identifier Delete/decrement the audit container identifier on: - an inherited audit container identifier dropped when child set - process exit - unshare call that drops a net namespace - setns call that drops a net namespace Add audit container identifier auxiliary record(s) to NETFILTER_PKT event standalone records. Iterate through all potential audit container identifiers associated with a network namespace. Please see the github audit kernel issue for contid net support: https://github.com/linux-audit/audit-kernel/issues/92 Please see the github audit testsuiite issue for the test case: https://github.com/linux-audit/audit-testsuite/issues/64 Please see the github audit wiki for the feature overview: https://github.com/linux-audit/audit-kernel/wiki/RFE-Audit-Container-ID Signed-off-by: Richard Guy Briggs Acked-by: Neil Horman Reviewed-by: Ondrej Mosnacek --- include/linux/audit.h | 20 ++++++ kernel/audit.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++- kernel/nsproxy.c | 4 ++ net/netfilter/nft_log.c | 11 +++- net/netfilter/xt_AUDIT.c | 11 +++- 5 files changed, 195 insertions(+), 7 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index c4a755ae0d61..304fbb7c3c5b 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 997c34178ee8..a862721dfd9b 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -59,6 +59,7 @@ #include #include #include +#include #include "audit.h" @@ -86,9 +87,13 @@ /** * 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 b03df67621d0..5eddb3377049 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 fe4831f2258f..98d1e7e1a83c 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 9cdc16b0d0d8..ecf868a1abde 100644 --- a/net/netfilter/xt_AUDIT.c +++ b/net/netfilter/xt_AUDIT.c @@ -68,10 +68,13 @@ static bool audit_ip6(struct audit_buffer *ab, struct sk_buff *skb) { 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 @@ static bool audit_ip6(struct audit_buffer *ab, struct sk_buff *skb) audit_log_end(ab); + net = xt_net(par); + audit_log_netns_contid_list(net, context); errout: + audit_free_context(context); +out: return XT_CONTINUE; } -- 1.8.3.1 -- Linux-audit mailing list Linux-audit@redhat.com https://www.redhat.com/mailman/listinfo/linux-audit