diff options
Diffstat (limited to 'src/firejail/util.c')
-rw-r--r-- | src/firejail/util.c | 180 |
1 files changed, 46 insertions, 134 deletions
diff --git a/src/firejail/util.c b/src/firejail/util.c index 094a68c60..55dcdc246 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -20,8 +20,6 @@ | |||
20 | #define _XOPEN_SOURCE 500 | 20 | #define _XOPEN_SOURCE 500 |
21 | #include "firejail.h" | 21 | #include "firejail.h" |
22 | #include "../include/gcov_wrapper.h" | 22 | #include "../include/gcov_wrapper.h" |
23 | #include <ftw.h> | ||
24 | #include <sys/stat.h> | ||
25 | #include <sys/mount.h> | 23 | #include <sys/mount.h> |
26 | #include <syslog.h> | 24 | #include <syslog.h> |
27 | #include <errno.h> | 25 | #include <errno.h> |
@@ -32,9 +30,6 @@ | |||
32 | #include <sys/wait.h> | 30 | #include <sys/wait.h> |
33 | #include <limits.h> | 31 | #include <limits.h> |
34 | 32 | ||
35 | #include <string.h> | ||
36 | #include <ctype.h> | ||
37 | |||
38 | #include <fcntl.h> | 33 | #include <fcntl.h> |
39 | #ifndef O_PATH | 34 | #ifndef O_PATH |
40 | #define O_PATH 010000000 | 35 | #define O_PATH 010000000 |
@@ -459,31 +454,21 @@ int is_dir(const char *fname) { | |||
459 | if (*fname == '\0') | 454 | if (*fname == '\0') |
460 | return 0; | 455 | return 0; |
461 | 456 | ||
462 | int called_as_root = 0; | ||
463 | if (geteuid() == 0) | ||
464 | called_as_root = 1; | ||
465 | |||
466 | if (called_as_root) | ||
467 | EUID_USER(); | ||
468 | |||
469 | // if fname doesn't end in '/', add one | 457 | // if fname doesn't end in '/', add one |
470 | int rv; | 458 | int rv; |
471 | struct stat s; | 459 | struct stat s; |
472 | if (fname[strlen(fname) - 1] == '/') | 460 | if (fname[strlen(fname) - 1] == '/') |
473 | rv = stat(fname, &s); | 461 | rv = stat_as_user(fname, &s); |
474 | else { | 462 | else { |
475 | char *tmp; | 463 | char *tmp; |
476 | if (asprintf(&tmp, "%s/", fname) == -1) { | 464 | if (asprintf(&tmp, "%s/", fname) == -1) { |
477 | fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__); | 465 | fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__); |
478 | errExit("asprintf"); | 466 | errExit("asprintf"); |
479 | } | 467 | } |
480 | rv = stat(tmp, &s); | 468 | rv = stat_as_user(tmp, &s); |
481 | free(tmp); | 469 | free(tmp); |
482 | } | 470 | } |
483 | 471 | ||
484 | if (called_as_root) | ||
485 | EUID_ROOT(); | ||
486 | |||
487 | if (rv == -1) | 472 | if (rv == -1) |
488 | return 0; | 473 | return 0; |
489 | 474 | ||
@@ -499,13 +484,6 @@ int is_link(const char *fname) { | |||
499 | if (*fname == '\0') | 484 | if (*fname == '\0') |
500 | return 0; | 485 | return 0; |
501 | 486 | ||
502 | int called_as_root = 0; | ||
503 | if (geteuid() == 0) | ||
504 | called_as_root = 1; | ||
505 | |||
506 | if (called_as_root) | ||
507 | EUID_USER(); | ||
508 | |||
509 | // remove trailing '/' if any | 487 | // remove trailing '/' if any |
510 | char *tmp = strdup(fname); | 488 | char *tmp = strdup(fname); |
511 | if (!tmp) | 489 | if (!tmp) |
@@ -513,12 +491,9 @@ int is_link(const char *fname) { | |||
513 | trim_trailing_slash_or_dot(tmp); | 491 | trim_trailing_slash_or_dot(tmp); |
514 | 492 | ||
515 | char c; | 493 | char c; |
516 | ssize_t rv = readlink(tmp, &c, 1); | 494 | ssize_t rv = readlink_as_user(tmp, &c, 1); |
517 | free(tmp); | 495 | free(tmp); |
518 | 496 | ||
519 | if (called_as_root) | ||
520 | EUID_ROOT(); | ||
521 | |||
522 | return (rv != -1); | 497 | return (rv != -1); |
523 | } | 498 | } |
524 | 499 | ||
@@ -540,6 +515,24 @@ char *realpath_as_user(const char *fname) { | |||
540 | return rv; | 515 | return rv; |
541 | } | 516 | } |
542 | 517 | ||
518 | ssize_t readlink_as_user(const char *fname, char *buf, size_t sz) { | ||
519 | assert(fname && buf && sz); | ||
520 | |||
521 | int called_as_root = 0; | ||
522 | if (geteuid() == 0) | ||
523 | called_as_root = 1; | ||
524 | |||
525 | if (called_as_root) | ||
526 | EUID_USER(); | ||
527 | |||
528 | ssize_t rv = readlink(fname, buf, sz); | ||
529 | |||
530 | if (called_as_root) | ||
531 | EUID_ROOT(); | ||
532 | |||
533 | return rv; | ||
534 | } | ||
535 | |||
543 | int stat_as_user(const char *fname, struct stat *s) { | 536 | int stat_as_user(const char *fname, struct stat *s) { |
544 | assert(fname); | 537 | assert(fname); |
545 | 538 | ||
@@ -974,12 +967,9 @@ uid_t pid_get_uid(pid_t pid) { | |||
974 | } | 967 | } |
975 | 968 | ||
976 | 969 | ||
977 | 970 | gid_t get_group_id(const char *groupname) { | |
978 | |||
979 | uid_t get_group_id(const char *group) { | ||
980 | // find tty group id | ||
981 | gid_t gid = 0; | 971 | gid_t gid = 0; |
982 | struct group *g = getgrnam(group); | 972 | struct group *g = getgrnam(groupname); |
983 | if (g) | 973 | if (g) |
984 | gid = g->gr_gid; | 974 | gid = g->gr_gid; |
985 | 975 | ||
@@ -987,86 +977,6 @@ uid_t get_group_id(const char *group) { | |||
987 | } | 977 | } |
988 | 978 | ||
989 | 979 | ||
990 | static int remove_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { | ||
991 | (void) sb; | ||
992 | (void) typeflag; | ||
993 | (void) ftwbuf; | ||
994 | assert(fpath); | ||
995 | |||
996 | if (strcmp(fpath, ".") == 0) | ||
997 | return 0; | ||
998 | |||
999 | if (remove(fpath)) { // removes the link not the actual file | ||
1000 | perror("remove"); | ||
1001 | fprintf(stderr, "Error: cannot remove file from user .firejail directory: %s\n", fpath); | ||
1002 | exit(1); | ||
1003 | } | ||
1004 | |||
1005 | return 0; | ||
1006 | } | ||
1007 | |||
1008 | |||
1009 | int remove_overlay_directory(void) { | ||
1010 | EUID_ASSERT(); | ||
1011 | sleep(1); | ||
1012 | |||
1013 | char *path; | ||
1014 | if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1) | ||
1015 | errExit("asprintf"); | ||
1016 | |||
1017 | if (access(path, F_OK) == 0) { | ||
1018 | pid_t child = fork(); | ||
1019 | if (child < 0) | ||
1020 | errExit("fork"); | ||
1021 | if (child == 0) { | ||
1022 | // open ~/.firejail | ||
1023 | int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | ||
1024 | if (fd == -1) { | ||
1025 | fprintf(stderr, "Error: cannot open %s\n", path); | ||
1026 | exit(1); | ||
1027 | } | ||
1028 | struct stat s; | ||
1029 | if (fstat(fd, &s) == -1) | ||
1030 | errExit("fstat"); | ||
1031 | if (!S_ISDIR(s.st_mode)) { | ||
1032 | if (S_ISLNK(s.st_mode)) | ||
1033 | fprintf(stderr, "Error: %s is a symbolic link\n", path); | ||
1034 | else | ||
1035 | fprintf(stderr, "Error: %s is not a directory\n", path); | ||
1036 | exit(1); | ||
1037 | } | ||
1038 | if (s.st_uid != getuid()) { | ||
1039 | fprintf(stderr, "Error: %s is not owned by the current user\n", path); | ||
1040 | exit(1); | ||
1041 | } | ||
1042 | // chdir to ~/.firejail | ||
1043 | if (fchdir(fd) == -1) | ||
1044 | errExit("fchdir"); | ||
1045 | close(fd); | ||
1046 | |||
1047 | EUID_ROOT(); | ||
1048 | // FTW_PHYS - do not follow symbolic links | ||
1049 | if (nftw(".", remove_callback, 64, FTW_DEPTH | FTW_PHYS) == -1) | ||
1050 | errExit("nftw"); | ||
1051 | |||
1052 | EUID_USER(); | ||
1053 | // remove ~/.firejail | ||
1054 | if (rmdir(path) == -1) | ||
1055 | errExit("rmdir"); | ||
1056 | |||
1057 | __gcov_flush(); | ||
1058 | |||
1059 | _exit(0); | ||
1060 | } | ||
1061 | // wait for the child to finish | ||
1062 | waitpid(child, NULL, 0); | ||
1063 | // check if ~/.firejail was deleted | ||
1064 | if (access(path, F_OK) == 0) | ||
1065 | return 1; | ||
1066 | } | ||
1067 | return 0; | ||
1068 | } | ||
1069 | |||
1070 | // flush stdin if it is connected to a tty and has input | 980 | // flush stdin if it is connected to a tty and has input |
1071 | void flush_stdin(void) { | 981 | void flush_stdin(void) { |
1072 | if (!isatty(STDIN_FILENO)) | 982 | if (!isatty(STDIN_FILENO)) |
@@ -1095,31 +1005,33 @@ int create_empty_dir_as_user(const char *dir, mode_t mode) { | |||
1095 | assert(dir); | 1005 | assert(dir); |
1096 | mode &= 07777; | 1006 | mode &= 07777; |
1097 | 1007 | ||
1098 | if (access(dir, F_OK) != 0) { | 1008 | if (access(dir, F_OK) == 0) |
1009 | return 0; | ||
1010 | |||
1011 | pid_t child = fork(); | ||
1012 | if (child < 0) | ||
1013 | errExit("fork"); | ||
1014 | if (child == 0) { | ||
1015 | // drop privileges | ||
1016 | drop_privs(0); | ||
1017 | |||
1099 | if (arg_debug) | 1018 | if (arg_debug) |
1100 | printf("Creating empty %s directory\n", dir); | 1019 | printf("Creating empty %s directory\n", dir); |
1101 | pid_t child = fork(); | 1020 | if (mkdir(dir, mode) == 0) { |
1102 | if (child < 0) | 1021 | int err = chmod(dir, mode); |
1103 | errExit("fork"); | 1022 | (void) err; |
1104 | if (child == 0) { | 1023 | } |
1105 | // drop privileges | 1024 | else if (arg_debug) |
1106 | drop_privs(0); | 1025 | printf("Directory %s not created: %s\n", dir, strerror(errno)); |
1107 | |||
1108 | if (mkdir(dir, mode) == 0) { | ||
1109 | int err = chmod(dir, mode); | ||
1110 | (void) err; | ||
1111 | } | ||
1112 | else if (arg_debug) | ||
1113 | printf("Directory %s not created: %s\n", dir, strerror(errno)); | ||
1114 | 1026 | ||
1115 | __gcov_flush(); | 1027 | __gcov_flush(); |
1116 | 1028 | ||
1117 | _exit(0); | 1029 | _exit(0); |
1118 | } | ||
1119 | waitpid(child, NULL, 0); | ||
1120 | if (access(dir, F_OK) == 0) | ||
1121 | return 1; | ||
1122 | } | 1030 | } |
1031 | waitpid(child, NULL, 0); | ||
1032 | |||
1033 | if (access(dir, F_OK) == 0) | ||
1034 | return 1; | ||
1123 | return 0; | 1035 | return 0; |
1124 | } | 1036 | } |
1125 | 1037 | ||
@@ -1509,7 +1421,7 @@ static int has_link(const char *dir) { | |||
1509 | void check_homedir(const char *dir) { | 1421 | void check_homedir(const char *dir) { |
1510 | assert(dir); | 1422 | assert(dir); |
1511 | if (dir[0] != '/') { | 1423 | if (dir[0] != '/') { |
1512 | fprintf(stderr, "Error: invalid user directory \"%s\"\n", cfg.homedir); | 1424 | fprintf(stderr, "Error: invalid user directory \"%s\"\n", dir); |
1513 | exit(1); | 1425 | exit(1); |
1514 | } | 1426 | } |
1515 | // symlinks are rejected in many places | 1427 | // symlinks are rejected in many places |