diff options
author | netblue30 <netblue30@yahoo.com> | 2016-09-04 13:52:16 -0400 |
---|---|---|
committer | netblue30 <netblue30@yahoo.com> | 2016-09-04 13:52:16 -0400 |
commit | 6aad9ad431f749003b4eab7b91cfdd0f218852a2 (patch) | |
tree | 9b7d89269ff05af0a6259668a8e8c5a456ac7666 | |
parent | fixes (diff) | |
download | firejail-6aad9ad431f749003b4eab7b91cfdd0f218852a2.tar.gz firejail-6aad9ad431f749003b4eab7b91cfdd0f218852a2.tar.zst firejail-6aad9ad431f749003b4eab7b91cfdd0f218852a2.zip |
bringing back --private-home
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | RELNOTES | 1 | ||||
-rwxr-xr-x | configure | 17 | ||||
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | etc/firejail.config | 3 | ||||
-rw-r--r-- | src/firejail/Makefile.in | 3 | ||||
-rw-r--r-- | src/firejail/checkcfg.c | 32 | ||||
-rw-r--r-- | src/firejail/firejail.h | 4 | ||||
-rw-r--r-- | src/firejail/fs_home.c | 408 | ||||
-rw-r--r-- | src/firejail/main.c | 51 | ||||
-rw-r--r-- | src/firejail/profile.c | 12 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 8 | ||||
-rw-r--r-- | src/firejail/usage.c | 22 | ||||
-rw-r--r-- | src/man/firejail-profile.txt | 6 | ||||
-rw-r--r-- | src/man/firejail.txt | 13 |
16 files changed, 446 insertions, 147 deletions
@@ -87,7 +87,7 @@ greigdp (https://github.com/greigdp) | |||
87 | Laurent Declercq (https://github.com/nuxwin) | 87 | Laurent Declercq (https://github.com/nuxwin) |
88 | - fixed test for shell interpreter in chroots | 88 | - fixed test for shell interpreter in chroots |
89 | Franco (nextime) Lanza (https://github.com/nextime) | 89 | Franco (nextime) Lanza (https://github.com/nextime) |
90 | - added --private-template | 90 | - added --private-template/--private-home |
91 | xee5ch (https://github.com/xee5ch) | 91 | xee5ch (https://github.com/xee5ch) |
92 | - skypeforlinux profile | 92 | - skypeforlinux profile |
93 | Peter Hogg (https://github.com/pigmonkey) | 93 | Peter Hogg (https://github.com/pigmonkey) |
@@ -38,6 +38,8 @@ FAQ: https://firejail.wordpress.com/support/frequently-asked-questions/ | |||
38 | 38 | ||
39 | Version 0.9.41~rc1 was released. | 39 | Version 0.9.41~rc1 was released. |
40 | 40 | ||
41 | ## Bringing back --private-home | ||
42 | |||
41 | ## Deprecated --user | 43 | ## Deprecated --user |
42 | 44 | ||
43 | --user option was deprecated, please use "sudo -u username firejail application" instead. | 45 | --user option was deprecated, please use "sudo -u username firejail application" instead. |
@@ -4,6 +4,7 @@ firejail (0.9.42~rc2) baseline; urgency=low | |||
4 | * security: tighten --chroot, submitted by Jann Horn | 4 | * security: tighten --chroot, submitted by Jann Horn |
5 | * security: terminal sandbox escape, submitted by Stephan Sokolow | 5 | * security: terminal sandbox escape, submitted by Stephan Sokolow |
6 | * security: several TOCTOU fixes submitted by Aleksey Manevich | 6 | * security: several TOCTOU fixes submitted by Aleksey Manevich |
7 | * modifs: bringing back --private-home option | ||
7 | * modifs: deprecated --user option, please use "sudo -u username firejail" | 8 | * modifs: deprecated --user option, please use "sudo -u username firejail" |
8 | * modifs: allow symlinks in home directory for --whitelist option | 9 | * modifs: allow symlinks in home directory for --whitelist option |
9 | * modifs: Firejail prompt is enabled by env variable FIREJAIL_PROMPT="yes" | 10 | * modifs: Firejail prompt is enabled by env variable FIREJAIL_PROMPT="yes" |
@@ -636,6 +636,7 @@ HAVE_GLOBALCFG | |||
636 | HAVE_BIND | 636 | HAVE_BIND |
637 | HAVE_CHROOT | 637 | HAVE_CHROOT |
638 | HAVE_SECCOMP | 638 | HAVE_SECCOMP |
639 | HAVE_PRIVATE_HOME | ||
639 | HAVE_OVERLAYFS | 640 | HAVE_OVERLAYFS |
640 | EXTRA_LDFLAGS | 641 | EXTRA_LDFLAGS |
641 | EGREP | 642 | EGREP |
@@ -696,6 +697,7 @@ ac_user_opts=' | |||
696 | enable_option_checking | 697 | enable_option_checking |
697 | enable_apparmor | 698 | enable_apparmor |
698 | enable_overlayfs | 699 | enable_overlayfs |
700 | enable_private_home | ||
699 | enable_seccomp | 701 | enable_seccomp |
700 | enable_chroot | 702 | enable_chroot |
701 | enable_bind | 703 | enable_bind |
@@ -1328,6 +1330,7 @@ Optional Features: | |||
1328 | --enable-FEATURE[=ARG] include FEATURE [ARG=yes] | 1330 | --enable-FEATURE[=ARG] include FEATURE [ARG=yes] |
1329 | --enable-apparmor enable apparmor | 1331 | --enable-apparmor enable apparmor |
1330 | --disable-overlayfs disable overlayfs | 1332 | --disable-overlayfs disable overlayfs |
1333 | --disable-private-home disable private home feature | ||
1331 | --disable-seccomp disable seccomp | 1334 | --disable-seccomp disable seccomp |
1332 | --disable-chroot disable chroot | 1335 | --disable-chroot disable chroot |
1333 | --disable-bind disable bind | 1336 | --disable-bind disable bind |
@@ -3527,6 +3530,19 @@ if test "x$enable_overlayfs" != "xno"; then : | |||
3527 | 3530 | ||
3528 | fi | 3531 | fi |
3529 | 3532 | ||
3533 | HAVE_PRIVATEHOME="" | ||
3534 | # Check whether --enable-private-home was given. | ||
3535 | if test "${enable_private_home+set}" = set; then : | ||
3536 | enableval=$enable_private_home; | ||
3537 | fi | ||
3538 | |||
3539 | if test "x$enable_private_home" != "xno"; then : | ||
3540 | |||
3541 | HAVE_PRIVATE_HOME="-DHAVE_PRIVATE_HOME" | ||
3542 | |||
3543 | |||
3544 | fi | ||
3545 | |||
3530 | HAVE_SECCOMP="" | 3546 | HAVE_SECCOMP="" |
3531 | # Check whether --enable-seccomp was given. | 3547 | # Check whether --enable-seccomp was given. |
3532 | if test "${enable_seccomp+set}" = set; then : | 3548 | if test "${enable_seccomp+set}" = set; then : |
@@ -4937,6 +4953,7 @@ echo " network: $HAVE_NETWORK" | |||
4937 | echo " user namespace: $HAVE_USERNS" | 4953 | echo " user namespace: $HAVE_USERNS" |
4938 | echo " X11 sandboxing support: $HAVE_X11" | 4954 | echo " X11 sandboxing support: $HAVE_X11" |
4939 | echo " whitelisting: $HAVE_WHITELIST" | 4955 | echo " whitelisting: $HAVE_WHITELIST" |
4956 | echo " private home support: $HAVE_PRIVATE_HOME" | ||
4940 | echo " file transfer support: $HAVE_FILE_TRANSFER" | 4957 | echo " file transfer support: $HAVE_FILE_TRANSFER" |
4941 | echo " overlayfs support: $HAVE_OVERLAYFS" | 4958 | echo " overlayfs support: $HAVE_OVERLAYFS" |
4942 | echo " fatal warnings: $HAVE_FATAL_WARNINGS" | 4959 | echo " fatal warnings: $HAVE_FATAL_WARNINGS" |
diff --git a/configure.ac b/configure.ac index 67b74e723..ca6066d25 100644 --- a/configure.ac +++ b/configure.ac | |||
@@ -38,6 +38,14 @@ AS_IF([test "x$enable_overlayfs" != "xno"], [ | |||
38 | AC_SUBST(HAVE_OVERLAYFS) | 38 | AC_SUBST(HAVE_OVERLAYFS) |
39 | ]) | 39 | ]) |
40 | 40 | ||
41 | HAVE_PRIVATEHOME="" | ||
42 | AC_ARG_ENABLE([private-home], | ||
43 | AS_HELP_STRING([--disable-private-home], [disable private home feature])) | ||
44 | AS_IF([test "x$enable_private_home" != "xno"], [ | ||
45 | HAVE_PRIVATE_HOME="-DHAVE_PRIVATE_HOME" | ||
46 | AC_SUBST(HAVE_PRIVATE_HOME) | ||
47 | ]) | ||
48 | |||
41 | HAVE_SECCOMP="" | 49 | HAVE_SECCOMP="" |
42 | AC_ARG_ENABLE([seccomp], | 50 | AC_ARG_ENABLE([seccomp], |
43 | AS_HELP_STRING([--disable-seccomp], [disable seccomp])) | 51 | AS_HELP_STRING([--disable-seccomp], [disable seccomp])) |
@@ -163,6 +171,7 @@ echo " network: $HAVE_NETWORK" | |||
163 | echo " user namespace: $HAVE_USERNS" | 171 | echo " user namespace: $HAVE_USERNS" |
164 | echo " X11 sandboxing support: $HAVE_X11" | 172 | echo " X11 sandboxing support: $HAVE_X11" |
165 | echo " whitelisting: $HAVE_WHITELIST" | 173 | echo " whitelisting: $HAVE_WHITELIST" |
174 | echo " private home support: $HAVE_PRIVATE_HOME" | ||
166 | echo " file transfer support: $HAVE_FILE_TRANSFER" | 175 | echo " file transfer support: $HAVE_FILE_TRANSFER" |
167 | echo " overlayfs support: $HAVE_OVERLAYFS" | 176 | echo " overlayfs support: $HAVE_OVERLAYFS" |
168 | echo " fatal warnings: $HAVE_FATAL_WARNINGS" | 177 | echo " fatal warnings: $HAVE_FATAL_WARNINGS" |
diff --git a/etc/firejail.config b/etc/firejail.config index 6b6ba7fdf..08ff5380d 100644 --- a/etc/firejail.config +++ b/etc/firejail.config | |||
@@ -29,6 +29,9 @@ | |||
29 | # Enable or disable overlayfs features, default enabled. | 29 | # Enable or disable overlayfs features, default enabled. |
30 | # overlayfs yes | 30 | # overlayfs yes |
31 | 31 | ||
32 | # Enable or disable private-home feature, default enabled | ||
33 | # private-home yes | ||
34 | |||
32 | # Enable --quiet as default every time the sandbox is started. Default disabled. | 35 | # Enable --quiet as default every time the sandbox is started. Default disabled. |
33 | # quiet-by-default no | 36 | # quiet-by-default no |
34 | 37 | ||
diff --git a/src/firejail/Makefile.in b/src/firejail/Makefile.in index f56137308..fce460906 100644 --- a/src/firejail/Makefile.in +++ b/src/firejail/Makefile.in | |||
@@ -20,13 +20,14 @@ HAVE_WHITELIST=@HAVE_WHITELIST@ | |||
20 | HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ | 20 | HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ |
21 | HAVE_APPARMOR=@HAVE_APPARMOR@ | 21 | HAVE_APPARMOR=@HAVE_APPARMOR@ |
22 | HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ | 22 | HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ |
23 | HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@ | ||
23 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | 24 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ |
24 | 25 | ||
25 | H_FILE_LIST = $(sort $(wildcard *.[h])) | 26 | H_FILE_LIST = $(sort $(wildcard *.[h])) |
26 | C_FILE_LIST = $(sort $(wildcard *.c)) | 27 | C_FILE_LIST = $(sort $(wildcard *.c)) |
27 | OBJS = $(C_FILE_LIST:.c=.o) | 28 | OBJS = $(C_FILE_LIST:.c=.o) |
28 | BINOBJS = $(foreach file, $(OBJS), $file) | 29 | BINOBJS = $(foreach file, $(OBJS), $file) |
29 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | 30 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security |
30 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread | 31 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread |
31 | 32 | ||
32 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h | 33 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h |
diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c index 5bc859f8d..99266c575 100644 --- a/src/firejail/checkcfg.c +++ b/src/firejail/checkcfg.c | |||
@@ -241,6 +241,14 @@ int checkcfg(int val) { | |||
241 | else | 241 | else |
242 | goto errout; | 242 | goto errout; |
243 | } | 243 | } |
244 | else if (strncmp(ptr, "private-home ", 13) == 0) { | ||
245 | if (strcmp(ptr + 13, "yes") == 0) | ||
246 | cfg_val[CFG_PRIVATE_HOME] = 1; | ||
247 | else if (strcmp(ptr + 13, "no") == 0) | ||
248 | cfg_val[CFG_PRIVATE_HOME] = 0; | ||
249 | else | ||
250 | goto errout; | ||
251 | } | ||
244 | else if (strncmp(ptr, "chroot-desktop ", 15) == 0) { | 252 | else if (strncmp(ptr, "chroot-desktop ", 15) == 0) { |
245 | if (strcmp(ptr + 15, "yes") == 0) | 253 | if (strcmp(ptr + 15, "yes") == 0) |
246 | cfg_val[CFG_CHROOT_DESKTOP] = 1; | 254 | cfg_val[CFG_CHROOT_DESKTOP] = 1; |
@@ -295,14 +303,6 @@ void print_compiletime_support(void) { | |||
295 | #endif | 303 | #endif |
296 | ); | 304 | ); |
297 | 305 | ||
298 | printf("\t- overlayfs support is %s\n", | ||
299 | #ifdef HAVE_OVERLAYFS | ||
300 | "enabled" | ||
301 | #else | ||
302 | "disabled" | ||
303 | #endif | ||
304 | ); | ||
305 | |||
306 | printf("\t- file and directory whitelisting support is %s\n", | 306 | printf("\t- file and directory whitelisting support is %s\n", |
307 | #ifdef HAVE_WHITELIST | 307 | #ifdef HAVE_WHITELIST |
308 | "enabled" | 308 | "enabled" |
@@ -332,6 +332,22 @@ void print_compiletime_support(void) { | |||
332 | printf("\t- networking features are available only to root user\n"); | 332 | printf("\t- networking features are available only to root user\n"); |
333 | #endif | 333 | #endif |
334 | 334 | ||
335 | printf("\t- overlayfs support is %s\n", | ||
336 | #ifdef HAVE_OVERLAYFS | ||
337 | "enabled" | ||
338 | #else | ||
339 | "disabled" | ||
340 | #endif | ||
341 | ); | ||
342 | |||
343 | printf("\t- private-home support is %s\n", | ||
344 | #ifdef HAVE_PRIVATE_HOME | ||
345 | "enabled" | ||
346 | #else | ||
347 | "disabled" | ||
348 | #endif | ||
349 | ); | ||
350 | |||
335 | printf("\t- seccomp-bpf support is %s\n", | 351 | printf("\t- seccomp-bpf support is %s\n", |
336 | #ifdef HAVE_SECCOMP | 352 | #ifdef HAVE_SECCOMP |
337 | "enabled" | 353 | "enabled" |
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 7be04f782..9b60d40c2 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -180,6 +180,7 @@ typedef struct config_t { | |||
180 | char *profile_ignore[MAX_PROFILE_IGNORE]; | 180 | char *profile_ignore[MAX_PROFILE_IGNORE]; |
181 | char *chrootdir; // chroot directory | 181 | char *chrootdir; // chroot directory |
182 | char *home_private; // private home directory | 182 | char *home_private; // private home directory |
183 | char *home_private_keep; // keep list for private home directory | ||
183 | char *etc_private_keep; // keep list for private etc directory | 184 | char *etc_private_keep; // keep list for private etc directory |
184 | char *bin_private_keep; // keep list for private bin directory | 185 | char *bin_private_keep; // keep list for private bin directory |
185 | char *cwd; // current working directory | 186 | char *cwd; // current working directory |
@@ -640,7 +641,8 @@ void sandboxfs(int op, pid_t pid, const char *patqh); | |||
640 | #define CFG_REMOUNT_PROC_SYS 11 | 641 | #define CFG_REMOUNT_PROC_SYS 11 |
641 | #define CFG_OVERLAYFS 12 | 642 | #define CFG_OVERLAYFS 12 |
642 | #define CFG_CHROOT_DESKTOP 13 | 643 | #define CFG_CHROOT_DESKTOP 13 |
643 | #define CFG_MAX 14 // this should always be the last entry | 644 | #define CFG_PRIVATE_HOME 14 |
645 | #define CFG_MAX 15 // this should always be the last entry | ||
644 | extern char *xephyr_screen; | 646 | extern char *xephyr_screen; |
645 | extern char *xephyr_extra_params; | 647 | extern char *xephyr_extra_params; |
646 | extern char *netfilter_default; | 648 | extern char *netfilter_default; |
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 85fa244be..a4b2ec046 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -313,61 +313,6 @@ void fs_private(void) { | |||
313 | 313 | ||
314 | } | 314 | } |
315 | 315 | ||
316 | int fs_copydir(const char *path, const struct stat *st, int ftype, struct FTW *sftw); | ||
317 | |||
318 | |||
319 | int fs_copydir(const char *path, const struct stat *st, int ftype, struct FTW *sftw) | ||
320 | { | ||
321 | (void) st; | ||
322 | (void) sftw; | ||
323 | char *homedir = cfg.homedir; | ||
324 | char *dest; | ||
325 | int srcbaselen = 0; | ||
326 | assert(homedir); | ||
327 | uid_t u = getuid(); | ||
328 | gid_t g = getgid(); | ||
329 | srcbaselen = strlen(cfg.private_template); | ||
330 | |||
331 | if(ftype == FTW_F || ftype == FTW_D) { | ||
332 | if (asprintf(&dest, "%s/%s", homedir, path + srcbaselen) == -1) | ||
333 | errExit("asprintf"); | ||
334 | struct stat s; | ||
335 | // don't copy it if we already have the file | ||
336 | if (stat(dest, &s) == 0) | ||
337 | return(0); | ||
338 | if (stat(path, &s) == 0) { | ||
339 | if(ftype == FTW_F) { | ||
340 | if (copy_file(path, dest, u, g, 0644) == 0) { | ||
341 | if (arg_debug) | ||
342 | printf("copy from %s to %s\n", path, dest); | ||
343 | fs_logger2("clone", path); | ||
344 | } | ||
345 | } | ||
346 | else if(ftype == FTW_D) { | ||
347 | if (mkdir(dest, s.st_mode) == -1) | ||
348 | errExit("mkdir"); | ||
349 | if (chown(dest, u, g) < 0) | ||
350 | errExit("chown"); | ||
351 | if (arg_debug) | ||
352 | printf("copy from %s to %s\n", path, dest); | ||
353 | fs_logger2("clone", path); | ||
354 | } | ||
355 | } | ||
356 | free(dest); | ||
357 | } | ||
358 | return(0); | ||
359 | } | ||
360 | |||
361 | void fs_private_template(void) { | ||
362 | |||
363 | fs_private(); | ||
364 | if(nftw(cfg.private_template, fs_copydir, 1, FTW_PHYS) != 0) { | ||
365 | fprintf(stderr, "Error: unable to copy template dir\n"); | ||
366 | exit(1); | ||
367 | } | ||
368 | |||
369 | } | ||
370 | |||
371 | // check new private home directory (--private= option) - exit if it fails | 316 | // check new private home directory (--private= option) - exit if it fails |
372 | void fs_check_private_dir(void) { | 317 | void fs_check_private_dir(void) { |
373 | EUID_ASSERT(); | 318 | EUID_ASSERT(); |
@@ -406,42 +351,323 @@ void fs_check_private_dir(void) { | |||
406 | } | 351 | } |
407 | } | 352 | } |
408 | 353 | ||
409 | // check new template home directoty (--private-template= option) - exit if it fails | 354 | //*********************************************************************************** |
410 | void fs_check_private_template(void) { | 355 | // --private-home |
411 | EUID_ASSERT(); | 356 | //*********************************************************************************** |
412 | invalid_filename(cfg.private_template); | 357 | #define PRIVATE_COPY_LIMIT (500 * 1024 *1024) |
413 | 358 | static int size_limit_reached = 0; | |
414 | // Expand the home directory | 359 | static unsigned file_cnt = 0; |
415 | char *tmp = expand_home(cfg.private_template, cfg.homedir); | 360 | static unsigned size_cnt = 0; |
416 | cfg.private_template = realpath(tmp, NULL); | 361 | static char *check_dir_or_file(const char *name); |
417 | free(tmp); | 362 | |
418 | 363 | int fs_copydir(const char *path, const struct stat *st, int ftype, struct FTW *sftw) { | |
419 | if (!cfg.private_template | 364 | if (size_limit_reached) |
420 | || !is_dir(cfg.private_template) | 365 | return 0; |
421 | || is_link(cfg.private_template) | 366 | |
422 | || strstr(cfg.private_template, "..")) { | 367 | struct stat s; |
423 | fprintf(stderr, "Error: invalid private template directory\n"); | 368 | char *dest; |
424 | exit(1); | 369 | if (asprintf(&dest, "%s%s", RUN_HOME_DIR, path + strlen(cfg.homedir)) == -1) |
425 | } | 370 | errExit("asprintf"); |
426 | 371 | ||
427 | // check home directory and chroot home directory have the same owner | 372 | // don't copy it if we already have the file |
428 | struct stat s2; | 373 | if (stat(dest, &s) == 0) { |
429 | int rv = stat(cfg.private_template, &s2); | 374 | free(dest); |
430 | if (rv < 0) { | 375 | return 0; |
431 | fprintf(stderr, "Error: cannot find %s directory\n", cfg.private_template); | 376 | } |
432 | exit(1); | 377 | |
433 | } | 378 | // extract mode and ownership |
434 | 379 | if (stat(path, &s) != 0) { | |
435 | struct stat s1; | 380 | free(dest); |
436 | rv = stat(cfg.homedir, &s1); | 381 | return 0; |
437 | if (rv < 0) { | 382 | } |
438 | fprintf(stderr, "Error: cannot find %s directory, full path name required\n", cfg.homedir); | 383 | |
439 | exit(1); | 384 | // check uid |
440 | } | 385 | if (s.st_uid != firejail_uid || s.st_gid != firejail_gid) { |
441 | if (s1.st_uid != s2.st_uid) { | 386 | free(dest); |
442 | printf("Error: --private-template directory should be owned by the current user\n"); | 387 | return 0; |
443 | exit(1); | 388 | } |
444 | } | 389 | |
390 | if ((s.st_size + size_cnt) > PRIVATE_COPY_LIMIT) { | ||
391 | size_limit_reached = 1; | ||
392 | free(dest); | ||
393 | return 0; | ||
394 | } | ||
395 | |||
396 | file_cnt++; | ||
397 | size_cnt += s.st_size; | ||
398 | |||
399 | if(ftype == FTW_F) | ||
400 | copy_file(path, dest, firejail_uid, firejail_gid, s.st_mode); | ||
401 | else if (ftype == FTW_D) { | ||
402 | if (mkdir(dest, s.st_mode) == -1) | ||
403 | errExit("mkdir"); | ||
404 | if (chmod(dest, s.st_mode) < 0) { | ||
405 | fprintf(stderr, "Error: cannot change mode for %s\n", path); | ||
406 | exit(1); | ||
407 | } | ||
408 | if (chown(dest, firejail_uid, firejail_gid) < 0) { | ||
409 | fprintf(stderr, "Error: cannot change ownership for %s\n", path); | ||
410 | exit(1); | ||
411 | } | ||
412 | |||
413 | #if 0 | ||
414 | struct stat s2; | ||
415 | if (stat(dest, &s2) == 0) { | ||
416 | printf("%s\t", dest); | ||
417 | printf((S_ISDIR(s.st_mode)) ? "d" : "-"); | ||
418 | printf((s.st_mode & S_IRUSR) ? "r" : "-"); | ||
419 | printf((s.st_mode & S_IWUSR) ? "w" : "-"); | ||
420 | printf((s.st_mode & S_IXUSR) ? "x" : "-"); | ||
421 | printf((s.st_mode & S_IRGRP) ? "r" : "-"); | ||
422 | printf((s.st_mode & S_IWGRP) ? "w" : "-"); | ||
423 | printf((s.st_mode & S_IXGRP) ? "x" : "-"); | ||
424 | printf((s.st_mode & S_IROTH) ? "r" : "-"); | ||
425 | printf((s.st_mode & S_IWOTH) ? "w" : "-"); | ||
426 | printf((s.st_mode & S_IXOTH) ? "x" : "-"); | ||
427 | printf("\n"); | ||
428 | } | ||
429 | #endif | ||
430 | |||
431 | fs_logger2("clone", path); | ||
432 | } | ||
433 | |||
434 | free(dest); | ||
435 | return(0); | ||
436 | } | ||
437 | |||
438 | static void duplicate(char *name) { | ||
439 | char *fname = check_dir_or_file(name); | ||
440 | |||
441 | if (arg_debug) | ||
442 | printf("Private home: duplicating %s\n", fname); | ||
443 | assert(strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0); | ||
444 | |||
445 | struct stat s; | ||
446 | if (stat(fname, &s) == -1) { | ||
447 | free(fname); | ||
448 | return; | ||
449 | } | ||
450 | |||
451 | if(nftw(fname, fs_copydir, 1, FTW_PHYS) != 0) { | ||
452 | fprintf(stderr, "Error: unable to copy template dir\n"); | ||
453 | exit(1); | ||
454 | } | ||
455 | fs_logger_print(); // save the current log | ||
456 | |||
457 | free(fname); | ||
458 | } | ||
459 | |||
460 | |||
461 | |||
462 | static char *check_dir_or_file(const char *name) { | ||
463 | assert(name); | ||
464 | struct stat s; | ||
465 | |||
466 | // basic checks | ||
467 | invalid_filename(name); | ||
468 | |||
469 | if (arg_debug) | ||
470 | printf("Private home: checking %s\n", name); | ||
471 | |||
472 | // expand home directory | ||
473 | char *fname = expand_home(name, cfg.homedir); | ||
474 | if (!fname) { | ||
475 | fprintf(stderr, "Error: file %s not found.\n", name); | ||
476 | exit(1); | ||
477 | } | ||
478 | |||
479 | // If it doesn't start with '/', it must be relative to homedir | ||
480 | if (fname[0] != '/') { | ||
481 | char* tmp; | ||
482 | if (asprintf(&tmp, "%s/%s", cfg.homedir, fname) == -1) | ||
483 | errExit("asprintf"); | ||
484 | free(fname); | ||
485 | fname = tmp; | ||
486 | } | ||
487 | |||
488 | // check the file is in user home directory | ||
489 | char *rname = realpath(fname, NULL); | ||
490 | if (!rname) { | ||
491 | fprintf(stderr, "Error: invalid file %s\n", name); | ||
492 | exit(1); | ||
493 | } | ||
494 | if (strncmp(rname, cfg.homedir, strlen(cfg.homedir)) != 0) { | ||
495 | fprintf(stderr, "Error: file %s is not in user home directory\n", name); | ||
496 | exit(1); | ||
497 | } | ||
498 | |||
499 | // a full home directory is not allowed | ||
500 | if (strcmp(rname, cfg.homedir) == 0) { | ||
501 | fprintf(stderr, "Error: invalid directory %s\n", rname); | ||
502 | exit(1); | ||
503 | } | ||
504 | |||
505 | // only top files and directories in user home are allowed | ||
506 | char *ptr = rname + strlen(cfg.homedir); | ||
507 | if (*ptr == '\0') { | ||
508 | fprintf(stderr, "Error: invalid file %s\n", name); | ||
509 | exit(1); | ||
510 | } | ||
511 | ptr++; | ||
512 | ptr = strchr(ptr, '/'); | ||
513 | if (ptr) { | ||
514 | if (*ptr != '\0') { | ||
515 | fprintf(stderr, "Error: only top files and directories in user home are allowed\n"); | ||
516 | exit(1); | ||
517 | } | ||
518 | } | ||
519 | |||
520 | if (stat(fname, &s) == -1) { | ||
521 | fprintf(stderr, "Error: file %s not found.\n", fname); | ||
522 | exit(1); | ||
523 | } | ||
524 | |||
525 | // check uid | ||
526 | uid_t uid = getuid(); | ||
527 | gid_t gid = getgid(); | ||
528 | if (s.st_uid != uid || s.st_gid != gid) { | ||
529 | fprintf(stderr, "Error: only files or directories created by the current user are allowed.\n"); | ||
530 | exit(1); | ||
531 | } | ||
532 | |||
533 | // dir or regular file | ||
534 | if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) { | ||
535 | free(fname); | ||
536 | return rname; // regular exit from the function | ||
537 | } | ||
538 | |||
539 | fprintf(stderr, "Error: invalid file type, %s.\n", fname); | ||
540 | exit(1); | ||
541 | } | ||
542 | |||
543 | |||
544 | // check directory list specified by user (--private-home option) - exit if it fails | ||
545 | void fs_check_home_list(void) { | ||
546 | if (strstr(cfg.home_private_keep, "..")) { | ||
547 | fprintf(stderr, "Error: invalid private-home list\n"); | ||
548 | exit(1); | ||
549 | } | ||
550 | |||
551 | char *dlist = strdup(cfg.home_private_keep); | ||
552 | if (!dlist) | ||
553 | errExit("strdup"); | ||
554 | |||
555 | char *ptr = strtok(dlist, ","); | ||
556 | char *tmp = check_dir_or_file(ptr); | ||
557 | free(tmp); | ||
558 | |||
559 | while ((ptr = strtok(NULL, ",")) != NULL) { | ||
560 | tmp = check_dir_or_file(ptr); | ||
561 | free(tmp); | ||
562 | } | ||
563 | |||
564 | free(dlist); | ||
445 | } | 565 | } |
446 | 566 | ||
447 | 567 | ||
568 | |||
569 | // private mode (--private-home=list): | ||
570 | // mount homedir on top of /home/user, | ||
571 | // tmpfs on top of /root in nonroot mode, | ||
572 | // tmpfs on top of /tmp in root mode, | ||
573 | // set skel files, | ||
574 | // restore .Xauthority | ||
575 | void fs_private_home_list(void) { | ||
576 | char *homedir = cfg.homedir; | ||
577 | char *private_list = cfg.home_private_keep; | ||
578 | assert(homedir); | ||
579 | assert(private_list); | ||
580 | |||
581 | int xflag = store_xauthority(); | ||
582 | int aflag = store_asoundrc(); | ||
583 | |||
584 | uid_t u = firejail_uid; | ||
585 | gid_t g = firejail_gid; | ||
586 | struct stat s; | ||
587 | if (stat(homedir, &s) == -1) { | ||
588 | fprintf(stderr, "Error: cannot find user home directory\n"); | ||
589 | exit(1); | ||
590 | } | ||
591 | |||
592 | // create /tmp/firejail/mnt/home directory | ||
593 | fs_build_mnt_dir(); | ||
594 | int rv = mkdir(RUN_HOME_DIR, 0755); | ||
595 | if (rv == -1) | ||
596 | errExit("mkdir"); | ||
597 | if (chown(RUN_HOME_DIR, u, g) < 0) | ||
598 | errExit("chown"); | ||
599 | if (chmod(RUN_HOME_DIR, 0755) < 0) | ||
600 | errExit("chmod"); | ||
601 | ASSERT_PERMS(RUN_HOME_DIR, u, g, 0755); | ||
602 | |||
603 | fs_logger_print(); // save the current log | ||
604 | |||
605 | // copy the list of files in the new home directory | ||
606 | // using a new child process without root privileges | ||
607 | pid_t child = fork(); | ||
608 | if (child < 0) | ||
609 | errExit("fork"); | ||
610 | if (child == 0) { | ||
611 | if (arg_debug) | ||
612 | printf("Copying files in the new home:\n"); | ||
613 | |||
614 | // drop privileges | ||
615 | if (setgroups(0, NULL) < 0) | ||
616 | errExit("setgroups"); | ||
617 | if (setgid(getgid()) < 0) | ||
618 | errExit("setgid/getgid"); | ||
619 | if (setuid(getuid()) < 0) | ||
620 | errExit("setuid/getuid"); | ||
621 | |||
622 | // copy the list of files in the new home directory | ||
623 | char *dlist = strdup(cfg.home_private_keep); | ||
624 | if (!dlist) | ||
625 | errExit("strdup"); | ||
626 | |||
627 | char *ptr = strtok(dlist, ","); | ||
628 | duplicate(ptr); | ||
629 | while ((ptr = strtok(NULL, ",")) != NULL) | ||
630 | duplicate(ptr); | ||
631 | |||
632 | if (!arg_quiet) { | ||
633 | if (size_limit_reached) | ||
634 | fprintf(stderr, "Warning: private-home copy limit of %u MB reached, not all the files were copied\n", | ||
635 | PRIVATE_COPY_LIMIT / (1024 *1024)); | ||
636 | else | ||
637 | printf("Private home: %u files, total size %u bytes\n", file_cnt, size_cnt); | ||
638 | } | ||
639 | |||
640 | fs_logger_print(); // save the current log | ||
641 | free(dlist); | ||
642 | exit(0); | ||
643 | } | ||
644 | // wait for the child to finish | ||
645 | waitpid(child, NULL, 0); | ||
646 | |||
647 | if (arg_debug) | ||
648 | printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir); | ||
649 | |||
650 | if (mount(RUN_HOME_DIR, homedir, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
651 | errExit("mount bind"); | ||
652 | |||
653 | if (u != 0) { | ||
654 | // mask /root | ||
655 | if (arg_debug) | ||
656 | printf("Mounting a new /root directory\n"); | ||
657 | if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) | ||
658 | errExit("mounting home directory"); | ||
659 | } | ||
660 | else { | ||
661 | // mask /home | ||
662 | if (arg_debug) | ||
663 | printf("Mounting a new /home directory\n"); | ||
664 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
665 | errExit("mounting home directory"); | ||
666 | } | ||
667 | |||
668 | skel(homedir, u, g); | ||
669 | if (xflag) | ||
670 | copy_xauthority(); | ||
671 | if (aflag) | ||
672 | copy_asoundrc(); | ||
673 | } | ||
diff --git a/src/firejail/main.c b/src/firejail/main.c index 501bccff2..94000d917 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -1560,21 +1560,14 @@ int main(int argc, char **argv) { | |||
1560 | arg_writable_var = 1; | 1560 | arg_writable_var = 1; |
1561 | } | 1561 | } |
1562 | else if (strcmp(argv[i], "--private") == 0) { | 1562 | else if (strcmp(argv[i], "--private") == 0) { |
1563 | #if 0 | ||
1564 | if (arg_private_template) { | ||
1565 | fprintf(stderr, "Error: --private and --private-template are mutually exclusive\n"); | ||
1566 | exit(1); | ||
1567 | } | ||
1568 | #endif | ||
1569 | arg_private = 1; | 1563 | arg_private = 1; |
1570 | } | 1564 | } |
1571 | else if (strncmp(argv[i], "--private=", 10) == 0) { | 1565 | else if (strncmp(argv[i], "--private=", 10) == 0) { |
1572 | #if 0 | 1566 | if (cfg.home_private_keep) { |
1573 | if (arg_private_template) { | 1567 | fprintf(stderr, "Error: a private list of files was already defined with --private-home option.\n"); |
1574 | fprintf(stderr, "Error: --private and --private-template are mutually exclusive\n"); | 1568 | exit(1); |
1575 | exit(1); | 1569 | } |
1576 | } | 1570 | |
1577 | #endif | ||
1578 | // extract private home dirname | 1571 | // extract private home dirname |
1579 | cfg.home_private = argv[i] + 10; | 1572 | cfg.home_private = argv[i] + 10; |
1580 | if (*cfg.home_private == '\0') { | 1573 | if (*cfg.home_private == '\0') { |
@@ -1584,21 +1577,25 @@ int main(int argc, char **argv) { | |||
1584 | fs_check_private_dir(); | 1577 | fs_check_private_dir(); |
1585 | arg_private = 1; | 1578 | arg_private = 1; |
1586 | } | 1579 | } |
1587 | #if 0 | 1580 | #ifdef HAVE_PRIVATE_HOME |
1588 | else if (strncmp(argv[i], "--private-template=", 19) == 0) { | 1581 | else if (strncmp(argv[i], "--private-home=", 15) == 0) { |
1589 | cfg.private_template = argv[i] + 19; | 1582 | if (checkcfg(CFG_PRIVATE_HOME)) { |
1590 | if (arg_private) { | 1583 | if (cfg.home_private) { |
1591 | fprintf(stderr, "Error: --private and --private-template are mutually exclusive\n"); | 1584 | fprintf(stderr, "Error: a private home directory was already defined with --private option.\n"); |
1592 | exit(1); | 1585 | exit(1); |
1593 | } | 1586 | } |
1594 | if (*cfg.private_template == '\0') { | 1587 | |
1595 | fprintf(stderr, "Error: invalid private-template option\n"); | 1588 | // extract private home dirname |
1596 | exit(1); | 1589 | cfg.home_private_keep = argv[i] + 15; |
1597 | } | 1590 | fs_check_home_list(); |
1598 | fs_check_private_template(); | 1591 | arg_private = 1; |
1599 | arg_private_template = 1; | 1592 | } |
1600 | } | 1593 | else { |
1601 | #endif | 1594 | fprintf(stderr, "Error: --private-home feature is disabled in Firejail configuration file\n"); |
1595 | exit(1); | ||
1596 | } | ||
1597 | } | ||
1598 | #endif | ||
1602 | else if (strcmp(argv[i], "--private-dev") == 0) { | 1599 | else if (strcmp(argv[i], "--private-dev") == 0) { |
1603 | arg_private_dev = 1; | 1600 | arg_private_dev = 1; |
1604 | } | 1601 | } |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index ee5d8c159..a516f3216 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -169,6 +169,18 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
169 | arg_private = 1; | 169 | arg_private = 1; |
170 | return 0; | 170 | return 0; |
171 | } | 171 | } |
172 | if (strncmp(ptr, "private-home ", 13) == 0) { | ||
173 | #ifdef HAVE_PRIVATE_HOME | ||
174 | if (checkcfg(CFG_PRIVATE_HOME)) { | ||
175 | cfg.home_private_keep = ptr + 13; | ||
176 | fs_check_home_list(); | ||
177 | arg_private = 1; | ||
178 | } | ||
179 | else | ||
180 | fprintf(stderr, "Warning: private-home is disabled in Firejail configuration file\n"); | ||
181 | #endif | ||
182 | return 0; | ||
183 | } | ||
172 | else if (strcmp(ptr, "private-dev") == 0) { | 184 | else if (strcmp(ptr, "private-dev") == 0) { |
173 | arg_private_dev = 1; | 185 | arg_private_dev = 1; |
174 | return 0; | 186 | return 0; |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 9423ae7e0..76efe996b 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -540,6 +540,14 @@ int sandbox(void* sandbox_arg) { | |||
540 | else | 540 | else |
541 | fs_private_homedir(); | 541 | fs_private_homedir(); |
542 | } | 542 | } |
543 | else if (cfg.home_private_keep) { // --private-home= | ||
544 | if (cfg.chrootdir) | ||
545 | fprintf(stderr, "Warning: private-home= feature is disabled in chroot\n"); | ||
546 | else if (arg_overlay) | ||
547 | fprintf(stderr, "Warning: private-home= feature is disabled in overlay\n"); | ||
548 | else | ||
549 | fs_private_home_list(); | ||
550 | } | ||
543 | else // --private | 551 | else // --private |
544 | fs_private(); | 552 | fs_private(); |
545 | } | 553 | } |
diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 363f973e8..52d9bbe7e 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c | |||
@@ -221,24 +221,10 @@ $ firejail \-\-overlay-path=~/jails/jail1 firefox | |||
221 | printf("\tfilesystems. All modifications are discarded when the sandbox is\n"); | 221 | printf("\tfilesystems. All modifications are discarded when the sandbox is\n"); |
222 | printf("\tclosed.\n\n"); | 222 | printf("\tclosed.\n\n"); |
223 | printf(" --private=directory - use directory as user home.\n\n"); | 223 | printf(" --private=directory - use directory as user home.\n\n"); |
224 | #if 0 | 224 | printf(" --private-home=file,directory - build a new user home in a temporary\n"); |
225 | printf(" --private-template=directory - same as --private but copy the\n"); | 225 | printf("\t\tfilesystem, and copy the files and directories in the list in\n"); |
226 | printf("\ttemplatedirectory in the tmpfs mounted user home.\n\n"); | 226 | printf("\t\tthe new home. All modifications are discarded when the sandbox\n"); |
227 | 227 | printf("\t\tis closed.\n\n"); | |
228 | .TP | ||
229 | \fB\-\-private-template=templatedir | ||
230 | Mount new /root and /home/user directories in temporary | ||
231 | filesystems, and copy all files in templatedir. All modifications are discarded when the sandbox is | ||
232 | closed. | ||
233 | .br | ||
234 | |||
235 | .br | ||
236 | Example: | ||
237 | .br | ||
238 | $ firejail \-\-private-template=/home/netblue/.config/mozilla firefox | ||
239 | #endif | ||
240 | |||
241 | |||
242 | 228 | ||
243 | printf(" --private-bin=file,file - build a new /bin in a temporary filesystem,\n"); | 229 | printf(" --private-bin=file,file - build a new /bin in a temporary filesystem,\n"); |
244 | printf("\tand copy the programs in the list.\n\n"); | 230 | printf("\tand copy the programs in the list.\n\n"); |
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index e3217bbff..51b45cd10 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt | |||
@@ -170,6 +170,12 @@ closed. | |||
170 | \fBprivate directory | 170 | \fBprivate directory |
171 | Use directory as user home. | 171 | Use directory as user home. |
172 | .TP | 172 | .TP |
173 | \f\private-home file,directory | ||
174 | Build a new user home in a temporary | ||
175 | filesystem, and copy the files and directories in the list in the | ||
176 | new home. All modifications are discarded when the sandbox is | ||
177 | closed. | ||
178 | .TP | ||
173 | \fBprivate-bin file,file | 179 | \fBprivate-bin file,file |
174 | Build a new /bin in a temporary filesystem, and copy the programs in the list. | 180 | Build a new /bin in a temporary filesystem, and copy the programs in the list. |
175 | The same directory is also bind-mounted over /sbin, /usr/bin and /usr/sbin. | 181 | The same directory is also bind-mounted over /sbin, /usr/bin and /usr/sbin. |
diff --git a/src/man/firejail.txt b/src/man/firejail.txt index dbb0df233..a5d3623b6 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt | |||
@@ -1086,6 +1086,19 @@ Example: | |||
1086 | $ firejail \-\-private=/home/netblue/firefox-home firefox | 1086 | $ firejail \-\-private=/home/netblue/firefox-home firefox |
1087 | 1087 | ||
1088 | .TP | 1088 | .TP |
1089 | \fB\-\-private-home=file,directory | ||
1090 | Build a new user home in a temporary | ||
1091 | filesystem, and copy the files and directories in the list in the | ||
1092 | new home. All modifications are discarded when the sandbox is | ||
1093 | closed. | ||
1094 | .br | ||
1095 | |||
1096 | .br | ||
1097 | Example: | ||
1098 | .br | ||
1099 | $ firejail \-\-private-home=.mozilla firefox | ||
1100 | |||
1101 | .TP | ||
1089 | \fB\-\-private-bin=file,file | 1102 | \fB\-\-private-bin=file,file |
1090 | Build a new /bin in a temporary filesystem, and copy the programs in the list. | 1103 | Build a new /bin in a temporary filesystem, and copy the programs in the list. |
1091 | If no listed file is found, /bin directory will be empty. | 1104 | If no listed file is found, /bin directory will be empty. |