Commit b91c3e4e authored by Mickaël Salaün's avatar Mickaël Salaün

landlock: Add support for file reparenting with LANDLOCK_ACCESS_FS_REFER

Add a new LANDLOCK_ACCESS_FS_REFER access right to enable policy writers
to allow sandboxed processes to link and rename files from and to a
specific set of file hierarchies.  This access right should be composed
with LANDLOCK_ACCESS_FS_MAKE_* for the destination of a link or rename,
and with LANDLOCK_ACCESS_FS_REMOVE_* for a source of a rename.  This
lift a Landlock limitation that always denied changing the parent of an
inode.

Renaming or linking to the same directory is still always allowed,
whatever LANDLOCK_ACCESS_FS_REFER is used or not, because it is not
considered a threat to user data.

However, creating multiple links or renaming to a different parent
directory may lead to privilege escalations if not handled properly.
Indeed, we must be sure that the source doesn't gain more privileges by
being accessible from the destination.  This is handled by making sure
that the source hierarchy (including the referenced file or directory
itself) restricts at least as much the destination hierarchy.  If it is
not the case, an EXDEV error is returned, making it potentially possible
for user space to copy the file hierarchy instead of moving or linking
it.

Instead of creating different access rights for the source and the
destination, we choose to make it simple and consistent for users.
Indeed, considering the previous constraint, it would be weird to
require such destination access right to be also granted to the source
(to make it a superset).  Moreover, RENAME_EXCHANGE would also add to
the confusion because of paths being both a source and a destination.

See the provided documentation for additional details.

New tests are provided with a following commit.
Reviewed-by: default avatarPaul Moore <paul@paul-moore.com>
Signed-off-by: default avatarMickaël Salaün <mic@digikod.net>
Link: https://lore.kernel.org/r/20220506161102.525323-8-mic@digikod.net
parent 100f59d9
...@@ -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;
}; };
...@@ -112,6 +118,22 @@ struct landlock_path_beneath_attr { ...@@ -112,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::
* *
...@@ -137,6 +159,7 @@ struct landlock_path_beneath_attr { ...@@ -137,6 +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 */ /* clang-format on */
#endif /* _UAPI_LINUX_LANDLOCK_H */ #endif /* _UAPI_LINUX_LANDLOCK_H */
This diff is collapsed.
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#define LANDLOCK_MAX_NUM_LAYERS 16 #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) #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
......
...@@ -129,7 +129,7 @@ static const struct file_operations ruleset_fops = { ...@@ -129,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
......
...@@ -75,7 +75,7 @@ TEST(abi_version) ...@@ -75,7 +75,7 @@ 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,
......
...@@ -401,7 +401,7 @@ TEST_F_FORK(layout1, inval) ...@@ -401,7 +401,7 @@ TEST_F_FORK(layout1, inval)
LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \
LANDLOCK_ACCESS_FS_READ_FILE) LANDLOCK_ACCESS_FS_READ_FILE)
#define ACCESS_LAST LANDLOCK_ACCESS_FS_MAKE_SYM #define ACCESS_LAST LANDLOCK_ACCESS_FS_REFER
#define ACCESS_ALL ( \ #define ACCESS_ALL ( \
ACCESS_FILE | \ ACCESS_FILE | \
...@@ -414,6 +414,7 @@ TEST_F_FORK(layout1, inval) ...@@ -414,6 +414,7 @@ TEST_F_FORK(layout1, inval)
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 | \
ACCESS_LAST) ACCESS_LAST)
/* clang-format on */ /* clang-format on */
......
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