summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@yahoo.com>2018-07-06 07:41:59 -0500
committerLibravatar GitHub <noreply@github.com>2018-07-06 07:41:59 -0500
commit5ea2bdfbd64725d7b31b869f459057b3b1f1c764 (patch)
tree5b7d19c42281269ef9392baeeca1f087c033fc3c
parentMerges + misc fixes (diff)
parentadditional whitelist hardening (diff)
downloadfirejail-5ea2bdfbd64725d7b31b869f459057b3b1f1c764.tar.gz
firejail-5ea2bdfbd64725d7b31b869f459057b3b1f1c764.tar.zst
firejail-5ea2bdfbd64725d7b31b869f459057b3b1f1c764.zip
Merge pull request #2033 from smitsohu/whitelist
additional whitelist hardening
-rw-r--r--src/firejail/fs_whitelist.c229
-rw-r--r--src/firejail/util.c16
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
192static void whitelist_path(ProfileEntry *entry) { 192static 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
370errexit:
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
377void fs_whitelist(void) { 380void 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.
1117int safe_fd(const char *path, int flags) { 1117int 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)