From patchwork Thu Sep 21 06:16:35 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 13394391 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EB174E7D0A2 for ; Thu, 21 Sep 2023 19:29:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229952AbjIUT3r (ORCPT ); Thu, 21 Sep 2023 15:29:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43716 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230301AbjIUT3T (ORCPT ); Thu, 21 Sep 2023 15:29:19 -0400 Received: from smtp-bc09.mail.infomaniak.ch (smtp-bc09.mail.infomaniak.ch [45.157.188.9]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6E05612446 for ; Thu, 21 Sep 2023 10:06:53 -0700 (PDT) Received: from smtp-2-0001.mail.infomaniak.ch (unknown [10.5.36.108]) by smtp-2-3000.mail.infomaniak.ch (Postfix) with ESMTPS id 4RrlY738kszMpnsb; Thu, 21 Sep 2023 06:16:59 +0000 (UTC) Received: from unknown by smtp-2-0001.mail.infomaniak.ch (Postfix) with ESMTPA id 4RrlY62SQNzMppKT; Thu, 21 Sep 2023 08:16:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=digikod.net; s=20191114; t=1695277019; bh=C96mByL5VMgKe+z3S4sdB6o2Dq3umyYV2RysHQLeFgI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NIh9AE6iGXfm/w7dRtI3eR/bv26/nMk6QUSrB46LLWPGRDS69Fz10CJeqMYOtM3Jf ptzdpk0XS+wZajN8f6q6NpzX9bYPyCWibReuzVDfHT/HDZAh7GkkTTYCTiZiNYxqj0 1xcDI7CD86C3lYgqB/e/ZiQ2kUmJEoNIcZ/oLGME= From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: Eric Paris , James Morris , Paul Moore , "Serge E . Hallyn" Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Ben Scarlato , =?utf-8?q?G=C3=BCnther_Noack?= , Jeff Xu , Jorge Lucangeli Obes , Konstantin Meskhidze , Shervin Oloumi , audit@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Subject: [RFC PATCH v1 1/7] lsm: Add audit_log_lsm_data() helper Date: Thu, 21 Sep 2023 08:16:35 +0200 Message-ID: <20230921061641.273654-2-mic@digikod.net> In-Reply-To: <20230921061641.273654-1-mic@digikod.net> References: <20230921061641.273654-1-mic@digikod.net> MIME-Version: 1.0 X-Infomaniak-Routing: alpha Precedence: bulk List-ID: Extract code from common_dump_audit_data() into the audit_log_lsm_data() helper. This helps reuse common LSM audit data while not abusing AUDIT_AVC records because of the common_lsm_audit() helper. Signed-off-by: Mickaël Salaün Acked-by: Paul Moore --- include/linux/lsm_audit.h | 2 ++ security/lsm_audit.c | 26 +++++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 97a8b21eb033..5f9a7ed0e7a5 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -122,6 +122,8 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, int ipv6_skb_to_auditdata(struct sk_buff *skb, struct common_audit_data *ad, u8 *proto); +void audit_log_lsm_data(struct audit_buffer *ab, struct common_audit_data *a); + void common_lsm_audit(struct common_audit_data *a, void (*pre_audit)(struct audit_buffer *, void *), void (*post_audit)(struct audit_buffer *, void *)); diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 849e832719e2..58f9b8bde22a 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -189,16 +189,12 @@ static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr, } /** - * dump_common_audit_data - helper to dump common audit data + * audit_log_lsm_data - helper to log common LSM audit data * @ab : the audit buffer * @a : common audit data - * */ -static void dump_common_audit_data(struct audit_buffer *ab, - struct common_audit_data *a) +void audit_log_lsm_data(struct audit_buffer *ab, struct common_audit_data *a) { - char comm[sizeof(current->comm)]; - /* * To keep stack sizes in check force programmers to notice if they * start making this union too large! See struct lsm_network_audit @@ -206,9 +202,6 @@ static void dump_common_audit_data(struct audit_buffer *ab, */ BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2); - audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current)); - audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm))); - switch (a->type) { case LSM_AUDIT_DATA_NONE: return; @@ -428,6 +421,21 @@ static void dump_common_audit_data(struct audit_buffer *ab, } /* switch (a->type) */ } +/** + * dump_common_audit_data - helper to dump common audit data + * @ab : the audit buffer + * @a : common audit data + */ +static void dump_common_audit_data(struct audit_buffer *ab, + struct common_audit_data *a) +{ + char comm[sizeof(current->comm)]; + + audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current)); + audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm))); + audit_log_lsm_data(ab, a); +} + /** * common_lsm_audit - generic LSM auditing function * @a: auxiliary audit data From patchwork Thu Sep 21 06:16:36 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 13394651 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1B88DE7D0AF for ; Thu, 21 Sep 2023 21:13:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229743AbjIUVNJ (ORCPT ); Thu, 21 Sep 2023 17:13:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42614 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229682AbjIUVMw (ORCPT ); Thu, 21 Sep 2023 17:12:52 -0400 Received: from smtp-bc08.mail.infomaniak.ch (smtp-bc08.mail.infomaniak.ch [IPv6:2001:1600:4:17::bc08]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7F316FFAA for ; Thu, 21 Sep 2023 10:07:29 -0700 (PDT) Received: from smtp-2-0000.mail.infomaniak.ch (unknown [10.5.36.107]) by smtp-3-3000.mail.infomaniak.ch (Postfix) with ESMTPS id 4RrlY82PP8zMqBR2; Thu, 21 Sep 2023 06:17:00 +0000 (UTC) Received: from unknown by smtp-2-0000.mail.infomaniak.ch (Postfix) with ESMTPA id 4RrlY768ddzMpnPp; Thu, 21 Sep 2023 08:16:59 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=digikod.net; s=20191114; t=1695277020; bh=BDXkAyV7VKfAr64LJDe20wdFO/sHv02Ha0JAcCm1Kgc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZplcROJUNZdIJvCz4sSUz6FTBiWpRTWGj5Dcbx+AlXmx1S6SfeuH/IO1a1UKUXuyR +Mi6L3c5RMDFY+YvpvI3UOZvfobOhrV447a7CxqcEJ4GYcKrQZ2qBSSmKhMeK3Csqr bnC6CekX5RKbp3Jv0Mt4+c8z0svoYwb6EWtu5YRQ= From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: Eric Paris , James Morris , Paul Moore , "Serge E . Hallyn" Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Ben Scarlato , =?utf-8?q?G=C3=BCnther_Noack?= , Jeff Xu , Jorge Lucangeli Obes , Konstantin Meskhidze , Shervin Oloumi , audit@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Subject: [RFC PATCH v1 2/7] landlock: Factor out check_access_path() Date: Thu, 21 Sep 2023 08:16:36 +0200 Message-ID: <20230921061641.273654-3-mic@digikod.net> In-Reply-To: <20230921061641.273654-1-mic@digikod.net> References: <20230921061641.273654-1-mic@digikod.net> MIME-Version: 1.0 X-Infomaniak-Routing: alpha Precedence: bulk List-ID: Merge check_access_path() into current_check_access_path() and make hook_path_mknod() use it. Remove explicit inlining that can be handled by the compiler. Signed-off-by: Mickaël Salaün --- security/landlock/fs.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 1c0c198f6fdb..978e325d8708 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -635,28 +635,22 @@ static bool is_access_to_paths_allowed( return allowed_parent1 && allowed_parent2; } -static inline int check_access_path(const struct landlock_ruleset *const domain, - const struct path *const path, - access_mask_t access_request) -{ - layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; - - access_request = init_layer_masks(domain, access_request, &layer_masks); - if (is_access_to_paths_allowed(domain, path, access_request, - &layer_masks, NULL, 0, NULL, NULL)) - return 0; - return -EACCES; -} - -static inline int current_check_access_path(const struct path *const path, - const access_mask_t access_request) +static int current_check_access_path(const struct path *const path, + access_mask_t access_request) { const struct landlock_ruleset *const dom = landlock_get_current_domain(); + layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; if (!dom) return 0; - return check_access_path(dom, path, access_request); + + access_request = init_layer_masks(dom, access_request, &layer_masks); + if (is_access_to_paths_allowed(dom, path, access_request, &layer_masks, + NULL, 0, NULL, NULL)) + return 0; + + return -EACCES; } static inline access_mask_t get_mode_access(const umode_t mode) @@ -1128,12 +1122,7 @@ static int hook_path_mknod(const struct path *const dir, struct dentry *const dentry, const umode_t mode, const unsigned int dev) { - const struct landlock_ruleset *const dom = - landlock_get_current_domain(); - - if (!dom) - return 0; - return check_access_path(dom, dir, get_mode_access(mode)); + return current_check_access_path(dir, get_mode_access(mode)); } static int hook_path_symlink(const struct path *const dir, From patchwork Thu Sep 21 06:16:37 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 13394158 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 73B05E7D0A2 for ; Thu, 21 Sep 2023 17:32:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230084AbjIURcP (ORCPT ); Thu, 21 Sep 2023 13:32:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55084 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230195AbjIURcD (ORCPT ); Thu, 21 Sep 2023 13:32:03 -0400 Received: from smtp-bc0e.mail.infomaniak.ch (smtp-bc0e.mail.infomaniak.ch [IPv6:2001:1600:4:17::bc0e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CD5C2325ED for ; Thu, 21 Sep 2023 10:07:30 -0700 (PDT) Received: from smtp-2-0000.mail.infomaniak.ch (unknown [10.5.36.107]) by smtp-3-3000.mail.infomaniak.ch (Postfix) with ESMTPS id 4RrlY91sQLzMpnTw; Thu, 21 Sep 2023 06:17:01 +0000 (UTC) Received: from unknown by smtp-2-0000.mail.infomaniak.ch (Postfix) with ESMTPA id 4RrlY85V4rzMpnPj; Thu, 21 Sep 2023 08:17:00 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=digikod.net; s=20191114; t=1695277021; bh=0yxdKsrrn283gB/3X+vWCGUVPztG5L56CxeUEdJPWtQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Fj5Ru4uW6OQ7QjgtJyzjur3mooTtOBgfvlxfyD0fUPgvpHFo+5+6Tq6bGWuvIYHAJ 3f0SDzTMFCXwFcCek1QH0jplv7pHnCJiAwsv05UctrD0e7DyBkygZF+Qta5eL0F117 7BnDLAgkUqYPsQ9XuCYFnzAXcQXC/dOT0TK3VtF0= From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: Eric Paris , James Morris , Paul Moore , "Serge E . Hallyn" Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Ben Scarlato , =?utf-8?q?G=C3=BCnther_Noack?= , Jeff Xu , Jorge Lucangeli Obes , Konstantin Meskhidze , Shervin Oloumi , audit@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Subject: [RFC PATCH v1 3/7] landlock: Log ruleset creation and release Date: Thu, 21 Sep 2023 08:16:37 +0200 Message-ID: <20230921061641.273654-4-mic@digikod.net> In-Reply-To: <20230921061641.273654-1-mic@digikod.net> References: <20230921061641.273654-1-mic@digikod.net> MIME-Version: 1.0 X-Infomaniak-Routing: alpha Precedence: bulk List-ID: Add audit support for ruleset/domain creation and release. Ruleset and domain IDs are generated from the same 64-bit counter to avoid confusing them. There is no need to hide the sequentiality to users that are already allowed to read logs. In the future, if these IDs were to be viewable by unprivileged users, then we'll need to scramble them. Add a new AUDIT_LANDLOCK record type. Signed-off-by: Mickaël Salaün --- include/uapi/linux/audit.h | 1 + security/landlock/Makefile | 2 + security/landlock/audit.c | 119 +++++++++++++++++++++++++++++++++++ security/landlock/audit.h | 35 +++++++++++ security/landlock/ruleset.c | 6 ++ security/landlock/ruleset.h | 10 +++ security/landlock/syscalls.c | 8 +++ 7 files changed, 181 insertions(+) create mode 100644 security/landlock/audit.c create mode 100644 security/landlock/audit.h diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index d676ed2b246e..385e134277b1 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -122,6 +122,7 @@ #define AUDIT_OPENAT2 1337 /* Record showing openat2 how args */ #define AUDIT_DM_CTRL 1338 /* Device Mapper target control */ #define AUDIT_DM_EVENT 1339 /* Device Mapper events */ +#define AUDIT_LANDLOCK 1340 /* Landlock event */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ diff --git a/security/landlock/Makefile b/security/landlock/Makefile index 7bbd2f413b3e..c3e048df7fec 100644 --- a/security/landlock/Makefile +++ b/security/landlock/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o landlock-y := setup.o syscalls.o object.o ruleset.o \ cred.o ptrace.o fs.o + +landlock-$(CONFIG_AUDIT) += audit.o diff --git a/security/landlock/audit.c b/security/landlock/audit.c new file mode 100644 index 000000000000..f58bd529784a --- /dev/null +++ b/security/landlock/audit.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Landlock LSM - Audit helpers + * + * Copyright © 2023 Microsoft Corporation + */ + +#include +#include +#include + +#include "audit.h" +#include "cred.h" + +atomic64_t ruleset_and_domain_counter = ATOMIC64_INIT(0); + +#define BIT_INDEX(bit) HWEIGHT(bit - 1) + +static void log_accesses(struct audit_buffer *const ab, + const access_mask_t accesses) +{ + const char *const desc[] = { + [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = "execute", + [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = "write_file", + [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_FILE)] = "read_file", + [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_DIR)] = "read_dir", + [BIT_INDEX(LANDLOCK_ACCESS_FS_REMOVE_DIR)] = "remove_dir", + [BIT_INDEX(LANDLOCK_ACCESS_FS_REMOVE_FILE)] = "remove_file", + [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_CHAR)] = "make_char", + [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_DIR)] = "make_dir", + [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_REG)] = "make_reg", + [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_SOCK)] = "make_sock", + [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_FIFO)] = "make_fifo", + [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_BLOCK)] = "make_block", + [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_SYM)] = "make_sym", + [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = "refer", + [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "truncate", + }; + const unsigned long access_mask = accesses; + unsigned long access_bit; + bool is_first = true; + + BUILD_BUG_ON(ARRAY_SIZE(desc) != LANDLOCK_NUM_ACCESS_FS); + + for_each_set_bit(access_bit, &access_mask, ARRAY_SIZE(desc)) { + audit_log_format(ab, "%s%s", is_first ? "" : ",", + desc[access_bit]); + is_first = false; + } +} + +/* Inspired by dump_common_audit_data(). */ +static void log_task(struct audit_buffer *const ab) +{ + /* 16 bytes (TASK_COMM_LEN) */ + char comm[sizeof(current->comm)]; + + /* + * Uses task_pid_nr() instead of task_tgid_nr() because of how + * credentials and Landlock work. + */ + audit_log_format(ab, "tid=%d comm=", task_pid_nr(current)); + audit_log_untrustedstring(ab, + memcpy(comm, current->comm, sizeof(comm))); +} + +void landlock_log_create_ruleset(struct landlock_ruleset *const ruleset) +{ + struct audit_buffer *ab; + + WARN_ON_ONCE(ruleset->id); + + ab = audit_log_start(audit_context(), GFP_ATOMIC, AUDIT_LANDLOCK); + if (!ab) + /* audit_log_lost() call */ + return; + + ruleset->id = atomic64_inc_return(&ruleset_and_domain_counter); + log_task(ab); + audit_log_format(ab, + " op=create-ruleset ruleset=%llu handled_access_fs=", + ruleset->id); + log_accesses(ab, ruleset->fs_access_masks[ruleset->num_layers - 1]); + audit_log_end(ab); +} + +/* + * This is useful to know when a domain or a ruleset will never show again in + * the audit log. + */ +void landlock_log_release_ruleset(const struct landlock_ruleset *const ruleset) +{ + struct audit_buffer *ab; + const char *name; + u64 id; + + ab = audit_log_start(audit_context(), GFP_ATOMIC, AUDIT_LANDLOCK); + if (!ab) + /* audit_log_lost() call */ + return; + + /* It should either be a domain or a ruleset. */ + if (ruleset->hierarchy) { + name = "domain"; + id = ruleset->hierarchy->id; + WARN_ON_ONCE(ruleset->id); + } else { + name = "ruleset"; + id = ruleset->id; + } + WARN_ON_ONCE(!id); + + /* + * Because this might be called by kernel threads, logging + * related task information with log_task() would be useless. + */ + audit_log_format(ab, "op=release-%s %s=%llu", name, name, id); + audit_log_end(ab); +} diff --git a/security/landlock/audit.h b/security/landlock/audit.h new file mode 100644 index 000000000000..2666e9151627 --- /dev/null +++ b/security/landlock/audit.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Landlock LSM - Audit helpers + * + * Copyright © 2023 Microsoft Corporation + */ + +#ifndef _SECURITY_LANDLOCK_AUDIT_H +#define _SECURITY_LANDLOCK_AUDIT_H + +#include +#include + +#include "ruleset.h" + +#ifdef CONFIG_AUDIT + +void landlock_log_create_ruleset(struct landlock_ruleset *const ruleset); +void landlock_log_release_ruleset(const struct landlock_ruleset *const ruleset); + +#else /* CONFIG_AUDIT */ + +static inline void +landlock_log_create_ruleset(struct landlock_ruleset *const ruleset) +{ +} + +static inline void +landlock_log_release_ruleset(const struct landlock_ruleset *const ruleset) +{ +} + +#endif /* CONFIG_AUDIT */ + +#endif /* _SECURITY_LANDLOCK_AUDIT_H */ diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c index 996484f98bfd..585ee0f77e67 100644 --- a/security/landlock/ruleset.c +++ b/security/landlock/ruleset.c @@ -20,6 +20,7 @@ #include #include +#include "audit.h" #include "limits.h" #include "object.h" #include "ruleset.h" @@ -379,6 +380,11 @@ static void free_ruleset_work(struct work_struct *const work) struct landlock_ruleset *ruleset; ruleset = container_of(work, struct landlock_ruleset, work_free); + + /* Only called by hook_cred_free(), hence for a domain. */ + WARN_ON_ONCE(!ruleset->hierarchy); + landlock_log_release_ruleset(ruleset); + free_ruleset(ruleset); } diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h index 55b1df8f66a8..c74f1ab60c33 100644 --- a/security/landlock/ruleset.h +++ b/security/landlock/ruleset.h @@ -74,6 +74,11 @@ struct landlock_rule { * struct landlock_hierarchy - Node in a ruleset hierarchy */ struct landlock_hierarchy { +#ifdef CONFIG_AUDIT + /* domain's ID */ + u64 id; +#endif /* CONFIG_AUDIT */ + /** * @parent: Pointer to the parent node, or NULL if it is a root * Landlock domain. @@ -93,6 +98,11 @@ struct landlock_hierarchy { * match an object. */ struct landlock_ruleset { +#ifdef CONFIG_AUDIT + /* ruleset's ID, must be 0 for a domain */ + u64 id; +#endif /* CONFIG_AUDIT */ + /** * @root: Root of a red-black tree containing &struct landlock_rule * nodes. Once a ruleset is tied to a process (i.e. as a domain), this diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 245cc650a4dc..373997a356e7 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -26,6 +26,7 @@ #include #include +#include "audit.h" #include "cred.h" #include "fs.h" #include "limits.h" @@ -98,6 +99,10 @@ static int fop_ruleset_release(struct inode *const inode, { struct landlock_ruleset *ruleset = filp->private_data; + /* Only called by ruleset_fops, hence for a ruleset. */ + WARN_ON_ONCE(ruleset->hierarchy); + landlock_log_release_ruleset(ruleset); + landlock_put_ruleset(ruleset); return 0; } @@ -198,6 +203,9 @@ SYSCALL_DEFINE3(landlock_create_ruleset, ruleset, O_RDWR | O_CLOEXEC); if (ruleset_fd < 0) landlock_put_ruleset(ruleset); + else + landlock_log_create_ruleset(ruleset); + return ruleset_fd; } From patchwork Thu Sep 21 06:16:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 13394483 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CC286E7D0A2 for ; Thu, 21 Sep 2023 20:10:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230183AbjIUUKn (ORCPT ); Thu, 21 Sep 2023 16:10:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52040 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232142AbjIUUKX (ORCPT ); Thu, 21 Sep 2023 16:10:23 -0400 Received: from smtp-42ae.mail.infomaniak.ch (smtp-42ae.mail.infomaniak.ch [84.16.66.174]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5931FA9FC for ; Thu, 21 Sep 2023 10:07:29 -0700 (PDT) Received: from smtp-2-0000.mail.infomaniak.ch (unknown [10.5.36.107]) by smtp-3-3000.mail.infomaniak.ch (Postfix) with ESMTPS id 4RrlYB1SMrzMqhBb; Thu, 21 Sep 2023 06:17:02 +0000 (UTC) Received: from unknown by smtp-2-0000.mail.infomaniak.ch (Postfix) with ESMTPA id 4RrlY94vr5zMpnPm; Thu, 21 Sep 2023 08:17:01 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=digikod.net; s=20191114; t=1695277022; bh=pwiClldhXZTIfu2VonXNuU9Opgc7X09SctOzocW98K0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KkZZDXG0h5eg5qf9E5uRpx65dfe7+huAplBMEQEh2rZJAX7DRa8SoWlQwaBZsd4md hMgYWcsBPnorxQyG6aAANU34q5RipSKJIFqxAR4pakJujsnoKb1KohJ9LUfeitYlEv fj+woUen9V4VTxCXcOd9rMxfs3wKs5gaJOOziVXQ= From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: Eric Paris , James Morris , Paul Moore , "Serge E . Hallyn" Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Ben Scarlato , =?utf-8?q?G=C3=BCnther_Noack?= , Jeff Xu , Jorge Lucangeli Obes , Konstantin Meskhidze , Shervin Oloumi , audit@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Subject: [RFC PATCH v1 4/7] landlock: Log domain creation and enforcement Date: Thu, 21 Sep 2023 08:16:38 +0200 Message-ID: <20230921061641.273654-5-mic@digikod.net> In-Reply-To: <20230921061641.273654-1-mic@digikod.net> References: <20230921061641.273654-1-mic@digikod.net> MIME-Version: 1.0 X-Infomaniak-Routing: alpha Precedence: bulk List-ID: Add audit support for domain creation, i.e. task self-restriction. Signed-off-by: Mickaël Salaün --- security/landlock/audit.c | 24 ++++++++++++++++++++++++ security/landlock/audit.h | 8 ++++++++ security/landlock/syscalls.c | 4 ++++ 3 files changed, 36 insertions(+) diff --git a/security/landlock/audit.c b/security/landlock/audit.c index f58bd529784a..d9589d07e126 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -84,6 +84,30 @@ void landlock_log_create_ruleset(struct landlock_ruleset *const ruleset) audit_log_end(ab); } +void landlock_log_restrict_self(struct landlock_ruleset *const domain, + struct landlock_ruleset *const ruleset) +{ + struct audit_buffer *ab; + + WARN_ON_ONCE(domain->id); + WARN_ON_ONCE(!ruleset->id); + + ab = audit_log_start(audit_context(), GFP_ATOMIC, AUDIT_LANDLOCK); + if (!ab) + /* audit_log_lost() call */ + return; + + domain->hierarchy->id = + atomic64_inc_return(&ruleset_and_domain_counter); + log_task(ab); + audit_log_format(ab, " op=restrict-self domain=%llu ruleset=%llu", + domain->hierarchy->id, ruleset->id); + audit_log_format( + ab, " parent=%llu", + domain->hierarchy->parent ? domain->hierarchy->parent->id : 0); + audit_log_end(ab); +} + /* * This is useful to know when a domain or a ruleset will never show again in * the audit log. diff --git a/security/landlock/audit.h b/security/landlock/audit.h index 2666e9151627..bc17dc8ca6f1 100644 --- a/security/landlock/audit.h +++ b/security/landlock/audit.h @@ -16,6 +16,8 @@ #ifdef CONFIG_AUDIT void landlock_log_create_ruleset(struct landlock_ruleset *const ruleset); +void landlock_log_restrict_self(struct landlock_ruleset *const domain, + struct landlock_ruleset *const ruleset); void landlock_log_release_ruleset(const struct landlock_ruleset *const ruleset); #else /* CONFIG_AUDIT */ @@ -25,6 +27,12 @@ landlock_log_create_ruleset(struct landlock_ruleset *const ruleset) { } +static inline void +landlock_log_restrict_self(struct landlock_ruleset *const domain, + struct landlock_ruleset *const ruleset) +{ +} + static inline void landlock_log_release_ruleset(const struct landlock_ruleset *const ruleset) { diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 373997a356e7..bfe5417a06c3 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -452,6 +452,10 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32, landlock_put_ruleset(new_llcred->domain); new_llcred->domain = new_dom; + // FIXME: Must be atomic between the ruleset merge and the audit log to + // be sure about the content of the domain. + // -> move mutex_lock() from merge_ruleset() into this function + landlock_log_restrict_self(new_dom, ruleset); landlock_put_ruleset(ruleset); return commit_creds(new_cred); From patchwork Thu Sep 21 06:16:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 13394390 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 86EDFE7D0A7 for ; Thu, 21 Sep 2023 19:28:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230311AbjIUT2i (ORCPT ); Thu, 21 Sep 2023 15:28:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43482 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229769AbjIUT2Y (ORCPT ); Thu, 21 Sep 2023 15:28:24 -0400 Received: from smtp-42ac.mail.infomaniak.ch (smtp-42ac.mail.infomaniak.ch [IPv6:2001:1600:4:17::42ac]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4C2171090F for ; Thu, 21 Sep 2023 10:07:30 -0700 (PDT) Received: from smtp-2-0001.mail.infomaniak.ch (unknown [10.5.36.108]) by smtp-3-3000.mail.infomaniak.ch (Postfix) with ESMTPS id 4RrlYC18FHzMqhBG; Thu, 21 Sep 2023 06:17:03 +0000 (UTC) Received: from unknown by smtp-2-0001.mail.infomaniak.ch (Postfix) with ESMTPA id 4RrlYB4Xq2zMppKP; Thu, 21 Sep 2023 08:17:02 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=digikod.net; s=20191114; t=1695277023; bh=tfluk0tM+VKusjLjyhKOpiGufsPkbpoIakP1eY7gXNs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kNfrZh6IDKZrDicB2+bARdYE6NtmcpduNn5qRqObGXEJru5ZIJQDWeYW2R7BGlgl7 f0s3As+HsvTImQURLGsLLzza6ouTcvPDM/dWFoI1eXF24ERqXtW6GNBOuIJxftRo4U BpJI1H8QL3dQ+Lr0nGxZbxCi4kd9hWSbWYBe4/mM= From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: Eric Paris , James Morris , Paul Moore , "Serge E . Hallyn" Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Ben Scarlato , =?utf-8?q?G=C3=BCnther_Noack?= , Jeff Xu , Jorge Lucangeli Obes , Konstantin Meskhidze , Shervin Oloumi , audit@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Subject: [RFC PATCH v1 5/7] landlock: Log file-related requests Date: Thu, 21 Sep 2023 08:16:39 +0200 Message-ID: <20230921061641.273654-6-mic@digikod.net> In-Reply-To: <20230921061641.273654-1-mic@digikod.net> References: <20230921061641.273654-1-mic@digikod.net> MIME-Version: 1.0 X-Infomaniak-Routing: alpha Precedence: bulk List-ID: Add audit support for mkdir, mknod, symlink, unlink, rmdir, truncate, and open requests. Signed-off-by: Mickaël Salaün --- security/landlock/audit.c | 114 ++++++++++++++++++++++++++++++++++++++ security/landlock/audit.h | 32 +++++++++++ security/landlock/fs.c | 62 ++++++++++++++++++--- 3 files changed, 199 insertions(+), 9 deletions(-) diff --git a/security/landlock/audit.c b/security/landlock/audit.c index d9589d07e126..148fc0fafef4 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -14,6 +14,25 @@ atomic64_t ruleset_and_domain_counter = ATOMIC64_INIT(0); +static const char *op_to_string(enum landlock_operation operation) +{ + const char *const desc[] = { + [0] = "", + [LANDLOCK_OP_MKDIR] = "mkdir", + [LANDLOCK_OP_MKNOD] = "mknod", + [LANDLOCK_OP_SYMLINK] = "symlink", + [LANDLOCK_OP_UNLINK] = "unlink", + [LANDLOCK_OP_RMDIR] = "rmdir", + [LANDLOCK_OP_TRUNCATE] = "truncate", + [LANDLOCK_OP_OPEN] = "open", + }; + + if (WARN_ON_ONCE(operation < 0 || operation > ARRAY_SIZE(desc))) + return "unknown"; + + return desc[operation]; +} + #define BIT_INDEX(bit) HWEIGHT(bit - 1) static void log_accesses(struct audit_buffer *const ab, @@ -141,3 +160,98 @@ void landlock_log_release_ruleset(const struct landlock_ruleset *const ruleset) audit_log_format(ab, "op=release-%s %s=%llu", name, name, id); audit_log_end(ab); } + +/* Update request.youngest_domain and request.missing_access */ +static void +update_request(struct landlock_request *const request, + const struct landlock_ruleset *const domain, + const access_mask_t access_request, + const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]) +{ + const unsigned long access_req = access_request; + unsigned long access_bit; + long youngest_denied_layer = -1; + const struct landlock_hierarchy *node = domain->hierarchy; + size_t i; + + WARN_ON_ONCE(request->youngest_domain); + WARN_ON_ONCE(request->missing_access); + + if (WARN_ON_ONCE(!access_request)) + return; + + if (WARN_ON_ONCE(!layer_masks)) + return; + + for_each_set_bit(access_bit, &access_req, ARRAY_SIZE(*layer_masks)) { + long domain_layer; + + if (!(*layer_masks)[access_bit]) + continue; + + domain_layer = __fls((*layer_masks)[access_bit]); + + /* + * Gets the access rights that are missing from + * the youngest (i.e. closest) domain. + */ + if (domain_layer == youngest_denied_layer) { + request->missing_access |= BIT_ULL(access_bit); + } else if (domain_layer > youngest_denied_layer) { + youngest_denied_layer = domain_layer; + request->missing_access = BIT_ULL(access_bit); + } + } + + WARN_ON_ONCE(!request->missing_access); + WARN_ON_ONCE(youngest_denied_layer < 0); + + /* Gets the nearest domain ID that denies request.missing_access */ + for (i = domain->num_layers - youngest_denied_layer - 1; i > 0; i--) + node = node->parent; + request->youngest_domain = node->id; +} + +static void +log_request(const int error, struct landlock_request *const request, + const struct landlock_ruleset *const domain, + const access_mask_t access_request, + const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]) +{ + struct audit_buffer *ab; + + if (WARN_ON_ONCE(!error)) + return; + if (WARN_ON_ONCE(!request)) + return; + if (WARN_ON_ONCE(!domain || !domain->hierarchy)) + return; + + /* Uses GFP_ATOMIC to not sleep. */ + ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN, + AUDIT_LANDLOCK); + if (!ab) + return; + + update_request(request, domain, access_request, layer_masks); + + log_task(ab); + audit_log_format(ab, " domain=%llu op=%s errno=%d missing-fs-accesses=", + request->youngest_domain, + op_to_string(request->operation), -error); + log_accesses(ab, request->missing_access); + audit_log_lsm_data(ab, &request->audit); + audit_log_end(ab); +} + +// TODO: Make it generic, not FS-centric. +int landlock_log_request( + const int error, struct landlock_request *const request, + const struct landlock_ruleset *const domain, + const access_mask_t access_request, + const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]) +{ + /* No need to log the access request, only the missing accesses. */ + log_request(error, request, domain, access_request, layer_masks); + return error; +} diff --git a/security/landlock/audit.h b/security/landlock/audit.h index bc17dc8ca6f1..8edc68b98fca 100644 --- a/security/landlock/audit.h +++ b/security/landlock/audit.h @@ -13,6 +13,23 @@ #include "ruleset.h" +enum landlock_operation { + LANDLOCK_OP_MKDIR = 1, + LANDLOCK_OP_MKNOD, + LANDLOCK_OP_SYMLINK, + LANDLOCK_OP_UNLINK, + LANDLOCK_OP_RMDIR, + LANDLOCK_OP_TRUNCATE, + LANDLOCK_OP_OPEN, +}; + +struct landlock_request { + const enum landlock_operation operation; + access_mask_t missing_access; + u64 youngest_domain; + struct common_audit_data audit; +}; + #ifdef CONFIG_AUDIT void landlock_log_create_ruleset(struct landlock_ruleset *const ruleset); @@ -20,6 +37,12 @@ void landlock_log_restrict_self(struct landlock_ruleset *const domain, struct landlock_ruleset *const ruleset); void landlock_log_release_ruleset(const struct landlock_ruleset *const ruleset); +int landlock_log_request( + const int error, struct landlock_request *const request, + const struct landlock_ruleset *const domain, + const access_mask_t access_request, + const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]); + #else /* CONFIG_AUDIT */ static inline void @@ -38,6 +61,15 @@ landlock_log_release_ruleset(const struct landlock_ruleset *const ruleset) { } +static inline int landlock_log_request( + const int error, struct landlock_request *const request, + const struct landlock_ruleset *const domain, + const access_mask_t access_request, + const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]) +{ + return error; +} + #endif /* CONFIG_AUDIT */ #endif /* _SECURITY_LANDLOCK_AUDIT_H */ diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 978e325d8708..104dfb2abc32 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #include #include +#include "audit.h" #include "common.h" #include "cred.h" #include "fs.h" @@ -636,7 +638,8 @@ static bool is_access_to_paths_allowed( } static int current_check_access_path(const struct path *const path, - access_mask_t access_request) + access_mask_t access_request, + struct landlock_request *const request) { const struct landlock_ruleset *const dom = landlock_get_current_domain(); @@ -650,7 +653,10 @@ static int current_check_access_path(const struct path *const path, NULL, 0, NULL, NULL)) return 0; - return -EACCES; + request->audit.type = LSM_AUDIT_DATA_PATH; + request->audit.u.path = *path; + return landlock_log_request(-EACCES, request, dom, access_request, + &layer_masks); } static inline access_mask_t get_mode_access(const umode_t mode) @@ -1097,6 +1103,7 @@ static int hook_path_link(struct dentry *const old_dentry, const struct path *const new_dir, struct dentry *const new_dentry) { + // TODO: Implement fine-grained audit return current_check_refer_path(old_dentry, new_dir, new_dentry, false, false); } @@ -1115,38 +1122,67 @@ static int hook_path_rename(const struct path *const old_dir, static int hook_path_mkdir(const struct path *const dir, struct dentry *const dentry, const umode_t mode) { - return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_DIR); + struct landlock_request request = { + .operation = LANDLOCK_OP_MKDIR, + }; + + return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_DIR, + &request); } static int hook_path_mknod(const struct path *const dir, struct dentry *const dentry, const umode_t mode, const unsigned int dev) { - return current_check_access_path(dir, get_mode_access(mode)); + struct landlock_request request = { + .operation = LANDLOCK_OP_MKNOD, + }; + + return current_check_access_path(dir, get_mode_access(mode), &request); } static int hook_path_symlink(const struct path *const dir, struct dentry *const dentry, const char *const old_name) { - return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_SYM); + struct landlock_request request = { + .operation = LANDLOCK_OP_SYMLINK, + }; + + return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_SYM, + &request); } static int hook_path_unlink(const struct path *const dir, struct dentry *const dentry) { - return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_FILE); + struct landlock_request request = { + .operation = LANDLOCK_OP_UNLINK, + }; + + return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_FILE, + &request); } static int hook_path_rmdir(const struct path *const dir, struct dentry *const dentry) { - return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_DIR); + struct landlock_request request = { + .operation = LANDLOCK_OP_RMDIR, + }; + + return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_DIR, + &request); } static int hook_path_truncate(const struct path *const path) { - return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE); + struct landlock_request request = { + .operation = LANDLOCK_OP_TRUNCATE, + }; + + return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE, + &request); } /* File hooks */ @@ -1199,6 +1235,13 @@ static int hook_file_open(struct file *const file) const access_mask_t optional_access = LANDLOCK_ACCESS_FS_TRUNCATE; const struct landlock_ruleset *const dom = landlock_get_current_domain(); + struct landlock_request request = { + .operation = LANDLOCK_OP_OPEN, + .audit = { + .type = LSM_AUDIT_DATA_PATH, + .u.path = file->f_path, + }, + }; if (!dom) return 0; @@ -1249,7 +1292,8 @@ static int hook_file_open(struct file *const file) if ((open_access_request & allowed_access) == open_access_request) return 0; - return -EACCES; + return landlock_log_request(-EACCES, &request, dom, open_access_request, + &layer_masks); } static int hook_file_truncate(struct file *const file) From patchwork Thu Sep 21 06:16:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 13394148 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 200D5E7D0A2 for ; Thu, 21 Sep 2023 17:24:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229597AbjIURYm (ORCPT ); Thu, 21 Sep 2023 13:24:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38524 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229595AbjIURYX (ORCPT ); Thu, 21 Sep 2023 13:24:23 -0400 Received: from smtp-8fa8.mail.infomaniak.ch (smtp-8fa8.mail.infomaniak.ch [83.166.143.168]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1D94B10906 for ; Thu, 21 Sep 2023 10:07:30 -0700 (PDT) Received: from smtp-2-0001.mail.infomaniak.ch (unknown [10.5.36.108]) by smtp-3-3000.mail.infomaniak.ch (Postfix) with ESMTPS id 4RrlYD10SYzMpp2X; Thu, 21 Sep 2023 06:17:04 +0000 (UTC) Received: from unknown by smtp-2-0001.mail.infomaniak.ch (Postfix) with ESMTPA id 4RrlYC4BjrzMppKR; Thu, 21 Sep 2023 08:17:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=digikod.net; s=20191114; t=1695277024; bh=aXxAzAZrVv/AQrM1hbU5eAstfAxS1hqSXWRkrvqIPdw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NIkVnGqhXST4ff8AXuEDBtYbO577M5gPCcl8ioUJLhFVZ/9MT+b5xSJWdc/Ejx95W 34dNrLXehRDfDf4p/H+ylzPrcZHOg03FmLJcrXkmVut5vV/xcwyn7vnt78ptkUEXtC 2pLnKDwNZSOXw6R9CHZ5lP7yTTxoAm0ndKQvkLGI= From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: Eric Paris , James Morris , Paul Moore , "Serge E . Hallyn" Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Ben Scarlato , =?utf-8?q?G=C3=BCnther_Noack?= , Jeff Xu , Jorge Lucangeli Obes , Konstantin Meskhidze , Shervin Oloumi , audit@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Subject: [RFC PATCH v1 6/7] landlock: Log mount-related requests Date: Thu, 21 Sep 2023 08:16:40 +0200 Message-ID: <20230921061641.273654-7-mic@digikod.net> In-Reply-To: <20230921061641.273654-1-mic@digikod.net> References: <20230921061641.273654-1-mic@digikod.net> MIME-Version: 1.0 X-Infomaniak-Routing: alpha Precedence: bulk List-ID: Add audit support for mount, move_mount, umount, remount, and pivot_root requests. Signed-off-by: Mickaël Salaün --- security/landlock/audit.c | 26 ++++++++++++- security/landlock/audit.h | 13 ++++++- security/landlock/fs.c | 82 ++++++++++++++++++++++++++++++++++----- 3 files changed, 109 insertions(+), 12 deletions(-) diff --git a/security/landlock/audit.c b/security/landlock/audit.c index 148fc0fafef4..89bd701d124f 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -18,6 +18,11 @@ static const char *op_to_string(enum landlock_operation operation) { const char *const desc[] = { [0] = "", + [LANDLOCK_OP_MOUNT] = "mount", + [LANDLOCK_OP_MOVE_MOUNT] = "move_mount", + [LANDLOCK_OP_UMOUNT] = "umount", + [LANDLOCK_OP_REMOUNT] = "remount", + [LANDLOCK_OP_PIVOT_ROOT] = "pivot_root", [LANDLOCK_OP_MKDIR] = "mkdir", [LANDLOCK_OP_MKNOD] = "mknod", [LANDLOCK_OP_SYMLINK] = "symlink", @@ -33,6 +38,20 @@ static const char *op_to_string(enum landlock_operation operation) return desc[operation]; } +static const char *perm_to_string(enum landlock_permission permission) +{ + const char *const desc[] = { + [0] = "", + [LANDLOCK_PERM_PTRACE] = "ptrace", + [LANDLOCK_PERM_FS_LAYOUT] = "fs_layout", + }; + + if (WARN_ON_ONCE(permission < 0 || permission > ARRAY_SIZE(desc))) + return "unknown"; + + return desc[permission]; +} + #define BIT_INDEX(bit) HWEIGHT(bit - 1) static void log_accesses(struct audit_buffer *const ab, @@ -177,8 +196,11 @@ update_request(struct landlock_request *const request, WARN_ON_ONCE(request->youngest_domain); WARN_ON_ONCE(request->missing_access); - if (WARN_ON_ONCE(!access_request)) + if (!access_request) { + /* No missing accesses. */ + request->youngest_domain = node->id; return; + } if (WARN_ON_ONCE(!layer_masks)) return; @@ -240,6 +262,8 @@ log_request(const int error, struct landlock_request *const request, request->youngest_domain, op_to_string(request->operation), -error); log_accesses(ab, request->missing_access); + audit_log_format(ab, " missing-permission=%s", + perm_to_string(request->missing_permission)); audit_log_lsm_data(ab, &request->audit); audit_log_end(ab); } diff --git a/security/landlock/audit.h b/security/landlock/audit.h index 8edc68b98fca..e559fb6a89dd 100644 --- a/security/landlock/audit.h +++ b/security/landlock/audit.h @@ -14,7 +14,12 @@ #include "ruleset.h" enum landlock_operation { - LANDLOCK_OP_MKDIR = 1, + LANDLOCK_OP_MOUNT = 1, + LANDLOCK_OP_MOVE_MOUNT, + LANDLOCK_OP_UMOUNT, + LANDLOCK_OP_REMOUNT, + LANDLOCK_OP_PIVOT_ROOT, + LANDLOCK_OP_MKDIR, LANDLOCK_OP_MKNOD, LANDLOCK_OP_SYMLINK, LANDLOCK_OP_UNLINK, @@ -23,8 +28,14 @@ enum landlock_operation { LANDLOCK_OP_OPEN, }; +enum landlock_permission { + LANDLOCK_PERM_PTRACE = 1, + LANDLOCK_PERM_FS_LAYOUT, +}; + struct landlock_request { const enum landlock_operation operation; + const enum landlock_permission missing_permission; access_mask_t missing_access; u64 youngest_domain; struct common_audit_data audit; diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 104dfb2abc32..8600530e304c 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -1050,17 +1050,41 @@ static int hook_sb_mount(const char *const dev_name, const struct path *const path, const char *const type, const unsigned long flags, void *const data) { - if (!landlock_get_current_domain()) + const struct landlock_ruleset *const dom = + landlock_get_current_domain(); + struct landlock_request request = { + .operation = LANDLOCK_OP_MOUNT, + .missing_permission = LANDLOCK_PERM_FS_LAYOUT, + .audit = { + .type = LSM_AUDIT_DATA_PATH, + .u.path = *path, + }, + }; + + if (!dom) return 0; - return -EPERM; + + return landlock_log_request(-EPERM, &request, dom, 0, NULL); } static int hook_move_mount(const struct path *const from_path, const struct path *const to_path) { - if (!landlock_get_current_domain()) + const struct landlock_ruleset *const dom = + landlock_get_current_domain(); + struct landlock_request request = { + .operation = LANDLOCK_OP_MOVE_MOUNT, + .missing_permission = LANDLOCK_PERM_FS_LAYOUT, + .audit = { + .type = LSM_AUDIT_DATA_PATH, + .u.path = *to_path, + }, + }; + + if (!dom) return 0; - return -EPERM; + + return landlock_log_request(-EPERM, &request, dom, 0, NULL); } /* @@ -1069,16 +1093,42 @@ static int hook_move_mount(const struct path *const from_path, */ static int hook_sb_umount(struct vfsmount *const mnt, const int flags) { - if (!landlock_get_current_domain()) + const struct landlock_ruleset *const dom = + landlock_get_current_domain(); + struct landlock_request request = { + .operation = LANDLOCK_OP_UMOUNT, + .missing_permission = LANDLOCK_PERM_FS_LAYOUT, + .audit = { + // TODO: try to print the mounted path + // cf. dentry_path() + .type = LSM_AUDIT_DATA_DENTRY, + .u.dentry = mnt->mnt_root, + }, + }; + + if (!dom) return 0; - return -EPERM; + + return landlock_log_request(-EPERM, &request, dom, 0, NULL); } static int hook_sb_remount(struct super_block *const sb, void *const mnt_opts) { - if (!landlock_get_current_domain()) + const struct landlock_ruleset *const dom = + landlock_get_current_domain(); + struct landlock_request request = { + .operation = LANDLOCK_OP_REMOUNT, + .missing_permission = LANDLOCK_PERM_FS_LAYOUT, + .audit = { + .type = LSM_AUDIT_DATA_DENTRY, + .u.dentry = sb->s_root, + }, + }; + + if (!dom) return 0; - return -EPERM; + + return landlock_log_request(-EPERM, &request, dom, 0, NULL); } /* @@ -1092,9 +1142,21 @@ static int hook_sb_remount(struct super_block *const sb, void *const mnt_opts) static int hook_sb_pivotroot(const struct path *const old_path, const struct path *const new_path) { - if (!landlock_get_current_domain()) + const struct landlock_ruleset *const dom = + landlock_get_current_domain(); + struct landlock_request request = { + .operation = LANDLOCK_OP_PIVOT_ROOT, + .missing_permission = LANDLOCK_PERM_FS_LAYOUT, + .audit = { + .type = LSM_AUDIT_DATA_PATH, + .u.path = *new_path, + }, + }; + + if (!dom) return 0; - return -EPERM; + + return landlock_log_request(-EPERM, &request, dom, 0, NULL); } /* Path hooks */ From patchwork Thu Sep 21 06:16:41 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 13394616 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 40B2AE7D0A2 for ; Thu, 21 Sep 2023 20:59:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231987AbjIUU7i (ORCPT ); Thu, 21 Sep 2023 16:59:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37572 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232230AbjIUU7F (ORCPT ); Thu, 21 Sep 2023 16:59:05 -0400 Received: from smtp-bc0b.mail.infomaniak.ch (smtp-bc0b.mail.infomaniak.ch [IPv6:2001:1600:3:17::bc0b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6AB9B12445 for ; Thu, 21 Sep 2023 10:06:53 -0700 (PDT) Received: from smtp-2-0001.mail.infomaniak.ch (unknown [10.5.36.108]) by smtp-2-3000.mail.infomaniak.ch (Postfix) with ESMTPS id 4RrlYF10fszMpnvZ; Thu, 21 Sep 2023 06:17:05 +0000 (UTC) Received: from unknown by smtp-2-0001.mail.infomaniak.ch (Postfix) with ESMTPA id 4RrlYD43PYzMppDY; Thu, 21 Sep 2023 08:17:04 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=digikod.net; s=20191114; t=1695277025; bh=cinTwRA+WcJIbifJHzOy8LJVvQYuI7/JZgdtZ57GtzM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=up0um0B1XHsyeQkuudhIT3U+5pTFTjDV0n7klWUvkP46xo+7VG6DPTYrZ+EB1ewzb pkHEEkMfvi0v0Vu+/YLb64C0x9i3a9Hm07og2eq4jJz/7pVdStNskZGiAewR1Py4Ye hPTIYrcmWLdYpcX3yAH37Zz0KL/duU1m/sFGabHE= From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: Eric Paris , James Morris , Paul Moore , "Serge E . Hallyn" Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Ben Scarlato , =?utf-8?q?G=C3=BCnther_Noack?= , Jeff Xu , Jorge Lucangeli Obes , Konstantin Meskhidze , Shervin Oloumi , audit@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Subject: [RFC PATCH v1 7/7] landlock: Log ptrace requests Date: Thu, 21 Sep 2023 08:16:41 +0200 Message-ID: <20230921061641.273654-8-mic@digikod.net> In-Reply-To: <20230921061641.273654-1-mic@digikod.net> References: <20230921061641.273654-1-mic@digikod.net> MIME-Version: 1.0 X-Infomaniak-Routing: alpha Precedence: bulk List-ID: Add audit support for ptrace and ptrace_traceme requests. Signed-off-by: Mickaël Salaün --- security/landlock/audit.c | 2 ++ security/landlock/audit.h | 4 +++- security/landlock/ptrace.c | 47 ++++++++++++++++++++++++++++++++++---- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/security/landlock/audit.c b/security/landlock/audit.c index 89bd701d124f..2ec2a00822d2 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -18,6 +18,8 @@ static const char *op_to_string(enum landlock_operation operation) { const char *const desc[] = { [0] = "", + [LANDLOCK_OP_PTRACE] = "ptrace", + [LANDLOCK_OP_PTRACE_TRACEME] = "ptrace_traceme", [LANDLOCK_OP_MOUNT] = "mount", [LANDLOCK_OP_MOVE_MOUNT] = "move_mount", [LANDLOCK_OP_UMOUNT] = "umount", diff --git a/security/landlock/audit.h b/security/landlock/audit.h index e559fb6a89dd..b69bba7b908c 100644 --- a/security/landlock/audit.h +++ b/security/landlock/audit.h @@ -14,7 +14,9 @@ #include "ruleset.h" enum landlock_operation { - LANDLOCK_OP_MOUNT = 1, + LANDLOCK_OP_PTRACE = 1, + LANDLOCK_OP_PTRACE_TRACEME, + LANDLOCK_OP_MOUNT, LANDLOCK_OP_MOVE_MOUNT, LANDLOCK_OP_UMOUNT, LANDLOCK_OP_REMOUNT, diff --git a/security/landlock/ptrace.c b/security/landlock/ptrace.c index 8a06d6c492bf..dbe219449a32 100644 --- a/security/landlock/ptrace.c +++ b/security/landlock/ptrace.c @@ -10,10 +10,12 @@ #include #include #include +#include #include #include #include +#include "audit.h" #include "common.h" #include "cred.h" #include "ptrace.h" @@ -64,11 +66,9 @@ static bool task_is_scoped(const struct task_struct *const parent, static int task_ptrace(const struct task_struct *const parent, const struct task_struct *const child) { - /* Quick return for non-landlocked tasks. */ - if (!landlocked(parent)) - return 0; if (task_is_scoped(parent, child)) return 0; + return -EPERM; } @@ -88,7 +88,26 @@ static int task_ptrace(const struct task_struct *const parent, static int hook_ptrace_access_check(struct task_struct *const child, const unsigned int mode) { - return task_ptrace(current, child); + const struct landlock_ruleset *const dom = + landlock_get_current_domain(); + struct landlock_request request = { + .operation = LANDLOCK_OP_PTRACE, + .missing_permission = LANDLOCK_PERM_PTRACE, + .audit = { + .type = LSM_AUDIT_DATA_TASK, + .u.tsk = child, + }, + }; + int err; + + if (!dom) + return 0; + + err = task_ptrace(current, child); + if (!err) + return 0; + + return landlock_log_request(err, &request, dom, 0, NULL); } /** @@ -105,7 +124,25 @@ static int hook_ptrace_access_check(struct task_struct *const child, */ static int hook_ptrace_traceme(struct task_struct *const parent) { - return task_ptrace(parent, current); + struct landlock_request request = { + .operation = LANDLOCK_OP_PTRACE_TRACEME, + .missing_permission = LANDLOCK_PERM_PTRACE, + .audit = { + .type = LSM_AUDIT_DATA_TASK, + .u.tsk = parent, + }, + }; + int err; + + if (!landlock_get_task_domain(parent)) + return 0; + + err = task_ptrace(parent, current); + if (!err) + return 0; + + return landlock_log_request(err, &request, + landlock_get_current_domain(), 0, NULL); } static struct security_hook_list landlock_hooks[] __ro_after_init = {