Commit cb44e4f0 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'landlock-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux

Pull Landlock updates from Mickaël Salaün:

 - improve the path_rename LSM hook implementations for RENAME_EXCHANGE;

 - fix a too-restrictive filesystem control for a rare corner case;

 - set the nested sandbox limitation to 16 layers;

 - add a new LANDLOCK_ACCESS_FS_REFER access right to properly handle
   file reparenting (i.e. full rename and link support);

 - add new tests and documentation;

 - format code with clang-format to make it easier to maintain and
   contribute.

* tag 'landlock-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux: (30 commits)
  landlock: Explain how to support Landlock
  landlock: Add design choices documentation for filesystem access rights
  landlock: Document good practices about filesystem policies
  landlock: Document LANDLOCK_ACCESS_FS_REFER and ABI versioning
  samples/landlock: Add support for file reparenting
  selftests/landlock: Add 11 new test suites dedicated to file reparenting
  landlock: Add support for file reparenting with LANDLOCK_ACCESS_FS_REFER
  LSM: Remove double path_rename hook calls for RENAME_EXCHANGE
  landlock: Move filesystem helpers and add a new one
  landlock: Fix same-layer rule unions
  landlock: Create find_rule() from unmask_layers()
  landlock: Reduce the maximum number of layers to 16
  landlock: Define access_mask_t to enforce a consistent access mask size
  selftests/landlock: Test landlock_create_ruleset(2) argument check ordering
  landlock: Change landlock_restrict_self(2) check ordering
  landlock: Change landlock_add_rule(2) argument check ordering
  selftests/landlock: Add tests for O_PATH
  selftests/landlock: Fully test file rename with "remove" access
  selftests/landlock: Extend access right tests to directories
  selftests/landlock: Add tests for unknown access rights
  ...
parents efd1df19 5e469829
...@@ -7,7 +7,7 @@ Landlock LSM: kernel documentation ...@@ -7,7 +7,7 @@ Landlock LSM: kernel documentation
================================== ==================================
:Author: Mickaël Salaün :Author: Mickaël Salaün
:Date: March 2021 :Date: May 2022
Landlock's goal is to create scoped access-control (i.e. sandboxing). To 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, harden a whole system, this feature should be available to any process,
...@@ -42,6 +42,21 @@ Guiding principles for safe access controls ...@@ -42,6 +42,21 @@ Guiding principles for safe access controls
* Computation related to Landlock operations (e.g. enforcing a ruleset) shall * Computation related to Landlock operations (e.g. enforcing a ruleset) shall
only impact the processes requesting them. 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 Tests
===== =====
......
This diff is collapsed.
...@@ -100,7 +100,7 @@ LSM_HOOK(int, 0, path_link, struct dentry *old_dentry, ...@@ -100,7 +100,7 @@ LSM_HOOK(int, 0, path_link, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry) const struct path *new_dir, struct dentry *new_dentry)
LSM_HOOK(int, 0, path_rename, const struct path *old_dir, LSM_HOOK(int, 0, path_rename, const struct path *old_dir,
struct dentry *old_dentry, const struct path *new_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_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_chown, const struct path *path, kuid_t uid, kgid_t gid)
LSM_HOOK(int, 0, path_chroot, const struct path *path) LSM_HOOK(int, 0, path_chroot, const struct path *path)
......
...@@ -358,6 +358,7 @@ ...@@ -358,6 +358,7 @@
* @old_dentry contains the dentry structure of the old link. * @old_dentry contains the dentry structure of the old link.
* @new_dir contains the path structure for parent of the new link. * @new_dir contains the path structure for parent of the new link.
* @new_dentry contains the dentry structure 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. * Return 0 if permission is granted.
* @path_chmod: * @path_chmod:
* Check for permission to change a mode of the file @path. The new * Check for permission to change a mode of the file @path. The new
......
...@@ -21,8 +21,14 @@ struct landlock_ruleset_attr { ...@@ -21,8 +21,14 @@ struct landlock_ruleset_attr {
/** /**
* @handled_access_fs: Bitmask of actions (cf. `Filesystem flags`_) * @handled_access_fs: Bitmask of actions (cf. `Filesystem flags`_)
* that is handled by this ruleset and should then be forbidden if no * that is handled by this ruleset and should then be forbidden if no
* rule explicitly allow them. This is needed for backward * rule explicitly allow them: it is a deny-by-default list that should
* compatibility reasons. * 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; __u64 handled_access_fs;
}; };
...@@ -33,7 +39,9 @@ struct landlock_ruleset_attr { ...@@ -33,7 +39,9 @@ struct landlock_ruleset_attr {
* - %LANDLOCK_CREATE_RULESET_VERSION: Get the highest supported Landlock ABI * - %LANDLOCK_CREATE_RULESET_VERSION: Get the highest supported Landlock ABI
* version. * version.
*/ */
/* clang-format off */
#define LANDLOCK_CREATE_RULESET_VERSION (1U << 0) #define LANDLOCK_CREATE_RULESET_VERSION (1U << 0)
/* clang-format on */
/** /**
* enum landlock_rule_type - Landlock rule type * enum landlock_rule_type - Landlock rule type
...@@ -60,8 +68,9 @@ struct landlock_path_beneath_attr { ...@@ -60,8 +68,9 @@ struct landlock_path_beneath_attr {
*/ */
__u64 allowed_access; __u64 allowed_access;
/** /**
* @parent_fd: File descriptor, open with ``O_PATH``, which identifies * @parent_fd: File descriptor, preferably opened with ``O_PATH``,
* the parent directory of a file hierarchy, or just a file. * which identifies the parent directory of a file hierarchy, or just a
* file.
*/ */
__s32 parent_fd; __s32 parent_fd;
/* /*
...@@ -109,6 +118,22 @@ struct landlock_path_beneath_attr { ...@@ -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_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_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_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:: * .. warning::
* *
...@@ -120,6 +145,7 @@ struct landlock_path_beneath_attr { ...@@ -120,6 +145,7 @@ struct landlock_path_beneath_attr {
* :manpage:`access(2)`. * :manpage:`access(2)`.
* Future Landlock evolutions will enable to restrict them. * Future Landlock evolutions will enable to restrict them.
*/ */
/* clang-format off */
#define LANDLOCK_ACCESS_FS_EXECUTE (1ULL << 0) #define LANDLOCK_ACCESS_FS_EXECUTE (1ULL << 0)
#define LANDLOCK_ACCESS_FS_WRITE_FILE (1ULL << 1) #define LANDLOCK_ACCESS_FS_WRITE_FILE (1ULL << 1)
#define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2) #define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2)
...@@ -133,5 +159,7 @@ struct landlock_path_beneath_attr { ...@@ -133,5 +159,7 @@ struct landlock_path_beneath_attr {
#define LANDLOCK_ACCESS_FS_MAKE_FIFO (1ULL << 10) #define LANDLOCK_ACCESS_FS_MAKE_FIFO (1ULL << 10)
#define LANDLOCK_ACCESS_FS_MAKE_BLOCK (1ULL << 11) #define LANDLOCK_ACCESS_FS_MAKE_BLOCK (1ULL << 11)
#define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12) #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12)
#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
/* clang-format on */
#endif /* _UAPI_LINUX_LANDLOCK_H */ #endif /* _UAPI_LINUX_LANDLOCK_H */
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
#include <unistd.h> #include <unistd.h>
#ifndef landlock_create_ruleset #ifndef landlock_create_ruleset
static inline int landlock_create_ruleset( static inline int
const struct landlock_ruleset_attr *const attr, landlock_create_ruleset(const struct landlock_ruleset_attr *const attr,
const size_t size, const __u32 flags) const size_t size, const __u32 flags)
{ {
return syscall(__NR_landlock_create_ruleset, attr, size, flags); return syscall(__NR_landlock_create_ruleset, attr, size, flags);
...@@ -33,10 +33,11 @@ static inline int landlock_create_ruleset( ...@@ -33,10 +33,11 @@ static inline int landlock_create_ruleset(
#ifndef landlock_add_rule #ifndef landlock_add_rule
static inline int landlock_add_rule(const int ruleset_fd, static inline int landlock_add_rule(const int ruleset_fd,
const enum landlock_rule_type rule_type, const enum landlock_rule_type rule_type,
const void *const rule_attr, const __u32 flags) const void *const rule_attr,
const __u32 flags)
{ {
return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr,
rule_attr, flags); flags);
} }
#endif #endif
...@@ -70,13 +71,16 @@ static int parse_path(char *env_path, const char ***const path_list) ...@@ -70,13 +71,16 @@ static int parse_path(char *env_path, const char ***const path_list)
return num_paths; return num_paths;
} }
/* clang-format off */
#define ACCESS_FILE ( \ #define ACCESS_FILE ( \
LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_EXECUTE | \
LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \
LANDLOCK_ACCESS_FS_READ_FILE) LANDLOCK_ACCESS_FS_READ_FILE)
static int populate_ruleset( /* clang-format on */
const char *const env_var, const int ruleset_fd,
static int populate_ruleset(const char *const env_var, const int ruleset_fd,
const __u64 allowed_access) const __u64 allowed_access)
{ {
int num_paths, i, ret = 1; int num_paths, i, ret = 1;
...@@ -107,12 +111,10 @@ static int populate_ruleset( ...@@ -107,12 +111,10 @@ static int populate_ruleset(
for (i = 0; i < num_paths; i++) { for (i = 0; i < num_paths; i++) {
struct stat statbuf; struct stat statbuf;
path_beneath.parent_fd = open(path_list[i], O_PATH | path_beneath.parent_fd = open(path_list[i], O_PATH | O_CLOEXEC);
O_CLOEXEC);
if (path_beneath.parent_fd < 0) { if (path_beneath.parent_fd < 0) {
fprintf(stderr, "Failed to open \"%s\": %s\n", fprintf(stderr, "Failed to open \"%s\": %s\n",
path_list[i], path_list[i], strerror(errno));
strerror(errno));
goto out_free_name; goto out_free_name;
} }
if (fstat(path_beneath.parent_fd, &statbuf)) { if (fstat(path_beneath.parent_fd, &statbuf)) {
...@@ -124,7 +126,8 @@ static int populate_ruleset( ...@@ -124,7 +126,8 @@ static int populate_ruleset(
path_beneath.allowed_access &= ACCESS_FILE; path_beneath.allowed_access &= ACCESS_FILE;
if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
&path_beneath, 0)) { &path_beneath, 0)) {
fprintf(stderr, "Failed to update the ruleset with \"%s\": %s\n", fprintf(stderr,
"Failed to update the ruleset with \"%s\": %s\n",
path_list[i], strerror(errno)); path_list[i], strerror(errno));
close(path_beneath.parent_fd); close(path_beneath.parent_fd);
goto out_free_name; goto out_free_name;
...@@ -139,6 +142,8 @@ static int populate_ruleset( ...@@ -139,6 +142,8 @@ static int populate_ruleset(
return ret; return ret;
} }
/* clang-format off */
#define ACCESS_FS_ROUGHLY_READ ( \ #define ACCESS_FS_ROUGHLY_READ ( \
LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_EXECUTE | \
LANDLOCK_ACCESS_FS_READ_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE | \
...@@ -154,29 +159,41 @@ static int populate_ruleset( ...@@ -154,29 +159,41 @@ static int populate_ruleset(
LANDLOCK_ACCESS_FS_MAKE_SOCK | \ LANDLOCK_ACCESS_FS_MAKE_SOCK | \
LANDLOCK_ACCESS_FS_MAKE_FIFO | \ LANDLOCK_ACCESS_FS_MAKE_FIFO | \
LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 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) int main(const int argc, char *const argv[], char *const *const envp)
{ {
const char *cmd_path; const char *cmd_path;
char *const *cmd_argv; 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 = { struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = ACCESS_FS_ROUGHLY_READ | .handled_access_fs = access_fs_rw,
ACCESS_FS_ROUGHLY_WRITE,
}; };
if (argc < 2) { if (argc < 2) {
fprintf(stderr, "usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n", fprintf(stderr,
"usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n",
ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
fprintf(stderr, "Launch a command in a restricted environment.\n\n"); fprintf(stderr,
"Launch a command in a restricted environment.\n\n");
fprintf(stderr, "Environment variables containing paths, " fprintf(stderr, "Environment variables containing paths, "
"each separated by a colon:\n"); "each separated by a colon:\n");
fprintf(stderr, "* %s: list of paths allowed to be used in a read-only way.\n", fprintf(stderr,
"* %s: list of paths allowed to be used in a read-only way.\n",
ENV_FS_RO_NAME); ENV_FS_RO_NAME);
fprintf(stderr, "* %s: list of paths allowed to be used in a read-write way.\n", fprintf(stderr,
"* %s: list of paths allowed to be used in a read-write way.\n",
ENV_FS_RW_NAME); ENV_FS_RW_NAME);
fprintf(stderr, "\nexample:\n" fprintf(stderr,
"\nexample:\n"
"%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" " "%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" "
"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " "%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
"%s bash -i\n", "%s bash -i\n",
...@@ -184,20 +201,22 @@ int main(const int argc, char *const argv[], char *const *const envp) ...@@ -184,20 +201,22 @@ int main(const int argc, char *const argv[], char *const *const envp)
return 1; return 1;
} }
ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
if (ruleset_fd < 0) { if (abi < 0) {
const int err = errno; const int err = errno;
perror("Failed to create a ruleset"); perror("Failed to check Landlock compatibility");
switch (err) { switch (err) {
case ENOSYS: case ENOSYS:
fprintf(stderr, "Hint: Landlock is not supported by the current kernel. " fprintf(stderr,
"Hint: Landlock is not supported by the current kernel. "
"To support it, build the kernel with " "To support it, build the kernel with "
"CONFIG_SECURITY_LANDLOCK=y and prepend " "CONFIG_SECURITY_LANDLOCK=y and prepend "
"\"landlock,\" to the content of CONFIG_LSM.\n"); "\"landlock,\" to the content of CONFIG_LSM.\n");
break; break;
case EOPNOTSUPP: case EOPNOTSUPP:
fprintf(stderr, "Hint: Landlock is currently disabled. " fprintf(stderr,
"Hint: Landlock is currently disabled. "
"It can be enabled in the kernel configuration by " "It can be enabled in the kernel configuration by "
"prepending \"landlock,\" to the content of CONFIG_LSM, " "prepending \"landlock,\" to the content of CONFIG_LSM, "
"or at boot time by setting the same content to the " "or at boot time by setting the same content to the "
...@@ -206,12 +225,23 @@ int main(const int argc, char *const argv[], char *const *const envp) ...@@ -206,12 +225,23 @@ int main(const int argc, char *const argv[], char *const *const envp)
} }
return 1; return 1;
} }
if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, /* Best-effort security. */
ACCESS_FS_ROUGHLY_READ)) { 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; goto err_close_ruleset;
} }
if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) {
ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE)) {
goto err_close_ruleset; goto err_close_ruleset;
} }
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
......
...@@ -354,13 +354,16 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_ ...@@ -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, 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; struct aa_label *label;
int error = 0; int error = 0;
if (!path_mediated_fs(old_dentry)) if (!path_mediated_fs(old_dentry))
return 0; return 0;
if ((flags & RENAME_EXCHANGE) && !path_mediated_fs(new_dentry))
return 0;
label = begin_current_label_crit_section(); label = begin_current_label_crit_section();
if (!unconfined(label)) { if (!unconfined(label)) {
...@@ -374,6 +377,23 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d ...@@ -374,6 +377,23 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
d_backing_inode(old_dentry)->i_mode d_backing_inode(old_dentry)->i_mode
}; };
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, error = aa_path_perm(OP_RENAME_SRC, label, &old_path, 0,
MAY_READ | AA_MAY_GETATTR | MAY_WRITE | MAY_READ | AA_MAY_GETATTR | MAY_WRITE |
AA_MAY_SETATTR | AA_MAY_DELETE, AA_MAY_SETATTR | AA_MAY_DELETE,
......
...@@ -20,8 +20,8 @@ struct landlock_cred_security { ...@@ -20,8 +20,8 @@ struct landlock_cred_security {
struct landlock_ruleset *domain; struct landlock_ruleset *domain;
}; };
static inline struct landlock_cred_security *landlock_cred( static inline struct landlock_cred_security *
const struct cred *cred) landlock_cred(const struct cred *cred)
{ {
return cred->security + landlock_blob_sizes.lbs_cred; return cred->security + landlock_blob_sizes.lbs_cred;
} }
...@@ -34,8 +34,8 @@ static inline const struct landlock_ruleset *landlock_get_current_domain(void) ...@@ -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. * The call needs to come from an RCU read-side critical section.
*/ */
static inline const struct landlock_ruleset *landlock_get_task_domain( static inline const struct landlock_ruleset *
const struct task_struct *const task) landlock_get_task_domain(const struct task_struct *const task)
{ {
return landlock_cred(__task_cred(task))->domain; return landlock_cred(__task_cred(task))->domain;
} }
......
This diff is collapsed.
...@@ -50,14 +50,14 @@ struct landlock_superblock_security { ...@@ -50,14 +50,14 @@ struct landlock_superblock_security {
atomic_long_t inode_refs; atomic_long_t inode_refs;
}; };
static inline struct landlock_inode_security *landlock_inode( static inline struct landlock_inode_security *
const struct inode *const inode) landlock_inode(const struct inode *const inode)
{ {
return inode->i_security + landlock_blob_sizes.lbs_inode; return inode->i_security + landlock_blob_sizes.lbs_inode;
} }
static inline struct landlock_superblock_security *landlock_superblock( static inline struct landlock_superblock_security *
const struct super_block *const superblock) landlock_superblock(const struct super_block *const superblock)
{ {
return superblock->s_security + landlock_blob_sizes.lbs_superblock; return superblock->s_security + landlock_blob_sizes.lbs_superblock;
} }
...@@ -65,6 +65,7 @@ static inline struct landlock_superblock_security *landlock_superblock( ...@@ -65,6 +65,7 @@ static inline struct landlock_superblock_security *landlock_superblock(
__init void landlock_add_fs_hooks(void); __init void landlock_add_fs_hooks(void);
int landlock_append_fs_rule(struct landlock_ruleset *const ruleset, int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
const struct path *const path, u32 access_hierarchy); const struct path *const path,
access_mask_t access_hierarchy);
#endif /* _SECURITY_LANDLOCK_FS_H */ #endif /* _SECURITY_LANDLOCK_FS_H */
...@@ -9,13 +9,19 @@ ...@@ -9,13 +9,19 @@
#ifndef _SECURITY_LANDLOCK_LIMITS_H #ifndef _SECURITY_LANDLOCK_LIMITS_H
#define _SECURITY_LANDLOCK_LIMITS_H #define _SECURITY_LANDLOCK_LIMITS_H
#include <linux/bitops.h>
#include <linux/limits.h> #include <linux/limits.h>
#include <uapi/linux/landlock.h> #include <uapi/linux/landlock.h>
#define LANDLOCK_MAX_NUM_LAYERS 64 /* clang-format off */
#define LANDLOCK_MAX_NUM_LAYERS 16
#define LANDLOCK_MAX_NUM_RULES U32_MAX #define LANDLOCK_MAX_NUM_RULES U32_MAX
#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_MAKE_SYM #define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_REFER
#define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1) #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
#define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
/* clang-format on */
#endif /* _SECURITY_LANDLOCK_LIMITS_H */ #endif /* _SECURITY_LANDLOCK_LIMITS_H */
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
#include "object.h" #include "object.h"
struct landlock_object *landlock_create_object( struct landlock_object *
const struct landlock_object_underops *const underops, landlock_create_object(const struct landlock_object_underops *const underops,
void *const underobj) void *const underobj)
{ {
struct landlock_object *new_object; struct landlock_object *new_object;
......
...@@ -76,8 +76,8 @@ struct landlock_object { ...@@ -76,8 +76,8 @@ struct landlock_object {
}; };
}; };
struct landlock_object *landlock_create_object( struct landlock_object *
const struct landlock_object_underops *const underops, landlock_create_object(const struct landlock_object_underops *const underops,
void *const underobj); void *const underobj);
void landlock_put_object(struct landlock_object *const object); void landlock_put_object(struct landlock_object *const object);
......
...@@ -28,8 +28,9 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers) ...@@ -28,8 +28,9 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
{ {
struct landlock_ruleset *new_ruleset; struct landlock_ruleset *new_ruleset;
new_ruleset = kzalloc(struct_size(new_ruleset, fs_access_masks, new_ruleset =
num_layers), GFP_KERNEL_ACCOUNT); kzalloc(struct_size(new_ruleset, fs_access_masks, num_layers),
GFP_KERNEL_ACCOUNT);
if (!new_ruleset) if (!new_ruleset)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
refcount_set(&new_ruleset->usage, 1); refcount_set(&new_ruleset->usage, 1);
...@@ -44,7 +45,8 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers) ...@@ -44,7 +45,8 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
return new_ruleset; return new_ruleset;
} }
struct landlock_ruleset *landlock_create_ruleset(const u32 fs_access_mask) struct landlock_ruleset *
landlock_create_ruleset(const access_mask_t fs_access_mask)
{ {
struct landlock_ruleset *new_ruleset; struct landlock_ruleset *new_ruleset;
...@@ -66,10 +68,9 @@ static void build_check_rule(void) ...@@ -66,10 +68,9 @@ static void build_check_rule(void)
BUILD_BUG_ON(rule.num_layers < LANDLOCK_MAX_NUM_LAYERS); BUILD_BUG_ON(rule.num_layers < LANDLOCK_MAX_NUM_LAYERS);
} }
static struct landlock_rule *create_rule( static struct landlock_rule *
struct landlock_object *const object, create_rule(struct landlock_object *const object,
const struct landlock_layer (*const layers)[], const struct landlock_layer (*const layers)[], const u32 num_layers,
const u32 num_layers,
const struct landlock_layer *const new_layer) const struct landlock_layer *const new_layer)
{ {
struct landlock_rule *new_rule; struct landlock_rule *new_rule;
...@@ -156,8 +157,8 @@ static int insert_rule(struct landlock_ruleset *const ruleset, ...@@ -156,8 +157,8 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
return -ENOENT; return -ENOENT;
walker_node = &(ruleset->root.rb_node); walker_node = &(ruleset->root.rb_node);
while (*walker_node) { while (*walker_node) {
struct landlock_rule *const this = rb_entry(*walker_node, struct landlock_rule *const this =
struct landlock_rule, node); rb_entry(*walker_node, struct landlock_rule, node);
if (this->object != object) { if (this->object != object) {
parent_node = *walker_node; parent_node = *walker_node;
...@@ -228,13 +229,14 @@ static void build_check_layer(void) ...@@ -228,13 +229,14 @@ static void build_check_layer(void)
/* @ruleset must be locked by the caller. */ /* @ruleset must be locked by the caller. */
int landlock_insert_rule(struct landlock_ruleset *const ruleset, int landlock_insert_rule(struct landlock_ruleset *const ruleset,
struct landlock_object *const object, const u32 access) struct landlock_object *const object,
const access_mask_t access)
{ {
struct landlock_layer layers[] = {{ struct landlock_layer layers[] = { {
.access = access, .access = access,
/* When @level is zero, insert_rule() extends @ruleset. */ /* When @level is zero, insert_rule() extends @ruleset. */
.level = 0, .level = 0,
}}; } };
build_check_layer(); build_check_layer();
return insert_rule(ruleset, object, &layers, ARRAY_SIZE(layers)); return insert_rule(ruleset, object, &layers, ARRAY_SIZE(layers));
...@@ -282,11 +284,11 @@ static int merge_ruleset(struct landlock_ruleset *const dst, ...@@ -282,11 +284,11 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
dst->fs_access_masks[dst->num_layers - 1] = src->fs_access_masks[0]; dst->fs_access_masks[dst->num_layers - 1] = src->fs_access_masks[0];
/* Merges the @src tree. */ /* Merges the @src tree. */
rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, &src->root,
&src->root, node) { node) {
struct landlock_layer layers[] = {{ struct landlock_layer layers[] = { {
.level = dst->num_layers, .level = dst->num_layers,
}}; } };
if (WARN_ON_ONCE(walker_rule->num_layers != 1)) { if (WARN_ON_ONCE(walker_rule->num_layers != 1)) {
err = -EINVAL; err = -EINVAL;
...@@ -327,7 +329,8 @@ static int inherit_ruleset(struct landlock_ruleset *const parent, ...@@ -327,7 +329,8 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
&parent->root, node) { &parent->root, node) {
err = insert_rule(child, walker_rule->object, err = insert_rule(child, walker_rule->object,
&walker_rule->layers, walker_rule->num_layers); &walker_rule->layers,
walker_rule->num_layers);
if (err) if (err)
goto out_unlock; goto out_unlock;
} }
...@@ -358,8 +361,7 @@ static void free_ruleset(struct landlock_ruleset *const ruleset) ...@@ -358,8 +361,7 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
struct landlock_rule *freeme, *next; struct landlock_rule *freeme, *next;
might_sleep(); might_sleep();
rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root, rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root, node)
node)
free_rule(freeme); free_rule(freeme);
put_hierarchy(ruleset->hierarchy); put_hierarchy(ruleset->hierarchy);
kfree(ruleset); kfree(ruleset);
...@@ -397,8 +399,8 @@ void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset) ...@@ -397,8 +399,8 @@ void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset)
* Returns the intersection of @parent and @ruleset, or returns @parent if * Returns the intersection of @parent and @ruleset, or returns @parent if
* @ruleset is empty, or returns a duplicate of @ruleset if @parent is empty. * @ruleset is empty, or returns a duplicate of @ruleset if @parent is empty.
*/ */
struct landlock_ruleset *landlock_merge_ruleset( struct landlock_ruleset *
struct landlock_ruleset *const parent, landlock_merge_ruleset(struct landlock_ruleset *const parent,
struct landlock_ruleset *const ruleset) struct landlock_ruleset *const ruleset)
{ {
struct landlock_ruleset *new_dom; struct landlock_ruleset *new_dom;
...@@ -421,8 +423,8 @@ struct landlock_ruleset *landlock_merge_ruleset( ...@@ -421,8 +423,8 @@ struct landlock_ruleset *landlock_merge_ruleset(
new_dom = create_ruleset(num_layers); new_dom = create_ruleset(num_layers);
if (IS_ERR(new_dom)) if (IS_ERR(new_dom))
return new_dom; return new_dom;
new_dom->hierarchy = kzalloc(sizeof(*new_dom->hierarchy), new_dom->hierarchy =
GFP_KERNEL_ACCOUNT); kzalloc(sizeof(*new_dom->hierarchy), GFP_KERNEL_ACCOUNT);
if (!new_dom->hierarchy) { if (!new_dom->hierarchy) {
err = -ENOMEM; err = -ENOMEM;
goto out_put_dom; goto out_put_dom;
...@@ -449,8 +451,8 @@ struct landlock_ruleset *landlock_merge_ruleset( ...@@ -449,8 +451,8 @@ struct landlock_ruleset *landlock_merge_ruleset(
/* /*
* The returned access has the same lifetime as @ruleset. * The returned access has the same lifetime as @ruleset.
*/ */
const struct landlock_rule *landlock_find_rule( const struct landlock_rule *
const struct landlock_ruleset *const ruleset, landlock_find_rule(const struct landlock_ruleset *const ruleset,
const struct landlock_object *const object) const struct landlock_object *const object)
{ {
const struct rb_node *node; const struct rb_node *node;
...@@ -459,8 +461,8 @@ const struct landlock_rule *landlock_find_rule( ...@@ -459,8 +461,8 @@ const struct landlock_rule *landlock_find_rule(
return NULL; return NULL;
node = ruleset->root.rb_node; node = ruleset->root.rb_node;
while (node) { while (node) {
struct landlock_rule *this = rb_entry(node, struct landlock_rule *this =
struct landlock_rule, node); rb_entry(node, struct landlock_rule, node);
if (this->object == object) if (this->object == object)
return this; return this;
......
...@@ -9,13 +9,26 @@ ...@@ -9,13 +9,26 @@
#ifndef _SECURITY_LANDLOCK_RULESET_H #ifndef _SECURITY_LANDLOCK_RULESET_H
#define _SECURITY_LANDLOCK_RULESET_H #define _SECURITY_LANDLOCK_RULESET_H
#include <linux/bitops.h>
#include <linux/build_bug.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/refcount.h> #include <linux/refcount.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include "limits.h"
#include "object.h" #include "object.h"
typedef u16 access_mask_t;
/* Makes sure all filesystem access rights can be stored. */
static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
/* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */
static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
typedef u16 layer_mask_t;
/* Makes sure all layers can be checked. */
static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS);
/** /**
* struct landlock_layer - Access rights for a given layer * struct landlock_layer - Access rights for a given layer
*/ */
...@@ -28,7 +41,7 @@ struct landlock_layer { ...@@ -28,7 +41,7 @@ struct landlock_layer {
* @access: Bitfield of allowed actions on the kernel object. They are * @access: Bitfield of allowed actions on the kernel object. They are
* relative to the object type (e.g. %LANDLOCK_ACTION_FS_READ). * relative to the object type (e.g. %LANDLOCK_ACTION_FS_READ).
*/ */
u16 access; access_mask_t access;
}; };
/** /**
...@@ -135,25 +148,27 @@ struct landlock_ruleset { ...@@ -135,25 +148,27 @@ struct landlock_ruleset {
* layers are set once and never changed for the * layers are set once and never changed for the
* lifetime of the ruleset. * lifetime of the ruleset.
*/ */
u16 fs_access_masks[]; access_mask_t fs_access_masks[];
}; };
}; };
}; };
struct landlock_ruleset *landlock_create_ruleset(const u32 fs_access_mask); struct landlock_ruleset *
landlock_create_ruleset(const access_mask_t fs_access_mask);
void landlock_put_ruleset(struct landlock_ruleset *const ruleset); void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset); void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
int landlock_insert_rule(struct landlock_ruleset *const ruleset, int landlock_insert_rule(struct landlock_ruleset *const ruleset,
struct landlock_object *const object, const u32 access); struct landlock_object *const object,
const access_mask_t access);
struct landlock_ruleset *landlock_merge_ruleset( struct landlock_ruleset *
struct landlock_ruleset *const parent, landlock_merge_ruleset(struct landlock_ruleset *const parent,
struct landlock_ruleset *const ruleset); struct landlock_ruleset *const ruleset);
const struct landlock_rule *landlock_find_rule( const struct landlock_rule *
const struct landlock_ruleset *const ruleset, landlock_find_rule(const struct landlock_ruleset *const ruleset,
const struct landlock_object *const object); const struct landlock_object *const object);
static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset) static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
......
...@@ -43,9 +43,10 @@ ...@@ -43,9 +43,10 @@
* @src: User space pointer or NULL. * @src: User space pointer or NULL.
* @usize: (Alleged) size of the data pointed to by @src. * @usize: (Alleged) size of the data pointed to by @src.
*/ */
static __always_inline int copy_min_struct_from_user(void *const dst, static __always_inline int
const size_t ksize, const size_t ksize_min, copy_min_struct_from_user(void *const dst, const size_t ksize,
const void __user *const src, const size_t usize) const size_t ksize_min, const void __user *const src,
const size_t usize)
{ {
/* Checks buffer inconsistencies. */ /* Checks buffer inconsistencies. */
BUILD_BUG_ON(!dst); BUILD_BUG_ON(!dst);
...@@ -128,7 +129,7 @@ static const struct file_operations ruleset_fops = { ...@@ -128,7 +129,7 @@ static const struct file_operations ruleset_fops = {
.write = fop_dummy_write, .write = fop_dummy_write,
}; };
#define LANDLOCK_ABI_VERSION 1 #define LANDLOCK_ABI_VERSION 2
/** /**
* sys_landlock_create_ruleset - Create a new ruleset * sys_landlock_create_ruleset - Create a new ruleset
...@@ -168,15 +169,16 @@ SYSCALL_DEFINE3(landlock_create_ruleset, ...@@ -168,15 +169,16 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (flags) { if (flags) {
if ((flags == LANDLOCK_CREATE_RULESET_VERSION) if ((flags == LANDLOCK_CREATE_RULESET_VERSION) && !attr &&
&& !attr && !size) !size)
return LANDLOCK_ABI_VERSION; return LANDLOCK_ABI_VERSION;
return -EINVAL; return -EINVAL;
} }
/* Copies raw user space buffer. */ /* Copies raw user space buffer. */
err = copy_min_struct_from_user(&ruleset_attr, sizeof(ruleset_attr), err = copy_min_struct_from_user(&ruleset_attr, sizeof(ruleset_attr),
offsetofend(typeof(ruleset_attr), handled_access_fs), offsetofend(typeof(ruleset_attr),
handled_access_fs),
attr, size); attr, size);
if (err) if (err)
return err; return err;
...@@ -244,8 +246,8 @@ static int get_path_from_fd(const s32 fd, struct path *const path) ...@@ -244,8 +246,8 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
struct fd f; struct fd f;
int err = 0; int err = 0;
BUILD_BUG_ON(!__same_type(fd, BUILD_BUG_ON(!__same_type(
((struct landlock_path_beneath_attr *)NULL)->parent_fd)); fd, ((struct landlock_path_beneath_attr *)NULL)->parent_fd));
/* Handles O_PATH. */ /* Handles O_PATH. */
f = fdget_raw(fd); f = fdget_raw(fd);
...@@ -290,19 +292,18 @@ static int get_path_from_fd(const s32 fd, struct path *const path) ...@@ -290,19 +292,18 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
* *
* - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
* - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e. * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
* &landlock_path_beneath_attr.allowed_access is not a subset of the rule's * &landlock_path_beneath_attr.allowed_access is not a subset of the
* accesses); * ruleset handled accesses);
* - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access); * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
* - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
* member of @rule_attr is not a file descriptor as expected; * member of @rule_attr is not a file descriptor as expected;
* - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
* @rule_attr is not the expected file descriptor type (e.g. file open * @rule_attr is not the expected file descriptor type;
* without O_PATH);
* - EPERM: @ruleset_fd has no write access to the underlying ruleset; * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
* - EFAULT: @rule_attr inconsistency. * - EFAULT: @rule_attr inconsistency.
*/ */
SYSCALL_DEFINE4(landlock_add_rule, SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
const int, ruleset_fd, const enum landlock_rule_type, rule_type, const enum landlock_rule_type, rule_type,
const void __user *const, rule_attr, const __u32, flags) const void __user *const, rule_attr, const __u32, flags)
{ {
struct landlock_path_beneath_attr path_beneath_attr; struct landlock_path_beneath_attr path_beneath_attr;
...@@ -317,20 +318,24 @@ SYSCALL_DEFINE4(landlock_add_rule, ...@@ -317,20 +318,24 @@ SYSCALL_DEFINE4(landlock_add_rule,
if (flags) if (flags)
return -EINVAL; return -EINVAL;
if (rule_type != LANDLOCK_RULE_PATH_BENEATH)
return -EINVAL;
/* Copies raw user space buffer, only one type for now. */
res = copy_from_user(&path_beneath_attr, rule_attr,
sizeof(path_beneath_attr));
if (res)
return -EFAULT;
/* Gets and checks the ruleset. */ /* Gets and checks the ruleset. */
ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE); ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
if (IS_ERR(ruleset)) if (IS_ERR(ruleset))
return PTR_ERR(ruleset); return PTR_ERR(ruleset);
if (rule_type != LANDLOCK_RULE_PATH_BENEATH) {
err = -EINVAL;
goto out_put_ruleset;
}
/* Copies raw user space buffer, only one type for now. */
res = copy_from_user(&path_beneath_attr, rule_attr,
sizeof(path_beneath_attr));
if (res) {
err = -EFAULT;
goto out_put_ruleset;
}
/* /*
* Informs about useless rule: empty allowed_access (i.e. deny rules) * Informs about useless rule: empty allowed_access (i.e. deny rules)
* are ignored in path walks. * are ignored in path walks.
...@@ -389,8 +394,8 @@ SYSCALL_DEFINE4(landlock_add_rule, ...@@ -389,8 +394,8 @@ SYSCALL_DEFINE4(landlock_add_rule,
* - E2BIG: The maximum number of stacked rulesets is reached for the current * - E2BIG: The maximum number of stacked rulesets is reached for the current
* thread. * thread.
*/ */
SYSCALL_DEFINE2(landlock_restrict_self, SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
const int, ruleset_fd, const __u32, flags) flags)
{ {
struct landlock_ruleset *new_dom, *ruleset; struct landlock_ruleset *new_dom, *ruleset;
struct cred *new_cred; struct cred *new_cred;
...@@ -400,10 +405,6 @@ SYSCALL_DEFINE2(landlock_restrict_self, ...@@ -400,10 +405,6 @@ SYSCALL_DEFINE2(landlock_restrict_self,
if (!landlock_initialized) if (!landlock_initialized)
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* No flag for now. */
if (flags)
return -EINVAL;
/* /*
* Similar checks as for seccomp(2), except that an -EPERM may be * Similar checks as for seccomp(2), except that an -EPERM may be
* returned. * returned.
...@@ -412,6 +413,10 @@ SYSCALL_DEFINE2(landlock_restrict_self, ...@@ -412,6 +413,10 @@ SYSCALL_DEFINE2(landlock_restrict_self,
!ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN)) !ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
/* No flag for now. */
if (flags)
return -EINVAL;
/* Gets and checks the ruleset. */ /* Gets and checks the ruleset. */
ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ); ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ);
if (IS_ERR(ruleset)) if (IS_ERR(ruleset))
......
...@@ -1198,15 +1198,8 @@ int security_path_rename(const struct path *old_dir, struct dentry *old_dentry, ...@@ -1198,15 +1198,8 @@ int security_path_rename(const struct path *old_dir, struct dentry *old_dentry,
(d_is_positive(new_dentry) && IS_PRIVATE(d_backing_inode(new_dentry))))) (d_is_positive(new_dentry) && IS_PRIVATE(d_backing_inode(new_dentry)))))
return 0; return 0;
if (flags & RENAME_EXCHANGE) {
int err = call_int_hook(path_rename, 0, new_dir, new_dentry,
old_dir, old_dentry);
if (err)
return err;
}
return call_int_hook(path_rename, 0, old_dir, old_dentry, new_dir, return call_int_hook(path_rename, 0, old_dir, old_dentry, new_dir,
new_dentry); new_dentry, flags);
} }
EXPORT_SYMBOL(security_path_rename); EXPORT_SYMBOL(security_path_rename);
......
...@@ -264,17 +264,26 @@ static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_di ...@@ -264,17 +264,26 @@ static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_di
* @old_dentry: Pointer to "struct dentry". * @old_dentry: Pointer to "struct dentry".
* @new_parent: Pointer to "struct path". * @new_parent: Pointer to "struct path".
* @new_dentry: Pointer to "struct dentry". * @new_dentry: Pointer to "struct dentry".
* @flags: Rename options.
* *
* Returns 0 on success, negative value otherwise. * Returns 0 on success, negative value otherwise.
*/ */
static int tomoyo_path_rename(const struct path *old_parent, static int tomoyo_path_rename(const struct path *old_parent,
struct dentry *old_dentry, struct dentry *old_dentry,
const struct path *new_parent, const struct path *new_parent,
struct dentry *new_dentry) struct dentry *new_dentry,
const unsigned int flags)
{ {
struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry }; struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry };
struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry }; struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry };
if (flags & RENAME_EXCHANGE) {
const int err = tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path2,
&path1);
if (err)
return err;
}
return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2); return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
} }
......
...@@ -21,7 +21,8 @@ ...@@ -21,7 +21,8 @@
#define O_PATH 010000000 #define O_PATH 010000000
#endif #endif
TEST(inconsistent_attr) { TEST(inconsistent_attr)
{
const long page_size = sysconf(_SC_PAGESIZE); const long page_size = sysconf(_SC_PAGESIZE);
char *const buf = malloc(page_size + 1); char *const buf = malloc(page_size + 1);
struct landlock_ruleset_attr *const ruleset_attr = (void *)buf; struct landlock_ruleset_attr *const ruleset_attr = (void *)buf;
...@@ -34,19 +35,25 @@ TEST(inconsistent_attr) { ...@@ -34,19 +35,25 @@ TEST(inconsistent_attr) {
ASSERT_EQ(EINVAL, errno); ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 1, 0)); ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 1, 0));
ASSERT_EQ(EINVAL, errno); ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 7, 0));
ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(-1, landlock_create_ruleset(NULL, 1, 0)); ASSERT_EQ(-1, landlock_create_ruleset(NULL, 1, 0));
/* The size if less than sizeof(struct landlock_attr_enforce). */ /* The size if less than sizeof(struct landlock_attr_enforce). */
ASSERT_EQ(EFAULT, errno); ASSERT_EQ(EFAULT, errno);
ASSERT_EQ(-1, landlock_create_ruleset(NULL, ASSERT_EQ(-1, landlock_create_ruleset(
sizeof(struct landlock_ruleset_attr), 0)); NULL, sizeof(struct landlock_ruleset_attr), 0));
ASSERT_EQ(EFAULT, errno); ASSERT_EQ(EFAULT, errno);
ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size + 1, 0)); ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size + 1, 0));
ASSERT_EQ(E2BIG, errno); ASSERT_EQ(E2BIG, errno);
ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, /* Checks minimal valid attribute size. */
ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 8, 0));
ASSERT_EQ(ENOMSG, errno);
ASSERT_EQ(-1, landlock_create_ruleset(
ruleset_attr,
sizeof(struct landlock_ruleset_attr), 0)); sizeof(struct landlock_ruleset_attr), 0));
ASSERT_EQ(ENOMSG, errno); ASSERT_EQ(ENOMSG, errno);
ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size, 0)); ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size, 0));
...@@ -63,11 +70,12 @@ TEST(inconsistent_attr) { ...@@ -63,11 +70,12 @@ TEST(inconsistent_attr) {
free(buf); free(buf);
} }
TEST(abi_version) { TEST(abi_version)
{
const struct landlock_ruleset_attr ruleset_attr = { const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
}; };
ASSERT_EQ(1, landlock_create_ruleset(NULL, 0, ASSERT_EQ(2, landlock_create_ruleset(NULL, 0,
LANDLOCK_CREATE_RULESET_VERSION)); LANDLOCK_CREATE_RULESET_VERSION));
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
...@@ -78,23 +86,28 @@ TEST(abi_version) { ...@@ -78,23 +86,28 @@ TEST(abi_version) {
LANDLOCK_CREATE_RULESET_VERSION)); LANDLOCK_CREATE_RULESET_VERSION));
ASSERT_EQ(EINVAL, errno); ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, ASSERT_EQ(-1,
sizeof(ruleset_attr), landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr),
LANDLOCK_CREATE_RULESET_VERSION)); LANDLOCK_CREATE_RULESET_VERSION));
ASSERT_EQ(EINVAL, errno); ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0, ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0,
LANDLOCK_CREATE_RULESET_VERSION | 1 << 31)); LANDLOCK_CREATE_RULESET_VERSION |
1 << 31));
ASSERT_EQ(EINVAL, errno); ASSERT_EQ(EINVAL, errno);
} }
TEST(inval_create_ruleset_flags) { /* Tests ordering of syscall argument checks. */
TEST(create_ruleset_checks_ordering)
{
const int last_flag = LANDLOCK_CREATE_RULESET_VERSION; const int last_flag = LANDLOCK_CREATE_RULESET_VERSION;
const int invalid_flag = last_flag << 1; const int invalid_flag = last_flag << 1;
int ruleset_fd;
const struct landlock_ruleset_attr ruleset_attr = { const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
}; };
/* Checks priority for invalid flags. */
ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0, invalid_flag)); ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0, invalid_flag));
ASSERT_EQ(EINVAL, errno); ASSERT_EQ(EINVAL, errno);
...@@ -105,41 +118,118 @@ TEST(inval_create_ruleset_flags) { ...@@ -105,41 +118,118 @@ TEST(inval_create_ruleset_flags) {
invalid_flag)); invalid_flag));
ASSERT_EQ(EINVAL, errno); ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, ASSERT_EQ(-1,
sizeof(ruleset_attr), invalid_flag)); landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr),
invalid_flag));
ASSERT_EQ(EINVAL, errno); ASSERT_EQ(EINVAL, errno);
/* Checks too big ruleset_attr size. */
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, -1, 0));
ASSERT_EQ(E2BIG, errno);
/* Checks too small ruleset_attr size. */
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, 0));
ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 1, 0));
ASSERT_EQ(EINVAL, errno);
/* Checks valid call. */
ruleset_fd =
landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd);
ASSERT_EQ(0, close(ruleset_fd));
} }
TEST(empty_path_beneath_attr) { /* Tests ordering of syscall argument checks. */
TEST(add_rule_checks_ordering)
{
const struct landlock_ruleset_attr ruleset_attr = { const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE, .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,
}; };
const int ruleset_fd = landlock_create_ruleset(&ruleset_attr, struct landlock_path_beneath_attr path_beneath_attr = {
sizeof(ruleset_attr), 0); .allowed_access = LANDLOCK_ACCESS_FS_EXECUTE,
.parent_fd = -1,
};
const int ruleset_fd =
landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd); ASSERT_LE(0, ruleset_fd);
/* Similar to struct landlock_path_beneath_attr.parent_fd = 0 */ /* Checks invalid flags. */
ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 1));
ASSERT_EQ(EINVAL, errno);
/* Checks invalid ruleset FD. */
ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 0));
ASSERT_EQ(EBADF, errno);
/* Checks invalid rule type. */
ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, 0, NULL, 0));
ASSERT_EQ(EINVAL, errno);
/* Checks invalid rule attr. */
ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
NULL, 0)); NULL, 0));
ASSERT_EQ(EFAULT, errno); ASSERT_EQ(EFAULT, errno);
/* Checks invalid path_beneath.parent_fd. */
ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
&path_beneath_attr, 0));
ASSERT_EQ(EBADF, errno);
/* Checks valid call. */
path_beneath_attr.parent_fd =
open("/tmp", O_PATH | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC);
ASSERT_LE(0, path_beneath_attr.parent_fd);
ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
&path_beneath_attr, 0));
ASSERT_EQ(0, close(path_beneath_attr.parent_fd));
ASSERT_EQ(0, close(ruleset_fd)); ASSERT_EQ(0, close(ruleset_fd));
} }
TEST(inval_fd_enforce) { /* Tests ordering of syscall argument and permission checks. */
TEST(restrict_self_checks_ordering)
{
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,
};
struct landlock_path_beneath_attr path_beneath_attr = {
.allowed_access = LANDLOCK_ACCESS_FS_EXECUTE,
.parent_fd = -1,
};
const int ruleset_fd =
landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd);
path_beneath_attr.parent_fd =
open("/tmp", O_PATH | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC);
ASSERT_LE(0, path_beneath_attr.parent_fd);
ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
&path_beneath_attr, 0));
ASSERT_EQ(0, close(path_beneath_attr.parent_fd));
/* Checks unprivileged enforcement without no_new_privs. */
drop_caps(_metadata);
ASSERT_EQ(-1, landlock_restrict_self(-1, -1));
ASSERT_EQ(EPERM, errno);
ASSERT_EQ(-1, landlock_restrict_self(-1, 0));
ASSERT_EQ(EPERM, errno);
ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0));
ASSERT_EQ(EPERM, errno);
ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
/* Checks invalid flags. */
ASSERT_EQ(-1, landlock_restrict_self(-1, -1));
ASSERT_EQ(EINVAL, errno);
/* Checks invalid ruleset FD. */
ASSERT_EQ(-1, landlock_restrict_self(-1, 0)); ASSERT_EQ(-1, landlock_restrict_self(-1, 0));
ASSERT_EQ(EBADF, errno); ASSERT_EQ(EBADF, errno);
}
TEST(unpriv_enforce_without_no_new_privs) {
int err;
drop_caps(_metadata); /* Checks valid call. */
err = landlock_restrict_self(-1, 0); ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
ASSERT_EQ(EPERM, errno); ASSERT_EQ(0, close(ruleset_fd));
ASSERT_EQ(err, -1);
} }
TEST(ruleset_fd_io) TEST(ruleset_fd_io)
...@@ -151,8 +241,8 @@ TEST(ruleset_fd_io) ...@@ -151,8 +241,8 @@ TEST(ruleset_fd_io)
char buf; char buf;
drop_caps(_metadata); drop_caps(_metadata);
ruleset_fd = landlock_create_ruleset(&ruleset_attr, ruleset_fd =
sizeof(ruleset_attr), 0); landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd); ASSERT_LE(0, ruleset_fd);
ASSERT_EQ(-1, write(ruleset_fd, ".", 1)); ASSERT_EQ(-1, write(ruleset_fd, ".", 1));
...@@ -197,13 +287,14 @@ TEST(ruleset_fd_transfer) ...@@ -197,13 +287,14 @@ TEST(ruleset_fd_transfer)
drop_caps(_metadata); drop_caps(_metadata);
/* Creates a test ruleset with a simple rule. */ /* Creates a test ruleset with a simple rule. */
ruleset_fd_tx = landlock_create_ruleset(&ruleset_attr, ruleset_fd_tx =
sizeof(ruleset_attr), 0); landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd_tx); ASSERT_LE(0, ruleset_fd_tx);
path_beneath_attr.parent_fd = open("/tmp", O_PATH | O_NOFOLLOW | path_beneath_attr.parent_fd =
O_DIRECTORY | O_CLOEXEC); open("/tmp", O_PATH | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC);
ASSERT_LE(0, path_beneath_attr.parent_fd); ASSERT_LE(0, path_beneath_attr.parent_fd);
ASSERT_EQ(0, landlock_add_rule(ruleset_fd_tx, LANDLOCK_RULE_PATH_BENEATH, ASSERT_EQ(0,
landlock_add_rule(ruleset_fd_tx, LANDLOCK_RULE_PATH_BENEATH,
&path_beneath_attr, 0)); &path_beneath_attr, 0));
ASSERT_EQ(0, close(path_beneath_attr.parent_fd)); ASSERT_EQ(0, close(path_beneath_attr.parent_fd));
...@@ -215,7 +306,8 @@ TEST(ruleset_fd_transfer) ...@@ -215,7 +306,8 @@ TEST(ruleset_fd_transfer)
memcpy(CMSG_DATA(cmsg), &ruleset_fd_tx, sizeof(ruleset_fd_tx)); memcpy(CMSG_DATA(cmsg), &ruleset_fd_tx, sizeof(ruleset_fd_tx));
/* Sends the ruleset FD over a socketpair and then close it. */ /* 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(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
socket_fds));
ASSERT_EQ(sizeof(data_tx), sendmsg(socket_fds[0], &msg, 0)); ASSERT_EQ(sizeof(data_tx), sendmsg(socket_fds[0], &msg, 0));
ASSERT_EQ(0, close(socket_fds[0])); ASSERT_EQ(0, close(socket_fds[0]));
ASSERT_EQ(0, close(ruleset_fd_tx)); ASSERT_EQ(0, close(ruleset_fd_tx));
...@@ -226,7 +318,8 @@ TEST(ruleset_fd_transfer) ...@@ -226,7 +318,8 @@ TEST(ruleset_fd_transfer)
int ruleset_fd_rx; int ruleset_fd_rx;
*(char *)msg.msg_iov->iov_base = '\0'; *(char *)msg.msg_iov->iov_base = '\0';
ASSERT_EQ(sizeof(data_tx), recvmsg(socket_fds[1], &msg, MSG_CMSG_CLOEXEC)); ASSERT_EQ(sizeof(data_tx),
recvmsg(socket_fds[1], &msg, MSG_CMSG_CLOEXEC));
ASSERT_EQ('.', *(char *)msg.msg_iov->iov_base); ASSERT_EQ('.', *(char *)msg.msg_iov->iov_base);
ASSERT_EQ(0, close(socket_fds[1])); ASSERT_EQ(0, close(socket_fds[1]));
cmsg = CMSG_FIRSTHDR(&msg); cmsg = CMSG_FIRSTHDR(&msg);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
* this to be possible, we must not call abort() but instead exit smoothly * this to be possible, we must not call abort() but instead exit smoothly
* (hence the step print). * (hence the step print).
*/ */
/* clang-format off */
#define TEST_F_FORK(fixture_name, test_name) \ #define TEST_F_FORK(fixture_name, test_name) \
static void fixture_name##_##test_name##_child( \ static void fixture_name##_##test_name##_child( \
struct __test_metadata *_metadata, \ struct __test_metadata *_metadata, \
...@@ -71,10 +72,11 @@ ...@@ -71,10 +72,11 @@
FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \ FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
const FIXTURE_VARIANT(fixture_name) \ const FIXTURE_VARIANT(fixture_name) \
__attribute__((unused)) *variant) __attribute__((unused)) *variant)
/* clang-format on */
#ifndef landlock_create_ruleset #ifndef landlock_create_ruleset
static inline int landlock_create_ruleset( static inline int
const struct landlock_ruleset_attr *const attr, landlock_create_ruleset(const struct landlock_ruleset_attr *const attr,
const size_t size, const __u32 flags) const size_t size, const __u32 flags)
{ {
return syscall(__NR_landlock_create_ruleset, attr, size, flags); return syscall(__NR_landlock_create_ruleset, attr, size, flags);
...@@ -84,10 +86,11 @@ static inline int landlock_create_ruleset( ...@@ -84,10 +86,11 @@ static inline int landlock_create_ruleset(
#ifndef landlock_add_rule #ifndef landlock_add_rule
static inline int landlock_add_rule(const int ruleset_fd, static inline int landlock_add_rule(const int ruleset_fd,
const enum landlock_rule_type rule_type, const enum landlock_rule_type rule_type,
const void *const rule_attr, const __u32 flags) const void *const rule_attr,
const __u32 flags)
{ {
return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr,
rule_attr, flags); flags);
} }
#endif #endif
...@@ -111,35 +114,40 @@ static void _init_caps(struct __test_metadata *const _metadata, bool drop_all) ...@@ -111,35 +114,40 @@ static void _init_caps(struct __test_metadata *const _metadata, bool drop_all)
}; };
cap_p = cap_get_proc(); cap_p = cap_get_proc();
EXPECT_NE(NULL, cap_p) { EXPECT_NE(NULL, cap_p)
{
TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); TH_LOG("Failed to cap_get_proc: %s", strerror(errno));
} }
EXPECT_NE(-1, cap_clear(cap_p)) { EXPECT_NE(-1, cap_clear(cap_p))
{
TH_LOG("Failed to cap_clear: %s", strerror(errno)); TH_LOG("Failed to cap_clear: %s", strerror(errno));
} }
if (!drop_all) { if (!drop_all) {
EXPECT_NE(-1, cap_set_flag(cap_p, CAP_PERMITTED, EXPECT_NE(-1, cap_set_flag(cap_p, CAP_PERMITTED,
ARRAY_SIZE(caps), caps, CAP_SET)) { ARRAY_SIZE(caps), caps, CAP_SET))
{
TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); TH_LOG("Failed to cap_set_flag: %s", strerror(errno));
} }
} }
EXPECT_NE(-1, cap_set_proc(cap_p)) { EXPECT_NE(-1, cap_set_proc(cap_p))
{
TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); TH_LOG("Failed to cap_set_proc: %s", strerror(errno));
} }
EXPECT_NE(-1, cap_free(cap_p)) { EXPECT_NE(-1, cap_free(cap_p))
{
TH_LOG("Failed to cap_free: %s", strerror(errno)); TH_LOG("Failed to cap_free: %s", strerror(errno));
} }
} }
/* We cannot put such helpers in a library because of kselftest_harness.h . */ /* We cannot put such helpers in a library because of kselftest_harness.h . */
__attribute__((__unused__)) __attribute__((__unused__)) static void
static void disable_caps(struct __test_metadata *const _metadata) disable_caps(struct __test_metadata *const _metadata)
{ {
_init_caps(_metadata, false); _init_caps(_metadata, false);
} }
__attribute__((__unused__)) __attribute__((__unused__)) static void
static void drop_caps(struct __test_metadata *const _metadata) drop_caps(struct __test_metadata *const _metadata)
{ {
_init_caps(_metadata, true); _init_caps(_metadata, true);
} }
...@@ -150,30 +158,32 @@ static void _effective_cap(struct __test_metadata *const _metadata, ...@@ -150,30 +158,32 @@ static void _effective_cap(struct __test_metadata *const _metadata,
cap_t cap_p; cap_t cap_p;
cap_p = cap_get_proc(); cap_p = cap_get_proc();
EXPECT_NE(NULL, cap_p) { EXPECT_NE(NULL, cap_p)
{
TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); TH_LOG("Failed to cap_get_proc: %s", strerror(errno));
} }
EXPECT_NE(-1, cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &caps, value)) { EXPECT_NE(-1, cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &caps, value))
{
TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); TH_LOG("Failed to cap_set_flag: %s", strerror(errno));
} }
EXPECT_NE(-1, cap_set_proc(cap_p)) { EXPECT_NE(-1, cap_set_proc(cap_p))
{
TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); TH_LOG("Failed to cap_set_proc: %s", strerror(errno));
} }
EXPECT_NE(-1, cap_free(cap_p)) { EXPECT_NE(-1, cap_free(cap_p))
{
TH_LOG("Failed to cap_free: %s", strerror(errno)); TH_LOG("Failed to cap_free: %s", strerror(errno));
} }
} }
__attribute__((__unused__)) __attribute__((__unused__)) static void
static void set_cap(struct __test_metadata *const _metadata, set_cap(struct __test_metadata *const _metadata, const cap_value_t caps)
const cap_value_t caps)
{ {
_effective_cap(_metadata, caps, CAP_SET); _effective_cap(_metadata, caps, CAP_SET);
} }
__attribute__((__unused__)) __attribute__((__unused__)) static void
static void clear_cap(struct __test_metadata *const _metadata, clear_cap(struct __test_metadata *const _metadata, const cap_value_t caps)
const cap_value_t caps)
{ {
_effective_cap(_metadata, caps, CAP_CLEAR); _effective_cap(_metadata, caps, CAP_CLEAR);
} }
This diff is collapsed.
...@@ -26,9 +26,10 @@ static void create_domain(struct __test_metadata *const _metadata) ...@@ -26,9 +26,10 @@ static void create_domain(struct __test_metadata *const _metadata)
.handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_BLOCK, .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_BLOCK,
}; };
ruleset_fd = landlock_create_ruleset(&ruleset_attr, ruleset_fd =
sizeof(ruleset_attr), 0); landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
EXPECT_LE(0, ruleset_fd) { EXPECT_LE(0, ruleset_fd)
{
TH_LOG("Failed to create a ruleset: %s", strerror(errno)); TH_LOG("Failed to create a ruleset: %s", strerror(errno));
} }
EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
...@@ -59,9 +60,12 @@ static int test_ptrace_read(const pid_t pid) ...@@ -59,9 +60,12 @@ static int test_ptrace_read(const pid_t pid)
return 0; return 0;
} }
FIXTURE(hierarchy) { }; /* clang-format off */
FIXTURE(hierarchy) {};
/* clang-format on */
FIXTURE_VARIANT(hierarchy) { FIXTURE_VARIANT(hierarchy)
{
const bool domain_both; const bool domain_both;
const bool domain_parent; const bool domain_parent;
const bool domain_child; const bool domain_child;
...@@ -83,7 +87,9 @@ FIXTURE_VARIANT(hierarchy) { ...@@ -83,7 +87,9 @@ FIXTURE_VARIANT(hierarchy) {
* \ P2 -> P1 : allow * \ P2 -> P1 : allow
* 'P2 * 'P2
*/ */
/* clang-format off */
FIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) { FIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) {
/* clang-format on */
.domain_both = false, .domain_both = false,
.domain_parent = false, .domain_parent = false,
.domain_child = false, .domain_child = false,
...@@ -98,7 +104,9 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) { ...@@ -98,7 +104,9 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) {
* | P2 | * | P2 |
* '------' * '------'
*/ */
/* clang-format off */
FIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) { FIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) {
/* clang-format on */
.domain_both = false, .domain_both = false,
.domain_parent = false, .domain_parent = false,
.domain_child = true, .domain_child = true,
...@@ -112,7 +120,9 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) { ...@@ -112,7 +120,9 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) {
* ' * '
* P2 * P2
*/ */
/* clang-format off */
FIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) { FIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) {
/* clang-format on */
.domain_both = false, .domain_both = false,
.domain_parent = true, .domain_parent = true,
.domain_child = false, .domain_child = false,
...@@ -127,7 +137,9 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) { ...@@ -127,7 +137,9 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) {
* | P2 | * | P2 |
* '------' * '------'
*/ */
/* clang-format off */
FIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) { FIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) {
/* clang-format on */
.domain_both = false, .domain_both = false,
.domain_parent = true, .domain_parent = true,
.domain_child = true, .domain_child = true,
...@@ -142,7 +154,9 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) { ...@@ -142,7 +154,9 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) {
* | P2 | * | P2 |
* '-------------' * '-------------'
*/ */
/* clang-format off */
FIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) { FIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) {
/* clang-format on */
.domain_both = true, .domain_both = true,
.domain_parent = false, .domain_parent = false,
.domain_child = false, .domain_child = false,
...@@ -158,7 +172,9 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) { ...@@ -158,7 +172,9 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) {
* | '------' | * | '------' |
* '-----------------' * '-----------------'
*/ */
/* clang-format off */
FIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) { FIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) {
/* clang-format on */
.domain_both = true, .domain_both = true,
.domain_parent = false, .domain_parent = false,
.domain_child = true, .domain_child = true,
...@@ -174,7 +190,9 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) { ...@@ -174,7 +190,9 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) {
* | P2 | * | P2 |
* '-----------------' * '-----------------'
*/ */
/* clang-format off */
FIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) { FIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) {
/* clang-format on */
.domain_both = true, .domain_both = true,
.domain_parent = true, .domain_parent = true,
.domain_child = false, .domain_child = false,
...@@ -192,17 +210,21 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) { ...@@ -192,17 +210,21 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) {
* | '------' | * | '------' |
* '-----------------' * '-----------------'
*/ */
/* clang-format off */
FIXTURE_VARIANT_ADD(hierarchy, deny_with_forked_domain) { FIXTURE_VARIANT_ADD(hierarchy, deny_with_forked_domain) {
/* clang-format on */
.domain_both = true, .domain_both = true,
.domain_parent = true, .domain_parent = true,
.domain_child = true, .domain_child = true,
}; };
FIXTURE_SETUP(hierarchy) FIXTURE_SETUP(hierarchy)
{ } {
}
FIXTURE_TEARDOWN(hierarchy) FIXTURE_TEARDOWN(hierarchy)
{ } {
}
/* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */ /* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */
TEST_F(hierarchy, trace) TEST_F(hierarchy, trace)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment