From patchwork Tue Jan 12 15:09:29 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Guy Briggs X-Patchwork-Id: 12013715 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=-21.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING, SPF_HELO_NONE,SPF_PASS autolearn=unavailable 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 5528CC432C3 for ; Tue, 12 Jan 2021 15:12:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 10D9423132 for ; Tue, 12 Jan 2021 15:12:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390490AbhALPMH (ORCPT ); Tue, 12 Jan 2021 10:12:07 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:38810 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390181AbhALPMB (ORCPT ); Tue, 12 Jan 2021 10:12:01 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610464233; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:in-reply-to:in-reply-to:references:references; bh=Br6UmZPweT8MUuQTTCD+NBxLtgua+tRUJV8L4ufRw/o=; b=A8Zfr8WYYN1IaymllkHweD+JPvDV1WiyNqNIXFnx0x5pGveOzhf1TUemyABig8mbRC1Qvn 7a5jiSl0Di/Af59pVN/tEabaoU1K/A4JRRcZ4YgTH3trMWXbc1rdFdHJdPtrOhFvKv8EV9 nXZUIVQjSmcbTKNfjFJ5aQ8o3pocYJc= 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-502-BHyM5plVMQqX5GwBatwVMQ-1; Tue, 12 Jan 2021 10:10:29 -0500 X-MC-Unique: BHyM5plVMQqX5GwBatwVMQ-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 76F98EC1A4; Tue, 12 Jan 2021 15:10:27 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2CC4A5D9D2; Tue, 12 Jan 2021 15:10:20 +0000 (UTC) From: Richard Guy Briggs To: Linux Containers List , Linux API , Linux-Audit Mailing List , Linux FSdevel , LKML , Linux NetDev Upstream Mailing List , Netfilter Devel List Cc: Neil Horman , David Howells , "Eric W. Biederman" , Simo Sorce , Eric Paris , mpatel@redhat.com, Serge Hallyn , Kees Cook , Jens Axboe , Christian Brauner , Richard Guy Briggs Subject: [PATCH ghak90 v11 01/11] audit: collect audit task parameters Date: Tue, 12 Jan 2021 10:09:29 -0500 Message-Id: <316570a25807c32f0d1061ce0119f3faba1dda7d.1610399347.git.rgb@redhat.com> In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org The audit-related parameters in struct task_struct should ideally be collected together and accessed through a standard audit API and the audit structures made opaque to other kernel subsystems. Collect the existing loginuid, sessionid and audit_context together in a new opaque struct audit_task_info called "audit" in struct task_struct. Use kmem_cache to manage this pool of memory. Un-inline audit_free() to be able to always recover that memory. Please see the upstream github issues https://github.com/linux-audit/audit-kernel/issues/81 https://github.com/linux-audit/audit-kernel/issues/90 Signed-off-by: Richard Guy Briggs --- Acks removed due to significant code changes hiding audit task struct: Acked-by: Neil Horman Reviewed-by: Ondrej Mosnacek --- fs/io-wq.c | 8 +-- fs/io_uring.c | 16 ++--- include/linux/audit.h | 49 +++++--------- include/linux/sched.h | 7 +- init/init_task.c | 3 +- init/main.c | 2 + kernel/audit.c | 154 +++++++++++++++++++++++++++++++++++++++++- kernel/audit.h | 7 ++ kernel/auditsc.c | 24 ++++--- kernel/fork.c | 1 - 10 files changed, 205 insertions(+), 66 deletions(-) diff --git a/fs/io-wq.c b/fs/io-wq.c index a564f36e260c..0c57a20f4feb 100644 --- a/fs/io-wq.c +++ b/fs/io-wq.c @@ -499,8 +499,8 @@ static void io_impersonate_work(struct io_worker *worker, current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; io_wq_switch_blkcg(worker, work); #ifdef CONFIG_AUDIT - current->loginuid = work->identity->loginuid; - current->sessionid = work->identity->sessionid; + audit_set_loginuid_iouring(work->identity->loginuid); + audit_set_sessionid_iouring(work->identity->sessionid); #endif } @@ -515,8 +515,8 @@ static void io_assign_current_work(struct io_worker *worker, } #ifdef CONFIG_AUDIT - current->loginuid = KUIDT_INIT(AUDIT_UID_UNSET); - current->sessionid = AUDIT_SID_UNSET; + audit_set_loginuid_iouring(KUIDT_INIT(AUDIT_UID_UNSET)); + audit_set_sessionid_iouring(AUDIT_SID_UNSET); #endif spin_lock_irq(&worker->lock); diff --git a/fs/io_uring.c b/fs/io_uring.c index ca46f314640b..7aaff14d2859 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -1216,8 +1216,8 @@ static void io_init_identity(struct io_identity *id) id->fs = current->fs; id->fsize = rlimit(RLIMIT_FSIZE); #ifdef CONFIG_AUDIT - id->loginuid = current->loginuid; - id->sessionid = current->sessionid; + id->loginuid = audit_get_loginuid(current); + id->sessionid = audit_get_sessionid(current); #endif refcount_set(&id->count, 1); } @@ -1473,8 +1473,8 @@ static bool io_grab_identity(struct io_kiocb *req) req->work.flags |= IO_WQ_WORK_CREDS; } #ifdef CONFIG_AUDIT - if (!uid_eq(current->loginuid, id->loginuid) || - current->sessionid != id->sessionid) + if (!uid_eq(audit_get_loginuid(current), id->loginuid) || + audit_get_sessionid(current) != id->sessionid) return false; #endif if (!(req->work.flags & IO_WQ_WORK_FS) && @@ -7016,8 +7016,8 @@ static int io_sq_thread(void *data) } io_sq_thread_associate_blkcg(ctx, &cur_css); #ifdef CONFIG_AUDIT - current->loginuid = ctx->loginuid; - current->sessionid = ctx->sessionid; + audit_set_loginuid_iouring(ctx->loginuid); + audit_set_sessionid_iouring(ctx->sessionid); #endif ret = __io_sq_thread(ctx, cap_entries); @@ -9528,8 +9528,8 @@ static int io_uring_create(unsigned entries, struct io_uring_params *p, ctx->user = user; ctx->creds = get_current_cred(); #ifdef CONFIG_AUDIT - ctx->loginuid = current->loginuid; - ctx->sessionid = current->sessionid; + ctx->loginuid = audit_get_loginuid(current); + ctx->sessionid = audit_get_sessionid(current); #endif ctx->sqo_task = get_task_struct(current); diff --git a/include/linux/audit.h b/include/linux/audit.h index 82b7c1116a85..515cc89a7e0c 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -154,6 +154,9 @@ struct filename; #ifdef CONFIG_AUDIT /* These are defined in audit.c */ /* Public API */ +extern int audit_alloc(struct task_struct *task); +extern void audit_free(struct task_struct *task); +extern void __init audit_task_init(void); extern __printf(4, 5) void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type, const char *fmt, ...); @@ -194,22 +197,26 @@ extern int audit_rule_change(int type, int seq, void *data, size_t datasz); extern int audit_list_rules_send(struct sk_buff *request_skb, int seq); extern int audit_set_loginuid(kuid_t loginuid); +extern void audit_set_loginuid_iouring(kuid_t loginuid); -static inline kuid_t audit_get_loginuid(struct task_struct *tsk) -{ - return tsk->loginuid; -} +extern kuid_t audit_get_loginuid(struct task_struct *tsk); -static inline unsigned int audit_get_sessionid(struct task_struct *tsk) -{ - return tsk->sessionid; -} +extern unsigned int audit_get_sessionid(struct task_struct *tsk); +extern void audit_set_sessionid_iouring(unsigned int sessionid); extern u32 audit_enabled; extern int audit_signal_info(int sig, struct task_struct *t); #else /* CONFIG_AUDIT */ +static inline int audit_alloc(struct task_struct *task) +{ + return 0; +} +static inline void audit_free(struct task_struct *task) +{ } +static inline void __init audit_task_init(void) +{ } static inline __printf(4, 5) void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type, const char *fmt, ...) @@ -285,8 +292,6 @@ static inline int audit_signal_info(int sig, struct task_struct *t) /* These are defined in auditsc.c */ /* Public API */ -extern int audit_alloc(struct task_struct *task); -extern void __audit_free(struct task_struct *task); extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3); extern void __audit_syscall_exit(int ret_success, long ret_value); @@ -303,26 +308,14 @@ extern void audit_seccomp_actions_logged(const char *names, const char *old_names, int res); extern void __audit_ptrace(struct task_struct *t); -static inline void audit_set_context(struct task_struct *task, struct audit_context *ctx) -{ - task->audit_context = ctx; -} - -static inline struct audit_context *audit_context(void) -{ - return current->audit_context; -} +extern struct audit_context *audit_context(void); static inline bool audit_dummy_context(void) { void *p = audit_context(); return !p || *(int *)p; } -static inline void audit_free(struct task_struct *task) -{ - if (unlikely(task->audit_context)) - __audit_free(task); -} + static inline void audit_syscall_entry(int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3) @@ -550,12 +543,6 @@ static inline void audit_log_nfcfg(const char *name, u8 af, extern int audit_n_rules; extern int audit_signals; #else /* CONFIG_AUDITSYSCALL */ -static inline int audit_alloc(struct task_struct *task) -{ - return 0; -} -static inline void audit_free(struct task_struct *task) -{ } static inline void audit_syscall_entry(int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3) @@ -566,8 +553,6 @@ static inline bool audit_dummy_context(void) { return true; } -static inline void audit_set_context(struct task_struct *task, struct audit_context *ctx) -{ } static inline struct audit_context *audit_context(void) { return NULL; diff --git a/include/linux/sched.h b/include/linux/sched.h index 6e3a5eeec509..1d10d81b8fd5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -38,7 +38,6 @@ #include /* task_struct member predeclarations (sorted alphabetically): */ -struct audit_context; struct backing_dev_info; struct bio_list; struct blk_plug; @@ -994,11 +993,7 @@ struct task_struct { struct callback_head *task_works; #ifdef CONFIG_AUDIT -#ifdef CONFIG_AUDITSYSCALL - struct audit_context *audit_context; -#endif - kuid_t loginuid; - unsigned int sessionid; + void *audit; #endif struct seccomp seccomp; struct syscall_user_dispatch syscall_dispatch; diff --git a/init/init_task.c b/init/init_task.c index 8a992d73e6fb..431e9e7a0b5c 100644 --- a/init/init_task.c +++ b/init/init_task.c @@ -134,8 +134,7 @@ struct task_struct init_task .thread_group = LIST_HEAD_INIT(init_task.thread_group), .thread_node = LIST_HEAD_INIT(init_signals.thread_head), #ifdef CONFIG_AUDIT - .loginuid = INVALID_UID, - .sessionid = AUDIT_SID_UNSET, + .audit = NULL, #endif #ifdef CONFIG_PERF_EVENTS .perf_event_mutex = __MUTEX_INITIALIZER(init_task.perf_event_mutex), diff --git a/init/main.c b/init/main.c index 6feee7f11eaf..7e92c22fbd2e 100644 --- a/init/main.c +++ b/init/main.c @@ -97,6 +97,7 @@ #include #include #include +#include #include #include @@ -1046,6 +1047,7 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void) nsfs_init(); cpuset_init(); cgroup_init(); + audit_task_init(); taskstats_init_early(); delayacct_init(); diff --git a/kernel/audit.c b/kernel/audit.c index 0bc0b20c6c04..a822fa3b8da3 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -208,6 +208,148 @@ struct audit_reply { struct sk_buff *skb; }; +struct audit_task_info { + kuid_t loginuid; + unsigned int sessionid; +#ifdef CONFIG_AUDITSYSCALL + struct audit_context *ctx; +#endif +}; + +static struct kmem_cache *audit_task_cache; + +void __init audit_task_init(void) +{ + audit_task_cache = kmem_cache_create("audit_task", + sizeof(struct audit_task_info), + 0, SLAB_PANIC, NULL); +} + +inline kuid_t audit_get_loginuid(struct task_struct *tsk) +{ + struct audit_task_info *info = tsk->audit; + + if (!info) + return INVALID_UID; + return info->loginuid; +} + +inline void audit_set_loginuid_iouring(kuid_t loginuid) +{ + struct audit_task_info *info = current->audit; + + if (!info) + return; + info->loginuid = loginuid; +} + +inline unsigned int audit_get_sessionid(struct task_struct *tsk) +{ + struct audit_task_info *info = tsk->audit; + + if (!info) + return AUDIT_SID_UNSET; + return info->sessionid; +} + +inline void audit_set_sessionid_iouring(unsigned int sessionid) +{ + struct audit_task_info *info = current->audit; + + if (!info) + return; + info->sessionid = sessionid; +} + +inline struct audit_context *_audit_context(struct task_struct *tsk) +{ + struct audit_task_info *info = tsk->audit; + + if (!info) + return NULL; + return info->ctx; +} + +struct audit_context *audit_context(void) +{ + return _audit_context(current); +} +EXPORT_SYMBOL(audit_context); + +static void audit_alloc_task(struct task_struct *tsk) +{ + struct audit_task_info *info = tsk->audit; + + if (info && !IS_ERR(info)) + return; + info = kmem_cache_alloc(audit_task_cache, GFP_KERNEL); + if (!info) { + tsk->audit = ERR_PTR(-ENOMEM); + return; + } + info->loginuid = audit_get_loginuid(current); + info->sessionid = audit_get_sessionid(current); + tsk->audit = info; +} + +void audit_set_context(struct task_struct *tsk, struct audit_context *ctx) +{ + struct audit_task_info *info; + + audit_alloc_task(tsk); + info = tsk->audit; + if (!IS_ERR(info)) + info->ctx = ctx; + else + tsk->audit = NULL; +} + +/** + * audit_alloc - allocate an audit info block for a task + * @tsk: task + * + * Call audit_alloc_syscall to filter on the task information and + * allocate a per-task audit context if necessary. This is called from + * copy_process, so no lock is needed. + */ +int audit_alloc(struct task_struct *tsk) +{ + int ret = 0; + + tsk->audit = NULL; + audit_alloc_task(tsk); + if (IS_ERR(tsk->audit)) { + ret = PTR_ERR(tsk->audit); + tsk->audit = NULL; + goto out; + } + ret = audit_alloc_syscall(tsk); + if (ret) { + kmem_cache_free(audit_task_cache, tsk->audit); + tsk->audit = NULL; + } +out: + return ret; +} + +/** + * audit_free - free per-task audit info + * @tsk: task whose audit info block to free + * + * Called from copy_process and do_exit + */ +void audit_free(struct task_struct *tsk) +{ + struct audit_task_info *info = tsk->audit; + + audit_free_syscall(tsk); + /* Freeing the audit_task_info struct must be performed after + * audit_log_exit() due to need for loginuid and sessionid. + */ + tsk->audit = NULL; + kmem_cache_free(audit_task_cache, info); +} + /** * auditd_test_task - Check to see if a given task is an audit daemon * @task: the task to check @@ -2310,6 +2452,7 @@ int audit_set_loginuid(kuid_t loginuid) unsigned int oldsessionid, sessionid = AUDIT_SID_UNSET; kuid_t oldloginuid; int rc; + struct audit_task_info *info; oldloginuid = audit_get_loginuid(current); oldsessionid = audit_get_sessionid(current); @@ -2317,6 +2460,12 @@ int audit_set_loginuid(kuid_t loginuid) rc = audit_set_loginuid_perm(loginuid); if (rc) goto out; + audit_alloc_task(current); + if (IS_ERR(current->audit)) { + rc = PTR_ERR(current->audit); + current->audit = NULL; + goto out; + } /* are we setting or clearing? */ if (uid_valid(loginuid)) { @@ -2325,8 +2474,9 @@ int audit_set_loginuid(kuid_t loginuid) sessionid = (unsigned int)atomic_inc_return(&session_id); } - current->sessionid = sessionid; - current->loginuid = loginuid; + info = current->audit; + info->sessionid = sessionid; + info->loginuid = loginuid; out: audit_log_set_loginuid(oldloginuid, loginuid, oldsessionid, sessionid, rc); return rc; diff --git a/kernel/audit.h b/kernel/audit.h index 3b9c0945225a..aa81d913a3d2 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -251,6 +251,10 @@ extern void audit_put_tty(struct tty_struct *tty); extern unsigned int audit_serial(void); extern int auditsc_get_stamp(struct audit_context *ctx, struct timespec64 *t, unsigned int *serial); +extern void audit_set_context(struct task_struct *task, struct audit_context *ctx); +extern struct audit_context *_audit_context(struct task_struct *tsk); +extern int audit_alloc_syscall(struct task_struct *tsk); +extern void audit_free_syscall(struct task_struct *tsk); extern void audit_put_watch(struct audit_watch *watch); extern void audit_get_watch(struct audit_watch *watch); @@ -292,6 +296,9 @@ extern void audit_filter_inodes(struct task_struct *tsk, extern struct list_head *audit_killed_trees(void); #else /* CONFIG_AUDITSYSCALL */ #define auditsc_get_stamp(c, t, s) 0 +#define audit_alloc_syscall(t) 0 +#define audit_free_syscall(t) {} + #define audit_put_watch(w) {} #define audit_get_watch(w) {} #define audit_to_watch(k, p, l, o) (-EINVAL) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index ce8c9e2279ba..018d2df70319 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -932,23 +932,25 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state) return context; } -/** - * audit_alloc - allocate an audit context block for a task +/* + * audit_alloc_syscall - allocate an audit context block for a task * @tsk: task * * Filter on the task information and allocate a per-task audit context * if necessary. Doing so turns on system call auditing for the - * specified task. This is called from copy_process, so no lock is - * needed. + * specified task. This is called from copy_process via audit_alloc, so + * no lock is needed. */ -int audit_alloc(struct task_struct *tsk) +int audit_alloc_syscall(struct task_struct *tsk) { struct audit_context *context; enum audit_state state; char *key = NULL; - if (likely(!audit_ever_enabled)) + if (likely(!audit_ever_enabled)) { + audit_set_context(tsk, NULL); return 0; /* Return if not auditing. */ + } state = audit_filter_task(tsk, &key); if (state == AUDIT_DISABLED) { @@ -958,7 +960,7 @@ int audit_alloc(struct task_struct *tsk) if (!(context = audit_alloc_context(state))) { kfree(key); - audit_log_lost("out of memory in audit_alloc"); + audit_log_lost("out of memory in audit_alloc_syscall"); return -ENOMEM; } context->filterkey = key; @@ -1603,14 +1605,14 @@ static void audit_log_exit(void) } /** - * __audit_free - free a per-task audit context + * audit_free_syscall - free per-task audit context info * @tsk: task whose audit context block to free * - * Called from copy_process and do_exit + * Called from audit_free */ -void __audit_free(struct task_struct *tsk) +void audit_free_syscall(struct task_struct *tsk) { - struct audit_context *context = tsk->audit_context; + struct audit_context *context = _audit_context(tsk); if (!context) return; diff --git a/kernel/fork.c b/kernel/fork.c index 37720a6d04ea..eea2707fcee1 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2028,7 +2028,6 @@ static __latent_entropy struct task_struct *copy_process( posix_cputimers_init(&p->posix_cputimers); p->io_context = NULL; - audit_set_context(p, NULL); cgroup_fork(p); #ifdef CONFIG_NUMA p->mempolicy = mpol_dup(p->mempolicy); From patchwork Tue Jan 12 15:09:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Guy Briggs X-Patchwork-Id: 12013749 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=-21.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=unavailable 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 86061C4332E for ; Tue, 12 Jan 2021 15:13:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4394423131 for ; Tue, 12 Jan 2021 15:13:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390793AbhALPM3 (ORCPT ); Tue, 12 Jan 2021 10:12:29 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:56187 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390541AbhALPMR (ORCPT ); Tue, 12 Jan 2021 10:12:17 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610464249; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:in-reply-to:in-reply-to:references:references; bh=wXW4v32PRIUorr74sndvNQGzZnX+Jchuaq0jcvoJaRE=; b=RyHORh/dRFFnQjWqd56qEpqtclXy2onEEKRitJOcqgZxgKwO02eT/BwZNby2Dwrh3EhabA 7FiC6ZcaPTdtnzSS0gOoWsYyyNMQii+ysswOTGZJRrqQfr0rfLprocWyNK4xNM0K0iDcfr FVhut0/wj+eUc2s+otY6HcJvM0rz4/8= 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-25-8kvIlTveOVCzbrwJk-h_3g-1; Tue, 12 Jan 2021 10:10:47 -0500 X-MC-Unique: 8kvIlTveOVCzbrwJk-h_3g-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id CE941107ACF7; Tue, 12 Jan 2021 15:10:44 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id BAB435D9D2; Tue, 12 Jan 2021 15:10:27 +0000 (UTC) From: Richard Guy Briggs To: Linux Containers List , Linux API , Linux-Audit Mailing List , Linux FSdevel , LKML , Linux NetDev Upstream Mailing List , Netfilter Devel List Cc: Neil Horman , David Howells , "Eric W. Biederman" , Simo Sorce , Eric Paris , mpatel@redhat.com, Serge Hallyn , Kees Cook , Jens Axboe , Christian Brauner , Richard Guy Briggs Subject: [PATCH ghak90 v11 02/11] audit: add container id Date: Tue, 12 Jan 2021 10:09:30 -0500 Message-Id: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement the proc fs write to set the audit container identifier of a process, emitting an AUDIT_CONTAINER_OP record to document the event. This is a write from the container orchestrator task to a proc entry of the form /proc/PID/audit_containerid where PID is the process ID of the newly created task that is to become the first task in a container, or an additional task added to a container. The write expects up to a u64 value (unset: 18446744073709551615). The writer must have capability CAP_AUDIT_CONTROL. This will produce an event such as this with the new CONTAINER_OP record: time->Thu Nov 26 10:24:27 2020 type=PROCTITLE msg=audit(1606404267.551:174524): proctitle=2F7573722F62696E2F7065726C002D7700636F6E7461696E657269642F74657374 type=SYSCALL msg=audit(1606404267.551:174524): arch=c000003e syscall=1 success=yes exit=20 a0=6 a1=557446aa9180 a2=14 a3=100 items=0 ppid=6827 pid=8724 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 comm="perl" exe="/usr/bin/perl" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null) type=CONTAINER_OP msg=audit(1606404267.551:174524): op=set opid=8730 contid=4515122123205246976 old-contid=-1 The "op" field indicates an initial set. The "opid" field is the object's PID, the process being "contained". New and old audit container identifier values are given in the "contid" fields. It is not permitted to unset the audit container identifier. A child inherits its parent's audit container identifier. Store the audit container identifier in a refcounted kernel object that is added to the master list of audit container identifiers. This will allow multiple container orchestrators/engines to work on the same machine without danger of inadvertantly re-using an existing identifier. It will also allow an orchestrator to inject a process into an existing container by checking if the original container owner is the one injecting the task. A hash table list is used to optimize searches. Since the life of each audit container indentifier is being tracked, we match the creation event with the destruction event. Log the drop of the audit container identifier when the last process in that container exits. Add support for reading the audit container identifier from the proc filesystem. This is a read from the proc entry of the form /proc/PID/audit_containerid where PID is the process ID of the task whose audit container identifier is sought. The read expects up to a u64 value (unset: (u64)-1). This read requires CAP_AUDIT_CONTROL. Add an entry to Documentation/ABI for /proc/$pid/audit_containerid. Please see the github audit kernel issue for the main feature: https://github.com/linux-audit/audit-kernel/issues/90 Please see the github audit userspace issue for supporting additions: https://github.com/linux-audit/audit-userspace/issues/51 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 --- Acks dropped due to log drop added 7.3, redo rcu/spin locking, u64/contobj Acked-by: Serge Hallyn Acked-by: Steve Grubb Acked-by: Neil Horman Reviewed-by: Ondrej Mosnacek --- .../ABI/testing/procfs-audit_containerid | 13 ++ fs/proc/base.c | 56 ++++- include/linux/audit.h | 5 + include/uapi/linux/audit.h | 2 + kernel/audit.c | 210 ++++++++++++++++++ kernel/audit.h | 2 + kernel/auditsc.c | 2 + 7 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/procfs-audit_containerid diff --git a/Documentation/ABI/testing/procfs-audit_containerid b/Documentation/ABI/testing/procfs-audit_containerid new file mode 100644 index 000000000000..30ea64790473 --- /dev/null +++ b/Documentation/ABI/testing/procfs-audit_containerid @@ -0,0 +1,13 @@ +What: Audit Container Identifier +Date: 2020-?? +KernelVersion: 5.10? +Contact: linux-audit@redhat.com +Format: u64 +Users: auditd, libaudit, audit-testsuite, podman(?), container orchestrators +Description: + The /proc/$pid/audit_containerid pseudofile it written + to set and read to get the audit container identifier of + process $pid. The accessor must have CAP_AUDIT_CONTROL + or have its own /proc/$pid/capcontainerid set to write + or read. + diff --git a/fs/proc/base.c b/fs/proc/base.c index b3422cda2a91..bf447e7932d2 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1244,7 +1244,7 @@ static const struct file_operations proc_oom_score_adj_operations = { }; #ifdef CONFIG_AUDIT -#define TMPBUFLEN 11 +#define TMPBUFLEN 21 static ssize_t proc_loginuid_read(struct file * file, char __user * buf, size_t count, loff_t *ppos) { @@ -1331,6 +1331,58 @@ static const struct file_operations proc_sessionid_operations = { .read = proc_sessionid_read, .llseek = generic_file_llseek, }; + +static ssize_t proc_contid_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode = file_inode(file); + struct task_struct *task = get_proc_task(inode); + ssize_t length; + char tmpbuf[TMPBUFLEN]; + + if (!task) + return -ESRCH; + length = audit_get_contid_proc(tmpbuf, TMPBUFLEN, task); + put_task_struct(task); + if (length < 0) + return length; + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); +} + +static ssize_t proc_contid_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode = file_inode(file); + u64 contid; + int rv; + struct task_struct *task = get_proc_task(inode); + + if (!task) + return -ESRCH; + if (*ppos != 0) { + /* No partial writes. */ + put_task_struct(task); + return -EINVAL; + } + + rv = kstrtou64_from_user(buf, count, 10, &contid); + if (rv < 0) { + put_task_struct(task); + return rv; + } + + rv = audit_set_contid(task, contid); + put_task_struct(task); + if (rv < 0) + return rv; + return count; +} + +static const struct file_operations proc_contid_operations = { + .read = proc_contid_read, + .write = proc_contid_write, + .llseek = generic_file_llseek, +}; #endif #ifdef CONFIG_FAULT_INJECTION @@ -3233,6 +3285,7 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_AUDIT REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), REG("sessionid", S_IRUGO, proc_sessionid_operations), + REG("audit_containerid", S_IWUSR|S_IRUSR, proc_contid_operations), #endif #ifdef CONFIG_FAULT_INJECTION REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations), @@ -3575,6 +3628,7 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_AUDIT REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), REG("sessionid", S_IRUGO, proc_sessionid_operations), + REG("audit_containerid", S_IWUSR|S_IRUSR, proc_contid_operations), #endif #ifdef CONFIG_FAULT_INJECTION REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations), diff --git a/include/linux/audit.h b/include/linux/audit.h index 515cc89a7e0c..30c55e6b6a3c 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -204,6 +204,11 @@ extern kuid_t audit_get_loginuid(struct task_struct *tsk); extern unsigned int audit_get_sessionid(struct task_struct *tsk); extern void audit_set_sessionid_iouring(unsigned int sessionid); +extern int audit_get_contid_proc(char *tmpbuf, int TMPBUFLEN, + struct task_struct *task); + +extern int audit_set_contid(struct task_struct *tsk, u64 contid); + extern u32 audit_enabled; extern int audit_signal_info(int sig, struct task_struct *t); diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index cd2d8279a5e4..26d65d0882e2 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -71,6 +71,7 @@ #define AUDIT_TTY_SET 1017 /* Set TTY auditing status */ #define AUDIT_SET_FEATURE 1018 /* Turn an audit feature on or off */ #define AUDIT_GET_FEATURE 1019 /* Get which features are enabled */ +#define AUDIT_CONTAINER_OP 1020 /* Define the container id and info */ #define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages mostly uninteresting to kernel */ #define AUDIT_USER_AVC 1107 /* We filter this differently */ @@ -495,6 +496,7 @@ struct audit_tty_status { #define AUDIT_UID_UNSET (unsigned int)-1 #define AUDIT_SID_UNSET ((unsigned int)-1) +#define AUDIT_CID_UNSET ((u64)-1) /* audit_rule_data supports filter rules with both integer and string * fields. It corresponds with AUDIT_ADD_RULE, AUDIT_DEL_RULE and diff --git a/kernel/audit.c b/kernel/audit.c index a822fa3b8da3..fe94b295a362 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -144,6 +144,15 @@ static atomic_t audit_backlog_wait_time_actual = ATOMIC_INIT(0); /* Hash for inode-based rules */ struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS]; +#define AUDIT_CONTID_BUCKETS 32 +/* Hash for contid object lists */ +static struct list_head audit_contid_hash[AUDIT_CONTID_BUCKETS]; +/* Lock all additions and deletions to the contid hash lists, assignment + * of container objects to tasks. There should be no need for + * interaction with tasklist_lock + */ +static DEFINE_SPINLOCK(_audit_contobj_list_lock); + static struct kmem_cache *audit_buffer_cache; /* queue msgs to send via kauditd_task */ @@ -208,9 +217,18 @@ struct audit_reply { struct sk_buff *skb; }; +struct audit_contobj { + struct list_head list; + u64 id; + struct task_struct *owner; + refcount_t refcount; + struct rcu_head rcu; +}; + struct audit_task_info { kuid_t loginuid; unsigned int sessionid; + struct audit_contobj *cont; #ifdef CONFIG_AUDITSYSCALL struct audit_context *ctx; #endif @@ -261,6 +279,15 @@ inline void audit_set_sessionid_iouring(unsigned int sessionid) info->sessionid = sessionid; } +static inline u64 audit_get_contid(struct task_struct *tsk) +{ + struct audit_task_info *info = tsk->audit; + + if (!info || !info->cont) + return AUDIT_CID_UNSET; + return info->cont->id; +} + inline struct audit_context *_audit_context(struct task_struct *tsk) { struct audit_task_info *info = tsk->audit; @@ -276,6 +303,39 @@ struct audit_context *audit_context(void) } EXPORT_SYMBOL(audit_context); +static struct audit_contobj *_audit_contobj_get(struct audit_contobj *cont) +{ + if (cont) + refcount_inc(&cont->refcount); + return cont; +} + +static struct audit_contobj *_audit_contobj_get_bytask(struct task_struct *tsk) +{ + struct audit_task_info *info = tsk->audit; + + if (!info) + return NULL; + return _audit_contobj_get(info->cont); +} + +/* _audit_contobj_list_lock must be held by caller */ +static void _audit_contobj_put(struct audit_contobj *cont) +{ + if (!cont) + return; + if (refcount_dec_and_test(&cont->refcount)) { + put_task_struct(cont->owner); + list_del_rcu(&cont->list); + kfree_rcu(cont, rcu); + } +} + +static inline int audit_hash_contid(u64 contid) +{ + return (contid & (AUDIT_CONTID_BUCKETS-1)); +} + static void audit_alloc_task(struct task_struct *tsk) { struct audit_task_info *info = tsk->audit; @@ -289,6 +349,9 @@ static void audit_alloc_task(struct task_struct *tsk) } info->loginuid = audit_get_loginuid(current); info->sessionid = audit_get_sessionid(current); + rcu_read_lock(); + info->cont = _audit_contobj_get_bytask(current); + rcu_read_unlock(); tsk->audit = info; } @@ -343,6 +406,9 @@ void audit_free(struct task_struct *tsk) struct audit_task_info *info = tsk->audit; audit_free_syscall(tsk); + spin_lock(&_audit_contobj_list_lock); + _audit_contobj_put(info->cont); + spin_unlock(&_audit_contobj_list_lock); /* Freeing the audit_task_info struct must be performed after * audit_log_exit() due to need for loginuid and sessionid. */ @@ -1795,6 +1861,9 @@ static int __init audit_init(void) for (i = 0; i < AUDIT_INODE_BUCKETS; i++) INIT_LIST_HEAD(&audit_inode_hash[i]); + for (i = 0; i < AUDIT_CONTID_BUCKETS; i++) + INIT_LIST_HEAD(&audit_contid_hash[i]); + mutex_init(&audit_cmd_mutex.lock); audit_cmd_mutex.owner = NULL; @@ -2509,6 +2578,147 @@ int audit_signal_info(int sig, struct task_struct *t) return audit_signal_info_syscall(t); } +/* + * audit_set_contid - set current task's audit contid + * @tsk: target task + * @contid: contid value + * + * Returns 0 on success, -EPERM on permission failure. + * + * If the original container owner goes away, no task injection is + * possible to an existing container. + * + * Called (set) from fs/proc/base.c::proc_contid_write(). + */ +int audit_set_contid(struct task_struct *tsk, u64 contid) +{ + int rc = 0; + struct audit_buffer *ab; + struct audit_contobj *oldcont = NULL; + struct audit_contobj *cont = NULL, *newcont = NULL; + int h; + struct audit_task_info *info = tsk->audit; + + /* Can't set if audit disabled */ + if (!info) { + task_unlock(tsk); + return -ENOPROTOOPT; + } + read_lock(&tasklist_lock); + task_lock(tsk); + if (contid == AUDIT_CID_UNSET) { + /* Don't allow the contid to be unset */ + rc = -EINVAL; + } else if (!capable(CAP_AUDIT_CONTROL)) { + /* if we don't have caps, reject */ + rc = -EPERM; + } else if (!list_empty(&tsk->children) || + !(thread_group_leader(tsk) && thread_group_empty(tsk))) { + /* if task has children or is not single-threaded, deny */ + rc = -EBUSY; + } else if (info->cont) { + /* if contid is already set, deny */ + rc = -EEXIST; + } + rcu_read_lock(); + oldcont = _audit_contobj_get_bytask(tsk); + if (rc) + goto error; + + h = audit_hash_contid(contid); + spin_lock(&_audit_contobj_list_lock); + list_for_each_entry_rcu(cont, &audit_contid_hash[h], list) + if (cont->id == contid) { + /* task injection to existing container */ + if (current == cont->owner) { + _audit_contobj_get(cont); + newcont = cont; + } else { + rc = -ENOTUNIQ; + spin_unlock(&_audit_contobj_list_lock); + goto error; + } + break; + } + if (!newcont) { + newcont = kmalloc(sizeof(*newcont), GFP_ATOMIC); + if (newcont) { + INIT_LIST_HEAD(&newcont->list); + newcont->id = contid; + newcont->owner = get_task_struct(current); + refcount_set(&newcont->refcount, 1); + list_add_rcu(&newcont->list, + &audit_contid_hash[h]); + } else { + rc = -ENOMEM; + spin_unlock(&_audit_contobj_list_lock); + goto error; + } + } + info->cont = newcont; + _audit_contobj_put(oldcont); + spin_unlock(&_audit_contobj_list_lock); +error: + rcu_read_unlock(); + task_unlock(tsk); + read_unlock(&tasklist_lock); + + if (!audit_enabled) + return rc; + + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONTAINER_OP); + if (!ab) + return rc; + + audit_log_format(ab, + "op=set opid=%d contid=%llu old-contid=%llu", + task_tgid_nr(tsk), contid, oldcont ? oldcont->id : -1); + spin_lock(&_audit_contobj_list_lock); + _audit_contobj_put(oldcont); + spin_unlock(&_audit_contobj_list_lock); + audit_log_end(ab); + return rc; +} + +int audit_get_contid_proc(char *tmpbuf, int TMPBUFLEN, + struct task_struct *tsk) +{ + int length; + + /* if we don't have caps, reject */ + if (!capable(CAP_AUDIT_CONTROL)) { + length = -EPERM; + goto out; + } + length = scnprintf(tmpbuf, TMPBUFLEN, "%llu", audit_get_contid(tsk)); +out: + return length; +} + +void audit_log_container_drop(void) +{ + struct audit_buffer *ab; + struct audit_contobj *cont; + + rcu_read_lock(); + cont = _audit_contobj_get_bytask(current); + rcu_read_unlock(); + if (!cont) + return; + if (refcount_read(&cont->refcount) > 2) + goto out; + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONTAINER_OP); + if (!ab) + goto out; + audit_log_format(ab, "op=drop opid=%d contid=-1 old-contid=%llu", + task_tgid_nr(current), cont->id); + audit_log_end(ab); +out: + spin_lock(&_audit_contobj_list_lock); + _audit_contobj_put(cont); + spin_unlock(&_audit_contobj_list_lock); +} + /** * audit_log_end - end one audit record * @ab: the audit_buffer diff --git a/kernel/audit.h b/kernel/audit.h index aa81d913a3d2..c4a3d7e03fbe 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -210,6 +210,8 @@ static inline int audit_hash_ino(u32 ino) return (ino & (AUDIT_INODE_BUCKETS-1)); } +extern void audit_log_container_drop(void); + /* Indicates that audit should log the full pathname. */ #define AUDIT_NAME_FULL -1 diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 018d2df70319..5056e32f9f47 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1596,6 +1596,8 @@ static void audit_log_exit(void) audit_log_proctitle(); + audit_log_container_drop(); + /* Send end of event record to help user space know we are finished */ ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE); if (ab) From patchwork Tue Jan 12 15:09:31 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Guy Briggs X-Patchwork-Id: 12013751 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=-21.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=unavailable 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 067DFC43331 for ; Tue, 12 Jan 2021 15:13:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C61E823132 for ; Tue, 12 Jan 2021 15:13:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2403796AbhALPMd (ORCPT ); Tue, 12 Jan 2021 10:12:33 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:56366 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389426AbhALPM1 (ORCPT ); Tue, 12 Jan 2021 10:12:27 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610464261; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:in-reply-to:in-reply-to:references:references; bh=Kk6Nzwn3umjTOuB5yGDNbtr85xT4w2cQHn90rXYF93k=; b=Z6awm/RmAY4S4Iz0Zl+wL7R4ph2/08sapYxpdKieTdubNfNRun+yW69/Evf13bpkbLgbK5 PQZ3lYdv+tbPztXWvRDjKYmyTw/ppG3aUe0i5qH7wV1OhmWz1xY9Hs4MGSJqAIa2Vo+ARP EboOx/iV/+ieWeihKH8YrHsZP0MNeuE= 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-142-CXcgC2LtNSOFZD8yy1MqpA-1; Tue, 12 Jan 2021 10:10:57 -0500 X-MC-Unique: CXcgC2LtNSOFZD8yy1MqpA-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 1422D801B12; Tue, 12 Jan 2021 15:10:55 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id 247595D9CD; Tue, 12 Jan 2021 15:10:44 +0000 (UTC) From: Richard Guy Briggs To: Linux Containers List , Linux API , Linux-Audit Mailing List , Linux FSdevel , LKML , Linux NetDev Upstream Mailing List , Netfilter Devel List Cc: Neil Horman , David Howells , "Eric W. Biederman" , Simo Sorce , Eric Paris , mpatel@redhat.com, Serge Hallyn , Kees Cook , Jens Axboe , Christian Brauner , Richard Guy Briggs Subject: [PATCH ghak90 v11 03/11] audit: log container info of syscalls Date: Tue, 12 Jan 2021 10:09:31 -0500 Message-Id: <84026735e3627d193d5f15ea2b15a2895ed31e3e.1610399347.git.rgb@redhat.com> In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Create a new audit record AUDIT_CONTAINER_ID to document the audit container identifier of a process if it is present. Called from audit_log_exit(), syscalls are covered. Include target_cid references from ptrace and signal. A sample raw event: time->Thu Nov 26 10:24:40 2020 type=PROCTITLE msg=audit(1606404280.226:174542): proctitle=2F7573722F62696E2F7065726C002D7700636F6E7461696E657269642F74657374 type=PATH msg=audit(1606404280.226:174542): item=1 name="/tmp/audit-testsuite-dir-8riQ/testsuite-1606404267-WNldVJCr" inode=428 dev=00:1f mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_tmp_t:s0 nametype=CREATE cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0 type=PATH msg=audit(1606404280.226:174542): item=0 name="/tmp/audit-testsuite-dir-8riQ/" inode=427 dev=00:1f mode=040700 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_tmp_t:s0 nametype=PARENT cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0 type=CWD msg=audit(1606404280.226:174542): cwd="/root/rgb/git/audit-testsuite/tests" type=SYSCALL msg=audit(1606404280.226:174542): arch=c000003e syscall=257 success=yes exit=6 a0=ffffff9c a1=557446bd5f10 a2=80241 a3=1b6 items=2 ppid=8724 pid=8758 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 comm="perl" exe="/usr/bin/perl" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="testsuite-1606404267-WNldVJCr" record=1 type=CONTAINER_ID msg=audit(1606404280.226:174542): record=1 contid=527940429489930240 Please see the github audit kernel issue for the main feature: https://github.com/linux-audit/audit-kernel/issues/90 Please see the github audit userspace issue for supporting additions: https://github.com/linux-audit/audit-userspace/issues/51 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 --- Acks removed due to added "record=" field, track container of signalled process Acked-by: Serge Hallyn Acked-by: Steve Grubb Acked-by: Neil Horman Reviewed-by: Ondrej Mosnacek --- include/uapi/linux/audit.h | 1 + kernel/audit.c | 74 ++++++++++++++++++++++++++++++++++++++ kernel/audit.h | 6 ++++ kernel/auditsc.c | 49 ++++++++++++++++++++----- 4 files changed, 122 insertions(+), 8 deletions(-) diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 26d65d0882e2..c56335e828dc 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -119,6 +119,7 @@ #define AUDIT_TIME_ADJNTPVAL 1333 /* NTP value adjustment */ #define AUDIT_BPF 1334 /* BPF subsystem */ #define AUDIT_EVENT_LISTENER 1335 /* Task joined multicast read socket */ +#define AUDIT_CONTAINER_ID 1336 /* Container ID */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ diff --git a/kernel/audit.c b/kernel/audit.c index fe94b295a362..5495b69bc505 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -319,6 +319,11 @@ static struct audit_contobj *_audit_contobj_get_bytask(struct task_struct *tsk) return _audit_contobj_get(info->cont); } +void *audit_contobj_get_bytask(struct task_struct *tsk) +{ + return (void *)_audit_contobj_get_bytask(tsk); +} + /* _audit_contobj_list_lock must be held by caller */ static void _audit_contobj_put(struct audit_contobj *cont) { @@ -331,6 +336,17 @@ static void _audit_contobj_put(struct audit_contobj *cont) } } +void audit_contobj_put(void **cont, int count) +{ + int i; + struct audit_contobj **contobj = (struct audit_contobj **)cont; + + spin_lock(&_audit_contobj_list_lock); + for (i = 0; i < count; i++) + _audit_contobj_put(contobj[i]); + spin_unlock(&_audit_contobj_list_lock); +} + static inline int audit_hash_contid(u64 contid) { return (contid & (AUDIT_CONTID_BUCKETS-1)); @@ -2327,6 +2343,59 @@ void audit_log_session_info(struct audit_buffer *ab) audit_log_format(ab, "auid=%u ses=%u", auid, sessionid); } +/* + * _audit_log_container_id - report container info + * @context: task or local context for record + * @cont: container object to report + * + * Returns 0 on record absence, positive integer on valid record id. + */ +static int _audit_log_container_id(struct audit_context *context, + struct audit_contobj *contobj) +{ + struct audit_buffer *ab; + int record; + + if (!contobj) + return 0; + /* Generate AUDIT_CONTAINER_ID record with container ID */ + ab = audit_log_start(context, GFP_KERNEL, AUDIT_CONTAINER_ID); + if (!ab) + return 0; + audit_log_format(ab, "record=%d contid=%llu", + record = ++context->contid_records, contobj->id); + audit_log_end(ab); + return record; +} + +int audit_log_container_id(struct audit_context *context, void *cont) +{ + return _audit_log_container_id(context, (struct audit_contobj *)cont); +} + +/* + * audit_log_container_id_ctx - report container info + * @context: task or local context for record + * + * Returns 0 on record absence, positive integer on valid record id. + */ +int audit_log_container_id_ctx(struct audit_context *context) +{ + struct audit_contobj *contobj; + int record; + + rcu_read_lock(); + contobj = _audit_contobj_get_bytask(current); + rcu_read_unlock(); + if (!contobj) + return 0; + record = _audit_log_container_id(context, contobj); + spin_lock(&_audit_contobj_list_lock); + _audit_contobj_put(contobj); + spin_unlock(&_audit_contobj_list_lock); + return record; +} + void audit_log_key(struct audit_buffer *ab, char *key) { audit_log_format(ab, " key="); @@ -2631,6 +2700,11 @@ int audit_set_contid(struct task_struct *tsk, u64 contid) if (cont->id == contid) { /* task injection to existing container */ if (current == cont->owner) { + if (!refcount_read(&cont->refcount)) { + rc = -ENOTUNIQ; + spin_unlock(&_audit_contobj_list_lock); + goto error; + } _audit_contobj_get(cont); newcont = cont; } else { diff --git a/kernel/audit.h b/kernel/audit.h index c4a3d7e03fbe..de79f59d623f 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -135,6 +135,8 @@ struct audit_context { kuid_t target_uid; unsigned int target_sessionid; u32 target_sid; + void *target_cid; + int contid_records; char target_comm[TASK_COMM_LEN]; struct audit_tree_refs *trees, *first_trees; @@ -211,6 +213,10 @@ static inline int audit_hash_ino(u32 ino) } extern void audit_log_container_drop(void); +extern void *audit_contobj_get_bytask(struct task_struct *tsk); +extern void audit_contobj_put(void **cont, int count); +extern int audit_log_container_id(struct audit_context *context, void *cont); +extern int audit_log_container_id_ctx(struct audit_context *context); /* Indicates that audit should log the full pathname. */ #define AUDIT_NAME_FULL -1 diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 5056e32f9f47..2d9762f2f432 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -112,6 +112,7 @@ struct audit_aux_data_pids { kuid_t target_uid[AUDIT_AUX_PIDS]; unsigned int target_sessionid[AUDIT_AUX_PIDS]; u32 target_sid[AUDIT_AUX_PIDS]; + void *target_cid[AUDIT_AUX_PIDS]; char target_comm[AUDIT_AUX_PIDS][TASK_COMM_LEN]; int pid_count; }; @@ -905,6 +906,7 @@ static inline void audit_free_names(struct audit_context *context) static inline void audit_free_aux(struct audit_context *context) { struct audit_aux_data *aux; + struct audit_aux_data_pids *axp; while ((aux = context->aux)) { context->aux = aux->next; @@ -912,6 +914,8 @@ static inline void audit_free_aux(struct audit_context *context) } while ((aux = context->aux_pids)) { context->aux_pids = aux->next; + axp = (struct audit_aux_data_pids *)aux; + audit_contobj_put(axp->target_cid, axp->pid_count); kfree(aux); } } @@ -985,7 +989,7 @@ static inline void audit_free_context(struct audit_context *context) static int audit_log_pid_context(struct audit_context *context, pid_t pid, kuid_t auid, kuid_t uid, unsigned int sessionid, - u32 sid, char *comm) + u32 sid, char *comm, int record) { struct audit_buffer *ab; char *ctx = NULL; @@ -1010,6 +1014,8 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid, } audit_log_format(ab, " ocomm="); audit_log_untrustedstring(ab, comm); + if (record) + audit_log_format(ab, " record=%d", record); audit_log_end(ab); return rc; @@ -1479,9 +1485,12 @@ static void audit_log_exit(void) struct audit_buffer *ab; struct audit_aux_data *aux; struct audit_names *n; + int record; context->personality = current->personality; + record = audit_log_container_id_ctx(context); + ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL); if (!ab) return; /* audit_panic has been called */ @@ -1504,6 +1513,8 @@ static void audit_log_exit(void) audit_log_task_info(ab); audit_log_key(ab, context->filterkey); + if (record) + audit_log_format(ab, " record=%d", record); audit_log_end(ab); for (aux = context->aux; aux; aux = aux->next) { @@ -1562,22 +1573,28 @@ static void audit_log_exit(void) for (aux = context->aux_pids; aux; aux = aux->next) { struct audit_aux_data_pids *axs = (void *)aux; - for (i = 0; i < axs->pid_count; i++) + for (i = 0; i < axs->pid_count; i++) { + record = audit_log_container_id(context, axs->target_cid[i]); if (audit_log_pid_context(context, axs->target_pid[i], axs->target_auid[i], axs->target_uid[i], axs->target_sessionid[i], axs->target_sid[i], - axs->target_comm[i])) + axs->target_comm[i], record)) call_panic = 1; + } } - if (context->target_pid && - audit_log_pid_context(context, context->target_pid, - context->target_auid, context->target_uid, - context->target_sessionid, - context->target_sid, context->target_comm)) + if (context->target_pid) { + record = audit_log_container_id(context, context->target_cid); + if (audit_log_pid_context(context, context->target_pid, + context->target_auid, + context->target_uid, + context->target_sessionid, + context->target_sid, + context->target_comm, record)) call_panic = 1; + } if (context->pwd.dentry && context->pwd.mnt) { ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD); @@ -1755,11 +1772,16 @@ void __audit_syscall_exit(int success, long return_code) audit_free_aux(context); context->aux = NULL; context->aux_pids = NULL; + if (context->target_pid) { + audit_contobj_put(&context->target_cid, 1); + context->target_cid = NULL; + } context->target_pid = 0; context->target_sid = 0; context->sockaddr_len = 0; context->type = 0; context->fds[0] = -1; + context->contid_records = 0; if (context->state != AUDIT_RECORD_CONTEXT) { kfree(context->filterkey); context->filterkey = NULL; @@ -2400,11 +2422,16 @@ void __audit_ptrace(struct task_struct *t) { struct audit_context *context = audit_context(); + if (context->target_pid) + audit_contobj_put(&context->target_cid, 1); context->target_pid = task_tgid_nr(t); context->target_auid = audit_get_loginuid(t); context->target_uid = task_uid(t); context->target_sessionid = audit_get_sessionid(t); security_task_getsecid(t, &context->target_sid); + rcu_read_lock(); + context->target_cid = audit_contobj_get_bytask(t); + rcu_read_unlock(); memcpy(context->target_comm, t->comm, TASK_COMM_LEN); } @@ -2432,6 +2459,9 @@ int audit_signal_info_syscall(struct task_struct *t) ctx->target_uid = t_uid; ctx->target_sessionid = audit_get_sessionid(t); security_task_getsecid(t, &ctx->target_sid); + rcu_read_lock(); + ctx->target_cid = audit_contobj_get_bytask(t); + rcu_read_unlock(); memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN); return 0; } @@ -2453,6 +2483,9 @@ int audit_signal_info_syscall(struct task_struct *t) axp->target_uid[axp->pid_count] = t_uid; axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t); security_task_getsecid(t, &axp->target_sid[axp->pid_count]); + rcu_read_lock(); + axp->target_cid[axp->pid_count] = audit_contobj_get_bytask(t); + rcu_read_unlock(); memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN); axp->pid_count++; From patchwork Tue Jan 12 15:09:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Guy Briggs X-Patchwork-Id: 12013753 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=-13.2 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, UNWANTED_LANGUAGE_BODY autolearn=unavailable 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 C1C84C43217 for ; Tue, 12 Jan 2021 15:13:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7D13023130 for ; Tue, 12 Jan 2021 15:13:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2404049AbhALPMr (ORCPT ); Tue, 12 Jan 2021 10:12:47 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:23051 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2403901AbhALPMo (ORCPT ); Tue, 12 Jan 2021 10:12:44 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610464276; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:in-reply-to:in-reply-to:references:references; bh=eLxohIAroViULxx7/3TYcwTyBtNxALiR7rsXmeEuzKw=; b=jNATru5ncTpmFJA3D9gTsoxuoWxLNXNyYeRVylKU/ewOonDbJImIWUABGo7Y0IoBJEjcq8 A9ju02Q98wX5MPKjWoTT30jNEtojzoYgrihJnDpt+dJfx3kJnkdbBOVcPTOZ3ZxvQrpbuy x8B8m2+4iGt7li5vUjJAVSv7rjZgU7U= 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-106-X8MXGvJjO6a3yrGoWzoMkQ-1; Tue, 12 Jan 2021 10:11:13 -0500 X-MC-Unique: X8MXGvJjO6a3yrGoWzoMkQ-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id EE179802B40; Tue, 12 Jan 2021 15:11:10 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id 56E735D9CD; Tue, 12 Jan 2021 15:10:55 +0000 (UTC) From: Richard Guy Briggs To: Linux Containers List , Linux API , Linux-Audit Mailing List , Linux FSdevel , LKML , Linux NetDev Upstream Mailing List , Netfilter Devel List Cc: Neil Horman , David Howells , "Eric W. Biederman" , Simo Sorce , Eric Paris , mpatel@redhat.com, Serge Hallyn , Kees Cook , Jens Axboe , Christian Brauner , Richard Guy Briggs Subject: [PATCH ghak90 v11 04/11] audit: add contid support for signalling the audit daemon Date: Tue, 12 Jan 2021 10:09:32 -0500 Message-Id: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add audit container identifier support to the action of signalling the audit daemon. Since this would need to add an element to the audit_sig_info struct, a new record type AUDIT_SIGNAL_INFO2 was created with a new audit_sig_info2 struct. Corresponding support is required in the userspace code to reflect the new record request and reply type. An older userspace won't break since it won't know to request this record type. Signed-off-by: Richard Guy Briggs --- Acks from nhorman/omosnace should have been added in v6. Acks dropped due to restructure audit_sig_info2 for nesting, sigcount, reap adtsk --- include/linux/audit.h | 7 +++ include/uapi/linux/audit.h | 1 + kernel/audit.c | 118 +++++++++++++++++++++++++++++++++--- security/selinux/nlmsgtab.c | 1 + 4 files changed, 119 insertions(+), 8 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 30c55e6b6a3c..7c1928e75cfe 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -23,6 +23,13 @@ struct audit_sig_info { char ctx[]; }; +struct audit_sig_info2 { + uid_t uid; + pid_t pid; + u32 cid_len; + char data[]; +}; + struct audit_buffer; struct audit_context; struct inode; diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index c56335e828dc..94dcf3085658 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -72,6 +72,7 @@ #define AUDIT_SET_FEATURE 1018 /* Turn an audit feature on or off */ #define AUDIT_GET_FEATURE 1019 /* Get which features are enabled */ #define AUDIT_CONTAINER_OP 1020 /* Define the container id and info */ +#define AUDIT_SIGNAL_INFO2 1021 /* Get info auditd signal sender */ #define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages mostly uninteresting to kernel */ #define AUDIT_USER_AVC 1107 /* We filter this differently */ diff --git a/kernel/audit.c b/kernel/audit.c index 5495b69bc505..314af418bf67 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -123,9 +123,11 @@ static u32 audit_backlog_limit = 64; static u32 audit_backlog_wait_time = AUDIT_BACKLOG_WAIT_TIME; /* The identity of the user shutting down the audit system. */ -static kuid_t audit_sig_uid = INVALID_UID; -static pid_t audit_sig_pid = -1; -static u32 audit_sig_sid; +static kuid_t audit_sig_uid = INVALID_UID; +static pid_t audit_sig_pid = -1; +static u32 audit_sig_sid; +static struct audit_contobj *audit_sig_cid; +static struct task_struct *audit_sig_adtsk; /* Records can be lost in several ways: 0) [suppressed in audit_alloc] @@ -222,6 +224,7 @@ struct audit_contobj { u64 id; struct task_struct *owner; refcount_t refcount; + refcount_t sigcount; struct rcu_head rcu; }; @@ -330,9 +333,39 @@ static void _audit_contobj_put(struct audit_contobj *cont) if (!cont) return; if (refcount_dec_and_test(&cont->refcount)) { - put_task_struct(cont->owner); - list_del_rcu(&cont->list); - kfree_rcu(cont, rcu); + if (!refcount_read(&cont->sigcount)) { + put_task_struct(cont->owner); + list_del_rcu(&cont->list); + kfree_rcu(cont, rcu); + } + } +} + +/* rcu_read_lock must be held by caller unless new */ +static struct audit_contobj *_audit_contobj_get_sig_bytask(struct task_struct *tsk) +{ + struct audit_contobj *cont; + struct audit_task_info *info = tsk->audit; + + if (!info) + return NULL; + cont = info->cont; + if (cont) + refcount_inc(&cont->sigcount); + return cont; +} + +/* rcu_read_lock must be held by caller */ +static void _audit_contobj_put_sig(struct audit_contobj *cont) +{ + if (!cont) + return; + if (refcount_dec_and_test(&cont->sigcount)) { + if (!refcount_read(&cont->refcount)) { + put_task_struct(cont->owner); + list_del_rcu(&cont->list); + kfree_rcu(cont, rcu); + } } } @@ -430,6 +463,15 @@ void audit_free(struct task_struct *tsk) */ tsk->audit = NULL; kmem_cache_free(audit_task_cache, info); + rcu_read_lock(); + if (audit_sig_adtsk == tsk) { + spin_lock(&_audit_contobj_list_lock); + _audit_contobj_put_sig(audit_sig_cid); + spin_unlock(&_audit_contobj_list_lock); + audit_sig_cid = NULL; + audit_sig_adtsk = NULL; + } + rcu_read_unlock(); } /** @@ -1252,6 +1294,7 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type) case AUDIT_ADD_RULE: case AUDIT_DEL_RULE: case AUDIT_SIGNAL_INFO: + case AUDIT_SIGNAL_INFO2: case AUDIT_TTY_GET: case AUDIT_TTY_SET: case AUDIT_TRIM: @@ -1414,6 +1457,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) struct audit_buffer *ab; u16 msg_type = nlh->nlmsg_type; struct audit_sig_info *sig_data; + struct audit_sig_info2 *sig_data2; char *ctx = NULL; u32 len; @@ -1685,7 +1729,58 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) audit_send_reply(skb, seq, AUDIT_SIGNAL_INFO, 0, 0, sig_data, sizeof(*sig_data) + len); kfree(sig_data); + spin_lock(&_audit_contobj_list_lock); + _audit_contobj_put_sig(audit_sig_cid); + spin_unlock(&_audit_contobj_list_lock); + audit_sig_cid = NULL; break; + case AUDIT_SIGNAL_INFO2: { + char *contidstr = NULL; + unsigned int contidstrlen = 0; + + len = 0; + if (audit_sig_sid) { + err = security_secid_to_secctx(audit_sig_sid, &ctx, + &len); + if (err) + return err; + } + if (audit_sig_cid) { + contidstr = kmalloc(21, GFP_KERNEL); + if (!contidstr) { + if (audit_sig_sid) + security_release_secctx(ctx, len); + return -ENOMEM; + } + contidstrlen = scnprintf(contidstr, 20, "%llu", audit_sig_cid->id); + } + sig_data2 = kmalloc(sizeof(*sig_data2) + contidstrlen + len, GFP_KERNEL); + if (!sig_data2) { + if (audit_sig_sid) + security_release_secctx(ctx, len); + kfree(contidstr); + return -ENOMEM; + } + sig_data2->uid = from_kuid(&init_user_ns, audit_sig_uid); + sig_data2->pid = audit_sig_pid; + if (audit_sig_cid) { + memcpy(sig_data2->data, contidstr, contidstrlen); + sig_data2->cid_len = contidstrlen; + kfree(contidstr); + } + if (audit_sig_sid) { + memcpy(sig_data2->data + contidstrlen, ctx, len); + security_release_secctx(ctx, len); + } + spin_lock(&_audit_contobj_list_lock); + _audit_contobj_put_sig(audit_sig_cid); + spin_unlock(&_audit_contobj_list_lock); + audit_sig_cid = NULL; + audit_send_reply(skb, seq, AUDIT_SIGNAL_INFO2, 0, 0, + sig_data2, sizeof(*sig_data2) + contidstrlen + len); + kfree(sig_data2); + break; + } case AUDIT_TTY_GET: { struct audit_tty_status s; unsigned int t; @@ -2630,11 +2725,11 @@ int audit_set_loginuid(kuid_t loginuid) */ int audit_signal_info(int sig, struct task_struct *t) { - kuid_t uid = current_uid(), auid; - if (auditd_test_task(t) && (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2)) { + kuid_t uid = current_uid(), auid; + audit_sig_pid = task_tgid_nr(current); auid = audit_get_loginuid(current); if (uid_valid(auid)) @@ -2642,6 +2737,13 @@ int audit_signal_info(int sig, struct task_struct *t) else audit_sig_uid = uid; security_task_getsecid(current, &audit_sig_sid); + spin_lock(&_audit_contobj_list_lock); + _audit_contobj_put_sig(audit_sig_cid); + spin_unlock(&_audit_contobj_list_lock); + rcu_read_lock(); + audit_sig_cid = _audit_contobj_get_sig_bytask(current); + rcu_read_unlock(); + audit_sig_adtsk = t; } return audit_signal_info_syscall(t); diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index b69231918686..8303bb7a63d0 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -137,6 +137,7 @@ static const struct nlmsg_perm nlmsg_audit_perms[] = { AUDIT_DEL_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_SIGNAL_INFO2, NETLINK_AUDIT_SOCKET__NLMSG_READ }, { AUDIT_TRIM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_MAKE_EQUIV, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ }, From patchwork Tue Jan 12 15:09:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Guy Briggs X-Patchwork-Id: 12013755 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=-16.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED autolearn=unavailable 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 852A8C4361A for ; Tue, 12 Jan 2021 15:13:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5830923131 for ; Tue, 12 Jan 2021 15:13:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2404107AbhALPMv (ORCPT ); Tue, 12 Jan 2021 10:12:51 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:47035 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2403983AbhALPMs (ORCPT ); Tue, 12 Jan 2021 10:12:48 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610464281; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:in-reply-to:in-reply-to:references:references; bh=B4MqYHKr8TbFkYs/2e1aPcB4fVxp3WF5DM/hpXMoX7w=; b=EHp8xoQ3uvCR330R2o8QuEJe0knfPWaqwH/wJYjHDOkDEBVJP/2v/HdksPRY/XcBVWnZF6 WShKxBhNztB78PTqbFemtWhMSgfezkRTGS3UOL8YAhRWCGOSZ0q/psVlKWsqIhR0BSIq/M 4+6honNf8E9qWG4Io/YB5QRBbxJXyH8= 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-306-Zw5pJXaIPCqj_W33p2gwkA-1; Tue, 12 Jan 2021 10:11:17 -0500 X-MC-Unique: Zw5pJXaIPCqj_W33p2gwkA-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 8139D196632C; Tue, 12 Jan 2021 15:11:15 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3B6155D9DB; Tue, 12 Jan 2021 15:11:11 +0000 (UTC) From: Richard Guy Briggs To: Linux Containers List , Linux API , Linux-Audit Mailing List , Linux FSdevel , LKML , Linux NetDev Upstream Mailing List , Netfilter Devel List Cc: Neil Horman , David Howells , "Eric W. Biederman" , Simo Sorce , Eric Paris , mpatel@redhat.com, Serge Hallyn , Kees Cook , Jens Axboe , Christian Brauner , Richard Guy Briggs Subject: [PATCH ghak90 v11 05/11] audit: add support for non-syscall auxiliary records Date: Tue, 12 Jan 2021 10:09:33 -0500 Message-Id: <189fca12a04d2d8b6ce59e009cc8fc4e995a52b4.1610399347.git.rgb@redhat.com> In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Standalone audit records have the timestamp and serial number generated on the fly and as such are unique, making them standalone. This new function audit_alloc_local() generates a local audit context that will be used only for a standalone record and its auxiliary record(s). The context is discarded immediately after the local associated records are produced. A new flag, "local" was used rather than "in_syscall" since it would be overloading the original purpose and meaning. Events using this function may not be triggered by a syscall but still need records linked by timestamp and serial. Signed-off-by: Richard Guy Briggs Acked-by: Serge Hallyn Acked-by: Neil Horman Reviewed-by: Ondrej Mosnacek --- include/linux/audit.h | 8 ++++++++ kernel/audit.h | 1 + kernel/auditsc.c | 31 ++++++++++++++++++++++++++----- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 7c1928e75cfe..9f0238f7960f 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -304,6 +304,8 @@ static inline int audit_signal_info(int sig, struct task_struct *t) /* These are defined in auditsc.c */ /* Public API */ +extern struct audit_context *audit_alloc_local(gfp_t gfpflags); +extern void audit_free_context(struct audit_context *context); extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3); extern void __audit_syscall_exit(int ret_success, long ret_value); @@ -555,6 +557,12 @@ static inline void audit_log_nfcfg(const char *name, u8 af, extern int audit_n_rules; extern int audit_signals; #else /* CONFIG_AUDITSYSCALL */ +static inline struct audit_context *audit_alloc_local(gfp_t gfpflags) +{ + return NULL; +} +static inline void audit_free_context(struct audit_context *context) +{ } static inline void audit_syscall_entry(int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3) diff --git a/kernel/audit.h b/kernel/audit.h index de79f59d623f..40e609787a0c 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -98,6 +98,7 @@ struct audit_proctitle { struct audit_context { int dummy; /* must be the first element */ int in_syscall; /* 1 if task is in a syscall */ + bool local; /* local context needed */ enum audit_state state, current_state; unsigned int serial; /* serial number for record */ int major; /* syscall number */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 2d9762f2f432..fc781ab5af2c 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -920,11 +920,12 @@ static inline void audit_free_aux(struct audit_context *context) } } -static inline struct audit_context *audit_alloc_context(enum audit_state state) +static inline struct audit_context *audit_alloc_context(enum audit_state state, + gfp_t gfpflags) { struct audit_context *context; - context = kzalloc(sizeof(*context), GFP_KERNEL); + context = kzalloc(sizeof(*context), gfpflags); if (!context) return NULL; context->state = state; @@ -962,7 +963,8 @@ int audit_alloc_syscall(struct task_struct *tsk) return 0; } - if (!(context = audit_alloc_context(state))) { + context = audit_alloc_context(state, GFP_KERNEL); + if (!context) { kfree(key); audit_log_lost("out of memory in audit_alloc_syscall"); return -ENOMEM; @@ -974,8 +976,26 @@ int audit_alloc_syscall(struct task_struct *tsk) return 0; } -static inline void audit_free_context(struct audit_context *context) +struct audit_context *audit_alloc_local(gfp_t gfpflags) { + struct audit_context *context; + + context = audit_alloc_context(AUDIT_RECORD_CONTEXT, gfpflags); + if (!context) { + audit_log_lost("out of memory in audit_alloc_local"); + return NULL; + } + context->serial = audit_serial(); + ktime_get_coarse_real_ts64(&context->ctime); + context->local = true; + return context; +} +EXPORT_SYMBOL(audit_alloc_local); + +void audit_free_context(struct audit_context *context) +{ + if (!context) + return; audit_free_module(context); audit_free_names(context); unroll_tree_refs(context, NULL, 0); @@ -986,6 +1006,7 @@ static inline void audit_free_context(struct audit_context *context) audit_proctitle_free(context); kfree(context); } +EXPORT_SYMBOL(audit_free_context); static int audit_log_pid_context(struct audit_context *context, pid_t pid, kuid_t auid, kuid_t uid, unsigned int sessionid, @@ -2223,7 +2244,7 @@ EXPORT_SYMBOL_GPL(__audit_inode_child); int auditsc_get_stamp(struct audit_context *ctx, struct timespec64 *t, unsigned int *serial) { - if (!ctx->in_syscall) + if (!ctx->in_syscall && !ctx->local) return 0; if (!ctx->serial) ctx->serial = audit_serial(); From patchwork Tue Jan 12 15:09:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Guy Briggs X-Patchwork-Id: 12013757 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=-16.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS 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 E51B1C072AD for ; Tue, 12 Jan 2021 15:13:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A4F0923130 for ; Tue, 12 Jan 2021 15:13:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2404217AbhALPNB (ORCPT ); Tue, 12 Jan 2021 10:13:01 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:46553 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2404175AbhALPM7 (ORCPT ); Tue, 12 Jan 2021 10:12:59 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610464293; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:in-reply-to:in-reply-to:references:references; bh=CEcMeW3r8WIQ8e8BupQyhzcepDHWsTIT5isBpREM2VE=; b=C4jwxqOiniQjJ0SIN8ZiJPEaQrI4eWXwlw+54Ef/mFgPgGBQDBRnJr5G+cR7Ty5ExpUTk7 hTh7/MLMb1vPrrCykJfdHr1WxCTTNsnokMZwWE1Yu4n8vONeGi31EFThpLjgQTAHUKJ6Ac glR7deafLGMYY14n5WrFxwa+zVn5aKY= 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-593-mqIA9YfLNGisrvaAxwTllQ-1; Tue, 12 Jan 2021 10:11:32 -0500 X-MC-Unique: mqIA9YfLNGisrvaAxwTllQ-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 752B7100C600; Tue, 12 Jan 2021 15:11:29 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id F36BB5D9CD; Tue, 12 Jan 2021 15:11:15 +0000 (UTC) From: Richard Guy Briggs To: Linux Containers List , Linux API , Linux-Audit Mailing List , Linux FSdevel , LKML , Linux NetDev Upstream Mailing List , Netfilter Devel List Cc: Neil Horman , David Howells , "Eric W. Biederman" , Simo Sorce , Eric Paris , mpatel@redhat.com, Serge Hallyn , Kees Cook , Jens Axboe , Christian Brauner , Richard Guy Briggs Subject: [PATCH ghak90 v11 06/11] audit: add containerid support for user records Date: Tue, 12 Jan 2021 10:09:34 -0500 Message-Id: <4a50ebc6f10c2365a04f271160d62b869a02a816.1610399347.git.rgb@redhat.com> In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add audit container identifier auxiliary record to user event standalone records. Signed-off-by: Richard Guy Briggs Acked-by: Neil Horman Reviewed-by: Ondrej Mosnacek --- kernel/audit.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 314af418bf67..946fe5862dc7 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1339,12 +1339,6 @@ static void audit_log_common_recv_msg(struct audit_context *context, audit_log_task_context(*ab); } -static inline void audit_log_user_recv_msg(struct audit_buffer **ab, - u16 msg_type) -{ - audit_log_common_recv_msg(NULL, ab, msg_type); -} - int is_audit_feature_set(int i) { return af.features & AUDIT_FEATURE_TO_MASK(i); @@ -1620,6 +1614,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) err = audit_filter(msg_type, AUDIT_FILTER_USER); if (err == 1) { /* match or error */ char *str = data; + struct audit_context *context; err = 0; if (msg_type == AUDIT_USER_TTY) { @@ -1627,7 +1622,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (err) break; } - audit_log_user_recv_msg(&ab, msg_type); + context = audit_alloc_local(GFP_KERNEL); + audit_log_common_recv_msg(context, &ab, msg_type); if (msg_type != AUDIT_USER_TTY) { /* ensure NULL termination */ str[data_len - 1] = '\0'; @@ -1641,6 +1637,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) audit_log_n_untrustedstring(ab, str, data_len); } audit_log_end(ab); + audit_log_container_id_ctx(context); + audit_free_context(context); } break; case AUDIT_ADD_RULE: From patchwork Tue Jan 12 15:09:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Guy Briggs X-Patchwork-Id: 12013767 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=-18.2 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING, SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY,URIBL_BLOCKED autolearn=unavailable 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 0E18CC07D5A for ; Tue, 12 Jan 2021 15:14:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B34F323131 for ; Tue, 12 Jan 2021 15:14:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2405215AbhALPOI (ORCPT ); Tue, 12 Jan 2021 10:14:08 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:30687 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2404282AbhALPNH (ORCPT ); Tue, 12 Jan 2021 10:13:07 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610464301; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:in-reply-to:in-reply-to:references:references; bh=kOwZr9/+JkNCar1Y9mTZLW5ViGaLSnxnpro4O3w8RZE=; b=csTMYo+Yu6bvQrmM8o4OLBIQRB6ztEXNiAOGC1KNv4TuWQRbFr2bNppaKxsjwR5Fa7NkuX n4IGo/LDx8Y35RBwefOVIQ0MmJmUVtSvvRLY545scGWZBYEQfHhiUQMGmJRpjVPSDuwqPV 20ayG/lqPAl+UFF4Vnu+peVYkJ76BPI= 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-211-7JNuFYDYMoGEa_M-jV9v7w-1; Tue, 12 Jan 2021 10:11:37 -0500 X-MC-Unique: 7JNuFYDYMoGEa_M-jV9v7w-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 86CEB801B18; Tue, 12 Jan 2021 15:11:35 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9660C5D9CD; Tue, 12 Jan 2021 15:11:29 +0000 (UTC) From: Richard Guy Briggs To: Linux Containers List , Linux API , Linux-Audit Mailing List , Linux FSdevel , LKML , Linux NetDev Upstream Mailing List , Netfilter Devel List Cc: Neil Horman , David Howells , "Eric W. Biederman" , Simo Sorce , Eric Paris , mpatel@redhat.com, Serge Hallyn , Kees Cook , Jens Axboe , Christian Brauner , Richard Guy Briggs Subject: [PATCH ghak90 v11 07/11] audit: add containerid filtering Date: Tue, 12 Jan 2021 10:09:35 -0500 Message-Id: <1da812001a8366654e66bdeedfd4a41365908c45.1610399347.git.rgb@redhat.com> In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement audit container identifier filtering using the AUDIT_CONTID field name to send an 8-character string representing a u64 since the value field is only u32. Sending it as two u32 was considered, but gathering and comparing two fields was more complex. The feature indicator is AUDIT_FEATURE_BITMAP_CONTAINERID. Please see the github audit kernel issue for the contid filter feature: https://github.com/linux-audit/audit-kernel/issues/91 Please see the github audit userspace issue for filter additions: https://github.com/linux-audit/audit-userspace/issues/40 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: Serge Hallyn Acked-by: Neil Horman Reviewed-by: Ondrej Mosnacek --- include/linux/audit.h | 1 + include/uapi/linux/audit.h | 5 ++++- kernel/audit.c | 5 +++++ kernel/audit.h | 3 +++ kernel/auditfilter.c | 46 ++++++++++++++++++++++++++++++++++++++ kernel/auditsc.c | 3 +++ 6 files changed, 62 insertions(+), 1 deletion(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 9f0238f7960f..056a7c9a12a2 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -69,6 +69,7 @@ struct audit_field { u32 type; union { u32 val; + u64 val64; kuid_t uid; kgid_t gid; struct { diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 94dcf3085658..66350e572e41 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -271,6 +271,7 @@ #define AUDIT_LOGINUID_SET 24 #define AUDIT_SESSIONID 25 /* Session ID */ #define AUDIT_FSTYPE 26 /* FileSystem Type */ +#define AUDIT_CONTID 27 /* Container ID */ /* These are ONLY useful when checking * at syscall exit time (AUDIT_AT_EXIT). */ @@ -353,6 +354,7 @@ enum { #define AUDIT_FEATURE_BITMAP_SESSIONID_FILTER 0x00000010 #define AUDIT_FEATURE_BITMAP_LOST_RESET 0x00000020 #define AUDIT_FEATURE_BITMAP_FILTER_FS 0x00000040 +#define AUDIT_FEATURE_BITMAP_CONTAINERID 0x00000080 #define AUDIT_FEATURE_BITMAP_ALL (AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT | \ AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME | \ @@ -360,7 +362,8 @@ enum { AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND | \ AUDIT_FEATURE_BITMAP_SESSIONID_FILTER | \ AUDIT_FEATURE_BITMAP_LOST_RESET | \ - AUDIT_FEATURE_BITMAP_FILTER_FS) + AUDIT_FEATURE_BITMAP_FILTER_FS | \ + AUDIT_FEATURE_BITMAP_CONTAINERID) /* deprecated: AUDIT_VERSION_* */ #define AUDIT_VERSION_LATEST AUDIT_FEATURE_BITMAP_ALL diff --git a/kernel/audit.c b/kernel/audit.c index 946fe5862dc7..533254167abf 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -2489,6 +2489,11 @@ int audit_log_container_id_ctx(struct audit_context *context) return record; } +int audit_contid_comparator(struct task_struct *tsk, u32 op, u64 right) +{ + return audit_comparator64(audit_get_contid(tsk), op, right); +} + void audit_log_key(struct audit_buffer *ab, char *key) { audit_log_format(ab, " key="); diff --git a/kernel/audit.h b/kernel/audit.h index 40e609787a0c..48c429c2d544 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -218,12 +218,15 @@ extern void *audit_contobj_get_bytask(struct task_struct *tsk); extern void audit_contobj_put(void **cont, int count); extern int audit_log_container_id(struct audit_context *context, void *cont); extern int audit_log_container_id_ctx(struct audit_context *context); +extern int audit_contid_comparator(struct task_struct *tsk, const u32 op, + const u64 right); /* Indicates that audit should log the full pathname. */ #define AUDIT_NAME_FULL -1 extern int audit_match_class(int class, unsigned syscall); extern int audit_comparator(const u32 left, const u32 op, const u32 right); +extern int audit_comparator64(const u64 left, const u32 op, const u64 right); extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right); extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right); extern int parent_len(const char *path); diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 333b3bcfc545..9362ee9cc414 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -399,6 +399,7 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f) case AUDIT_FILETYPE: case AUDIT_FIELD_COMPARE: case AUDIT_EXE: + case AUDIT_CONTID: /* only equal and not equal valid ops */ if (f->op != Audit_not_equal && f->op != Audit_equal) return -EINVAL; @@ -590,6 +591,14 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, entry->rule.buflen += f_val; entry->rule.exe = audit_mark; break; + case AUDIT_CONTID: + if (f_val != sizeof(u64)) + goto exit_free; + str = audit_unpack_string(&bufp, &remain, f_val); + if (IS_ERR(str)) + goto exit_free; + f->val64 = ((u64 *)str)[0]; + break; default: f->val = f_val; break; @@ -675,6 +684,11 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) data->buflen += data->values[i] = audit_pack_string(&bufp, audit_mark_path(krule->exe)); break; + case AUDIT_CONTID: + data->buflen += data->values[i] = sizeof(u64); + memcpy(bufp, &f->val64, sizeof(u64)); + bufp += sizeof(u64); + break; case AUDIT_LOGINUID_SET: if (krule->pflags & AUDIT_LOGINUID_LEGACY && !f->val) { data->fields[i] = AUDIT_LOGINUID; @@ -761,6 +775,10 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) if (!gid_eq(a->fields[i].gid, b->fields[i].gid)) return 1; break; + case AUDIT_CONTID: + if (a->fields[i].val64 != b->fields[i].val64) + return 1; + break; default: if (a->fields[i].val != b->fields[i].val) return 1; @@ -1216,6 +1234,30 @@ int audit_comparator(u32 left, u32 op, u32 right) } } +int audit_comparator64(u64 left, u32 op, u64 right) +{ + switch (op) { + case Audit_equal: + return (left == right); + case Audit_not_equal: + return (left != right); + case Audit_lt: + return (left < right); + case Audit_le: + return (left <= right); + case Audit_gt: + return (left > right); + case Audit_ge: + return (left >= right); + case Audit_bitmask: + return (left & right); + case Audit_bittest: + return ((left & right) == right); + default: + return 0; + } +} + int audit_uid_comparator(kuid_t left, u32 op, kuid_t right) { switch (op) { @@ -1350,6 +1392,10 @@ int audit_filter(int msgtype, unsigned int listtype) result = audit_comparator(audit_loginuid_set(current), f->op, f->val); break; + case AUDIT_CONTID: + result = audit_contid_comparator(current, f->op, + f->val64); + break; case AUDIT_MSGTYPE: result = audit_comparator(msgtype, f->op, f->val); break; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index fc781ab5af2c..2b8cc4bea84f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -656,6 +656,9 @@ static int audit_filter_rules(struct task_struct *tsk, result = audit_comparator(ctx->sockaddr->ss_family, f->op, f->val); break; + case AUDIT_CONTID: + result = audit_contid_comparator(tsk, f->op, f->val64); + break; case AUDIT_SUBJ_USER: case AUDIT_SUBJ_ROLE: case AUDIT_SUBJ_TYPE: From patchwork Tue Jan 12 15:09:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Guy Briggs X-Patchwork-Id: 12013765 X-Patchwork-Delegate: kuba@kernel.org 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=-21.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=unavailable 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 50F0AC07542 for ; Tue, 12 Jan 2021 15:14:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1186623357 for ; Tue, 12 Jan 2021 15:14:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2405200AbhALPOB (ORCPT ); Tue, 12 Jan 2021 10:14:01 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:53852 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2404454AbhALPNO (ORCPT ); Tue, 12 Jan 2021 10:13:14 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610464306; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:in-reply-to:in-reply-to:references:references; bh=6oy5RQwuGgN2Ocz1cm2fLisCyMPLBMFR7557JMkQCxQ=; b=g8A6TU8GTMBxCKdLdjEttLt3P6eBWhzhrry2tZdUDJ1vjKBKGVUz9dkb30i9gwCGoMqbvq 5jBylLgCjPHuUx72y71E49k/Hhhqd4LMe+1sMepbWtAVk8X8bir3Gjz72pQ/8qQhgvRnnG T+m9kvBaSHrBG/Um0w5EEEfMjnsELco= 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-347-Y2CnmYuVPBORx7liunpURg-1; Tue, 12 Jan 2021 10:11:44 -0500 X-MC-Unique: Y2CnmYuVPBORx7liunpURg-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 4047C107ACF7; Tue, 12 Jan 2021 15:11:42 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id CA25B5D9CD; Tue, 12 Jan 2021 15:11:35 +0000 (UTC) From: Richard Guy Briggs To: Linux Containers List , Linux API , Linux-Audit Mailing List , Linux FSdevel , LKML , Linux NetDev Upstream Mailing List , Netfilter Devel List Cc: Neil Horman , David Howells , "Eric W. Biederman" , Simo Sorce , Eric Paris , mpatel@redhat.com, Serge Hallyn , Kees Cook , Jens Axboe , Christian Brauner , Richard Guy Briggs Subject: [PATCH ghak90 v11 08/11] audit: add support for containerid to network namespaces Date: Tue, 12 Jan 2021 10:09:36 -0500 Message-Id: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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. Add the "record=" field to the A sample event: time->Thu Nov 26 10:24:47 2020 type=NETFILTER_PKT msg=audit(1606404287.984:174549): mark=0x15766399 saddr=127.0.0.1 daddr=127.0.0.1 proto=1 record=1 type=CONTAINER_ID msg=audit(1606404287.984:174549): record=1 contid=4112973747854606336,1916436506412318720 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 --- Acks removed due to redo rcu/spin locking: Acked-by: Neil Horman Reviewed-by: Ondrej Mosnacek --- include/linux/audit.h | 17 +++ kernel/audit.c | 229 ++++++++++++++++++++++++++++++++++----- kernel/nsproxy.c | 4 + net/netfilter/nft_log.c | 14 ++- net/netfilter/xt_AUDIT.c | 14 ++- 5 files changed, 249 insertions(+), 29 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 056a7c9a12a2..014f73296fec 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -217,6 +217,12 @@ extern int audit_get_contid_proc(char *tmpbuf, int TMPBUFLEN, extern int audit_set_contid(struct task_struct *tsk, u64 contid); +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 int audit_log_netns_contid_list(struct net *net, + struct audit_context *context); + extern u32 audit_enabled; extern int audit_signal_info(int sig, struct task_struct *t); @@ -281,6 +287,17 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk) return AUDIT_SID_UNSET; } +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 int audit_log_netns_contid_list(struct net *net, + struct audit_context *context) +{ + return 0; +} + #define audit_enabled AUDIT_OFF static inline int audit_signal_info(int sig, struct task_struct *t) diff --git a/kernel/audit.c b/kernel/audit.c index 533254167abf..c30bcd525dad 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; }; /** @@ -239,6 +244,16 @@ struct audit_task_info { static struct kmem_cache *audit_task_cache; +struct audit_contobj_netns { + struct list_head list; + struct audit_contobj *obj; + int count; + struct rcu_head rcu; +}; + +static void audit_netns_contid_add(struct net *net, struct audit_contobj *cont); +static 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", @@ -373,11 +388,12 @@ void audit_contobj_put(void **cont, int count) { int i; struct audit_contobj **contobj = (struct audit_contobj **)cont; + unsigned long flags; - spin_lock(&_audit_contobj_list_lock); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); for (i = 0; i < count; i++) _audit_contobj_put(contobj[i]); - spin_unlock(&_audit_contobj_list_lock); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); } static inline int audit_hash_contid(u64 contid) @@ -453,11 +469,24 @@ int audit_alloc(struct task_struct *tsk) void audit_free(struct task_struct *tsk) { struct audit_task_info *info = tsk->audit; + unsigned long flags; + struct nsproxy *ns = tsk->nsproxy; + struct audit_contobj *cont; + rcu_read_lock(); + cont = _audit_contobj_get_bytask(tsk); + rcu_read_unlock(); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); + if (ns) { + audit_netns_contid_del(ns->net_ns, cont); + _audit_contobj_put(cont); + } + _audit_contobj_put(cont); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); audit_free_syscall(tsk); - spin_lock(&_audit_contobj_list_lock); - _audit_contobj_put(info->cont); - spin_unlock(&_audit_contobj_list_lock); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); + _audit_contobj_put(cont); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); /* Freeing the audit_task_info struct must be performed after * audit_log_exit() due to need for loginuid and sessionid. */ @@ -465,9 +494,9 @@ void audit_free(struct task_struct *tsk) kmem_cache_free(audit_task_cache, info); rcu_read_lock(); if (audit_sig_adtsk == tsk) { - spin_lock(&_audit_contobj_list_lock); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); _audit_contobj_put_sig(audit_sig_cid); - spin_unlock(&_audit_contobj_list_lock); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); audit_sig_cid = NULL; audit_sig_adtsk = NULL; } @@ -565,6 +594,145 @@ 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_bytask(tsk); + rcu_read_unlock(); + audit_netns_contid_add(net, cont); +} + +void audit_switch_task_namespaces(struct nsproxy *ns, struct task_struct *p) +{ + struct audit_contobj *cont; + struct nsproxy *new = p->nsproxy; + unsigned long flags; + + rcu_read_lock(); + cont = _audit_contobj_get_bytask(p); + rcu_read_unlock(); + if (!cont) + return; + audit_netns_contid_del(ns->net_ns, cont); + if (new) + audit_netns_contid_add(new->net_ns, cont); + else { + spin_lock_irqsave(&_audit_contobj_list_lock, flags); + _audit_contobj_put(cont); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); + } + spin_lock_irqsave(&_audit_contobj_list_lock, flags); + _audit_contobj_put(cont); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); +} + +/** + * audit_log_netns_contid_list - List contids for the given network namespace + * @net: the network namespace of interest + * @context: the audit context to use + * + * Returns 1 for record, 0 for none. + * + * Description: + * Issues a CONTAINER_ID record with a CSV list of contids associated + * with a network namespace to accompany a NETFILTER_PKT record. + */ +int 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 = net_generic(net, audit_net_id); + unsigned int record; + + if (!aunet) + return 0; + rcu_read_lock(); + list_for_each_entry_rcu(cont, &aunet->contobj_list, list) { + record = 1; + 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, "record=1 contid=%llu", + cont->obj->id); + } else { + audit_log_format(ab, ",%llu", cont->obj->id); + } + } + audit_log_end(ab); +out: + rcu_read_unlock(); + return record; +} +EXPORT_SYMBOL(audit_log_netns_contid_list); + void audit_panic(const char *message) { switch (audit_failure) { @@ -1454,6 +1622,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) struct audit_sig_info2 *sig_data2; char *ctx = NULL; u32 len; + unsigned long flags; err = audit_netlink_ok(skb, msg_type); if (err) @@ -1727,9 +1896,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) audit_send_reply(skb, seq, AUDIT_SIGNAL_INFO, 0, 0, sig_data, sizeof(*sig_data) + len); kfree(sig_data); - spin_lock(&_audit_contobj_list_lock); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); _audit_contobj_put_sig(audit_sig_cid); - spin_unlock(&_audit_contobj_list_lock); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); audit_sig_cid = NULL; break; case AUDIT_SIGNAL_INFO2: { @@ -1770,9 +1939,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) memcpy(sig_data2->data + contidstrlen, ctx, len); security_release_secctx(ctx, len); } - spin_lock(&_audit_contobj_list_lock); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); _audit_contobj_put_sig(audit_sig_cid); - spin_unlock(&_audit_contobj_list_lock); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); audit_sig_cid = NULL; audit_send_reply(skb, seq, AUDIT_SIGNAL_INFO2, 0, 0, sig_data2, sizeof(*sig_data2) + contidstrlen + len); @@ -1927,7 +2096,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; } @@ -2476,6 +2646,7 @@ int audit_log_container_id_ctx(struct audit_context *context) { struct audit_contobj *contobj; int record; + unsigned long flags; rcu_read_lock(); contobj = _audit_contobj_get_bytask(current); @@ -2483,9 +2654,9 @@ int audit_log_container_id_ctx(struct audit_context *context) if (!contobj) return 0; record = _audit_log_container_id(context, contobj); - spin_lock(&_audit_contobj_list_lock); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); _audit_contobj_put(contobj); - spin_unlock(&_audit_contobj_list_lock); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); return record; } @@ -2732,6 +2903,7 @@ int audit_signal_info(int sig, struct task_struct *t) (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2)) { kuid_t uid = current_uid(), auid; + unsigned long flags; audit_sig_pid = task_tgid_nr(current); auid = audit_get_loginuid(current); @@ -2740,9 +2912,9 @@ int audit_signal_info(int sig, struct task_struct *t) else audit_sig_uid = uid; security_task_getsecid(current, &audit_sig_sid); - spin_lock(&_audit_contobj_list_lock); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); _audit_contobj_put_sig(audit_sig_cid); - spin_unlock(&_audit_contobj_list_lock); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); rcu_read_lock(); audit_sig_cid = _audit_contobj_get_sig_bytask(current); rcu_read_unlock(); @@ -2772,6 +2944,8 @@ int audit_set_contid(struct task_struct *tsk, u64 contid) struct audit_contobj *cont = NULL, *newcont = NULL; int h; struct audit_task_info *info = tsk->audit; + unsigned long flags; + struct net *net = tsk->nsproxy->net_ns; /* Can't set if audit disabled */ if (!info) { @@ -2800,21 +2974,21 @@ int audit_set_contid(struct task_struct *tsk, u64 contid) goto error; h = audit_hash_contid(contid); - spin_lock(&_audit_contobj_list_lock); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); list_for_each_entry_rcu(cont, &audit_contid_hash[h], list) if (cont->id == contid) { /* task injection to existing container */ if (current == cont->owner) { if (!refcount_read(&cont->refcount)) { rc = -ENOTUNIQ; - spin_unlock(&_audit_contobj_list_lock); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); goto error; } _audit_contobj_get(cont); newcont = cont; } else { rc = -ENOTUNIQ; - spin_unlock(&_audit_contobj_list_lock); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); goto error; } break; @@ -2830,13 +3004,17 @@ int audit_set_contid(struct task_struct *tsk, u64 contid) &audit_contid_hash[h]); } else { rc = -ENOMEM; - spin_unlock(&_audit_contobj_list_lock); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); goto error; } } info->cont = newcont; _audit_contobj_put(oldcont); - spin_unlock(&_audit_contobj_list_lock); + audit_netns_contid_del(net, oldcont); + _audit_contobj_put(oldcont); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); + _audit_contobj_get(newcont); + audit_netns_contid_add(net, newcont); error: rcu_read_unlock(); task_unlock(tsk); @@ -2852,9 +3030,9 @@ int audit_set_contid(struct task_struct *tsk, u64 contid) audit_log_format(ab, "op=set opid=%d contid=%llu old-contid=%llu", task_tgid_nr(tsk), contid, oldcont ? oldcont->id : -1); - spin_lock(&_audit_contobj_list_lock); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); _audit_contobj_put(oldcont); - spin_unlock(&_audit_contobj_list_lock); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); audit_log_end(ab); return rc; } @@ -2878,6 +3056,7 @@ void audit_log_container_drop(void) { struct audit_buffer *ab; struct audit_contobj *cont; + unsigned long flags; rcu_read_lock(); cont = _audit_contobj_get_bytask(current); @@ -2893,9 +3072,9 @@ void audit_log_container_drop(void) task_tgid_nr(current), cont->id); audit_log_end(ab); out: - spin_lock(&_audit_contobj_list_lock); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); _audit_contobj_put(cont); - spin_unlock(&_audit_contobj_list_lock); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); } /** diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index abc01fcad8c7..352bddea6e6f 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -26,6 +26,7 @@ #include #include #include +#include static struct kmem_cache *nsproxy_cachep; @@ -182,6 +183,8 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) timens_on_fork(new_ns, tsk); tsk->nsproxy = new_ns; + if (flags & CLONE_NEWNET) + audit_copy_namespaces(new_ns->net_ns, tsk); return 0; } @@ -244,6 +247,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) put_nsproxy(ns); diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c index a06a46b039c5..f433c93c80e1 100644 --- a/net/netfilter/nft_log.c +++ b/net/netfilter/nft_log.c @@ -66,13 +66,19 @@ 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; + int record; if (!audit_enabled) return; - ab = audit_log_start(NULL, GFP_ATOMIC, AUDIT_NETFILTER_PKT); + context = audit_alloc_local(GFP_ATOMIC); + net = xt_net(&pkt->xt); + record = audit_log_netns_contid_list(net, context); + ab = audit_log_start(context, GFP_ATOMIC, AUDIT_NETFILTER_PKT); if (!ab) - return; + goto errout; audit_log_format(ab, "mark=%#x", skb->mark); @@ -98,7 +104,11 @@ static void nft_log_eval_audit(const struct nft_pktinfo *pkt) if (fam == -1) audit_log_format(ab, " saddr=? daddr=? proto=-1"); + if (record) + audit_log_format(ab, " record=%d", record); audit_log_end(ab); +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..d2c5069745ea 100644 --- a/net/netfilter/xt_AUDIT.c +++ b/net/netfilter/xt_AUDIT.c @@ -68,10 +68,16 @@ 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; + int record; 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); + net = xt_net(par); + record = audit_log_netns_contid_list(net, context); + ab = audit_log_start(context, GFP_ATOMIC, AUDIT_NETFILTER_PKT); if (ab == NULL) goto errout; @@ -99,9 +105,13 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par) if (fam == -1) audit_log_format(ab, " saddr=? daddr=? proto=-1"); + if (record) + audit_log_format(ab, " record=%d", record); audit_log_end(ab); errout: + audit_free_context(context); +out: return XT_CONTINUE; } From patchwork Tue Jan 12 15:09:37 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Guy Briggs X-Patchwork-Id: 12013763 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=-16.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=unavailable 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 3439EC433DB for ; Tue, 12 Jan 2021 15:14:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 03B2823130 for ; Tue, 12 Jan 2021 15:14:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2405183AbhALPNx (ORCPT ); Tue, 12 Jan 2021 10:13:53 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:39666 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2404671AbhALPNV (ORCPT ); Tue, 12 Jan 2021 10:13:21 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610464315; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:in-reply-to:in-reply-to:references:references; bh=JMNDxOpcSJpy1boyDiRFGw1VU2TRGbjXVre/O5w1TLo=; b=WXKL7fix78CqLKio9qN/MC3/dTmWc48LukUEWeh3ztvSM1Z0nYJ3wIBUhtIFTaxRtMCkbZ 88JhR/XT4XoDVrW2ybAxSFTeBgj0RVJnVvBUgO3ZDJ4rxJkNLjTvVLBbb6juSuXE1xaENP WrY9aIhobrexxYdU3o5ZukySJ4DvoDs= 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-158-5ZMed7MhNqO0gmxicNO--Q-1; Tue, 12 Jan 2021 10:11:53 -0500 X-MC-Unique: 5ZMed7MhNqO0gmxicNO--Q-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 8A9D4EC1A7; Tue, 12 Jan 2021 15:11:51 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8E9B75D9CD; Tue, 12 Jan 2021 15:11:42 +0000 (UTC) From: Richard Guy Briggs To: Linux Containers List , Linux API , Linux-Audit Mailing List , Linux FSdevel , LKML , Linux NetDev Upstream Mailing List , Netfilter Devel List Cc: Neil Horman , David Howells , "Eric W. Biederman" , Simo Sorce , Eric Paris , mpatel@redhat.com, Serge Hallyn , Kees Cook , Jens Axboe , Christian Brauner , Richard Guy Briggs Subject: [PATCH ghak90 v11 09/11] audit: contid check descendancy and nesting Date: Tue, 12 Jan 2021 10:09:37 -0500 Message-Id: <33d06da75cd65fa88c92968e35b6eb4346e98ffc.1610399347.git.rgb@redhat.com> In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Require the target task to be a descendant of the container orchestrator/engine. You would only change the audit container ID from one set or inherited value to another if you were nesting containers. If changing the contid, the container orchestrator/engine must be a descendant and not same orchestrator as the one that set it so it is not possible to change the contid of another orchestrator's container. Since the task_is_descendant() function is used in YAMA and in audit, remove the duplication and pull the function into kernel/core/sched.c Signed-off-by: Richard Guy Briggs --- include/linux/sched.h | 3 +++ kernel/audit.c | 26 +++++++++++++++++++++++--- kernel/sched/core.c | 33 +++++++++++++++++++++++++++++++++ security/yama/yama_lsm.c | 33 --------------------------------- 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h index 1d10d81b8fd5..b30bbfec31ab 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2097,4 +2097,7 @@ int sched_trace_rq_nr_running(struct rq *rq); const struct cpumask *sched_trace_rd_span(struct root_domain *rd); +extern int task_is_descendant(struct task_struct *parent, + struct task_struct *child); + #endif diff --git a/kernel/audit.c b/kernel/audit.c index c30bcd525dad..fcb78a6d8e4a 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -476,11 +476,13 @@ void audit_free(struct task_struct *tsk) rcu_read_lock(); cont = _audit_contobj_get_bytask(tsk); rcu_read_unlock(); - spin_lock_irqsave(&_audit_contobj_list_lock, flags); if (ns) { audit_netns_contid_del(ns->net_ns, cont); + spin_lock_irqsave(&_audit_contobj_list_lock, flags); _audit_contobj_put(cont); + spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); } + spin_lock_irqsave(&_audit_contobj_list_lock, flags); _audit_contobj_put(cont); spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); audit_free_syscall(tsk); @@ -2924,6 +2926,21 @@ int audit_signal_info(int sig, struct task_struct *t) return audit_signal_info_syscall(t); } +static bool audit_contid_isnesting(struct task_struct *tsk) +{ + bool isowner = false; + bool ownerisparent = false; + struct audit_task_info *info = tsk->audit; + + rcu_read_lock(); + if (info && info->cont) { + isowner = current == info->cont->owner; + ownerisparent = task_is_descendant(info->cont->owner, current); + } + rcu_read_unlock(); + return !isowner && ownerisparent; +} + /* * audit_set_contid - set current task's audit contid * @tsk: target task @@ -2964,8 +2981,11 @@ int audit_set_contid(struct task_struct *tsk, u64 contid) !(thread_group_leader(tsk) && thread_group_empty(tsk))) { /* if task has children or is not single-threaded, deny */ rc = -EBUSY; - } else if (info->cont) { - /* if contid is already set, deny */ + } else if (tsk == current || !task_is_descendant(current, tsk)) { + /* if task is not descendant, block */ + rc = -EXDEV; + } else if (info->cont && !audit_contid_isnesting(tsk)) { + /* only allow contid setting again if nesting */ rc = -EEXIST; } rcu_read_lock(); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 15d2562118d1..f769bcba4ee8 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -9072,6 +9072,39 @@ void dump_cpu_task(int cpu) sched_show_task(cpu_curr(cpu)); } +/* + * task_is_descendant - walk up a process family tree looking for a match + * @parent: the process to compare against while walking up from child + * @child: the process to start from while looking upwards for parent + * + * Returns 1 if child is a descendant of parent, 0 if not. + */ +int task_is_descendant(struct task_struct *parent, + struct task_struct *child) +{ + int rc = 0; + struct task_struct *walker = child; + + if (!parent || !child) + return 0; + + rcu_read_lock(); + if (!thread_group_leader(parent)) + parent = rcu_dereference(parent->group_leader); + while (walker->pid > 0) { + if (!thread_group_leader(walker)) + walker = rcu_dereference(walker->group_leader); + if (walker == parent) { + rc = 1; + break; + } + walker = rcu_dereference(walker->real_parent); + } + rcu_read_unlock(); + + return rc; +} + /* * Nice levels are multiplicative, with a gentle 10% change for every * nice level changed. I.e. when a CPU-bound task goes from nice 0 to diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 06e226166aab..2930e42eafc2 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -262,39 +262,6 @@ static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, return rc; } -/** - * task_is_descendant - walk up a process family tree looking for a match - * @parent: the process to compare against while walking up from child - * @child: the process to start from while looking upwards for parent - * - * Returns 1 if child is a descendant of parent, 0 if not. - */ -static int task_is_descendant(struct task_struct *parent, - struct task_struct *child) -{ - int rc = 0; - struct task_struct *walker = child; - - if (!parent || !child) - return 0; - - rcu_read_lock(); - if (!thread_group_leader(parent)) - parent = rcu_dereference(parent->group_leader); - while (walker->pid > 0) { - if (!thread_group_leader(walker)) - walker = rcu_dereference(walker->group_leader); - if (walker == parent) { - rc = 1; - break; - } - walker = rcu_dereference(walker->real_parent); - } - rcu_read_unlock(); - - return rc; -} - /** * ptracer_exception_found - tracer registered as exception for this tracee * @tracer: the task_struct of the process attempting ptrace From patchwork Tue Jan 12 15:09:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Guy Briggs X-Patchwork-Id: 12013759 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=-16.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=unavailable 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 60579C4332D for ; Tue, 12 Jan 2021 15:14:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 29BAC23132 for ; Tue, 12 Jan 2021 15:14:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2404945AbhALPN1 (ORCPT ); Tue, 12 Jan 2021 10:13:27 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:56994 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2404820AbhALPN0 (ORCPT ); Tue, 12 Jan 2021 10:13:26 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610464319; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:in-reply-to:in-reply-to:references:references; bh=finGuG2ApMK4JJ38FNio/KBQwRfw0aPFGgdls4QF7m4=; b=X6h8lEoP2pU85zYvTwCQOVx/8hbM5tb6K+JlgUJuhvPpGeiWJBAPdvlm3o4AR0ahXwrqYJ AjQpyChq+3qskwK+nELwHM6fAFq2dd0uxZwO46hZQjpxBKBjV5Ys88OOhuYi96owZoqB1r 8F/EeqQa/VfRDlLNE7VyCTkjpHU8Q/o= 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-3-2AuaJkmLOGuh7Uww0biOvQ-1; Tue, 12 Jan 2021 10:11:57 -0500 X-MC-Unique: 2AuaJkmLOGuh7Uww0biOvQ-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 77664107ACF8; Tue, 12 Jan 2021 15:11:55 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id CF6765D9CD; Tue, 12 Jan 2021 15:11:51 +0000 (UTC) From: Richard Guy Briggs To: Linux Containers List , Linux API , Linux-Audit Mailing List , Linux FSdevel , LKML , Linux NetDev Upstream Mailing List , Netfilter Devel List Cc: Neil Horman , David Howells , "Eric W. Biederman" , Simo Sorce , Eric Paris , mpatel@redhat.com, Serge Hallyn , Kees Cook , Jens Axboe , Christian Brauner , Richard Guy Briggs Subject: [PATCH ghak90 v11 10/11] audit: track container nesting Date: Tue, 12 Jan 2021 10:09:38 -0500 Message-Id: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Track the parent container of a container to be able to filter and report nesting. Now that we have a way to track and check the parent container of a container, modify the contid field format to be able to report that nesting using a carrat ("^") modifier to indicate nesting. The original field format was "contid=" for task-associated records and "contid=[,[...]]" for network-namespace-associated records. The new field format is "contid=[,^[...]][,[...]]". For task event example, an orchestrator in contid 1 spawns tasks in contid 2 and contid 3, then the task in contid 2 spawns a task in contid 4. An event happens in the task in contid 4: type=SYSCALL ... type=CONTAINER_ID msg=audit(:): contid=4,^2,^1 For a network namespace event example, an orchestrator in contid 1 in network namespace A spawns peer tasks 2 and 3 in network namespace B. An event happens in network namespace B: type=NETFILTER_PKT ... type=CONTAINER_ID msg=audit(:): contid=2,^1,3,^1 Signed-off-by: Richard Guy Briggs --- kernel/audit.c | 75 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index fcb78a6d8e4a..d2e9d803e5fd 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -231,6 +231,7 @@ struct audit_contobj { refcount_t refcount; refcount_t sigcount; struct rcu_head rcu; + struct audit_contobj *parent; }; struct audit_task_info { @@ -253,6 +254,7 @@ struct audit_contobj_netns { static void audit_netns_contid_add(struct net *net, struct audit_contobj *cont); static void audit_netns_contid_del(struct net *net, struct audit_contobj *cont); +static void audit_log_contid(struct audit_buffer *ab, struct audit_contobj *cont); void __init audit_task_init(void) { @@ -378,6 +380,7 @@ static void _audit_contobj_put_sig(struct audit_contobj *cont) if (refcount_dec_and_test(&cont->sigcount)) { if (!refcount_read(&cont->refcount)) { put_task_struct(cont->owner); + _audit_contobj_put(cont->parent); list_del_rcu(&cont->list); kfree_rcu(cont, rcu); } @@ -722,11 +725,11 @@ int audit_log_netns_contid_list(struct net *net, struct audit_context *context) audit_log_lost("out of memory in audit_log_netns_contid_list"); goto out; } - audit_log_format(ab, "record=1 contid=%llu", - cont->obj->id); + audit_log_format(ab, "record=1 contid="); } else { - audit_log_format(ab, ",%llu", cont->obj->id); + audit_log_format(ab, ","); } + audit_log_contid(ab, cont->obj); } audit_log_end(ab); out: @@ -1906,6 +1909,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case AUDIT_SIGNAL_INFO2: { char *contidstr = NULL; unsigned int contidstrlen = 0; + struct audit_contobj *cont = audit_sig_cid; len = 0; if (audit_sig_sid) { @@ -1915,13 +1919,27 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) return err; } if (audit_sig_cid) { - contidstr = kmalloc(21, GFP_KERNEL); + contidstr = kmalloc(AUDIT_MESSAGE_TEXT_MAX, GFP_KERNEL); if (!contidstr) { if (audit_sig_sid) security_release_secctx(ctx, len); return -ENOMEM; } - contidstrlen = scnprintf(contidstr, 20, "%llu", audit_sig_cid->id); + rcu_read_lock(); + while (cont) { + if (cont->parent) + contidstrlen += scnprintf(contidstr, + AUDIT_MESSAGE_TEXT_MAX - + contidstrlen, + "%llu,^", cont->id); + else + contidstrlen += scnprintf(contidstr, + AUDIT_MESSAGE_TEXT_MAX - + contidstrlen, + "%llu", cont->id); + cont = cont->parent; + } + rcu_read_unlock(); } sig_data2 = kmalloc(sizeof(*sig_data2) + contidstrlen + len, GFP_KERNEL); if (!sig_data2) { @@ -2608,6 +2626,23 @@ void audit_log_session_info(struct audit_buffer *ab) audit_log_format(ab, "auid=%u ses=%u", auid, sessionid); } +static void audit_log_contid(struct audit_buffer *ab, struct audit_contobj *cont) +{ + if (!cont) { + audit_log_format(ab, "-1"); + return; + } + rcu_read_lock(); + while (cont) { + if (cont->parent) + audit_log_format(ab, "%llu,^", cont->id); + else + audit_log_format(ab, "%llu", cont->id); + cont = cont->parent; + } + rcu_read_unlock(); +} + /* * _audit_log_container_id - report container info * @context: task or local context for record @@ -2627,8 +2662,9 @@ static int _audit_log_container_id(struct audit_context *context, ab = audit_log_start(context, GFP_KERNEL, AUDIT_CONTAINER_ID); if (!ab) return 0; - audit_log_format(ab, "record=%d contid=%llu", - record = ++context->contid_records, contobj->id); + audit_log_format(ab, "record=%d contid=", + record = ++context->contid_records); + audit_log_contid(ab, contobj); audit_log_end(ab); return record; } @@ -2664,7 +2700,18 @@ int audit_log_container_id_ctx(struct audit_context *context) int audit_contid_comparator(struct task_struct *tsk, u32 op, u64 right) { - return audit_comparator64(audit_get_contid(tsk), op, right); + struct audit_contobj *cont = NULL; + int h; + int result = 0; + u64 left = audit_get_contid(tsk); + + h = audit_hash_contid(left); + list_for_each_entry_rcu(cont, &audit_contid_hash[h], list) { + result = audit_comparator64(cont->id, op, right); + if (result) + break; + } + return result; } void audit_log_key(struct audit_buffer *ab, char *key) @@ -3019,6 +3066,7 @@ int audit_set_contid(struct task_struct *tsk, u64 contid) INIT_LIST_HEAD(&newcont->list); newcont->id = contid; newcont->owner = get_task_struct(current); + newcont->parent = _audit_contobj_get_bytask(newcont->owner); refcount_set(&newcont->refcount, 1); list_add_rcu(&newcont->list, &audit_contid_hash[h]); @@ -3047,9 +3095,9 @@ int audit_set_contid(struct task_struct *tsk, u64 contid) if (!ab) return rc; - audit_log_format(ab, - "op=set opid=%d contid=%llu old-contid=%llu", - task_tgid_nr(tsk), contid, oldcont ? oldcont->id : -1); + audit_log_format(ab, "op=set opid=%d contid=%llu old-contid=", + task_tgid_nr(tsk), contid); + audit_log_contid(ab, oldcont); spin_lock_irqsave(&_audit_contobj_list_lock, flags); _audit_contobj_put(oldcont); spin_unlock_irqrestore(&_audit_contobj_list_lock, flags); @@ -3088,8 +3136,9 @@ void audit_log_container_drop(void) ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONTAINER_OP); if (!ab) goto out; - audit_log_format(ab, "op=drop opid=%d contid=-1 old-contid=%llu", - task_tgid_nr(current), cont->id); + audit_log_format(ab, "op=drop opid=%d contid=-1 old-contid=", + task_tgid_nr(current)); + audit_log_contid(ab, cont); audit_log_end(ab); out: spin_lock_irqsave(&_audit_contobj_list_lock, flags); From patchwork Tue Jan 12 15:09:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Guy Briggs X-Patchwork-Id: 12013761 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=-16.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=unavailable 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 414ADC43219 for ; Tue, 12 Jan 2021 15:14:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F217E23131 for ; Tue, 12 Jan 2021 15:14:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2405116AbhALPNh (ORCPT ); Tue, 12 Jan 2021 10:13:37 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:30375 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2405041AbhALPNd (ORCPT ); Tue, 12 Jan 2021 10:13:33 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610464326; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:in-reply-to:in-reply-to:references:references; bh=eYAwFL1bjtKuxrU7qTxXl+J5KIWZcpoUKayCM4CfTx4=; b=R1ho4XMeBIHD8Nqf+yzS6AzVstEkShfY6rRlpqKU5viVDJvC3n9ILorPZ8y9kQGdWxZBbq zUQajotW15MTORp34iDuFzOOICWxNCBfVTHkwShjrHQz2LUqjVqCpNU/paIHqROvuEsv6F OrhdjrIGi0XVEebkLg50efTs41BW7Os= 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-585-iK-nb-EHMbicuqqU05FD5Q-1; Tue, 12 Jan 2021 10:12:04 -0500 X-MC-Unique: iK-nb-EHMbicuqqU05FD5Q-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id CEE4C100C601; Tue, 12 Jan 2021 15:12:01 +0000 (UTC) Received: from madcap2.tricolour.ca (unknown [10.10.110.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id BB2325D9CD; Tue, 12 Jan 2021 15:11:55 +0000 (UTC) From: Richard Guy Briggs To: Linux Containers List , Linux API , Linux-Audit Mailing List , Linux FSdevel , LKML , Linux NetDev Upstream Mailing List , Netfilter Devel List Cc: Neil Horman , David Howells , "Eric W. Biederman" , Simo Sorce , Eric Paris , mpatel@redhat.com, Serge Hallyn , Kees Cook , Jens Axboe , Christian Brauner , Richard Guy Briggs Subject: [PATCH ghak90 v11 11/11] audit: add capcontid to set contid outside init_user_ns Date: Tue, 12 Jan 2021 10:09:39 -0500 Message-Id: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Provide a mechanism similar to CAP_AUDIT_CONTROL to explicitly give a process in a non-init user namespace the capability to set audit container identifiers of individual children. Provide the /proc/$PID/audit_capcontid interface to capcontid. Valid values are: 1==enabled, 0==disabled Writing a "1" to this special file for the target process $PID will enable the target process to set audit container identifiers of its descendants. A process must already have CAP_AUDIT_CONTROL in the initial user namespace or have had audit_capcontid enabled by a previous use of this feature by its parent on this process in order to be able to enable it for another process. The target process must be a descendant of the calling process. Report this action in new message type AUDIT_SET_CAPCONTID 1022 with fields opid= capcontid= old-capcontid= Add an entry to Documentation/ABI. Signed-off-by: Richard Guy Briggs --- .../ABI/testing/procfs-audit_containerid | 16 +++++ fs/proc/base.c | 54 +++++++++++++++ include/linux/audit.h | 4 +- include/uapi/linux/audit.h | 1 + kernel/audit.c | 65 ++++++++++++++++++- 5 files changed, 137 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/procfs-audit_containerid b/Documentation/ABI/testing/procfs-audit_containerid index 30ea64790473..c697d7da0ad1 100644 --- a/Documentation/ABI/testing/procfs-audit_containerid +++ b/Documentation/ABI/testing/procfs-audit_containerid @@ -11,3 +11,19 @@ Description: or have its own /proc/$pid/capcontainerid set to write or read. + +What: Capability to set or get the Audit Container Identifier +Date: 2020-?? +KernelVersion: 5.10? +Contact: linux-audit@redhat.com +Format: u32 +Users: auditd, libaudit, audit-testsuite, podman(?), container orchestrators +Description: + The /proc/$pid/audit_capcontainerid pseudofile is + written to set and is read to get the capability of + process $pid to write or to read the /proc/$pid/containerid + audit container identifier of any of its descendants. + "1" allows and "0" denies that capability. This + property is an extension to CAP_AUDIT_CONTROL outside of + the initial user namespace. + diff --git a/fs/proc/base.c b/fs/proc/base.c index bf447e7932d2..7495eec1c73b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1383,6 +1383,58 @@ static const struct file_operations proc_contid_operations = { .write = proc_contid_write, .llseek = generic_file_llseek, }; + +static ssize_t proc_capcontid_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode = file_inode(file); + struct task_struct *task = get_proc_task(inode); + ssize_t length; + char tmpbuf[TMPBUFLEN]; + + if (!task) + return -ESRCH; + length = audit_get_capcontid_proc(tmpbuf, TMPBUFLEN, task); + put_task_struct(task); + if (length < 0) + return length; + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); +} + +static ssize_t proc_capcontid_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode = file_inode(file); + u32 capcontid; + int rv; + struct task_struct *task = get_proc_task(inode); + + if (!task) + return -ESRCH; + if (*ppos != 0) { + /* No partial writes. */ + put_task_struct(task); + return -EINVAL; + } + + rv = kstrtou32_from_user(buf, count, 10, &capcontid); + if (rv < 0) { + put_task_struct(task); + return rv; + } + + rv = audit_set_capcontid(task, capcontid); + put_task_struct(task); + if (rv < 0) + return rv; + return count; +} + +static const struct file_operations proc_capcontid_operations = { + .read = proc_capcontid_read, + .write = proc_capcontid_write, + .llseek = generic_file_llseek, +}; #endif #ifdef CONFIG_FAULT_INJECTION @@ -3286,6 +3338,7 @@ static const struct pid_entry tgid_base_stuff[] = { REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), REG("sessionid", S_IRUGO, proc_sessionid_operations), REG("audit_containerid", S_IWUSR|S_IRUSR, proc_contid_operations), + REG("audit_capcontainerid", S_IWUSR|S_IRUSR, proc_capcontid_operations), #endif #ifdef CONFIG_FAULT_INJECTION REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations), @@ -3629,6 +3682,7 @@ static const struct pid_entry tid_base_stuff[] = { REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), REG("sessionid", S_IRUGO, proc_sessionid_operations), REG("audit_containerid", S_IWUSR|S_IRUSR, proc_contid_operations), + REG("audit_capcontainerid", S_IWUSR|S_IRUSR, proc_capcontid_operations), #endif #ifdef CONFIG_FAULT_INJECTION REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations), diff --git a/include/linux/audit.h b/include/linux/audit.h index 014f73296fec..3f5444393618 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -214,8 +214,10 @@ extern void audit_set_sessionid_iouring(unsigned int sessionid); extern int audit_get_contid_proc(char *tmpbuf, int TMPBUFLEN, struct task_struct *task); - +extern int audit_get_capcontid_proc(char *tmpbuf, int TMPBUFLEN, + struct task_struct *task); extern int audit_set_contid(struct task_struct *tsk, u64 contid); +extern int audit_set_capcontid(struct task_struct *tsk, u32 enable); extern void audit_copy_namespaces(struct net *net, struct task_struct *tsk); extern void audit_switch_task_namespaces(struct nsproxy *ns, diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 66350e572e41..b7c7ab2e6a1d 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -73,6 +73,7 @@ #define AUDIT_GET_FEATURE 1019 /* Get which features are enabled */ #define AUDIT_CONTAINER_OP 1020 /* Define the container id and info */ #define AUDIT_SIGNAL_INFO2 1021 /* Get info auditd signal sender */ +#define AUDIT_SET_CAPCONTID 1022 /* Set cap_contid of a task */ #define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages mostly uninteresting to kernel */ #define AUDIT_USER_AVC 1107 /* We filter this differently */ diff --git a/kernel/audit.c b/kernel/audit.c index d2e9d803e5fd..7e825305b3c2 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -238,6 +238,7 @@ struct audit_task_info { kuid_t loginuid; unsigned int sessionid; struct audit_contobj *cont; + u32 capcontid; #ifdef CONFIG_AUDITSYSCALL struct audit_context *ctx; #endif @@ -308,6 +309,15 @@ static inline u64 audit_get_contid(struct task_struct *tsk) return info->cont->id; } +static inline u32 audit_get_capcontid(struct task_struct *tsk) +{ + struct audit_task_info *info = tsk->audit; + + if (!info) + return 0; + return info->capcontid; +} + inline struct audit_context *_audit_context(struct task_struct *tsk) { struct audit_task_info *info = tsk->audit; @@ -420,6 +430,7 @@ static void audit_alloc_task(struct task_struct *tsk) rcu_read_lock(); info->cont = _audit_contobj_get_bytask(current); rcu_read_unlock(); + info->capcontid = 0; tsk->audit = info; } @@ -2988,6 +2999,56 @@ static bool audit_contid_isnesting(struct task_struct *tsk) return !isowner && ownerisparent; } +int audit_set_capcontid(struct task_struct *tsk, u32 enable) +{ + u32 oldcapcontid; + int rc = 0; + struct audit_buffer *ab; + struct audit_task_info *info = tsk->audit; + + if (!info) + return -ENOPROTOOPT; + oldcapcontid = audit_get_capcontid(tsk); + /* if task is not descendant, block */ + if (tsk == current || !task_is_descendant(current, tsk)) + rc = -EXDEV; + else if (current_user_ns() == &init_user_ns) { + if (!capable(CAP_AUDIT_CONTROL) && + !audit_get_capcontid(current)) + rc = -EPERM; + } + if (!rc) + info->capcontid = enable; + + if (!audit_enabled) + return rc; + + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_SET_CAPCONTID); + if (!ab) + return rc; + + audit_log_format(ab, + "opid=%d capcontid=%u old-capcontid=%u", + task_tgid_nr(tsk), enable, oldcapcontid); + audit_log_end(ab); + return rc; +} + +int audit_get_capcontid_proc(char *tmpbuf, int TMPBUFLEN, + struct task_struct *tsk) +{ + int length; + + /* if we don't have caps, reject */ + if (!capable(CAP_AUDIT_CONTROL) && !audit_get_capcontid(current)) { + length = -EPERM; + goto out; + } + length = scnprintf(tmpbuf, TMPBUFLEN, "%u", audit_get_capcontid(tsk)); +out: + return length; +} + /* * audit_set_contid - set current task's audit contid * @tsk: target task @@ -3021,7 +3082,7 @@ int audit_set_contid(struct task_struct *tsk, u64 contid) if (contid == AUDIT_CID_UNSET) { /* Don't allow the contid to be unset */ rc = -EINVAL; - } else if (!capable(CAP_AUDIT_CONTROL)) { + } else if (!capable(CAP_AUDIT_CONTROL) && !audit_get_capcontid(current)) { /* if we don't have caps, reject */ rc = -EPERM; } else if (!list_empty(&tsk->children) || @@ -3111,7 +3172,7 @@ int audit_get_contid_proc(char *tmpbuf, int TMPBUFLEN, int length; /* if we don't have caps, reject */ - if (!capable(CAP_AUDIT_CONTROL)) { + if (!capable(CAP_AUDIT_CONTROL) && !audit_get_capcontid(current)) { length = -EPERM; goto out; }