aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@yahoo.com>2016-09-04 13:52:16 -0400
committerLibravatar netblue30 <netblue30@yahoo.com>2016-09-04 13:52:16 -0400
commit6aad9ad431f749003b4eab7b91cfdd0f218852a2 (patch)
tree9b7d89269ff05af0a6259668a8e8c5a456ac7666
parentfixes (diff)
downloadfirejail-6aad9ad431f749003b4eab7b91cfdd0f218852a2.tar.gz
firejail-6aad9ad431f749003b4eab7b91cfdd0f218852a2.tar.zst
firejail-6aad9ad431f749003b4eab7b91cfdd0f218852a2.zip
bringing back --private-home
-rw-r--r--README2
-rw-r--r--README.md2
-rw-r--r--RELNOTES1
-rwxr-xr-xconfigure17
-rw-r--r--configure.ac9
-rw-r--r--etc/firejail.config3
-rw-r--r--src/firejail/Makefile.in3
-rw-r--r--src/firejail/checkcfg.c32
-rw-r--r--src/firejail/firejail.h4
-rw-r--r--src/firejail/fs_home.c408
-rw-r--r--src/firejail/main.c51
-rw-r--r--src/firejail/profile.c12
-rw-r--r--src/firejail/sandbox.c8
-rw-r--r--src/firejail/usage.c22
-rw-r--r--src/man/firejail-profile.txt6
-rw-r--r--src/man/firejail.txt13
16 files changed, 446 insertions, 147 deletions
diff --git a/README b/README
index 8923abaf4..7ba78a05f 100644
--- a/README
+++ b/README
@@ -87,7 +87,7 @@ greigdp (https://github.com/greigdp)
87Laurent Declercq (https://github.com/nuxwin) 87Laurent Declercq (https://github.com/nuxwin)
88 - fixed test for shell interpreter in chroots 88 - fixed test for shell interpreter in chroots
89Franco (nextime) Lanza (https://github.com/nextime) 89Franco (nextime) Lanza (https://github.com/nextime)
90 - added --private-template 90 - added --private-template/--private-home
91xee5ch (https://github.com/xee5ch) 91xee5ch (https://github.com/xee5ch)
92 - skypeforlinux profile 92 - skypeforlinux profile
93Peter Hogg (https://github.com/pigmonkey) 93Peter Hogg (https://github.com/pigmonkey)
diff --git a/README.md b/README.md
index ebd39de5b..2f2ab38e7 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,8 @@ FAQ: https://firejail.wordpress.com/support/frequently-asked-questions/
38 38
39Version 0.9.41~rc1 was released. 39Version 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.
diff --git a/RELNOTES b/RELNOTES
index d07bfa896..734a92b4f 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -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"
diff --git a/configure b/configure
index 3f9d0fc42..0b05d42e5 100755
--- a/configure
+++ b/configure
@@ -636,6 +636,7 @@ HAVE_GLOBALCFG
636HAVE_BIND 636HAVE_BIND
637HAVE_CHROOT 637HAVE_CHROOT
638HAVE_SECCOMP 638HAVE_SECCOMP
639HAVE_PRIVATE_HOME
639HAVE_OVERLAYFS 640HAVE_OVERLAYFS
640EXTRA_LDFLAGS 641EXTRA_LDFLAGS
641EGREP 642EGREP
@@ -696,6 +697,7 @@ ac_user_opts='
696enable_option_checking 697enable_option_checking
697enable_apparmor 698enable_apparmor
698enable_overlayfs 699enable_overlayfs
700enable_private_home
699enable_seccomp 701enable_seccomp
700enable_chroot 702enable_chroot
701enable_bind 703enable_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
3528fi 3531fi
3529 3532
3533HAVE_PRIVATEHOME=""
3534# Check whether --enable-private-home was given.
3535if test "${enable_private_home+set}" = set; then :
3536 enableval=$enable_private_home;
3537fi
3538
3539if test "x$enable_private_home" != "xno"; then :
3540
3541 HAVE_PRIVATE_HOME="-DHAVE_PRIVATE_HOME"
3542
3543
3544fi
3545
3530HAVE_SECCOMP="" 3546HAVE_SECCOMP=""
3531# Check whether --enable-seccomp was given. 3547# Check whether --enable-seccomp was given.
3532if test "${enable_seccomp+set}" = set; then : 3548if test "${enable_seccomp+set}" = set; then :
@@ -4937,6 +4953,7 @@ echo " network: $HAVE_NETWORK"
4937echo " user namespace: $HAVE_USERNS" 4953echo " user namespace: $HAVE_USERNS"
4938echo " X11 sandboxing support: $HAVE_X11" 4954echo " X11 sandboxing support: $HAVE_X11"
4939echo " whitelisting: $HAVE_WHITELIST" 4955echo " whitelisting: $HAVE_WHITELIST"
4956echo " private home support: $HAVE_PRIVATE_HOME"
4940echo " file transfer support: $HAVE_FILE_TRANSFER" 4957echo " file transfer support: $HAVE_FILE_TRANSFER"
4941echo " overlayfs support: $HAVE_OVERLAYFS" 4958echo " overlayfs support: $HAVE_OVERLAYFS"
4942echo " fatal warnings: $HAVE_FATAL_WARNINGS" 4959echo " 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
41HAVE_PRIVATEHOME=""
42AC_ARG_ENABLE([private-home],
43 AS_HELP_STRING([--disable-private-home], [disable private home feature]))
44AS_IF([test "x$enable_private_home" != "xno"], [
45 HAVE_PRIVATE_HOME="-DHAVE_PRIVATE_HOME"
46 AC_SUBST(HAVE_PRIVATE_HOME)
47])
48
41HAVE_SECCOMP="" 49HAVE_SECCOMP=""
42AC_ARG_ENABLE([seccomp], 50AC_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"
163echo " user namespace: $HAVE_USERNS" 171echo " user namespace: $HAVE_USERNS"
164echo " X11 sandboxing support: $HAVE_X11" 172echo " X11 sandboxing support: $HAVE_X11"
165echo " whitelisting: $HAVE_WHITELIST" 173echo " whitelisting: $HAVE_WHITELIST"
174echo " private home support: $HAVE_PRIVATE_HOME"
166echo " file transfer support: $HAVE_FILE_TRANSFER" 175echo " file transfer support: $HAVE_FILE_TRANSFER"
167echo " overlayfs support: $HAVE_OVERLAYFS" 176echo " overlayfs support: $HAVE_OVERLAYFS"
168echo " fatal warnings: $HAVE_FATAL_WARNINGS" 177echo " 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@
20HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ 20HAVE_GLOBALCFG=@HAVE_GLOBALCFG@
21HAVE_APPARMOR=@HAVE_APPARMOR@ 21HAVE_APPARMOR=@HAVE_APPARMOR@
22HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ 22HAVE_OVERLAYFS=@HAVE_OVERLAYFS@
23HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@
23EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ 24EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@
24 25
25H_FILE_LIST = $(sort $(wildcard *.[h])) 26H_FILE_LIST = $(sort $(wildcard *.[h]))
26C_FILE_LIST = $(sort $(wildcard *.c)) 27C_FILE_LIST = $(sort $(wildcard *.c))
27OBJS = $(C_FILE_LIST:.c=.o) 28OBJS = $(C_FILE_LIST:.c=.o)
28BINOBJS = $(foreach file, $(OBJS), $file) 29BINOBJS = $(foreach file, $(OBJS), $file)
29CFLAGS += -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 30CFLAGS += -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
30LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread 31LDFLAGS += -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
644extern char *xephyr_screen; 646extern char *xephyr_screen;
645extern char *xephyr_extra_params; 647extern char *xephyr_extra_params;
646extern char *netfilter_default; 648extern 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
316int fs_copydir(const char *path, const struct stat *st, int ftype, struct FTW *sftw);
317
318
319int 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
361void 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
372void fs_check_private_dir(void) { 317void 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//***********************************************************************************
410void 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 358static int size_limit_reached = 0;
414 // Expand the home directory 359static unsigned file_cnt = 0;
415 char *tmp = expand_home(cfg.private_template, cfg.homedir); 360static unsigned size_cnt = 0;
416 cfg.private_template = realpath(tmp, NULL); 361static char *check_dir_or_file(const char *name);
417 free(tmp); 362
418 363int 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
414struct stat s2;
415if (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
438static 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
462static 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
545void 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
575void 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
230Mount new /root and /home/user directories in temporary
231filesystems, and copy all files in templatedir. All modifications are discarded when the sandbox is
232closed.
233.br
234
235.br
236Example:
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
171Use directory as user home. 171Use directory as user home.
172.TP 172.TP
173\f\private-home file,directory
174Build a new user home in a temporary
175filesystem, and copy the files and directories in the list in the
176new home. All modifications are discarded when the sandbox is
177closed.
178.TP
173\fBprivate-bin file,file 179\fBprivate-bin file,file
174Build a new /bin in a temporary filesystem, and copy the programs in the list. 180Build a new /bin in a temporary filesystem, and copy the programs in the list.
175The same directory is also bind-mounted over /sbin, /usr/bin and /usr/sbin. 181The 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
1090Build a new user home in a temporary
1091filesystem, and copy the files and directories in the list in the
1092new home. All modifications are discarded when the sandbox is
1093closed.
1094.br
1095
1096.br
1097Example:
1098.br
1099$ firejail \-\-private-home=.mozilla firefox
1100
1101.TP
1089\fB\-\-private-bin=file,file 1102\fB\-\-private-bin=file,file
1090Build a new /bin in a temporary filesystem, and copy the programs in the list. 1103Build a new /bin in a temporary filesystem, and copy the programs in the list.
1091If no listed file is found, /bin directory will be empty. 1104If no listed file is found, /bin directory will be empty.