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.c408
1 files changed, 317 insertions, 91 deletions
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}