diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/fs_whitelist.c | 229 | ||||
-rw-r--r-- | src/firejail/util.c | 16 |
2 files changed, 127 insertions, 118 deletions
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index d52b3996a..9fbe45726 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c | |||
@@ -191,189 +191,192 @@ static int mkpath(const char* path, mode_t mode) { | |||
191 | 191 | ||
192 | static void whitelist_path(ProfileEntry *entry) { | 192 | static void whitelist_path(ProfileEntry *entry) { |
193 | assert(entry); | 193 | assert(entry); |
194 | char *path = entry->data + 10; | 194 | const char *path = entry->data + 10; |
195 | assert(path); | 195 | assert(path); |
196 | const char *fname; | 196 | const char *fname; |
197 | char *wfile = NULL; | 197 | char *wfile = NULL; |
198 | 198 | ||
199 | if (entry->home_dir) { | 199 | if (entry->home_dir) { |
200 | if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) == 0) { | 200 | if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) != 0) |
201 | fname = path + strlen(cfg.homedir); | ||
202 | if (*fname == '\0') | ||
203 | goto errexit; | ||
204 | } | ||
205 | else | ||
206 | // symlink pointing outside /home, skip the mount | 201 | // symlink pointing outside /home, skip the mount |
207 | return; | 202 | return; |
208 | 203 | ||
204 | fname = path + strlen(cfg.homedir); | ||
205 | |||
209 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_HOME_USER_DIR, fname) == -1) | 206 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_HOME_USER_DIR, fname) == -1) |
210 | errExit("asprintf"); | 207 | errExit("asprintf"); |
211 | } | 208 | } |
212 | else if (entry->tmp_dir) { | 209 | else if (entry->tmp_dir) { |
213 | fname = path + 5; // strlen("/tmp/") | 210 | fname = path + 5; // strlen("/tmp/") |
214 | #ifndef TEST_MOUNTINFO | ||
215 | if (*fname == '\0') | ||
216 | errLogExit("whitelisting /tmp problem"); | ||
217 | #endif | ||
218 | 211 | ||
219 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_TMP_DIR, fname) == -1) | 212 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_TMP_DIR, fname) == -1) |
220 | errExit("asprintf"); | 213 | errExit("asprintf"); |
221 | } | 214 | } |
222 | else if (entry->media_dir) { | 215 | else if (entry->media_dir) { |
223 | fname = path + 7; // strlen("/media/") | 216 | fname = path + 7; // strlen("/media/") |
224 | if (*fname == '\0') | ||
225 | goto errexit; | ||
226 | 217 | ||
227 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MEDIA_DIR, fname) == -1) | 218 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MEDIA_DIR, fname) == -1) |
228 | errExit("asprintf"); | 219 | errExit("asprintf"); |
229 | } | 220 | } |
230 | else if (entry->mnt_dir) { | 221 | else if (entry->mnt_dir) { |
231 | fname = path + 5; // strlen("/mnt/") | 222 | fname = path + 5; // strlen("/mnt/") |
232 | if (*fname == '\0') | ||
233 | goto errexit; | ||
234 | 223 | ||
235 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MNT_DIR, fname) == -1) | 224 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MNT_DIR, fname) == -1) |
236 | errExit("asprintf"); | 225 | errExit("asprintf"); |
237 | } | 226 | } |
238 | else if (entry->var_dir) { | 227 | else if (entry->var_dir) { |
239 | if (strncmp(path, "/var/", 5) == 0) { | 228 | if (strncmp(path, "/var/", 5) != 0) |
240 | fname = path + 5; // strlen("/var/") | ||
241 | if (*fname == '\0') | ||
242 | goto errexit; | ||
243 | } | ||
244 | else | ||
245 | // symlink pointing outside /var, skip the mount | 229 | // symlink pointing outside /var, skip the mount |
246 | return; | 230 | return; |
247 | 231 | ||
232 | fname = path + 5; // strlen("/var/") | ||
233 | |||
248 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_VAR_DIR, fname) == -1) | 234 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_VAR_DIR, fname) == -1) |
249 | errExit("asprintf"); | 235 | errExit("asprintf"); |
250 | } | 236 | } |
251 | else if (entry->dev_dir) { | 237 | else if (entry->dev_dir) { |
252 | if (strncmp(path, "/dev/", 5) == 0) { | 238 | if (strncmp(path, "/dev/", 5) != 0) |
253 | fname = path + 5; // strlen("/dev/") | ||
254 | if (*fname == '\0') | ||
255 | goto errexit; | ||
256 | } | ||
257 | else | ||
258 | // symlink pointing outside /dev, skip the mount | 239 | // symlink pointing outside /dev, skip the mount |
259 | return; | 240 | return; |
260 | 241 | ||
242 | fname = path + 5; // strlen("/dev/") | ||
243 | |||
261 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_DEV_DIR, fname) == -1) | 244 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_DEV_DIR, fname) == -1) |
262 | errExit("asprintf"); | 245 | errExit("asprintf"); |
263 | } | 246 | } |
264 | else if (entry->opt_dir) { | 247 | else if (entry->opt_dir) { |
265 | fname = path + 5; // strlen("/opt/") | 248 | fname = path + 5; // strlen("/opt/") |
266 | if (*fname == '\0') | ||
267 | goto errexit; | ||
268 | 249 | ||
269 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_OPT_DIR, fname) == -1) | 250 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_OPT_DIR, fname) == -1) |
270 | errExit("asprintf"); | 251 | errExit("asprintf"); |
271 | } | 252 | } |
272 | else if (entry->srv_dir) { | 253 | else if (entry->srv_dir) { |
273 | fname = path + 5; // strlen("/srv/") | 254 | fname = path + 5; // strlen("/srv/") |
274 | if (*fname == '\0') | ||
275 | goto errexit; | ||
276 | 255 | ||
277 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SRV_DIR, fname) == -1) | 256 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SRV_DIR, fname) == -1) |
278 | errExit("asprintf"); | 257 | errExit("asprintf"); |
279 | } | 258 | } |
280 | else if (entry->etc_dir) { | 259 | else if (entry->etc_dir) { |
281 | if (strncmp(path, "/etc/", 5) == 0) { | 260 | if (strncmp(path, "/etc/", 5) != 0) |
282 | fname = path + 5; // strlen("/etc/") | ||
283 | if (*fname == '\0') | ||
284 | goto errexit; | ||
285 | } | ||
286 | else | ||
287 | // symlink pointing outside /etc, skip the mount | 261 | // symlink pointing outside /etc, skip the mount |
288 | return; | 262 | return; |
289 | 263 | ||
264 | fname = path + 5; // strlen("/etc/") | ||
265 | |||
290 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_ETC_DIR, fname) == -1) | 266 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_ETC_DIR, fname) == -1) |
291 | errExit("asprintf"); | 267 | errExit("asprintf"); |
292 | } | 268 | } |
293 | else if (entry->share_dir) { | 269 | else if (entry->share_dir) { |
294 | fname = path + 11; // strlen("/usr/share/") | 270 | fname = path + 11; // strlen("/usr/share/") |
295 | if (*fname == '\0') | ||
296 | goto errexit; | ||
297 | 271 | ||
298 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SHARE_DIR, fname) == -1) | 272 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SHARE_DIR, fname) == -1) |
299 | errExit("asprintf"); | 273 | errExit("asprintf"); |
300 | } | 274 | } |
301 | else if (entry->module_dir) { | 275 | else if (entry->module_dir) { |
302 | fname = path + 12; // strlen("/sys/module/") | 276 | fname = path + 12; // strlen("/sys/module/") |
303 | if (*fname == '\0') | ||
304 | goto errexit; | ||
305 | 277 | ||
306 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MODULE_DIR, fname) == -1) | 278 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MODULE_DIR, fname) == -1) |
307 | errExit("asprintf"); | 279 | errExit("asprintf"); |
308 | } | 280 | } |
309 | assert(wfile); | 281 | assert(wfile); |
310 | 282 | ||
311 | // check if the file exists | 283 | // check if the file exists, confirm again there is no symlink |
312 | EUID_USER(); | 284 | EUID_USER(); |
313 | struct stat s; | 285 | int fd = safe_fd(wfile, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
314 | if (stat(wfile, &s) == 0) { | 286 | if (fd == -1) { |
315 | if (arg_debug || arg_debug_whitelists) | ||
316 | printf("Whitelisting %s\n", path); | ||
317 | } | ||
318 | else { | ||
319 | free(wfile); | 287 | free(wfile); |
320 | EUID_ROOT(); | 288 | EUID_ROOT(); |
321 | return; | 289 | return; |
322 | } | 290 | } |
323 | EUID_ROOT(); | 291 | struct stat wfilestat; |
292 | if (fstat(fd, &wfilestat) == -1) | ||
293 | errExit("fstat"); | ||
294 | if (S_ISLNK(wfilestat.st_mode)) { | ||
295 | fprintf(stderr, "Error: unexpected symbolic link %s\n", path); | ||
296 | exit(1); | ||
297 | } | ||
298 | close(fd); | ||
324 | 299 | ||
325 | // create the path if necessary | 300 | if (arg_debug || arg_debug_whitelists) |
326 | mkpath(path, s.st_mode); | 301 | printf("Whitelisting %s\n", path); |
327 | fs_logger2("whitelist", path); | 302 | fs_logger2("whitelist", path); |
328 | 303 | ||
329 | // process directory | 304 | // create the path if necessary |
330 | if (S_ISDIR(s.st_mode)) { | 305 | EUID_ROOT(); |
331 | // create directory | 306 | struct stat s; |
332 | int rv = mkdir(path, 0755); | 307 | if (stat(path, &s) == -1) { |
333 | (void) rv; | 308 | mkpath(path, 0755); |
334 | } | 309 | if (S_ISDIR(wfilestat.st_mode)) { |
335 | 310 | int rv = mkdir(path, 0755); | |
336 | // process regular file | 311 | if (rv) { |
337 | else { | 312 | fprintf(stderr, "Error: cannot create directory %s\n", path); |
338 | if (access(path, R_OK)) { | ||
339 | // create an empty file | ||
340 | FILE *fp = fopen(path, "w"); | ||
341 | if (!fp) { | ||
342 | fprintf(stderr, "Error: cannot create empty file in home directory\n"); | ||
343 | exit(1); | 313 | exit(1); |
344 | } | 314 | } |
345 | // set file properties | ||
346 | SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); | ||
347 | fclose(fp); | ||
348 | } | 315 | } |
349 | else { | 316 | else { |
317 | int fd2 = open(path, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR|S_IWUSR); | ||
318 | if (fd2 == -1) { | ||
319 | fprintf(stderr, "Error: cannot create empty file %s\n", path); | ||
320 | exit(1); | ||
321 | } | ||
322 | close(fd2); | ||
323 | } | ||
324 | } | ||
325 | else { | ||
326 | if (!S_ISDIR(s.st_mode)) { | ||
350 | free(wfile); | 327 | free(wfile); |
351 | return; // the file is already present | 328 | return; // the file is already present |
352 | } | 329 | } |
353 | } | 330 | } |
354 | 331 | ||
355 | // mount | 332 | // get a file descriptor for path; if path contains anything other than directories |
356 | if (mount(wfile, path, NULL, MS_BIND|MS_REC, NULL) < 0) | 333 | // or a regular file, assume it is whitelisted already |
334 | int fd3 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | ||
335 | if (fd3 == -1) { | ||
336 | free(wfile); | ||
337 | return; | ||
338 | } | ||
339 | if (fstat(fd3, &s) == -1) | ||
340 | errExit("fstat"); | ||
341 | if (!(S_ISDIR(s.st_mode) || S_ISREG(s.st_mode))) { | ||
342 | free(wfile); | ||
343 | close(fd3); | ||
344 | return; | ||
345 | } | ||
346 | |||
347 | // mount via the link in /proc/self/fd | ||
348 | char *proc; | ||
349 | if (asprintf(&proc, "/proc/self/fd/%d", fd3) == -1) | ||
350 | errExit("asprintf"); | ||
351 | if (mount(wfile, proc, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
357 | errExit("mount bind"); | 352 | errExit("mount bind"); |
353 | free(proc); | ||
354 | close(fd3); | ||
358 | 355 | ||
359 | // check the last mount operation | 356 | // check the last mount operation |
360 | MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found | 357 | MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found |
361 | 358 | ||
359 | if (strncmp(mptr->dir, path, strlen(path)) != 0) | ||
360 | errLogExit("invalid whitelist mount"); | ||
362 | // No mounts are allowed on top level directories. A destination such as "/etc" is very bad! | 361 | // No mounts are allowed on top level directories. A destination such as "/etc" is very bad! |
363 | // - there should be more than one '/' char in dest string | 362 | // - there should be more than one '/' char in dest string |
364 | if (mptr->dir == strrchr(mptr->dir, '/')) | 363 | if (mptr->dir == strrchr(mptr->dir, '/')) |
365 | errLogExit("invalid whitelist mount\n"); | 364 | errLogExit("invalid whitelist mount"); |
365 | // confirm the correct file is mounted on path | ||
366 | int fd4 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | ||
367 | if (fd4 == -1) | ||
368 | errExit("safe_fd"); | ||
369 | if (fstat(fd4, &s) == -1) | ||
370 | errExit("fstat"); | ||
371 | if (s.st_dev != wfilestat.st_dev || s.st_ino != wfilestat.st_ino) | ||
372 | errLogExit("invalid whitelist mount"); | ||
373 | close(fd4); | ||
366 | 374 | ||
367 | free(wfile); | 375 | free(wfile); |
368 | return; | 376 | return; |
369 | |||
370 | errexit: | ||
371 | fprintf(stderr, "Error: file %s is not in the whitelisted directory\n", path); | ||
372 | exit(1); | ||
373 | } | 377 | } |
374 | 378 | ||
375 | 379 | ||
376 | // whitelist for /home/user directory | ||
377 | void fs_whitelist(void) { | 380 | void fs_whitelist(void) { |
378 | char *homedir = cfg.homedir; | 381 | char *homedir = cfg.homedir; |
379 | assert(homedir); | 382 | assert(homedir); |
@@ -402,6 +405,7 @@ void fs_whitelist(void) { | |||
402 | 405 | ||
403 | // verify whitelist files, extract symbolic links, etc. | 406 | // verify whitelist files, extract symbolic links, etc. |
404 | EUID_USER(); | 407 | EUID_USER(); |
408 | struct stat s; | ||
405 | while (entry) { | 409 | while (entry) { |
406 | int nowhitelist_flag = 0; | 410 | int nowhitelist_flag = 0; |
407 | 411 | ||
@@ -544,10 +548,14 @@ void fs_whitelist(void) { | |||
544 | __LINE__, fname, cfg.homedir); | 548 | __LINE__, fname, cfg.homedir); |
545 | 549 | ||
546 | // both path and absolute path are under /home | 550 | // both path and absolute path are under /home |
547 | if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) != 0) { | 551 | if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0) { |
552 | // entire home directory is not allowed | ||
553 | if (*(fname + strlen(cfg.homedir)) != '/') | ||
554 | goto errexit; | ||
555 | } | ||
556 | else { | ||
548 | if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) { | 557 | if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) { |
549 | // check if the file is owned by the user | 558 | // check if the file is owned by the user |
550 | struct stat s; | ||
551 | if (stat(fname, &s) == 0 && s.st_uid != getuid()) | 559 | if (stat(fname, &s) == 0 && s.st_uid != getuid()) |
552 | goto errexit; | 560 | goto errexit; |
553 | } | 561 | } |
@@ -709,20 +717,25 @@ void fs_whitelist(void) { | |||
709 | free(nowhitelist); | 717 | free(nowhitelist); |
710 | 718 | ||
711 | EUID_ROOT(); | 719 | EUID_ROOT(); |
712 | // /home/user | 720 | // /home/user mountpoint |
713 | if (home_dir) { | 721 | if (home_dir) { |
714 | // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR | 722 | // check if /home/user directory exists |
715 | mkdir_attr(RUN_WHITELIST_HOME_USER_DIR, 0755, getuid(), getgid()); | 723 | if (stat(cfg.homedir, &s) == 0) { |
716 | if (mount(cfg.homedir, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 724 | // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR |
717 | errExit("mount bind"); | 725 | mkdir_attr(RUN_WHITELIST_HOME_USER_DIR, 0755, getuid(), getgid()); |
726 | if (mount(cfg.homedir, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
727 | errExit("mount bind"); | ||
718 | 728 | ||
719 | // mount a tmpfs and initialize /home/user | 729 | // mount a tmpfs and initialize /home/user |
720 | fs_private(); | 730 | fs_private(); |
731 | } | ||
732 | else | ||
733 | home_dir = 0; | ||
721 | } | 734 | } |
722 | 735 | ||
723 | // /tmp mountpoint | 736 | // /tmp mountpoint |
724 | if (tmp_dir) { | 737 | if (tmp_dir) { |
725 | // keep a copy of real /tmp directory in | 738 | // keep a copy of real /tmp directory in RUN_WHITELIST_TMP_DIR |
726 | mkdir_attr(RUN_WHITELIST_TMP_DIR, 1777, 0, 0); | 739 | mkdir_attr(RUN_WHITELIST_TMP_DIR, 1777, 0, 0); |
727 | if (mount("/tmp", RUN_WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 740 | if (mount("/tmp", RUN_WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) |
728 | errExit("mount bind"); | 741 | errExit("mount bind"); |
@@ -738,7 +751,6 @@ void fs_whitelist(void) { | |||
738 | // /media mountpoint | 751 | // /media mountpoint |
739 | if (media_dir) { | 752 | if (media_dir) { |
740 | // some distros don't have a /media directory | 753 | // some distros don't have a /media directory |
741 | struct stat s; | ||
742 | if (stat("/media", &s) == 0) { | 754 | if (stat("/media", &s) == 0) { |
743 | // keep a copy of real /media directory in RUN_WHITELIST_MEDIA_DIR | 755 | // keep a copy of real /media directory in RUN_WHITELIST_MEDIA_DIR |
744 | mkdir_attr(RUN_WHITELIST_MEDIA_DIR, 0755, 0, 0); | 756 | mkdir_attr(RUN_WHITELIST_MEDIA_DIR, 0755, 0, 0); |
@@ -759,7 +771,6 @@ void fs_whitelist(void) { | |||
759 | // /mnt mountpoint | 771 | // /mnt mountpoint |
760 | if (mnt_dir) { | 772 | if (mnt_dir) { |
761 | // check if /mnt directory exists | 773 | // check if /mnt directory exists |
762 | struct stat s; | ||
763 | if (stat("/mnt", &s) == 0) { | 774 | if (stat("/mnt", &s) == 0) { |
764 | // keep a copy of real /mnt directory in RUN_WHITELIST_MNT_DIR | 775 | // keep a copy of real /mnt directory in RUN_WHITELIST_MNT_DIR |
765 | mkdir_attr(RUN_WHITELIST_MNT_DIR, 0755, 0, 0); | 776 | mkdir_attr(RUN_WHITELIST_MNT_DIR, 0755, 0, 0); |
@@ -810,23 +821,27 @@ void fs_whitelist(void) { | |||
810 | 821 | ||
811 | // /opt mountpoint | 822 | // /opt mountpoint |
812 | if (opt_dir) { | 823 | if (opt_dir) { |
813 | // keep a copy of real /opt directory in RUN_WHITELIST_OPT_DIR | 824 | // check if /opt directory exists |
814 | mkdir_attr(RUN_WHITELIST_OPT_DIR, 0755, 0, 0); | 825 | if (stat("/opt", &s) == 0) { |
815 | if (mount("/opt", RUN_WHITELIST_OPT_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 826 | // keep a copy of real /opt directory in RUN_WHITELIST_OPT_DIR |
816 | errExit("mount bind"); | 827 | mkdir_attr(RUN_WHITELIST_OPT_DIR, 0755, 0, 0); |
828 | if (mount("/opt", RUN_WHITELIST_OPT_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
829 | errExit("mount bind"); | ||
817 | 830 | ||
818 | // mount tmpfs on /opt | 831 | // mount tmpfs on /opt |
819 | if (arg_debug || arg_debug_whitelists) | 832 | if (arg_debug || arg_debug_whitelists) |
820 | printf("Mounting tmpfs on /opt directory\n"); | 833 | printf("Mounting tmpfs on /opt directory\n"); |
821 | if (mount("tmpfs", "/opt", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 834 | if (mount("tmpfs", "/opt", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
822 | errExit("mounting tmpfs on /opt"); | 835 | errExit("mounting tmpfs on /opt"); |
823 | fs_logger("tmpfs /opt"); | 836 | fs_logger("tmpfs /opt"); |
837 | } | ||
838 | else | ||
839 | opt_dir = 0; | ||
824 | } | 840 | } |
825 | 841 | ||
826 | // /srv mountpoint | 842 | // /srv mountpoint |
827 | if (srv_dir) { | 843 | if (srv_dir) { |
828 | // check if /srv directory exists | 844 | // check if /srv directory exists |
829 | struct stat s; | ||
830 | if (stat("/srv", &s) == 0) { | 845 | if (stat("/srv", &s) == 0) { |
831 | // keep a copy of real /srv directory in RUN_WHITELIST_SRV_DIR | 846 | // keep a copy of real /srv directory in RUN_WHITELIST_SRV_DIR |
832 | mkdir_attr(RUN_WHITELIST_SRV_DIR, 0755, 0, 0); | 847 | mkdir_attr(RUN_WHITELIST_SRV_DIR, 0755, 0, 0); |
@@ -847,7 +862,6 @@ void fs_whitelist(void) { | |||
847 | // /etc mountpoint | 862 | // /etc mountpoint |
848 | if (etc_dir) { | 863 | if (etc_dir) { |
849 | // check if /etc directory exists | 864 | // check if /etc directory exists |
850 | struct stat s; | ||
851 | if (stat("/etc", &s) == 0) { | 865 | if (stat("/etc", &s) == 0) { |
852 | // keep a copy of real /etc directory in RUN_WHITELIST_ETC_DIR | 866 | // keep a copy of real /etc directory in RUN_WHITELIST_ETC_DIR |
853 | mkdir_attr(RUN_WHITELIST_ETC_DIR, 0755, 0, 0); | 867 | mkdir_attr(RUN_WHITELIST_ETC_DIR, 0755, 0, 0); |
@@ -868,7 +882,6 @@ void fs_whitelist(void) { | |||
868 | // /usr/share mountpoint | 882 | // /usr/share mountpoint |
869 | if (share_dir) { | 883 | if (share_dir) { |
870 | // check if /usr/share directory exists | 884 | // check if /usr/share directory exists |
871 | struct stat s; | ||
872 | if (stat("/usr/share", &s) == 0) { | 885 | if (stat("/usr/share", &s) == 0) { |
873 | // keep a copy of real /usr/share directory in RUN_WHITELIST_ETC_DIR | 886 | // keep a copy of real /usr/share directory in RUN_WHITELIST_ETC_DIR |
874 | mkdir_attr(RUN_WHITELIST_SHARE_DIR, 0755, 0, 0); | 887 | mkdir_attr(RUN_WHITELIST_SHARE_DIR, 0755, 0, 0); |
@@ -889,7 +902,6 @@ void fs_whitelist(void) { | |||
889 | // /sys/module mountpoint | 902 | // /sys/module mountpoint |
890 | if (module_dir) { | 903 | if (module_dir) { |
891 | // check if /sys/module directory exists | 904 | // check if /sys/module directory exists |
892 | struct stat s; | ||
893 | if (stat("/sys/module", &s) == 0) { | 905 | if (stat("/sys/module", &s) == 0) { |
894 | // keep a copy of real /sys/module directory in RUN_WHITELIST_MODULE_DIR | 906 | // keep a copy of real /sys/module directory in RUN_WHITELIST_MODULE_DIR |
895 | mkdir_attr(RUN_WHITELIST_MODULE_DIR, 0755, 0, 0); | 907 | mkdir_attr(RUN_WHITELIST_MODULE_DIR, 0755, 0, 0); |
@@ -924,24 +936,15 @@ void fs_whitelist(void) { | |||
924 | // create the link if any | 936 | // create the link if any |
925 | if (entry->link) { | 937 | if (entry->link) { |
926 | // if the link is already there, do not bother | 938 | // if the link is already there, do not bother |
927 | struct stat s; | 939 | if (lstat(entry->link, &s) != 0) { |
928 | if (stat(entry->link, &s) != 0) { | ||
929 | // create the path if necessary | 940 | // create the path if necessary |
930 | mkpath(entry->link, s.st_mode); | 941 | mkpath(entry->link, 0755); |
931 | 942 | ||
932 | int rv = symlink(entry->data + 10, entry->link); | 943 | int rv = symlink(entry->data + 10, entry->link); |
933 | if (rv) | 944 | if (rv) |
934 | fprintf(stderr, "Warning cannot create symbolic link %s\n", entry->link); | 945 | fprintf(stderr, "Warning cannot create symbolic link %s\n", entry->link); |
935 | else if (arg_debug || arg_debug_whitelists) | 946 | else if (arg_debug || arg_debug_whitelists) |
936 | printf("Created symbolic link %s -> %s\n", entry->link, entry->data + 10); | 947 | printf("Created symbolic link %s -> %s\n", entry->link, entry->data + 10); |
937 | |||
938 | // check again for files in /tmp directory | ||
939 | if (strncmp(entry->link, "/tmp/", 5) == 0) { | ||
940 | char *path = realpath(entry->link, NULL); | ||
941 | if (path == NULL || strncmp(path, "/tmp/", 5) != 0) | ||
942 | errLogExit("invalid whitelist symlink %s\n", entry->link); | ||
943 | free(path); | ||
944 | } | ||
945 | } | 948 | } |
946 | } | 949 | } |
947 | 950 | ||
diff --git a/src/firejail/util.c b/src/firejail/util.c index eb59e36be..1d36980bb 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -1116,20 +1116,26 @@ errexit: | |||
1116 | // user controlled paths. Passed flags are ignored if path is a top level directory. | 1116 | // user controlled paths. Passed flags are ignored if path is a top level directory. |
1117 | int safe_fd(const char *path, int flags) { | 1117 | int safe_fd(const char *path, int flags) { |
1118 | assert(path); | 1118 | assert(path); |
1119 | int fd; | 1119 | int fd = -1; |
1120 | 1120 | ||
1121 | // work with a copy of path | 1121 | // work with a copy of path |
1122 | char *dup = strdup(path); | 1122 | char *dup = strdup(path); |
1123 | if (dup == NULL) | 1123 | if (dup == NULL) |
1124 | errExit("strdup"); | 1124 | errExit("strdup"); |
1125 | if (*dup != '/') | 1125 | // reject relative path and empty string |
1126 | errExit("relative path"); // or empty string | 1126 | if (*dup != '/') { |
1127 | fprintf(stderr, "Error: invalid pathname: %s\n", path); | ||
1128 | exit(1); | ||
1129 | } | ||
1127 | 1130 | ||
1128 | char *p = strrchr(dup, '/'); | 1131 | char *p = strrchr(dup, '/'); |
1129 | if (p == NULL) | 1132 | if (p == NULL) |
1130 | errExit("strrchr"); | 1133 | errExit("strrchr"); |
1131 | if (*(p + 1) == '\0') | 1134 | // reject trailing slash and root dir |
1132 | errExit("trailing slash"); // or root dir | 1135 | if (*(p + 1) == '\0') { |
1136 | fprintf(stderr, "Error: invalid pathname: %s\n", path); | ||
1137 | exit(1); | ||
1138 | } | ||
1133 | 1139 | ||
1134 | int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC); | 1140 | int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC); |
1135 | if (parentfd == -1) | 1141 | if (parentfd == -1) |