aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/util.c
diff options
context:
space:
mode:
authorLibravatar Reiner Herrmann <reiner@reiner-h.de>2021-06-21 23:10:09 +0200
committerLibravatar Reiner Herrmann <reiner@reiner-h.de>2021-06-21 23:10:09 +0200
commit0f0325459e211ff31895ed7cbbbaae6c2c6ae9a2 (patch)
tree0875693a6ceef54818511972601d587a09a1aab4 /src/firejail/util.c
parentstyle: grammer and codestyle improvements (diff)
parentcreating alpine.profile (#4350) (diff)
downloadfirejail-0f0325459e211ff31895ed7cbbbaae6c2c6ae9a2.tar.gz
firejail-0f0325459e211ff31895ed7cbbbaae6c2c6ae9a2.tar.zst
firejail-0f0325459e211ff31895ed7cbbbaae6c2c6ae9a2.zip
Merge branch 'master' into kuesji/master
Diffstat (limited to 'src/firejail/util.c')
-rw-r--r--src/firejail/util.c258
1 files changed, 204 insertions, 54 deletions
diff --git a/src/firejail/util.c b/src/firejail/util.c
index e2a4a1d90..68b76b8e8 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -44,6 +44,10 @@
44#include <linux/openat2.h> 44#include <linux/openat2.h>
45#endif 45#endif
46 46
47#ifdef HAVE_GCOV
48#include <gcov.h>
49#endif
50
47#define MAX_GROUPS 1024 51#define MAX_GROUPS 1024
48#define MAXBUF 4098 52#define MAXBUF 4098
49#define EMPTY_STRING ("") 53#define EMPTY_STRING ("")
@@ -435,11 +439,11 @@ void touch_file_as_user(const char *fname, mode_t mode) {
435 // drop privileges 439 // drop privileges
436 drop_privs(0); 440 drop_privs(0);
437 441
438 FILE *fp = fopen(fname, "wx"); 442 int fd = open(fname, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWUSR);
439 if (fp) { 443 if (fd > -1) {
440 fprintf(fp, "\n"); 444 int err = fchmod(fd, mode);
441 SET_PERMS_STREAM(fp, -1, -1, mode); 445 (void) err;
442 fclose(fp); 446 close(fd);
443 } 447 }
444 else 448 else
445 fwarning("cannot create %s\n", fname); 449 fwarning("cannot create %s\n", fname);
@@ -458,6 +462,13 @@ int is_dir(const char *fname) {
458 if (*fname == '\0') 462 if (*fname == '\0')
459 return 0; 463 return 0;
460 464
465 int called_as_root = 0;
466 if (geteuid() == 0)
467 called_as_root = 1;
468
469 if (called_as_root)
470 EUID_USER();
471
461 // if fname doesn't end in '/', add one 472 // if fname doesn't end in '/', add one
462 int rv; 473 int rv;
463 struct stat s; 474 struct stat s;
@@ -473,6 +484,9 @@ int is_dir(const char *fname) {
473 free(tmp); 484 free(tmp);
474 } 485 }
475 486
487 if (called_as_root)
488 EUID_ROOT();
489
476 if (rv == -1) 490 if (rv == -1)
477 return 0; 491 return 0;
478 492
@@ -488,18 +502,83 @@ int is_link(const char *fname) {
488 if (*fname == '\0') 502 if (*fname == '\0')
489 return 0; 503 return 0;
490 504
491 char *dup = strdup(fname); 505 int called_as_root = 0;
492 if (!dup) 506 if (geteuid() == 0)
507 called_as_root = 1;
508
509 if (called_as_root)
510 EUID_USER();
511
512 // remove trailing '/' if any
513 char *tmp = strdup(fname);
514 if (!tmp)
493 errExit("strdup"); 515 errExit("strdup");
494 trim_trailing_slash_or_dot(dup); 516 trim_trailing_slash_or_dot(tmp);
495 517
496 char c; 518 char c;
497 ssize_t rv = readlink(dup, &c, 1); 519 ssize_t rv = readlink(tmp, &c, 1);
520 free(tmp);
521
522 if (called_as_root)
523 EUID_ROOT();
498 524
499 free(dup);
500 return (rv != -1); 525 return (rv != -1);
501} 526}
502 527
528char *realpath_as_user(const char *fname) {
529 assert(fname);
530
531 int called_as_root = 0;
532 if (geteuid() == 0)
533 called_as_root = 1;
534
535 if (called_as_root)
536 EUID_USER();
537
538 char *rv = realpath(fname, NULL);
539
540 if (called_as_root)
541 EUID_ROOT();
542
543 return rv;
544}
545
546int stat_as_user(const char *fname, struct stat *s) {
547 assert(fname);
548
549 int called_as_root = 0;
550 if (geteuid() == 0)
551 called_as_root = 1;
552
553 if (called_as_root)
554 EUID_USER();
555
556 int rv = stat(fname, s);
557
558 if (called_as_root)
559 EUID_ROOT();
560
561 return rv;
562}
563
564int lstat_as_user(const char *fname, struct stat *s) {
565 assert(fname);
566
567 int called_as_root = 0;
568 if (geteuid() == 0)
569 called_as_root = 1;
570
571 if (called_as_root)
572 EUID_USER();
573
574 int rv = lstat(fname, s);
575
576 if (called_as_root)
577 EUID_ROOT();
578
579 return rv;
580}
581
503// remove all slashes and single dots from the end of a path 582// remove all slashes and single dots from the end of a path
504// for example /foo/bar///././. -> /foo/bar 583// for example /foo/bar///././. -> /foo/bar
505void trim_trailing_slash_or_dot(char *path) { 584void trim_trailing_slash_or_dot(char *path) {
@@ -688,9 +767,11 @@ int find_child(pid_t parent, pid_t *child) {
688 if (parent == atoi(ptr)) { 767 if (parent == atoi(ptr)) {
689 // we don't want /usr/bin/xdg-dbus-proxy! 768 // we don't want /usr/bin/xdg-dbus-proxy!
690 char *cmdline = pid_proc_cmdline(pid); 769 char *cmdline = pid_proc_cmdline(pid);
691 if (strncmp(cmdline, XDG_DBUS_PROXY_PATH, strlen(XDG_DBUS_PROXY_PATH)) != 0) 770 if (cmdline) {
692 *child = pid; 771 if (strncmp(cmdline, XDG_DBUS_PROXY_PATH, strlen(XDG_DBUS_PROXY_PATH)) != 0)
693 free(cmdline); 772 *child = pid;
773 free(cmdline);
774 }
694 } 775 }
695 break; // stop reading the file 776 break; // stop reading the file
696 } 777 }
@@ -930,35 +1011,37 @@ static int remove_callback(const char *fpath, const struct stat *sb, int typefla
930 1011
931int remove_overlay_directory(void) { 1012int remove_overlay_directory(void) {
932 EUID_ASSERT(); 1013 EUID_ASSERT();
933 struct stat s;
934 sleep(1); 1014 sleep(1);
935 1015
936 char *path; 1016 char *path;
937 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1) 1017 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1)
938 errExit("asprintf"); 1018 errExit("asprintf");
939 1019
940 if (lstat(path, &s) == 0) { 1020 if (access(path, F_OK) == 0) {
941 // deal with obvious problems such as symlinks and root ownership
942 if (!S_ISDIR(s.st_mode)) {
943 if (S_ISLNK(s.st_mode))
944 fprintf(stderr, "Error: %s is a symbolic link\n", path);
945 else
946 fprintf(stderr, "Error: %s is not a directory\n", path);
947 exit(1);
948 }
949 if (s.st_uid != getuid()) {
950 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
951 exit(1);
952 }
953
954 pid_t child = fork(); 1021 pid_t child = fork();
955 if (child < 0) 1022 if (child < 0)
956 errExit("fork"); 1023 errExit("fork");
957 if (child == 0) { 1024 if (child == 0) {
958 // open ~/.firejail, fails if there is any symlink 1025 // open ~/.firejail
959 int fd = safer_openat(-1, path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 1026 int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
960 if (fd == -1) 1027 if (fd == -1) {
961 errExit("safer_openat"); 1028 fprintf(stderr, "Error: cannot open %s\n", path);
1029 exit(1);
1030 }
1031 struct stat s;
1032 if (fstat(fd, &s) == -1)
1033 errExit("fstat");
1034 if (!S_ISDIR(s.st_mode)) {
1035 if (S_ISLNK(s.st_mode))
1036 fprintf(stderr, "Error: %s is a symbolic link\n", path);
1037 else
1038 fprintf(stderr, "Error: %s is not a directory\n", path);
1039 exit(1);
1040 }
1041 if (s.st_uid != getuid()) {
1042 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
1043 exit(1);
1044 }
962 // chdir to ~/.firejail 1045 // chdir to ~/.firejail
963 if (fchdir(fd) == -1) 1046 if (fchdir(fd) == -1)
964 errExit("fchdir"); 1047 errExit("fchdir");
@@ -981,7 +1064,7 @@ int remove_overlay_directory(void) {
981 // wait for the child to finish 1064 // wait for the child to finish
982 waitpid(child, NULL, 0); 1065 waitpid(child, NULL, 0);
983 // check if ~/.firejail was deleted 1066 // check if ~/.firejail was deleted
984 if (stat(path, &s) == 0) 1067 if (access(path, F_OK) == 0)
985 return 1; 1068 return 1;
986 } 1069 }
987 return 0; 1070 return 0;
@@ -1014,9 +1097,8 @@ void flush_stdin(void) {
1014int create_empty_dir_as_user(const char *dir, mode_t mode) { 1097int create_empty_dir_as_user(const char *dir, mode_t mode) {
1015 assert(dir); 1098 assert(dir);
1016 mode &= 07777; 1099 mode &= 07777;
1017 struct stat s;
1018 1100
1019 if (stat(dir, &s)) { 1101 if (access(dir, F_OK) != 0) {
1020 if (arg_debug) 1102 if (arg_debug)
1021 printf("Creating empty %s directory\n", dir); 1103 printf("Creating empty %s directory\n", dir);
1022 pid_t child = fork(); 1104 pid_t child = fork();
@@ -1027,8 +1109,8 @@ int create_empty_dir_as_user(const char *dir, mode_t mode) {
1027 drop_privs(0); 1109 drop_privs(0);
1028 1110
1029 if (mkdir(dir, mode) == 0) { 1111 if (mkdir(dir, mode) == 0) {
1030 if (chmod(dir, mode) == -1) 1112 int err = chmod(dir, mode);
1031 {;} // do nothing 1113 (void) err;
1032 } 1114 }
1033 else if (arg_debug) 1115 else if (arg_debug)
1034 printf("Directory %s not created: %s\n", dir, strerror(errno)); 1116 printf("Directory %s not created: %s\n", dir, strerror(errno));
@@ -1038,7 +1120,7 @@ int create_empty_dir_as_user(const char *dir, mode_t mode) {
1038 _exit(0); 1120 _exit(0);
1039 } 1121 }
1040 waitpid(child, NULL, 0); 1122 waitpid(child, NULL, 0);
1041 if (stat(dir, &s) == 0) 1123 if (access(dir, F_OK) == 0)
1042 return 1; 1124 return 1;
1043 } 1125 }
1044 return 0; 1126 return 0;
@@ -1149,20 +1231,34 @@ unsigned extract_timeout(const char *str) {
1149} 1231}
1150 1232
1151void disable_file_or_dir(const char *fname) { 1233void disable_file_or_dir(const char *fname) {
1234 assert(fname);
1235
1236 EUID_USER();
1237 int fd = open(fname, O_PATH|O_CLOEXEC);
1238 EUID_ROOT();
1239 if (fd < 0)
1240 return;
1241
1152 struct stat s; 1242 struct stat s;
1153 if (stat(fname, &s) != -1) { 1243 if (fstat(fd, &s) < 0) { // FUSE
1154 if (arg_debug) 1244 if (errno != EACCES)
1155 printf("blacklist %s\n", fname); 1245 errExit("fstat");
1156 if (is_dir(fname)) { 1246 close(fd);
1157 if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 1247 return;
1158 errExit("disable directory");
1159 }
1160 else {
1161 if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0)
1162 errExit("disable file");
1163 }
1164 fs_logger2("blacklist", fname);
1165 } 1248 }
1249
1250 if (arg_debug)
1251 printf("blacklist %s\n", fname);
1252 if (S_ISDIR(s.st_mode)) {
1253 if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0)
1254 errExit("disable directory");
1255 }
1256 else {
1257 if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0)
1258 errExit("disable file");
1259 }
1260 close(fd);
1261 fs_logger2("blacklist", fname);
1166} 1262}
1167 1263
1168void disable_file_path(const char *path, const char *file) { 1264void disable_file_path(const char *path, const char *file) {
@@ -1249,6 +1345,60 @@ int safer_openat(int dirfd, const char *path, int flags) {
1249 return fd; 1345 return fd;
1250} 1346}
1251 1347
1348int remount_by_fd(int dst, unsigned long mountflags) {
1349 char *proc;
1350 if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0)
1351 errExit("asprintf");
1352
1353 int rv = mount(NULL, proc, NULL, mountflags|MS_BIND|MS_REMOUNT, NULL);
1354 if (rv < 0 && arg_debug)
1355 printf("Failed mount: %s\n", strerror(errno));
1356
1357 free(proc);
1358 return rv;
1359}
1360
1361int bind_mount_by_fd(int src, int dst) {
1362 char *proc_src, *proc_dst;
1363 if (asprintf(&proc_src, "/proc/self/fd/%d", src) < 0 ||
1364 asprintf(&proc_dst, "/proc/self/fd/%d", dst) < 0)
1365 errExit("asprintf");
1366
1367 int rv = mount(proc_src, proc_dst, NULL, MS_BIND|MS_REC, NULL);
1368 if (rv < 0 && arg_debug)
1369 printf("Failed mount: %s\n", strerror(errno));
1370
1371 free(proc_src);
1372 free(proc_dst);
1373 return rv;
1374}
1375
1376int bind_mount_fd_to_path(int src, const char *destname) {
1377 char *proc;
1378 if (asprintf(&proc, "/proc/self/fd/%d", src) < 0)
1379 errExit("asprintf");
1380
1381 int rv = mount(proc, destname, NULL, MS_BIND|MS_REC, NULL);
1382 if (rv < 0 && arg_debug)
1383 printf("Failed mount: %s\n", strerror(errno));
1384
1385 free(proc);
1386 return rv;
1387}
1388
1389int bind_mount_path_to_fd(const char *srcname, int dst) {
1390 char *proc;
1391 if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0)
1392 errExit("asprintf");
1393
1394 int rv = mount(srcname, proc, NULL, MS_BIND|MS_REC, NULL);
1395 if (rv < 0 && arg_debug)
1396 printf("Failed mount: %s\n", strerror(errno));
1397
1398 free(proc);
1399 return rv;
1400}
1401
1252int has_handler(pid_t pid, int signal) { 1402int has_handler(pid_t pid, int signal) {
1253 if (signal > 0 && signal <= SIGRTMAX) { 1403 if (signal > 0 && signal <= SIGRTMAX) {
1254 char *fname; 1404 char *fname;
@@ -1358,14 +1508,14 @@ static int has_link(const char *dir) {
1358 return 0; 1508 return 0;
1359} 1509}
1360 1510
1361void check_homedir(void) { 1511void check_homedir(const char *dir) {
1362 assert(cfg.homedir); 1512 assert(dir);
1363 if (cfg.homedir[0] != '/') { 1513 if (dir[0] != '/') {
1364 fprintf(stderr, "Error: invalid user directory \"%s\"\n", cfg.homedir); 1514 fprintf(stderr, "Error: invalid user directory \"%s\"\n", cfg.homedir);
1365 exit(1); 1515 exit(1);
1366 } 1516 }
1367 // symlinks are rejected in many places 1517 // symlinks are rejected in many places
1368 if (has_link(cfg.homedir)) { 1518 if (has_link(dir)) {
1369 fprintf(stderr, "No full support for symbolic links in path of user directory.\n" 1519 fprintf(stderr, "No full support for symbolic links in path of user directory.\n"
1370 "Please provide resolved path in password database (/etc/passwd).\n\n"); 1520 "Please provide resolved path in password database (/etc/passwd).\n\n");
1371 } 1521 }