diff options
24 files changed, 2592 insertions, 706 deletions
diff --git a/Documentation/security/landlock.rst b/Documentation/security/landlock.rst index 3df68cb1d10f..5c77730b4479 100644 --- a/Documentation/security/landlock.rst +++ b/Documentation/security/landlock.rst @@ -7,7 +7,7 @@ Landlock LSM: kernel documentation ================================== :Author: Mickaël Salaün -:Date: March 2021 +:Date: May 2022 Landlock's goal is to create scoped access-control (i.e. sandboxing). To harden a whole system, this feature should be available to any process, @@ -42,6 +42,21 @@ Guiding principles for safe access controls * Computation related to Landlock operations (e.g. enforcing a ruleset) shall only impact the processes requesting them. +Design choices +============== + +Filesystem access rights +------------------------ + +All access rights are tied to an inode and what can be accessed through it. +Reading the content of a directory doesn't imply to be allowed to read the +content of a listed inode. Indeed, a file name is local to its parent +directory, and an inode can be referenced by multiple file names thanks to +(hard) links. Being able to unlink a file only has a direct impact on the +directory, not the unlinked inode. This is the reason why +`LANDLOCK_ACCESS_FS_REMOVE_FILE` or `LANDLOCK_ACCESS_FS_REFER` are not allowed +to be tied to files but only to directories. + Tests ===== diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst index f35552ff19ba..b8ea59493964 100644 --- a/Documentation/userspace-api/landlock.rst +++ b/Documentation/userspace-api/landlock.rst @@ -1,14 +1,14 @@ .. SPDX-License-Identifier: GPL-2.0 .. Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> .. Copyright © 2019-2020 ANSSI -.. Copyright © 2021 Microsoft Corporation +.. Copyright © 2021-2022 Microsoft Corporation ===================================== Landlock: unprivileged access control ===================================== :Author: Mickaël Salaün -:Date: March 2021 +:Date: May 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 @@ -18,6 +18,13 @@ is expected to help mitigate the security impact of bugs or unexpected/malicious behaviors in user space applications. Landlock empowers any process, including unprivileged ones, to securely restrict themselves. +We can quickly make sure that Landlock is enabled in the running system by +looking for "landlock: Up and running" in kernel logs (as root): ``dmesg | grep +landlock || journalctl -kg landlock`` . Developers can also easily check for +Landlock support with a :ref:`related system call <landlock_abi_versions>`. If +Landlock is not currently supported, we need to :ref:`configure the kernel +appropriately <kernel_support>`. + Landlock rules ============== @@ -29,14 +36,15 @@ the thread enforcing it, and its future children. Defining and enforcing a security policy ---------------------------------------- -We first need to create the ruleset that will contain our rules. For this +We first need to define the ruleset that will contain our rules. For this example, the ruleset will contain rules that only allow read actions, but write actions will be denied. The ruleset then needs to handle both of these kind of -actions. +actions. This is required for backward and forward compatibility (i.e. the +kernel and user space may not know each other's supported restrictions), hence +the need to be explicit about the denied-by-default access rights. .. code-block:: c - int ruleset_fd; struct landlock_ruleset_attr ruleset_attr = { .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE | @@ -51,9 +59,34 @@ actions. LANDLOCK_ACCESS_FS_MAKE_SOCK | LANDLOCK_ACCESS_FS_MAKE_FIFO | LANDLOCK_ACCESS_FS_MAKE_BLOCK | - LANDLOCK_ACCESS_FS_MAKE_SYM, + LANDLOCK_ACCESS_FS_MAKE_SYM | + LANDLOCK_ACCESS_FS_REFER, }; +Because we may not know on which kernel version an application will be +executed, it is safer to follow a best-effort security approach. Indeed, we +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. + +.. code-block:: c + + int abi; + + abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + if (abi < 2) { + ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER; + } + +This enables to create an inclusive ruleset that will contain our rules. + +.. code-block:: c + + int ruleset_fd; + ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); if (ruleset_fd < 0) { perror("Failed to create a ruleset"); @@ -92,6 +125,11 @@ descriptor. return 1; } +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. + 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 restrict the current thread from gaining more privileges (e.g. thanks to a SUID @@ -125,6 +163,27 @@ ruleset. Full working code can be found in `samples/landlock/sandboxer.c`_. +Good practices +-------------- + +It is recommended setting access rights to file hierarchy leaves as much as +possible. For instance, it is better to be able to have ``~/doc/`` as a +read-only hierarchy and ``~/tmp/`` as a read-write hierarchy, compared to +``~/`` as a read-only hierarchy and ``~/tmp/`` as a read-write hierarchy. +Following this good practice leads to self-sufficient hierarchies that don't +depend on their location (i.e. parent directories). This is particularly +relevant when we want to allow linking or renaming. Indeed, having consistent +access rights per directory enables to change the location of such directory +without relying on the destination directory access rights (except those that +are required for this operation, see `LANDLOCK_ACCESS_FS_REFER` documentation). +Having self-sufficient hierarchies also helps to tighten the required access +rights to the minimal set of data. This also helps avoid sinkhole directories, +i.e. directories where data can be linked to but not linked from. However, +this depends on data organization, which might not be controlled by developers. +In this case, granting read-write access to ``~/tmp/``, instead of write-only +access, would potentially allow to move ``~/tmp/`` to a non-readable directory +and still keep the ability to list the content of ``~/tmp/``. + Layers of file path access rights --------------------------------- @@ -192,6 +251,58 @@ 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. +Compatibility +============= + +Backward and forward compatibility +---------------------------------- + +Landlock is designed to be compatible with past and future versions of the +kernel. This is achieved thanks to the system call attributes and the +associated bitflags, particularly the ruleset's `handled_access_fs`. Making +handled access right explicit enables the kernel and user space to have a clear +contract with each other. This is required to make sure sandboxing will not +get stricter with a system update, which could break applications. + +Developers can subscribe to the `Landlock mailing list +<https://subspace.kernel.org/lists.linux.dev.html>`_ to knowingly update and +test their applications with the latest available features. In the interest of +users, and because they may use different kernel versions, it is strongly +encouraged to follow a best-effort security approach by checking the Landlock +ABI version at runtime and only enforcing the supported features. + +.. _landlock_abi_versions: + +Landlock ABI versions +--------------------- + +The Landlock ABI version can be read with the sys_landlock_create_ruleset() +system call: + +.. code-block:: c + + int abi; + + abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + if (abi < 0) { + switch (errno) { + case ENOSYS: + printf("Landlock is not supported by the current kernel.\n"); + break; + case EOPNOTSUPP: + printf("Landlock is currently disabled.\n"); + break; + } + return 0; + } + if (abi >= 2) { + printf("Landlock supports LANDLOCK_ACCESS_FS_REFER.\n"); + } + +The following kernel interfaces are implicitly supported by the first ABI +version. Features only supported from a specific version are explicitly marked +as such. + Kernel interface ================ @@ -228,21 +339,6 @@ Enforcing a ruleset Current limitations =================== -File renaming and linking -------------------------- - -Because Landlock targets unprivileged access controls, it is needed to properly -handle composition of rules. Such property also implies rules nesting. -Properly handling multiple layers of ruleset, each one of them able to restrict -access to files, also implies to inherit the ruleset restrictions from a parent -to its hierarchy. Because files are identified and restricted by their -hierarchy, moving or linking a file from one directory to another implies to -propagate the hierarchy constraints. To protect against privilege escalations -through renaming or linking, and for the sake of simplicity, Landlock currently -limits linking and renaming to the same directory. Future Landlock evolutions -will enable more flexibility for renaming and linking, with dedicated ruleset -flags. - Filesystem topology modification -------------------------------- @@ -267,8 +363,8 @@ restrict such paths with dedicated ruleset flags. Ruleset layers -------------- -There is a limit of 64 layers of stacked rulesets. This can be an issue for a -task willing to enforce a new ruleset in complement to its 64 inherited +There is a limit of 16 layers of stacked rulesets. This can be an issue for a +task willing to enforce a new ruleset in complement to its 16 inherited rulesets. Once this limit is reached, sys_landlock_restrict_self() returns E2BIG. It is then strongly suggested to carefully build rulesets once in the life of a thread, especially for applications able to launch other applications @@ -281,6 +377,44 @@ Memory usage Kernel memory allocated to create rulesets is accounted and can be restricted by the Documentation/admin-guide/cgroup-v1/memory.rst. +Previous limitations +==================== + +File renaming and linking (ABI 1) +--------------------------------- + +Because Landlock targets unprivileged access controls, it needs to properly +handle composition of rules. Such property also implies rules nesting. +Properly handling multiple layers of rulesets, each one of them able to +restrict access to files, also implies inheritance of the ruleset restrictions +from a parent to its hierarchy. Because files are identified and restricted by +their hierarchy, moving or linking a file from one directory to another implies +propagation of the hierarchy constraints, or restriction of these actions +according to the potentially lost constraints. To protect against privilege +escalations through renaming or linking, and for the sake of simplicity, +Landlock previously limited linking and renaming to the same directory. +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. + +.. _kernel_support: + +Kernel support +============== + +Landlock was first introduced in Linux 5.13 but it must be configured at build +time with `CONFIG_SECURITY_LANDLOCK=y`. Landlock must also be enabled at boot +time as the other security modules. The list of security modules enabled by +default is set with `CONFIG_LSM`. The kernel configuration should then +contains `CONFIG_LSM=landlock,[...]` with `[...]` as the list of other +potentially useful security modules for the running system (see the +`CONFIG_LSM` help). + +If the running kernel doesn't have `landlock` in `CONFIG_LSM`, then we can +still enable it by adding ``lsm=landlock,[...]`` to +Documentation/admin-guide/kernel-parameters.rst thanks to the bootloader +configuration. + Questions and answers ===================== diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index db924fe379c9..eafa1d2489fd 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -100,7 +100,7 @@ LSM_HOOK(int, 0, path_link, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) LSM_HOOK(int, 0, path_rename, const struct path *old_dir, struct dentry *old_dentry, const struct path *new_dir, - struct dentry *new_dentry) + struct dentry *new_dentry, unsigned int flags) LSM_HOOK(int, 0, path_chmod, const struct path *path, umode_t mode) LSM_HOOK(int, 0, path_chown, const struct path *path, kuid_t uid, kgid_t gid) LSM_HOOK(int, 0, path_chroot, const struct path *path) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 47cdf3fbecef..91c8146649f5 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -358,6 +358,7 @@ * @old_dentry contains the dentry structure of the old link. * @new_dir contains the path structure for parent of the new link. * @new_dentry contains the dentry structure of the new link. + * @flags may contain rename options such as RENAME_EXCHANGE. * Return 0 if permission is granted. * @path_chmod: * Check for permission to change a mode of the file @path. The new diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index b3d952067f59..23df4e0e8ace 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -21,8 +21,14 @@ struct landlock_ruleset_attr { /** * @handled_access_fs: Bitmask of actions (cf. `Filesystem flags`_) * that is handled by this ruleset and should then be forbidden if no - * rule explicitly allow them. This is needed for backward - * compatibility reasons. + * rule explicitly allow them: it is a deny-by-default list that should + * contain as much Landlock access rights as possible. Indeed, all + * Landlock filesystem access rights that are not part of + * handled_access_fs are allowed. This is needed for backward + * compatibility reasons. One exception is the + * LANDLOCK_ACCESS_FS_REFER access right, which is always implicitly + * handled, but must still be explicitly handled to add new rules with + * this access right. */ __u64 handled_access_fs; }; @@ -33,7 +39,9 @@ struct landlock_ruleset_attr { * - %LANDLOCK_CREATE_RULESET_VERSION: Get the highest supported Landlock ABI * version. */ +/* clang-format off */ #define LANDLOCK_CREATE_RULESET_VERSION (1U << 0) +/* clang-format on */ /** * enum landlock_rule_type - Landlock rule type @@ -60,8 +68,9 @@ struct landlock_path_beneath_attr { */ __u64 allowed_access; /** - * @parent_fd: File descriptor, open with ``O_PATH``, which identifies - * the parent directory of a file hierarchy, or just a file. + * @parent_fd: File descriptor, preferably opened with ``O_PATH``, + * which identifies the parent directory of a file hierarchy, or just a + * file. */ __s32 parent_fd; /* @@ -109,6 +118,22 @@ struct landlock_path_beneath_attr { * - %LANDLOCK_ACCESS_FS_MAKE_FIFO: Create (or rename or link) a named pipe. * - %LANDLOCK_ACCESS_FS_MAKE_BLOCK: Create (or rename or link) a block device. * - %LANDLOCK_ACCESS_FS_MAKE_SYM: Create (or rename or link) a symbolic link. + * - %LANDLOCK_ACCESS_FS_REFER: Link or rename a file from or to a different + * directory (i.e. reparent a file hierarchy). This access right is + * available since the second version of the Landlock ABI. This is also the + * only access right which is always considered handled by any ruleset in + * such a way that reparenting a file hierarchy is always denied by default. + * To avoid privilege escalation, it is not enough to add a rule with this + * access right. When linking or renaming a file, the destination directory + * hierarchy must also always have the same or a superset of restrictions of + * the source hierarchy. If it is not the case, or if the domain doesn't + * handle this access right, such actions are denied by default with errno + * set to EXDEV. Linking also requires a LANDLOCK_ACCESS_FS_MAKE_* access + * right on the destination directory, and renaming also requires a + * LANDLOCK_ACCESS_FS_REMOVE_* access right on the source's (file or + * directory) parent. Otherwise, such actions are denied with errno set to + * EACCES. The EACCES errno prevails over EXDEV to let user space + * efficiently deal with an unrecoverable error. * * .. warning:: * @@ -120,6 +145,7 @@ struct landlock_path_beneath_attr { * :manpage:`access(2)`. * Future Landlock evolutions will enable to restrict them. */ +/* clang-format off */ #define LANDLOCK_ACCESS_FS_EXECUTE (1ULL << 0) #define LANDLOCK_ACCESS_FS_WRITE_FILE (1ULL << 1) #define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2) @@ -133,5 +159,7 @@ struct landlock_path_beneath_attr { #define LANDLOCK_ACCESS_FS_MAKE_FIFO (1ULL << 10) #define LANDLOCK_ACCESS_FS_MAKE_BLOCK (1ULL << 11) #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12) +#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13) +/* clang-format on */ #endif /* _UAPI_LINUX_LANDLOCK_H */ diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c index 8859fc193542..3e404e51ec64 100644 --- a/samples/landlock/sandboxer.c +++ b/samples/landlock/sandboxer.c @@ -22,9 +22,9 @@ #include <unistd.h> #ifndef landlock_create_ruleset -static inline int landlock_create_ruleset( - const struct landlock_ruleset_attr *const attr, - const size_t size, const __u32 flags) +static inline int +landlock_create_ruleset(const struct landlock_ruleset_attr *const attr, + const size_t size, const __u32 flags) { return syscall(__NR_landlock_create_ruleset, attr, size, flags); } @@ -32,17 +32,18 @@ static inline int landlock_create_ruleset( #ifndef landlock_add_rule static inline int landlock_add_rule(const int ruleset_fd, - const enum landlock_rule_type rule_type, - const void *const rule_attr, const __u32 flags) + const enum landlock_rule_type rule_type, + const void *const rule_attr, + const __u32 flags) { - return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, - rule_attr, flags); + return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, + flags); } #endif #ifndef landlock_restrict_self static inline int landlock_restrict_self(const int ruleset_fd, - const __u32 flags) + const __u32 flags) { return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); } @@ -70,14 +71,17 @@ static int parse_path(char *env_path, const char ***const path_list) return num_paths; } +/* clang-format off */ + #define ACCESS_FILE ( \ LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE) -static int populate_ruleset( - const char *const env_var, const int ruleset_fd, - const __u64 allowed_access) +/* clang-format on */ + +static int populate_ruleset(const char *const env_var, const int ruleset_fd, + const __u64 allowed_access) { int num_paths, i, ret = 1; char *env_path_name; @@ -107,12 +111,10 @@ static int populate_ruleset( for (i = 0; i < num_paths; i++) { struct stat statbuf; - path_beneath.parent_fd = open(path_list[i], O_PATH | - O_CLOEXEC); + path_beneath.parent_fd = open(path_list[i], O_PATH | O_CLOEXEC); if (path_beneath.parent_fd < 0) { fprintf(stderr, "Failed to open \"%s\": %s\n", - path_list[i], - strerror(errno)); + path_list[i], strerror(errno)); goto out_free_name; } if (fstat(path_beneath.parent_fd, &statbuf)) { @@ -123,9 +125,10 @@ static int populate_ruleset( if (!S_ISDIR(statbuf.st_mode)) path_beneath.allowed_access &= ACCESS_FILE; if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, - &path_beneath, 0)) { - fprintf(stderr, "Failed to update the ruleset with \"%s\": %s\n", - path_list[i], strerror(errno)); + &path_beneath, 0)) { + fprintf(stderr, + "Failed to update the ruleset with \"%s\": %s\n", + path_list[i], strerror(errno)); close(path_beneath.parent_fd); goto out_free_name; } @@ -139,6 +142,8 @@ out_free_name: return ret; } +/* clang-format off */ + #define ACCESS_FS_ROUGHLY_READ ( \ LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_READ_FILE | \ @@ -154,64 +159,89 @@ out_free_name: LANDLOCK_ACCESS_FS_MAKE_SOCK | \ LANDLOCK_ACCESS_FS_MAKE_FIFO | \ LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ - LANDLOCK_ACCESS_FS_MAKE_SYM) + LANDLOCK_ACCESS_FS_MAKE_SYM | \ + LANDLOCK_ACCESS_FS_REFER) + +#define ACCESS_ABI_2 ( \ + LANDLOCK_ACCESS_FS_REFER) + +/* clang-format on */ int main(const int argc, char *const argv[], char *const *const envp) { const char *cmd_path; char *const *cmd_argv; - int ruleset_fd; + int ruleset_fd, abi; + __u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ, + access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE; struct landlock_ruleset_attr ruleset_attr = { - .handled_access_fs = ACCESS_FS_ROUGHLY_READ | - ACCESS_FS_ROUGHLY_WRITE, + .handled_access_fs = access_fs_rw, }; if (argc < 2) { - fprintf(stderr, "usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n", - ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); - fprintf(stderr, "Launch a command in a restricted environment.\n\n"); + fprintf(stderr, + "usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n", + ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); + fprintf(stderr, + "Launch a command in a restricted environment.\n\n"); fprintf(stderr, "Environment variables containing paths, " "each separated by a colon:\n"); - fprintf(stderr, "* %s: list of paths allowed to be used in a read-only way.\n", - ENV_FS_RO_NAME); - fprintf(stderr, "* %s: list of paths allowed to be used in a read-write way.\n", - ENV_FS_RW_NAME); - fprintf(stderr, "\nexample:\n" - "%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" " - "%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " - "%s bash -i\n", - ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); + fprintf(stderr, + "* %s: list of paths allowed to be used in a read-only way.\n", + ENV_FS_RO_NAME); + fprintf(stderr, + "* %s: list of paths allowed to be used in a read-write way.\n", + ENV_FS_RW_NAME); + fprintf(stderr, + "\nexample:\n" + "%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" " + "%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " + "%s bash -i\n", + ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); return 1; } - ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); - if (ruleset_fd < 0) { + abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + if (abi < 0) { const int err = errno; - perror("Failed to create a ruleset"); + perror("Failed to check Landlock compatibility"); switch (err) { case ENOSYS: - fprintf(stderr, "Hint: Landlock is not supported by the current kernel. " - "To support it, build the kernel with " - "CONFIG_SECURITY_LANDLOCK=y and prepend " - "\"landlock,\" to the content of CONFIG_LSM.\n"); + fprintf(stderr, + "Hint: Landlock is not supported by the current kernel. " + "To support it, build the kernel with " + "CONFIG_SECURITY_LANDLOCK=y and prepend " + "\"landlock,\" to the content of CONFIG_LSM.\n"); break; case EOPNOTSUPP: - fprintf(stderr, "Hint: Landlock is currently disabled. " - "It can be enabled in the kernel configuration by " - "prepending \"landlock,\" to the content of CONFIG_LSM, " - "or at boot time by setting the same content to the " - "\"lsm\" kernel parameter.\n"); + fprintf(stderr, + "Hint: Landlock is currently disabled. " + "It can be enabled in the kernel configuration by " + "prepending \"landlock,\" to the content of CONFIG_LSM, " + "or at boot time by setting the same content to the " + "\"lsm\" kernel parameter.\n"); break; } return 1; } - if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, - ACCESS_FS_ROUGHLY_READ)) { + /* Best-effort security. */ + if (abi < 2) { + ruleset_attr.handled_access_fs &= ~ACCESS_ABI_2; + access_fs_ro &= ~ACCESS_ABI_2; + access_fs_rw &= ~ACCESS_ABI_2; + } + + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + if (ruleset_fd < 0) { + perror("Failed to create a ruleset"); + return 1; + } + if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) { goto err_close_ruleset; } - if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, - ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE)) { + if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) { goto err_close_ruleset; } if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { @@ -228,7 +258,7 @@ int main(const int argc, char *const argv[], char *const *const envp) cmd_argv = argv + 1; execvpe(cmd_path, cmd_argv, envp); fprintf(stderr, "Failed to execute \"%s\": %s\n", cmd_path, - strerror(errno)); + strerror(errno)); fprintf(stderr, "Hint: access to the binary, the interpreter or " "shared libraries may be denied.\n"); return 1; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 4f0eecb67dde..900bc540656a 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -354,13 +354,16 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_ } static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_dentry, - const struct path *new_dir, struct dentry *new_dentry) + const struct path *new_dir, struct dentry *new_dentry, + const unsigned int flags) { struct aa_label *label; int error = 0; if (!path_mediated_fs(old_dentry)) return 0; + if ((flags & RENAME_EXCHANGE) && !path_mediated_fs(new_dentry)) + return 0; label = begin_current_label_crit_section(); if (!unconfined(label)) { @@ -374,10 +377,27 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d d_backing_inode(old_dentry)->i_mode }; - error = aa_path_perm(OP_RENAME_SRC, label, &old_path, 0, - MAY_READ | AA_MAY_GETATTR | MAY_WRITE | - AA_MAY_SETATTR | AA_MAY_DELETE, - &cond); + if (flags & RENAME_EXCHANGE) { + struct path_cond cond_exchange = { + i_uid_into_mnt(mnt_userns, d_backing_inode(new_dentry)), + d_backing_inode(new_dentry)->i_mode + }; + + error = aa_path_perm(OP_RENAME_SRC, label, &new_path, 0, + MAY_READ | AA_MAY_GETATTR | MAY_WRITE | + AA_MAY_SETATTR | AA_MAY_DELETE, + &cond_exchange); + if (!error) + error = aa_path_perm(OP_RENAME_DEST, label, &old_path, + 0, MAY_WRITE | AA_MAY_SETATTR | + AA_MAY_CREATE, &cond_exchange); + } + + if (!error) + error = aa_path_perm(OP_RENAME_SRC, label, &old_path, 0, + MAY_READ | AA_MAY_GETATTR | MAY_WRITE | + AA_MAY_SETATTR | AA_MAY_DELETE, + &cond); if (!error) error = aa_path_perm(OP_RENAME_DEST, label, &new_path, 0, MAY_WRITE | AA_MAY_SETATTR | diff --git a/security/landlock/cred.c b/security/landlock/cred.c index 6725af24c684..ec6c37f04a19 100644 --- a/security/landlock/cred.c +++ b/security/landlock/cred.c @@ -15,7 +15,7 @@ #include "setup.h" static int hook_cred_prepare(struct cred *const new, - const struct cred *const old, const gfp_t gfp) + const struct cred *const old, const gfp_t gfp) { struct landlock_ruleset *const old_dom = landlock_cred(old)->domain; @@ -42,5 +42,5 @@ static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { __init void landlock_add_cred_hooks(void) { security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), - LANDLOCK_NAME); + LANDLOCK_NAME); } diff --git a/security/landlock/cred.h b/security/landlock/cred.h index 5f99d3decade..af89ab00e6d1 100644 --- a/security/landlock/cred.h +++ b/security/landlock/cred.h @@ -20,8 +20,8 @@ struct landlock_cred_security { struct landlock_ruleset *domain; }; -static inline struct landlock_cred_security *landlock_cred( - const struct cred *cred) +static inline struct landlock_cred_security * +landlock_cred(const struct cred *cred) { return cred->security + landlock_blob_sizes.lbs_cred; } @@ -34,8 +34,8 @@ static inline const struct landlock_ruleset *landlock_get_current_domain(void) /* * The call needs to come from an RCU read-side critical section. */ -static inline const struct landlock_ruleset *landlock_get_task_domain( - const struct task_struct *const task) +static inline const struct landlock_ruleset * +landlock_get_task_domain(const struct task_struct *const task) { return landlock_cred(__task_cred(task))->domain; } diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 97b8e421f617..ec5a6247cd3e 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -4,6 +4,7 @@ * * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> * Copyright © 2018-2020 ANSSI + * Copyright © 2021-2022 Microsoft Corporation */ #include <linux/atomic.h> @@ -141,23 +142,26 @@ retry: } /* All access rights that can be tied to files. */ +/* clang-format off */ #define ACCESS_FILE ( \ LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE) +/* clang-format on */ /* * @path: Should have been checked by get_path_from_fd(). */ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset, - const struct path *const path, u32 access_rights) + const struct path *const path, + access_mask_t access_rights) { int err; struct landlock_object *object; /* Files only get access rights that make sense. */ - if (!d_is_dir(path->dentry) && (access_rights | ACCESS_FILE) != - ACCESS_FILE) + if (!d_is_dir(path->dentry) && + (access_rights | ACCESS_FILE) != ACCESS_FILE) return -EINVAL; if (WARN_ON_ONCE(ruleset->num_layers != 1)) return -EINVAL; @ |
