aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/fs_home.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/firejail/fs_home.c')
-rw-r--r--src/firejail/fs_home.c315
1 files changed, 77 insertions, 238 deletions
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c
index 242482d26..1f8da398e 100644
--- a/src/firejail/fs_home.c
+++ b/src/firejail/fs_home.c
@@ -28,7 +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//#include <ftw.h>
32 32
33static void skel(const char *homedir, uid_t u, gid_t g) { 33static void skel(const char *homedir, uid_t u, gid_t g) {
34 char *fname; 34 char *fname;
@@ -212,12 +212,6 @@ void fs_private_homedir(void) {
212 212
213 uid_t u = getuid(); 213 uid_t u = getuid();
214 gid_t g = getgid(); 214 gid_t g = getgid();
215 struct stat s;
216 if (stat(homedir, &s) == -1) {
217 fprintf(stderr, "Error: cannot find user home directory\n");
218 exit(1);
219 }
220
221 215
222 // mount bind private_homedir on top of homedir 216 // mount bind private_homedir on top of homedir
223 if (arg_debug) 217 if (arg_debug)
@@ -349,122 +343,17 @@ void fs_check_private_dir(void) {
349//*********************************************************************************** 343//***********************************************************************************
350// --private-home 344// --private-home
351//*********************************************************************************** 345//***********************************************************************************
352#define PRIVATE_COPY_LIMIT (500 * 1024 *1024)
353static int size_limit_reached = 0;
354static unsigned file_cnt = 0;
355static unsigned size_cnt = 0;
356static char *check_dir_or_file(const char *name);
357
358int fs_copydir(const char *path, const struct stat *st, int ftype, struct FTW *sftw) {
359 (void) st;
360 (void) sftw;
361 if (size_limit_reached)
362 return 0;
363
364 struct stat s;
365 char *dest;
366 if (asprintf(&dest, "%s%s", RUN_HOME_DIR, path + strlen(cfg.homedir)) == -1)
367 errExit("asprintf");
368
369 // don't copy it if we already have the file
370 if (stat(dest, &s) == 0) {
371 free(dest);
372 return 0;
373 }
374
375 // extract mode and ownership
376 if (stat(path, &s) != 0) {
377 free(dest);
378 return 0;
379 }
380
381 // check uid
382 if (s.st_uid != firejail_uid || s.st_gid != firejail_gid) {
383 free(dest);
384 return 0;
385 }
386
387 if ((s.st_size + size_cnt) > PRIVATE_COPY_LIMIT) {
388 size_limit_reached = 1;
389 free(dest);
390 return 0;
391 }
392
393 file_cnt++;
394 size_cnt += s.st_size;
395
396 if(ftype == FTW_F)
397 copy_file(path, dest, firejail_uid, firejail_gid, s.st_mode);
398 else if (ftype == FTW_D) {
399 if (mkdir(dest, s.st_mode) == -1)
400 errExit("mkdir");
401 if (set_perms(dest, firejail_uid, firejail_gid, s.st_mode))
402 errExit("set_perms");
403#if 0
404struct stat s2;
405if (stat(dest, &s2) == 0) {
406 printf("%s\t", dest);
407 printf((S_ISDIR(s.st_mode)) ? "d" : "-");
408 printf((s.st_mode & S_IRUSR) ? "r" : "-");
409 printf((s.st_mode & S_IWUSR) ? "w" : "-");
410 printf((s.st_mode & S_IXUSR) ? "x" : "-");
411 printf((s.st_mode & S_IRGRP) ? "r" : "-");
412 printf((s.st_mode & S_IWGRP) ? "w" : "-");
413 printf((s.st_mode & S_IXGRP) ? "x" : "-");
414 printf((s.st_mode & S_IROTH) ? "r" : "-");
415 printf((s.st_mode & S_IWOTH) ? "w" : "-");
416 printf((s.st_mode & S_IXOTH) ? "x" : "-");
417 printf("\n");
418}
419#endif
420
421 fs_logger2("clone", path);
422 }
423
424 free(dest);
425 return(0);
426}
427
428static void duplicate(char *name) {
429 char *fname = check_dir_or_file(name);
430
431 if (arg_debug)
432 printf("Private home: duplicating %s\n", fname);
433 assert(strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0);
434
435 struct stat s;
436 if (stat(fname, &s) == -1) {
437 free(fname);
438 return;
439 }
440
441 if(nftw(fname, fs_copydir, 1, FTW_PHYS) != 0) {
442 fprintf(stderr, "Error: unable to copy template dir\n");
443 exit(1);
444 }
445 fs_logger_print(); // save the current log
446
447 free(fname);
448}
449
450
451
452static char *check_dir_or_file(const char *name) { 346static char *check_dir_or_file(const char *name) {
453 assert(name); 347 assert(name);
454 struct stat s;
455 348
456 // basic checks 349 // basic checks
457 invalid_filename(name); 350 invalid_filename(name);
458
459 if (arg_debug) 351 if (arg_debug)
460 printf("Private home: checking %s\n", name); 352 printf("Private home: checking %s\n", name);
461 353
462 // expand home directory 354 // expand home directory
463 char *fname = expand_home(name, cfg.homedir); 355 char *fname = expand_home(name, cfg.homedir);
464 if (!fname) { 356 assert(fname);
465 fprintf(stderr, "Error: file %s not found.\n", name);
466 exit(1);
467 }
468 357
469 // If it doesn't start with '/', it must be relative to homedir 358 // If it doesn't start with '/', it must be relative to homedir
470 if (fname[0] != '/') { 359 if (fname[0] != '/') {
@@ -475,87 +364,77 @@ static char *check_dir_or_file(const char *name) {
475 fname = tmp; 364 fname = tmp;
476 } 365 }
477 366
478 // check the file is in user home directory 367 // we allow only files in user home directory or symbolic links to files or directories owned by the user
479 char *rname = realpath(fname, NULL); 368 struct stat s;
480 if (!rname) { 369 if (lstat(fname, &s) == 0 && S_ISLNK(s.st_mode)) {
481 fprintf(stderr, "Error: invalid file %s\n", name); 370 if (stat(fname, &s) == 0) {
482 exit(1); 371 if (s.st_uid != getuid()) {
483 } 372 fprintf(stderr, "Error: symbolic link %s to file or directory not owned by the user\n", fname);
484 if (strncmp(rname, cfg.homedir, strlen(cfg.homedir)) != 0) { 373 exit(1);
485 fprintf(stderr, "Error: file %s is not in user home directory\n", name); 374 }
486 exit(1); 375 return fname;
487 } 376 }
488 377 else {
489 // a full home directory is not allowed 378 fprintf(stderr, "Error: invalid file %s\n", name);
490 if (strcmp(rname, cfg.homedir) == 0) {
491 fprintf(stderr, "Error: invalid directory %s\n", rname);
492 exit(1);
493 }
494
495 // only top files and directories in user home are allowed
496 char *ptr = rname + strlen(cfg.homedir);
497 if (*ptr == '\0') {
498 fprintf(stderr, "Error: invalid file %s\n", name);
499 exit(1);
500 }
501 ptr++;
502 ptr = strchr(ptr, '/');
503 if (ptr) {
504 if (*ptr != '\0') {
505 fprintf(stderr, "Error: only top files and directories in user home are allowed\n");
506 exit(1); 379 exit(1);
507 } 380 }
508 } 381 }
509 382 else {
510 if (stat(fname, &s) == -1) { 383 // check the file is in user home directory, a full home directory is not allowed
511 fprintf(stderr, "Error: file %s not found.\n", fname); 384 char *rname = realpath(fname, NULL);
512 exit(1); 385 if (!rname ||
513 } 386 strncmp(rname, cfg.homedir, strlen(cfg.homedir)) != 0 ||
514 387 strcmp(rname, cfg.homedir) == 0) {
515 // check uid 388 fprintf(stderr, "Error: invalid file %s\n", name);
516 uid_t uid = getuid(); 389 exit(1);
517 gid_t gid = getgid(); 390 }
518 if (s.st_uid != uid || s.st_gid != gid) { 391
519 fprintf(stderr, "Error: only files or directories created by the current user are allowed.\n"); 392 // only top files and directories in user home are allowed
520 exit(1); 393 char *ptr = rname + strlen(cfg.homedir);
521 } 394 assert(*ptr != '\0');
522 395 ptr = strchr(++ptr, '/');
523 // dir or regular file 396 if (ptr) {
524 if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) { 397 if (*ptr != '\0') {
398 fprintf(stderr, "Error: only top files and directories in user home are allowed\n");
399 exit(1);
400 }
401 }
525 free(fname); 402 free(fname);
526 return rname; // regular exit from the function 403 return rname;
527 } 404 }
528
529 fprintf(stderr, "Error: invalid file type, %s.\n", fname);
530 exit(1);
531} 405}
532 406
407static void duplicate(char *name) {
408 char *fname = check_dir_or_file(name);
533 409
534// check directory list specified by user (--private-home option) - exit if it fails 410 if (arg_debug)
535void fs_check_home_list(void) { 411 printf("Private home: duplicating %s\n", fname);
536 if (strstr(cfg.home_private_keep, "..")) { 412 assert(strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0);
537 fprintf(stderr, "Error: invalid private-home list\n");
538 exit(1);
539 }
540
541 char *dlist = strdup(cfg.home_private_keep);
542 if (!dlist)
543 errExit("strdup");
544
545 char *ptr = strtok(dlist, ",");
546 char *tmp = check_dir_or_file(ptr);
547 free(tmp);
548 413
549 while ((ptr = strtok(NULL, ",")) != NULL) { 414 struct stat s;
550 tmp = check_dir_or_file(ptr); 415 if (lstat(fname, &s) == -1) {
551 free(tmp); 416 free(fname);
417 return;
418 }
419 else if (S_ISDIR(s.st_mode)) {
420 // create the directory in RUN_HOME_DIR
421 char *name;
422 char *ptr = strrchr(fname, '/');
423 ptr++;
424 if (asprintf(&name, "%s/%s", RUN_HOME_DIR, ptr) == -1)
425 errExit("asprintf");
426 mkdir_attr(name, 0755, getuid(), getgid());
427 sbox_run(SBOX_USER| SBOX_CAPS_NONE | SBOX_SECCOMP, 3, PATH_FCOPY, fname, name);
428 free(name);
552 } 429 }
430 else
431 sbox_run(SBOX_USER| SBOX_CAPS_NONE | SBOX_SECCOMP, 3, PATH_FCOPY, fname, RUN_HOME_DIR);
432 fs_logger2("clone", fname);
433 fs_logger_print(); // save the current log
553 434
554 free(dlist); 435 free(fname);
555} 436}
556 437
557
558
559// private mode (--private-home=list): 438// private mode (--private-home=list):
560// mount homedir on top of /home/user, 439// mount homedir on top of /home/user,
561// tmpfs on top of /root in nonroot mode, 440// tmpfs on top of /root in nonroot mode,
@@ -571,68 +450,28 @@ void fs_private_home_list(void) {
571 int xflag = store_xauthority(); 450 int xflag = store_xauthority();
572 int aflag = store_asoundrc(); 451 int aflag = store_asoundrc();
573 452
574 uid_t u = firejail_uid; 453 uid_t uid = getuid();
575 gid_t g = firejail_gid; 454 gid_t gid = getgid();
576 struct stat s;
577 if (stat(homedir, &s) == -1) {
578 fprintf(stderr, "Error: cannot find user home directory\n");
579 exit(1);
580 }
581 455
582 // create /run/firejail/mnt/home directory 456 // create /run/firejail/mnt/home directory
583 int rv = mkdir(RUN_HOME_DIR, 0755); 457 mkdir_attr(RUN_HOME_DIR, 0755, uid, gid);
584 if (rv == -1)
585 errExit("mkdir");
586 if (set_perms(RUN_HOME_DIR, u, g, 0755))
587 errExit("set_perms");
588 ASSERT_PERMS(RUN_HOME_DIR, u, g, 0755);
589
590 fs_logger_print(); // save the current log 458 fs_logger_print(); // save the current log
591 459
460 if (arg_debug)
461 printf("Copying files in the new home:\n");
462
592 // copy the list of files in the new home directory 463 // copy the list of files in the new home directory
593 // using a new child process without root privileges 464 char *dlist = strdup(cfg.home_private_keep);
594 pid_t child = fork(); 465 if (!dlist)
595 if (child < 0) 466 errExit("strdup");
596 errExit("fork"); 467
597 if (child == 0) { 468 char *ptr = strtok(dlist, ",");
598 if (arg_debug) 469 duplicate(ptr);
599 printf("Copying files in the new home:\n"); 470 while ((ptr = strtok(NULL, ",")) != NULL)
600
601 // drop privileges
602 if (setgroups(0, NULL) < 0)
603 errExit("setgroups");
604 if (setgid(getgid()) < 0)
605 errExit("setgid/getgid");
606 if (setuid(getuid()) < 0)
607 errExit("setuid/getuid");
608
609 // copy the list of files in the new home directory
610 char *dlist = strdup(cfg.home_private_keep);
611 if (!dlist)
612 errExit("strdup");
613
614 char *ptr = strtok(dlist, ",");
615 duplicate(ptr); 471 duplicate(ptr);
616 while ((ptr = strtok(NULL, ",")) != NULL)
617 duplicate(ptr);
618
619 if (!arg_quiet) {
620 if (size_limit_reached)
621 fprintf(stderr, "Warning: private-home copy limit of %u MB reached, not all the files were copied\n",
622 PRIVATE_COPY_LIMIT / (1024 *1024));
623 else
624 printf("Private home: %u files, total size %u bytes\n", file_cnt, size_cnt);
625 }
626 472
627 fs_logger_print(); // save the current log 473 fs_logger_print(); // save the current log
628 free(dlist); 474 free(dlist);
629#ifdef HAVE_GCOV
630 __gcov_flush();
631#endif
632 _exit(0);
633 }
634 // wait for the child to finish
635 waitpid(child, NULL, 0);
636 475
637 if (arg_debug) 476 if (arg_debug)
638 printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir); 477 printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir);
@@ -640,7 +479,7 @@ void fs_private_home_list(void) {
640 if (mount(RUN_HOME_DIR, homedir, NULL, MS_BIND|MS_REC, NULL) < 0) 479 if (mount(RUN_HOME_DIR, homedir, NULL, MS_BIND|MS_REC, NULL) < 0)
641 errExit("mount bind"); 480 errExit("mount bind");
642 481
643 if (u != 0) { 482 if (uid != 0) {
644 // mask /root 483 // mask /root
645 if (arg_debug) 484 if (arg_debug)
646 printf("Mounting a new /root directory\n"); 485 printf("Mounting a new /root directory\n");
@@ -655,7 +494,7 @@ void fs_private_home_list(void) {
655 errExit("mounting home directory"); 494 errExit("mounting home directory");
656 } 495 }
657 496
658 skel(homedir, u, g); 497 skel(homedir, uid, gid);
659 if (xflag) 498 if (xflag)
660 copy_xauthority(); 499 copy_xauthority();
661 if (aflag) 500 if (aflag)