From patchwork Sat Oct 8 10:09:27 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=BCnther_Noack?= X-Patchwork-Id: 13001733 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 852BDC433F5 for ; Sat, 8 Oct 2022 10:09:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229591AbiJHKJr (ORCPT ); Sat, 8 Oct 2022 06:09:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50440 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229468AbiJHKJq (ORCPT ); Sat, 8 Oct 2022 06:09:46 -0400 Received: from mail-ed1-x52b.google.com (mail-ed1-x52b.google.com [IPv6:2a00:1450:4864:20::52b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4CC7C642E; Sat, 8 Oct 2022 03:09:42 -0700 (PDT) Received: by mail-ed1-x52b.google.com with SMTP id e18so9985428edj.3; Sat, 08 Oct 2022 03:09:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Cf/WV5pOF8a0ECxnWQLAe5ldbFVyN/FWX5XjeUMVcYc=; b=X1j30P/441SWmv/uHKs0fZH+Awm94eDhpVn45r6aUf5bzRVtS79ZM7WFc5IFPuNinZ 35JTUVzFSDGvdmtnbuVvECFK6o/f1d/4oTFCYvalfLIQLv0O/2o7Ghy0/NEYTL+ZzEM6 79JwA+6NYChdxg5xovwVqdVbazP6syk1rpyvpIokBYlTvXvakk1hMHc/0t7ErIBwXC8p sqeMTl5sNC97IyWOwiDMe0KG/wm0He+o4FM/ytMDTiAiBH62uMFKvKJKAZy8wgNCEIV+ +BZ5Qv9pEIhtuJKkHl5zqS+PFusOE+69y5jUFkuAyDbEwMNVeas97ZVcntr9oSJELuX3 Ejyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Cf/WV5pOF8a0ECxnWQLAe5ldbFVyN/FWX5XjeUMVcYc=; b=TyoZBZ+5RlxdR7k0v6LTvUwZly/L3Pbvagnr3L2aA2NB5hbpho+Civ4xTTUg7uVWZy KjTY6mAk5eCUvsLrDyPsLqDwKvQ4irVUFIZ09Dw/vD2dHQsNGCNVs6Rze4viw+DR/Kva kg5eNYIFZLEhIjykNXVwsNYuu5TmQ+2+exo42GiqDrgudTq9/vF4gjcU5dcAvid3bzqT iZh71ofXU1VnuhzGD0wdfMb2uIUfmj52GMVO5hIsAU62ZQom9PX8+mQpXA8HI9L4/UOp kv2zOw0PSP0UsJRN7fllhIbvFbixk+MAGc3cJynkrNQCC2gTECZIwjBHVUMDGq0WyGDD x1Eg== X-Gm-Message-State: ACrzQf23IlBZpxEVNK79RUIEk8X89UX3U7ZQ3m85B3KdV8Cmg/yq/qS+ 7pMTmYUGb1gLifHi4XpzMkffPyf94Bw= X-Google-Smtp-Source: AMsMyM6U4agXLuvpgnc22ypflJlO/XtZy7US3CBA3zq17VSd3x8YcT81HAeC+UwFzpsw6q72+y2P7Q== X-Received: by 2002:a05:6402:3591:b0:451:8397:3e9 with SMTP id y17-20020a056402359100b00451839703e9mr8237821edc.409.1665223780546; Sat, 08 Oct 2022 03:09:40 -0700 (PDT) Received: from nuc.i.gnoack.org ([2a02:168:633b:1:1e69:7aff:fe05:97e6]) by smtp.gmail.com with ESMTPSA id e9-20020aa7d7c9000000b00452878cba5bsm3092012eds.97.2022.10.08.03.09.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Oct 2022 03:09:40 -0700 (PDT) From: =?utf-8?q?G=C3=BCnther_Noack?= To: linux-security-module@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , James Morris , Paul Moore , "Serge E . Hallyn" , linux-fsdevel@vger.kernel.org, Konstantin Meskhidze , Nathan Chancellor , =?utf-8?q?G=C3=BCnther_Noack?= , Tetsuo Handa , John Johansen Subject: [PATCH v9 01/11] security: Create file_truncate hook from path_truncate hook Date: Sat, 8 Oct 2022 12:09:27 +0200 Message-Id: <20221008100935.73706-2-gnoack3000@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221008100935.73706-1-gnoack3000@gmail.com> References: <20221008100935.73706-1-gnoack3000@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: Like path_truncate, the file_truncate hook also restricts file truncation, but is called in the cases where truncation is attempted on an already-opened file. This is required in a subsequent commit to handle ftruncate() operations differently to truncate() operations. Acked-by: Tetsuo Handa Acked-by: John Johansen Acked-by: Paul Moore Signed-off-by: Günther Noack --- fs/namei.c | 2 +- fs/open.c | 2 +- include/linux/lsm_hook_defs.h | 1 + include/linux/lsm_hooks.h | 10 +++++++++- include/linux/security.h | 6 ++++++ security/apparmor/lsm.c | 6 ++++++ security/security.c | 5 +++++ security/tomoyo/tomoyo.c | 13 +++++++++++++ 8 files changed, 42 insertions(+), 3 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 8533087e5dac..776da6043d89 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3211,7 +3211,7 @@ static int handle_truncate(struct user_namespace *mnt_userns, struct file *filp) if (error) return error; - error = security_path_truncate(path); + error = security_file_truncate(filp); if (!error) { error = do_truncate(mnt_userns, path->dentry, 0, ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, diff --git a/fs/open.c b/fs/open.c index a81319b6177f..c92f76ab341a 100644 --- a/fs/open.c +++ b/fs/open.c @@ -188,7 +188,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small) if (IS_APPEND(file_inode(f.file))) goto out_putf; sb_start_write(inode->i_sb); - error = security_path_truncate(&f.file->f_path); + error = security_file_truncate(f.file); if (!error) error = do_truncate(file_mnt_user_ns(f.file), dentry, length, ATTR_MTIME | ATTR_CTIME, f.file); diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index ec119da1d89b..f67025823d92 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -177,6 +177,7 @@ LSM_HOOK(int, 0, file_send_sigiotask, struct task_struct *tsk, struct fown_struct *fown, int sig) LSM_HOOK(int, 0, file_receive, struct file *file) LSM_HOOK(int, 0, file_open, struct file *file) +LSM_HOOK(int, 0, file_truncate, struct file *file) LSM_HOOK(int, 0, task_alloc, struct task_struct *task, unsigned long clone_flags) LSM_HOOK(void, LSM_RET_VOID, task_free, struct task_struct *task) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 4ec80b96c22e..fad93a6d5293 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -409,7 +409,9 @@ * @attr is the iattr structure containing the new file attributes. * Return 0 if permission is granted. * @path_truncate: - * Check permission before truncating a file. + * Check permission before truncating the file indicated by path. + * Note that truncation permissions may also be checked based on + * already opened files, using the @file_truncate hook. * @path contains the path structure for the file. * Return 0 if permission is granted. * @inode_getattr: @@ -598,6 +600,12 @@ * to receive an open file descriptor via socket IPC. * @file contains the file structure being received. * Return 0 if permission is granted. + * @file_truncate: + * Check permission before truncating a file, i.e. using ftruncate. + * Note that truncation permission may also be checked based on the path, + * using the @path_truncate hook. + * @file contains the file structure for the file. + * Return 0 if permission is granted. * @file_open: * Save open-time permission checking state for later use upon * file_permission, and recheck access if anything has changed diff --git a/include/linux/security.h b/include/linux/security.h index 87fac3af6dad..8ef7263ae457 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -394,6 +394,7 @@ int security_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int sig); int security_file_receive(struct file *file); int security_file_open(struct file *file); +int security_file_truncate(struct file *file); int security_task_alloc(struct task_struct *task, unsigned long clone_flags); void security_task_free(struct task_struct *task); int security_cred_alloc_blank(struct cred *cred, gfp_t gfp); @@ -1012,6 +1013,11 @@ static inline int security_file_open(struct file *file) return 0; } +static inline int security_file_truncate(struct file *file) +{ + return 0; +} + static inline int security_task_alloc(struct task_struct *task, unsigned long clone_flags) { diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f56070270c69..be31549cfb40 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -329,6 +329,11 @@ static int apparmor_path_truncate(const struct path *path) return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_SETATTR); } +static int apparmor_file_truncate(struct file *file) +{ + return apparmor_path_truncate(&file->f_path); +} + static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry, const char *old_name) { @@ -1232,6 +1237,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(mmap_file, apparmor_mmap_file), LSM_HOOK_INIT(file_mprotect, apparmor_file_mprotect), LSM_HOOK_INIT(file_lock, apparmor_file_lock), + LSM_HOOK_INIT(file_truncate, apparmor_file_truncate), LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), diff --git a/security/security.c b/security/security.c index 8312b3bf1169..6794c770a262 100644 --- a/security/security.c +++ b/security/security.c @@ -1650,6 +1650,11 @@ int security_file_open(struct file *file) return fsnotify_perm(file, MAY_OPEN); } +int security_file_truncate(struct file *file) +{ + return call_int_hook(file_truncate, 0, file); +} + int security_task_alloc(struct task_struct *task, unsigned long clone_flags) { int rc = lsm_task_alloc(task); diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 71e82d855ebf..af04a7b7eb28 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -134,6 +134,18 @@ static int tomoyo_path_truncate(const struct path *path) return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path, NULL); } +/** + * tomoyo_file_truncate - Target for security_file_truncate(). + * + * @file: Pointer to "struct file". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_file_truncate(struct file *file) +{ + return tomoyo_path_truncate(&file->f_path); +} + /** * tomoyo_path_unlink - Target for security_path_unlink(). * @@ -545,6 +557,7 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(bprm_check_security, tomoyo_bprm_check_security), LSM_HOOK_INIT(file_fcntl, tomoyo_file_fcntl), LSM_HOOK_INIT(file_open, tomoyo_file_open), + LSM_HOOK_INIT(file_truncate, tomoyo_file_truncate), LSM_HOOK_INIT(path_truncate, tomoyo_path_truncate), LSM_HOOK_INIT(path_unlink, tomoyo_path_unlink), LSM_HOOK_INIT(path_mkdir, tomoyo_path_mkdir), From patchwork Sat Oct 8 10:09:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=BCnther_Noack?= X-Patchwork-Id: 13001732 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 F2B9CC4332F for ; Sat, 8 Oct 2022 10:09:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229468AbiJHKJs (ORCPT ); Sat, 8 Oct 2022 06:09:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50448 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229472AbiJHKJq (ORCPT ); Sat, 8 Oct 2022 06:09:46 -0400 Received: from mail-ej1-x636.google.com (mail-ej1-x636.google.com [IPv6:2a00:1450:4864:20::636]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B5512F67; Sat, 8 Oct 2022 03:09:42 -0700 (PDT) Received: by mail-ej1-x636.google.com with SMTP id bj12so15840018ejb.13; Sat, 08 Oct 2022 03:09:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=4FuCoIWD2t78PwWblywGhp3stRwE/o0mS9jcK6KpYrk=; b=HQinCFyD7FAy1a1BwqInnsIPGuJ+HVpiCSmQo/sgcHmjwPizttRFLZBhf7FCl0v9JL uCzEB4mqBtzB07o41bymoCUZl9uYstkLy7g7OgsUcLLVzRnI9k7VvWiQ1rOL1AcziHp4 OhUyJt8CamwIxs+VnRZLqUceY7hZ+yjFj7VyIWhOfHpzWLN4kFFdNJapAUkEPgiPI8/9 h8dJ8wAXAUaxM1zimBXlv7hlEM9JshguFiIXLRBkU0sgU0OFR9Ajpk1Is6jnLwlFDYcd 10c7OW3rGi2xoNntx8vY3K4IXLmBzecdB+YyUBG7IFxpeIIZqDh4/B4CAwSivxdZve2Y ZyEA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=4FuCoIWD2t78PwWblywGhp3stRwE/o0mS9jcK6KpYrk=; b=tZsRiWFPynxi5J/Zn0F4NqQtVZlA8xz4TqXpj+oYY4Q2NR/TLWBeKPbvTD3y0OWllg FlI7uSP37F76It/V0vwOXpT4l6SFc1VNVyYyFUlCm6POOp7LGWMdNEX7GuEzkSWDNHu3 ayk7L2/oPG+5PoGj1480T8Vj6KqP7G+cNuvyX6jGzFAkji5b6JMxruCdMsYGTrk4S626 OSw2ElgJ2k0TjamKdgC8MRVsYNnkJHmHTJf+9F//lAqxEQ2g5HIVHFydql0Tt8uoDKlx V/lsculscM6isd0cJUqgIpsPQczP7HOGNr2voh6QHU4yIkZcVoST5wdE7p2wA/8iP6wZ 6u6w== X-Gm-Message-State: ACrzQf2Bc1oHzbF/DoLqEgeCOq4ObN5Ni2UtVs22QoAZ+lrvH7TfRk0x nbvF4I6KUjFedj4KgJ4X4iSU0zdSCaw= X-Google-Smtp-Source: AMsMyM4Uvyo7nla/F0VVsDLXadaxvRoiP6LtalTj3yv2zAaU7e116jqWteVmu2xIP9pcVwS/JoW99w== X-Received: by 2002:a17:907:3e11:b0:78d:9918:217f with SMTP id hp17-20020a1709073e1100b0078d9918217fmr1270586ejc.742.1665223781249; Sat, 08 Oct 2022 03:09:41 -0700 (PDT) Received: from nuc.i.gnoack.org ([2a02:168:633b:1:1e69:7aff:fe05:97e6]) by smtp.gmail.com with ESMTPSA id e9-20020aa7d7c9000000b00452878cba5bsm3092012eds.97.2022.10.08.03.09.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Oct 2022 03:09:40 -0700 (PDT) From: =?utf-8?q?G=C3=BCnther_Noack?= To: linux-security-module@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , James Morris , Paul Moore , "Serge E . Hallyn" , linux-fsdevel@vger.kernel.org, Konstantin Meskhidze , Nathan Chancellor , =?utf-8?q?G=C3=BCnther_Noack?= Subject: [PATCH v9 02/11] landlock: Refactor check_access_path_dual() into is_access_to_paths_allowed() Date: Sat, 8 Oct 2022 12:09:28 +0200 Message-Id: <20221008100935.73706-3-gnoack3000@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221008100935.73706-1-gnoack3000@gmail.com> References: <20221008100935.73706-1-gnoack3000@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: Rename check_access_path_dual() to is_access_to_paths_allowed(). Make it return true iff the access is allowed. Calculate the EXDEV/EACCES error code in the one place where it's needed. Suggested-by: Mickaël Salaün Signed-off-by: Günther Noack --- security/landlock/fs.c | 89 +++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 64ed7665455f..277868e3c6ce 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -430,7 +430,7 @@ is_eacces(const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS], } /** - * check_access_path_dual - Check accesses for requests with a common path + * is_access_to_paths_allowed - Check accesses for requests with a common path * * @domain: Domain to check against. * @path: File hierarchy to walk through. @@ -465,14 +465,10 @@ is_eacces(const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS], * allow the request. * * Returns: - * - 0 if the access request is granted; - * - -EACCES if it is denied because of access right other than - * LANDLOCK_ACCESS_FS_REFER; - * - -EXDEV if the renaming or linking would be a privileged escalation - * (according to each layered policies), or if LANDLOCK_ACCESS_FS_REFER is - * not allowed by the source or the destination. + * - true if the access request is granted; + * - false otherwise. */ -static int check_access_path_dual( +static bool is_access_to_paths_allowed( const struct landlock_ruleset *const domain, const struct path *const path, const access_mask_t access_request_parent1, @@ -492,17 +488,17 @@ static int check_access_path_dual( (*layer_masks_child2)[LANDLOCK_NUM_ACCESS_FS] = NULL; if (!access_request_parent1 && !access_request_parent2) - return 0; + return true; if (WARN_ON_ONCE(!domain || !path)) - return 0; + return true; if (is_nouser_or_private(path->dentry)) - return 0; + return true; if (WARN_ON_ONCE(domain->num_layers < 1 || !layer_masks_parent1)) - return -EACCES; + return false; if (unlikely(layer_masks_parent2)) { if (WARN_ON_ONCE(!dentry_child1)) - return -EACCES; + return false; /* * For a double request, first check for potential privilege * escalation by looking at domain handled accesses (which are @@ -513,7 +509,7 @@ static int check_access_path_dual( is_dom_check = true; } else { if (WARN_ON_ONCE(dentry_child1 || dentry_child2)) - return -EACCES; + return false; /* For a simple request, only check for requested accesses. */ access_masked_parent1 = access_request_parent1; access_masked_parent2 = access_request_parent2; @@ -622,24 +618,7 @@ static int check_access_path_dual( } path_put(&walker_path); - if (allowed_parent1 && allowed_parent2) - return 0; - - /* - * This prioritizes EACCES over EXDEV for all actions, including - * renames with RENAME_EXCHANGE. - */ - if (likely(is_eacces(layer_masks_parent1, access_request_parent1) || - is_eacces(layer_masks_parent2, access_request_parent2))) - return -EACCES; - - /* - * Gracefully forbids reparenting if the destination directory - * hierarchy is not a superset of restrictions of the source directory - * hierarchy, or if LANDLOCK_ACCESS_FS_REFER is not allowed by the - * source or the destination. - */ - return -EXDEV; + return allowed_parent1 && allowed_parent2; } static inline int check_access_path(const struct landlock_ruleset *const domain, @@ -649,8 +628,10 @@ static inline int check_access_path(const struct landlock_ruleset *const domain, layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; access_request = init_layer_masks(domain, access_request, &layer_masks); - return check_access_path_dual(domain, path, access_request, - &layer_masks, NULL, 0, NULL, NULL); + 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, @@ -711,8 +692,9 @@ static inline access_mask_t maybe_remove(const struct dentry *const dentry) * file. While walking from @dir to @mnt_root, we record all the domain's * allowed accesses in @layer_masks_dom. * - * This is similar to check_access_path_dual() but much simpler because it only - * handles walking on the same mount point and only checks one set of accesses. + * This is similar to is_access_to_paths_allowed() but much simpler because it + * only handles walking on the same mount point and only checks one set of + * accesses. * * Returns: * - true if all the domain access rights are allowed for @dir; @@ -857,10 +839,11 @@ static int current_check_refer_path(struct dentry *const old_dentry, access_request_parent1 = init_layer_masks( dom, access_request_parent1 | access_request_parent2, &layer_masks_parent1); - return check_access_path_dual(dom, new_dir, - access_request_parent1, - &layer_masks_parent1, NULL, 0, - NULL, NULL); + if (is_access_to_paths_allowed( + dom, new_dir, access_request_parent1, + &layer_masks_parent1, NULL, 0, NULL, NULL)) + return 0; + return -EACCES; } access_request_parent1 |= LANDLOCK_ACCESS_FS_REFER; @@ -886,11 +869,27 @@ static int current_check_refer_path(struct dentry *const old_dentry, * parent access rights. This will be useful to compare with the * destination parent access rights. */ - return check_access_path_dual(dom, &mnt_dir, access_request_parent1, - &layer_masks_parent1, old_dentry, - access_request_parent2, - &layer_masks_parent2, - exchange ? new_dentry : NULL); + if (is_access_to_paths_allowed( + dom, &mnt_dir, access_request_parent1, &layer_masks_parent1, + old_dentry, access_request_parent2, &layer_masks_parent2, + exchange ? new_dentry : NULL)) + return 0; + + /* + * This prioritizes EACCES over EXDEV for all actions, including + * renames with RENAME_EXCHANGE. + */ + if (likely(is_eacces(&layer_masks_parent1, access_request_parent1) || + is_eacces(&layer_masks_parent2, access_request_parent2))) + return -EACCES; + + /* + * Gracefully forbids reparenting if the destination directory + * hierarchy is not a superset of restrictions of the source directory + * hierarchy, or if LANDLOCK_ACCESS_FS_REFER is not allowed by the + * source or the destination. + */ + return -EXDEV; } /* Inode hooks */ From patchwork Sat Oct 8 10:09:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=BCnther_Noack?= X-Patchwork-Id: 13001734 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 33B81C4321E for ; Sat, 8 Oct 2022 10:09:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229650AbiJHKJs (ORCPT ); Sat, 8 Oct 2022 06:09:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50454 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229508AbiJHKJq (ORCPT ); Sat, 8 Oct 2022 06:09:46 -0400 Received: from mail-ej1-x62c.google.com (mail-ej1-x62c.google.com [IPv6:2a00:1450:4864:20::62c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7103FA19B; Sat, 8 Oct 2022 03:09:43 -0700 (PDT) Received: by mail-ej1-x62c.google.com with SMTP id q9so11388911ejd.0; Sat, 08 Oct 2022 03:09:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=LUBbOEFW53KqYnQJv0OgmvKNjQw4g+0MWXRaawv7+iQ=; b=jRLnIgZeJgeAIfPETbUfVElQeRfUwoEc8v1pTrgHC0xgw7C6ONQ+FJFE0n2d9uoeV0 E/OgcdtCGBRK/5EBVsnTeARRNCK7pO3CIEjGACj3eAVjiuh6blJ9zV5JLHqlcIusUUgO hEJLYl0uN8grRRbaxmiCxJ822afnNJomqfb/QufkWjeIHOuoGkb6Qm/XSwPMKhS3J6++ 3tAvUqn5pU6P07c9UJr/4QUV3J6mX83OUmsfwnRgVfQuJo9io0KpSvnn0pNgB2d3bTP3 1gwgc5MGKX3Mkg681S6aVWsMRfaSfh67JPPpKwdwgqCvOCkXwIqgzQJ5szc87j7yQBPm 5Xyg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=LUBbOEFW53KqYnQJv0OgmvKNjQw4g+0MWXRaawv7+iQ=; b=fYUwA3z3NvgUruLgLV4mn1v1zXezm0ad/kUeL5e9wmBQjsMfB1tBjhWfzbXhMOF/4+ 3OPoelf6FqfQtlYo1r6jKDJ7lqgTImANrCYSM6h7zf0F3N4PUdx+JJNqbxZVtZKhu+2b UaTguhoDr9Ly020KTS4pB9117CdOVughgw8nVyY/I12Z/GO84SfVEed5knXHScbx0vPb waNs2Weu9ztvsVAtpxraHL7HSSrfsxT+/QMaElDp5k9XC3B4uvhlQZcJwYBDvdrydyNp 7FC78JPRzGg2T77971oLZbX5z1YBiZzmY+thTCtnCoSH+vmZnDBHyKt54Pxbvgov/rvV jiPg== X-Gm-Message-State: ACrzQf0tQiE+lZgex1ctYgj4qs0KhiS151Yru7uyxeUIkfvMO7k5w7sd N/v+B7MFsWoWkCZQI1syErJlv0Mr7qY= X-Google-Smtp-Source: AMsMyM4tMFLBbfK2cQ86s9DSUgDTx93BFsakbl9XwOeVAF4JC2TcZaInKndfbApsKi1wW6i3/gZQ3w== X-Received: by 2002:a17:906:cc18:b0:78d:8f26:706c with SMTP id ml24-20020a170906cc1800b0078d8f26706cmr3077141ejb.424.1665223781921; Sat, 08 Oct 2022 03:09:41 -0700 (PDT) Received: from nuc.i.gnoack.org ([2a02:168:633b:1:1e69:7aff:fe05:97e6]) by smtp.gmail.com with ESMTPSA id e9-20020aa7d7c9000000b00452878cba5bsm3092012eds.97.2022.10.08.03.09.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Oct 2022 03:09:41 -0700 (PDT) From: =?utf-8?q?G=C3=BCnther_Noack?= To: linux-security-module@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , James Morris , Paul Moore , "Serge E . Hallyn" , linux-fsdevel@vger.kernel.org, Konstantin Meskhidze , Nathan Chancellor , =?utf-8?q?G=C3=BCnther_Noack?= Subject: [PATCH v9 03/11] landlock: Document init_layer_masks() helper Date: Sat, 8 Oct 2022 12:09:29 +0200 Message-Id: <20221008100935.73706-4-gnoack3000@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221008100935.73706-1-gnoack3000@gmail.com> References: <20221008100935.73706-1-gnoack3000@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: Add kernel-doc to the init_layer_masks() function. Signed-off-by: Günther Noack --- security/landlock/fs.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 277868e3c6ce..87fde50eb550 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -297,6 +297,19 @@ get_handled_accesses(const struct landlock_ruleset *const domain) return access_dom & LANDLOCK_MASK_ACCESS_FS; } +/** + * init_layer_masks - Initialize layer masks from an access request + * + * Populates @layer_masks such that for each access right in @access_request, + * the bits for all the layers are set where this access right is handled. + * + * @domain: The domain that defines the current restrictions. + * @access_request: The requested access rights to check. + * @layer_masks: The layer masks to populate. + * + * Returns: An access mask where each access right bit is set which is handled + * in any of the active layers in @domain. + */ static inline access_mask_t init_layer_masks(const struct landlock_ruleset *const domain, const access_mask_t access_request, From patchwork Sat Oct 8 10:09:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=BCnther_Noack?= X-Patchwork-Id: 13001735 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 75D6AC4332F for ; Sat, 8 Oct 2022 10:09:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229651AbiJHKJu (ORCPT ); Sat, 8 Oct 2022 06:09:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50460 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229511AbiJHKJq (ORCPT ); Sat, 8 Oct 2022 06:09:46 -0400 Received: from mail-ej1-x62a.google.com (mail-ej1-x62a.google.com [IPv6:2a00:1450:4864:20::62a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 432C1A1A1; Sat, 8 Oct 2022 03:09:44 -0700 (PDT) Received: by mail-ej1-x62a.google.com with SMTP id 13so15947424ejn.3; Sat, 08 Oct 2022 03:09:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=aOA70iruPTRKJNEkVKejCgUmJP9zK8c7F96d7brXOss=; b=Th/dTgq8PIh9Mfb94Swc34B4Yd3Htm7ejs+M4dkLU9v2glwmps7pWYETz3xyKSqIAH DgukWDiU3bPn4paxOjaP7wI8LpnqPGLb2Zm4iEiIUVR1VGUevJfsQ3SJs+gG1HNUzLYa 3VjS3CvMKQtZ44JQfv2UWWNTk68ghLZzOLxkG8VQ6jWpwXm08bmoJt7K6fv/XaHREB3C a2iFhJY61jKfmbA1ykvMglpB4x0tJa3X50JOdo2jyk/yxAg1y6M9C5OBDgZ11CwHGSAI t5CvYBjIDArIymU/vm7igW2jP57R4itR2kUO6rjucvNSy85fKtZWFVJ79TQB7g9sbcst WzKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=aOA70iruPTRKJNEkVKejCgUmJP9zK8c7F96d7brXOss=; b=UrZgipI9PdvWifC7TFgOboTHJhV7vjO19ED43wZ93PqNkkrk8oJ+x7iEwb5OYtfLwA hXVYgb7bd/gBBqUF5PM21m72LYtZ0ycYEh/WbkBOMlTST1RV039KgBx/CFyDpErrt/GT RH2ECJSJfnSYO1l+HJiAZ7uUv57szSdmr+HB8TIUxbpzllB3TmcJsUBTcQvDU8v36rlH yJMgs4COp1PgWfzN3TUoGhGKXcGjGKh5h4biIOz9HxnXQDbdcZkvS4vdpj4SPaUK6udb 96KzNwS+75zBnxhMRle37AB4NRtt+5vHxgnERtOeOidRUuoIaRhAwNl0w2p81lTOwxAK HJJg== X-Gm-Message-State: ACrzQf1LjUihxoA9qr9lBBhD92b0dWTaV1pI/gac0zk3UTkxpuj/k0bh RZ8dp7wuy6HFdN4aQ2jTVlrzsIh4gAU= X-Google-Smtp-Source: AMsMyM58Lix+yrU75LfzSkOZorhJ6E3akbJSYR4+im05tcH6v2cy33i0fYa6qJLo9K3KXKQvtSulhw== X-Received: by 2002:a17:907:75f1:b0:78a:f935:647d with SMTP id jz17-20020a17090775f100b0078af935647dmr7126328ejc.587.1665223782719; Sat, 08 Oct 2022 03:09:42 -0700 (PDT) Received: from nuc.i.gnoack.org ([2a02:168:633b:1:1e69:7aff:fe05:97e6]) by smtp.gmail.com with ESMTPSA id e9-20020aa7d7c9000000b00452878cba5bsm3092012eds.97.2022.10.08.03.09.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Oct 2022 03:09:42 -0700 (PDT) From: =?utf-8?q?G=C3=BCnther_Noack?= To: linux-security-module@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , James Morris , Paul Moore , "Serge E . Hallyn" , linux-fsdevel@vger.kernel.org, Konstantin Meskhidze , Nathan Chancellor , =?utf-8?q?G=C3=BCnther_Noack?= Subject: [PATCH v9 04/11] landlock: Support file truncation Date: Sat, 8 Oct 2022 12:09:30 +0200 Message-Id: <20221008100935.73706-5-gnoack3000@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221008100935.73706-1-gnoack3000@gmail.com> References: <20221008100935.73706-1-gnoack3000@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: Introduce the LANDLOCK_ACCESS_FS_TRUNCATE flag for file truncation. This flag hooks into the path_truncate, file_truncate and file_alloc_security LSM hooks and covers file truncation using truncate(2), ftruncate(2), open(2) with O_TRUNC, as well as creat(). This change also increments the Landlock ABI version, updates corresponding selftests, and updates code documentation to document the flag. The following operations are restricted: open(2): requires the LANDLOCK_ACCESS_FS_TRUNCATE right if a file gets implicitly truncated as part of the open() (e.g. using O_TRUNC). Notable special cases: * open(..., O_RDONLY|O_TRUNC) can truncate files as well in Linux * open() with O_TRUNC does *not* need the TRUNCATE right when it creates a new file. truncate(2) (on a path): requires the LANDLOCK_ACCESS_FS_TRUNCATE right. ftruncate(2) (on a file): requires that the file had the TRUNCATE right when it was previously opened. File descriptors acquired by other means than open(2) (e.g. memfd_create(2)) continue to support truncation with ftruncate(2). Signed-off-by: Günther Noack --- include/uapi/linux/landlock.h | 21 +++- security/landlock/fs.c | 102 ++++++++++++++++++- security/landlock/fs.h | 24 +++++ security/landlock/limits.h | 2 +- security/landlock/setup.c | 1 + security/landlock/syscalls.c | 2 +- tools/testing/selftests/landlock/base_test.c | 2 +- tools/testing/selftests/landlock/fs_test.c | 7 +- 8 files changed, 145 insertions(+), 16 deletions(-) diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index 9c4bcc37a455..5e9d36329918 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -95,8 +95,19 @@ struct landlock_path_beneath_attr { * A file can only receive these access rights: * * - %LANDLOCK_ACCESS_FS_EXECUTE: Execute a file. - * - %LANDLOCK_ACCESS_FS_WRITE_FILE: Open a file with write access. + * - %LANDLOCK_ACCESS_FS_WRITE_FILE: Open a file with write access. Note that + * you might additionally need the %LANDLOCK_ACCESS_FS_TRUNCATE right in order + * to overwrite files with :manpage:`open(2)` using %O_TRUNC or + * :manpage:`creat(2)`. * - %LANDLOCK_ACCESS_FS_READ_FILE: Open a file with read access. + * - %LANDLOCK_ACCESS_FS_TRUNCATE: Truncate a file with :manpage:`truncate(2)`, + * :manpage:`ftruncate(2)`, :manpage:`creat(2)`, or :manpage:`open(2)` with + * %O_TRUNC. Whether an opened file can be truncated with + * :manpage:`ftruncate(2)` is determined during :manpage:`open(2)`, in the + * same way as read and write permissions are checked during + * :manpage:`open(2)` using %LANDLOCK_ACCESS_FS_READ_FILE and + * %LANDLOCK_ACCESS_FS_WRITE_FILE. This access right is available since the + * third version of the Landlock ABI. * * A directory can receive access rights related to files or directories. The * following access right is applied to the directory itself, and the @@ -139,10 +150,9 @@ struct landlock_path_beneath_attr { * * It is currently not possible to restrict some file-related actions * accessible through these syscall families: :manpage:`chdir(2)`, - * :manpage:`truncate(2)`, :manpage:`stat(2)`, :manpage:`flock(2)`, - * :manpage:`chmod(2)`, :manpage:`chown(2)`, :manpage:`setxattr(2)`, - * :manpage:`utime(2)`, :manpage:`ioctl(2)`, :manpage:`fcntl(2)`, - * :manpage:`access(2)`. + * :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`chmod(2)`, + * :manpage:`chown(2)`, :manpage:`setxattr(2)`, :manpage:`utime(2)`, + * :manpage:`ioctl(2)`, :manpage:`fcntl(2)`, :manpage:`access(2)`. * Future Landlock evolutions will enable to restrict them. */ /* clang-format off */ @@ -160,6 +170,7 @@ struct landlock_path_beneath_attr { #define LANDLOCK_ACCESS_FS_MAKE_BLOCK (1ULL << 11) #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12) #define LANDLOCK_ACCESS_FS_REFER (1ULL << 13) +#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14) /* clang-format on */ #endif /* _UAPI_LINUX_LANDLOCK_H */ diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 87fde50eb550..9fb052c0140c 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -146,7 +146,8 @@ static struct landlock_object *get_inode_object(struct inode *const inode) #define ACCESS_FILE ( \ LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \ - LANDLOCK_ACCESS_FS_READ_FILE) + LANDLOCK_ACCESS_FS_READ_FILE | \ + LANDLOCK_ACCESS_FS_TRUNCATE) /* clang-format on */ /* @@ -1154,9 +1155,21 @@ static int hook_path_rmdir(const struct path *const dir, return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_DIR); } +static int hook_path_truncate(const struct path *const path) +{ + return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE); +} + /* File hooks */ -static inline access_mask_t get_file_access(const struct file *const file) +/** + * get_required_file_open_access - Get rights needed to open a file + * + * Returns the access rights that are required for opening the given file, + * depending on the file type and open mode. + */ +static inline access_mask_t +get_required_file_open_access(const struct file *const file) { access_mask_t access = 0; @@ -1174,19 +1187,95 @@ static inline access_mask_t get_file_access(const struct file *const file) return access; } +static int hook_file_alloc_security(struct file *const file) +{ + /* + * Grants all access rights, even if most of them are not + * checked later on. It is more consistent. + * + * Notably, file descriptors for regular files can also be acquired + * without going through the file_open hook, for example when using + * memfd_create(2). + */ + landlock_file(file)->allowed_access = LANDLOCK_MASK_ACCESS_FS; + return 0; +} + static int hook_file_open(struct file *const file) { + layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; + access_mask_t open_access_request, full_access_request, allowed_access; + const access_mask_t optional_access = LANDLOCK_ACCESS_FS_TRUNCATE; const struct landlock_ruleset *const dom = landlock_get_current_domain(); if (!dom) return 0; + /* - * Because a file may be opened with O_PATH, get_file_access() may - * return 0. This case will be handled with a future Landlock + * Because a file may be opened with O_PATH, get_required_file_open_access() + * may return 0. This case will be handled with a future Landlock * evolution. */ - return check_access_path(dom, &file->f_path, get_file_access(file)); + open_access_request = get_required_file_open_access(file); + + /* + * We look up more access than what we immediately need for open(), so + * that we can later authorize operations on opened files. + */ + full_access_request = open_access_request | optional_access; + + if (is_access_to_paths_allowed( + dom, &file->f_path, + init_layer_masks(dom, full_access_request, &layer_masks), + &layer_masks, NULL, 0, NULL, NULL)) { + allowed_access = full_access_request; + } else { + unsigned long access_bit; + const unsigned long access_req = full_access_request; + + /* + * Calculate the actual allowed access rights from layer_masks. + * Add each access right to allowed_access which has not been + * vetoed by any layer. + */ + allowed_access = 0; + for_each_set_bit(access_bit, &access_req, + ARRAY_SIZE(layer_masks)) { + if (!layer_masks[access_bit]) + allowed_access |= BIT_ULL(access_bit); + } + } + + /* + * For operations on already opened files (i.e. ftruncate()), it is the + * access rights at the time of open() which decide whether the + * operation is permitted. Therefore, we record the relevant subset of + * file access rights in the opened struct file. + */ + landlock_file(file)->allowed_access = allowed_access; + + if ((open_access_request & allowed_access) == open_access_request) + return 0; + + return -EACCES; +} + +static int hook_file_truncate(struct file *const file) +{ + /* + * Allows truncation if the truncate right was available at the time of + * opening the file, to get a consistent access check as for read, write + * and execute operations. + * + * Note: For checks done based on the file's Landlock allowed access, we + * enforce them independently of whether the current thread is in a + * Landlock domain, so that open files passed between independent + * processes retain their behaviour. + */ + if (landlock_file(file)->allowed_access & LANDLOCK_ACCESS_FS_TRUNCATE) + return 0; + return -EACCES; } static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { @@ -1206,8 +1295,11 @@ static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(path_symlink, hook_path_symlink), LSM_HOOK_INIT(path_unlink, hook_path_unlink), LSM_HOOK_INIT(path_rmdir, hook_path_rmdir), + LSM_HOOK_INIT(path_truncate, hook_path_truncate), + LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security), LSM_HOOK_INIT(file_open, hook_file_open), + LSM_HOOK_INIT(file_truncate, hook_file_truncate), }; __init void landlock_add_fs_hooks(void) diff --git a/security/landlock/fs.h b/security/landlock/fs.h index 8db7acf9109b..488e4813680a 100644 --- a/security/landlock/fs.h +++ b/security/landlock/fs.h @@ -36,6 +36,24 @@ struct landlock_inode_security { struct landlock_object __rcu *object; }; +/** + * struct landlock_file_security - File security blob + * + * This information is populated when opening a file in hook_file_open, and + * tracks the relevant Landlock access rights that were available at the time + * of opening the file. Other LSM hooks use these rights in order to authorize + * operations on already opened files. + */ +struct landlock_file_security { + /** + * @allowed_access: Access rights that were available at the time of + * opening the file. This is not necessarily the full set of access + * rights available at that time, but it's the necessary subset as + * needed to authorize later operations on the open file. + */ + access_mask_t allowed_access; +}; + /** * struct landlock_superblock_security - Superblock security blob * @@ -50,6 +68,12 @@ struct landlock_superblock_security { atomic_long_t inode_refs; }; +static inline struct landlock_file_security * +landlock_file(const struct file *const file) +{ + return file->f_security + landlock_blob_sizes.lbs_file; +} + static inline struct landlock_inode_security * landlock_inode(const struct inode *const inode) { diff --git a/security/landlock/limits.h b/security/landlock/limits.h index b54184ab9439..82288f0e9e5e 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -18,7 +18,7 @@ #define LANDLOCK_MAX_NUM_LAYERS 16 #define LANDLOCK_MAX_NUM_RULES U32_MAX -#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_REFER +#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_TRUNCATE #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1) #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS) diff --git a/security/landlock/setup.c b/security/landlock/setup.c index f8e8e980454c..3f196d2ce4f9 100644 --- a/security/landlock/setup.c +++ b/security/landlock/setup.c @@ -19,6 +19,7 @@ bool landlock_initialized __lsm_ro_after_init = false; struct lsm_blob_sizes landlock_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct landlock_cred_security), + .lbs_file = sizeof(struct landlock_file_security), .lbs_inode = sizeof(struct landlock_inode_security), .lbs_superblock = sizeof(struct landlock_superblock_security), }; diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 2ca0ccbd905a..245cc650a4dc 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -129,7 +129,7 @@ static const struct file_operations ruleset_fops = { .write = fop_dummy_write, }; -#define LANDLOCK_ABI_VERSION 2 +#define LANDLOCK_ABI_VERSION 3 /** * sys_landlock_create_ruleset - Create a new ruleset diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c index da9290817866..72cdae277b02 100644 --- a/tools/testing/selftests/landlock/base_test.c +++ b/tools/testing/selftests/landlock/base_test.c @@ -75,7 +75,7 @@ TEST(abi_version) const struct landlock_ruleset_attr ruleset_attr = { .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, }; - ASSERT_EQ(2, landlock_create_ruleset(NULL, 0, + ASSERT_EQ(3, landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION)); ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index 45de42a027c5..87b28d14a1aa 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -406,9 +406,10 @@ TEST_F_FORK(layout1, inval) #define ACCESS_FILE ( \ LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \ - LANDLOCK_ACCESS_FS_READ_FILE) + LANDLOCK_ACCESS_FS_READ_FILE | \ + LANDLOCK_ACCESS_FS_TRUNCATE) -#define ACCESS_LAST LANDLOCK_ACCESS_FS_REFER +#define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE #define ACCESS_ALL ( \ ACCESS_FILE | \ @@ -422,7 +423,7 @@ TEST_F_FORK(layout1, inval) LANDLOCK_ACCESS_FS_MAKE_FIFO | \ LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ LANDLOCK_ACCESS_FS_MAKE_SYM | \ - ACCESS_LAST) + LANDLOCK_ACCESS_FS_REFER) /* clang-format on */ From patchwork Sat Oct 8 10:09:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=BCnther_Noack?= X-Patchwork-Id: 13001736 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 3B424C433FE for ; Sat, 8 Oct 2022 10:10:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229707AbiJHKKB (ORCPT ); Sat, 8 Oct 2022 06:10:01 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50560 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229445AbiJHKJr (ORCPT ); Sat, 8 Oct 2022 06:09:47 -0400 Received: from mail-ej1-x635.google.com (mail-ej1-x635.google.com [IPv6:2a00:1450:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 04312D101; Sat, 8 Oct 2022 03:09:45 -0700 (PDT) Received: by mail-ej1-x635.google.com with SMTP id r17so15904292eja.7; Sat, 08 Oct 2022 03:09:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=s+g3SXvO/8Bh7JPwh3SfT/06Lg3wGAqn3lvO15O0qAY=; b=XCcYJMzY5g7ToJ6qMYJrfu4sJsVWEkF6HWc9wCTfc2kkt/7lPHKc6xZYYTHDXk4IxF hBl7yK4ek8uHpWZ3BvAOA9ym4c5z0y/8WYC5tQjL0gGwcivqu+V5UxFE6afbJXVbpozI 7tO8+G5leE6U0zepFZ5Zxknsk2Hj2BY+Oae6aZVYtl5P4Qp2omQ7sXDbjkyqBNTluz9H /cVZOXsSnEVL88aUcDa38YtvYtibFgFfnGtLqyVLWMYyJdBiYhGr2zyj3DRglAkWyCLA TdPuv9Ktv2X5q8qet3MP5Ag+ev0WumXsiQOaLAQILcbpc/I50YJT6vxeJKaVbpakn0/Z LSBg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=s+g3SXvO/8Bh7JPwh3SfT/06Lg3wGAqn3lvO15O0qAY=; b=epDrAFfywiDIg4B4PhJey67oqYXf0KLiKYvGPDqgtUAykjf/Nmd7jSPv+0x4mDqLOh NHK7iXFxYZCLu+UhzMui3ED04E+JQLXJxLc0mVlbUOHdkEp9RA85m7Uleml2TsJ/ARCk u+aWulKZxFeU8c8sj3EyEFVyCPYrm4Khro8wZnJKJzBOMlDk0qO5PhTsMp8Lh1+K8IZK mm/1buXAiQa+70yq6ofWOMdUE6o/UQ5Cb9NXE3dfY3x/zYRXpQATXXaYzdzP96fB7WKW gtiU4PZmoJWhdcgQS/dYZUe7tuI5fYsxogqehNl/7LnK05N3nUiT4/5t0yRiNlUxJTl4 CY/g== X-Gm-Message-State: ACrzQf3GClYaoSTI8yZlZVwy4xLxpU2NRDUpOv4ost8XGanWF+guRHzb Stu1yQKdysUURMIYPZBq3GbnC++rJR4= X-Google-Smtp-Source: AMsMyM63zfW6LERfgFziJbY7n+zBYSbMpEXArSuOqw3D9cEE2nrZHAfZWRsAizydGiQ0Zj96rgh+yw== X-Received: by 2002:a17:907:e9f:b0:78d:3bab:e5df with SMTP id ho31-20020a1709070e9f00b0078d3babe5dfmr7346085ejc.65.1665223783504; Sat, 08 Oct 2022 03:09:43 -0700 (PDT) Received: from nuc.i.gnoack.org ([2a02:168:633b:1:1e69:7aff:fe05:97e6]) by smtp.gmail.com with ESMTPSA id e9-20020aa7d7c9000000b00452878cba5bsm3092012eds.97.2022.10.08.03.09.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Oct 2022 03:09:43 -0700 (PDT) From: =?utf-8?q?G=C3=BCnther_Noack?= To: linux-security-module@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , James Morris , Paul Moore , "Serge E . Hallyn" , linux-fsdevel@vger.kernel.org, Konstantin Meskhidze , Nathan Chancellor , =?utf-8?q?G=C3=BCnther_Noack?= Subject: [PATCH v9 05/11] selftests/landlock: Test file truncation support Date: Sat, 8 Oct 2022 12:09:31 +0200 Message-Id: <20221008100935.73706-6-gnoack3000@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221008100935.73706-1-gnoack3000@gmail.com> References: <20221008100935.73706-1-gnoack3000@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: These tests exercise the following truncation operations: * truncate() (truncate by path) * ftruncate() (truncate by file descriptor) * open with the O_TRUNC flag * special case: creat(), which is open with O_CREAT|O_WRONLY|O_TRUNC. in the following scenarios: * Files with read, write and truncate rights. * Files with read and truncate rights. * Files with the truncate right. * Files without the truncate right. In particular, the following scenarios are enforced with the test: * open() with O_TRUNC requires the truncate right, if it truncates a file. open() already checks security_path_truncate() in this case, and it required no additional check in the Landlock LSM's file_open hook. * creat() requires the truncate right when called with an existing filename. * creat() does *not* require the truncate right when it's creating a new file. * ftruncate() requires that the file was opened by a thread that had the truncate right for the file at the time of open(). (The rights are carried along with the opened file.) Signed-off-by: Günther Noack --- tools/testing/selftests/landlock/fs_test.c | 287 +++++++++++++++++++++ 1 file changed, 287 insertions(+) diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index 87b28d14a1aa..718543fd3dfc 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -58,6 +58,7 @@ static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1"; static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; static const char dir_s3d1[] = TMP_DIR "/s3d1"; +static const char file1_s3d1[] = TMP_DIR "/s3d1/f1"; /* dir_s3d2 is a mount point. */ static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; @@ -83,6 +84,7 @@ static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; * │   ├── f1 * │   └── f2 * └── s3d1 + *    ├── f1 * └── s3d2 * └── s3d3 */ @@ -208,6 +210,7 @@ static void create_layout1(struct __test_metadata *const _metadata) create_file(_metadata, file1_s2d3); create_file(_metadata, file2_s2d3); + create_file(_metadata, file1_s3d1); create_directory(_metadata, dir_s3d2); set_cap(_metadata, CAP_SYS_ADMIN); ASSERT_EQ(0, mount("tmp", dir_s3d2, "tmpfs", 0, "size=4m,mode=700")); @@ -230,6 +233,7 @@ static void remove_layout1(struct __test_metadata *const _metadata) EXPECT_EQ(0, remove_path(file1_s2d2)); EXPECT_EQ(0, remove_path(file1_s2d1)); + EXPECT_EQ(0, remove_path(file1_s3d1)); EXPECT_EQ(0, remove_path(dir_s3d3)); set_cap(_metadata, CAP_SYS_ADMIN); umount(dir_s3d2); @@ -3158,6 +3162,289 @@ TEST_F_FORK(layout1, proc_pipe) ASSERT_EQ(0, close(pipe_fds[1])); } +/* Invokes truncate(2) and returns its errno or 0. */ +static int test_truncate(const char *const path) +{ + if (truncate(path, 10) < 0) + return errno; + return 0; +} + +/* + * Invokes creat(2) and returns its errno or 0. + * Closes the opened file descriptor on success. + */ +static int test_creat(const char *const path) +{ + int fd = creat(path, 0600); + + if (fd < 0) + return errno; + + /* + * Mixing error codes from close(2) and creat(2) should not lead to any + * (access type) confusion for this test. + */ + if (close(fd) < 0) + return errno; + return 0; +} + +/* + * Exercises file truncation when it's not restricted, + * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed. + */ +TEST_F_FORK(layout1, truncate_unhandled) +{ + const char *const file_r = file1_s1d1; + const char *const file_w = file2_s1d1; + const char *const file_none = file1_s1d2; + const struct rule rules[] = { + { + .path = file_r, + .access = LANDLOCK_ACCESS_FS_READ_FILE, + }, + { + .path = file_w, + .access = LANDLOCK_ACCESS_FS_WRITE_FILE, + }, + /* Implicitly: No rights for file_none. */ + {}, + }; + + const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE; + int ruleset_fd; + + /* Enable Landlock. */ + ruleset_fd = create_ruleset(_metadata, handled, rules); + + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + /* + * Checks read right: truncate and open with O_TRUNC work, unless the + * file is attempted to be opened for writing. + */ + EXPECT_EQ(0, test_truncate(file_r)); + EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC)); + EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC)); + EXPECT_EQ(EACCES, test_creat(file_r)); + + /* + * Checks write right: truncate and open with O_TRUNC work, unless the + * file is attempted to be opened for reading. + */ + EXPECT_EQ(0, test_truncate(file_w)); + EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC)); + EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC)); + EXPECT_EQ(0, test_creat(file_w)); + + /* + * Checks "no rights" case: truncate works but all open attempts fail, + * including creat. + */ + EXPECT_EQ(0, test_truncate(file_none)); + EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); + EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); + EXPECT_EQ(EACCES, test_creat(file_none)); +} + +TEST_F_FORK(layout1, truncate) +{ + const char *const file_rwt = file1_s1d1; + const char *const file_rw = file2_s1d1; + const char *const file_rt = file1_s1d2; + const char *const file_t = file2_s1d2; + const char *const file_none = file1_s1d3; + const char *const dir_t = dir_s2d1; + const char *const file_in_dir_t = file1_s2d1; + const char *const dir_w = dir_s3d1; + const char *const file_in_dir_w = file1_s3d1; + const struct rule rules[] = { + { + .path = file_rwt, + .access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_TRUNCATE, + }, + { + .path = file_rw, + .access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE, + }, + { + .path = file_rt, + .access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_TRUNCATE, + }, + { + .path = file_t, + .access = LANDLOCK_ACCESS_FS_TRUNCATE, + }, + /* Implicitly: No access rights for file_none. */ + { + .path = dir_t, + .access = LANDLOCK_ACCESS_FS_TRUNCATE, + }, + { + .path = dir_w, + .access = LANDLOCK_ACCESS_FS_WRITE_FILE, + }, + {}, + }; + const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_TRUNCATE; + int ruleset_fd; + + /* Enable Landlock. */ + ruleset_fd = create_ruleset(_metadata, handled, rules); + + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + /* Checks read, write and truncate rights: truncation works. */ + EXPECT_EQ(0, test_truncate(file_rwt)); + EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC)); + EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC)); + + /* Checks read and write rights: no truncate variant works. */ + EXPECT_EQ(EACCES, test_truncate(file_rw)); + EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC)); + EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC)); + + /* + * Checks read and truncate rights: truncation works. + * + * Note: Files can get truncated using open() even with O_RDONLY. + */ + EXPECT_EQ(0, test_truncate(file_rt)); + EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC)); + EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC)); + + /* Checks truncate right: truncate works, but can't open file. */ + EXPECT_EQ(0, test_truncate(file_t)); + EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC)); + EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC)); + + /* Checks "no rights" case: No form of truncation works. */ + EXPECT_EQ(EACCES, test_truncate(file_none)); + EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); + EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); + + /* + * Checks truncate right on directory: truncate works on contained + * files. + */ + EXPECT_EQ(0, test_truncate(file_in_dir_t)); + EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC)); + EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC)); + + /* + * Checks creat in dir_w: This requires the truncate right when + * overwriting an existing file, but does not require it when the file + * is new. + */ + EXPECT_EQ(EACCES, test_creat(file_in_dir_w)); + + ASSERT_EQ(0, unlink(file_in_dir_w)); + EXPECT_EQ(0, test_creat(file_in_dir_w)); +} + +/* Invokes ftruncate(2) and returns its errno or 0. */ +static int test_ftruncate(int fd) +{ + if (ftruncate(fd, 10) < 0) + return errno; + return 0; +} + +TEST_F_FORK(layout1, ftruncate) +{ + /* + * This test opens a new file descriptor at different stages of + * Landlock restriction: + * + * without restriction: ftruncate works + * something else but truncate restricted: ftruncate works + * truncate restricted and permitted: ftruncate works + * truncate restricted and not permitted: ftruncate fails + * + * Whether this works or not is expected to depend on the time when the + * FD was opened, not to depend on the time when ftruncate() was + * called. + */ + const char *const path = file1_s1d1; + const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE; + const struct rule layer1[] = { + { + .path = path, + .access = LANDLOCK_ACCESS_FS_WRITE_FILE, + }, + {}, + }; + const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE; + const struct rule layer2[] = { + { + .path = path, + .access = LANDLOCK_ACCESS_FS_TRUNCATE, + }, + {}, + }; + const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE | + LANDLOCK_ACCESS_FS_WRITE_FILE; + const struct rule layer3[] = { + { + .path = path, + .access = LANDLOCK_ACCESS_FS_WRITE_FILE, + }, + {}, + }; + int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd; + + fd_layer0 = open(path, O_WRONLY); + EXPECT_EQ(0, test_ftruncate(fd_layer0)); + + ruleset_fd = create_ruleset(_metadata, handled1, layer1); + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + fd_layer1 = open(path, O_WRONLY); + EXPECT_EQ(0, test_ftruncate(fd_layer0)); + EXPECT_EQ(0, test_ftruncate(fd_layer1)); + + ruleset_fd = create_ruleset(_metadata, handled2, layer2); + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + fd_layer2 = open(path, O_WRONLY); + EXPECT_EQ(0, test_ftruncate(fd_layer0)); + EXPECT_EQ(0, test_ftruncate(fd_layer1)); + EXPECT_EQ(0, test_ftruncate(fd_layer2)); + + ruleset_fd = create_ruleset(_metadata, handled3, layer3); + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + fd_layer3 = open(path, O_WRONLY); + EXPECT_EQ(0, test_ftruncate(fd_layer0)); + EXPECT_EQ(0, test_ftruncate(fd_layer1)); + EXPECT_EQ(0, test_ftruncate(fd_layer2)); + EXPECT_EQ(EACCES, test_ftruncate(fd_layer3)); + + ASSERT_EQ(0, close(fd_layer0)); + ASSERT_EQ(0, close(fd_layer1)); + ASSERT_EQ(0, close(fd_layer2)); + ASSERT_EQ(0, close(fd_layer3)); +} + /* clang-format off */ FIXTURE(layout1_bind) {}; /* clang-format on */ From patchwork Sat Oct 8 10:09:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=BCnther_Noack?= X-Patchwork-Id: 13001737 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 DB647C4332F for ; Sat, 8 Oct 2022 10:10:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229445AbiJHKKB (ORCPT ); Sat, 8 Oct 2022 06:10:01 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50562 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229586AbiJHKJr (ORCPT ); Sat, 8 Oct 2022 06:09:47 -0400 Received: from mail-ej1-x629.google.com (mail-ej1-x629.google.com [IPv6:2a00:1450:4864:20::629]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 92517E030; Sat, 8 Oct 2022 03:09:45 -0700 (PDT) Received: by mail-ej1-x629.google.com with SMTP id o21so15857221ejm.11; Sat, 08 Oct 2022 03:09:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=U7EZyBx7bCF7vqPgVGnwZCbGudLlBsaHfU69mkM4HyE=; b=UlTRsMsoil36bhQi0nZxlF9l6pAittSuiEgua2XcGOb2T0PONZEUt4NSM+be88IZNY J9c1VyL5HAqj30Mk5BTZnoi8fsFOtdslYxuU94K9MSI1X1CXMWNxeRjc36hhhHJ9Oabg 0HXQmI4JJpyzYfo2pE75HwkL1zLoRVcQo50hyYA8+SFARwaY/RpcqUOoaeek0bTA+ESL F8loHfqCJI5NWTnDGmCM8/H2Lp/s1p1EfYUP2Njq0eBEa+bAagkq051KZJ+/Z/sP+8xM lGjE3GgcxDjGEFuJlD97rI+4Itr2LUKVF16CFztUJuO2b2XiuWd3Rf0iBgEvDEdrWWMn HbIw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=U7EZyBx7bCF7vqPgVGnwZCbGudLlBsaHfU69mkM4HyE=; b=wPF/1WGNxDkZz05VAkk1xZfY1fmXX4BG5qvIDbjCkwsW6NoKjmaQ8CrP2uUZQoOYFY 2zZY57x+aqcx3YxmesEwFTqUGYjUoiyaa9koKn0T+38fmUgcba3PAPiY9SjqgdMs1dfn GMs7UJI8rQ0WkZS0a9bHTNXDxOtm7TfR4Ta8l1brCfEkFtz/lEcDW+8ICkihDjmEM9BU kjceiZP2ctV5LWQMi9Lc3bhyKPDX/hmSJ5oFTbH6iWxQf/Ya3zXQRuSYmH/G4zQC+QUD osZrOYHWU7ctaZ0zWoXFoR0/Fok5ybfPvkcMl92LF/eC25XwBVZZPUvGWgf9qUj3Chbm bm+Q== X-Gm-Message-State: ACrzQf2KJqrwBdRf8yx8TiNw2RJwZ96IKveazkE88PZJYICLS1YZllkG go3wWKN2AdfEOTWIaO2skZBh8BF0iHU= X-Google-Smtp-Source: AMsMyM6QuNag39owg0zIYZ37JHWtEvIZqkhRUov69252VCziGYJcEBYuNDgNHRQyAK5G0/XRKWxBCA== X-Received: by 2002:a17:907:96a5:b0:78d:9e77:f967 with SMTP id hd37-20020a17090796a500b0078d9e77f967mr246284ejc.702.1665223784189; Sat, 08 Oct 2022 03:09:44 -0700 (PDT) Received: from nuc.i.gnoack.org ([2a02:168:633b:1:1e69:7aff:fe05:97e6]) by smtp.gmail.com with ESMTPSA id e9-20020aa7d7c9000000b00452878cba5bsm3092012eds.97.2022.10.08.03.09.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Oct 2022 03:09:43 -0700 (PDT) From: =?utf-8?q?G=C3=BCnther_Noack?= To: linux-security-module@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , James Morris , Paul Moore , "Serge E . Hallyn" , linux-fsdevel@vger.kernel.org, Konstantin Meskhidze , Nathan Chancellor , =?utf-8?q?G=C3=BCnther_Noack?= Subject: [PATCH v9 06/11] selftests/landlock: Test open() and ftruncate() in multiple scenarios Date: Sat, 8 Oct 2022 12:09:32 +0200 Message-Id: <20221008100935.73706-7-gnoack3000@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221008100935.73706-1-gnoack3000@gmail.com> References: <20221008100935.73706-1-gnoack3000@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: This test uses multiple fixture variants to exercise a broader set of scnenarios. Signed-off-by: Günther Noack --- tools/testing/selftests/landlock/fs_test.c | 96 ++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index 718543fd3dfc..308f6f36e8c0 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -3445,6 +3445,102 @@ TEST_F_FORK(layout1, ftruncate) ASSERT_EQ(0, close(fd_layer3)); } +/* clang-format off */ +FIXTURE(ftruncate) {}; +/* clang-format on */ + +FIXTURE_SETUP(ftruncate) +{ + prepare_layout(_metadata); + create_file(_metadata, file1_s1d1); +} + +FIXTURE_TEARDOWN(ftruncate) +{ + EXPECT_EQ(0, remove_path(file1_s1d1)); + cleanup_layout(_metadata); +} + +FIXTURE_VARIANT(ftruncate) +{ + const __u64 handled; + const __u64 permitted; + const int expected_open_result; + const int expected_ftruncate_result; +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(ftruncate, w_w) { + /* clang-format on */ + .handled = LANDLOCK_ACCESS_FS_WRITE_FILE, + .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, + .expected_open_result = 0, + .expected_ftruncate_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(ftruncate, t_t) { + /* clang-format on */ + .handled = LANDLOCK_ACCESS_FS_TRUNCATE, + .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, + .expected_open_result = 0, + .expected_ftruncate_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(ftruncate, wt_w) { + /* clang-format on */ + .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, + .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, + .expected_open_result = 0, + .expected_ftruncate_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(ftruncate, wt_wt) { + /* clang-format on */ + .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, + .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_TRUNCATE, + .expected_open_result = 0, + .expected_ftruncate_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(ftruncate, wt_t) { + /* clang-format on */ + .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, + .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, + .expected_open_result = EACCES, +}; + +TEST_F_FORK(ftruncate, open_and_ftruncate) +{ + const char *const path = file1_s1d1; + const struct rule rules[] = { + { + .path = path, + .access = variant->permitted, + }, + {}, + }; + int fd, ruleset_fd; + + /* Enable Landlock. */ + ruleset_fd = create_ruleset(_metadata, variant->handled, rules); + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + fd = open(path, O_WRONLY); + EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); + if (fd >= 0) { + EXPECT_EQ(variant->expected_ftruncate_result, + test_ftruncate(fd)); + ASSERT_EQ(0, close(fd)); + } +} + /* clang-format off */ FIXTURE(layout1_bind) {}; /* clang-format on */ From patchwork Sat Oct 8 10:09:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=BCnther_Noack?= X-Patchwork-Id: 13001738 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 08FCFC433FE for ; Sat, 8 Oct 2022 10:10:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229487AbiJHKKP (ORCPT ); Sat, 8 Oct 2022 06:10:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50616 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229593AbiJHKJr (ORCPT ); Sat, 8 Oct 2022 06:09:47 -0400 Received: from mail-ej1-x62b.google.com (mail-ej1-x62b.google.com [IPv6:2a00:1450:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5941D12AE2; Sat, 8 Oct 2022 03:09:46 -0700 (PDT) Received: by mail-ej1-x62b.google.com with SMTP id o21so15857249ejm.11; Sat, 08 Oct 2022 03:09:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=NAI6zB9+0AGnJiVMY5vVZsK//rjwT955A9i61fTtXGs=; b=QftAXJ/bSX96aZ3IhSGRVx89o8TX04cVeOB8ESBghFFDTefGH43LzjrMFB90rh/Rq5 jkjRGzYT1aPSdVmshXg2waKrr+Vjk24Mh6JC41aXzaABCS1go7myiC0Z6KsHAnCDKgRQ sORDquoNwEb15HsZQqrJDRWovRbhm5AmHqv3nNlgS5vlA/sAo80eKbdURE+lqx/IkoEu XcWT2Pdin9K4vzbFcB+HXRu7T4E6vXEjOBVvnWrKHFKu7sSjbqx44TIkPxq5SZlrsbNs g7PT9LUpXHZ4vj9sSn2VNGLy/ASqs+6wtQpSy7YJisAblui2+yNFA9pS7FbcnZDJVUQj wGEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=NAI6zB9+0AGnJiVMY5vVZsK//rjwT955A9i61fTtXGs=; b=ZEqfyjXaOGIZ0ctnL2+TY19KDPmZu+TZyg+rMXAZLwKFDzLibr9Ekpnq2RutiZUohR RngkxHMMU7S2HI6k2Qi00dcV9BWF9PgelB6NdkxY+e3Y/+jiK1zurAY2dhvGxt2z192x z4R1PclhjPey965WweU8i20lo7DclqNphUkOCcJVedmOZEWLBSImxkYgpUgbCR1Ov5eZ 0lVkwJXEAH+ivhVnyLrSME60DlLc9evR1j7/MUDKAksEuWKZQZ8z9rgJ0p6kXvuHpDAP /F1hnWzBRM5Xrn/Tl5XlT9wxzulZFiKUS6kcQx0asyz8uIfc6tMJXLAJYQME7hZYLEPM JPeg== X-Gm-Message-State: ACrzQf0jC0zgnp7aAk6c/zbrTDvhja2SUbULS7HRHaR+iHaTl0lYxhJm niDowBHnJs3ok3OBtHwMggQzSK+KMnw= X-Google-Smtp-Source: AMsMyM5bhFFLtoCoRv6/tY7vwE0LIBm1/6hF1jzrETVZWidS+D2h/ZlxksQ2CsICbSTJQbmAdMw8mw== X-Received: by 2002:a17:906:5a4b:b0:78d:8790:d4a1 with SMTP id my11-20020a1709065a4b00b0078d8790d4a1mr4306387ejc.329.1665223784867; Sat, 08 Oct 2022 03:09:44 -0700 (PDT) Received: from nuc.i.gnoack.org ([2a02:168:633b:1:1e69:7aff:fe05:97e6]) by smtp.gmail.com with ESMTPSA id e9-20020aa7d7c9000000b00452878cba5bsm3092012eds.97.2022.10.08.03.09.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Oct 2022 03:09:44 -0700 (PDT) From: =?utf-8?q?G=C3=BCnther_Noack?= To: linux-security-module@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , James Morris , Paul Moore , "Serge E . Hallyn" , linux-fsdevel@vger.kernel.org, Konstantin Meskhidze , Nathan Chancellor , =?utf-8?q?G=C3=BCnther_Noack?= Subject: [PATCH v9 07/11] selftests/landlock: Locally define __maybe_unused Date: Sat, 8 Oct 2022 12:09:33 +0200 Message-Id: <20221008100935.73706-8-gnoack3000@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221008100935.73706-1-gnoack3000@gmail.com> References: <20221008100935.73706-1-gnoack3000@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: The checkpatch tool started to flag __attribute__(__unused__), which we previously used. The header where this is normally defined is not currently compatible with selftests. This is the same approach as used in selftests/net/psock_lib.h. Signed-off-by: Günther Noack --- tools/testing/selftests/landlock/common.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h index 7ba18eb23783..7d34592471db 100644 --- a/tools/testing/selftests/landlock/common.h +++ b/tools/testing/selftests/landlock/common.h @@ -17,6 +17,10 @@ #include "../kselftest_harness.h" +#ifndef __maybe_unused +#define __maybe_unused __attribute__((__unused__)) +#endif + /* * TEST_F_FORK() is useful when a test drop privileges but the corresponding * FIXTURE_TEARDOWN() requires them (e.g. to remove files from a directory @@ -140,14 +144,12 @@ static void _init_caps(struct __test_metadata *const _metadata, bool drop_all) } /* We cannot put such helpers in a library because of kselftest_harness.h . */ -__attribute__((__unused__)) static void -disable_caps(struct __test_metadata *const _metadata) +static void __maybe_unused disable_caps(struct __test_metadata *const _metadata) { _init_caps(_metadata, false); } -__attribute__((__unused__)) static void -drop_caps(struct __test_metadata *const _metadata) +static void __maybe_unused drop_caps(struct __test_metadata *const _metadata) { _init_caps(_metadata, true); } @@ -176,14 +178,14 @@ static void _effective_cap(struct __test_metadata *const _metadata, } } -__attribute__((__unused__)) static void -set_cap(struct __test_metadata *const _metadata, const cap_value_t caps) +static void __maybe_unused set_cap(struct __test_metadata *const _metadata, + const cap_value_t caps) { _effective_cap(_metadata, caps, CAP_SET); } -__attribute__((__unused__)) static void -clear_cap(struct __test_metadata *const _metadata, const cap_value_t caps) +static void __maybe_unused clear_cap(struct __test_metadata *const _metadata, + const cap_value_t caps) { _effective_cap(_metadata, caps, CAP_CLEAR); } From patchwork Sat Oct 8 10:09:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=BCnther_Noack?= X-Patchwork-Id: 13001739 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 D50C2C433FE for ; Sat, 8 Oct 2022 10:10:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229686AbiJHKKZ (ORCPT ); Sat, 8 Oct 2022 06:10:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50644 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229607AbiJHKJr (ORCPT ); Sat, 8 Oct 2022 06:09:47 -0400 Received: from mail-ej1-x62f.google.com (mail-ej1-x62f.google.com [IPv6:2a00:1450:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0F82C12771; Sat, 8 Oct 2022 03:09:46 -0700 (PDT) Received: by mail-ej1-x62f.google.com with SMTP id r17so15904404eja.7; Sat, 08 Oct 2022 03:09:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=MCmk/tBVjbF+7TqU0s2kMsuRpHDv0d5bCD7ppPQIYwk=; b=UVXS8anx5Txkk3Nd5214wh8p7gjo5jsdHYueAn4jWMlsbhmu+fiQdGu/zIVniLBd+v T8h9Tf9TdFWsENBlRm36qgn7LEqn7sE1HRmUesNY7jw7kxe2b+pC94ox5Acd1WA31fgu kEI51KQJFAbYArzhsfDBbYSYfu/ct/N5yA7pei6NodqQfu0FSbeJMxs+OwlHaPffiTzG MW/9blMi3lu8iVL5UNyQUIY91nDCp1kOuWd+zXMvTqDJ+IMedE7nS9MAc28v3+U0YZTK HR4foAlbvM6PmeEOyMHGCR6vJ8Yp3EMyG6gMST+y1PJe5qABubZkiVMdrkaHei8ok7cF txnQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=MCmk/tBVjbF+7TqU0s2kMsuRpHDv0d5bCD7ppPQIYwk=; b=ezUoJlgT5ue/Ni5P2FbSdnBehlEMPdAVw1ayCjrWILwpAfMj+gQtZ/mgRT1ljHYzIN f2NkiUeZleaCczxvZvx0EsL0KOrAbw/FETrOMRZ8QUQsuxwQhI4duMLeWKaA5K86kXxe W34yfFK1ZgmS8AhnoMKPtVoFhUBS2FebjFpKaLyEvR/xeU3v797EmoABwK1YDIRKRMpP hV+eGCASuTkQ/zvaEisrvVimVzKB5Y/aj4IvBPnn6IsaDRnmxRxuWFNzbMn7hOy4OApM PQoCgyaq0dmWtHEUc/0KUUDiQB0vLZFQI1+LAltlwgePG4sMfX3qSBTP4hS2BOGXnz+W XS8w== X-Gm-Message-State: ACrzQf3bL4egfczW1w1StncjkbmHoOrUpbGGQBoq1BvoXP96Xm3mGzdt xtP+g+lssKOtEqFYieL/5xlbw7yCozM= X-Google-Smtp-Source: AMsMyM5VpgQIYbaxJsaccosJ7hbMkRPX6k/GprDHj9BNjkId3oUryZDPtwhASTf6OIKr8ZG2l4DEzA== X-Received: by 2002:a17:906:6a1a:b0:78c:fa9c:e621 with SMTP id qw26-20020a1709066a1a00b0078cfa9ce621mr7453287ejc.160.1665223785548; Sat, 08 Oct 2022 03:09:45 -0700 (PDT) Received: from nuc.i.gnoack.org ([2a02:168:633b:1:1e69:7aff:fe05:97e6]) by smtp.gmail.com with ESMTPSA id e9-20020aa7d7c9000000b00452878cba5bsm3092012eds.97.2022.10.08.03.09.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Oct 2022 03:09:45 -0700 (PDT) From: =?utf-8?q?G=C3=BCnther_Noack?= To: linux-security-module@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , James Morris , Paul Moore , "Serge E . Hallyn" , linux-fsdevel@vger.kernel.org, Konstantin Meskhidze , Nathan Chancellor , =?utf-8?q?G=C3=BCnther_Noack?= Subject: [PATCH v9 08/11] selftests/landlock: Test FD passing from restricted to unrestricted processes Date: Sat, 8 Oct 2022 12:09:34 +0200 Message-Id: <20221008100935.73706-9-gnoack3000@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221008100935.73706-1-gnoack3000@gmail.com> References: <20221008100935.73706-1-gnoack3000@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: A file descriptor created in a restricted process carries Landlock restrictions with it which will apply even if the same opened file is used from an unrestricted process. This change extracts suitable FD-passing helpers from base_test.c and moves them to common.h. We use the fixture variants from the ftruncate fixture to exercise the same scenarios as in the open_and_ftruncate test, but doing the Landlock restriction and open() in a different process than the ftruncate() call. Signed-off-by: Günther Noack --- tools/testing/selftests/landlock/base_test.c | 36 +---------- tools/testing/selftests/landlock/common.h | 67 ++++++++++++++++++++ tools/testing/selftests/landlock/fs_test.c | 62 ++++++++++++++++++ 3 files changed, 132 insertions(+), 33 deletions(-) diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c index 72cdae277b02..792c3f0a59b4 100644 --- a/tools/testing/selftests/landlock/base_test.c +++ b/tools/testing/selftests/landlock/base_test.c @@ -263,23 +263,6 @@ TEST(ruleset_fd_transfer) .allowed_access = LANDLOCK_ACCESS_FS_READ_DIR, }; int ruleset_fd_tx, dir_fd; - union { - /* Aligned ancillary data buffer. */ - char buf[CMSG_SPACE(sizeof(ruleset_fd_tx))]; - struct cmsghdr _align; - } cmsg_tx = {}; - char data_tx = '.'; - struct iovec io = { - .iov_base = &data_tx, - .iov_len = sizeof(data_tx), - }; - struct msghdr msg = { - .msg_iov = &io, - .msg_iovlen = 1, - .msg_control = &cmsg_tx.buf, - .msg_controllen = sizeof(cmsg_tx.buf), - }; - struct cmsghdr *cmsg; int socket_fds[2]; pid_t child; int status; @@ -298,33 +281,20 @@ TEST(ruleset_fd_transfer) &path_beneath_attr, 0)); ASSERT_EQ(0, close(path_beneath_attr.parent_fd)); - cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(NULL, cmsg); - cmsg->cmsg_len = CMSG_LEN(sizeof(ruleset_fd_tx)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), &ruleset_fd_tx, sizeof(ruleset_fd_tx)); - /* Sends the ruleset FD over a socketpair and then close it. */ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, socket_fds)); - ASSERT_EQ(sizeof(data_tx), sendmsg(socket_fds[0], &msg, 0)); + ASSERT_EQ(0, send_fd(socket_fds[0], ruleset_fd_tx)); ASSERT_EQ(0, close(socket_fds[0])); ASSERT_EQ(0, close(ruleset_fd_tx)); child = fork(); ASSERT_LE(0, child); if (child == 0) { - int ruleset_fd_rx; + const int ruleset_fd_rx = recv_fd(socket_fds[1]); - *(char *)msg.msg_iov->iov_base = '\0'; - ASSERT_EQ(sizeof(data_tx), - recvmsg(socket_fds[1], &msg, MSG_CMSG_CLOEXEC)); - ASSERT_EQ('.', *(char *)msg.msg_iov->iov_base); + ASSERT_LE(0, ruleset_fd_rx); ASSERT_EQ(0, close(socket_fds[1])); - cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(ruleset_fd_tx))); - memcpy(&ruleset_fd_rx, CMSG_DATA(cmsg), sizeof(ruleset_fd_tx)); /* Enforces the received ruleset on the child. */ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h index 7d34592471db..d7987ae8d7fc 100644 --- a/tools/testing/selftests/landlock/common.h +++ b/tools/testing/selftests/landlock/common.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -189,3 +190,69 @@ static void __maybe_unused clear_cap(struct __test_metadata *const _metadata, { _effective_cap(_metadata, caps, CAP_CLEAR); } + +/* Receives an FD from a UNIX socket. Returns the received FD, or -errno. */ +static int __maybe_unused recv_fd(int usock) +{ + int fd_rx; + union { + /* Aligned ancillary data buffer. */ + char buf[CMSG_SPACE(sizeof(fd_rx))]; + struct cmsghdr _align; + } cmsg_rx = {}; + char data = '\0'; + struct iovec io = { + .iov_base = &data, + .iov_len = sizeof(data), + }; + struct msghdr msg = { + .msg_iov = &io, + .msg_iovlen = 1, + .msg_control = &cmsg_rx.buf, + .msg_controllen = sizeof(cmsg_rx.buf), + }; + struct cmsghdr *cmsg; + int res; + + res = recvmsg(usock, &msg, MSG_CMSG_CLOEXEC); + if (res < 0) + return -errno; + + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd_rx))) + return -EIO; + + memcpy(&fd_rx, CMSG_DATA(cmsg), sizeof(fd_rx)); + return fd_rx; +} + +/* Sends an FD on a UNIX socket. Returns 0 on success or -errno. */ +static int __maybe_unused send_fd(int usock, int fd_tx) +{ + union { + /* Aligned ancillary data buffer. */ + char buf[CMSG_SPACE(sizeof(fd_tx))]; + struct cmsghdr _align; + } cmsg_tx = {}; + char data_tx = '.'; + struct iovec io = { + .iov_base = &data_tx, + .iov_len = sizeof(data_tx), + }; + struct msghdr msg = { + .msg_iov = &io, + .msg_iovlen = 1, + .msg_control = &cmsg_tx.buf, + .msg_controllen = sizeof(cmsg_tx.buf), + }; + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + + cmsg->cmsg_len = CMSG_LEN(sizeof(fd_tx)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), &fd_tx, sizeof(fd_tx)); + + if (sendmsg(usock, &msg, 0) < 0) + return -errno; + return 0; +} diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index 308f6f36e8c0..f8aae01a2409 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -3541,6 +3541,68 @@ TEST_F_FORK(ftruncate, open_and_ftruncate) } } +TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) +{ + int child, fd, status; + int socket_fds[2]; + + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, + socket_fds)); + + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + /* + * Enables Landlock in the child process, open a file descriptor + * where truncation is forbidden and send it to the + * non-landlocked parent process. + */ + const char *const path = file1_s1d1; + const struct rule rules[] = { + { + .path = path, + .access = variant->permitted, + }, + {}, + }; + int fd, ruleset_fd; + + ruleset_fd = create_ruleset(_metadata, variant->handled, rules); + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + fd = open(path, O_WRONLY); + ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); + + if (fd >= 0) { + ASSERT_EQ(0, send_fd(socket_fds[0], fd)); + ASSERT_EQ(0, close(fd)); + } + + ASSERT_EQ(0, close(socket_fds[0])); + + _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); + return; + } + + if (variant->expected_open_result == 0) { + fd = recv_fd(socket_fds[1]); + ASSERT_LE(0, fd); + + EXPECT_EQ(variant->expected_ftruncate_result, + test_ftruncate(fd)); + ASSERT_EQ(0, close(fd)); + } + + ASSERT_EQ(child, waitpid(child, &status, 0)); + ASSERT_EQ(1, WIFEXITED(status)); + ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); + + ASSERT_EQ(0, close(socket_fds[0])); + ASSERT_EQ(0, close(socket_fds[1])); +} + /* clang-format off */ FIXTURE(layout1_bind) {}; /* clang-format on */ From patchwork Sat Oct 8 10:09:35 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=BCnther_Noack?= X-Patchwork-Id: 13001740 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 38F39C4332F for ; Sat, 8 Oct 2022 10:10:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229716AbiJHKK0 (ORCPT ); Sat, 8 Oct 2022 06:10:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50796 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229565AbiJHKJs (ORCPT ); Sat, 8 Oct 2022 06:09:48 -0400 Received: from mail-ej1-x633.google.com (mail-ej1-x633.google.com [IPv6:2a00:1450:4864:20::633]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A941F13E29; Sat, 8 Oct 2022 03:09:47 -0700 (PDT) Received: by mail-ej1-x633.google.com with SMTP id d26so8813928eje.10; Sat, 08 Oct 2022 03:09:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=TcJinYSqP/P8butZE17/ltEboJUG5yfSi0K+q7kH9Mo=; b=Gc586E66aRWub1D3GwjfjWcSbm8C7Fv6Czzbp8ZoJDsl7KnN3ogn/ANN43P8WrB3nm dJLheHypnf0RQgEU2IrxDcGETBFMabcMZHI/a7APdds+J13h8HGVvWVQaf+AyeduBwu3 Ra+1RhnqlnD3kzpzJmtOd27xMqxL0pvJfWQoHQwLczsb13htB0t4lZWrZXbX5fGOxh92 lb9tQUvUnS0ZVRHYWdZlTGO/FLlc0Jd7llN0YSusWaXPwUzPfUBhUIhOrM7y2dclx09Z IwNHjgjGlbsl244ucmW9F2OLUN4UQQaMNeEv3F5Wvoy3BNGtf5/sZRLNNNedzhd3iMyL ea1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=TcJinYSqP/P8butZE17/ltEboJUG5yfSi0K+q7kH9Mo=; b=J+de1bTcOZpdNyE/cyT0927GFcmatSNUXLbTzmML5GwCB2aSHIxE/TwjGHQW6Dvj6W SUuYVG90+lnVoELbQNoYcv69r5GZQNVPnj+HpB64XUDFKUtG7SHtjCSFBQVA1juvs7VV zRdIGG7ezadgWf6BBHk1WCHhh4Dmrk/B9FPrJo6oizrdR9B05j5qCRPe/olvD2XYIaUL DBQTpDIp1yBz7JWPIVBLuhY04kXZPF+Wuqt/LGkuwOEvOURCPF9r7C7T25aJH1G4JNby 3Y32moMIqGH5fJABlxO/aERU1rMvDtcZB03ZWSq3RmQUdJsHUgxNGtybiWqp9+35XTRp OHcQ== X-Gm-Message-State: ACrzQf2YsR5pyf643DUAGjCplfQaLOEnU1iIw177Fkg031u9VnZ2AnYR ju5KrpEXwiHpAJHuiu2yg/TTDnH3AVY= X-Google-Smtp-Source: AMsMyM5yPT8Zx2JGwZkZovchKDm8Dvn9BbIOJwiEmbKsnfbHrEWSIuZQMhPo/JaIlJr8cFfN3dlxuw== X-Received: by 2002:a17:907:1691:b0:78d:4051:9429 with SMTP id hc17-20020a170907169100b0078d40519429mr7505157ejc.721.1665223786304; Sat, 08 Oct 2022 03:09:46 -0700 (PDT) Received: from nuc.i.gnoack.org ([2a02:168:633b:1:1e69:7aff:fe05:97e6]) by smtp.gmail.com with ESMTPSA id e9-20020aa7d7c9000000b00452878cba5bsm3092012eds.97.2022.10.08.03.09.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Oct 2022 03:09:45 -0700 (PDT) From: =?utf-8?q?G=C3=BCnther_Noack?= To: linux-security-module@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , James Morris , Paul Moore , "Serge E . Hallyn" , linux-fsdevel@vger.kernel.org, Konstantin Meskhidze , Nathan Chancellor , =?utf-8?q?G=C3=BCnther_Noack?= Subject: [PATCH v9 09/11] selftests/landlock: Test ftruncate on FDs created by memfd_create(2) Date: Sat, 8 Oct 2022 12:09:35 +0200 Message-Id: <20221008100935.73706-10-gnoack3000@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221008100935.73706-1-gnoack3000@gmail.com> References: <20221008100935.73706-1-gnoack3000@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: All file descriptors that are truncatable need to have the Landlock access rights set correctly on the file's Landlock security blob. This is also the case for files that are opened by other means than open(2). Signed-off-by: Günther Noack --- tools/testing/selftests/landlock/fs_test.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index f8aae01a2409..d5dab986f612 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -3603,6 +3603,22 @@ TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) ASSERT_EQ(0, close(socket_fds[1])); } +TEST(memfd_ftruncate) +{ + int fd; + + fd = memfd_create("name", MFD_CLOEXEC); + ASSERT_LE(0, fd); + + /* + * Checks that ftruncate is permitted on file descriptors that are + * created in ways other than open(2). + */ + EXPECT_EQ(0, test_ftruncate(fd)); + + ASSERT_EQ(0, close(fd)); +} + /* clang-format off */ FIXTURE(layout1_bind) {}; /* clang-format on */ From patchwork Sat Oct 8 11:18:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=BCnther_Noack?= X-Patchwork-Id: 13001759 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 0279EC433FE for ; Sat, 8 Oct 2022 11:18:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229777AbiJHLSZ (ORCPT ); Sat, 8 Oct 2022 07:18:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59032 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229623AbiJHLSY (ORCPT ); Sat, 8 Oct 2022 07:18:24 -0400 Received: from mail-ed1-x52f.google.com (mail-ed1-x52f.google.com [IPv6:2a00:1450:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CCBB54B498; Sat, 8 Oct 2022 04:18:22 -0700 (PDT) Received: by mail-ed1-x52f.google.com with SMTP id s30so10136776eds.1; Sat, 08 Oct 2022 04:18:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ihEjAwkgFK6TMTS0NNrc4Hr909XAXppfc/t74qtEVys=; b=GPrpv/4WhT392+kBXYlHojQjIwXTeUSwxv1MMUrHy5ZNl6WRUvCf6h18KseeyKHgOn HInuGl8BxdJS5xnni0Hv6bcN7kwPy0MnZh6v9VDEGiKiOpLK3u4SS2BNOFh2Q4ZrJFm7 5zBUhmFK1HgBGkS0B5WV2rkZadLWnwRqIR6Oa/NefbNgwBvJmKg49hPU3r9aZSQyx6dn n6C1ycT1y9kzxJsF6X69i+cnBsIyfQO4BNAHamFG9rWKnerKa5QNCEejktVHqMsaNn93 5qcyEhuvlpM/jsylma2H6RqOO4OLHx0MTRRFwFyIPnwaufwqGSXKgxLW8h9tBhvROytQ wrEA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ihEjAwkgFK6TMTS0NNrc4Hr909XAXppfc/t74qtEVys=; b=dL5r0XvIXUf6zsXjWORw7lRjj+uixXANfM/ZXxXZwRIwaBdqf1dm9kqxFi9Ncd/xd1 xOdNnerd9X7rqk4qI03vHP6uQjfGE0/qKmYWVuDqWv2NBekuAjX78OrBQAz5DG2oOyxX IHOrje769OlwSYzW3eqHPAeWS1nqmff3mYDfZxlh/y9qQffz9gzxg5HpBxIUJka9Glqq JwaInvu/6Do23kCcD/Z+xXhK/SlAYGo75sMGNKmA3OfDyCvnG3sd/PWbje/573wFS4w2 Zcytv/znYI7NitDx1nrCr3A9ikZYL5uB7SpT3wBcP7oFrV99eXRRw32fhnoj53I0l5o+ YfKg== X-Gm-Message-State: ACrzQf0wqkvJYwqCpsgbi5LUhlBAly7HZThzCM/8RpjNwCtTOsATkmhs 7RaRpffWROV+bcP1gm4o4fph86ShMF0= X-Google-Smtp-Source: AMsMyM4KUdXAsoAv9B6DtS+n8R3y214lW0abameZoQGRDI929xMTP7rEgPBOhQ9cM8FN6vQxfJ2a3w== X-Received: by 2002:a05:6402:ea0:b0:454:38bf:aa3d with SMTP id h32-20020a0564020ea000b0045438bfaa3dmr8494544eda.291.1665227901439; Sat, 08 Oct 2022 04:18:21 -0700 (PDT) Received: from nuc.i.gnoack.org ([2a02:168:633b:1:1e69:7aff:fe05:97e6]) by smtp.gmail.com with ESMTPSA id 1-20020a170906218100b0073ddd36ba8csm2580136eju.145.2022.10.08.04.18.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Oct 2022 04:18:21 -0700 (PDT) From: =?utf-8?q?G=C3=BCnther_Noack?= To: linux-security-module@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , James Morris , Paul Moore , "Serge E . Hallyn" , linux-fsdevel@vger.kernel.org, Konstantin Meskhidze , Nathan Chancellor , =?utf-8?q?G=C3=BCnther_Noack?= Subject: [PATCH v9 10/11] samples/landlock: Extend sample tool to support LANDLOCK_ACCESS_FS_TRUNCATE Date: Sat, 8 Oct 2022 13:18:13 +0200 Message-Id: <20221008111814.75251-1-gnoack3000@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221008100935.73706-1-gnoack3000@gmail.com> References: <20221008100935.73706-1-gnoack3000@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: Update the sandboxer sample to restrict truncate actions. This is automatically enabled by default if the running kernel supports LANDLOCK_ACCESS_FS_TRUNCATE, except for the paths listed in the LL_FS_RW environment variable. Signed-off-by: Günther Noack --- samples/landlock/sandboxer.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c index f29bb3c72230..fd4237c64fb2 100644 --- a/samples/landlock/sandboxer.c +++ b/samples/landlock/sandboxer.c @@ -76,7 +76,8 @@ static int parse_path(char *env_path, const char ***const path_list) #define ACCESS_FILE ( \ LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \ - LANDLOCK_ACCESS_FS_READ_FILE) + LANDLOCK_ACCESS_FS_READ_FILE | \ + LANDLOCK_ACCESS_FS_TRUNCATE) /* clang-format on */ @@ -160,11 +161,12 @@ static int populate_ruleset(const char *const env_var, const int ruleset_fd, LANDLOCK_ACCESS_FS_MAKE_FIFO | \ LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ LANDLOCK_ACCESS_FS_MAKE_SYM | \ - LANDLOCK_ACCESS_FS_REFER) + LANDLOCK_ACCESS_FS_REFER | \ + LANDLOCK_ACCESS_FS_TRUNCATE) /* clang-format on */ -#define LANDLOCK_ABI_LAST 2 +#define LANDLOCK_ABI_LAST 3 int main(const int argc, char *const argv[], char *const *const envp) { @@ -234,6 +236,10 @@ int main(const int argc, char *const argv[], char *const *const envp) case 1: /* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */ ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER; + __attribute__((fallthrough)); + case 2: + /* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */ + ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE; fprintf(stderr, "Hint: You should update the running kernel " From patchwork Sat Oct 8 11:18:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=BCnther_Noack?= X-Patchwork-Id: 13001760 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 86A87C433FE for ; Sat, 8 Oct 2022 11:18:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229623AbiJHLS3 (ORCPT ); Sat, 8 Oct 2022 07:18:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59068 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229795AbiJHLS2 (ORCPT ); Sat, 8 Oct 2022 07:18:28 -0400 Received: from mail-ej1-x62d.google.com (mail-ej1-x62d.google.com [IPv6:2a00:1450:4864:20::62d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 40AD34D26F; Sat, 8 Oct 2022 04:18:27 -0700 (PDT) Received: by mail-ej1-x62d.google.com with SMTP id k2so16171142ejr.2; Sat, 08 Oct 2022 04:18:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=BCqE9e12XoBqA5RscNmFvGeITIorfGLdOj2bFbTji74=; b=Mc4cFSxZtwA/sDJ40eW+H2GgdAF86Y1598mToQUqMSrusl6xQfIe1zvNXA4eBGxan6 ClkBIK1Jnpnzo9dbOZieAGKUWNmRwIuTZZy6ajTEMbaxGjYmc2fC2oD3bkADlA7vO3c3 SE3wuyAiFNU355N9frrxMYOiSvKB5pvS4b0UImPYAAq6l2zuhZ7wV8DaD22+/6Y+kiY4 3LJN1Qemjfnn0dX1Q2me508n+uLDCkvKXyTPptvq0lbzrBT4BMydYvT4mWIU/t4c4gHt eybD6htWzUXifbXpGHgWxHScpWhfeicJZMK+jXeT8B5Fidutki8qQMdo7+1xEHH6v4yG J3EA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=BCqE9e12XoBqA5RscNmFvGeITIorfGLdOj2bFbTji74=; b=Vu+roTiN5iPrVv+HGQ9IYz+SJLV119p21ce/ypjkdjgDmY/AVDBDtSXmCZTWUBtfgR zpNuGi/e1Sc6Ajbc7AFkB1PsOM+K7YWWtWoBkJmw3SjFaVkA3vObI00zsX1Vx4+vNPY3 MZL1MxHutIHcJ3oUGx0Yp26MZcs67ctkwTz9eVK0G9nye3AZAiE+cLOOqhBW+BO+kDJU KDOsFpv7xoRv5VWCXCcyDfTvfCR5XzwxmoGiyI41Wrqb9rHHC6uWzPXQMldkLFGP9syR kHSOuwsck9Bo++jDMnkk+duyaxgkAtuItlFKCJPpYziK8NAyS//wNbt5c5OBv0CtDbB+ 5sow== X-Gm-Message-State: ACrzQf2DG7T3kczaqfd59AyPC24lw31cP8QM2bh+wKzi6Ya5U1dJ1zdB jGPVpkB5fnuwga1jKIsphegRsLU8s4k= X-Google-Smtp-Source: AMsMyM4HQbxs2uOdzHepyw1r3znobYMqMayzagWAynLBuy10ibyYIszzsWEzucrGDVE9s+73N2vvKQ== X-Received: by 2002:a17:907:608f:b0:787:a1ae:1d3b with SMTP id ht15-20020a170907608f00b00787a1ae1d3bmr7422718ejc.431.1665227905811; Sat, 08 Oct 2022 04:18:25 -0700 (PDT) Received: from nuc.i.gnoack.org ([2a02:168:633b:1:1e69:7aff:fe05:97e6]) by smtp.gmail.com with ESMTPSA id 1-20020a170906218100b0073ddd36ba8csm2580136eju.145.2022.10.08.04.18.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Oct 2022 04:18:25 -0700 (PDT) From: =?utf-8?q?G=C3=BCnther_Noack?= To: linux-security-module@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , James Morris , Paul Moore , "Serge E . Hallyn" , linux-fsdevel@vger.kernel.org, Konstantin Meskhidze , Nathan Chancellor , =?utf-8?q?G=C3=BCnther_Noack?= Subject: [PATCH v9 11/11] landlock: Document Landlock's file truncation support Date: Sat, 8 Oct 2022 13:18:14 +0200 Message-Id: <20221008111814.75251-2-gnoack3000@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221008111814.75251-1-gnoack3000@gmail.com> References: <20221008100935.73706-1-gnoack3000@gmail.com> <20221008111814.75251-1-gnoack3000@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: Use the LANDLOCK_ACCESS_FS_TRUNCATE flag in the tutorial. Adapt the backwards compatibility example and discussion to remove the truncation flag where needed. Point out potential surprising behaviour related to truncate. Signed-off-by: Günther Noack --- Documentation/userspace-api/landlock.rst | 67 +++++++++++++++++++++--- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst index cec780c2f497..d8cd8cd9ce25 100644 --- a/Documentation/userspace-api/landlock.rst +++ b/Documentation/userspace-api/landlock.rst @@ -8,7 +8,7 @@ Landlock: unprivileged access control ===================================== :Author: Mickaël Salaün -:Date: September 2022 +:Date: October 2022 The goal of Landlock is to enable to restrict ambient rights (e.g. global filesystem access) for a set of processes. Because Landlock is a stackable @@ -60,7 +60,8 @@ the need to be explicit about the denied-by-default access rights. LANDLOCK_ACCESS_FS_MAKE_FIFO | LANDLOCK_ACCESS_FS_MAKE_BLOCK | LANDLOCK_ACCESS_FS_MAKE_SYM | - LANDLOCK_ACCESS_FS_REFER, + LANDLOCK_ACCESS_FS_REFER | + LANDLOCK_ACCESS_FS_TRUNCATE, }; Because we may not know on which kernel version an application will be @@ -69,16 +70,28 @@ should try to protect users as much as possible whatever the kernel they are using. To avoid binary enforcement (i.e. either all security features or none), we can leverage a dedicated Landlock command to get the current version of the Landlock ABI and adapt the handled accesses. Let's check if we should -remove the ``LANDLOCK_ACCESS_FS_REFER`` access right which is only supported -starting with the second version of the ABI. +remove the ``LANDLOCK_ACCESS_FS_REFER`` or ``LANDLOCK_ACCESS_FS_TRUNCATE`` +access rights, which are only supported starting with the second and third +version of the ABI. .. code-block:: c int abi; abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); - if (abi < 2) { + if (abi < 0) { + /* Degrades gracefully if Landlock is not handled. */ + perror("The running kernel does not enable to use Landlock"); + return 0; + } + switch (abi) { + case 1: + /* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */ ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER; + __attribute__((fallthrough)); + case 2: + /* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */ + ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE; } This enables to create an inclusive ruleset that will contain our rules. @@ -127,8 +140,8 @@ descriptor. It may also be required to create rules following the same logic as explained for the ruleset creation, by filtering access rights according to the Landlock -ABI version. In this example, this is not required because -``LANDLOCK_ACCESS_FS_REFER`` is not allowed by any rule. +ABI version. In this example, this is not required because all of the requested +``allowed_access`` rights are already available in ABI 1. We now have a ruleset with one rule allowing read access to ``/usr`` while denying all other handled accesses for the filesystem. The next step is to @@ -252,6 +265,37 @@ To be allowed to use :manpage:`ptrace(2)` and related syscalls on a target process, a sandboxed process should have a subset of the target process rules, which means the tracee must be in a sub-domain of the tracer. +Truncating files +---------------- + +The operations covered by ``LANDLOCK_ACCESS_FS_WRITE_FILE`` and +``LANDLOCK_ACCESS_FS_TRUNCATE`` both change the contents of a file and sometimes +overlap in non-intuitive ways. It is recommended to always specify both of +these together. + +A particularly surprising example is :manpage:`creat(2)`. The name suggests +that this system call requires the rights to create and write files. However, +it also requires the truncate right if an existing file under the same name is +already present. + +It should also be noted that truncating files does not require the +``LANDLOCK_ACCESS_FS_WRITE_FILE`` right. Apart from the :manpage:`truncate(2)` +system call, this can also be done through :manpage:`open(2)` with the flags +``O_RDONLY | O_TRUNC``. + +When opening a file, the availability of the ``LANDLOCK_ACCESS_FS_TRUNCATE`` +right is associated with the newly created file descriptor and will be used for +subsequent truncation attempts using :manpage:`ftruncate(2)`. The behavior is +similar to opening a file for reading or writing, where permissions are checked +during :manpage:`open(2)`, but not during the subsequent :manpage:`read(2)` and +:manpage:`write(2)` calls. + +As a consequence, it is possible to have multiple open file descriptors for the +same file, where one grants the right to truncate the file and the other does +not. It is also possible to pass such file descriptors between processes, +keeping their Landlock properties, even when these processes do not have an +enforced Landlock ruleset. + Compatibility ============= @@ -398,6 +442,15 @@ Starting with the Landlock ABI version 2, it is now possible to securely control renaming and linking thanks to the new ``LANDLOCK_ACCESS_FS_REFER`` access right. +File truncation (ABI < 3) +------------------------- + +File truncation could not be denied before the third Landlock ABI, so it is +always allowed when using a kernel that only supports the first or second ABI. + +Starting with the Landlock ABI version 3, it is now possible to securely control +truncation thanks to the new ``LANDLOCK_ACCESS_FS_TRUNCATE`` access right. + .. _kernel_support: Kernel support