diff options
-rw-r--r-- | src/firejail/pulseaudio.c | 77 | ||||
-rw-r--r-- | src/firejail/x11.c | 46 |
2 files changed, 83 insertions, 40 deletions
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c index 15d44e4cc..e43307d70 100644 --- a/src/firejail/pulseaudio.c +++ b/src/firejail/pulseaudio.c | |||
@@ -24,6 +24,13 @@ | |||
24 | #include <dirent.h> | 24 | #include <dirent.h> |
25 | #include <sys/wait.h> | 25 | #include <sys/wait.h> |
26 | 26 | ||
27 | // on Debian 7 we are missing O_PATH definition | ||
28 | #include <fcntl.h> | ||
29 | #ifndef O_PATH | ||
30 | #define O_PATH 010000000 | ||
31 | #endif | ||
32 | |||
33 | |||
27 | // disable pulseaudio socket | 34 | // disable pulseaudio socket |
28 | void pulseaudio_disable(void) { | 35 | void pulseaudio_disable(void) { |
29 | if (arg_debug) | 36 | if (arg_debug) |
@@ -72,14 +79,19 @@ void pulseaudio_init(void) { | |||
72 | struct stat s; | 79 | struct stat s; |
73 | 80 | ||
74 | // do we have pulseaudio in the system? | 81 | // do we have pulseaudio in the system? |
75 | if (stat("/etc/pulse/client.conf", &s) == -1) | 82 | if (stat("/etc/pulse/client.conf", &s) == -1) { |
83 | if (arg_debug) | ||
84 | printf("/etc/pulse/client.conf not found\n"); | ||
76 | return; | 85 | return; |
86 | } | ||
77 | 87 | ||
78 | // create the new user pulseaudio directory | 88 | // create the new user pulseaudio directory |
79 | int rv = mkdir(RUN_PULSE_DIR, 0700); | 89 | if (mkdir(RUN_PULSE_DIR, 0700) == -1) |
80 | (void) rv; // in --chroot mode the directory can already be there | 90 | errExit("mkdir"); |
81 | if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700)) | 91 | // make it a mount point and add mount flags |
82 | errExit("set_perms"); | 92 | if (mount(RUN_PULSE_DIR, RUN_PULSE_DIR, NULL, MS_BIND, NULL) < 0 || |
93 | mount(NULL, RUN_PULSE_DIR, NULL, MS_NOEXEC|MS_NODEV|MS_NOSUID|MS_BIND|MS_REMOUNT, NULL) < 0) | ||
94 | errExit("mount RUN_PULSE_DIR"); | ||
83 | 95 | ||
84 | // create the new client.conf file | 96 | // create the new client.conf file |
85 | char *pulsecfg = NULL; | 97 | char *pulsecfg = NULL; |
@@ -93,12 +105,15 @@ void pulseaudio_init(void) { | |||
93 | fprintf(fp, "%s", "\nenable-shm = no\n"); | 105 | fprintf(fp, "%s", "\nenable-shm = no\n"); |
94 | SET_PERMS_STREAM(fp, getuid(), getgid(), 0644); | 106 | SET_PERMS_STREAM(fp, getuid(), getgid(), 0644); |
95 | fclose(fp); | 107 | fclose(fp); |
108 | // hand over the directory to the user | ||
109 | if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700)) | ||
110 | errExit("set_perms"); | ||
96 | 111 | ||
97 | // create ~/.config/pulse directory if not present | 112 | // create ~/.config/pulse directory if not present |
98 | char *dir1; | 113 | char *dir1; |
99 | if (asprintf(&dir1, "%s/.config", cfg.homedir) == -1) | 114 | if (asprintf(&dir1, "%s/.config", cfg.homedir) == -1) |
100 | errExit("asprintf"); | 115 | errExit("asprintf"); |
101 | if (stat(dir1, &s) == -1) { | 116 | if (lstat(dir1, &s) == -1) { |
102 | pid_t child = fork(); | 117 | pid_t child = fork(); |
103 | if (child < 0) | 118 | if (child < 0) |
104 | errExit("fork"); | 119 | errExit("fork"); |
@@ -121,9 +136,12 @@ void pulseaudio_init(void) { | |||
121 | fs_logger2("create", dir1); | 136 | fs_logger2("create", dir1); |
122 | } | 137 | } |
123 | else { | 138 | else { |
124 | // make sure the directory is owned by the user | 139 | // we expect a user owned directory |
125 | if (s.st_uid != getuid()) { | 140 | if (!S_ISDIR(s.st_mode) || s.st_uid != getuid()) { |
126 | fprintf(stderr, "Error: user .config directory is not owned by the current user\n"); | 141 | if (S_ISLNK(s.st_mode)) |
142 | fprintf(stderr, "Error: user .config is a symbolic link\n"); | ||
143 | else | ||
144 | fprintf(stderr, "Error: user .config is not a directory owned by the current user\n"); | ||
127 | exit(1); | 145 | exit(1); |
128 | } | 146 | } |
129 | } | 147 | } |
@@ -131,7 +149,7 @@ void pulseaudio_init(void) { | |||
131 | 149 | ||
132 | if (asprintf(&dir1, "%s/.config/pulse", cfg.homedir) == -1) | 150 | if (asprintf(&dir1, "%s/.config/pulse", cfg.homedir) == -1) |
133 | errExit("asprintf"); | 151 | errExit("asprintf"); |
134 | if (stat(dir1, &s) == -1) { | 152 | if (lstat(dir1, &s) == -1) { |
135 | pid_t child = fork(); | 153 | pid_t child = fork(); |
136 | if (child < 0) | 154 | if (child < 0) |
137 | errExit("fork"); | 155 | errExit("fork"); |
@@ -154,33 +172,42 @@ void pulseaudio_init(void) { | |||
154 | fs_logger2("create", dir1); | 172 | fs_logger2("create", dir1); |
155 | } | 173 | } |
156 | else { | 174 | else { |
157 | // make sure the directory is owned by the user | 175 | // we expect a user owned directory |
158 | if (s.st_uid != getuid()) { | 176 | if (!S_ISDIR(s.st_mode) || s.st_uid != getuid()) { |
159 | fprintf(stderr, "Error: user .config/pulse directory is not owned by the current user\n"); | 177 | if (S_ISLNK(s.st_mode)) |
178 | fprintf(stderr, "Error: user .config/pulse is a symbolic link\n"); | ||
179 | else | ||
180 | fprintf(stderr, "Error: user .config/pulse is not a directory owned by the current user\n"); | ||
160 | exit(1); | 181 | exit(1); |
161 | } | 182 | } |
162 | } | 183 | } |
163 | free(dir1); | 184 | free(dir1); |
164 | 185 | ||
165 | // if we have ~/.config/pulse mount the new directory, else set environment variable | 186 | // if we have ~/.config/pulse mount the new directory, else set environment variable. |
166 | char *homeusercfg; | 187 | char *homeusercfg; |
167 | if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1) | 188 | if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1) |
168 | errExit("asprintf"); | 189 | errExit("asprintf"); |
169 | if (stat(homeusercfg, &s) == 0) { | 190 | if (stat(homeusercfg, &s) == 0) { |
170 | if (is_link(homeusercfg)) { | 191 | // get a file descriptor for ~/.config/pulse, fails if there is any symlink |
171 | fprintf(stderr, "Error: user .config/pulse is a symbolic link\n"); | 192 | int fd = safe_fd(homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
172 | exit(1); | 193 | if (fd == -1) |
173 | } | 194 | errExit("safe_fd"); |
174 | if (mount(RUN_PULSE_DIR, homeusercfg, "none", MS_BIND, NULL) < 0 || | 195 | // confirm the actual mount destination is owned by the user |
175 | mount(NULL, homeusercfg, NULL, MS_NOEXEC|MS_NODEV|MS_NOSUID|MS_BIND|MS_REMOUNT, NULL) < 0) | 196 | if (fstat(fd, &s) == -1 || s.st_uid != getuid()) |
197 | errExit("fstat"); | ||
198 | |||
199 | // mount via the link in /proc/self/fd | ||
200 | char *proc; | ||
201 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
202 | errExit("asprintf"); | ||
203 | if (mount(RUN_PULSE_DIR, proc, "none", MS_BIND, NULL) < 0) | ||
176 | errExit("mount pulseaudio"); | 204 | errExit("mount pulseaudio"); |
177 | fs_logger2("tmpfs", homeusercfg); | 205 | fs_logger2("tmpfs", homeusercfg); |
178 | 206 | free(proc); | |
207 | close(fd); | ||
179 | // check /proc/self/mountinfo to confirm the mount is ok | 208 | // check /proc/self/mountinfo to confirm the mount is ok |
180 | MountData *mptr = get_last_mount(); | 209 | MountData *mptr = get_last_mount(); |
181 | if (strcmp(mptr->dir, homeusercfg) != 0) | 210 | if (strcmp(mptr->dir, homeusercfg) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) |
182 | errLogExit("invalid pulseaudio mount"); | ||
183 | if (strcmp(mptr->fstype, "tmpfs") != 0) | ||
184 | errLogExit("invalid pulseaudio mount"); | 211 | errLogExit("invalid pulseaudio mount"); |
185 | 212 | ||
186 | char *p; | 213 | char *p; |
diff --git a/src/firejail/x11.c b/src/firejail/x11.c index 4019e6001..62a769508 100644 --- a/src/firejail/x11.c +++ b/src/firejail/x11.c | |||
@@ -22,7 +22,6 @@ | |||
22 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
23 | #include <sys/socket.h> | 23 | #include <sys/socket.h> |
24 | #include <sys/un.h> | 24 | #include <sys/un.h> |
25 | #include <fcntl.h> | ||
26 | #include <unistd.h> | 25 | #include <unistd.h> |
27 | #include <signal.h> | 26 | #include <signal.h> |
28 | #include <stdlib.h> | 27 | #include <stdlib.h> |
@@ -32,6 +31,13 @@ | |||
32 | #include <errno.h> | 31 | #include <errno.h> |
33 | #include <limits.h> | 32 | #include <limits.h> |
34 | 33 | ||
34 | // on Debian 7 we are missing O_PATH definition | ||
35 | #include <fcntl.h> | ||
36 | #ifndef O_PATH | ||
37 | #define O_PATH 010000000 | ||
38 | #endif | ||
39 | |||
40 | |||
35 | // Parse the DISPLAY environment variable and return a display number. | 41 | // Parse the DISPLAY environment variable and return a display number. |
36 | // Returns -1 if DISPLAY is not set, or is set to anything other than :ddd. | 42 | // Returns -1 if DISPLAY is not set, or is set to anything other than :ddd. |
37 | int x11_display(void) { | 43 | int x11_display(void) { |
@@ -1095,7 +1101,7 @@ void x11_xorg(void) { | |||
1095 | } | 1101 | } |
1096 | 1102 | ||
1097 | // temporarily mount a tempfs on top of /tmp directory | 1103 | // temporarily mount a tempfs on top of /tmp directory |
1098 | if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) | 1104 | if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) |
1099 | errExit("mounting /tmp"); | 1105 | errExit("mounting /tmp"); |
1100 | 1106 | ||
1101 | // create the temporary .Xauthority file | 1107 | // create the temporary .Xauthority file |
@@ -1156,38 +1162,48 @@ void x11_xorg(void) { | |||
1156 | fprintf(stderr, "Error: cannot create the new .Xauthority file\n"); | 1162 | fprintf(stderr, "Error: cannot create the new .Xauthority file\n"); |
1157 | exit(1); | 1163 | exit(1); |
1158 | } | 1164 | } |
1159 | ASSERT_PERMS(RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600); | ||
1160 | /* coverity[toctou] */ | 1165 | /* coverity[toctou] */ |
1161 | unlink(tmpfname); | 1166 | unlink(tmpfname); |
1162 | umount("/tmp"); | 1167 | umount("/tmp"); |
1163 | 1168 | ||
1164 | // Ensure there is already a file in the usual location, so that bind-mount below will work. | 1169 | // Ensure there is already a file in the usual location, so that bind-mount below will work. |
1165 | // todo: fix TOCTOU races, currently managed by imposing /usr/bin/xauth as executable | ||
1166 | char *dest; | 1170 | char *dest; |
1167 | if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) | 1171 | if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) |
1168 | errExit("asprintf"); | 1172 | errExit("asprintf"); |
1169 | if (stat(dest, &s) == -1) { | 1173 | if (lstat(dest, &s) == -1) |
1170 | // create an .Xauthority file | ||
1171 | touch_file_as_user(dest, getuid(), getgid(), 0600); | 1174 | touch_file_as_user(dest, getuid(), getgid(), 0600); |
1172 | } | 1175 | |
1173 | if (is_link(dest)) { | 1176 | // get a file descriptor for .Xauthority |
1174 | fprintf(stderr, "Error: .Xauthority is a symbolic link\n"); | 1177 | fd = safe_fd(dest, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
1178 | if (fd == -1) | ||
1179 | errExit("safe_fd"); | ||
1180 | // check if the actual mount destination is a user owned regular file | ||
1181 | if (fstat(fd, &s) == -1) | ||
1182 | errExit("fstat"); | ||
1183 | if (!S_ISREG(s.st_mode) || s.st_uid != getuid()) { | ||
1184 | if (S_ISLNK(s.st_mode)) | ||
1185 | fprintf(stderr, "Error: .Xauthority is a symbolic link\n"); | ||
1186 | else | ||
1187 | fprintf(stderr, "Error: .Xauthority is not a user owned regular file\n"); | ||
1175 | exit(1); | 1188 | exit(1); |
1176 | } | 1189 | } |
1177 | 1190 | ||
1178 | // mount | 1191 | // mount via the link in /proc/self/fd |
1179 | if (mount(RUN_XAUTHORITY_SEC_FILE, dest, "none", MS_BIND, "mode=0600") == -1) { | 1192 | char *proc; |
1193 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
1194 | errExit("asprintf"); | ||
1195 | if (mount(RUN_XAUTHORITY_SEC_FILE, proc, "none", MS_BIND, "mode=0600") == -1) { | ||
1180 | fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); | 1196 | fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); |
1181 | exit(1); | 1197 | exit(1); |
1182 | } | 1198 | } |
1183 | 1199 | free(proc); | |
1200 | close(fd); | ||
1184 | // check /proc/self/mountinfo to confirm the mount is ok | 1201 | // check /proc/self/mountinfo to confirm the mount is ok |
1185 | MountData *mptr = get_last_mount(); | 1202 | MountData *mptr = get_last_mount(); |
1186 | if (strcmp(mptr->dir, dest) != 0) | 1203 | if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) |
1187 | errLogExit("invalid .Xauthority mount"); | ||
1188 | if (strcmp(mptr->fstype, "tmpfs") != 0) | ||
1189 | errLogExit("invalid .Xauthority mount"); | 1204 | errLogExit("invalid .Xauthority mount"); |
1190 | 1205 | ||
1206 | ASSERT_PERMS(dest, getuid(), getgid(), 0600); | ||
1191 | free(dest); | 1207 | free(dest); |
1192 | #endif | 1208 | #endif |
1193 | } | 1209 | } |