From 61b15442898eeb1db2d23b6b2eb72a705ceb368a Mon Sep 17 00:00:00 2001 From: Азалия Смарагдова Date: Mon, 15 Aug 2022 12:19:11 +0500 Subject: Landlock support has been added. --- src/man/firejail-profile.txt | 21 +++++++++++++++++++++ src/man/firejail.txt | 28 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) (limited to 'src/man') diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index 138aae8af..6e75aceed 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt @@ -497,6 +497,27 @@ Blacklist all Linux capabilities. .TP \fBcaps.keep capability,capability,capability Whitelist given Linux capabilities. +#ifdef HAVE_LANDLOCK +.TP +\fBlandlock-read path +Create a Landlock ruleset (if it doesn't already exist) and add a read access rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +.br + +.TP +\fBlandlock-write path +Create a Landlock ruleset (if it doesn't already exist) and add a write access rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +.br + +.TP +\fBlandlock-restricted-write path +Create a Landlock ruleset (if it doesn't already exist) and add a write access rule for path. This type of write access doesn't include the permission to create Unix domain sockets, FIFO pipes and block devices. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +.br + +.TP +\fBlandlock-execute path +Create a Landlock ruleset (if it doesn't already exist) and add an execution permission rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +.br +#endif .TP \fBmemory-deny-write-execute Install a seccomp filter to block attempts to create memory mappings diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 2d8adb0b7..7082fe0ab 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -1144,6 +1144,33 @@ Example: .br $ firejail --keep-var-tmp +#ifdef HAVE_LANDLOCK +.TP +\fB\-\-landlock-read=path +Create a Landlock ruleset (if it doesn't already exist) and add a read access rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +.br + +.TP +\fB\-\-landlock-write=path +Create a Landlock ruleset (if it doesn't already exist) and add a write access rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +.br + +.TP +\fB\-\-landlock-restricted-write=path +Create a Landlock ruleset (if it doesn't already exist) and add a write access rule for path. This type of write access doesn't include the permission to create Unix domain sockets, FIFO pipes and block devices. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +.br + +.TP +\fB\-\-landlock-execute=path +Create a Landlock ruleset (if it doesn't already exist) and add an execution permission rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +.br + +.br +Example: +.br +$ firejail \-\-landlock-read=/ \-\-landlock-restricted-write=/home \-\-landlock-execute=/usr +#endif + .TP \fB\-\-list List all sandboxes, see \fBMONITORING\fR section for more details. @@ -1261,6 +1288,7 @@ $ firejail --list .br 1312:netblue:browser-1312:firejail --name=browser --private firefox --no-remote .br + #ifdef HAVE_NETWORK .TP \fB\-\-net=bridge_interface -- cgit v1.2.3-54-g00ecf From 460fa7a6f98cc1e7aec2953e6523f32677d546c7 Mon Sep 17 00:00:00 2001 From: Азалия Смарагдова Date: Tue, 16 Aug 2022 12:03:50 +0500 Subject: Proposed fixes. --- contrib/vim/syntax/firejail.vim | 2 +- src/bash_completion/firejail.bash_completion.in | 8 ++-- src/firejail/firejail.h | 7 ++- src/firejail/landlock.c | 23 ++++++--- src/firejail/main.c | 63 +++++++++++++++++++++--- src/firejail/profile.c | 64 ++++++++++++++++++++++--- src/firejail/sandbox.c | 35 +++++++++----- src/firejail/usage.c | 10 ++-- src/firejail/util.c | 4 ++ src/man/firejail-profile.txt | 24 ++++++---- src/man/firejail.txt | 53 ++++++++++++++++---- src/zsh_completion/_firejail.in | 10 ++-- 12 files changed, 242 insertions(+), 61 deletions(-) (limited to 'src/man') diff --git a/contrib/vim/syntax/firejail.vim b/contrib/vim/syntax/firejail.vim index d3631b5df..7c1c33421 100644 --- a/contrib/vim/syntax/firejail.vim +++ b/contrib/vim/syntax/firejail.vim @@ -52,7 +52,7 @@ syn match fjVar /\v\$\{(CFG|DESKTOP|DOCUMENTS|DOWNLOADS|HOME|MUSIC|PATH|PICTURES " Commands grabbed from: src/firejail/profile.c " Generate list with: { rg -o 'strn?cmp\(ptr, "([^"]+) "' -r '$1' src/firejail/profile.c; echo private-lib; } | grep -vEx '(include|ignore|caps\.drop|caps\.keep|protocol|restrict-namespaces|seccomp|seccomp\.drop|seccomp\.keep|env|rmenv|net|ip)' | sort -u | tr $'\n' '|' # private-lib is special-cased in the code and doesn't match the regex; grep-ed patterns are handled later with 'syn match nextgroup=' directives (except for include which is special-cased as a fjCommandNoCond keyword) -syn match fjCommand /\v(apparmor|bind|blacklist|blacklist-nolog|cpu|defaultgw|dns|hostname|hosts-file|ip6|iprange|join-or-start|landlock-read|landlock-write|landlock-restricted-write|landlock-execute|mac|mkdir|mkfile|mtu|name|netfilter|netfilter6|netmask|nice|noblacklist|noexec|nowhitelist|overlay-named|private|private-bin|private-cwd|private-etc|private-home|private-lib|private-opt|private-srv|read-only|read-write|rlimit-as|rlimit-cpu|rlimit-fsize|rlimit-nofile|rlimit-nproc|rlimit-sigpending|timeout|tmpfs|veth-name|whitelist|xephyr-screen) / skipwhite contained +syn match fjCommand /\v(apparmor|bind|blacklist|blacklist-nolog|cpu|defaultgw|dns|hostname|hosts-file|ip6|iprange|join-or-start|landlock|landlock.proc|landlock.read|landlock.write|landlock.special|landlock.execute|mac|mkdir|mkfile|mtu|name|netfilter|netfilter6|netmask|nice|noblacklist|noexec|nowhitelist|overlay-named|private|private-bin|private-cwd|private-etc|private-home|private-lib|private-opt|private-srv|read-only|read-write|rlimit-as|rlimit-cpu|rlimit-fsize|rlimit-nofile|rlimit-nproc|rlimit-sigpending|timeout|tmpfs|veth-name|whitelist|xephyr-screen) / skipwhite contained " Generate list with: rg -o 'strn?cmp\(ptr, "([^ "]*[^ ])"' -r '$1' src/firejail/profile.c | grep -vEx '(include|rlimit|quiet)' | sed -e 's/\./\\./' | sort -u | tr $'\n' '|' # include/rlimit are false positives, quiet is special-cased below syn match fjCommand /\v(allow-debuggers|allusers|apparmor|caps|deterministic-exit-code|deterministic-shutdown|disable-mnt|ipc-namespace|keep-config-pulse|keep-dev-shm|keep-fd|keep-var-tmp|machine-id|memory-deny-write-execute|netfilter|no3d|noautopulse|nodbus|nodvd|nogroups|noinput|nonewprivs|noprinters|noroot|nosound|notv|nou2f|novideo|overlay|overlay-tmpfs|private|private-cache|private-cwd|private-dev|private-lib|private-tmp|seccomp|seccomp\.32|seccomp\.block-secondary|tracelog|writable-etc|writable-run-user|writable-var|writable-var-log|x11)$/ contained syn match fjCommand /ignore / nextgroup=fjCommand,fjCommandNoCond skipwhite contained diff --git a/src/bash_completion/firejail.bash_completion.in b/src/bash_completion/firejail.bash_completion.in index 83d11d766..4829f1fde 100644 --- a/src/bash_completion/firejail.bash_completion.in +++ b/src/bash_completion/firejail.bash_completion.in @@ -42,19 +42,19 @@ _firejail() _filedir -d return 0 ;; - --landlock-read) + --landlock.read) _filedir return 0 ;; - --landlock-write) + --landlock.write) _filedir return 0 ;; - --landlock-restricted-write) + --landlock.special) _filedir return 0 ;; - --landlock-execute) + --landlock.execute) _filedir return 0 ;; diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 35e2dbf50..9c2b53c18 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -44,7 +44,9 @@ extern int create_full_ruleset(); extern int add_read_access_rule_by_path(int rset_fd,char *allowed_path); -extern int add_write_access_rule_by_path(int rset_fd,char *allowed_path,int restricted); +extern int add_write_access_rule_by_path(int rset_fd,char *allowed_path); + +extern int add_create_special_rule_by_path(int rset_fd,char *allowed_path); extern int add_execute_rule_by_path(int rset_fd,char *allowed_path); @@ -305,7 +307,10 @@ extern int arg_seccomp32; // enable default seccomp filter for 32 bit arch extern int arg_seccomp_postexec; // need postexec ld.preload library? extern int arg_seccomp_block_secondary; // block any secondary architectures +#ifdef HAVE_LANDLOCK extern int arg_landlock; // Landlock ruleset file descriptor +extern int arg_landlock_proc; // Landlock rule for accessing /proc (0 for no access, 1 for read-only and 2 for read-write) +#endif extern int arg_caps_default_filter; // enable default capabilities filter extern int arg_caps_drop; // drop list diff --git a/src/firejail/landlock.c b/src/firejail/landlock.c index 5d6b0260e..67e2b2cfc 100644 --- a/src/firejail/landlock.c +++ b/src/firejail/landlock.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include int landlock_create_ruleset(struct landlock_ruleset_attr *rsattr,size_t size,__u32 flags) { @@ -17,6 +19,7 @@ int landlock_add_rule(int fd,enum landlock_rule_type t,void *attr,__u32 flags) { } int landlock_restrict_self(int fd,__u32 flags) { + prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0); int result = syscall(__NR_landlock_restrict_self,fd,flags); if (result!=0) return result; else { @@ -42,17 +45,23 @@ int add_read_access_rule_by_path(int rset_fd,char *allowed_path) { return result; } -int add_write_access_rule_by_path(int rset_fd,char *allowed_path,int restricted) { +int add_write_access_rule_by_path(int rset_fd,char *allowed_path) { int result; int allowed_fd = open(allowed_path,O_PATH | O_CLOEXEC); struct landlock_path_beneath_attr target; target.parent_fd = allowed_fd; - if (restricted==0) target.allowed_access = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | LANDLOCK_ACCESS_FS_MAKE_CHAR | LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_MAKE_SOCK | LANDLOCK_ACCESS_FS_MAKE_FIFO | LANDLOCK_ACCESS_FS_MAKE_BLOCK | LANDLOCK_ACCESS_FS_MAKE_SYM; - else if (restricted==1) target.allowed_access = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | LANDLOCK_ACCESS_FS_MAKE_CHAR | LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_MAKE_SYM; - else { - close(allowed_fd); - return -1; - } + target.allowed_access = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | LANDLOCK_ACCESS_FS_MAKE_CHAR | LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_MAKE_SYM; + result = landlock_add_rule(rset_fd,LANDLOCK_RULE_PATH_BENEATH,&target,0); + close(allowed_fd); + return result; +} + +int add_create_special_rule_by_path(int rset_fd,char *allowed_path) { + int result; + int allowed_fd = open(allowed_path,O_PATH | O_CLOEXEC); + struct landlock_path_beneath_attr target; + target.parent_fd = allowed_fd; + target.allowed_access = LANDLOCK_ACCESS_FS_MAKE_SOCK | LANDLOCK_ACCESS_FS_MAKE_FIFO | LANDLOCK_ACCESS_FS_MAKE_BLOCK; result = landlock_add_rule(rset_fd,LANDLOCK_RULE_PATH_BENEATH,&target,0); close(allowed_fd); return result; diff --git a/src/firejail/main.c b/src/firejail/main.c index c78d4d2b8..3f0dc960a 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -83,6 +83,7 @@ int arg_seccomp_error_action = 0; #ifdef HAVE_LANDLOCK int arg_landlock = -1; // Landlock ruleset file descriptor (-1 if it doesn't exist) +int arg_landlock_proc = 0; // Landlock rule for accessing /proc (0 for no access, 1 for read-only and 2 for read-write) #endif int arg_caps_default_filter = 0; // enable default capabilities filter @@ -1406,25 +1407,75 @@ int main(int argc, char **argv, char **envp) { exit_err_feature("seccomp"); } #ifdef HAVE_LANDLOCK - else if (strncmp(argv[i], "--landlock-read=", 16) == 0) { + else if (strcmp(argv[i], "--landlock") == 0) { + if (arg_landlock == -1) arg_landlock = create_full_ruleset(); + const char *home_dir = env_get("HOME"); + int home_fd = open(home_dir,O_PATH | O_CLOEXEC); + struct landlock_path_beneath_attr target; + target.parent_fd = home_fd; + target.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | LANDLOCK_ACCESS_FS_MAKE_CHAR | LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_MAKE_SYM; + if (landlock_add_rule(arg_landlock,LANDLOCK_RULE_PATH_BENEATH,&target,0)) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + close(home_fd); + if (add_read_access_rule_by_path(arg_landlock, "/bin/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_execute_rule_by_path(arg_landlock, "/bin/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/dev/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/etc/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/lib/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_execute_rule_by_path(arg_landlock, "/lib/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/opt/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_execute_rule_by_path(arg_landlock, "/opt/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/usr/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_execute_rule_by_path(arg_landlock, "/usr/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/var/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + } + else if (strncmp(argv[i], "--landlock.proc=", 16) == 0) { + if (strncmp(argv[i]+16, "no", 2) == 0) arg_landlock_proc = 0; + else if (strncmp(argv[i]+16, "ro", 2) == 0) arg_landlock_proc = 1; + else if (strncmp(argv[i]+16, "rw", 2) == 0) arg_landlock_proc = 2; + } + else if (strncmp(argv[i], "--landlock.read=", 16) == 0) { if (arg_landlock == -1) arg_landlock = create_full_ruleset(); if (add_read_access_rule_by_path(arg_landlock, argv[i]+16)) { fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); } } - else if (strncmp(argv[i], "--landlock-write=", 17) == 0) { + else if (strncmp(argv[i], "--landlock.write=", 17) == 0) { if (arg_landlock == -1) arg_landlock = create_full_ruleset(); - if (add_write_access_rule_by_path(arg_landlock, argv[i]+17,0)) { + if (add_write_access_rule_by_path(arg_landlock, argv[i]+17)) { fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); } } - else if (strncmp(argv[i], "--landlock-restricted-write=", 28) == 0) { + else if (strncmp(argv[i], "--landlock.special=", 17) == 0) { if (arg_landlock == -1) arg_landlock = create_full_ruleset(); - if (add_write_access_rule_by_path(arg_landlock, argv[i]+28,1)) { + if (add_create_special_rule_by_path(arg_landlock, argv[i]+17)) { fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); } } - else if (strncmp(argv[i], "--landlock-execute=", 19) == 0) { + else if (strncmp(argv[i], "--landlock.execute=", 19) == 0) { if (arg_landlock == -1) arg_landlock = create_full_ruleset(); if (add_execute_rule_by_path(arg_landlock, argv[i]+19)) { fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 64a82767c..2969db85b 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -1047,28 +1047,80 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { #ifdef HAVE_LANDLOCK // Landlock ruleset paths - if (strncmp(ptr, "landlock-read ", 14) == 0) { + if (strcmp(ptr, "landlock") == 0) { + if (arg_landlock == -1) arg_landlock = create_full_ruleset(); + const char *home_dir = env_get("HOME"); + int home_fd = open(home_dir,O_PATH | O_CLOEXEC); + struct landlock_path_beneath_attr target; + target.parent_fd = home_fd; + target.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | LANDLOCK_ACCESS_FS_MAKE_CHAR | LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_MAKE_SYM; + if (landlock_add_rule(arg_landlock,LANDLOCK_RULE_PATH_BENEATH,&target,0)) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + close(home_fd); + if (add_read_access_rule_by_path(arg_landlock, "/bin/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_execute_rule_by_path(arg_landlock, "/bin/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/dev/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/etc/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/lib/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_execute_rule_by_path(arg_landlock, "/lib/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/opt/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_execute_rule_by_path(arg_landlock, "/opt/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/usr/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_execute_rule_by_path(arg_landlock, "/usr/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + if (add_read_access_rule_by_path(arg_landlock, "/var/")) { + fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); + } + return 0; + } + if (strncmp(ptr, "landlock.proc ", 14) == 0) { + if (strncmp(ptr+14, "no", 2) == 0) arg_landlock_proc = 0; + else if (strncmp(ptr+14, "ro", 2) == 0) arg_landlock_proc = 1; + else if (strncmp(ptr+14, "rw", 2) == 0) arg_landlock_proc = 2; + return 0; + } + if (strncmp(ptr, "landlock.read ", 14) == 0) { if (arg_landlock == -1) arg_landlock = create_full_ruleset(); if (add_read_access_rule_by_path(arg_landlock, ptr+14)) { fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); } return 0; } - else if (strncmp(ptr, "landlock-write ", 15) == 0) { + if (strncmp(ptr, "landlock.write ", 15) == 0) { if (arg_landlock == -1) arg_landlock = create_full_ruleset(); - if (add_write_access_rule_by_path(arg_landlock, ptr+15,0)) { + if (add_write_access_rule_by_path(arg_landlock, ptr+15)) { fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); } return 0; } - else if (strncmp(ptr, "landlock-restricted-write ", 26) == 0) { + if (strncmp(ptr, "landlock.special ", 26) == 0) { if (arg_landlock == -1) arg_landlock = create_full_ruleset(); - if (add_write_access_rule_by_path(arg_landlock, ptr+26,1)) { + if (add_create_special_rule_by_path(arg_landlock, ptr+26)) { fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); } return 0; } - else if (strncmp(ptr, "landlock-execute ", 17) == 0) { + if (strncmp(ptr, "landlock.execute ", 17) == 0) { if (arg_landlock == -1) arg_landlock = create_full_ruleset(); if (add_execute_rule_by_path(arg_landlock, ptr+17)) { fprintf(stderr,"An error has occured while adding a rule to the Landlock ruleset.\n"); diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 014b31932..5fcccbd92 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -488,15 +488,6 @@ void start_application(int no_sandbox, int fd, char *set_sandbox_status) { #ifdef HAVE_APPARMOR set_apparmor(); #endif -#ifdef HAVE_LANDLOCK - // set Landlock - if (arg_landlock >= 0) { - if (landlock_restrict_self(arg_landlock,0)) { - fprintf(stderr,"An error has occured while enabling Landlock self-restriction. Exiting...\n"); - exit(1); // it isn't safe to continue if Landlock self-restriction was enabled and the "landlock_restrict_self" syscall has failed - } - } -#endif close_file_descriptors(); @@ -519,6 +510,16 @@ void start_application(int no_sandbox, int fd, char *set_sandbox_status) { printf("LD_PRELOAD=%s\n", getenv("LD_PRELOAD")); } +#ifdef HAVE_LANDLOCK + // set Landlock + if (arg_landlock >= 0) { + if (landlock_restrict_self(arg_landlock,0)) { + fprintf(stderr,"An error has occured while enabling Landlock self-restriction. Exiting...\n"); + exit(1); // it isn't safe to continue if Landlock self-restriction was enabled and the "landlock_restrict_self" syscall has failed + } + } +#endif + if (just_run_the_shell) { char *arg[2]; arg[0] = cfg.usershell; @@ -1008,6 +1009,15 @@ int sandbox(void* sandbox_arg) { //**************************** fs_proc_sys_dev_boot(); + //**************************** + // Allow access to /proc + //**************************** +#ifdef HAVE_LANDLOCK + if (arg_landlock>-1) { + if (arg_landlock_proc >= 1) add_read_access_rule_by_path(arg_landlock, "/proc/"); + if (arg_landlock_proc == 2) add_write_access_rule_by_path(arg_landlock, "/proc/"); +} +#endif //**************************** // handle /mnt and /media //**************************** @@ -1103,9 +1113,12 @@ int sandbox(void* sandbox_arg) { //**************************** // rebuild etc directory, set dns //**************************** - if (!arg_writable_etc) + if (!arg_writable_etc){ fs_rebuild_etc(); - +#ifdef HAVE_LANDLOCK + if (arg_landlock>-1) add_read_access_rule_by_path(arg_landlock, "/etc/"); +#endif + } //**************************** // start dhcp client //**************************** diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 485d08ab0..e0751ef5c 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c @@ -122,10 +122,12 @@ static char *usage_str = " --keep-dev-shm - /dev/shm directory is untouched (even with --private-dev).\n" " --keep-fd - inherit open file descriptors to sandbox.\n" " --keep-var-tmp - /var/tmp directory is untouched.\n" - " --landlock-read=path - add a read access rule for the path to the Landlock ruleset.\n" - " --landlock-write=path - add a write access rule for the path to the Landlock ruleset.\n" - " --landlock-restricted-write=path - add a write access rule that doesn't include creating FIFO pipes, Unix domain sockets and block devices for the path to the Landlock ruleset.\n" - " --landlock-execute=path - add an execution-permitting rule for the path to the Landlock ruleset.\n" + " --landlock - add basic rules to the Landlock ruleset.\n" + " --landlock.proc=no|ro|rw - add an access rule for /proc to the Landlock ruleset.\n" + " --landlock.read=path - add a read access rule for the path to the Landlock ruleset.\n" + " --landlock.write=path - add a write access rule for the path to the Landlock ruleset.\n" + " --landlock.special=path - add an access rule for creating FIFO pipes, Unix domain sockets and block devices for the path to the Landlock ruleset.\n" + " --landlock.execute=path - add an execution-permitting rule for the path to the Landlock ruleset.\n" " --list - list all sandboxes.\n" #ifdef HAVE_FILE_TRANSFER " --ls=name|pid dir_or_filename - list files in sandbox container.\n" diff --git a/src/firejail/util.c b/src/firejail/util.c index a01290cf2..b6a7ca08c 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c @@ -1338,6 +1338,10 @@ void close_all(int *keep_list, size_t sz) { if (keep) continue; + // don't close the file descriptor of the Landlock ruleset -- it will be automatically closed by the landlock_restrict_self wrapper function +#ifdef HAVE_LANDLOCK + if (fd == arg_landlock) continue; +#endif close(fd); } closedir(dir); diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index 6e75aceed..1f543980e 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt @@ -499,23 +499,31 @@ Blacklist all Linux capabilities. Whitelist given Linux capabilities. #ifdef HAVE_LANDLOCK .TP -\fBlandlock-read path -Create a Landlock ruleset (if it doesn't already exist) and add a read access rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +\fBlandlock +Create a Landlock ruleset (if it doesn't already exist) and add basic access rules to it. +.br +.TP +\fBlandlock.proc no|ro|rw +Add an access rule for /proc directory (read-only if set to \fBro\fR and read-write if set to \fBrw\fR). The access rule for /proc is added after this directory is set up in the sandbox. Access rules for /proc set up with other Landlock-related profile options have no effect. +.br +.TP +\fBlandlock.read path +Create a Landlock ruleset (if it doesn't already exist) and add a read access rule for path. .br .TP -\fBlandlock-write path -Create a Landlock ruleset (if it doesn't already exist) and add a write access rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +\fBlandlock.write path +Create a Landlock ruleset (if it doesn't already exist) and add a write access rule for path. .br .TP -\fBlandlock-restricted-write path -Create a Landlock ruleset (if it doesn't already exist) and add a write access rule for path. This type of write access doesn't include the permission to create Unix domain sockets, FIFO pipes and block devices. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +\fBlandlock.special path +Create a Landlock ruleset (if it doesn't already exist) and add an access rule for creation of FIFO pipes, Unix-domain sockets and block devices beneath given path. .br .TP -\fBlandlock-execute path -Create a Landlock ruleset (if it doesn't already exist) and add an execution permission rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +\fBlandlock.execute path +Create a Landlock ruleset (if it doesn't already exist) and add an execution permission rule for path. .br #endif .TP diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 7082fe0ab..7922a53d0 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -1146,29 +1146,37 @@ $ firejail --keep-var-tmp #ifdef HAVE_LANDLOCK .TP -\fB\-\-landlock-read=path -Create a Landlock ruleset (if it doesn't already exist) and add a read access rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +\fB\-\-landlock +Create a Landlock ruleset (if it doesn't already exist) and add basic access rules to it. See \fBLANDLOCK\fR section for more information. +.br +.TP +\fB\-\-landlock.proc=no|ro|rw +Add an access rule for /proc directory (read-only if set to \fBro\fR and read-write if set to \fBrw\fR). The access rule for /proc is added after this directory is set up in the sandbox. Access rules for /proc set up with other Landlock-related command-line options have no effect. +.br +.TP +\fB\-\-landlock.read=path +Create a Landlock ruleset (if it doesn't already exist) and add a read access rule for path. .br .TP -\fB\-\-landlock-write=path -Create a Landlock ruleset (if it doesn't already exist) and add a write access rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +\fB\-\-landlock.write=path +Create a Landlock ruleset (if it doesn't already exist) and add a write access rule for path. .br .TP -\fB\-\-landlock-restricted-write=path -Create a Landlock ruleset (if it doesn't already exist) and add a write access rule for path. This type of write access doesn't include the permission to create Unix domain sockets, FIFO pipes and block devices. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +\fB\-\-landlock.special=path +Create a Landlock ruleset (if it doesn't already exist) and add a permission rule to create FIFO pipes, Unix domain sockets and block devices beneath given path. .br .TP -\fB\-\-landlock-execute=path -Create a Landlock ruleset (if it doesn't already exist) and add an execution permission rule for path. Note: if a process doesn't have CAP_SYS_ADMIN and the "No New Privileges" restriction is not enabled, the Landlock self-restriction will fail and Firejail will exit with an error. +\fB\-\-landlock.execute=path +Create a Landlock ruleset (if it doesn't already exist) and add an execution permission rule for path. .br .br Example: .br -$ firejail \-\-landlock-read=/ \-\-landlock-restricted-write=/home \-\-landlock-execute=/usr +$ firejail \-\-landlock.read=/ \-\-landlock.write=/home \-\-landlock.execute=/usr #endif .TP @@ -3216,7 +3224,34 @@ To enable AppArmor confinement on top of your current Firejail security features .br $ firejail --apparmor firefox #endif +#ifdef HAVE_LANDLOCK +.SH LANDLOCK +.TP +Landlock is a Linux security module first introduced in the 5.13 version of Linux kernel. It allows unprivileged processes to restrict their access to the filesystem. Once imposed, these restrictions can never be removed, and all child processes created by a Landlock-restricted processes inherit these restrictions. Firejail supports Landlock as an additional sandboxing feature. It can be used to ensure that a sandboxed application can only access files and directories that it was explicitly allowed to access. Firejail supports populating the ruleset with both basic set of rules and with custom set of rules. Basic set of rules allows read-only access to /bin, /dev, /etc, /lib, /opt, /proc, /usr and /var, read-write access to the home directory, and allows execution of binaries located in /bin, /opt and /usr. +.br + +.TP +Important notes: +.br +.br +- A process can install a Landlock ruleset only if it has either \fBCAP_SYS_ADMIN\fR in its effective capability set, or the "No New Privileges" restriction enabled. Because of this, enabling the Landlock feature will also cause Firejail to enable the "No New Privileges" restriction, regardless of the profile or the \fB\-\-no\-new\-privs\fR command line option. +.br + +.br +- Access to the /proc directory is managed through the \fB\-\-landlock.proc\fR command line option. + +.br +- Access to the /etc directory is automatically allowed. To override this, use the \fB\-\-writable\-etc\fR command line option. You can also use the \fB\-\-private\-etc\fR option to restrict access to the /etc directory. +.br + +.TP +To enable Landlock self-restriction on top of your current Firejail security features, pass \fB\-\-landlock\fR flag to Firejail command line. You can also use \fB\-\-landlock.read\fR, \fB\-\-landlock.write\fR, \fB\-\-landlock.special\fR and \fB\-\-landlock.execute\fR options together with \fB\-\-landlock\fR or instead of it. Example: +.br + +.br +$ firejail --landlock --landlock.read=/media --landlock.proc=ro mc +#endif .SH DESKTOP INTEGRATION A symbolic link to /usr/bin/firejail under the name of a program, will start the program in Firejail sandbox. The symbolic link should be placed in the first $PATH position. On most systems, a good place diff --git a/src/zsh_completion/_firejail.in b/src/zsh_completion/_firejail.in index da024eae5..ed7337762 100644 --- a/src/zsh_completion/_firejail.in +++ b/src/zsh_completion/_firejail.in @@ -105,10 +105,12 @@ _firejail_args=( '--keep-dev-shm[/dev/shm directory is untouched (even with --private-dev)]' '--keep-fd[inherit open file descriptors to sandbox]: :' '--keep-var-tmp[/var/tmp directory is untouched]' - '--landlock-read=-[Landlock read access rule]: :_files' - '--landlock-write=-[Landlock write access rule]: :_files' - "--landlock-restricted-write=-[Landlock write access rule that doesn't include creation of FIFO pipes, sockets and block devices]: :_files" - '--landlock-execute=-[Landlock execution-permitting rule]: :_files' + '--landlock[Basic Landlock ruleset]' + '--landlock.proc=-[Access to the /proc directory]: :(no ro rw)' + '--landlock.read=-[Landlock read access rule]: :_files' + '--landlock.write=-[Landlock write access rule]: :_files' + "--landlock.special=-[Landlock access rule for creation of FIFO pipes, sockets and block devices]: :_files" + '--landlock.execute=-[Landlock execution-permitting rule]: :_files' '--machine-id[spoof /etc/machine-id with a random id]' '--memory-deny-write-execute[seccomp filter to block attempts to create memory mappings that are both writable and executable]' '*--mkdir=-[create a directory]:' -- cgit v1.2.3-54-g00ecf