aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/firejail/util.c')
-rw-r--r--src/firejail/util.c227
1 files changed, 186 insertions, 41 deletions
diff --git a/src/firejail/util.c b/src/firejail/util.c
index dc9fe2449..b8643ff60 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -394,11 +394,11 @@ void touch_file_as_user(const char *fname, mode_t mode) {
394 // drop privileges 394 // drop privileges
395 drop_privs(0); 395 drop_privs(0);
396 396
397 FILE *fp = fopen(fname, "wx"); 397 int fd = open(fname, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWUSR);
398 if (fp) { 398 if (fd > -1) {
399 fprintf(fp, "\n"); 399 int err = fchmod(fd, mode);
400 SET_PERMS_STREAM(fp, -1, -1, mode); 400 (void) err;
401 fclose(fp); 401 close(fd);
402 } 402 }
403 else 403 else
404 fwarning("cannot create %s\n", fname); 404 fwarning("cannot create %s\n", fname);
@@ -417,6 +417,13 @@ int is_dir(const char *fname) {
417 if (*fname == '\0') 417 if (*fname == '\0')
418 return 0; 418 return 0;
419 419
420 int called_as_root = 0;
421 if (geteuid() == 0)
422 called_as_root = 1;
423
424 if (called_as_root)
425 EUID_USER();
426
420 // if fname doesn't end in '/', add one 427 // if fname doesn't end in '/', add one
421 int rv; 428 int rv;
422 struct stat s; 429 struct stat s;
@@ -432,6 +439,9 @@ int is_dir(const char *fname) {
432 free(tmp); 439 free(tmp);
433 } 440 }
434 441
442 if (called_as_root)
443 EUID_ROOT();
444
435 if (rv == -1) 445 if (rv == -1)
436 return 0; 446 return 0;
437 447
@@ -447,6 +457,14 @@ int is_link(const char *fname) {
447 if (*fname == '\0') 457 if (*fname == '\0')
448 return 0; 458 return 0;
449 459
460 int called_as_root = 0;
461 if (geteuid() == 0)
462 called_as_root = 1;
463
464 if (called_as_root)
465 EUID_USER();
466
467 // remove trailing '/' if any
450 char *tmp = strdup(fname); 468 char *tmp = strdup(fname);
451 if (!tmp) 469 if (!tmp)
452 errExit("strdup"); 470 errExit("strdup");
@@ -456,9 +474,66 @@ int is_link(const char *fname) {
456 ssize_t rv = readlink(tmp, &c, 1); 474 ssize_t rv = readlink(tmp, &c, 1);
457 free(tmp); 475 free(tmp);
458 476
477 if (called_as_root)
478 EUID_ROOT();
479
459 return (rv != -1); 480 return (rv != -1);
460} 481}
461 482
483char *realpath_as_user(const char *fname) {
484 assert(fname);
485
486 int called_as_root = 0;
487 if (geteuid() == 0)
488 called_as_root = 1;
489
490 if (called_as_root)
491 EUID_USER();
492
493 char *rv = realpath(fname, NULL);
494
495 if (called_as_root)
496 EUID_ROOT();
497
498 return rv;
499}
500
501int stat_as_user(const char *fname, struct stat *s) {
502 assert(fname);
503
504 int called_as_root = 0;
505 if (geteuid() == 0)
506 called_as_root = 1;
507
508 if (called_as_root)
509 EUID_USER();
510
511 int rv = stat(fname, s);
512
513 if (called_as_root)
514 EUID_ROOT();
515
516 return rv;
517}
518
519int lstat_as_user(const char *fname, struct stat *s) {
520 assert(fname);
521
522 int called_as_root = 0;
523 if (geteuid() == 0)
524 called_as_root = 1;
525
526 if (called_as_root)
527 EUID_USER();
528
529 int rv = lstat(fname, s);
530
531 if (called_as_root)
532 EUID_ROOT();
533
534 return rv;
535}
536
462// remove all slashes and single dots from the end of a path 537// remove all slashes and single dots from the end of a path
463// for example /foo/bar///././. -> /foo/bar 538// for example /foo/bar///././. -> /foo/bar
464void trim_trailing_slash_or_dot(char *path) { 539void trim_trailing_slash_or_dot(char *path) {
@@ -891,35 +966,37 @@ static int remove_callback(const char *fpath, const struct stat *sb, int typefla
891 966
892int remove_overlay_directory(void) { 967int remove_overlay_directory(void) {
893 EUID_ASSERT(); 968 EUID_ASSERT();
894 struct stat s;
895 sleep(1); 969 sleep(1);
896 970
897 char *path; 971 char *path;
898 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1) 972 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1)
899 errExit("asprintf"); 973 errExit("asprintf");
900 974
901 if (lstat(path, &s) == 0) { 975 if (access(path, F_OK) == 0) {
902 // deal with obvious problems such as symlinks and root ownership
903 if (!S_ISDIR(s.st_mode)) {
904 if (S_ISLNK(s.st_mode))
905 fprintf(stderr, "Error: %s is a symbolic link\n", path);
906 else
907 fprintf(stderr, "Error: %s is not a directory\n", path);
908 exit(1);
909 }
910 if (s.st_uid != getuid()) {
911 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
912 exit(1);
913 }
914
915 pid_t child = fork(); 976 pid_t child = fork();
916 if (child < 0) 977 if (child < 0)
917 errExit("fork"); 978 errExit("fork");
918 if (child == 0) { 979 if (child == 0) {
919 // open ~/.firejail, fails if there is any symlink 980 // open ~/.firejail
920 int fd = safer_openat(-1, path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 981 int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
921 if (fd == -1) 982 if (fd == -1) {
922 errExit("safer_openat"); 983 fprintf(stderr, "Error: cannot open %s\n", path);
984 _exit(1);
985 }
986 struct stat s;
987 if (fstat(fd, &s) == -1)
988 errExit("fstat");
989 if (!S_ISDIR(s.st_mode)) {
990 if (S_ISLNK(s.st_mode))
991 fprintf(stderr, "Error: %s is a symbolic link\n", path);
992 else
993 fprintf(stderr, "Error: %s is not a directory\n", path);
994 _exit(1);
995 }
996 if (s.st_uid != getuid()) {
997 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
998 _exit(1);
999 }
923 // chdir to ~/.firejail 1000 // chdir to ~/.firejail
924 if (fchdir(fd) == -1) 1001 if (fchdir(fd) == -1)
925 errExit("fchdir"); 1002 errExit("fchdir");
@@ -942,7 +1019,7 @@ int remove_overlay_directory(void) {
942 // wait for the child to finish 1019 // wait for the child to finish
943 waitpid(child, NULL, 0); 1020 waitpid(child, NULL, 0);
944 // check if ~/.firejail was deleted 1021 // check if ~/.firejail was deleted
945 if (stat(path, &s) == 0) 1022 if (access(path, F_OK) == 0)
946 return 1; 1023 return 1;
947 } 1024 }
948 return 0; 1025 return 0;
@@ -975,9 +1052,8 @@ void flush_stdin(void) {
975int create_empty_dir_as_user(const char *dir, mode_t mode) { 1052int create_empty_dir_as_user(const char *dir, mode_t mode) {
976 assert(dir); 1053 assert(dir);
977 mode &= 07777; 1054 mode &= 07777;
978 struct stat s;
979 1055
980 if (stat(dir, &s)) { 1056 if (access(dir, F_OK) != 0) {
981 if (arg_debug) 1057 if (arg_debug)
982 printf("Creating empty %s directory\n", dir); 1058 printf("Creating empty %s directory\n", dir);
983 pid_t child = fork(); 1059 pid_t child = fork();
@@ -988,8 +1064,8 @@ int create_empty_dir_as_user(const char *dir, mode_t mode) {
988 drop_privs(0); 1064 drop_privs(0);
989 1065
990 if (mkdir(dir, mode) == 0) { 1066 if (mkdir(dir, mode) == 0) {
991 if (chmod(dir, mode) == -1) 1067 int err = chmod(dir, mode);
992 {;} // do nothing 1068 (void) err;
993 } 1069 }
994 else if (arg_debug) 1070 else if (arg_debug)
995 printf("Directory %s not created: %s\n", dir, strerror(errno)); 1071 printf("Directory %s not created: %s\n", dir, strerror(errno));
@@ -999,7 +1075,7 @@ int create_empty_dir_as_user(const char *dir, mode_t mode) {
999 _exit(0); 1075 _exit(0);
1000 } 1076 }
1001 waitpid(child, NULL, 0); 1077 waitpid(child, NULL, 0);
1002 if (stat(dir, &s) == 0) 1078 if (access(dir, F_OK) == 0)
1003 return 1; 1079 return 1;
1004 } 1080 }
1005 return 0; 1081 return 0;
@@ -1110,20 +1186,35 @@ unsigned extract_timeout(const char *str) {
1110} 1186}
1111 1187
1112void disable_file_or_dir(const char *fname) { 1188void disable_file_or_dir(const char *fname) {
1189 assert(fname);
1190 assert(geteuid() == 0);
1191
1192 EUID_USER();
1193 int fd = open(fname, O_PATH|O_CLOEXEC);
1194 EUID_ROOT();
1195 if (fd < 0)
1196 return;
1197
1113 struct stat s; 1198 struct stat s;
1114 if (stat(fname, &s) != -1) { 1199 if (fstat(fd, &s) < 0) { // FUSE
1115 if (arg_debug) 1200 if (errno != EACCES)
1116 printf("blacklist %s\n", fname); 1201 errExit("fstat");
1117 if (is_dir(fname)) { 1202 close(fd);
1118 if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 1203 return;
1204 }
1205
1206 if (arg_debug)
1207 printf("blacklist %s\n", fname);
1208 if (S_ISDIR(s.st_mode)) {
1209 if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0)
1119 errExit("disable directory"); 1210 errExit("disable directory");
1120 }
1121 else {
1122 if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0)
1123 errExit("disable file");
1124 }
1125 fs_logger2("blacklist", fname);
1126 } 1211 }
1212 else {
1213 if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0)
1214 errExit("disable file");
1215 }
1216 close(fd);
1217 fs_logger2("blacklist", fname);
1127} 1218}
1128 1219
1129void disable_file_path(const char *path, const char *file) { 1220void disable_file_path(const char *path, const char *file) {
@@ -1210,6 +1301,60 @@ int safer_openat(int dirfd, const char *path, int flags) {
1210 return fd; 1301 return fd;
1211} 1302}
1212 1303
1304int remount_by_fd(int dst, unsigned long mountflags) {
1305 char *proc;
1306 if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0)
1307 errExit("asprintf");
1308
1309 int rv = mount(NULL, proc, NULL, mountflags|MS_BIND|MS_REMOUNT, NULL);
1310 if (rv < 0 && arg_debug)
1311 printf("Failed mount: %s\n", strerror(errno));
1312
1313 free(proc);
1314 return rv;
1315}
1316
1317int bind_mount_by_fd(int src, int dst) {
1318 char *proc_src, *proc_dst;
1319 if (asprintf(&proc_src, "/proc/self/fd/%d", src) < 0 ||
1320 asprintf(&proc_dst, "/proc/self/fd/%d", dst) < 0)
1321 errExit("asprintf");
1322
1323 int rv = mount(proc_src, proc_dst, NULL, MS_BIND|MS_REC, NULL);
1324 if (rv < 0 && arg_debug)
1325 printf("Failed mount: %s\n", strerror(errno));
1326
1327 free(proc_src);
1328 free(proc_dst);
1329 return rv;
1330}
1331
1332int bind_mount_fd_to_path(int src, const char *destname) {
1333 char *proc;
1334 if (asprintf(&proc, "/proc/self/fd/%d", src) < 0)
1335 errExit("asprintf");
1336
1337 int rv = mount(proc, destname, NULL, MS_BIND|MS_REC, NULL);
1338 if (rv < 0 && arg_debug)
1339 printf("Failed mount: %s\n", strerror(errno));
1340
1341 free(proc);
1342 return rv;
1343}
1344
1345int bind_mount_path_to_fd(const char *srcname, int dst) {
1346 char *proc;
1347 if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0)
1348 errExit("asprintf");
1349
1350 int rv = mount(srcname, proc, NULL, MS_BIND|MS_REC, NULL);
1351 if (rv < 0 && arg_debug)
1352 printf("Failed mount: %s\n", strerror(errno));
1353
1354 free(proc);
1355 return rv;
1356}
1357
1213int has_handler(pid_t pid, int signal) { 1358int has_handler(pid_t pid, int signal) {
1214 if (signal > 0 && signal <= SIGRTMAX) { 1359 if (signal > 0 && signal <= SIGRTMAX) {
1215 char *fname; 1360 char *fname;