diff options
author | netblue30 <netblue30@yahoo.com> | 2016-08-04 09:20:19 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-08-04 09:20:19 -0400 |
commit | bf84057200bb6dab84af11f777e3a45cb6e66239 (patch) | |
tree | 102e6c6a952f03d98f6132cc26f2d98f87d27b37 | |
parent | apparmor fixes for Arch Linux (diff) | |
parent | Fix dir creation owner (diff) | |
download | firejail-bf84057200bb6dab84af11f777e3a45cb6e66239.tar.gz firejail-bf84057200bb6dab84af11f777e3a45cb6e66239.tar.zst firejail-bf84057200bb6dab84af11f777e3a45cb6e66239.zip |
Merge pull request #681 from nextime/master
add --private-template=directory option
-rw-r--r-- | src/firejail/firejail.h | 7 | ||||
-rw-r--r-- | src/firejail/fs_home.c | 95 | ||||
-rw-r--r-- | src/firejail/main.c | 25 | ||||
-rw-r--r-- | src/firejail/profile.c | 11 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 3 | ||||
-rw-r--r-- | src/firejail/usage.c | 3 | ||||
-rw-r--r-- | src/man/firejail.txt | 12 |
7 files changed, 154 insertions, 2 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 821a8e003..333cd92f4 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -136,6 +136,7 @@ typedef struct config_t { | |||
136 | char *bin_private_keep; // keep list for private bin directory | 136 | char *bin_private_keep; // keep list for private bin directory |
137 | char *cwd; // current working directory | 137 | char *cwd; // current working directory |
138 | char *overlay_dir; | 138 | char *overlay_dir; |
139 | char *private_template; // template dir for tmpfs home | ||
139 | 140 | ||
140 | // networking | 141 | // networking |
141 | char *name; // sandbox name | 142 | char *name; // sandbox name |
@@ -212,6 +213,7 @@ static inline int any_interface_configured(void) { | |||
212 | void clear_run_files(pid_t pid); | 213 | void clear_run_files(pid_t pid); |
213 | 214 | ||
214 | extern int arg_private; // mount private /home | 215 | extern int arg_private; // mount private /home |
216 | extern int arg_private_template; // private /home template | ||
215 | extern int arg_debug; // print debug messages | 217 | extern int arg_debug; // print debug messages |
216 | extern int arg_debug_check_filename; // print debug messages for filename checking | 218 | extern int arg_debug_check_filename; // print debug messages for filename checking |
217 | extern int arg_debug_blacklists; // print debug messages for blacklists | 219 | extern int arg_debug_blacklists; // print debug messages for blacklists |
@@ -417,9 +419,12 @@ void fs_dev_disable_sound(); | |||
417 | void fs_private(void); | 419 | void fs_private(void); |
418 | // private mode (--private=homedir) | 420 | // private mode (--private=homedir) |
419 | void fs_private_homedir(void); | 421 | void fs_private_homedir(void); |
422 | // private template (--private-template=templatedir) | ||
423 | void fs_private_template(void); | ||
420 | // check new private home directory (--private= option) - exit if it fails | 424 | // check new private home directory (--private= option) - exit if it fails |
421 | void fs_check_private_dir(void); | 425 | void fs_check_private_dir(void); |
422 | 426 | // check new private template home directory (--private-template= option) exit if it fails | |
427 | void fs_check_private_template(void); | ||
423 | 428 | ||
424 | // seccomp.c | 429 | // seccomp.c |
425 | int seccomp_filter_drop(int enforce_seccomp); | 430 | int seccomp_filter_drop(int enforce_seccomp); |
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 41092de2b..105092036 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <sys/wait.h> | 28 | #include <sys/wait.h> |
29 | #include <unistd.h> | 29 | #include <unistd.h> |
30 | #include <grp.h> | 30 | #include <grp.h> |
31 | #include <ftw.h> | ||
31 | 32 | ||
32 | static void skel(const char *homedir, uid_t u, gid_t g) { | 33 | static void skel(const char *homedir, uid_t u, gid_t g) { |
33 | char *fname; | 34 | char *fname; |
@@ -334,6 +335,61 @@ void fs_private(void) { | |||
334 | 335 | ||
335 | } | 336 | } |
336 | 337 | ||
338 | int fs_copydir(const char *path, const struct stat *st, int ftype, struct FTW *sftw); | ||
339 | |||
340 | |||
341 | int fs_copydir(const char *path, const struct stat *st, int ftype, struct FTW *sftw) | ||
342 | { | ||
343 | |||
344 | char *homedir = cfg.homedir; | ||
345 | char *dest; | ||
346 | int srcbaselen = 0; | ||
347 | assert(homedir); | ||
348 | uid_t u = getuid(); | ||
349 | gid_t g = getgid(); | ||
350 | srcbaselen = strlen(cfg.private_template); | ||
351 | |||
352 | if(ftype == FTW_F || ftype == FTW_D) { | ||
353 | if (asprintf(&dest, "%s/%s", homedir, path + srcbaselen) == -1) | ||
354 | errExit("asprintf"); | ||
355 | struct stat s; | ||
356 | // don't copy it if we already have the file | ||
357 | if (stat(dest, &s) == 0) | ||
358 | return(0); | ||
359 | if (stat(path, &s) == 0) { | ||
360 | if(ftype == FTW_F) { | ||
361 | if (copy_file(path, dest) == 0) { | ||
362 | if (arg_debug) | ||
363 | printf("copy from %s to %s\n", path, dest); | ||
364 | if (chown(dest, u, g) == -1) | ||
365 | errExit("chown"); | ||
366 | fs_logger2("clone", path); | ||
367 | } | ||
368 | } | ||
369 | else if(ftype == FTW_D) { | ||
370 | if (mkdir(dest, s.st_mode) == -1) | ||
371 | errExit("mkdir"); | ||
372 | if (chown(dest, u, g) < 0) | ||
373 | errExit("chown"); | ||
374 | if (arg_debug) | ||
375 | printf("copy from %s to %s\n", path, dest); | ||
376 | fs_logger2("clone", path); | ||
377 | } | ||
378 | } | ||
379 | free(dest); | ||
380 | } | ||
381 | return(0); | ||
382 | } | ||
383 | |||
384 | void fs_private_template(void) { | ||
385 | |||
386 | fs_private(); | ||
387 | if(nftw(cfg.private_template, fs_copydir, 1, FTW_PHYS) != 0) { | ||
388 | fprintf(stderr, "Error: unable to copy template dir\n"); | ||
389 | exit(1); | ||
390 | } | ||
391 | |||
392 | } | ||
337 | 393 | ||
338 | // check new private home directory (--private= option) - exit if it fails | 394 | // check new private home directory (--private= option) - exit if it fails |
339 | void fs_check_private_dir(void) { | 395 | void fs_check_private_dir(void) { |
@@ -373,3 +429,42 @@ void fs_check_private_dir(void) { | |||
373 | } | 429 | } |
374 | } | 430 | } |
375 | 431 | ||
432 | // check new template home directoty (--private-template= option) - exit if it fails | ||
433 | void fs_check_private_template(void) { | ||
434 | EUID_ASSERT(); | ||
435 | invalid_filename(cfg.private_template); | ||
436 | |||
437 | // Expand the home directory | ||
438 | char *tmp = expand_home(cfg.private_template, cfg.homedir); | ||
439 | cfg.private_template = realpath(tmp, NULL); | ||
440 | free(tmp); | ||
441 | |||
442 | if (!cfg.private_template | ||
443 | || !is_dir(cfg.private_template) | ||
444 | || is_link(cfg.private_template) | ||
445 | || strstr(cfg.private_template, "..")) { | ||
446 | fprintf(stderr, "Error: invalid private template directory\n"); | ||
447 | exit(1); | ||
448 | } | ||
449 | |||
450 | // check home directory and chroot home directory have the same owner | ||
451 | struct stat s2; | ||
452 | int rv = stat(cfg.private_template, &s2); | ||
453 | if (rv < 0) { | ||
454 | fprintf(stderr, "Error: cannot find %s directory\n", cfg.private_template); | ||
455 | exit(1); | ||
456 | } | ||
457 | |||
458 | struct stat s1; | ||
459 | rv = stat(cfg.homedir, &s1); | ||
460 | if (rv < 0) { | ||
461 | fprintf(stderr, "Error: cannot find %s directory, full path name required\n", cfg.homedir); | ||
462 | exit(1); | ||
463 | } | ||
464 | if (s1.st_uid != s2.st_uid) { | ||
465 | printf("Error: --private-template directory should be owned by the current user\n"); | ||
466 | exit(1); | ||
467 | } | ||
468 | } | ||
469 | |||
470 | |||
diff --git a/src/firejail/main.c b/src/firejail/main.c index b6fd745a2..9f6fa5142 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -51,6 +51,7 @@ uid_t firejail_uid = 0; | |||
51 | static char child_stack[STACK_SIZE]; // space for child's stack | 51 | static char child_stack[STACK_SIZE]; // space for child's stack |
52 | Config cfg; // configuration | 52 | Config cfg; // configuration |
53 | int arg_private = 0; // mount private /home and /tmp directoryu | 53 | int arg_private = 0; // mount private /home and /tmp directoryu |
54 | int arg_private_template = 0; // mount private /home using a template | ||
54 | int arg_debug = 0; // print debug messages | 55 | int arg_debug = 0; // print debug messages |
55 | int arg_debug_check_filename; // print debug messages for filename checking | 56 | int arg_debug_check_filename; // print debug messages for filename checking |
56 | int arg_debug_blacklists; // print debug messages for blacklists | 57 | int arg_debug_blacklists; // print debug messages for blacklists |
@@ -1348,9 +1349,18 @@ int main(int argc, char **argv) { | |||
1348 | else if (strcmp(argv[i], "--writable-var") == 0) { | 1349 | else if (strcmp(argv[i], "--writable-var") == 0) { |
1349 | arg_writable_var = 1; | 1350 | arg_writable_var = 1; |
1350 | } | 1351 | } |
1351 | else if (strcmp(argv[i], "--private") == 0) | 1352 | else if (strcmp(argv[i], "--private") == 0) { |
1353 | if (arg_private_template) { | ||
1354 | fprintf(stderr, "Error: --private and --private-template are mutually exclusive\n"); | ||
1355 | exit(1); | ||
1356 | } | ||
1352 | arg_private = 1; | 1357 | arg_private = 1; |
1358 | } | ||
1353 | else if (strncmp(argv[i], "--private=", 10) == 0) { | 1359 | else if (strncmp(argv[i], "--private=", 10) == 0) { |
1360 | if (arg_private_template) { | ||
1361 | fprintf(stderr, "Error: --private and --private-template are mutually exclusive\n"); | ||
1362 | exit(1); | ||
1363 | } | ||
1354 | // extract private home dirname | 1364 | // extract private home dirname |
1355 | cfg.home_private = argv[i] + 10; | 1365 | cfg.home_private = argv[i] + 10; |
1356 | if (*cfg.home_private == '\0') { | 1366 | if (*cfg.home_private == '\0') { |
@@ -1360,6 +1370,19 @@ int main(int argc, char **argv) { | |||
1360 | fs_check_private_dir(); | 1370 | fs_check_private_dir(); |
1361 | arg_private = 1; | 1371 | arg_private = 1; |
1362 | } | 1372 | } |
1373 | else if (strncmp(argv[i], "--private-template=", 19) == 0) { | ||
1374 | cfg.private_template = argv[i] + 19; | ||
1375 | if (arg_private) { | ||
1376 | fprintf(stderr, "Error: --private and --private-template are mutually exclusive\n"); | ||
1377 | exit(1); | ||
1378 | } | ||
1379 | if (*cfg.private_template == '\0') { | ||
1380 | fprintf(stderr, "Error: invalid private-template option\n"); | ||
1381 | exit(1); | ||
1382 | } | ||
1383 | fs_check_private_template(); | ||
1384 | arg_private_template = 1; | ||
1385 | } | ||
1363 | else if (strcmp(argv[i], "--private-dev") == 0) { | 1386 | else if (strcmp(argv[i], "--private-dev") == 0) { |
1364 | arg_private_dev = 1; | 1387 | arg_private_dev = 1; |
1365 | } | 1388 | } |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 15cc1e55a..1403db704 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -614,6 +614,17 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
614 | return 0; | 614 | return 0; |
615 | } | 615 | } |
616 | 616 | ||
617 | if (strncmp(ptr, "private-template ", 17) == 0) { | ||
618 | if (arg_private) { | ||
619 | fprintf(stderr, "Error: --private and --private-template are mutually exclusive\n"); | ||
620 | exit(1); | ||
621 | } | ||
622 | cfg.private_template = ptr + 17; | ||
623 | fs_check_private_template(); | ||
624 | arg_private_template = 1; | ||
625 | |||
626 | return 0; | ||
627 | } | ||
617 | // private /etc list of files and directories | 628 | // private /etc list of files and directories |
618 | if (strncmp(ptr, "private-etc ", 12) == 0) { | 629 | if (strncmp(ptr, "private-etc ", 12) == 0) { |
619 | if (arg_writable_etc) { | 630 | if (arg_writable_etc) { |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index dc107f00a..f37605e20 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -524,6 +524,9 @@ int sandbox(void* sandbox_arg) { | |||
524 | fs_private(); | 524 | fs_private(); |
525 | } | 525 | } |
526 | 526 | ||
527 | if (arg_private_template) | ||
528 | fs_private_template(); | ||
529 | |||
527 | if (arg_private_dev) | 530 | if (arg_private_dev) |
528 | fs_private_dev(); | 531 | fs_private_dev(); |
529 | if (arg_private_etc) { | 532 | if (arg_private_etc) { |
diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 958a16da7..baba93791 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c | |||
@@ -189,6 +189,9 @@ void usage(void) { | |||
189 | printf("\tclosed.\n\n"); | 189 | printf("\tclosed.\n\n"); |
190 | printf(" --private=directory - use directory as user home.\n\n"); | 190 | printf(" --private=directory - use directory as user home.\n\n"); |
191 | 191 | ||
192 | printf(" --private-template=directory - same as --private but copy the\n"); | ||
193 | printf("\ttemplatedirectory in the tmpfs mounted user home.\n\n"); | ||
194 | |||
192 | printf(" --private-bin=file,file - build a new /bin in a temporary filesystem,\n"); | 195 | printf(" --private-bin=file,file - build a new /bin in a temporary filesystem,\n"); |
193 | printf("\tand copy the programs in the list.\n\n"); | 196 | printf("\tand copy the programs in the list.\n\n"); |
194 | 197 | ||
diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 9e6916534..c6b73f428 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt | |||
@@ -1045,6 +1045,18 @@ Example: | |||
1045 | $ firejail \-\-private=/home/netblue/firefox-home firefox | 1045 | $ firejail \-\-private=/home/netblue/firefox-home firefox |
1046 | 1046 | ||
1047 | .TP | 1047 | .TP |
1048 | \fB\-\-private-template=templatedir | ||
1049 | Mount new /root and /home/user directories in temporary | ||
1050 | filesystems, and copy all files in templatedir. All modifications are discarded when the sandbox is | ||
1051 | closed. | ||
1052 | .br | ||
1053 | |||
1054 | .br | ||
1055 | Example: | ||
1056 | .br | ||
1057 | $ firejail \-\-private-template=/home/netblue/.config/mozilla firefox | ||
1058 | |||
1059 | .TP | ||
1048 | \fB\-\-private-bin=file,file | 1060 | \fB\-\-private-bin=file,file |
1049 | Build a new /bin in a temporary filesystem, and copy the programs in the list. | 1061 | Build a new /bin in a temporary filesystem, and copy the programs in the list. |
1050 | If no listed file is found, /bin directory will be empty. | 1062 | If no listed file is found, /bin directory will be empty. |