diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/firejail.h | 9 | ||||
-rw-r--r-- | src/firejail/fs.c | 18 | ||||
-rw-r--r-- | src/firejail/pulseaudio.c | 62 | ||||
-rw-r--r-- | src/firejail/util.c | 9 | ||||
-rw-r--r-- | src/firejail/x11.c | 15 |
5 files changed, 57 insertions, 56 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 10133142a..c442a97bf 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -45,6 +45,15 @@ | |||
45 | assert(s.st_gid == gid);\ | 45 | assert(s.st_gid == gid);\ |
46 | assert((s.st_mode & 07777) == (mode));\ | 46 | assert((s.st_mode & 07777) == (mode));\ |
47 | } while (0) | 47 | } while (0) |
48 | #define ASSERT_PERMS_AS_USER(file, uid, gid, mode) \ | ||
49 | do { \ | ||
50 | assert(file);\ | ||
51 | struct stat s;\ | ||
52 | if (stat_as_user(file, &s) == -1) errExit("stat");\ | ||
53 | assert(s.st_uid == uid);\ | ||
54 | assert(s.st_gid == gid);\ | ||
55 | assert((s.st_mode & 07777) == (mode));\ | ||
56 | } while (0) | ||
48 | #define ASSERT_PERMS_FD(fd, uid, gid, mode) \ | 57 | #define ASSERT_PERMS_FD(fd, uid, gid, mode) \ |
49 | do { \ | 58 | do { \ |
50 | struct stat s;\ | 59 | struct stat s;\ |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 01182bd2c..bf78f8a17 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -77,7 +77,7 @@ static void disable_file(OPERATION op, const char *filename) { | |||
77 | 77 | ||
78 | EUID_ROOT(); | 78 | EUID_ROOT(); |
79 | int err = bind_mount_path_to_fd(RUN_RO_DIR, fd); | 79 | int err = bind_mount_path_to_fd(RUN_RO_DIR, fd); |
80 | if (err < 0) | 80 | if (err != 0) |
81 | err = bind_mount_path_to_fd(RUN_RO_FILE, fd); | 81 | err = bind_mount_path_to_fd(RUN_RO_FILE, fd); |
82 | EUID_USER(); | 82 | EUID_USER(); |
83 | close(fd); | 83 | close(fd); |
@@ -655,8 +655,13 @@ static void fs_remount_rec(const char *dir, OPERATION op) { | |||
655 | // resolve a path and remount it | 655 | // resolve a path and remount it |
656 | void fs_remount(const char *path, OPERATION op, int rec) { | 656 | void fs_remount(const char *path, OPERATION op, int rec) { |
657 | assert(path); | 657 | assert(path); |
658 | assert(geteuid() == 0); | 658 | |
659 | EUID_USER(); | 659 | int called_as_root = 0; |
660 | if (geteuid() == 0) | ||
661 | called_as_root = 1; | ||
662 | |||
663 | if (called_as_root) | ||
664 | EUID_USER(); | ||
660 | 665 | ||
661 | char *rpath = realpath(path, NULL); | 666 | char *rpath = realpath(path, NULL); |
662 | if (rpath) { | 667 | if (rpath) { |
@@ -666,7 +671,9 @@ void fs_remount(const char *path, OPERATION op, int rec) { | |||
666 | fs_remount_simple(rpath, op); | 671 | fs_remount_simple(rpath, op); |
667 | free(rpath); | 672 | free(rpath); |
668 | } | 673 | } |
669 | EUID_ROOT(); | 674 | |
675 | if (called_as_root) | ||
676 | EUID_ROOT(); | ||
670 | } | 677 | } |
671 | 678 | ||
672 | // Disable /mnt, /media, /run/mount and /run/media access | 679 | // Disable /mnt, /media, /run/mount and /run/media access |
@@ -821,7 +828,6 @@ void disable_config(void) { | |||
821 | 828 | ||
822 | 829 | ||
823 | // build a basic read-only filesystem | 830 | // build a basic read-only filesystem |
824 | // top level directories could be links, run no after-mount checks | ||
825 | void fs_basic_fs(void) { | 831 | void fs_basic_fs(void) { |
826 | uid_t uid = getuid(); | 832 | uid_t uid = getuid(); |
827 | 833 | ||
@@ -831,6 +837,7 @@ void fs_basic_fs(void) { | |||
831 | if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) | 837 | if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) |
832 | errExit("mounting /proc"); | 838 | errExit("mounting /proc"); |
833 | 839 | ||
840 | EUID_USER(); | ||
834 | if (arg_debug) | 841 | if (arg_debug) |
835 | printf("Basic read-only filesystem:\n"); | 842 | printf("Basic read-only filesystem:\n"); |
836 | if (!arg_writable_etc) { | 843 | if (!arg_writable_etc) { |
@@ -850,6 +857,7 @@ void fs_basic_fs(void) { | |||
850 | fs_remount("/lib64", MOUNT_READONLY, 1); | 857 | fs_remount("/lib64", MOUNT_READONLY, 1); |
851 | fs_remount("/lib32", MOUNT_READONLY, 1); | 858 | fs_remount("/lib32", MOUNT_READONLY, 1); |
852 | fs_remount("/libx32", MOUNT_READONLY, 1); | 859 | fs_remount("/libx32", MOUNT_READONLY, 1); |
860 | EUID_ROOT(); | ||
853 | 861 | ||
854 | // update /var directory in order to support multiple sandboxes running on the same root directory | 862 | // update /var directory in order to support multiple sandboxes running on the same root directory |
855 | fs_var_lock(); | 863 | fs_var_lock(); |
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c index be0f5aea6..f8d4c2f3c 100644 --- a/src/firejail/pulseaudio.c +++ b/src/firejail/pulseaudio.c | |||
@@ -75,31 +75,34 @@ void pulseaudio_disable(void) { | |||
75 | closedir(dir); | 75 | closedir(dir); |
76 | } | 76 | } |
77 | 77 | ||
78 | static void pulseaudio_fallback(const char *path) { | ||
79 | assert(path); | ||
80 | |||
81 | fmessage("Cannot mount tmpfs on %s/.config/pulse\n", cfg.homedir); | ||
82 | env_store_name_val("PULSE_CLIENTCONFIG", path, SETENV); | ||
83 | } | ||
84 | |||
85 | // disable shm in pulseaudio (issue #69) | 78 | // disable shm in pulseaudio (issue #69) |
86 | void pulseaudio_init(void) { | 79 | void pulseaudio_init(void) { |
87 | struct stat s; | ||
88 | |||
89 | // do we have pulseaudio in the system? | 80 | // do we have pulseaudio in the system? |
90 | if (stat(PULSE_CLIENT_SYSCONF, &s) == -1) { | 81 | if (access(PULSE_CLIENT_SYSCONF, R_OK)) { |
91 | if (arg_debug) | 82 | if (arg_debug) |
92 | printf("%s not found\n", PULSE_CLIENT_SYSCONF); | 83 | printf("Cannot read %s\n", PULSE_CLIENT_SYSCONF); |
93 | return; | 84 | return; |
94 | } | 85 | } |
95 | 86 | ||
87 | // create ~/.config/pulse directory if not present | ||
88 | char *homeusercfg = NULL; | ||
89 | if (asprintf(&homeusercfg, "%s/.config", cfg.homedir) == -1) | ||
90 | errExit("asprintf"); | ||
91 | if (create_empty_dir_as_user(homeusercfg, 0700)) | ||
92 | fs_logger2("create", homeusercfg); | ||
93 | |||
94 | free(homeusercfg); | ||
95 | if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1) | ||
96 | errExit("asprintf"); | ||
97 | if (create_empty_dir_as_user(homeusercfg, 0700)) | ||
98 | fs_logger2("create", homeusercfg); | ||
99 | |||
96 | // create the new user pulseaudio directory | 100 | // create the new user pulseaudio directory |
101 | // that will be mounted over ~/.config/pulse | ||
97 | if (mkdir(RUN_PULSE_DIR, 0700) == -1) | 102 | if (mkdir(RUN_PULSE_DIR, 0700) == -1) |
98 | errExit("mkdir"); | 103 | errExit("mkdir"); |
99 | selinux_relabel_path(RUN_PULSE_DIR, RUN_PULSE_DIR); | 104 | selinux_relabel_path(RUN_PULSE_DIR, homeusercfg); |
100 | // mount it nosuid, noexec, nodev | ||
101 | fs_remount(RUN_PULSE_DIR, MOUNT_NOEXEC, 0); | 105 | fs_remount(RUN_PULSE_DIR, MOUNT_NOEXEC, 0); |
102 | |||
103 | // create the new client.conf file | 106 | // create the new client.conf file |
104 | char *pulsecfg = NULL; | 107 | char *pulsecfg = NULL; |
105 | if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1) | 108 | if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1) |
@@ -116,37 +119,14 @@ void pulseaudio_init(void) { | |||
116 | if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700)) | 119 | if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700)) |
117 | errExit("set_perms"); | 120 | errExit("set_perms"); |
118 | 121 | ||
119 | // create ~/.config/pulse directory if not present | ||
120 | char *homeusercfg = NULL; | ||
121 | if (asprintf(&homeusercfg, "%s/.config", cfg.homedir) == -1) | ||
122 | errExit("asprintf"); | ||
123 | if (create_empty_dir_as_user(homeusercfg, 0700)) | ||
124 | fs_logger2("create", homeusercfg); | ||
125 | |||
126 | free(homeusercfg); | ||
127 | if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1) | ||
128 | errExit("asprintf"); | ||
129 | if (create_empty_dir_as_user(homeusercfg, 0700)) | ||
130 | fs_logger2("create", homeusercfg); | ||
131 | |||
132 | // if ~/.config/pulse exists and there are no symbolic links, mount the new directory | 122 | // if ~/.config/pulse exists and there are no symbolic links, mount the new directory |
133 | // else set environment variable | 123 | // else set environment variable |
124 | EUID_USER(); | ||
134 | int fd = safer_openat(-1, homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 125 | int fd = safer_openat(-1, homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
126 | EUID_ROOT(); | ||
135 | if (fd == -1) { | 127 | if (fd == -1) { |
136 | pulseaudio_fallback(pulsecfg); | 128 | fwarning("not mounting tmpfs on %s\n", homeusercfg); |
137 | goto out; | 129 | env_store_name_val("PULSE_CLIENTCONFIG", pulsecfg, SETENV); |
138 | } | ||
139 | // confirm the actual mount destination is owned by the user | ||
140 | if (fstat(fd, &s) == -1) { // FUSE | ||
141 | if (errno != EACCES) | ||
142 | errExit("fstat"); | ||
143 | close(fd); | ||
144 | pulseaudio_fallback(pulsecfg); | ||
145 | goto out; | ||
146 | } | ||
147 | if (s.st_uid != getuid()) { | ||
148 | close(fd); | ||
149 | pulseaudio_fallback(pulsecfg); | ||
150 | goto out; | 130 | goto out; |
151 | } | 131 | } |
152 | // preserve a read-only mount | 132 | // preserve a read-only mount |
diff --git a/src/firejail/util.c b/src/firejail/util.c index b8643ff60..edd08bb41 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -981,7 +981,7 @@ int remove_overlay_directory(void) { | |||
981 | int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 981 | int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
982 | if (fd == -1) { | 982 | if (fd == -1) { |
983 | fprintf(stderr, "Error: cannot open %s\n", path); | 983 | fprintf(stderr, "Error: cannot open %s\n", path); |
984 | _exit(1); | 984 | exit(1); |
985 | } | 985 | } |
986 | struct stat s; | 986 | struct stat s; |
987 | if (fstat(fd, &s) == -1) | 987 | if (fstat(fd, &s) == -1) |
@@ -991,11 +991,11 @@ int remove_overlay_directory(void) { | |||
991 | fprintf(stderr, "Error: %s is a symbolic link\n", path); | 991 | fprintf(stderr, "Error: %s is a symbolic link\n", path); |
992 | else | 992 | else |
993 | fprintf(stderr, "Error: %s is not a directory\n", path); | 993 | fprintf(stderr, "Error: %s is not a directory\n", path); |
994 | _exit(1); | 994 | exit(1); |
995 | } | 995 | } |
996 | if (s.st_uid != getuid()) { | 996 | if (s.st_uid != getuid()) { |
997 | fprintf(stderr, "Error: %s is not owned by the current user\n", path); | 997 | fprintf(stderr, "Error: %s is not owned by the current user\n", path); |
998 | _exit(1); | 998 | exit(1); |
999 | } | 999 | } |
1000 | // chdir to ~/.firejail | 1000 | // chdir to ~/.firejail |
1001 | if (fchdir(fd) == -1) | 1001 | if (fchdir(fd) == -1) |
@@ -1187,7 +1187,6 @@ unsigned extract_timeout(const char *str) { | |||
1187 | 1187 | ||
1188 | void disable_file_or_dir(const char *fname) { | 1188 | void disable_file_or_dir(const char *fname) { |
1189 | assert(fname); | 1189 | assert(fname); |
1190 | assert(geteuid() == 0); | ||
1191 | 1190 | ||
1192 | EUID_USER(); | 1191 | EUID_USER(); |
1193 | int fd = open(fname, O_PATH|O_CLOEXEC); | 1192 | int fd = open(fname, O_PATH|O_CLOEXEC); |
@@ -1207,7 +1206,7 @@ void disable_file_or_dir(const char *fname) { | |||
1207 | printf("blacklist %s\n", fname); | 1206 | printf("blacklist %s\n", fname); |
1208 | if (S_ISDIR(s.st_mode)) { | 1207 | if (S_ISDIR(s.st_mode)) { |
1209 | if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0) | 1208 | if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0) |
1210 | errExit("disable directory"); | 1209 | errExit("disable directory"); |
1211 | } | 1210 | } |
1212 | else { | 1211 | else { |
1213 | if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0) | 1212 | if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0) |
diff --git a/src/firejail/x11.c b/src/firejail/x11.c index 09956b903..0619ff380 100644 --- a/src/firejail/x11.c +++ b/src/firejail/x11.c | |||
@@ -1204,14 +1204,13 @@ void x11_xorg(void) { | |||
1204 | fmessage("Generating a new .Xauthority file\n"); | 1204 | fmessage("Generating a new .Xauthority file\n"); |
1205 | mkdir_attr(RUN_XAUTHORITY_SEC_DIR, 0700, getuid(), getgid()); | 1205 | mkdir_attr(RUN_XAUTHORITY_SEC_DIR, 0700, getuid(), getgid()); |
1206 | // create new Xauthority file in RUN_XAUTHORITY_SEC_DIR | 1206 | // create new Xauthority file in RUN_XAUTHORITY_SEC_DIR |
1207 | EUID_USER(); | ||
1207 | char tmpfname[] = RUN_XAUTHORITY_SEC_DIR "/.Xauth-XXXXXX"; | 1208 | char tmpfname[] = RUN_XAUTHORITY_SEC_DIR "/.Xauth-XXXXXX"; |
1208 | int fd = mkstemp(tmpfname); | 1209 | int fd = mkstemp(tmpfname); |
1209 | if (fd == -1) { | 1210 | if (fd == -1) { |
1210 | fprintf(stderr, "Error: cannot create .Xauthority file\n"); | 1211 | fprintf(stderr, "Error: cannot create .Xauthority file\n"); |
1211 | exit(1); | 1212 | exit(1); |
1212 | } | 1213 | } |
1213 | if (fchown(fd, getuid(), getgid()) == -1) | ||
1214 | errExit("chown"); | ||
1215 | close(fd); | 1214 | close(fd); |
1216 | 1215 | ||
1217 | // run xauth | 1216 | // run xauth |
@@ -1221,8 +1220,6 @@ void x11_xorg(void) { | |||
1221 | else | 1220 | else |
1222 | sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7, RUN_XAUTH_FILE, "-f", tmpfname, | 1221 | sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7, RUN_XAUTH_FILE, "-f", tmpfname, |
1223 | "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted"); | 1222 | "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted"); |
1224 | // remove xauth copy | ||
1225 | unlink(RUN_XAUTH_FILE); | ||
1226 | 1223 | ||
1227 | // ensure there is already a file ~/.Xauthority, so that bind-mount below will work. | 1224 | // ensure there is already a file ~/.Xauthority, so that bind-mount below will work. |
1228 | char *dest; | 1225 | char *dest; |
@@ -1273,10 +1270,12 @@ void x11_xorg(void) { | |||
1273 | // mount via the link in /proc/self/fd | 1270 | // mount via the link in /proc/self/fd |
1274 | if (arg_debug) | 1271 | if (arg_debug) |
1275 | printf("Mounting %s on %s\n", tmpfname, dest); | 1272 | printf("Mounting %s on %s\n", tmpfname, dest); |
1273 | EUID_ROOT(); | ||
1276 | if (bind_mount_by_fd(src, dst)) { | 1274 | if (bind_mount_by_fd(src, dst)) { |
1277 | fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); | 1275 | fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); |
1278 | exit(1); | 1276 | exit(1); |
1279 | } | 1277 | } |
1278 | EUID_USER(); | ||
1280 | // check /proc/self/mountinfo to confirm the mount is ok | 1279 | // check /proc/self/mountinfo to confirm the mount is ok |
1281 | MountData *mptr = get_last_mount(); | 1280 | MountData *mptr = get_last_mount(); |
1282 | if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) | 1281 | if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) |
@@ -1289,9 +1288,10 @@ void x11_xorg(void) { | |||
1289 | // blacklist user .Xauthority file if it is not masked already | 1288 | // blacklist user .Xauthority file if it is not masked already |
1290 | const char *envar = env_get("XAUTHORITY"); | 1289 | const char *envar = env_get("XAUTHORITY"); |
1291 | if (envar) { | 1290 | if (envar) { |
1292 | char *rp = realpath_as_user(envar); | 1291 | char *rp = realpath(envar, NULL); |
1293 | if (rp) { | 1292 | if (rp) { |
1294 | if (strcmp(rp, dest) != 0) | 1293 | if (strcmp(rp, dest) != 0) |
1294 | // disable_file_or_dir returns with EUID 0 | ||
1295 | disable_file_or_dir(rp); | 1295 | disable_file_or_dir(rp); |
1296 | free(rp); | 1296 | free(rp); |
1297 | } | 1297 | } |
@@ -1301,9 +1301,13 @@ void x11_xorg(void) { | |||
1301 | free(dest); | 1301 | free(dest); |
1302 | 1302 | ||
1303 | // mask RUN_XAUTHORITY_SEC_DIR | 1303 | // mask RUN_XAUTHORITY_SEC_DIR |
1304 | EUID_ROOT(); | ||
1304 | if (mount("tmpfs", RUN_XAUTHORITY_SEC_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) | 1305 | if (mount("tmpfs", RUN_XAUTHORITY_SEC_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) |
1305 | errExit("mounting tmpfs"); | 1306 | errExit("mounting tmpfs"); |
1306 | fs_logger2("tmpfs", RUN_XAUTHORITY_SEC_DIR); | 1307 | fs_logger2("tmpfs", RUN_XAUTHORITY_SEC_DIR); |
1308 | |||
1309 | // cleanup | ||
1310 | unlink(RUN_XAUTH_FILE); | ||
1307 | #endif | 1311 | #endif |
1308 | } | 1312 | } |
1309 | 1313 | ||
@@ -1352,6 +1356,7 @@ void fs_x11(void) { | |||
1352 | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME, | 1356 | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME, |
1353 | "mode=1777,uid=0,gid=0") < 0) | 1357 | "mode=1777,uid=0,gid=0") < 0) |
1354 | errExit("mounting tmpfs on /tmp/.X11-unix"); | 1358 | errExit("mounting tmpfs on /tmp/.X11-unix"); |
1359 | selinux_relabel_path("/tmp/.X11-unix", "/tmp/.X11-unix"); | ||
1355 | fs_logger("tmpfs /tmp/.X11-unix"); | 1360 | fs_logger("tmpfs /tmp/.X11-unix"); |
1356 | 1361 | ||
1357 | // create an empty root-owned file which will have the desired socket bind-mounted over it | 1362 | // create an empty root-owned file which will have the desired socket bind-mounted over it |