2022-08-27 11:18:39

by Xiu Jianfeng

[permalink] [raw]
Subject: [PATCH -next v2 3/6] landlock: add chmod and chown support

Add two flags LANDLOCK_ACCESS_FS_CHMOD and LANDLOCK_ACCESS_FS_CHGRP to
support restriction to chmod(2) and chown(2) with landlock.

If these two access rights are set on a directory, they only take effect
for its context, not the directory itself.

This patch also change the landlock ABI version from 3 to 4.

Signed-off-by: Xiu Jianfeng <[email protected]>
---
include/uapi/linux/landlock.h | 10 +++--
security/landlock/fs.c | 43 +++++++++++++++++++-
security/landlock/limits.h | 2 +-
security/landlock/syscalls.c | 2 +-
tools/testing/selftests/landlock/base_test.c | 2 +-
tools/testing/selftests/landlock/fs_test.c | 6 ++-
6 files changed, 56 insertions(+), 9 deletions(-)

diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index 735b1fe8326e..07b73626ff20 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -141,14 +141,16 @@ struct landlock_path_beneath_attr {
* 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.
+ * - %LANDLOCK_ACCESS_FS_CHMOD: Change the file mode bits of a file.
+ * - %LANDLOCK_ACCESS_FS_CHGRP: Change the owner and/or group of a file.
*
* .. warning::
*
* It is currently not possible to restrict some file-related actions
* accessible through these syscall families: :manpage:`chdir(2)`,
- * :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`chmod(2)`,
- * :manpage:`chown(2)`, :manpage:`setxattr(2)`, :manpage:`utime(2)`,
- * :manpage:`ioctl(2)`, :manpage:`fcntl(2)`, :manpage:`access(2)`.
+ * :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`setxattr(2)`,
+ * :manpage:`utime(2)`,:manpage:`ioctl(2)`, :manpage:`fcntl(2)`,
+ * :manpage:`access(2)`.
* Future Landlock evolutions will enable to restrict them.
*/
/* clang-format off */
@@ -167,6 +169,8 @@ struct landlock_path_beneath_attr {
#define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12)
#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
+#define LANDLOCK_ACCESS_FS_CHMOD (1ULL << 15)
+#define LANDLOCK_ACCESS_FS_CHGRP (1ULL << 16)
/* clang-format on */

#endif /* _UAPI_LINUX_LANDLOCK_H */
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 4ef614a4ea22..6ac83d96ada7 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -185,7 +185,9 @@ static struct landlock_object *get_inode_object(struct inode *const inode)
LANDLOCK_ACCESS_FS_EXECUTE | \
LANDLOCK_ACCESS_FS_WRITE_FILE | \
LANDLOCK_ACCESS_FS_READ_FILE | \
- LANDLOCK_ACCESS_FS_TRUNCATE)
+ LANDLOCK_ACCESS_FS_TRUNCATE | \
+ LANDLOCK_ACCESS_FS_CHMOD | \
+ LANDLOCK_ACCESS_FS_CHGRP)
/* clang-format on */

/*
@@ -690,6 +692,31 @@ static inline int current_check_access_path(const struct path *const path,
return check_access_path(dom, path, access_request);
}

+static inline int
+current_check_access_path_context_only(const struct path *const path,
+ const access_mask_t access_request)
+{
+ const struct landlock_ruleset *const dom =
+ landlock_get_current_domain();
+ struct path eff_path;
+ int ret;
+
+ if (!dom)
+ return 0;
+ eff_path = *path;
+ /* if it's dir, check its visible parent. */
+ if (d_is_dir(eff_path.dentry)) {
+ path_get(&eff_path);
+ /* dont care if reaches the root or not. */
+ walk_to_visible_parent(&eff_path);
+ ret = current_check_access_path(&eff_path, access_request);
+ path_put(&eff_path);
+ } else {
+ ret = current_check_access_path(&eff_path, access_request);
+ }
+ return ret;
+}
+
static inline access_mask_t get_mode_access(const umode_t mode)
{
switch (mode & S_IFMT) {
@@ -1177,6 +1204,18 @@ static int hook_path_truncate(const struct path *const path)
return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
}

+static int hook_path_chmod(const struct path *const path, umode_t mode)
+{
+ return current_check_access_path_context_only(path,
+ LANDLOCK_ACCESS_FS_CHMOD);
+}
+
+static int hook_path_chown(const struct path *const path, kuid_t uid, kgid_t gid)
+{
+ return current_check_access_path_context_only(path,
+ LANDLOCK_ACCESS_FS_CHGRP);
+}
+
/* File hooks */

static inline access_mask_t get_file_access(const struct file *const file)
@@ -1230,6 +1269,8 @@ static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(path_unlink, hook_path_unlink),
LSM_HOOK_INIT(path_rmdir, hook_path_rmdir),
LSM_HOOK_INIT(path_truncate, hook_path_truncate),
+ LSM_HOOK_INIT(path_chmod, hook_path_chmod),
+ LSM_HOOK_INIT(path_chown, hook_path_chown),

LSM_HOOK_INIT(file_open, hook_file_open),
};
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 82288f0e9e5e..7cdd7d467d12 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -18,7 +18,7 @@
#define LANDLOCK_MAX_NUM_LAYERS 16
#define LANDLOCK_MAX_NUM_RULES U32_MAX

-#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_TRUNCATE
+#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_CHGRP
#define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
#define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)

diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index f4d6fc7ed17f..469e0e11735c 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -129,7 +129,7 @@ static const struct file_operations ruleset_fops = {
.write = fop_dummy_write,
};

-#define LANDLOCK_ABI_VERSION 3
+#define LANDLOCK_ABI_VERSION 4

/**
* sys_landlock_create_ruleset - Create a new ruleset
diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
index 72cdae277b02..9f00582f639c 100644
--- a/tools/testing/selftests/landlock/base_test.c
+++ b/tools/testing/selftests/landlock/base_test.c
@@ -75,7 +75,7 @@ TEST(abi_version)
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
};
- ASSERT_EQ(3, landlock_create_ruleset(NULL, 0,
+ ASSERT_EQ(4, landlock_create_ruleset(NULL, 0,
LANDLOCK_CREATE_RULESET_VERSION));

ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index debe2d9ea6cf..f513cd8d9d51 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -404,9 +404,11 @@ TEST_F_FORK(layout1, inval)
LANDLOCK_ACCESS_FS_EXECUTE | \
LANDLOCK_ACCESS_FS_WRITE_FILE | \
LANDLOCK_ACCESS_FS_READ_FILE | \
- LANDLOCK_ACCESS_FS_TRUNCATE)
+ LANDLOCK_ACCESS_FS_TRUNCATE | \
+ LANDLOCK_ACCESS_FS_CHMOD | \
+ LANDLOCK_ACCESS_FS_CHGRP)

-#define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE
+#define ACCESS_LAST LANDLOCK_ACCESS_FS_CHGRP

#define ACCESS_ALL ( \
ACCESS_FILE | \
--
2.17.1


2022-08-27 21:17:55

by Günther Noack

[permalink] [raw]
Subject: Re: [PATCH -next v2 3/6] landlock: add chmod and chown support

Hello!

the mapping between Landlock rights to LSM hooks is now as follows in
your patch set:

* LANDLOCK_ACCESS_FS_CHMOD controls hook_path_chmod
* LANDLOCK_ACCESS_FS_CHGRP controls hook_path_chown
(this hook can restrict both the chown(2) and chgrp(2) syscalls)

Is this the desired mapping?

The previous discussion I found on the topic was in

[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/
[3] https://lore.kernel.org/all/[email protected]/

In my understanding the main arguments were the ones in [2] and [3].

There were no further responses to [3], so I was under the impression
that we were gravitating towards an approach where the
file-metadata-modification operations were grouped more coarsely?

For example with the approach suggested in [3], which would be to
group the operations coarsely into (a) one Landlock right for
modifying file metadata that is used in security contexts, and (b) one
Landlock right for modifying metadata that was used in non-security
contexts. That would mean that there would be:

(a) LANDLOCK_ACCESS_FS_MODIFY_SECURITY_ATTRIBUTES to control the
following operations:
* chmod(2)-variants through hook_path_chmod,
* chown(2)-variants and chgrp(2)-variants through hook_path_chown,
* setxattr(2)-variants and removexattr(2)-variants for extended
attributes that are not "user extended attributes" as described in
xattr(7) through hook_inode_setxattr and hook_inode_removexattr

(b) LANDLOCK_ACCESS_FS_MODIFY_NON_SECURITY_ATTRIBUTES to control the
following operations:
* utimes(2) and other operations for setting other non-security
sensitive attributes, probably through hook_inode_setattr(?)
* xattr modifications like above, but for the "user extended
attributes", though hook_inode_setxattr and hook_inode_removexattr

In my mind, this would be a sensible grouping, and it would also help
to decouple the userspace-exposed API from the underlying
implementation, as Casey suggested to do in [2].

Specifically for this patch set, if you want to use this grouping, you
would only need to add one new Landlock right
(LANDLOCK_ACCESS_FS_MODIFY_SECURITY_ATTRIBUTES) as described above
under (a) (and maybe we can find a shorter name for it... :))?

Did I miss any operations here that would be necessary to restrict?

Would that make sense to you? Xiu, what is your opinion on how this
should be grouped? Do you have use cases in mind where a more
fine-grained grouping would be required?

—Günther

P.S.: Regarding utimes: The hook_inode_setattr hook *also* gets called
on a variety on attribute changes including file ownership, file size
and file mode, so it might potentially interact with a bunch of other
existing Landlock rights. Maybe that is not the right approach. In any
case, it seems like it might require more thinking and it might be
sensible to do that in a separate patch set IMHO.

On Sat, Aug 27, 2022 at 07:12:12PM +0800, Xiu Jianfeng wrote:
> Add two flags LANDLOCK_ACCESS_FS_CHMOD and LANDLOCK_ACCESS_FS_CHGRP to
> support restriction to chmod(2) and chown(2) with landlock.
>
> If these two access rights are set on a directory, they only take effect
> for its context, not the directory itself.
>
> This patch also change the landlock ABI version from 3 to 4.
>
> Signed-off-by: Xiu Jianfeng <[email protected]>
> ---
> include/uapi/linux/landlock.h | 10 +++--
> security/landlock/fs.c | 43 +++++++++++++++++++-
> security/landlock/limits.h | 2 +-
> security/landlock/syscalls.c | 2 +-
> tools/testing/selftests/landlock/base_test.c | 2 +-
> tools/testing/selftests/landlock/fs_test.c | 6 ++-
> 6 files changed, 56 insertions(+), 9 deletions(-)
>
> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
> index 735b1fe8326e..07b73626ff20 100644
> --- a/include/uapi/linux/landlock.h
> +++ b/include/uapi/linux/landlock.h
> @@ -141,14 +141,16 @@ struct landlock_path_beneath_attr {
> * 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.
> + * - %LANDLOCK_ACCESS_FS_CHMOD: Change the file mode bits of a file.
> + * - %LANDLOCK_ACCESS_FS_CHGRP: Change the owner and/or group of a file.
> *
> * .. warning::
> *
> * It is currently not possible to restrict some file-related actions
> * accessible through these syscall families: :manpage:`chdir(2)`,
> - * :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`chmod(2)`,
> - * :manpage:`chown(2)`, :manpage:`setxattr(2)`, :manpage:`utime(2)`,
> - * :manpage:`ioctl(2)`, :manpage:`fcntl(2)`, :manpage:`access(2)`.
> + * :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`setxattr(2)`,
> + * :manpage:`utime(2)`,:manpage:`ioctl(2)`, :manpage:`fcntl(2)`,
> + * :manpage:`access(2)`.
> * Future Landlock evolutions will enable to restrict them.
> */
> /* clang-format off */
> @@ -167,6 +169,8 @@ struct landlock_path_beneath_attr {
> #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12)
> #define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
> #define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
> +#define LANDLOCK_ACCESS_FS_CHMOD (1ULL << 15)
> +#define LANDLOCK_ACCESS_FS_CHGRP (1ULL << 16)
> /* clang-format on */
>
> #endif /* _UAPI_LINUX_LANDLOCK_H */
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index 4ef614a4ea22..6ac83d96ada7 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -185,7 +185,9 @@ static struct landlock_object *get_inode_object(struct inode *const inode)
> LANDLOCK_ACCESS_FS_EXECUTE | \
> LANDLOCK_ACCESS_FS_WRITE_FILE | \
> LANDLOCK_ACCESS_FS_READ_FILE | \
> - LANDLOCK_ACCESS_FS_TRUNCATE)
> + LANDLOCK_ACCESS_FS_TRUNCATE | \
> + LANDLOCK_ACCESS_FS_CHMOD | \
> + LANDLOCK_ACCESS_FS_CHGRP)
> /* clang-format on */
>
> /*
> @@ -690,6 +692,31 @@ static inline int current_check_access_path(const struct path *const path,
> return check_access_path(dom, path, access_request);
> }
>
> +static inline int
> +current_check_access_path_context_only(const struct path *const path,
> + const access_mask_t access_request)
> +{
> + const struct landlock_ruleset *const dom =
> + landlock_get_current_domain();
> + struct path eff_path;
> + int ret;
> +
> + if (!dom)
> + return 0;
> + eff_path = *path;
> + /* if it's dir, check its visible parent. */
> + if (d_is_dir(eff_path.dentry)) {
> + path_get(&eff_path);
> + /* dont care if reaches the root or not. */
> + walk_to_visible_parent(&eff_path);
> + ret = current_check_access_path(&eff_path, access_request);
> + path_put(&eff_path);
> + } else {
> + ret = current_check_access_path(&eff_path, access_request);
> + }
> + return ret;
> +}
> +
> static inline access_mask_t get_mode_access(const umode_t mode)
> {
> switch (mode & S_IFMT) {
> @@ -1177,6 +1204,18 @@ static int hook_path_truncate(const struct path *const path)
> return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
> }
>
> +static int hook_path_chmod(const struct path *const path, umode_t mode)
> +{
> + return current_check_access_path_context_only(path,
> + LANDLOCK_ACCESS_FS_CHMOD);
> +}
> +
> +static int hook_path_chown(const struct path *const path, kuid_t uid, kgid_t gid)
> +{
> + return current_check_access_path_context_only(path,
> + LANDLOCK_ACCESS_FS_CHGRP);
> +}
> +
> /* File hooks */
>
> static inline access_mask_t get_file_access(const struct file *const file)
> @@ -1230,6 +1269,8 @@ static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = {
> LSM_HOOK_INIT(path_unlink, hook_path_unlink),
> LSM_HOOK_INIT(path_rmdir, hook_path_rmdir),
> LSM_HOOK_INIT(path_truncate, hook_path_truncate),
> + LSM_HOOK_INIT(path_chmod, hook_path_chmod),
> + LSM_HOOK_INIT(path_chown, hook_path_chown),
>
> LSM_HOOK_INIT(file_open, hook_file_open),
> };
> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
> index 82288f0e9e5e..7cdd7d467d12 100644
> --- a/security/landlock/limits.h
> +++ b/security/landlock/limits.h
> @@ -18,7 +18,7 @@
> #define LANDLOCK_MAX_NUM_LAYERS 16
> #define LANDLOCK_MAX_NUM_RULES U32_MAX
>
> -#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_TRUNCATE
> +#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_CHGRP
> #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
> #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
>
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index f4d6fc7ed17f..469e0e11735c 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -129,7 +129,7 @@ static const struct file_operations ruleset_fops = {
> .write = fop_dummy_write,
> };
>
> -#define LANDLOCK_ABI_VERSION 3
> +#define LANDLOCK_ABI_VERSION 4
>
> /**
> * sys_landlock_create_ruleset - Create a new ruleset
> diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
> index 72cdae277b02..9f00582f639c 100644
> --- a/tools/testing/selftests/landlock/base_test.c
> +++ b/tools/testing/selftests/landlock/base_test.c
> @@ -75,7 +75,7 @@ TEST(abi_version)
> const struct landlock_ruleset_attr ruleset_attr = {
> .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
> };
> - ASSERT_EQ(3, landlock_create_ruleset(NULL, 0,
> + ASSERT_EQ(4, landlock_create_ruleset(NULL, 0,
> LANDLOCK_CREATE_RULESET_VERSION));
>
> ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
> diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
> index debe2d9ea6cf..f513cd8d9d51 100644
> --- a/tools/testing/selftests/landlock/fs_test.c
> +++ b/tools/testing/selftests/landlock/fs_test.c
> @@ -404,9 +404,11 @@ TEST_F_FORK(layout1, inval)
> LANDLOCK_ACCESS_FS_EXECUTE | \
> LANDLOCK_ACCESS_FS_WRITE_FILE | \
> LANDLOCK_ACCESS_FS_READ_FILE | \
> - LANDLOCK_ACCESS_FS_TRUNCATE)
> + LANDLOCK_ACCESS_FS_TRUNCATE | \
> + LANDLOCK_ACCESS_FS_CHMOD | \
> + LANDLOCK_ACCESS_FS_CHGRP)
>
> -#define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE
> +#define ACCESS_LAST LANDLOCK_ACCESS_FS_CHGRP
>
> #define ACCESS_ALL ( \
> ACCESS_FILE | \
> --
> 2.17.1
>

--

2022-08-29 06:47:54

by Xiu Jianfeng

[permalink] [raw]
Subject: Re: [PATCH -next v2 3/6] landlock: add chmod and chown support

Hi,

?? 2022/8/27 19:12, Xiu Jianfeng д??:
> Add two flags LANDLOCK_ACCESS_FS_CHMOD and LANDLOCK_ACCESS_FS_CHGRP to
> support restriction to chmod(2) and chown(2) with landlock.
>
> If these two access rights are set on a directory, they only take effect
> for its context, not the directory itself.
>
> This patch also change the landlock ABI version from 3 to 4.
>
> Signed-off-by: Xiu Jianfeng <[email protected]>
> ---
> include/uapi/linux/landlock.h | 10 +++--
> security/landlock/fs.c | 43 +++++++++++++++++++-
> security/landlock/limits.h | 2 +-
> security/landlock/syscalls.c | 2 +-
> tools/testing/selftests/landlock/base_test.c | 2 +-
> tools/testing/selftests/landlock/fs_test.c | 6 ++-
> 6 files changed, 56 insertions(+), 9 deletions(-)
>
> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
> index 735b1fe8326e..07b73626ff20 100644
> --- a/include/uapi/linux/landlock.h
> +++ b/include/uapi/linux/landlock.h
> @@ -141,14 +141,16 @@ struct landlock_path_beneath_attr {
> * 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.
> + * - %LANDLOCK_ACCESS_FS_CHMOD: Change the file mode bits of a file.
> + * - %LANDLOCK_ACCESS_FS_CHGRP: Change the owner and/or group of a file.
> *
> * .. warning::
> *
> * It is currently not possible to restrict some file-related actions
> * accessible through these syscall families: :manpage:`chdir(2)`,
> - * :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`chmod(2)`,
> - * :manpage:`chown(2)`, :manpage:`setxattr(2)`, :manpage:`utime(2)`,
> - * :manpage:`ioctl(2)`, :manpage:`fcntl(2)`, :manpage:`access(2)`.
> + * :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`setxattr(2)`,
> + * :manpage:`utime(2)`,:manpage:`ioctl(2)`, :manpage:`fcntl(2)`,
> + * :manpage:`access(2)`.
> * Future Landlock evolutions will enable to restrict them.
> */
> /* clang-format off */
> @@ -167,6 +169,8 @@ struct landlock_path_beneath_attr {
> #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12)
> #define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
> #define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
> +#define LANDLOCK_ACCESS_FS_CHMOD (1ULL << 15)
> +#define LANDLOCK_ACCESS_FS_CHGRP (1ULL << 16)
> /* clang-format on */
>
> #endif /* _UAPI_LINUX_LANDLOCK_H */
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index 4ef614a4ea22..6ac83d96ada7 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -185,7 +185,9 @@ static struct landlock_object *get_inode_object(struct inode *const inode)
> LANDLOCK_ACCESS_FS_EXECUTE | \
> LANDLOCK_ACCESS_FS_WRITE_FILE | \
> LANDLOCK_ACCESS_FS_READ_FILE | \
> - LANDLOCK_ACCESS_FS_TRUNCATE)
> + LANDLOCK_ACCESS_FS_TRUNCATE | \
> + LANDLOCK_ACCESS_FS_CHMOD | \
> + LANDLOCK_ACCESS_FS_CHGRP)
> /* clang-format on */
>
> /*
> @@ -690,6 +692,31 @@ static inline int current_check_access_path(const struct path *const path,
> return check_access_path(dom, path, access_request);
> }
>
> +static inline int
> +current_check_access_path_context_only(const struct path *const path,
> + const access_mask_t access_request)
> +{
> + const struct landlock_ruleset *const dom =
> + landlock_get_current_domain();
> + struct path eff_path;
> + int ret;
> +
> + if (!dom)
> + return 0;
> + eff_path = *path;
> + /* if it's dir, check its visible parent. */
> + if (d_is_dir(eff_path.dentry)) {
> + path_get(&eff_path);
> + /* dont care if reaches the root or not. */

I may made a mistake here, I think it should return -EACCES directly if
the walk result is not WALK_CONTINUE.

> + walk_to_visible_parent(&eff_path);
> + ret = current_check_access_path(&eff_path, access_request);
> + path_put(&eff_path);
> + } else {
> + ret = current_check_access_path(&eff_path, access_request);
> + }
> + return ret;
> +}
> +
> static inline access_mask_t get_mode_access(const umode_t mode)
> {
> switch (mode & S_IFMT) {
> @@ -1177,6 +1204,18 @@ static int hook_path_truncate(const struct path *const path)
> return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
> }
>
> +static int hook_path_chmod(const struct path *const path, umode_t mode)
> +{
> + return current_check_access_path_context_only(path,
> + LANDLOCK_ACCESS_FS_CHMOD);
> +}
> +
> +static int hook_path_chown(const struct path *const path, kuid_t uid, kgid_t gid)
> +{
> + return current_check_access_path_context_only(path,
> + LANDLOCK_ACCESS_FS_CHGRP);
> +}
> +
> /* File hooks */
>
> static inline access_mask_t get_file_access(const struct file *const file)
> @@ -1230,6 +1269,8 @@ static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = {
> LSM_HOOK_INIT(path_unlink, hook_path_unlink),
> LSM_HOOK_INIT(path_rmdir, hook_path_rmdir),
> LSM_HOOK_INIT(path_truncate, hook_path_truncate),
> + LSM_HOOK_INIT(path_chmod, hook_path_chmod),
> + LSM_HOOK_INIT(path_chown, hook_path_chown),
>
> LSM_HOOK_INIT(file_open, hook_file_open),
> };
> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
> index 82288f0e9e5e..7cdd7d467d12 100644
> --- a/security/landlock/limits.h
> +++ b/security/landlock/limits.h
> @@ -18,7 +18,7 @@
> #define LANDLOCK_MAX_NUM_LAYERS 16
> #define LANDLOCK_MAX_NUM_RULES U32_MAX
>
> -#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_TRUNCATE
> +#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_CHGRP
> #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
> #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
>
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index f4d6fc7ed17f..469e0e11735c 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -129,7 +129,7 @@ static const struct file_operations ruleset_fops = {
> .write = fop_dummy_write,
> };
>
> -#define LANDLOCK_ABI_VERSION 3
> +#define LANDLOCK_ABI_VERSION 4
>
> /**
> * sys_landlock_create_ruleset - Create a new ruleset
> diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
> index 72cdae277b02..9f00582f639c 100644
> --- a/tools/testing/selftests/landlock/base_test.c
> +++ b/tools/testing/selftests/landlock/base_test.c
> @@ -75,7 +75,7 @@ TEST(abi_version)
> const struct landlock_ruleset_attr ruleset_attr = {
> .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
> };
> - ASSERT_EQ(3, landlock_create_ruleset(NULL, 0,
> + ASSERT_EQ(4, landlock_create_ruleset(NULL, 0,
> LANDLOCK_CREATE_RULESET_VERSION));
>
> ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
> diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
> index debe2d9ea6cf..f513cd8d9d51 100644
> --- a/tools/testing/selftests/landlock/fs_test.c
> +++ b/tools/testing/selftests/landlock/fs_test.c
> @@ -404,9 +404,11 @@ TEST_F_FORK(layout1, inval)
> LANDLOCK_ACCESS_FS_EXECUTE | \
> LANDLOCK_ACCESS_FS_WRITE_FILE | \
> LANDLOCK_ACCESS_FS_READ_FILE | \
> - LANDLOCK_ACCESS_FS_TRUNCATE)
> + LANDLOCK_ACCESS_FS_TRUNCATE | \
> + LANDLOCK_ACCESS_FS_CHMOD | \
> + LANDLOCK_ACCESS_FS_CHGRP)
>
> -#define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE
> +#define ACCESS_LAST LANDLOCK_ACCESS_FS_CHGRP
>
> #define ACCESS_ALL ( \
> ACCESS_FILE | \
>

2022-08-29 16:34:09

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH -next v2 3/6] landlock: add chmod and chown support


On 29/08/2022 03:17, xiujianfeng wrote:
>
> Hi,
>
> 在 2022/8/28 3:30, Günther Noack 写道:
>> Hello!
>>
>> the mapping between Landlock rights to LSM hooks is now as follows in
>> your patch set:
>>
>> * LANDLOCK_ACCESS_FS_CHMOD controls hook_path_chmod
>> * LANDLOCK_ACCESS_FS_CHGRP controls hook_path_chown
>> (this hook can restrict both the chown(2) and chgrp(2) syscalls)
>>
>> Is this the desired mapping?
>>
>> The previous discussion I found on the topic was in
>>
>> [1] https://lore.kernel.org/all/[email protected]/
>> [2] https://lore.kernel.org/all/[email protected]/
>> [3] https://lore.kernel.org/all/[email protected]/
>>
>> In my understanding the main arguments were the ones in [2] and [3].
>>
>> There were no further responses to [3], so I was under the impression
>> that we were gravitating towards an approach where the
>> file-metadata-modification operations were grouped more coarsely?
>>
>> For example with the approach suggested in [3], which would be to
>> group the operations coarsely into (a) one Landlock right for
>> modifying file metadata that is used in security contexts, and (b) one
>> Landlock right for modifying metadata that was used in non-security
>> contexts. That would mean that there would be:
>>
>> (a) LANDLOCK_ACCESS_FS_MODIFY_SECURITY_ATTRIBUTES to control the
>> following operations:
>> * chmod(2)-variants through hook_path_chmod,
>> * chown(2)-variants and chgrp(2)-variants through hook_path_chown,
>> * setxattr(2)-variants and removexattr(2)-variants for extended
>> attributes that are not "user extended attributes" as described in
>> xattr(7) through hook_inode_setxattr and hook_inode_removexattr
>>
>> (b) LANDLOCK_ACCESS_FS_MODIFY_NON_SECURITY_ATTRIBUTES to control the
>> following operations:
>> * utimes(2) and other operations for setting other non-security
>> sensitive attributes, probably through hook_inode_setattr(?)
>> * xattr modifications like above, but for the "user extended
>> attributes", though hook_inode_setxattr and hook_inode_removexattr
>>
>> In my mind, this would be a sensible grouping, and it would also help
>> to decouple the userspace-exposed API from the underlying
>> implementation, as Casey suggested to do in [2].
>>
>> Specifically for this patch set, if you want to use this grouping, you
>> would only need to add one new Landlock right
>> (LANDLOCK_ACCESS_FS_MODIFY_SECURITY_ATTRIBUTES) as described above
>> under (a) (and maybe we can find a shorter name for it... :))?
>>
>> Did I miss any operations here that would be necessary to restrict?
>>
>> Would that make sense to you? Xiu, what is your opinion on how this
>> should be grouped? Do you have use cases in mind where a more
>> fine-grained grouping would be required?
>
> I apologize I may missed that discussion when I prepared v2:(
>
> Yes, agreed, this grouping is more sensible and resonnable. so in this
> patchset only one right will be added, and I suppose the first commit
> which expand access_mask_t to u32 can be droped.
>
>>
>> —Günther
>>
>> P.S.: Regarding utimes: The hook_inode_setattr hook *also* gets called
>> on a variety on attribute changes including file ownership, file size
>> and file mode, so it might potentially interact with a bunch of other
>> existing Landlock rights. Maybe that is not the right approach. In any
>> case, it seems like it might require more thinking and it might be
>> sensible to do that in a separate patch set IMHO.
>
> Thanks for you reminder, that seems it's more complicated to support
> utimes, so I think we'd better not support it in this patchset.

The issue with this approach is that it makes it impossible to properly
group such access rights. Indeed, to avoid inconsistencies and much more
complexity, we cannot extend a Landlock access right once it is defined.

We also need to consider that file ownership and permissions have a
default (e.g. umask), which is also a way to set them. How to
consistently manage that? What if the application wants to protect its
files with chmod 0400?

About the naming, I think we can start with:
- LANDLOCK_ACCESS_FS_READ_METADATA (read any file/dir metadata);
- LANDLOCK_ACCESS_FS_WRITE_SAFE_METADATA: change file times, user xattr;
- LANDLOCK_ACCESS_FS_WRITE_UNSAFE_METADATA: interpreted by the kernel
(could change non-Landlock DAC or MAC, which could be considered as a
policy bypass; or other various xattr that might be interpreted by
filesystems), this should be denied most of the time.

2022-09-01 17:57:50

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH -next v2 3/6] landlock: add chmod and chown support

CCing [email protected]


On 01/09/2022 15:06, xiujianfeng wrote:
> Hi,
>
> 在 2022/8/30 0:01, Mickaël Salaün 写道:
>>
>> On 29/08/2022 03:17, xiujianfeng wrote:
>>>
>>> Hi,
>>>
>>> 在 2022/8/28 3:30, Günther Noack 写道:
>>>> Hello!
>>>>
>>>> the mapping between Landlock rights to LSM hooks is now as follows in
>>>> your patch set:
>>>>
>>>> * LANDLOCK_ACCESS_FS_CHMOD controls hook_path_chmod
>>>> * LANDLOCK_ACCESS_FS_CHGRP controls hook_path_chown
>>>>     (this hook can restrict both the chown(2) and chgrp(2) syscalls)
>>>>
>>>> Is this the desired mapping?
>>>>
>>>> The previous discussion I found on the topic was in
>>>>
>>>> [1]
>>>> https://lore.kernel.org/all/[email protected]/
>>>>
>>>> [2]
>>>> https://lore.kernel.org/all/[email protected]/
>>>>
>>>> [3]
>>>> https://lore.kernel.org/all/[email protected]/
>>>>
>>>>
>>>> In my understanding the main arguments were the ones in [2] and [3].
>>>>
>>>> There were no further responses to [3], so I was under the impression
>>>> that we were gravitating towards an approach where the
>>>> file-metadata-modification operations were grouped more coarsely?
>>>>
>>>> For example with the approach suggested in [3], which would be to
>>>> group the operations coarsely into (a) one Landlock right for
>>>> modifying file metadata that is used in security contexts, and (b) one
>>>> Landlock right for modifying metadata that was used in non-security
>>>> contexts. That would mean that there would be:
>>>>
>>>> (a) LANDLOCK_ACCESS_FS_MODIFY_SECURITY_ATTRIBUTES to control the
>>>> following operations:
>>>>     * chmod(2)-variants through hook_path_chmod,
>>>>     * chown(2)-variants and chgrp(2)-variants through hook_path_chown,
>>>>     * setxattr(2)-variants and removexattr(2)-variants for extended
>>>>       attributes that are not "user extended attributes" as described in
>>>>       xattr(7) through hook_inode_setxattr and hook_inode_removexattr
>>>>
>>>> (b) LANDLOCK_ACCESS_FS_MODIFY_NON_SECURITY_ATTRIBUTES to control the
>>>> following operations:
>>>>     * utimes(2) and other operations for setting other non-security
>>>>       sensitive attributes, probably through hook_inode_setattr(?)
>>>>     * xattr modifications like above, but for the "user extended
>>>>       attributes", though hook_inode_setxattr and hook_inode_removexattr
>>>>
>>>> In my mind, this would be a sensible grouping, and it would also help
>>>> to decouple the userspace-exposed API from the underlying
>>>> implementation, as Casey suggested to do in [2].
>>>>
>>>> Specifically for this patch set, if you want to use this grouping, you
>>>> would only need to add one new Landlock right
>>>> (LANDLOCK_ACCESS_FS_MODIFY_SECURITY_ATTRIBUTES) as described above
>>>> under (a) (and maybe we can find a shorter name for it... :))?
>>>>
>>>> Did I miss any operations here that would be necessary to restrict?
>>>>
>>>> Would that make sense to you? Xiu, what is your opinion on how this
>>>> should be grouped? Do you have use cases in mind where a more
>>>> fine-grained grouping would be required?
>>>
>>> I apologize I may missed that discussion when I prepared v2:(
>>>
>>> Yes, agreed, this grouping is more sensible and resonnable. so in this
>>> patchset only one right will be added, and I suppose the first commit
>>> which expand access_mask_t to u32 can be droped.
>>>
>>>>
>>>> —Günther
>>>>
>>>> P.S.: Regarding utimes: The hook_inode_setattr hook *also* gets called
>>>> on a variety on attribute changes including file ownership, file size
>>>> and file mode, so it might potentially interact with a bunch of other
>>>> existing Landlock rights. Maybe that is not the right approach. In any
>>>> case, it seems like it might require more thinking and it might be
>>>> sensible to do that in a separate patch set IMHO.
>>>
>>> Thanks for you reminder, that seems it's more complicated to support
>>> utimes, so I think we'd better not support it in this patchset.
>>
>> The issue with this approach is that it makes it impossible to properly
>> group such access rights. Indeed, to avoid inconsistencies and much more
>> complexity, we cannot extend a Landlock access right once it is defined.
>>
>> We also need to consider that file ownership and permissions have a
>> default (e.g. umask), which is also a way to set them. How to
>> consistently manage that? What if the application wants to protect its
>> files with chmod 0400?
>
> what do you mean by this? do you mean that we should have a set of
> default permissions for files created by applications within the
> sandbox, so that it can update metadata of its own file.

I mean that we need a consistent access control system, and for this we
need to consider all the ways an extended attribute can be set.

We can either extend the meaning of current access rights (controlled
with a ruleset flag for compatibility reasons), or create new access
rights. I think it would be better to add new dedicated rights to make
it more explicit and flexible.

I'm not sure about the right approach to properly control file
permission. We need to think about it. Do you have some ideas?

BTW, utimes can be controlled with the inode_setattr() LSM hook. Being
able to control arbitrary file time modification could be part of the
FS_WRITE_SAFE_METADATA, but modification and access time should always
be updated according to the file operation.


>
>>
>> About the naming, I think we can start with:
>> - LANDLOCK_ACCESS_FS_READ_METADATA (read any file/dir metadata);
>> - LANDLOCK_ACCESS_FS_WRITE_SAFE_METADATA: change file times, user xattr;
>
> do you mean we should have permission controls on metadata level or
> operation level? e.g. should we allow update on user xattr but deny
> update on security xattr? or should we disallow update on any xattr?
>
>> - LANDLOCK_ACCESS_FS_WRITE_UNSAFE_METADATA: interpreted by the kernel
>> (could change non-Landlock DAC or MAC, which could be considered as a
>> policy bypass; or other various xattr that might be interpreted by
>> filesystems), this should be denied most of the time.
>
> do you mean FS_WRITE_UNSAFE_METADATA is security-related? and
> FS_WRITE_SAFE_METADATA is non-security-related?

Yes, FS_WRITE_UNSAFE_METADATA would be for security related
xattr/chmod/chown, and FS_WRITE_SAFE_METADATA for non-security xattr.
Both are mutually exclusive. This would involve the inode_setattr and
inode_setxattr LSM hooks. Looking at the calling sites, it seems
possible to replace all inode arguments with paths.

2022-11-14 14:29:45

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH -next v2 3/6] landlock: add chmod and chown support


On 29/10/2022 10:33, xiujianfeng wrote:
> Hi,
>
> 在 2022/9/2 1:34, Mickaël Salaün 写道:
>> CCing [email protected]
>>
>>
>> On 01/09/2022 15:06, xiujianfeng wrote:
>>> Hi,
>>>
>>> 在 2022/8/30 0:01, Mickaël Salaün 写道:
>>>>
>>>> On 29/08/2022 03:17, xiujianfeng wrote:
>>>>>
>>>>> Hi,
>>>>>
>>>>> 在 2022/8/28 3:30, Günther Noack 写道:
>>>>>> Hello!
>>>>>>
>>>>>> the mapping between Landlock rights to LSM hooks is now as follows in
>>>>>> your patch set:
>>>>>>
>>>>>> * LANDLOCK_ACCESS_FS_CHMOD controls hook_path_chmod
>>>>>> * LANDLOCK_ACCESS_FS_CHGRP controls hook_path_chown
>>>>>>      (this hook can restrict both the chown(2) and chgrp(2) syscalls)
>>>>>>
>>>>>> Is this the desired mapping?
>>>>>>
>>>>>> The previous discussion I found on the topic was in
>>>>>>
>>>>>> [1]
>>>>>> https://lore.kernel.org/all/[email protected]/
>>>>>>
>>>>>>
>>>>>> [2]
>>>>>> https://lore.kernel.org/all/[email protected]/
>>>>>>
>>>>>>
>>>>>> [3]
>>>>>> https://lore.kernel.org/all/[email protected]/
>>>>>>
>>>>>>
>>>>>>
>>>>>> In my understanding the main arguments were the ones in [2] and [3].
>>>>>>
>>>>>> There were no further responses to [3], so I was under the impression
>>>>>> that we were gravitating towards an approach where the
>>>>>> file-metadata-modification operations were grouped more coarsely?
>>>>>>
>>>>>> For example with the approach suggested in [3], which would be to
>>>>>> group the operations coarsely into (a) one Landlock right for
>>>>>> modifying file metadata that is used in security contexts, and (b) one
>>>>>> Landlock right for modifying metadata that was used in non-security
>>>>>> contexts. That would mean that there would be:
>>>>>>
>>>>>> (a) LANDLOCK_ACCESS_FS_MODIFY_SECURITY_ATTRIBUTES to control the
>>>>>> following operations:
>>>>>>      * chmod(2)-variants through hook_path_chmod,
>>>>>>      * chown(2)-variants and chgrp(2)-variants through
>>>>>> hook_path_chown,
>>>>>>      * setxattr(2)-variants and removexattr(2)-variants for extended
>>>>>>        attributes that are not "user extended attributes" as
>>>>>> described in
>>>>>>        xattr(7) through hook_inode_setxattr and hook_inode_removexattr
>>>>>>
>>>>>> (b) LANDLOCK_ACCESS_FS_MODIFY_NON_SECURITY_ATTRIBUTES to control the
>>>>>> following operations:
>>>>>>      * utimes(2) and other operations for setting other non-security
>>>>>>        sensitive attributes, probably through hook_inode_setattr(?)
>>>>>>      * xattr modifications like above, but for the "user extended
>>>>>>        attributes", though hook_inode_setxattr and
>>>>>> hook_inode_removexattr
>>>>>>
>>>>>> In my mind, this would be a sensible grouping, and it would also help
>>>>>> to decouple the userspace-exposed API from the underlying
>>>>>> implementation, as Casey suggested to do in [2].
>>>>>>
>>>>>> Specifically for this patch set, if you want to use this grouping, you
>>>>>> would only need to add one new Landlock right
>>>>>> (LANDLOCK_ACCESS_FS_MODIFY_SECURITY_ATTRIBUTES) as described above
>>>>>> under (a) (and maybe we can find a shorter name for it... :))?
>>>>>>
>>>>>> Did I miss any operations here that would be necessary to restrict?
>>>>>>
>>>>>> Would that make sense to you? Xiu, what is your opinion on how this
>>>>>> should be grouped? Do you have use cases in mind where a more
>>>>>> fine-grained grouping would be required?
>>>>>
>>>>> I apologize I may missed that discussion when I prepared v2:(
>>>>>
>>>>> Yes, agreed, this grouping is more sensible and resonnable. so in this
>>>>> patchset only one right will be added, and I suppose the first commit
>>>>> which expand access_mask_t to u32 can be droped.
>>>>>
>>>>>>
>>>>>> —Günther
>>>>>>
>>>>>> P.S.: Regarding utimes: The hook_inode_setattr hook *also* gets called
>>>>>> on a variety on attribute changes including file ownership, file size
>>>>>> and file mode, so it might potentially interact with a bunch of other
>>>>>> existing Landlock rights. Maybe that is not the right approach. In any
>>>>>> case, it seems like it might require more thinking and it might be
>>>>>> sensible to do that in a separate patch set IMHO.
>>>>>
>>>>> Thanks for you reminder, that seems it's more complicated to support
>>>>> utimes, so I think we'd better not support it in this patchset.
>>>>
>>>> The issue with this approach is that it makes it impossible to properly
>>>> group such access rights. Indeed, to avoid inconsistencies and much more
>>>> complexity, we cannot extend a Landlock access right once it is defined.
>>>>
>>>> We also need to consider that file ownership and permissions have a
>>>> default (e.g. umask), which is also a way to set them. How to
>>>> consistently manage that? What if the application wants to protect its
>>>> files with chmod 0400?
>>>
>>> what do you mean by this? do you mean that we should have a set of
>>> default permissions for files created by applications within the
>>> sandbox, so that it can update metadata of its own file.
>>
>> I mean that we need a consistent access control system, and for this we
>> need to consider all the ways an extended attribute can be set.
>>
>> We can either extend the meaning of current access rights (controlled
>> with a ruleset flag for compatibility reasons), or create new access
>> rights. I think it would be better to add new dedicated rights to make
>> it more explicit and flexible.
>>
>> I'm not sure about the right approach to properly control file
>> permission. We need to think about it. Do you have some ideas?
>>
>> BTW, utimes can be controlled with the inode_setattr() LSM hook. Being
>> able to control arbitrary file time modification could be part of the
>> FS_WRITE_SAFE_METADATA, but modification and access time should always
>> be updated according to the file operation.
>>
>>
>>>
>>>>
>>>> About the naming, I think we can start with:
>>>> - LANDLOCK_ACCESS_FS_READ_METADATA (read any file/dir metadata);
>>>> - LANDLOCK_ACCESS_FS_WRITE_SAFE_METADATA: change file times, user xattr;
>>>
>>> do you mean we should have permission controls on metadata level or
>>> operation level? e.g. should we allow update on user xattr but deny
>>> update on security xattr? or should we disallow update on any xattr?
>>>
>>>> - LANDLOCK_ACCESS_FS_WRITE_UNSAFE_METADATA: interpreted by the kernel
>>>> (could change non-Landlock DAC or MAC, which could be considered as a
>>>> policy bypass; or other various xattr that might be interpreted by
>>>> filesystems), this should be denied most of the time.
>>>
>>> do you mean FS_WRITE_UNSAFE_METADATA is security-related? and
>>> FS_WRITE_SAFE_METADATA is non-security-related?
>>
>> Yes, FS_WRITE_UNSAFE_METADATA would be for security related
>> xattr/chmod/chown, and FS_WRITE_SAFE_METADATA for non-security xattr.
>> Both are mutually exclusive. This would involve the inode_setattr and
>> inode_setxattr LSM hooks. Looking at the calling sites, it seems
>> possible to replace all inode arguments with paths.

I though about differentiating user xattr, atime/mtime, DAC
(chown/chmod, posix ACLs), and other xattr, but it would be too complex
to get a consistent approach because of indirect consequences (e.g.
controlling umask, setegid, settimeofday…). Let's make it simple for now.

Here is an update on my previous proposal:

LANDLOCK_ACCESS_FS_READ_METADATA to read any file/dir metadata (i.e.
inode attr and xattr). In practice, for most use cases, this access
right should be granted whenever LANDLOCK_ACCESS_READ_DIR is allowed.

LANDLOCK_ACCESS_FS_WRITE_METADATA to *explicitly* write any inode attr
or xattr (i.e. chmod, chown, utime, and all xattr). It should be noted
that file modification time and access time should always be updated
according to the file operation (e.g. write, truncate) even when this
access is not explicitly allowed (according to vfs_utimes(),
ATTR_TIMES_SET and ATTR_TOUCH should enable to differentiate from
implicit time changes).


>
> Sorry for the late reply, I have problems with this work, for example,
> before:
> security_inode_setattr(struct user_namespace *mnt_userns,
> struct dentry *dentry,
> struct iattr *attr)
> after:
> security_inode_setattr(struct user_namespace *mnt_userns,
> struct path *path,
> struct iattr *attr)
> then I change the second argument in notify_change() from struct *dentry
> to struct path *, that makes this kind of changes in fs/overlayfs/
> spread to lots of places because overlayfs basicly uses dentry instead
> of path, the worst case may be here:
>
> ovl_special_inode_operations.set_acl hook calls:
> -->
> ovl_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
> struct posix_acl *acl, int type)
> -->
> ovl_setattr(struct user_namespace *mnt_userns, struct dentry
> *dentry,struct iattr *attr)
> -->
> ovl_do_notify_change(struct ovl_fs *ofs, struct dentry *upperdentry,
> struct iattr *attr)
>
> from the top of this callchain, I can not find a path to replace dentry,
> did I miss something? or do you have better idea?

I think this can be solved thanks to the ovl_path_real() helper.

2022-11-18 13:06:35

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH -next v2 3/6] landlock: add chmod and chown support


On 18/11/2022 10:03, xiujianfeng wrote:
>
>
> 在 2022/11/14 22:12, Mickaël Salaün 写道:
>>
>> On 29/10/2022 10:33, xiujianfeng wrote:
>>> Hi,
>>>
>>> 在 2022/9/2 1:34, Mickaël Salaün 写道:
>>>> CCing [email protected]
>>>>
>>>>
>>>> On 01/09/2022 15:06, xiujianfeng wrote:
>>>>> Hi,
>>>>>
>>>>> 在 2022/8/30 0:01, Mickaël Salaün 写道:
>>>>>>
>>>>>> On 29/08/2022 03:17, xiujianfeng wrote:
>>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> 在 2022/8/28 3:30, Günther Noack 写道:
>>>>>>>> Hello!
>>>>>>>>
>>>>>>>> the mapping between Landlock rights to LSM hooks is now as
>>>>>>>> follows in
>>>>>>>> your patch set:
>>>>>>>>
>>>>>>>> * LANDLOCK_ACCESS_FS_CHMOD controls hook_path_chmod
>>>>>>>> * LANDLOCK_ACCESS_FS_CHGRP controls hook_path_chown
>>>>>>>>       (this hook can restrict both the chown(2) and chgrp(2)
>>>>>>>> syscalls)
>>>>>>>>
>>>>>>>> Is this the desired mapping?
>>>>>>>>
>>>>>>>> The previous discussion I found on the topic was in
>>>>>>>>
>>>>>>>> [1]
>>>>>>>> https://lore.kernel.org/all/[email protected]/
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> [2]
>>>>>>>> https://lore.kernel.org/all/[email protected]/
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> [3]
>>>>>>>> https://lore.kernel.org/all/[email protected]/
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> In my understanding the main arguments were the ones in [2] and [3].
>>>>>>>>
>>>>>>>> There were no further responses to [3], so I was under the
>>>>>>>> impression
>>>>>>>> that we were gravitating towards an approach where the
>>>>>>>> file-metadata-modification operations were grouped more coarsely?
>>>>>>>>
>>>>>>>> For example with the approach suggested in [3], which would be to
>>>>>>>> group the operations coarsely into (a) one Landlock right for
>>>>>>>> modifying file metadata that is used in security contexts, and
>>>>>>>> (b) one
>>>>>>>> Landlock right for modifying metadata that was used in non-security
>>>>>>>> contexts. That would mean that there would be:
>>>>>>>>
>>>>>>>> (a) LANDLOCK_ACCESS_FS_MODIFY_SECURITY_ATTRIBUTES to control the
>>>>>>>> following operations:
>>>>>>>>       * chmod(2)-variants through hook_path_chmod,
>>>>>>>>       * chown(2)-variants and chgrp(2)-variants through
>>>>>>>> hook_path_chown,
>>>>>>>>       * setxattr(2)-variants and removexattr(2)-variants for
>>>>>>>> extended
>>>>>>>>         attributes that are not "user extended attributes" as
>>>>>>>> described in
>>>>>>>>         xattr(7) through hook_inode_setxattr and
>>>>>>>> hook_inode_removexattr
>>>>>>>>
>>>>>>>> (b) LANDLOCK_ACCESS_FS_MODIFY_NON_SECURITY_ATTRIBUTES to control the
>>>>>>>> following operations:
>>>>>>>>       * utimes(2) and other operations for setting other
>>>>>>>> non-security
>>>>>>>>         sensitive attributes, probably through hook_inode_setattr(?)
>>>>>>>>       * xattr modifications like above, but for the "user extended
>>>>>>>>         attributes", though hook_inode_setxattr and
>>>>>>>> hook_inode_removexattr
>>>>>>>>
>>>>>>>> In my mind, this would be a sensible grouping, and it would also
>>>>>>>> help
>>>>>>>> to decouple the userspace-exposed API from the underlying
>>>>>>>> implementation, as Casey suggested to do in [2].
>>>>>>>>
>>>>>>>> Specifically for this patch set, if you want to use this
>>>>>>>> grouping, you
>>>>>>>> would only need to add one new Landlock right
>>>>>>>> (LANDLOCK_ACCESS_FS_MODIFY_SECURITY_ATTRIBUTES) as described above
>>>>>>>> under (a) (and maybe we can find a shorter name for it... :))?
>>>>>>>>
>>>>>>>> Did I miss any operations here that would be necessary to restrict?
>>>>>>>>
>>>>>>>> Would that make sense to you? Xiu, what is your opinion on how this
>>>>>>>> should be grouped? Do you have use cases in mind where a more
>>>>>>>> fine-grained grouping would be required?
>>>>>>>
>>>>>>> I apologize I may missed that discussion when I prepared v2:(
>>>>>>>
>>>>>>> Yes, agreed, this grouping is more sensible and resonnable. so in
>>>>>>> this
>>>>>>> patchset only one right will be added, and I suppose the first commit
>>>>>>> which expand access_mask_t to u32 can be droped.
>>>>>>>
>>>>>>>>
>>>>>>>> —Günther
>>>>>>>>
>>>>>>>> P.S.: Regarding utimes: The hook_inode_setattr hook *also* gets
>>>>>>>> called
>>>>>>>> on a variety on attribute changes including file ownership, file
>>>>>>>> size
>>>>>>>> and file mode, so it might potentially interact with a bunch of
>>>>>>>> other
>>>>>>>> existing Landlock rights. Maybe that is not the right approach.
>>>>>>>> In any
>>>>>>>> case, it seems like it might require more thinking and it might be
>>>>>>>> sensible to do that in a separate patch set IMHO.
>>>>>>>
>>>>>>> Thanks for you reminder, that seems it's more complicated to support
>>>>>>> utimes, so I think we'd better not support it in this patchset.
>>>>>>
>>>>>> The issue with this approach is that it makes it impossible to
>>>>>> properly
>>>>>> group such access rights. Indeed, to avoid inconsistencies and much
>>>>>> more
>>>>>> complexity, we cannot extend a Landlock access right once it is
>>>>>> defined.
>>>>>>
>>>>>> We also need to consider that file ownership and permissions have a
>>>>>> default (e.g. umask), which is also a way to set them. How to
>>>>>> consistently manage that? What if the application wants to protect its
>>>>>> files with chmod 0400?
>>>>>
>>>>> what do you mean by this? do you mean that we should have a set of
>>>>> default permissions for files created by applications within the
>>>>> sandbox, so that it can update metadata of its own file.
>>>>
>>>> I mean that we need a consistent access control system, and for this we
>>>> need to consider all the ways an extended attribute can be set.
>>>>
>>>> We can either extend the meaning of current access rights (controlled
>>>> with a ruleset flag for compatibility reasons), or create new access
>>>> rights. I think it would be better to add new dedicated rights to make
>>>> it more explicit and flexible.
>>>>
>>>> I'm not sure about the right approach to properly control file
>>>> permission. We need to think about it. Do you have some ideas?
>>>>
>>>> BTW, utimes can be controlled with the inode_setattr() LSM hook. Being
>>>> able to control arbitrary file time modification could be part of the
>>>> FS_WRITE_SAFE_METADATA, but modification and access time should always
>>>> be updated according to the file operation.
>>>>
>>>>
>>>>>
>>>>>>
>>>>>> About the naming, I think we can start with:
>>>>>> - LANDLOCK_ACCESS_FS_READ_METADATA (read any file/dir metadata);
>>>>>> - LANDLOCK_ACCESS_FS_WRITE_SAFE_METADATA: change file times, user
>>>>>> xattr;
>>>>>
>>>>> do you mean we should have permission controls on metadata level or
>>>>> operation level? e.g. should we allow update on user xattr but deny
>>>>> update on security xattr? or should we disallow update on any xattr?
>>>>>
>>>>>> - LANDLOCK_ACCESS_FS_WRITE_UNSAFE_METADATA: interpreted by the kernel
>>>>>> (could change non-Landlock DAC or MAC, which could be considered as a
>>>>>> policy bypass; or other various xattr that might be interpreted by
>>>>>> filesystems), this should be denied most of the time.
>>>>>
>>>>> do you mean FS_WRITE_UNSAFE_METADATA is security-related? and
>>>>> FS_WRITE_SAFE_METADATA is non-security-related?
>>>>
>>>> Yes, FS_WRITE_UNSAFE_METADATA would be for security related
>>>> xattr/chmod/chown, and FS_WRITE_SAFE_METADATA for non-security xattr.
>>>> Both are mutually exclusive. This would involve the inode_setattr and
>>>> inode_setxattr LSM hooks. Looking at the calling sites, it seems
>>>> possible to replace all inode arguments with paths.
>>
>> I though about differentiating user xattr, atime/mtime, DAC
>> (chown/chmod, posix ACLs), and other xattr, but it would be too complex
>> to get a consistent approach because of indirect consequences (e.g.
>> controlling umask, setegid, settimeofday…). Let's make it simple for now.
>>
>> Here is an update on my previous proposal:
>>
>> LANDLOCK_ACCESS_FS_READ_METADATA to read any file/dir metadata (i.e.
>> inode attr and xattr). In practice, for most use cases, this access
>> right should be granted whenever LANDLOCK_ACCESS_READ_DIR is allowed.
>>
>> LANDLOCK_ACCESS_FS_WRITE_METADATA to *explicitly* write any inode attr
>> or xattr (i.e. chmod, chown, utime, and all xattr). It should be noted
>> that file modification time and access time should always be updated
>> according to the file operation (e.g. write, truncate) even when this
>> access is not explicitly allowed (according to vfs_utimes(),
>> ATTR_TIMES_SET and ATTR_TOUCH should enable to differentiate from
>> implicit time changes).
>>
> Thanks, I analyzed the relevant functions and the use of lsm hooks.
> so I think what to do will be as follows:
>
> LANDLOCK_ACCESS_FS_WRITE_METADATA controls the following hooks:
> 1.security_path_chmod
> 2.security_path_chown

These two chmod/chown hooks would be redundant with
security_inode_setattr(). We then don't need to implement them.


> 3.security_inode_setattr
> 4.security_inode_setxattr
> 5.security_inode_removexattr > 6.security_inode_set_acl

Good catch. This new security_inode_set_acl hook is a good example of
API refactoring. BTW, the related Cc list should be included in your
next patch series.

>
> LANDLOCK_ACCESS_FS_READ_METADATA controls the following hooks:
> 1.security_inode_getattr
> 2.security_inode_get_acl
> 3.security_inode_getxattr

Correct

>
> and the following 7 hooks are using struct dentry * as parameter, should
> be changed to struct path *, and also their callers.
>
> security_inode_setattr
> security_inode_setxattr
> security_inode_removexattr
> security_inode_set_acl
> security_inode_getattr
> security_inode_get_acl
> security_inode_getxattr
>
> Looks like it's a big change.

Your proposed approach looks good, and this will indeed touch a lot of
files.

Because it interacts a lot with the filesystem subsystem, I propose to
first write a set of patches that refactor the security_inode_*attr and
security_inode_*_acl hooks to use struct file (or struct path when it
makes sense) instead of struct dentry/inode (and to remove struct
user_namespace as argument because it can be inferred thanks to
file_mnt_user_ns). As for [1], using struct file only makes sense for a
specific set of calls, and struct path should be used otherwise (e.g.
syscalls dealing with file descriptors vs. with file paths).

You need to base this work on Christian's branch to be up-to-date with
ongoing FS changes. I suggest to create one patch per function API
change e.g., notify_change (merge the mnt_userns and dentry in a file
argument), struct inode_operations.setattr (use a file argument instead
of dentry)…

Once this refactoring will be in -next, the landlock_file_security
changes [1] will already be merged in master, and you will then be able
to work on the Landlock specific parts with the new hooks.

[1] https://git.kernel.org/mic/c/b9f5ce27c8f8


>
>>
>>>
>>> Sorry for the late reply, I have problems with this work, for example,
>>> before:
>>> security_inode_setattr(struct user_namespace *mnt_userns,
>>>                                            struct dentry *dentry,
>>>                                            struct iattr *attr)
>>> after:
>>> security_inode_setattr(struct user_namespace *mnt_userns,
>>>                                            struct path *path,
>>>                                            struct iattr *attr)
>>> then I change the second argument in notify_change() from struct *dentry
>>> to struct path *, that makes this kind of changes in fs/overlayfs/
>>> spread to lots of places because overlayfs basicly uses dentry instead
>>> of path, the worst case may be here:
>>>
>>> ovl_special_inode_operations.set_acl hook calls:
>>> -->
>>> ovl_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
>>> struct posix_acl *acl, int type)
>>> -->
>>> ovl_setattr(struct user_namespace *mnt_userns, struct dentry
>>> *dentry,struct iattr *attr)
>>> -->
>>> ovl_do_notify_change(struct ovl_fs *ofs, struct dentry *upperdentry,
>>> struct iattr *attr)
>>>
>>> from the top of this callchain, I can not find a path to replace dentry,
>>> did I miss something? or do you have better idea?
>>
>> I think this can be solved thanks to the ovl_path_real() helper.
>> .