From: Justin Suess <utilityemal77@gmail.com>
To: Tingmao Wang <m@maowtm.org>
Cc: "Mickaël Salaün" <mic@digikod.net>,
"Günther Noack" <gnoack3000@gmail.com>, "Jan Kara" <jack@suse.cz>,
"Abhinav Saxena" <xandfury@gmail.com>,
linux-security-module@vger.kernel.org
Subject: Re: [PATCH v9 4/9] samples/landlock: Add quiet flag support to sandboxer
Date: Thu, 28 May 2026 22:34:55 -0400 [thread overview]
Message-ID: <ahj6gwXIbb58VFsZ@zenbox> (raw)
In-Reply-To: <7d5ad9631a51df6c2b857ff9c0122ff8ed491b7d.1779843375.git.m@maowtm.org>
On Wed, May 27, 2026 at 02:01:14AM +0100, Tingmao Wang wrote:
> Adds ability to set which access bits to quiet via LL_*_QUIET_ACCESS (FS,
> NET or SCOPED), and attach quiet flags to individual objects via
> LL_*_QUIET for FS and NET.
>
> Signed-off-by: Tingmao Wang <m@maowtm.org>
> ---
>
> Changes in v9:
> - Add udp connect / bind quiet flag support
>
> Changes in v8:
> - Rebase on top of mic/next
> - populate_ruleset_net() already does not require the env var to be
> present, so remove redundant comment and check above
> populate_ruleset_net(ENV_NET_QUIET_NAME, ...).
>
> Changes in v6:
> - Make populate_ruleset_{fs,net} take a flags argument instead of a bool
> quiet (suggested by Justin Suess)
> - Fix if braces style
>
> Changes in v3:
> - Minor change to the above commit message.
>
> Changes in v2:
> - Added new environment variables to control which quiet access bits to
> set on the rule, and populate quiet_access_* from it.
> - Added support for quieting net rules and scoped access. Renamed patch
> title.
> - Increment ABI version
>
> samples/landlock/sandboxer.c | 134 ++++++++++++++++++++++++++++++++---
> 1 file changed, 123 insertions(+), 11 deletions(-)
>
> diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
> index 94e399e6b146..74ee53afed6a 100644
> --- a/samples/landlock/sandboxer.c
> +++ b/samples/landlock/sandboxer.c
> @@ -58,9 +58,14 @@ static inline int landlock_restrict_self(const int ruleset_fd,
>
> #define ENV_FS_RO_NAME "LL_FS_RO"
> #define ENV_FS_RW_NAME "LL_FS_RW"
> +#define ENV_FS_QUIET_NAME "LL_FS_QUIET"
> +#define ENV_FS_QUIET_ACCESS_NAME "LL_FS_QUIET_ACCESS"
> #define ENV_TCP_BIND_NAME "LL_TCP_BIND"
> #define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT"
> +#define ENV_NET_QUIET_NAME "LL_NET_QUIET"
> +#define ENV_NET_QUIET_ACCESS_NAME "LL_NET_QUIET_ACCESS"
> #define ENV_SCOPED_NAME "LL_SCOPED"
> +#define ENV_SCOPED_QUIET_ACCESS_NAME "LL_SCOPED_QUIET_ACCESS"
> #define ENV_FORCE_LOG_NAME "LL_FORCE_LOG"
> #define ENV_UDP_BIND_NAME "LL_UDP_BIND"
> #define ENV_UDP_CONNECT_SEND_NAME "LL_UDP_CONNECT_SEND"
> @@ -119,7 +124,7 @@ static int parse_path(char *env_path, const char ***const path_list)
> /* clang-format on */
>
> static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
> - const __u64 allowed_access)
> + const __u64 allowed_access, __u32 flags)
> {
> int num_paths, i, ret = 1;
> char *env_path_name;
> @@ -169,7 +174,7 @@ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
> if (!S_ISDIR(statbuf.st_mode))
> path_beneath.allowed_access &= ACCESS_FILE;
> if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
> - &path_beneath, 0)) {
> + &path_beneath, flags)) {
> fprintf(stderr,
> "Failed to update the ruleset with \"%s\": %s\n",
> path_list[i], strerror(errno));
> @@ -187,7 +192,7 @@ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
> }
>
> static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
> - const __u64 allowed_access)
> + const __u64 allowed_access, __u32 flags)
> {
> int ret = 1;
> char *env_port_name, *env_port_name_next, *strport;
> @@ -215,7 +220,7 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
> }
> net_port.port = port;
> if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
> - &net_port, 0)) {
> + &net_port, flags)) {
> fprintf(stderr,
> "Failed to update the ruleset with port \"%llu\": %s\n",
> net_port.port, strerror(errno));
> @@ -303,6 +308,58 @@ static bool check_ruleset_scope(const char *const env_var,
>
> /* clang-format on */
>
> +static int add_quiet_access(__u64 *const quiet_access,
> + const __u64 handled_access,
> + const char *const env_var, const bool default_all)
> +{
> + char *env_quiet_access, *env_quiet_access_next, *str_access;
> +
> + if (default_all)
> + *quiet_access = handled_access;
> + else
> + *quiet_access = 0;
> +
> + env_quiet_access = getenv(env_var);
> + if (!env_quiet_access)
> + return 0;
> +
> + env_quiet_access = strdup(env_quiet_access);
> + env_quiet_access_next = env_quiet_access;
> + unsetenv(env_var);
> + *quiet_access = 0;
> +
> + while ((str_access = strsep(&env_quiet_access_next, ENV_DELIMITER))) {
> + if (strcmp(str_access, "") == 0)
> + continue;
> + else if (strcmp(str_access, "r") == 0)
> + *quiet_access |= ACCESS_FS_ROUGHLY_READ;
> + else if (strcmp(str_access, "w") == 0)
> + *quiet_access |= ACCESS_FS_ROUGHLY_WRITE;
> + else if (strcmp(str_access, "b") == 0)
> + *quiet_access |= LANDLOCK_ACCESS_NET_BIND_TCP;
> + else if (strcmp(str_access, "c") == 0)
> + *quiet_access |= LANDLOCK_ACCESS_NET_CONNECT_TCP;
> + else if (strcmp(str_access, "ub") == 0)
> + *quiet_access |= LANDLOCK_ACCESS_NET_BIND_UDP;
> + else if (strcmp(str_access, "uc") == 0)
> + *quiet_access |= LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
> + else if (strcmp(str_access, "a") == 0)
> + *quiet_access |= LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET;
> + else if (strcmp(str_access, "s") == 0)
> + *quiet_access |= LANDLOCK_SCOPE_SIGNAL;
You don't need to do it in this patch but these strings should probably
be centrally defined somewhere... as we add more they could be easy to
mix up.
> + else {
> + fprintf(stderr, "Unknown quiet access \"%s\"\n",
> + str_access);
> + free(env_quiet_access);
> + return -1;
> + }
> + }
> +
> + free(env_quiet_access);
> + *quiet_access &= handled_access;
> + return 0;
> +}
> +
> #define LANDLOCK_ABI_LAST 10
>
> #define XSTR(s) #s
> @@ -336,6 +393,22 @@ static const char help[] =
> "\n"
> "A sandboxer should not log denied access requests to avoid spamming logs, "
> "but to test audit we can set " ENV_FORCE_LOG_NAME "=1\n"
> + ENV_FS_QUIET_NAME " and " ENV_NET_QUIET_NAME ", both optional, can then be used "
> + "to make access to some denied paths or network ports not trigger audit logging.\n"
> + ENV_FS_QUIET_ACCESS_NAME " and " ENV_NET_QUIET_ACCESS_NAME " can be used to specify "
> + "which accesses should be quieted (defaults to all):\n"
> + "* " ENV_FS_QUIET_ACCESS_NAME ": file system accesses to quiet\n"
> + " - \"r\" to quiet all file/dir read accesses\n"
> + " - \"w\" to quiet all file/dir write accesses\n"
> + "* " ENV_NET_QUIET_ACCESS_NAME ": network accesses to quiet\n"
> + " - \"b\" to quiet tcp bind denials\n"
> + " - \"c\" to quiet tcp connect denials\n"
> + " - \"ub\" to quiet udp bind denials\n"
> + " - \"uc\" to quiet udp connect / send denials\n"
> + "In addition, " ENV_SCOPED_QUIET_ACCESS_NAME " can be set to quiet all denials for "
> + "scoped actions (defaults to none).\n"
> + " - \"a\" to quiet abstract unix socket denials\n"
> + " - \"s\" to quiet signal denials\n"
> "\n"
> "Example:\n"
> ENV_FS_RO_NAME "=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" "
> @@ -368,7 +441,12 @@ int main(const int argc, char *const argv[], char *const *const envp)
> LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
> .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
> LANDLOCK_SCOPE_SIGNAL,
> + .quiet_access_fs = 0,
> + .quiet_access_net = 0,
> + .quiet_scoped = 0,
> };
> +
> + bool quiet_supported = true;
> int supported_restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
> int set_restrict_flags = 0;
>
> @@ -459,6 +537,9 @@ int main(const int argc, char *const argv[], char *const *const envp)
> ruleset_attr.handled_access_net &=
> ~(LANDLOCK_ACCESS_NET_BIND_UDP |
> LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
> + __attribute__((fallthrough));
The fallthrough should be the last statement in the switch case;
otherwise this causes a build warning.
> + /* Don't add quiet flags for ABI < 10 later on. */
> + quiet_supported = false;
>
> /* Must be printed for any ABI < LANDLOCK_ABI_LAST. */
> fprintf(stderr,
> @@ -525,6 +606,25 @@ int main(const int argc, char *const argv[], char *const *const envp)
> unsetenv(ENV_FORCE_LOG_NAME);
> }
>
> + /*
> + * Add quiet for fs/net handled access bits. Doing this alone has no
> + * effect unless we later add quiet rules per FS_QUIET/NET_QUIET.
> + */
> + if (quiet_supported) {
> + if (add_quiet_access(&ruleset_attr.quiet_access_fs,
> + ruleset_attr.handled_access_fs,
> + ENV_FS_QUIET_ACCESS_NAME, true))
> + return 1;
> + if (add_quiet_access(&ruleset_attr.quiet_access_net,
> + ruleset_attr.handled_access_net,
> + ENV_NET_QUIET_ACCESS_NAME, true))
> + return 1;
> + if (add_quiet_access(&ruleset_attr.quiet_scoped,
> + ruleset_attr.scoped,
> + ENV_SCOPED_QUIET_ACCESS_NAME, false))
> + return 1;
> + }
> +
> ruleset_fd =
> landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> if (ruleset_fd < 0) {
> @@ -532,30 +632,42 @@ int main(const int argc, char *const argv[], char *const *const envp)
> return 1;
> }
>
> - if (populate_ruleset_fs(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) {
> + if (populate_ruleset_fs(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro, 0))
> goto err_close_ruleset;
> - }
> - if (populate_ruleset_fs(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) {
> + if (populate_ruleset_fs(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw, 0))
> goto err_close_ruleset;
> +
> + /* Don't require this env to be present. */
> + if (quiet_supported && getenv(ENV_FS_QUIET_NAME)) {
> + if (populate_ruleset_fs(ENV_FS_QUIET_NAME, ruleset_fd, 0,
> + LANDLOCK_ADD_RULE_QUIET))
> + goto err_close_ruleset;
> }
>
> if (populate_ruleset_net(ENV_TCP_BIND_NAME, ruleset_fd,
> - LANDLOCK_ACCESS_NET_BIND_TCP)) {
> + LANDLOCK_ACCESS_NET_BIND_TCP, 0)) {
> goto err_close_ruleset;
> }
> if (populate_ruleset_net(ENV_TCP_CONNECT_NAME, ruleset_fd,
> - LANDLOCK_ACCESS_NET_CONNECT_TCP)) {
> + LANDLOCK_ACCESS_NET_CONNECT_TCP, 0)) {
> goto err_close_ruleset;
> }
> if (populate_ruleset_net(ENV_UDP_BIND_NAME, ruleset_fd,
> - LANDLOCK_ACCESS_NET_BIND_UDP)) {
> + LANDLOCK_ACCESS_NET_BIND_UDP, 0)) {
> goto err_close_ruleset;
> }
> if (populate_ruleset_net(ENV_UDP_CONNECT_SEND_NAME, ruleset_fd,
> - LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP)) {
> + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, 0)) {
> goto err_close_ruleset;
> }
>
> + if (quiet_supported) {
> + if (populate_ruleset_net(ENV_NET_QUIET_NAME, ruleset_fd, 0,
> + LANDLOCK_ADD_RULE_QUIET)) {
> + goto err_close_ruleset;
> + }
> + }
> +
> if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
> perror("Failed to restrict privileges");
> goto err_close_ruleset;
> --
> 2.54.0
next prev parent reply other threads:[~2026-05-29 2:34 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-27 1:01 [PATCH v9 0/9] Implement LANDLOCK_ADD_RULE_QUIET Tingmao Wang
2026-05-27 1:01 ` [PATCH v9 1/9] landlock: Add a place for flags to layer rules Tingmao Wang
2026-05-28 22:01 ` Justin Suess
2026-05-27 1:01 ` [PATCH v9 2/9] landlock: Add API support and docs for the quiet flags Tingmao Wang
2026-05-27 1:01 ` [PATCH v9 3/9] landlock: Suppress logging when quiet flag is present Tingmao Wang
2026-05-27 1:01 ` [PATCH v9 4/9] samples/landlock: Add quiet flag support to sandboxer Tingmao Wang
2026-05-29 2:34 ` Justin Suess [this message]
2026-05-27 1:01 ` [PATCH v9 5/9] selftests/landlock: Replace hard-coded 16 with a constant Tingmao Wang
2026-05-27 1:01 ` [PATCH v9 6/9] selftests/landlock: add tests for quiet flag with fs rules Tingmao Wang
2026-05-27 1:01 ` [PATCH v9 7/9] selftests/landlock: add tests for quiet flag with net rules Tingmao Wang
2026-05-27 1:01 ` [PATCH v9 8/9] selftests/landlock: Add tests for quiet flag with scope Tingmao Wang
2026-05-27 1:01 ` [PATCH v9 9/9] selftests/landlock: Add tests for invalid use of quiet flag Tingmao Wang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ahj6gwXIbb58VFsZ@zenbox \
--to=utilityemal77@gmail.com \
--cc=gnoack3000@gmail.com \
--cc=jack@suse.cz \
--cc=linux-security-module@vger.kernel.org \
--cc=m@maowtm.org \
--cc=mic@digikod.net \
--cc=xandfury@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox