diff options
-rw-r--r-- | src/firejail/firejail.h | 9 | ||||
-rw-r--r-- | src/firejail/fs_home.c | 77 | ||||
-rw-r--r-- | src/firejail/main.c | 14 | ||||
-rw-r--r-- | src/firejail/profile.c | 13 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 3 |
5 files changed, 115 insertions, 1 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 821a8e003..02a4966bc 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 |
@@ -327,6 +328,9 @@ void fs_chroot(const char *rootdir); | |||
327 | int fs_check_chroot_dir(const char *rootdir); | 328 | int fs_check_chroot_dir(const char *rootdir); |
328 | void fs_private_tmp(void); | 329 | void fs_private_tmp(void); |
329 | 330 | ||
331 | // copy all (normal) files and directory recursively | ||
332 | int fs_copydir(const char *path, const struct stat *st, int ftype, struct FTW *sftw); | ||
333 | |||
330 | // profile.c | 334 | // profile.c |
331 | // find and read the profile specified by name from dir directory | 335 | // find and read the profile specified by name from dir directory |
332 | int profile_find(const char *name, const char *dir); | 336 | int profile_find(const char *name, const char *dir); |
@@ -417,9 +421,12 @@ void fs_dev_disable_sound(); | |||
417 | void fs_private(void); | 421 | void fs_private(void); |
418 | // private mode (--private=homedir) | 422 | // private mode (--private=homedir) |
419 | void fs_private_homedir(void); | 423 | void fs_private_homedir(void); |
424 | // private template (--private-template=templatedir) | ||
425 | void fs_private_template(void); | ||
420 | // check new private home directory (--private= option) - exit if it fails | 426 | // check new private home directory (--private= option) - exit if it fails |
421 | void fs_check_private_dir(void); | 427 | void fs_check_private_dir(void); |
422 | 428 | // check new private template home directory (--private-template= option) exit if it fails | |
429 | void fs_check_private_template(void); | ||
423 | 430 | ||
424 | // seccomp.c | 431 | // seccomp.c |
425 | int seccomp_filter_drop(int enforce_seccomp); | 432 | int seccomp_filter_drop(int enforce_seccomp); |
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 41092de2b..76f99cead 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,43 @@ 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 | char *homedir = cfg.homedir; | ||
342 | char *dest; | ||
343 | int srcbaselen = 0; | ||
344 | assert(homedir); | ||
345 | uid_t u = getuid(); | ||
346 | gid_t g = getgid(); | ||
347 | srcbaselen = strlen(cfg.private_template); | ||
348 | |||
349 | if(ftype == FTW_F || ftype == FTW_D) { | ||
350 | if (asprintf(&dest, "%s/%s", homedir, path + srcbaselen) == -1) | ||
351 | errExit("asprintf"); | ||
352 | struct stat s; | ||
353 | // don't copy it if we already have the file | ||
354 | if (stat(dest, &s) == 0) | ||
355 | return 0; | ||
356 | if (stat(path, &s) == 0) { | ||
357 | if (copy_file(path, dest) == 0) { | ||
358 | if (chown(dest, u, g) == -1) | ||
359 | errExit("chown"); | ||
360 | fs_logger("clone %s", path); | ||
361 | } | ||
362 | } | ||
363 | free(dest); | ||
364 | } | ||
365 | return(0); | ||
366 | } | ||
367 | |||
368 | void fs_private_template(void) { | ||
369 | fs_private(); | ||
370 | if(!nftw(cfg.private_template, fs_copydir, 1, FTW_PHYS)) { | ||
371 | fprintf(stderr, "Error: unable to copy template dir\n"); | ||
372 | exit(1); | ||
373 | } | ||
374 | } | ||
337 | 375 | ||
338 | // check new private home directory (--private= option) - exit if it fails | 376 | // check new private home directory (--private= option) - exit if it fails |
339 | void fs_check_private_dir(void) { | 377 | void fs_check_private_dir(void) { |
@@ -373,3 +411,42 @@ void fs_check_private_dir(void) { | |||
373 | } | 411 | } |
374 | } | 412 | } |
375 | 413 | ||
414 | // check new template home directoty (--private-template= option) - exit if it fails | ||
415 | void fs_check_private_template(void) { | ||
416 | EUID_ASSERT(); | ||
417 | invalid_filename(cfg.private_template); | ||
418 | |||
419 | // Expand the home directory | ||
420 | char *tmp = expand_home(cfg.private_template, cfg.homedir); | ||
421 | cfg.private_template = realpath(tmp, NULL); | ||
422 | free(tmp); | ||
423 | |||
424 | if (!cfg.private_template | ||
425 | || !is_dir(cfg.private_template) | ||
426 | || is_link(cfg.private_template) | ||
427 | || strstr(cfg.private_template, "..")) { | ||
428 | fprintf(stderr, "Error: invalid private template directory\n"); | ||
429 | exit(1); | ||
430 | } | ||
431 | |||
432 | // check home directory and chroot home directory have the same owner | ||
433 | struct stat s2; | ||
434 | int rv = stat(cfg.private_template, &s2); | ||
435 | if (rv < 0) { | ||
436 | fprintf(stderr, "Error: cannot find %s directory\n", cfg.private_template); | ||
437 | exit(1); | ||
438 | } | ||
439 | |||
440 | struct stat s1; | ||
441 | rv = stat(cfg.homedir, &s1); | ||
442 | if (rv < 0) { | ||
443 | fprintf(stderr, "Error: cannot find %s directory, full path name required\n", cfg.homedir); | ||
444 | exit(1); | ||
445 | } | ||
446 | if (s1.st_uid != s2.st_uid) { | ||
447 | printf("Error: --private-template directory should be owned by the current user\n"); | ||
448 | exit(1); | ||
449 | } | ||
450 | } | ||
451 | |||
452 | |||
diff --git a/src/firejail/main.c b/src/firejail/main.c index b6fd745a2..b6b97c98c 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 |
@@ -1360,6 +1361,19 @@ int main(int argc, char **argv) { | |||
1360 | fs_check_private_dir(); | 1361 | fs_check_private_dir(); |
1361 | arg_private = 1; | 1362 | arg_private = 1; |
1362 | } | 1363 | } |
1364 | else if (strcmp(argv[i], "--private-template=", 19) == 0) { | ||
1365 | cfg.private_template = argv[i] + 14; | ||
1366 | if (arg_private) { | ||
1367 | fprintf(stderr, "Error: --private and --private-template are mutually exclusive\n"); | ||
1368 | exit(1); | ||
1369 | } | ||
1370 | if (*cfg.private_template == '\0') { | ||
1371 | fprintf(stderr, "Error: invalid private-template option\n"); | ||
1372 | exit(1); | ||
1373 | } | ||
1374 | fs_check_private_template(); | ||
1375 | arg_private_template = 1; | ||
1376 | } | ||
1363 | else if (strcmp(argv[i], "--private-dev") == 0) { | 1377 | else if (strcmp(argv[i], "--private-dev") == 0) { |
1364 | arg_private_dev = 1; | 1378 | arg_private_dev = 1; |
1365 | } | 1379 | } |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 15cc1e55a..5aeba2f55 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -169,6 +169,10 @@ 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 | else if (strcmp(ptr, "private-template") == 0) { | ||
173 | arg_private_template = 1; | ||
174 | return 0; | ||
175 | } | ||
172 | else if (strcmp(ptr, "private-dev") == 0) { | 176 | else if (strcmp(ptr, "private-dev") == 0) { |
173 | arg_private_dev = 1; | 177 | arg_private_dev = 1; |
174 | return 0; | 178 | return 0; |
@@ -614,6 +618,15 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
614 | return 0; | 618 | return 0; |
615 | } | 619 | } |
616 | 620 | ||
621 | if (strncmp(ptr, "private-template ", 17) == 0) { | ||
622 | if (arg_private) { | ||
623 | fprintf(stderr, "Error: --private and --private-template are mutually exclusive\n"); | ||
624 | exit(1); | ||
625 | } | ||
626 | cfg.private_template = ptr + 17; | ||
627 | fs_check_private_template(); | ||
628 | arg_private_template = 1; | ||
629 | } | ||
617 | // private /etc list of files and directories | 630 | // private /etc list of files and directories |
618 | if (strncmp(ptr, "private-etc ", 12) == 0) { | 631 | if (strncmp(ptr, "private-etc ", 12) == 0) { |
619 | if (arg_writable_etc) { | 632 | if (arg_writable_etc) { |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 9bf2a0a39..d9866385e 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) { |