aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2018-07-06 03:27:18 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2018-07-06 03:27:18 +0200
commitf57dd4a1437e1e6a1096012345067c2ac8bbb9d2 (patch)
tree5b7d19c42281269ef9392baeeca1f087c033fc3c /src
parentremove redundant checks in whitelist_path (diff)
downloadfirejail-f57dd4a1437e1e6a1096012345067c2ac8bbb9d2.tar.gz
firejail-f57dd4a1437e1e6a1096012345067c2ac8bbb9d2.tar.zst
firejail-f57dd4a1437e1e6a1096012345067c2ac8bbb9d2.zip
additional whitelist hardening
Diffstat (limited to 'src')
-rw-r--r--src/firejail/fs_whitelist.c165
-rw-r--r--src/firejail/util.c16
2 files changed, 115 insertions, 66 deletions
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index d11f727ec..9fbe45726 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -191,7 +191,7 @@ 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;
@@ -280,68 +280,103 @@ static void whitelist_path(ProfileEntry *entry) {
280 } 280 }
281 assert(wfile); 281 assert(wfile);
282 282
283 // check if the file exists 283 // check if the file exists, confirm again there is no symlink
284 EUID_USER(); 284 EUID_USER();
285 struct stat s; 285 int fd = safe_fd(wfile, O_PATH|O_NOFOLLOW|O_CLOEXEC);
286 if (stat(wfile, &s) == 0) { 286 if (fd == -1) {
287 if (arg_debug || arg_debug_whitelists)
288 printf("Whitelisting %s\n", path);
289 }
290 else {
291 free(wfile); 287 free(wfile);
292 EUID_ROOT(); 288 EUID_ROOT();
293 return; 289 return;
294 } 290 }
295 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);
296 299
297 // create the path if necessary 300 if (arg_debug || arg_debug_whitelists)
298 mkpath(path, s.st_mode); 301 printf("Whitelisting %s\n", path);
299 fs_logger2("whitelist", path); 302 fs_logger2("whitelist", path);
300 303
301 // process directory 304 // create the path if necessary
302 if (S_ISDIR(s.st_mode)) { 305 EUID_ROOT();
303 // create directory 306 struct stat s;
304 int rv = mkdir(path, 0755); 307 if (stat(path, &s) == -1) {
305 (void) rv; 308 mkpath(path, 0755);
306 } 309 if (S_ISDIR(wfilestat.st_mode)) {
307 310 int rv = mkdir(path, 0755);
308 // process regular file 311 if (rv) {
309 else { 312 fprintf(stderr, "Error: cannot create directory %s\n", path);
310 if (access(path, R_OK)) {
311 // create an empty file
312 FILE *fp = fopen(path, "w");
313 if (!fp) {
314 fprintf(stderr, "Error: cannot create empty file in home directory\n");
315 exit(1); 313 exit(1);
316 } 314 }
317 // set file properties
318 SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode);
319 fclose(fp);
320 } 315 }
321 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)) {
322 free(wfile); 327 free(wfile);
323 return; // the file is already present 328 return; // the file is already present
324 } 329 }
325 } 330 }
326 331
327 // mount 332 // get a file descriptor for path; if path contains anything other than directories
328 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)
329 errExit("mount bind"); 352 errExit("mount bind");
353 free(proc);
354 close(fd3);
330 355
331 // check the last mount operation 356 // check the last mount operation
332 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
333 358
359 if (strncmp(mptr->dir, path, strlen(path)) != 0)
360 errLogExit("invalid whitelist mount");
334 // 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!
335 // - there should be more than one '/' char in dest string 362 // - there should be more than one '/' char in dest string
336 if (mptr->dir == strrchr(mptr->dir, '/')) 363 if (mptr->dir == strrchr(mptr->dir, '/'))
337 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);
338 374
339 free(wfile); 375 free(wfile);
340 return; 376 return;
341} 377}
342 378
343 379
344// whitelist for /home/user directory
345void fs_whitelist(void) { 380void fs_whitelist(void) {
346 char *homedir = cfg.homedir; 381 char *homedir = cfg.homedir;
347 assert(homedir); 382 assert(homedir);
@@ -370,6 +405,7 @@ void fs_whitelist(void) {
370 405
371 // verify whitelist files, extract symbolic links, etc. 406 // verify whitelist files, extract symbolic links, etc.
372 EUID_USER(); 407 EUID_USER();
408 struct stat s;
373 while (entry) { 409 while (entry) {
374 int nowhitelist_flag = 0; 410 int nowhitelist_flag = 0;
375 411
@@ -512,10 +548,14 @@ void fs_whitelist(void) {
512 __LINE__, fname, cfg.homedir); 548 __LINE__, fname, cfg.homedir);
513 549
514 // both path and absolute path are under /home 550 // both path and absolute path are under /home
515 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 {
516 if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) { 557 if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) {
517 // check if the file is owned by the user 558 // check if the file is owned by the user
518 struct stat s;
519 if (stat(fname, &s) == 0 && s.st_uid != getuid()) 559 if (stat(fname, &s) == 0 && s.st_uid != getuid())
520 goto errexit; 560 goto errexit;
521 } 561 }
@@ -677,20 +717,25 @@ void fs_whitelist(void) {
677 free(nowhitelist); 717 free(nowhitelist);
678 718
679 EUID_ROOT(); 719 EUID_ROOT();
680 // /home/user 720 // /home/user mountpoint
681 if (home_dir) { 721 if (home_dir) {
682 // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR 722 // check if /home/user directory exists
683 mkdir_attr(RUN_WHITELIST_HOME_USER_DIR, 0755, getuid(), getgid()); 723 if (stat(cfg.homedir, &s) == 0) {
684 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
685 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");
686 728
687 // mount a tmpfs and initialize /home/user 729 // mount a tmpfs and initialize /home/user
688 fs_private(); 730 fs_private();
731 }
732 else
733 home_dir = 0;
689 } 734 }
690 735
691 // /tmp mountpoint 736 // /tmp mountpoint
692 if (tmp_dir) { 737 if (tmp_dir) {
693 // keep a copy of real /tmp directory in 738 // keep a copy of real /tmp directory in RUN_WHITELIST_TMP_DIR
694 mkdir_attr(RUN_WHITELIST_TMP_DIR, 1777, 0, 0); 739 mkdir_attr(RUN_WHITELIST_TMP_DIR, 1777, 0, 0);
695 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)
696 errExit("mount bind"); 741 errExit("mount bind");
@@ -706,7 +751,6 @@ void fs_whitelist(void) {
706 // /media mountpoint 751 // /media mountpoint
707 if (media_dir) { 752 if (media_dir) {
708 // some distros don't have a /media directory 753 // some distros don't have a /media directory
709 struct stat s;
710 if (stat("/media", &s) == 0) { 754 if (stat("/media", &s) == 0) {
711 // 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
712 mkdir_attr(RUN_WHITELIST_MEDIA_DIR, 0755, 0, 0); 756 mkdir_attr(RUN_WHITELIST_MEDIA_DIR, 0755, 0, 0);
@@ -727,7 +771,6 @@ void fs_whitelist(void) {
727 // /mnt mountpoint 771 // /mnt mountpoint
728 if (mnt_dir) { 772 if (mnt_dir) {
729 // check if /mnt directory exists 773 // check if /mnt directory exists
730 struct stat s;
731 if (stat("/mnt", &s) == 0) { 774 if (stat("/mnt", &s) == 0) {
732 // 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
733 mkdir_attr(RUN_WHITELIST_MNT_DIR, 0755, 0, 0); 776 mkdir_attr(RUN_WHITELIST_MNT_DIR, 0755, 0, 0);
@@ -778,23 +821,27 @@ void fs_whitelist(void) {
778 821
779 // /opt mountpoint 822 // /opt mountpoint
780 if (opt_dir) { 823 if (opt_dir) {
781 // keep a copy of real /opt directory in RUN_WHITELIST_OPT_DIR 824 // check if /opt directory exists
782 mkdir_attr(RUN_WHITELIST_OPT_DIR, 0755, 0, 0); 825 if (stat("/opt", &s) == 0) {
783 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
784 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");
785 830
786 // mount tmpfs on /opt 831 // mount tmpfs on /opt
787 if (arg_debug || arg_debug_whitelists) 832 if (arg_debug || arg_debug_whitelists)
788 printf("Mounting tmpfs on /opt directory\n"); 833 printf("Mounting tmpfs on /opt directory\n");
789 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)
790 errExit("mounting tmpfs on /opt"); 835 errExit("mounting tmpfs on /opt");
791 fs_logger("tmpfs /opt"); 836 fs_logger("tmpfs /opt");
837 }
838 else
839 opt_dir = 0;
792 } 840 }
793 841
794 // /srv mountpoint 842 // /srv mountpoint
795 if (srv_dir) { 843 if (srv_dir) {
796 // check if /srv directory exists 844 // check if /srv directory exists
797 struct stat s;
798 if (stat("/srv", &s) == 0) { 845 if (stat("/srv", &s) == 0) {
799 // 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
800 mkdir_attr(RUN_WHITELIST_SRV_DIR, 0755, 0, 0); 847 mkdir_attr(RUN_WHITELIST_SRV_DIR, 0755, 0, 0);
@@ -815,7 +862,6 @@ void fs_whitelist(void) {
815 // /etc mountpoint 862 // /etc mountpoint
816 if (etc_dir) { 863 if (etc_dir) {
817 // check if /etc directory exists 864 // check if /etc directory exists
818 struct stat s;
819 if (stat("/etc", &s) == 0) { 865 if (stat("/etc", &s) == 0) {
820 // 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
821 mkdir_attr(RUN_WHITELIST_ETC_DIR, 0755, 0, 0); 867 mkdir_attr(RUN_WHITELIST_ETC_DIR, 0755, 0, 0);
@@ -836,7 +882,6 @@ void fs_whitelist(void) {
836 // /usr/share mountpoint 882 // /usr/share mountpoint
837 if (share_dir) { 883 if (share_dir) {
838 // check if /usr/share directory exists 884 // check if /usr/share directory exists
839 struct stat s;
840 if (stat("/usr/share", &s) == 0) { 885 if (stat("/usr/share", &s) == 0) {
841 // 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
842 mkdir_attr(RUN_WHITELIST_SHARE_DIR, 0755, 0, 0); 887 mkdir_attr(RUN_WHITELIST_SHARE_DIR, 0755, 0, 0);
@@ -857,7 +902,6 @@ void fs_whitelist(void) {
857 // /sys/module mountpoint 902 // /sys/module mountpoint
858 if (module_dir) { 903 if (module_dir) {
859 // check if /sys/module directory exists 904 // check if /sys/module directory exists
860 struct stat s;
861 if (stat("/sys/module", &s) == 0) { 905 if (stat("/sys/module", &s) == 0) {
862 // 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
863 mkdir_attr(RUN_WHITELIST_MODULE_DIR, 0755, 0, 0); 907 mkdir_attr(RUN_WHITELIST_MODULE_DIR, 0755, 0, 0);
@@ -892,10 +936,9 @@ void fs_whitelist(void) {
892 // create the link if any 936 // create the link if any
893 if (entry->link) { 937 if (entry->link) {
894 // if the link is already there, do not bother 938 // if the link is already there, do not bother
895 struct stat s; 939 if (lstat(entry->link, &s) != 0) {
896 if (stat(entry->link, &s) != 0) {
897 // create the path if necessary 940 // create the path if necessary
898 mkpath(entry->link, s.st_mode); 941 mkpath(entry->link, 0755);
899 942
900 int rv = symlink(entry->data + 10, entry->link); 943 int rv = symlink(entry->data + 10, entry->link);
901 if (rv) 944 if (rv)
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)