aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar startx2017 <vradu.startx@yandex.com>2018-09-04 08:28:14 -0400
committerLibravatar startx2017 <vradu.startx@yandex.com>2018-09-04 08:28:14 -0400
commite8989dc95bc55644661ded3c2b68212e296d19bd (patch)
tree7ecdf9603d4bd354902db65d8bd7ca944f993447
parentmainline merges (diff)
downloadfirejail-e8989dc95bc55644661ded3c2b68212e296d19bd.tar.gz
firejail-e8989dc95bc55644661ded3c2b68212e296d19bd.tar.zst
firejail-e8989dc95bc55644661ded3c2b68212e296d19bd.zip
mainline merge
-rw-r--r--src/firecfg/desktop_files.c12
-rw-r--r--src/firecfg/firecfg.config4
-rw-r--r--src/firecfg/main.c5
-rw-r--r--src/firejail/arp.c4
-rw-r--r--src/firejail/cmdline.c1
-rw-r--r--src/firejail/fs.c397
-rw-r--r--src/firejail/fs_whitelist.c40
-rw-r--r--src/firejail/main.c108
-rw-r--r--src/firejail/network.c6
-rw-r--r--src/firejail/preproc.c2
-rw-r--r--src/firejail/profile.c1
-rw-r--r--src/firejail/restrict_users.c2
-rw-r--r--src/firejail/sandbox.c36
-rw-r--r--src/firejail/sbox.c4
-rw-r--r--src/firejail/util.c78
-rw-r--r--src/firemon/interface.c2
-rw-r--r--src/fnet/arp.c2
-rw-r--r--src/fnet/interface.c16
-rw-r--r--src/fsec-optimize/main.c3
-rw-r--r--src/fsec-print/main.c3
-rw-r--r--src/include/common.h2
-rw-r--r--src/libpostexecseccomp/libpostexecseccomp.c6
22 files changed, 458 insertions, 276 deletions
diff --git a/src/firecfg/desktop_files.c b/src/firecfg/desktop_files.c
index 71b39390e..f09a23977 100644
--- a/src/firecfg/desktop_files.c
+++ b/src/firecfg/desktop_files.c
@@ -144,6 +144,8 @@ void fix_desktop_files(char *homedir) {
144 perror("opendir"); 144 perror("opendir");
145 fprintf(stderr, "Warning: cannot access /usr/share/applications directory, desktop files fixing skipped...\n"); 145 fprintf(stderr, "Warning: cannot access /usr/share/applications directory, desktop files fixing skipped...\n");
146 free(user_apps_dir); 146 free(user_apps_dir);
147 if (dir)
148 closedir(dir);
147 return; 149 return;
148 } 150 }
149 151
@@ -182,7 +184,9 @@ void fix_desktop_files(char *homedir) {
182 } 184 }
183 185
184 fseek(fp, 0, SEEK_END); 186 fseek(fp, 0, SEEK_END);
185 size_t size = ftell(fp); 187 long size = ftell(fp);
188 if (size == -1)
189 errExit("ftell");
186 fseek(fp, 0, SEEK_SET); 190 fseek(fp, 0, SEEK_SET);
187 char *buf = malloc(size + 1); 191 char *buf = malloc(size + 1);
188 if (!buf) 192 if (!buf)
@@ -266,12 +270,16 @@ void fix_desktop_files(char *homedir) {
266 270
267 if (stat(outname, &sb) == 0) { 271 if (stat(outname, &sb) == 0) {
268 printf(" %s skipped: file exists\n", filename); 272 printf(" %s skipped: file exists\n", filename);
273 if (change_exec)
274 free(change_exec);
269 continue; 275 continue;
270 } 276 }
271 277
272 FILE *fpin = fopen(filename, "r"); 278 FILE *fpin = fopen(filename, "r");
273 if (!fpin) { 279 if (!fpin) {
274 fprintf(stderr, "Warning: cannot open /usr/share/applications/%s\n", filename); 280 fprintf(stderr, "Warning: cannot open /usr/share/applications/%s\n", filename);
281 if (change_exec)
282 free(change_exec);
275 continue; 283 continue;
276 } 284 }
277 285
@@ -279,6 +287,8 @@ void fix_desktop_files(char *homedir) {
279 if (!fpout) { 287 if (!fpout) {
280 fprintf(stderr, "Warning: cannot open ~/.local/share/applications/%s\n", outname); 288 fprintf(stderr, "Warning: cannot open ~/.local/share/applications/%s\n", outname);
281 fclose(fpin); 289 fclose(fpin);
290 if (change_exec)
291 free(change_exec);
282 continue; 292 continue;
283 } 293 }
284 fprintf(fpout, "# converted by firecfg\n"); 294 fprintf(fpout, "# converted by firecfg\n");
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config
index 0bbafb343..676c2d90a 100644
--- a/src/firecfg/firecfg.config
+++ b/src/firecfg/firecfg.config
@@ -10,6 +10,7 @@ Discord
10DiscordCanary 10DiscordCanary
11FossaMail 11FossaMail
12Fritzing 12Fritzing
13JDownloader
13Mathematica 14Mathematica
14Natron 15Natron
15Telegram 16Telegram
@@ -94,6 +95,7 @@ deadbeef
94deluge 95deluge
95dex2jar 96dex2jar
96dia 97dia
98dig
97digikam 99digikam
98dillo 100dillo
99dino 101dino
@@ -217,6 +219,7 @@ inox
217iridium 219iridium
218iridium-browser 220iridium-browser
219jd-gui 221jd-gui
222jdownloader
220jitsi 223jitsi
221k3b 224k3b
222kaffeine 225kaffeine
@@ -442,6 +445,7 @@ weechat
442weechat-curses 445weechat-curses
443wesnoth 446wesnoth
444wget 447wget
448whois
445wine 449wine
446wire-desktop 450wire-desktop
447wireshark 451wireshark
diff --git a/src/firecfg/main.c b/src/firecfg/main.c
index 298314d4f..810af6ff2 100644
--- a/src/firecfg/main.c
+++ b/src/firecfg/main.c
@@ -318,13 +318,14 @@ int main(int argc, char **argv) {
318 318
319 // user setup 319 // user setup
320 char *user = get_user(); 320 char *user = get_user();
321 assert(user);
321 uid_t uid; 322 uid_t uid;
322 gid_t gid; 323 gid_t gid;
323 char *home = get_homedir(user, &uid, &gid); 324 char *home = get_homedir(user, &uid, &gid);
324 325
325 326
326 // check for --bindir 327 // check for --bindir
327 for (i = i; i < argc; i++) { 328 for (i = 1; i < argc; i++) {
328 if (strncmp(argv[i], "--bindir=", 9) == 0) { 329 if (strncmp(argv[i], "--bindir=", 9) == 0) {
329 if (strncmp(argv[i] + 9, "~/", 2) == 0) { 330 if (strncmp(argv[i] + 9, "~/", 2) == 0) {
330 if (asprintf(&arg_bindir, "%s/%s", home, argv[i] + 11) == -1) 331 if (asprintf(&arg_bindir, "%s/%s", home, argv[i] + 11) == -1)
@@ -430,7 +431,7 @@ int main(int argc, char **argv) {
430 set_links_firecfg(); 431 set_links_firecfg();
431 432
432 // add user to firejail access database - only for root 433 // add user to firejail access database - only for root
433 if (user && getuid() == 0) { 434 if (getuid() == 0) {
434 printf("\nAdding user %s to Firejail access database in %s/firejail.users\n", user, SYSCONFDIR); 435 printf("\nAdding user %s to Firejail access database in %s/firejail.users\n", user, SYSCONFDIR);
435 firejail_user_add(user); 436 firejail_user_add(user);
436 } 437 }
diff --git a/src/firejail/arp.c b/src/firejail/arp.c
index c19cb0a47..288e5ded3 100644
--- a/src/firejail/arp.c
+++ b/src/firejail/arp.c
@@ -66,7 +66,7 @@ void arp_announce(const char *dev, Bridge *br) {
66 // Find interface MAC address 66 // Find interface MAC address
67 struct ifreq ifr; 67 struct ifreq ifr;
68 memset(&ifr, 0, sizeof (ifr)); 68 memset(&ifr, 0, sizeof (ifr));
69 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 69 strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1);
70 if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) 70 if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
71 errExit("ioctl"); 71 errExit("ioctl");
72 close(sock); 72 close(sock);
@@ -138,7 +138,7 @@ int arp_check(const char *dev, uint32_t destaddr) {
138 // Find interface MAC address 138 // Find interface MAC address
139 struct ifreq ifr; 139 struct ifreq ifr;
140 memset(&ifr, 0, sizeof (ifr)); 140 memset(&ifr, 0, sizeof (ifr));
141 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 141 strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1);
142 if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) 142 if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
143 errExit("ioctl"); 143 errExit("ioctl");
144 close(sock); 144 close(sock);
diff --git a/src/firejail/cmdline.c b/src/firejail/cmdline.c
index ce1e281a5..1fe5a2398 100644
--- a/src/firejail/cmdline.c
+++ b/src/firejail/cmdline.c
@@ -208,4 +208,5 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc,
208 208
209 // free strdup 209 // free strdup
210 free(tmp1); 210 free(tmp1);
211 free(command_line_tmp);
211} 212}
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index d28ff534f..d92b6b1e0 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -555,6 +555,7 @@ void fs_mnt(void) {
555 555
556// mount /proc and /sys directories 556// mount /proc and /sys directories
557void fs_proc_sys_dev_boot(void) { 557void fs_proc_sys_dev_boot(void) {
558
558 if (arg_debug) 559 if (arg_debug)
559 printf("Remounting /proc and /proc/sys filesystems\n"); 560 printf("Remounting /proc and /proc/sys filesystems\n");
560 if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) 561 if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
@@ -571,8 +572,11 @@ void fs_proc_sys_dev_boot(void) {
571 /* Mount a version of /sys that describes the network namespace */ 572 /* Mount a version of /sys that describes the network namespace */
572 if (arg_debug) 573 if (arg_debug)
573 printf("Remounting /sys directory\n"); 574 printf("Remounting /sys directory\n");
574 if (umount2("/sys", MNT_DETACH) < 0) 575 // if this is an overlay, don't try to unmount, just mount a new sysfs
575 fwarning("failed to unmount /sys\n"); 576 if (!arg_overlay) {
577 if (umount2("/sys", MNT_DETACH) < 0 && !cfg.chrootdir)
578 fwarning("failed to unmount /sys\n");
579 }
576 if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0) 580 if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0)
577 fwarning("failed to mount /sys\n"); 581 fwarning("failed to mount /sys\n");
578 else 582 else
@@ -779,19 +783,28 @@ void fs_basic_fs(void) {
779#ifndef LTS 783#ifndef LTS
780#ifdef HAVE_OVERLAYFS 784#ifdef HAVE_OVERLAYFS
781char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { 785char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) {
786 assert(subdirname);
782 struct stat s; 787 struct stat s;
783 char *dirname; 788 char *dirname;
784 789
785 // create ~/.firejail directory
786 if (asprintf(&dirname, "%s/.firejail", cfg.homedir) == -1) 790 if (asprintf(&dirname, "%s/.firejail", cfg.homedir) == -1)
787 errExit("asprintf"); 791 errExit("asprintf");
788 792 // check if ~/.firejail already exists
789 if (is_link(dirname)) { 793 if (lstat(dirname, &s) == 0) {
790 fprintf(stderr, "Error: ~/.firejail directory is a symbolic link\n"); 794 if (!S_ISDIR(s.st_mode)) {
791 exit(1); 795 if (S_ISLNK(s.st_mode))
796 fprintf(stderr, "Error: ~/.firejail is a symbolic link\n");
797 else
798 fprintf(stderr, "Error: ~/.firejail is not a directory\n");
799 exit(1);
800 }
801 if (s.st_uid != getuid()) {
802 fprintf(stderr, "Error: ~/.firejail directory is not owned by the current user\n");
803 exit(1);
804 }
792 } 805 }
793 if (stat(dirname, &s) == -1) { 806 else {
794 // create directory 807 // create ~/.firejail directory
795 pid_t child = fork(); 808 pid_t child = fork();
796 if (child < 0) 809 if (child < 0)
797 errExit("fork"); 810 errExit("fork");
@@ -805,6 +818,9 @@ char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) {
805 if (chmod(dirname, 0700) == -1) 818 if (chmod(dirname, 0700) == -1)
806 errExit("chmod"); 819 errExit("chmod");
807 ASSERT_PERMS(dirname, getuid(), getgid(), 0700); 820 ASSERT_PERMS(dirname, getuid(), getgid(), 0700);
821#ifdef HAVE_GCOV
822 __gcov_flush();
823#endif
808 _exit(0); 824 _exit(0);
809 } 825 }
810 // wait for the child to finish 826 // wait for the child to finish
@@ -813,23 +829,27 @@ char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) {
813 fprintf(stderr, "Error: cannot create ~/.firejail directory\n"); 829 fprintf(stderr, "Error: cannot create ~/.firejail directory\n");
814 exit(1); 830 exit(1);
815 } 831 }
816 } 832 fs_logger2("create", dirname);
817 else if (s.st_uid != getuid()) {
818 fprintf(stderr, "Error: ~/.firejail directory is not owned by the current user\n");
819 exit(1);
820 } 833 }
821 free(dirname); 834 free(dirname);
822 835
823 // check overlay directory 836 // check overlay directory
824 if (asprintf(&dirname, "%s/.firejail/%s", cfg.homedir, subdirname) == -1) 837 if (asprintf(&dirname, "%s/.firejail/%s", cfg.homedir, subdirname) == -1)
825 errExit("asprintf"); 838 errExit("asprintf");
826 if (is_link(dirname)) { 839 if (lstat(dirname, &s) == 0) {
827 fprintf(stderr, "Error: overlay directory is a symbolic link\n"); 840 if (!S_ISDIR(s.st_mode)) {
828 exit(1); 841 if (S_ISLNK(s.st_mode))
829 } 842 fprintf(stderr, "Error: %s is a symbolic link\n", dirname);
830 if (allow_reuse == 0) { 843 else
831 if (stat(dirname, &s) == 0) { 844 fprintf(stderr, "Error: %s is not a directory\n", dirname);
832 fprintf(stderr, "Error: overlay directory already exists: %s\n", dirname); 845 exit(1);
846 }
847 if (s.st_uid != 0) {
848 fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", dirname);
849 exit(1);
850 }
851 if (allow_reuse == 0) {
852 fprintf(stderr, "Error: overlay directory exists, but reuse is not allowed\n");
833 exit(1); 853 exit(1);
834 } 854 }
835 } 855 }
@@ -866,9 +886,11 @@ char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) {
866// # umount /root/overlay/root 886// # umount /root/overlay/root
867 887
868 888
869// to do: fix the code below; also, it might work without /dev; impose seccomp/caps filters when not root 889// to do: fix the code below; also, it might work without /dev, but consider keeping /dev/shm; add locking mechanism for overlay-clean
870#include <sys/utsname.h> 890#include <sys/utsname.h>
871void fs_overlayfs(void) { 891void fs_overlayfs(void) {
892 struct stat s;
893
872 // check kernel version 894 // check kernel version
873 struct utsname u; 895 struct utsname u;
874 int rv = uname(&u); 896 int rv = uname(&u);
@@ -894,50 +916,78 @@ void fs_overlayfs(void) {
894 char *oroot = RUN_OVERLAY_ROOT; 916 char *oroot = RUN_OVERLAY_ROOT;
895 mkdir_attr(oroot, 0755, 0, 0); 917 mkdir_attr(oroot, 0755, 0, 0);
896 918
897 struct stat s; 919 // set base for working and diff directories
898 char *basedir = RUN_MNT_DIR; 920 char *basedir = RUN_MNT_DIR;
921 int basefd = -1;
922
899 if (arg_overlay_keep) { 923 if (arg_overlay_keep) {
900 // set base for working and diff directories
901 basedir = cfg.overlay_dir; 924 basedir = cfg.overlay_dir;
902 assert(basedir); 925 assert(basedir);
903 926 // get a file descriptor for ~/.firejail, fails if there is any symlink
904 // does the overlay exist? 927 char *firejail;
905 if (stat(basedir, &s) == 0) { 928 if (asprintf(&firejail, "%s/.firejail", cfg.homedir) == -1)
906 if (arg_overlay_reuse == 0) { 929 errExit("asprintf");
907 fprintf(stderr, "Error: overlay directory exists, but reuse is not allowed\n"); 930 int fd = safe_fd(firejail, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
908 exit(1); 931 if (fd == -1)
909 } 932 errExit("safe_fd");
910 } 933 free(firejail);
911 else { 934 // create basedir if it doesn't exist
912 /* coverity[toctou] */ 935 // the new directory will be owned by root
913 if (mkdir(basedir, 0755) != 0) { 936 const char *dirname = gnu_basename(basedir);
914 fprintf(stderr, "Error: cannot create overlay directory\n"); 937 if (mkdirat(fd, dirname, 0755) == -1 && errno != EEXIST) {
915 exit(1); 938 perror("mkdir");
916 } 939 fprintf(stderr, "Error: cannot create overlay directory %s\n", basedir);
940 exit(1);
917 } 941 }
942 // open basedir
943 basefd = openat(fd, dirname, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
944 close(fd);
945 }
946 else {
947 basefd = open(basedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
948 }
949 if (basefd == -1) {
950 perror("open");
951 fprintf(stderr, "Error: cannot open overlay directory %s\n", basedir);
952 exit(1);
918 } 953 }
919 954
920 char *odiff; 955 // confirm once more base is owned by root
921 if(asprintf(&odiff, "%s/odiff", basedir) == -1) 956 if (fstat(basefd, &s) == -1)
922 errExit("asprintf"); 957 errExit("fstat");
958 if (s.st_uid != 0) {
959 fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", basedir);
960 exit(1);
961 }
962 // confirm permissions of base are 0755
963 if (((S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) & s.st_mode) != (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) {
964 fprintf(stderr, "Error: invalid permissions on overlay directory %s\n", basedir);
965 exit(1);
966 }
923 967
968 // create diff and work directories inside base
924 // no need to check arg_overlay_reuse 969 // no need to check arg_overlay_reuse
925 if (stat(odiff, &s) != 0) { 970 char *odiff;
926 mkdir_attr(odiff, 0755, 0, 0); 971 if (asprintf(&odiff, "%s/odiff", basedir) == -1)
972 errExit("asprintf");
973 // the new directory will be owned by root
974 if (mkdirat(basefd, "odiff", 0755) == -1 && errno != EEXIST) {
975 perror("mkdir");
976 fprintf(stderr, "Error: cannot create overlay directory %s\n", odiff);
977 exit(1);
927 } 978 }
928 else if (set_perms(odiff, 0, 0, 0755)) 979 ASSERT_PERMS(odiff, 0, 0, 0755);
929 errExit("set_perms");
930 980
931 char *owork; 981 char *owork;
932 if(asprintf(&owork, "%s/owork", basedir) == -1) 982 if (asprintf(&owork, "%s/owork", basedir) == -1)
933 errExit("asprintf"); 983 errExit("asprintf");
934 984 // the new directory will be owned by root
935 // no need to check arg_overlay_reuse 985 if (mkdirat(basefd, "owork", 0755) == -1 && errno != EEXIST) {
936 if (stat(owork, &s) != 0) { 986 perror("mkdir");
937 mkdir_attr(owork, 0755, 0, 0); 987 fprintf(stderr, "Error: cannot create overlay directory %s\n", owork);
988 exit(1);
938 } 989 }
939 else if (set_perms(owork, 0, 0, 0755)) 990 ASSERT_PERMS(owork, 0, 0, 0755);
940 errExit("chown");
941 991
942 // mount overlayfs 992 // mount overlayfs
943 if (arg_debug) 993 if (arg_debug)
@@ -977,43 +1027,48 @@ void fs_overlayfs(void) {
977 1027
978 // BEFORE NEXT, WE NEED TO TEST IF /home has any contents or do we need to mount it? 1028 // BEFORE NEXT, WE NEED TO TEST IF /home has any contents or do we need to mount it?
979 // must create var for oroot/cfg.homedir 1029 // must create var for oroot/cfg.homedir
980 if (asprintf(&overlayhome,"%s%s",oroot,cfg.homedir) == -1) 1030 if (asprintf(&overlayhome, "%s%s", oroot, cfg.homedir) == -1)
981 errExit("asprintf"); 1031 errExit("asprintf");
982 if (arg_debug) printf ("DEBUG: overlayhome var holds ##%s##\n",overlayhome); 1032 if (arg_debug) printf ("DEBUG: overlayhome var holds ##%s##\n", overlayhome);
983 1033
984 // if no homedir in overlay -- create another overlay for /home 1034 // if no homedir in overlay -- create another overlay for /home
985 if (stat(overlayhome, &s) == -1) { 1035 if (stat(cfg.homedir, &s) == 0 && stat(overlayhome, &s) == -1) {
986
987 if(asprintf(&hroot, "%s/oroot/home", RUN_MNT_DIR) == -1)
988 errExit("asprintf");
989
990 if(asprintf(&hdiff, "%s/hdiff", basedir) == -1)
991 errExit("asprintf");
992 1036
993 // no need to check arg_overlay_reuse 1037 // no need to check arg_overlay_reuse
994 if (stat(hdiff, &s) != 0) { 1038 if (asprintf(&hdiff, "%s/hdiff", basedir) == -1)
995 mkdir_attr(hdiff, S_IRWXU | S_IRWXG | S_IRWXO, 0, 0);
996 }
997 else if (set_perms(hdiff, 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH))
998 errExit("set_perms");
999
1000 if(asprintf(&hwork, "%s/hwork", basedir) == -1)
1001 errExit("asprintf"); 1039 errExit("asprintf");
1040 // the new directory will be owned by root
1041 if (mkdirat(basefd, "hdiff", 0755) == -1 && errno != EEXIST) {
1042 perror("mkdir");
1043 fprintf(stderr, "Error: cannot create overlay directory %s\n", hdiff);
1044 exit(1);
1045 }
1046 ASSERT_PERMS(hdiff, 0, 0, 0755);
1002 1047
1003 // no need to check arg_overlay_reuse 1048 // no need to check arg_overlay_reuse
1004 if (stat(hwork, &s) != 0) { 1049 if (asprintf(&hwork, "%s/hwork", basedir) == -1)
1005 mkdir_attr(hwork, S_IRWXU | S_IRWXG | S_IRWXO, 0, 0); 1050 errExit("asprintf");
1051 // the new directory will be owned by root
1052 if (mkdirat(basefd, "hwork", 0755) == -1 && errno != EEXIST) {
1053 perror("mkdir");
1054 fprintf(stderr, "Error: cannot create overlay directory %s\n", hwork);
1055 exit(1);
1006 } 1056 }
1007 else if (set_perms(hwork, 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) 1057 ASSERT_PERMS(hwork, 0, 0, 0755);
1008 errExit("set_perms");
1009 1058
1010 // no homedir in overlay so now mount another overlay for /home 1059 // no homedir in overlay so now mount another overlay for /home
1060 if (asprintf(&hroot, "%s/home", oroot) == -1)
1061 errExit("asprintf");
1011 if (asprintf(&option, "lowerdir=/home,upperdir=%s,workdir=%s", hdiff, hwork) == -1) 1062 if (asprintf(&option, "lowerdir=/home,upperdir=%s,workdir=%s", hdiff, hwork) == -1)
1012 errExit("asprintf"); 1063 errExit("asprintf");
1013 if (mount("overlay", hroot, "overlay", MS_MGC_VAL, option) < 0) 1064 if (mount("overlay", hroot, "overlay", MS_MGC_VAL, option) < 0)
1014 errExit("mounting overlayfs for mounted home directory"); 1065 errExit("mounting overlayfs for mounted home directory");
1015 1066
1016 printf("OverlayFS for /home configured in %s directory\n", basedir); 1067 printf("OverlayFS for /home configured in %s directory\n", basedir);
1068 free(hroot);
1069 free(hdiff);
1070 free(hwork);
1071
1017 } // stat(overlayhome) 1072 } // stat(overlayhome)
1018 free(overlayhome); 1073 free(overlayhome);
1019 } 1074 }
@@ -1021,7 +1076,9 @@ void fs_overlayfs(void) {
1021 //*************************** 1076 //***************************
1022 } 1077 }
1023 fmessage("OverlayFS configured in %s directory\n", basedir); 1078 fmessage("OverlayFS configured in %s directory\n", basedir);
1079 close(basefd);
1024 1080
1081 // /dev, /run and /tmp are not covered by the overlay
1025 // mount-bind dev directory 1082 // mount-bind dev directory
1026 if (arg_debug) 1083 if (arg_debug)
1027 printf("Mounting /dev\n"); 1084 printf("Mounting /dev\n");
@@ -1042,19 +1099,15 @@ void fs_overlayfs(void) {
1042 errExit("mounting /run"); 1099 errExit("mounting /run");
1043 fs_logger("whitelist /run"); 1100 fs_logger("whitelist /run");
1044 1101
1045 // mount-bind /tmp/.X11-unix directory 1102 // mount-bind tmp directory
1046 if (stat("/tmp/.X11-unix", &s) == 0) { 1103 if (arg_debug)
1047 if (arg_debug) 1104 printf("Mounting /tmp\n");
1048 printf("Mounting /tmp/.X11-unix\n"); 1105 char *tmp;
1049 char *x11; 1106 if (asprintf(&tmp, "%s/tmp", oroot) == -1)
1050 if (asprintf(&x11, "%s/tmp/.X11-unix", oroot) == -1) 1107 errExit("asprintf");
1051 errExit("asprintf"); 1108 if (mount("/tmp", tmp, NULL, MS_BIND|MS_REC, NULL) < 0)
1052 if (mount("/tmp/.X11-unix", x11, NULL, MS_BIND|MS_REC, NULL) < 0) 1109 errExit("mounting /tmp");
1053 fwarning("cannot mount /tmp/.X11-unix in overlay\n"); 1110 fs_logger("whitelist /tmp");
1054 else
1055 fs_logger("whitelist /tmp/.X11-unix");
1056 free(x11);
1057 }
1058 1111
1059 // chroot in the new filesystem 1112 // chroot in the new filesystem
1060#ifdef HAVE_GCOV 1113#ifdef HAVE_GCOV
@@ -1068,12 +1121,9 @@ void fs_overlayfs(void) {
1068// fs_dev_shm(); 1121// fs_dev_shm();
1069 fs_var_lock(); 1122 fs_var_lock();
1070 if (!arg_keep_var_tmp) 1123 if (!arg_keep_var_tmp)
1071 fs_var_tmp(); 1124 fs_var_tmp();
1072 if (!arg_writable_var_log) 1125 if (!arg_writable_var_log)
1073 fs_var_log(); 1126 fs_var_log();
1074 else
1075 fs_rdwr("/var/log");
1076
1077 fs_var_lib(); 1127 fs_var_lib();
1078 fs_var_cache(); 1128 fs_var_cache();
1079 fs_var_utmp(); 1129 fs_var_utmp();
@@ -1089,97 +1139,124 @@ void fs_overlayfs(void) {
1089 // cleanup and exit 1139 // cleanup and exit
1090 free(option); 1140 free(option);
1091 free(odiff); 1141 free(odiff);
1142 free(owork);
1143 free(dev);
1144 free(run);
1145 free(tmp);
1092} 1146}
1093#endif 1147#endif
1094 1148
1095 1149
1096#ifdef HAVE_CHROOT 1150#ifdef HAVE_CHROOT
1097// return 1 if error 1151// exit if error
1098void fs_check_chroot_dir(const char *rootdir) { 1152void fs_check_chroot_dir(const char *rootdir) {
1099 EUID_ASSERT(); 1153 EUID_ASSERT();
1100 assert(rootdir); 1154 assert(rootdir);
1101 struct stat s; 1155 struct stat s;
1102 char *name; 1156 int fd = -1;
1157 int parentfd = -1;
1103 1158
1104 if (strcmp(rootdir, "/tmp") == 0 || strcmp(rootdir, "/var/tmp") == 0) { 1159 char *overlay;
1105 fprintf(stderr, "Error: invalid chroot directory\n"); 1160 if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1)
1161 errExit("asprintf");
1162 if (strncmp(rootdir, overlay, strlen(overlay)) == 0) {
1163 fprintf(stderr, "Error: invalid chroot directory: no directories in ~/.firejail are allowed\n");
1106 exit(1); 1164 exit(1);
1107 } 1165 }
1166 free(overlay);
1108 1167
1109 // rootdir has to be owned by root 1168 // fails if there is any symlink or if rootdir is not a directory
1110 if (stat(rootdir, &s) != 0) { 1169 parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1111 fprintf(stderr, "Error: cannot find chroot directory\n"); 1170 if (parentfd == -1) {
1171 fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir);
1112 exit(1); 1172 exit(1);
1113 } 1173 }
1174 // rootdir has to be owned by root and is not allowed to be generally writable,
1175 // this also excludes /tmp, /var/tmp and such
1176 if (fstat(parentfd, &s) == -1)
1177 errExit("fstat");
1114 if (s.st_uid != 0) { 1178 if (s.st_uid != 0) {
1115 fprintf(stderr, "Error: chroot directory should be owned by root\n"); 1179 fprintf(stderr, "Error: chroot directory should be owned by root\n");
1116 exit(1); 1180 exit(1);
1117 } 1181 }
1182 if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
1183 fprintf(stderr, "Error: only root user should be given write permission on chroot directory\n");
1184 exit(1);
1185 }
1118 1186
1119 // check /dev 1187 // check /dev
1120 if (asprintf(&name, "%s/dev", rootdir) == -1) 1188 fd = openat(parentfd, "dev", O_PATH|O_CLOEXEC);
1121 errExit("asprintf"); 1189 if (fd == -1) {
1122 if (stat(name, &s) == -1) { 1190 fprintf(stderr, "Error: cannot open /dev in chroot directory\n");
1123 fprintf(stderr, "Error: cannot find /dev in chroot directory\n");
1124 exit(1); 1191 exit(1);
1125 } 1192 }
1126 if (s.st_uid != 0) { 1193 if (fstat(fd, &s) == -1)
1127 fprintf(stderr, "Error: chroot /dev directory should be owned by root\n"); 1194 errExit("fstat");
1195 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1196 fprintf(stderr, "Error: chroot /dev should be a directory owned by root\n");
1128 exit(1); 1197 exit(1);
1129 } 1198 }
1130 free(name); 1199 close(fd);
1131 1200
1132 // check /var/tmp 1201 // check /var/tmp
1133 if (asprintf(&name, "%s/var/tmp", rootdir) == -1) 1202 fd = openat(parentfd, "var/tmp", O_PATH|O_CLOEXEC);
1134 errExit("asprintf"); 1203 if (fd == -1) {
1135 if (stat(name, &s) == -1) { 1204 fprintf(stderr, "Error: cannot open /var/tmp in chroot directory\n");
1136 fprintf(stderr, "Error: cannot find /var/tmp in chroot directory\n");
1137 exit(1); 1205 exit(1);
1138 } 1206 }
1139 if (s.st_uid != 0) { 1207 if (fstat(fd, &s) == -1)
1140 fprintf(stderr, "Error: chroot /var/tmp directory should be owned by root\n"); 1208 errExit("fstat");
1209 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1210 fprintf(stderr, "Error: chroot /var/tmp should be a directory owned by root\n");
1141 exit(1); 1211 exit(1);
1142 } 1212 }
1143 free(name); 1213 close(fd);
1144 1214
1145 // check /proc 1215 // check /proc
1146 if (asprintf(&name, "%s/proc", rootdir) == -1) 1216 fd = openat(parentfd, "proc", O_PATH|O_CLOEXEC);
1147 errExit("asprintf"); 1217 if (fd == -1) {
1148 if (stat(name, &s) == -1) { 1218 fprintf(stderr, "Error: cannot open /proc in chroot directory\n");
1149 fprintf(stderr, "Error: cannot find /proc in chroot directory\n");
1150 exit(1); 1219 exit(1);
1151 } 1220 }
1152 if (s.st_uid != 0) { 1221 if (fstat(fd, &s) == -1)
1153 fprintf(stderr, "Error: chroot /proc directory should be owned by root\n"); 1222 errExit("fstat");
1223 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1224 fprintf(stderr, "Error: chroot /proc should be a directory owned by root\n");
1154 exit(1); 1225 exit(1);
1155 } 1226 }
1156 free(name); 1227 close(fd);
1157 1228
1158 // check /tmp 1229 // check /tmp
1159 if (asprintf(&name, "%s/tmp", rootdir) == -1) 1230 fd = openat(parentfd, "tmp", O_PATH|O_CLOEXEC);
1160 errExit("asprintf"); 1231 if (fd == -1) {
1161 if (stat(name, &s) == -1) { 1232 fprintf(stderr, "Error: cannot open /tmp in chroot directory\n");
1162 fprintf(stderr, "Error: cannot find /tmp in chroot directory\n");
1163 exit(1); 1233 exit(1);
1164 } 1234 }
1165 if (s.st_uid != 0) { 1235 if (fstat(fd, &s) == -1)
1166 fprintf(stderr, "Error: chroot /tmp directory should be owned by root\n"); 1236 errExit("fstat");
1237 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1238 fprintf(stderr, "Error: chroot /tmp should be a directory owned by root\n");
1167 exit(1); 1239 exit(1);
1168 } 1240 }
1169 free(name); 1241 close(fd);
1170 1242
1171 // check /etc 1243 // check /etc
1172 if (asprintf(&name, "%s/etc", rootdir) == -1) 1244 fd = openat(parentfd, "etc", O_PATH|O_CLOEXEC);
1173 errExit("asprintf"); 1245 if (fd == -1) {
1174 if (stat(name, &s) == -1) { 1246 fprintf(stderr, "Error: cannot open /etc in chroot directory\n");
1175 fprintf(stderr, "Error: cannot find /etc in chroot directory\n");
1176 exit(1); 1247 exit(1);
1177 } 1248 }
1178 if (s.st_uid != 0) { 1249 if (fstat(fd, &s) == -1)
1179 fprintf(stderr, "Error: chroot /etc directory should be owned by root\n"); 1250 errExit("fstat");
1251 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1252 fprintf(stderr, "Error: chroot /etc should be a directory owned by root\n");
1180 exit(1); 1253 exit(1);
1181 } 1254 }
1182 free(name); 1255 if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
1256 fprintf(stderr, "Error: only root user should be given write permission on chroot /etc\n");
1257 exit(1);
1258 }
1259 close(fd);
1183 1260
1184 // there should be no checking on <chrootdir>/etc/resolv.conf 1261 // there should be no checking on <chrootdir>/etc/resolv.conf
1185 // the file is replaced with the real /etc/resolv.conf anyway 1262 // the file is replaced with the real /etc/resolv.conf anyway
@@ -1211,19 +1288,21 @@ void fs_check_chroot_dir(const char *rootdir) {
1211 1288
1212 // check x11 socket directory 1289 // check x11 socket directory
1213 if (getenv("FIREJAIL_X11")) { 1290 if (getenv("FIREJAIL_X11")) {
1214 char *name; 1291 fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_CLOEXEC);
1215 if (asprintf(&name, "%s/tmp/.X11-unix", rootdir) == -1) 1292 if (fd == -1) {
1216 errExit("asprintf"); 1293 fprintf(stderr, "Error: cannot open /tmp/.X11-unix in chroot directory\n");
1217 if (stat(name, &s) == -1) {
1218 fprintf(stderr, "Error: cannot find /tmp/.X11-unix in chroot directory\n");
1219 exit(1); 1294 exit(1);
1220 } 1295 }
1221 if (s.st_uid != 0) { 1296 if (fstat(fd, &s) == -1)
1222 fprintf(stderr, "Error: chroot /tmp/.X11-unix directory should be owned by root\n"); 1297 errExit("fstat");
1298 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1299 fprintf(stderr, "Error: chroot /tmp/.X11-unix should be a directory owned by root\n");
1223 exit(1); 1300 exit(1);
1224 } 1301 }
1225 free(name); 1302 close(fd);
1226 } 1303 }
1304
1305 close(parentfd);
1227} 1306}
1228 1307
1229// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf 1308// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
@@ -1254,27 +1333,49 @@ void fs_chroot(const char *rootdir) {
1254 1333
1255 // some older distros don't have a /run directory 1334 // some older distros don't have a /run directory
1256 // create one by default 1335 // create one by default
1257 // create /run/firejail directory in chroot
1258 char *rundir; 1336 char *rundir;
1259 if (asprintf(&rundir, "%s/run", rootdir) == -1) 1337 if (asprintf(&rundir, "%s/run", rootdir) == -1)
1260 errExit("asprintf"); 1338 errExit("asprintf");
1261 if (is_link(rundir)) { 1339 struct stat s;
1262 fprintf(stderr, "Error: invalid run directory inside chroot\n"); 1340 if (lstat(rundir, &s) == 0) {
1263 exit(1); 1341 if (S_ISLNK(s.st_mode)) {
1342 fprintf(stderr, "Error: chroot /run is a symbolic link\n");
1343 exit(1);
1344 }
1345 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1346 fprintf(stderr, "Error: chroot /run should be a directory owned by root\n");
1347 exit(1);
1348 }
1349 if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
1350 fprintf(stderr, "Error: only root user should be given write permission on chroot /run\n");
1351 exit(1);
1352 }
1353 }
1354 else {
1355 // several sandboxes could race to create /run
1356 if (mkdir(rundir, 0755) == -1 && errno != EEXIST)
1357 errExit("mkdir");
1358 ASSERT_PERMS(rundir, 0, 0, 0755);
1264 } 1359 }
1265 create_empty_dir_as_root(rundir, 0755);
1266 free(rundir); 1360 free(rundir);
1361
1362 // create /run/firejail directory in chroot
1267 if (asprintf(&rundir, "%s/run/firejail", rootdir) == -1) 1363 if (asprintf(&rundir, "%s/run/firejail", rootdir) == -1)
1268 errExit("asprintf"); 1364 errExit("asprintf");
1269 create_empty_dir_as_root(rundir, 0755); 1365 if (mkdir(rundir, 0755) == -1 && errno != EEXIST)
1366 errExit("mkdir");
1367 ASSERT_PERMS(rundir, 0, 0, 0755);
1270 free(rundir); 1368 free(rundir);
1271 1369
1272 // create /run/firejail/mnt directory in chroot and mount the current one 1370 // create /run/firejail/mnt directory in chroot and mount the current one
1273 if (asprintf(&rundir, "%s%s", rootdir, RUN_MNT_DIR) == -1) 1371 if (asprintf(&rundir, "%s%s", rootdir, RUN_MNT_DIR) == -1)
1274 errExit("asprintf"); 1372 errExit("asprintf");
1275 create_empty_dir_as_root(rundir, 0755); 1373 if (mkdir(rundir, 0755) == -1 && errno != EEXIST)
1374 errExit("mkdir");
1375 ASSERT_PERMS(rundir, 0, 0, 0755);
1276 if (mount(RUN_MNT_DIR, rundir, NULL, MS_BIND|MS_REC, NULL) < 0) 1376 if (mount(RUN_MNT_DIR, rundir, NULL, MS_BIND|MS_REC, NULL) < 0)
1277 errExit("mount bind"); 1377 errExit("mount bind");
1378 free(rundir);
1278 1379
1279 // copy /etc/resolv.conf in chroot directory 1380 // copy /etc/resolv.conf in chroot directory
1280 char *fname; 1381 char *fname;
@@ -1285,6 +1386,7 @@ void fs_chroot(const char *rootdir) {
1285 unlink(fname); 1386 unlink(fname);
1286 if (copy_file("/etc/resolv.conf", fname, 0, 0, 0644) == -1) // root needed 1387 if (copy_file("/etc/resolv.conf", fname, 0, 0, 0644) == -1) // root needed
1287 fwarning("/etc/resolv.conf not initialized\n"); 1388 fwarning("/etc/resolv.conf not initialized\n");
1389 free(fname);
1288 1390
1289 // chroot into the new directory 1391 // chroot into the new directory
1290#ifdef HAVE_GCOV 1392#ifdef HAVE_GCOV
@@ -1295,7 +1397,8 @@ void fs_chroot(const char *rootdir) {
1295 if (arg_debug) 1397 if (arg_debug)
1296 printf("Chrooting into %s\n", rootdir); 1398 printf("Chrooting into %s\n", rootdir);
1297 char *oroot = RUN_OVERLAY_ROOT; 1399 char *oroot = RUN_OVERLAY_ROOT;
1298 mkdir_attr(oroot, 0755, 0, 0); 1400 if (mkdir(oroot, 0755) == -1)
1401 errExit("mkdir");
1299 if (mount(rootdir, oroot, NULL, MS_BIND|MS_REC, NULL) < 0) 1402 if (mount(rootdir, oroot, NULL, MS_BIND|MS_REC, NULL) < 0)
1300 errExit("mounting rootdir oroot"); 1403 errExit("mounting rootdir oroot");
1301 if (chroot(oroot) < 0) 1404 if (chroot(oroot) < 0)
@@ -1312,8 +1415,6 @@ void fs_chroot(const char *rootdir) {
1312 fs_var_tmp(); 1415 fs_var_tmp();
1313 if (!arg_writable_var_log) 1416 if (!arg_writable_var_log)
1314 fs_var_log(); 1417 fs_var_log();
1315 else
1316 fs_rdwr("/var/log");
1317 1418
1318 fs_var_lib(); 1419 fs_var_lib();
1319 fs_var_cache(); 1420 fs_var_cache();
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 602985b4e..1fd1fb675 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -371,10 +371,13 @@ void fs_whitelist(void) {
371 371
372 // resolve macros 372 // resolve macros
373 if (is_macro(dataptr)) { 373 if (is_macro(dataptr)) {
374 char *tmp = resolve_macro(dataptr); 374 char *tmp = resolve_macro(dataptr); // returns allocated mem
375 if (tmp != NULL) 375 if (tmp != NULL) {
376 tmp = parse_nowhitelist(nowhitelist_flag, tmp); 376 char *tmp1 = parse_nowhitelist(nowhitelist_flag, tmp);
377 377 assert(tmp1);
378 free(tmp);
379 tmp = tmp1;
380 }
378 if (tmp) { 381 if (tmp) {
379 entry->data = tmp; 382 entry->data = tmp;
380 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; 383 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
@@ -491,6 +494,7 @@ void fs_whitelist(void) {
491 printf("\"%s\" disabled by --private\n", entry->data); 494 printf("\"%s\" disabled by --private\n", entry->data);
492 495
493 entry->data = EMPTY_STRING; 496 entry->data = EMPTY_STRING;
497 free(fname);
494 continue; 498 continue;
495 } 499 }
496 500
@@ -503,14 +507,18 @@ void fs_whitelist(void) {
503 // both path and absolute path are under /home 507 // both path and absolute path are under /home
504 if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0) { 508 if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0) {
505 // entire home directory is not allowed 509 // entire home directory is not allowed
506 if (*(fname + strlen(cfg.homedir)) != '/') 510 if (*(fname + strlen(cfg.homedir)) != '/') {
511 free(fname);
507 goto errexit; 512 goto errexit;
513 }
508 } 514 }
509 else { 515 else {
510 if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) { 516 if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) {
511 // check if the file is owned by the user 517 // check if the file is owned by the user
512 if (stat(fname, &s) == 0 && s.st_uid != getuid()) 518 if (stat(fname, &s) == 0 && s.st_uid != getuid()) {
519 free(fname);
513 goto errexit; 520 goto errexit;
521 }
514 } 522 }
515 } 523 }
516 } 524 }
@@ -520,6 +528,7 @@ void fs_whitelist(void) {
520 528
521 // both path and absolute path are under /tmp 529 // both path and absolute path are under /tmp
522 if (strncmp(fname, "/tmp/", 5) != 0) { 530 if (strncmp(fname, "/tmp/", 5) != 0) {
531 free(fname);
523 goto errexit; 532 goto errexit;
524 } 533 }
525 } 534 }
@@ -528,6 +537,7 @@ void fs_whitelist(void) {
528 media_dir = 1; 537 media_dir = 1;
529 // both path and absolute path are under /media 538 // both path and absolute path are under /media
530 if (strncmp(fname, "/media/", 7) != 0) { 539 if (strncmp(fname, "/media/", 7) != 0) {
540 free(fname);
531 goto errexit; 541 goto errexit;
532 } 542 }
533 } 543 }
@@ -536,6 +546,7 @@ void fs_whitelist(void) {
536 mnt_dir = 1; 546 mnt_dir = 1;
537 // both path and absolute path are under /mnt 547 // both path and absolute path are under /mnt
538 if (strncmp(fname, "/mnt/", 5) != 0) { 548 if (strncmp(fname, "/mnt/", 5) != 0) {
549 free(fname);
539 goto errexit; 550 goto errexit;
540 } 551 }
541 } 552 }
@@ -550,6 +561,7 @@ void fs_whitelist(void) {
550 else { 561 else {
551 // both path and absolute path are under /var 562 // both path and absolute path are under /var
552 if (strncmp(fname, "/var/", 5) != 0) { 563 if (strncmp(fname, "/var/", 5) != 0) {
564 free(fname);
553 goto errexit; 565 goto errexit;
554 } 566 }
555 } 567 }
@@ -570,6 +582,7 @@ void fs_whitelist(void) {
570 else { 582 else {
571 // both path and absolute path are under /dev 583 // both path and absolute path are under /dev
572 if (strncmp(fname, "/dev/", 5) != 0) { 584 if (strncmp(fname, "/dev/", 5) != 0) {
585 free(fname);
573 goto errexit; 586 goto errexit;
574 } 587 }
575 } 588 }
@@ -579,6 +592,7 @@ void fs_whitelist(void) {
579 opt_dir = 1; 592 opt_dir = 1;
580 // both path and absolute path are under /dev 593 // both path and absolute path are under /dev
581 if (strncmp(fname, "/opt/", 5) != 0) { 594 if (strncmp(fname, "/opt/", 5) != 0) {
595 free(fname);
582 goto errexit; 596 goto errexit;
583 } 597 }
584 } 598 }
@@ -587,6 +601,7 @@ void fs_whitelist(void) {
587 srv_dir = 1; 601 srv_dir = 1;
588 // both path and absolute path are under /srv 602 // both path and absolute path are under /srv
589 if (strncmp(fname, "/srv/", 5) != 0) { 603 if (strncmp(fname, "/srv/", 5) != 0) {
604 free(fname);
590 goto errexit; 605 goto errexit;
591 } 606 }
592 } 607 }
@@ -599,25 +614,32 @@ void fs_whitelist(void) {
599 else if (strcmp(new_name, "/etc/os-release") == 0); 614 else if (strcmp(new_name, "/etc/os-release") == 0);
600 // both path and absolute path are under /etc 615 // both path and absolute path are under /etc
601 else { 616 else {
602 if (strncmp(fname, "/etc/", 5) != 0) 617 if (strncmp(fname, "/etc/", 5) != 0) {
618 free(fname);
603 goto errexit; 619 goto errexit;
620 }
604 } 621 }
605 } 622 }
606 else if (strncmp(new_name, "/usr/share/", 11) == 0) { 623 else if (strncmp(new_name, "/usr/share/", 11) == 0) {
607 entry->share_dir = 1; 624 entry->share_dir = 1;
608 share_dir = 1; 625 share_dir = 1;
609 // both path and absolute path are under /etc 626 // both path and absolute path are under /etc
610 if (strncmp(fname, "/usr/share/", 11) != 0) 627 if (strncmp(fname, "/usr/share/", 11) != 0) {
628 free(fname);
611 goto errexit; 629 goto errexit;
630 }
612 } 631 }
613 else if (strncmp(new_name, "/sys/module/", 12) == 0) { 632 else if (strncmp(new_name, "/sys/module/", 12) == 0) {
614 entry->module_dir = 1; 633 entry->module_dir = 1;
615 module_dir = 1; 634 module_dir = 1;
616 // both path and absolute path are under /sys/module 635 // both path and absolute path are under /sys/module
617 if (strncmp(fname, "/sys/module/", 12) != 0) 636 if (strncmp(fname, "/sys/module/", 12) != 0) {
637 free(fname);
618 goto errexit; 638 goto errexit;
639 }
619 } 640 }
620 else { 641 else {
642 free(fname);
621 goto errexit; 643 goto errexit;
622 } 644 }
623 645
diff --git a/src/firejail/main.c b/src/firejail/main.c
index c87032f6d..d35d8c207 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -777,18 +777,15 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
777 777
778} 778}
779 779
780
781
782char *guess_shell(void) { 780char *guess_shell(void) {
783 char *shell = NULL; 781 char *shell = NULL;
784 struct stat s; 782 struct stat s;
785 783
786 shell = getenv("SHELL"); 784 shell = getenv("SHELL");
787 if (shell) { 785 if (shell) {
788 // TODO: handle rogue shell variables? 786 invalid_filename(shell, 0); // no globbing
789 if (stat(shell, &s) == 0 && access(shell, R_OK) == 0) { 787 if (!is_dir(shell) && strstr(shell, "..") == NULL && stat(shell, &s) == 0 && access(shell, X_OK) == 0)
790 return shell; 788 return shell;
791 }
792 } 789 }
793 790
794 // shells in order of preference 791 // shells in order of preference
@@ -797,7 +794,7 @@ char *guess_shell(void) {
797 int i = 0; 794 int i = 0;
798 while (shells[i] != NULL) { 795 while (shells[i] != NULL) {
799 // access call checks as real UID/GID, not as effective UID/GID 796 // access call checks as real UID/GID, not as effective UID/GID
800 if (stat(shells[i], &s) == 0 && access(shells[i], R_OK) == 0) { 797 if (stat(shells[i], &s) == 0 && access(shells[i], X_OK) == 0) {
801 shell = shells[i]; 798 shell = shells[i];
802 break; 799 break;
803 } 800 }
@@ -867,6 +864,8 @@ int main(int argc, char **argv) {
867 int lockfd_directory = -1; 864 int lockfd_directory = -1;
868 int option_cgroup = 0; 865 int option_cgroup = 0;
869 int custom_profile = 0; // custom profile loaded 866 int custom_profile = 0; // custom profile loaded
867 int arg_seccomp_cmdline = 0; // seccomp requested on command line (used to break out of --chroot)
868 int arg_caps_cmdline = 0; // caps requested on command line (used to break out of --chroot)
870 869
871 // drop permissions by default and rise them when required 870 // drop permissions by default and rise them when required
872 EUID_INIT(); 871 EUID_INIT();
@@ -1154,6 +1153,7 @@ int main(int argc, char **argv) {
1154 } 1153 }
1155 arg_seccomp = 1; 1154 arg_seccomp = 1;
1156 cfg.seccomp_list = seccomp_check_list(argv[i] + 10); 1155 cfg.seccomp_list = seccomp_check_list(argv[i] + 10);
1156 arg_seccomp_cmdline = 1;
1157 } 1157 }
1158 else 1158 else
1159 exit_err_feature("seccomp"); 1159 exit_err_feature("seccomp");
@@ -1166,6 +1166,7 @@ int main(int argc, char **argv) {
1166 } 1166 }
1167 arg_seccomp = 1; 1167 arg_seccomp = 1;
1168 cfg.seccomp_list_drop = seccomp_check_list(argv[i] + 15); 1168 cfg.seccomp_list_drop = seccomp_check_list(argv[i] + 15);
1169 arg_seccomp_cmdline = 1;
1169 } 1170 }
1170 else 1171 else
1171 exit_err_feature("seccomp"); 1172 exit_err_feature("seccomp");
@@ -1178,6 +1179,7 @@ int main(int argc, char **argv) {
1178 } 1179 }
1179 arg_seccomp = 1; 1180 arg_seccomp = 1;
1180 cfg.seccomp_list_keep = seccomp_check_list(argv[i] + 15); 1181 cfg.seccomp_list_keep = seccomp_check_list(argv[i] + 15);
1182 arg_seccomp_cmdline = 1;
1181 } 1183 }
1182 else 1184 else
1183 exit_err_feature("seccomp"); 1185 exit_err_feature("seccomp");
@@ -1196,8 +1198,10 @@ int main(int argc, char **argv) {
1196 exit_err_feature("seccomp"); 1198 exit_err_feature("seccomp");
1197 } 1199 }
1198#endif 1200#endif
1199 else if (strcmp(argv[i], "--caps") == 0) 1201 else if (strcmp(argv[i], "--caps") == 0) {
1200 arg_caps_default_filter = 1; 1202 arg_caps_default_filter = 1;
1203 arg_caps_cmdline = 1;
1204 }
1201 else if (strcmp(argv[i], "--caps.drop=all") == 0) 1205 else if (strcmp(argv[i], "--caps.drop=all") == 0)
1202 arg_caps_drop_all = 1; 1206 arg_caps_drop_all = 1;
1203 else if (strncmp(argv[i], "--caps.drop=", 12) == 0) { 1207 else if (strncmp(argv[i], "--caps.drop=", 12) == 0) {
@@ -1207,6 +1211,7 @@ int main(int argc, char **argv) {
1207 errExit("strdup"); 1211 errExit("strdup");
1208 // verify caps list and exit if problems 1212 // verify caps list and exit if problems
1209 caps_check_list(arg_caps_list, NULL); 1213 caps_check_list(arg_caps_list, NULL);
1214 arg_caps_cmdline = 1;
1210 } 1215 }
1211 else if (strncmp(argv[i], "--caps.keep=", 12) == 0) { 1216 else if (strncmp(argv[i], "--caps.keep=", 12) == 0) {
1212 arg_caps_keep = 1; 1217 arg_caps_keep = 1;
@@ -1215,6 +1220,7 @@ int main(int argc, char **argv) {
1215 errExit("strdup"); 1220 errExit("strdup");
1216 // verify caps list and exit if problems 1221 // verify caps list and exit if problems
1217 caps_check_list(arg_caps_list, NULL); 1222 caps_check_list(arg_caps_list, NULL);
1223 arg_caps_cmdline = 1;
1218 } 1224 }
1219 1225
1220 1226
@@ -1516,13 +1522,12 @@ int main(int argc, char **argv) {
1516 cfg.chrootdir = tmp; 1522 cfg.chrootdir = tmp;
1517 } 1523 }
1518 1524
1519 // check chroot dirname exists 1525 if (strstr(cfg.chrootdir, "..") || is_link(cfg.chrootdir)) {
1520 if (strstr(cfg.chrootdir, "..") || !is_dir(cfg.chrootdir) || is_link(cfg.chrootdir)) { 1526 fprintf(stderr, "Error: invalid chroot directory %s\n", cfg.chrootdir);
1521 fprintf(stderr, "Error: invalid directory %s\n", cfg.chrootdir);
1522 return 1; 1527 return 1;
1523 } 1528 }
1524 1529
1525 // don't allow "--chroot=/" 1530 // check chroot dirname exists, don't allow "--chroot=/"
1526 char *rpath = realpath(cfg.chrootdir, NULL); 1531 char *rpath = realpath(cfg.chrootdir, NULL);
1527 if (rpath == NULL || strcmp(rpath, "/") == 0) { 1532 if (rpath == NULL || strcmp(rpath, "/") == 0) {
1528 fprintf(stderr, "Error: invalid chroot directory\n"); 1533 fprintf(stderr, "Error: invalid chroot directory\n");
@@ -2159,12 +2164,12 @@ int main(int argc, char **argv) {
2159 char *shellpath; 2164 char *shellpath;
2160 if (asprintf(&shellpath, "%s%s", cfg.chrootdir, cfg.shell) == -1) 2165 if (asprintf(&shellpath, "%s%s", cfg.chrootdir, cfg.shell) == -1)
2161 errExit("asprintf"); 2166 errExit("asprintf");
2162 if (access(shellpath, R_OK)) { 2167 if (access(shellpath, X_OK)) {
2163 fprintf(stderr, "Error: cannot access shell file in chroot\n"); 2168 fprintf(stderr, "Error: cannot access shell file in chroot\n");
2164 exit(1); 2169 exit(1);
2165 } 2170 }
2166 free(shellpath); 2171 free(shellpath);
2167 } else if (access(cfg.shell, R_OK)) { 2172 } else if (access(cfg.shell, X_OK)) {
2168 fprintf(stderr, "Error: cannot access shell file\n"); 2173 fprintf(stderr, "Error: cannot access shell file\n");
2169 exit(1); 2174 exit(1);
2170 } 2175 }
@@ -2200,12 +2205,6 @@ int main(int argc, char **argv) {
2200 return 1; 2205 return 1;
2201 } 2206 }
2202 } 2207 }
2203 else if (strcmp(argv[i], "--git-install") == 0 ||
2204 strcmp(argv[i], "--git-uninstall") == 0) {
2205 fprintf(stderr, "This feature is not enabled in the current build\n");
2206 exit(1);
2207 }
2208
2209 else if (strcmp(argv[i], "--") == 0) { 2208 else if (strcmp(argv[i], "--") == 0) {
2210 // double dash - positional params to follow 2209 // double dash - positional params to follow
2211 arg_doubledash = 1; 2210 arg_doubledash = 1;
@@ -2242,6 +2241,14 @@ int main(int argc, char **argv) {
2242 } 2241 }
2243 EUID_ASSERT(); 2242 EUID_ASSERT();
2244 2243
2244 // exit for --chroot sandboxes when secomp or caps are explicitly specified on command line
2245 if (getuid() != 0 && cfg.chrootdir && (arg_seccomp_cmdline || arg_caps_cmdline)) {
2246 fprintf(stderr, "Error: for chroot sandboxes, default seccomp and capabilities filters are\n"
2247 "enabled by default. Please remove all --seccomp and --caps options from the\n"
2248 "command line.\n");
2249 exit(1);
2250 }
2251
2245 // prog_index could still be -1 if no program was specified 2252 // prog_index could still be -1 if no program was specified
2246 if (prog_index == -1 && arg_shell_none) { 2253 if (prog_index == -1 && arg_shell_none) {
2247 fprintf(stderr, "Error: shell=none configured, but no program specified\n"); 2254 fprintf(stderr, "Error: shell=none configured, but no program specified\n");
@@ -2257,12 +2264,12 @@ int main(int argc, char **argv) {
2257 // check user namespace (--noroot) options 2264 // check user namespace (--noroot) options
2258 if (arg_noroot) { 2265 if (arg_noroot) {
2259 if (arg_overlay) { 2266 if (arg_overlay) {
2260 fprintf(stderr, "Error: --overlay and --noroot are mutually exclusive.\n"); 2267 fwarning("--overlay and --noroot are mutually exclusive, --noroot disabled...\n");
2261 exit(1); 2268 arg_noroot = 0;
2262 } 2269 }
2263 else if (cfg.chrootdir) { 2270 else if (cfg.chrootdir) {
2264 fprintf(stderr, "Error: --chroot and --noroot are mutually exclusive.\n"); 2271 fwarning("--chroot and --noroot are mutually exclusive, --noroot disabled...\n");
2265 exit(1); 2272 arg_noroot = 0;
2266 } 2273 }
2267 } 2274 }
2268 2275
@@ -2336,39 +2343,30 @@ int main(int argc, char **argv) {
2336 2343
2337 // use default.profile as the default 2344 // use default.profile as the default
2338 if (!custom_profile && !arg_noprofile) { 2345 if (!custom_profile && !arg_noprofile) {
2339 if (cfg.chrootdir) { 2346 char *profile_name = DEFAULT_USER_PROFILE;
2340 fwarning("default profile disabled by --chroot option\n"); 2347 if (getuid() == 0)
2341 } 2348 profile_name = DEFAULT_ROOT_PROFILE;
2342// else if (arg_overlay) { 2349 if (arg_debug)
2343// fwarning("default profile disabled by --overlay option\n"); 2350 printf("Attempting to find %s.profile...\n", profile_name);
2344// }
2345 else {
2346 // try to load a default profile
2347 char *profile_name = DEFAULT_USER_PROFILE;
2348 if (getuid() == 0)
2349 profile_name = DEFAULT_ROOT_PROFILE;
2350 if (arg_debug)
2351 printf("Attempting to find %s.profile...\n", profile_name);
2352
2353 // look for the profile in ~/.config/firejail directory
2354 char *usercfgdir;
2355 if (asprintf(&usercfgdir, "%s/.config/firejail", cfg.homedir) == -1)
2356 errExit("asprintf");
2357 custom_profile = profile_find(profile_name, usercfgdir);
2358 free(usercfgdir);
2359 2351
2360 if (!custom_profile) 2352 // look for the profile in ~/.config/firejail directory
2361 // look for the profile in /etc/firejail directory 2353 char *usercfgdir;
2362 custom_profile = profile_find(profile_name, SYSCONFDIR); 2354 if (asprintf(&usercfgdir, "%s/.config/firejail", cfg.homedir) == -1)
2355 errExit("asprintf");
2356 custom_profile = profile_find(profile_name, usercfgdir);
2357 free(usercfgdir);
2363 2358
2364 if (!custom_profile) { 2359 if (!custom_profile)
2365 fprintf(stderr, "Error: no default.profile installed\n"); 2360 // look for the profile in /etc/firejail directory
2366 exit(1); 2361 custom_profile = profile_find(profile_name, SYSCONFDIR);
2367 }
2368 2362
2369 if (custom_profile) 2363 if (!custom_profile) {
2370 fmessage("\n** Note: you can use --noprofile to disable %s.profile **\n\n", profile_name); 2364 fprintf(stderr, "Error: no default.profile installed\n");
2365 exit(1);
2371 } 2366 }
2367
2368 if (custom_profile)
2369 fmessage("\n** Note: you can use --noprofile to disable %s.profile **\n\n", profile_name);
2372 } 2370 }
2373 EUID_ASSERT(); 2371 EUID_ASSERT();
2374 2372
@@ -2436,8 +2434,10 @@ int main(int argc, char **argv) {
2436 if (display > 0) 2434 if (display > 0)
2437 set_x11_run_file(sandbox_pid, display); 2435 set_x11_run_file(sandbox_pid, display);
2438#endif 2436#endif
2439 flock(lockfd_directory, LOCK_UN); 2437 if (lockfd_directory != -1) {
2440 close(lockfd_directory); 2438 flock(lockfd_directory, LOCK_UN);
2439 close(lockfd_directory);
2440 }
2441 EUID_USER(); 2441 EUID_USER();
2442 2442
2443 // clone environment 2443 // clone environment
diff --git a/src/firejail/network.c b/src/firejail/network.c
index 7b84854d3..fed7539ca 100644
--- a/src/firejail/network.c
+++ b/src/firejail/network.c
@@ -78,7 +78,7 @@ int net_get_mtu(const char *ifname) {
78 78
79 memset(&ifr, 0, sizeof(ifr)); 79 memset(&ifr, 0, sizeof(ifr));
80 ifr.ifr_addr.sa_family = AF_INET; 80 ifr.ifr_addr.sa_family = AF_INET;
81 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 81 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
82 if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == 0) 82 if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == 0)
83 mtu = ifr.ifr_mtu; 83 mtu = ifr.ifr_mtu;
84 if (arg_debug) 84 if (arg_debug)
@@ -106,7 +106,7 @@ void net_set_mtu(const char *ifname, int mtu) {
106 106
107 memset(&ifr, 0, sizeof(ifr)); 107 memset(&ifr, 0, sizeof(ifr));
108 ifr.ifr_addr.sa_family = AF_INET; 108 ifr.ifr_addr.sa_family = AF_INET;
109 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 109 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
110 ifr.ifr_mtu = mtu; 110 ifr.ifr_mtu = mtu;
111 if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) != 0) 111 if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) != 0)
112 fwarning("cannot set mtu for interface %s\n", ifname); 112 fwarning("cannot set mtu for interface %s\n", ifname);
@@ -269,7 +269,7 @@ int net_get_mac(const char *ifname, unsigned char mac[6]) {
269 errExit("socket"); 269 errExit("socket");
270 270
271 memset(&ifr, 0, sizeof(ifr)); 271 memset(&ifr, 0, sizeof(ifr));
272 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 272 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
273 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; 273 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
274 274
275 if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) 275 if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1)
diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c
index 9fb4840c6..f519ed85f 100644
--- a/src/firejail/preproc.c
+++ b/src/firejail/preproc.c
@@ -140,6 +140,8 @@ void preproc_clean_run(void) {
140 if (fp) { 140 if (fp) {
141 int val; 141 int val;
142 if (fscanf(fp, "%d", &val) == 1) { 142 if (fscanf(fp, "%d", &val) == 1) {
143 if (val > 4194304) // this is the max value supported on 64 bit Linux kernels
144 val = 4194304;
143 if (val >= max_pids) 145 if (val >= max_pids)
144 max_pids = val + 1; 146 max_pids = val + 1;
145 } 147 }
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index a90a5e7d6..5f6707e4b 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -728,6 +728,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
728 cfg.dns4 = dns; 728 cfg.dns4 = dns;
729 else { 729 else {
730 fprintf(stderr, "Error: up to 4 DNS servers can be specified\n"); 730 fprintf(stderr, "Error: up to 4 DNS servers can be specified\n");
731 free(dns);
731 return 1; 732 return 1;
732 } 733 }
733 return 0; 734 return 0;
diff --git a/src/firejail/restrict_users.c b/src/firejail/restrict_users.c
index d66deeb97..fa672eccb 100644
--- a/src/firejail/restrict_users.c
+++ b/src/firejail/restrict_users.c
@@ -41,6 +41,8 @@ static void ulist_add(const char *user) {
41 assert(user); 41 assert(user);
42 42
43 USER_LIST *nlist = malloc(sizeof(USER_LIST)); 43 USER_LIST *nlist = malloc(sizeof(USER_LIST));
44 if (!nlist)
45 errExit("malloc");
44 memset(nlist, 0, sizeof(USER_LIST)); 46 memset(nlist, 0, sizeof(USER_LIST));
45 nlist->user = user; 47 nlist->user = user;
46 nlist->next = ulist; 48 nlist->next = ulist;
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 380257223..e0bc0e02f 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -546,7 +546,8 @@ static void enforce_filters(void) {
546 // drop all supplementary groups; /etc/group file inside chroot 546 // drop all supplementary groups; /etc/group file inside chroot
547 // is controlled by a regular usr 547 // is controlled by a regular usr
548 arg_nogroups = 1; 548 arg_nogroups = 1;
549 fmessage("Dropping all Linux capabilities and enforcing default seccomp filter\n"); 549 fmessage("\n** Warning: dropping all Linux capabilities and enforcing **\n");
550 fmessage("** default seccomp filter **\n\n");
550} 551}
551 552
552int sandbox(void* sandbox_arg) { 553int sandbox(void* sandbox_arg) {
@@ -751,6 +752,17 @@ int sandbox(void* sandbox_arg) {
751#else 752#else
752 bool need_preload = arg_seccomp_postexec; 753 bool need_preload = arg_seccomp_postexec;
753#endif 754#endif
755 // for --appimage, --chroot and --overlay* we replace the seccomp filter with the default one
756 // we also drop all capabilities
757 if (getuid() != 0 && (arg_appimage || cfg.chrootdir || arg_overlay)) {
758 enforce_filters();
759#ifdef LTS
760 need_preload = 0;
761#else
762 need_preload = arg_trace || arg_tracelog;
763#endif
764 arg_seccomp = 1;
765 }
754 // trace pre-install 766 // trace pre-install
755 if (need_preload) 767 if (need_preload)
756 fs_trace_preload(); 768 fs_trace_preload();
@@ -762,20 +774,11 @@ int sandbox(void* sandbox_arg) {
762 //**************************** 774 //****************************
763 // configure filesystem 775 // configure filesystem
764 //**************************** 776 //****************************
765 if (arg_appimage)
766 enforce_filters();
767
768#ifndef LTS 777#ifndef LTS
769#ifdef HAVE_CHROOT 778#ifdef HAVE_CHROOT
770 if (cfg.chrootdir) { 779 if (cfg.chrootdir) {
771 fs_chroot(cfg.chrootdir); 780 fs_chroot(cfg.chrootdir);
772 781
773 // force caps and seccomp if not started as root
774 if (getuid() != 0)
775 enforce_filters();
776 else
777 arg_seccomp = 1;
778
779 //**************************** 782 //****************************
780 // trace pre-install, this time inside chroot 783 // trace pre-install, this time inside chroot
781 //**************************** 784 //****************************
@@ -785,14 +788,8 @@ int sandbox(void* sandbox_arg) {
785 else 788 else
786#endif 789#endif
787#ifdef HAVE_OVERLAYFS 790#ifdef HAVE_OVERLAYFS
788 if (arg_overlay) { 791 if (arg_overlay)
789 fs_overlayfs(); 792 fs_overlayfs();
790 // force caps and seccomp if not started as root
791 if (getuid() != 0)
792 enforce_filters();
793 else
794 arg_seccomp = 1;
795 }
796 else 793 else
797#endif 794#endif
798#endif // LTS 795#endif // LTS
@@ -1052,7 +1049,10 @@ int sandbox(void* sandbox_arg) {
1052 //**************************** 1049 //****************************
1053 // set capabilities 1050 // set capabilities
1054 set_caps(); 1051 set_caps();
1055 1052#ifndef LTS
1053 // set rlimits
1054 set_rlimits();
1055#endif //LTS
1056 // set cpu affinity 1056 // set cpu affinity
1057 if (cfg.cpus) { 1057 if (cfg.cpus) {
1058 save_cpu(); // save cpu affinity mask to CPU_CFG file 1058 save_cpu(); // save cpu affinity mask to CPU_CFG file
diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c
index c11daad58..1c6f3c327 100644
--- a/src/firejail/sbox.c
+++ b/src/firejail/sbox.c
@@ -142,8 +142,10 @@ int sbox_run(unsigned filter, int num, ...) {
142 } 142 }
143 else if ((filter & SBOX_ALLOW_STDIN) == 0) { 143 else if ((filter & SBOX_ALLOW_STDIN) == 0) {
144 int fd = open("/dev/null",O_RDWR, 0); 144 int fd = open("/dev/null",O_RDWR, 0);
145 if (fd != -1) 145 if (fd != -1) {
146 dup2(fd, STDIN_FILENO); 146 dup2(fd, STDIN_FILENO);
147 close(fd);
148 }
147 else // the user could run the sandbox without /dev/null 149 else // the user could run the sandbox without /dev/null
148 close(STDIN_FILENO); 150 close(STDIN_FILENO);
149 } 151 }
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 050f7534a..ff2bceacd 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -828,25 +828,19 @@ uid_t get_group_id(const char *group) {
828 return gid; 828 return gid;
829} 829}
830 830
831static int len_homedir = 0; 831
832static int remove_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { 832static int remove_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
833 (void) sb; 833 (void) sb;
834 (void) typeflag; 834 (void) typeflag;
835 (void) ftwbuf; 835 (void) ftwbuf;
836 assert(fpath); 836 assert(fpath);
837 837
838 if (len_homedir == 0) 838 if (strcmp(fpath, ".") == 0)
839 len_homedir = strlen(cfg.homedir); 839 return 0;
840
841 char *rp = realpath(fpath, NULL); // this should never fail!
842 if (!rp)
843 return 1;
844 if (strncmp(rp, cfg.homedir, len_homedir) != 0)
845 return 1;
846 free(rp);
847 840
848 if (remove(fpath)) { // removes the link not the actual file 841 if (remove(fpath)) { // removes the link not the actual file
849 fprintf(stderr, "Error: cannot remove file %s\n", fpath); 842 perror("remove");
843 fprintf(stderr, "Error: cannot remove file from user .firejail directory: %s\n", fpath);
850 exit(1); 844 exit(1);
851 } 845 }
852 846
@@ -855,26 +849,64 @@ static int remove_callback(const char *fpath, const struct stat *sb, int typefla
855 849
856 850
857int remove_overlay_directory(void) { 851int remove_overlay_directory(void) {
852 EUID_ASSERT();
853 struct stat s;
858 sleep(1); 854 sleep(1);
859 855
860 char *path; 856 char *path;
861 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1) 857 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1)
862 errExit("asprintf"); 858 errExit("asprintf");
863 859
864 // deal with obvious problems such as symlinks and root ownership 860 if (lstat(path, &s) == 0) {
865 if (is_link(path)) 861 // deal with obvious problems such as symlinks and root ownership
866 errLogExit("overlay directory is a symlink\n"); 862 if (!S_ISDIR(s.st_mode)) {
867 if (access(path, R_OK | W_OK | X_OK) == -1) 863 if (S_ISLNK(s.st_mode))
868 errLogExit("no access to overlay directory\n"); 864 fprintf(stderr, "Error: %s is a symbolic link\n", path);
865 else
866 fprintf(stderr, "Error: %s is not a directory\n", path);
867 exit(1);
868 }
869 if (s.st_uid != getuid()) {
870 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
871 exit(1);
872 }
873
874 pid_t child = fork();
875 if (child < 0)
876 errExit("fork");
877 if (child == 0) {
878 // open ~/.firejail, fails if there is any symlink
879 int fd = safe_fd(path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
880 if (fd == -1)
881 errExit("safe_fd");
882 // chdir to ~/.firejail
883 if (fchdir(fd) == -1)
884 errExit("fchdir");
885 close(fd);
869 886
870 EUID_ROOT(); 887 EUID_ROOT();
871 if (setreuid(0, 0) < 0 || 888 // FTW_PHYS - do not follow symbolic links
872 setregid(0, 0) < 0) 889 if (nftw(".", remove_callback, 64, FTW_DEPTH | FTW_PHYS) == -1)
873 errExit("setreuid/setregid"); 890 errExit("nftw");
874 errno = 0;
875 891
876 // FTW_PHYS - do not follow symbolic links 892 EUID_USER();
877 return nftw(path, remove_callback, 64, FTW_DEPTH | FTW_PHYS); 893 // remove ~/.firejail
894 if (rmdir(path) == -1)
895 errExit("rmdir");
896#ifdef HAVE_GCOV
897 __gcov_flush();
898#endif
899 _exit(0);
900 }
901 // wait for the child to finish
902 waitpid(child, NULL, 0);
903 // check if ~/.firejail was deleted
904 if (stat(path, &s) == -1)
905 return 0;
906 else
907 return 1;
908 }
909 return 0;
878} 910}
879 911
880void flush_stdin(void) { 912void flush_stdin(void) {
diff --git a/src/firemon/interface.c b/src/firemon/interface.c
index 71026c7b7..3e0f10d0b 100644
--- a/src/firemon/interface.c
+++ b/src/firemon/interface.c
@@ -62,7 +62,7 @@ static void net_ifprint(void) {
62 // extract mac address 62 // extract mac address
63 struct ifreq ifr; 63 struct ifreq ifr;
64 memset(&ifr, 0, sizeof(ifr)); 64 memset(&ifr, 0, sizeof(ifr));
65 strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ); 65 strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ - 1);
66 int rv = ioctl (fd, SIOCGIFHWADDR, &ifr); 66 int rv = ioctl (fd, SIOCGIFHWADDR, &ifr);
67 67
68 if (rv == 0) 68 if (rv == 0)
diff --git a/src/fnet/arp.c b/src/fnet/arp.c
index 2b6df6945..794f6c8c8 100644
--- a/src/fnet/arp.c
+++ b/src/fnet/arp.c
@@ -60,7 +60,7 @@ void arp_scan(const char *dev, uint32_t ifip, uint32_t ifmask) {
60 errExit("socket"); 60 errExit("socket");
61 struct ifreq ifr; 61 struct ifreq ifr;
62 memset(&ifr, 0, sizeof (ifr)); 62 memset(&ifr, 0, sizeof (ifr));
63 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 63 strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1);
64 if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) 64 if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
65 errExit("ioctl"); 65 errExit("ioctl");
66 close(sock); 66 close(sock);
diff --git a/src/fnet/interface.c b/src/fnet/interface.c
index f3e9a8993..283c6d312 100644
--- a/src/fnet/interface.c
+++ b/src/fnet/interface.c
@@ -58,7 +58,7 @@ void net_bridge_add_interface(const char *bridge, const char *dev) {
58 errExit("socket"); 58 errExit("socket");
59 59
60 memset(&ifr, 0, sizeof(ifr)); 60 memset(&ifr, 0, sizeof(ifr));
61 strncpy(ifr.ifr_name, bridge, IFNAMSIZ); 61 strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
62#ifdef SIOCBRADDIF 62#ifdef SIOCBRADDIF
63 ifr.ifr_ifindex = ifindex; 63 ifr.ifr_ifindex = ifindex;
64 err = ioctl(sock, SIOCBRADDIF, &ifr); 64 err = ioctl(sock, SIOCBRADDIF, &ifr);
@@ -90,7 +90,7 @@ void net_if_up(const char *ifname) {
90 // get the existing interface flags 90 // get the existing interface flags
91 struct ifreq ifr; 91 struct ifreq ifr;
92 memset(&ifr, 0, sizeof(ifr)); 92 memset(&ifr, 0, sizeof(ifr));
93 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 93 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
94 ifr.ifr_addr.sa_family = AF_INET; 94 ifr.ifr_addr.sa_family = AF_INET;
95 95
96 // read the existing flags 96 // read the existing flags
@@ -135,7 +135,7 @@ int net_get_mtu(const char *ifname) {
135 135
136 memset(&ifr, 0, sizeof(ifr)); 136 memset(&ifr, 0, sizeof(ifr));
137 ifr.ifr_addr.sa_family = AF_INET; 137 ifr.ifr_addr.sa_family = AF_INET;
138 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 138 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
139 if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == 0) 139 if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == 0)
140 mtu = ifr.ifr_mtu; 140 mtu = ifr.ifr_mtu;
141 close(s); 141 close(s);
@@ -154,7 +154,7 @@ void net_set_mtu(const char *ifname, int mtu) {
154 154
155 memset(&ifr, 0, sizeof(ifr)); 155 memset(&ifr, 0, sizeof(ifr));
156 ifr.ifr_addr.sa_family = AF_INET; 156 ifr.ifr_addr.sa_family = AF_INET;
157 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 157 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
158 ifr.ifr_mtu = mtu; 158 ifr.ifr_mtu = mtu;
159 if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) != 0) { 159 if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) != 0) {
160 if (!arg_quiet) 160 if (!arg_quiet)
@@ -238,7 +238,7 @@ int net_get_mac(const char *ifname, unsigned char mac[6]) {
238 errExit("socket"); 238 errExit("socket");
239 239
240 memset(&ifr, 0, sizeof(ifr)); 240 memset(&ifr, 0, sizeof(ifr));
241 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 241 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
242 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; 242 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
243 243
244 if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) 244 if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1)
@@ -258,7 +258,7 @@ void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu) {
258 258
259 struct ifreq ifr; 259 struct ifreq ifr;
260 memset(&ifr, 0, sizeof(ifr)); 260 memset(&ifr, 0, sizeof(ifr));
261 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 261 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
262 ifr.ifr_addr.sa_family = AF_INET; 262 ifr.ifr_addr.sa_family = AF_INET;
263 263
264 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip); 264 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip);
@@ -292,7 +292,7 @@ int net_if_mac(const char *ifname, const unsigned char mac[6]) {
292 errExit("socket"); 292 errExit("socket");
293 293
294 memset(&ifr, 0, sizeof(ifr)); 294 memset(&ifr, 0, sizeof(ifr));
295 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 295 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
296 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; 296 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
297 memcpy(ifr.ifr_hwaddr.sa_data, mac, 6); 297 memcpy(ifr.ifr_hwaddr.sa_data, mac, 6);
298 298
@@ -350,7 +350,7 @@ void net_if_ip6(const char *ifname, const char *addr6) {
350 // find interface index 350 // find interface index
351 struct ifreq ifr; 351 struct ifreq ifr;
352 memset(&ifr, 0, sizeof(ifr)); 352 memset(&ifr, 0, sizeof(ifr));
353 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 353 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
354 ifr.ifr_addr.sa_family = AF_INET; 354 ifr.ifr_addr.sa_family = AF_INET;
355 if (ioctl(sock, SIOGIFINDEX, &ifr) < 0) { 355 if (ioctl(sock, SIOGIFINDEX, &ifr) < 0) {
356 perror("ioctl SIOGIFINDEX"); 356 perror("ioctl SIOGIFINDEX");
diff --git a/src/fsec-optimize/main.c b/src/fsec-optimize/main.c
index f4300f350..80ca098cb 100644
--- a/src/fsec-optimize/main.c
+++ b/src/fsec-optimize/main.c
@@ -87,7 +87,8 @@ printf("\n");
87 87
88 return 0; 88 return 0;
89errexit: 89errexit:
90 close(fd); 90 if (fd != -1)
91 close(fd);
91 fprintf(stderr, "Error: cannot read %s\n", fname); 92 fprintf(stderr, "Error: cannot read %s\n", fname);
92 exit(1); 93 exit(1);
93 94
diff --git a/src/fsec-print/main.c b/src/fsec-print/main.c
index 94c60687f..5a1e34080 100644
--- a/src/fsec-print/main.c
+++ b/src/fsec-print/main.c
@@ -74,7 +74,8 @@ printf("\n");
74 close(fd); 74 close(fd);
75 return 0; 75 return 0;
76errexit: 76errexit:
77 close(fd); 77 if (fd != -1)
78 close(fd);
78 fprintf(stderr, "Error: cannot read %s\n", fname); 79 fprintf(stderr, "Error: cannot read %s\n", fname);
79 exit(1); 80 exit(1);
80 81
diff --git a/src/include/common.h b/src/include/common.h
index 413cf98a5..a80ad4688 100644
--- a/src/include/common.h
+++ b/src/include/common.h
@@ -32,7 +32,7 @@
32#include <ctype.h> 32#include <ctype.h>
33#include <assert.h> 33#include <assert.h>
34 34
35#define errExit(msg) do { char msgout[500]; sprintf(msgout, "Error %s: %s:%d %s", msg, __FILE__, __LINE__, __FUNCTION__); perror(msgout); exit(1);} while (0) 35#define errExit(msg) do { char msgout[500]; snprintf(msgout, 500, "Error %s: %s:%d %s", msg, __FILE__, __LINE__, __FUNCTION__); perror(msgout); exit(1);} while (0)
36 36
37// macro to print ip addresses in a printf statement 37// macro to print ip addresses in a printf statement
38#define PRINT_IP(A) \ 38#define PRINT_IP(A) \
diff --git a/src/libpostexecseccomp/libpostexecseccomp.c b/src/libpostexecseccomp/libpostexecseccomp.c
index 0ccb74b10..de64d50c5 100644
--- a/src/libpostexecseccomp/libpostexecseccomp.c
+++ b/src/libpostexecseccomp/libpostexecseccomp.c
@@ -31,7 +31,9 @@ static void load_seccomp(void) {
31 if (fd == -1) 31 if (fd == -1)
32 return; 32 return;
33 33
34 int size = lseek(fd, 0, SEEK_END); 34 off_t size = lseek(fd, 0, SEEK_END);
35 if (size <= 0)
36 return;
35 unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); 37 unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter);
36 struct sock_filter *filter = MAP_FAILED; 38 struct sock_filter *filter = MAP_FAILED;
37 if (size != 0) 39 if (size != 0)
@@ -39,7 +41,7 @@ static void load_seccomp(void) {
39 41
40 close(fd); 42 close(fd);
41 43
42 if (size == 0 || filter == MAP_FAILED) 44 if (filter == MAP_FAILED)
43 return; 45 return;
44 46
45 // install filter 47 // install filter