diff options
author | 2018-09-06 07:41:11 -0400 | |
---|---|---|
committer | 2018-09-06 07:41:11 -0400 | |
commit | 5e8d4cd110825f1d1608f99d6e1a8cf59e1be537 (patch) | |
tree | 8ed044526eb2dbd135b821c5ccac715d37a3bc0c | |
parent | mainline merges (diff) | |
download | firejail-5e8d4cd110825f1d1608f99d6e1a8cf59e1be537.tar.gz firejail-5e8d4cd110825f1d1608f99d6e1a8cf59e1be537.tar.zst firejail-5e8d4cd110825f1d1608f99d6e1a8cf59e1be537.zip |
final cleanup
-rw-r--r-- | src/firejail/fs.c | 650 | ||||
-rw-r--r-- | src/firejail/fs_etc.c | 112 | ||||
-rw-r--r-- | src/firejail/fs_home.c | 172 | ||||
-rw-r--r-- | src/firejail/fs_trace.c | 9 | ||||
-rw-r--r-- | src/firejail/join.c | 24 | ||||
-rw-r--r-- | src/firejail/main.c | 583 | ||||
-rw-r--r-- | src/firejail/profile.c | 319 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 145 |
8 files changed, 1 insertions, 2013 deletions
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index d92b6b1e0..a4f34b883 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -780,656 +780,6 @@ void fs_basic_fs(void) { | |||
780 | } | 780 | } |
781 | 781 | ||
782 | 782 | ||
783 | #ifndef LTS | ||
784 | #ifdef HAVE_OVERLAYFS | ||
785 | char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { | ||
786 | assert(subdirname); | ||
787 | struct stat s; | ||
788 | char *dirname; | ||
789 | |||
790 | if (asprintf(&dirname, "%s/.firejail", cfg.homedir) == -1) | ||
791 | errExit("asprintf"); | ||
792 | // check if ~/.firejail already exists | ||
793 | if (lstat(dirname, &s) == 0) { | ||
794 | if (!S_ISDIR(s.st_mode)) { | ||
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 | } | ||
805 | } | ||
806 | else { | ||
807 | // create ~/.firejail directory | ||
808 | pid_t child = fork(); | ||
809 | if (child < 0) | ||
810 | errExit("fork"); | ||
811 | if (child == 0) { | ||
812 | // drop privileges | ||
813 | drop_privs(0); | ||
814 | |||
815 | // create directory | ||
816 | if (mkdir(dirname, 0700)) | ||
817 | errExit("mkdir"); | ||
818 | if (chmod(dirname, 0700) == -1) | ||
819 | errExit("chmod"); | ||
820 | ASSERT_PERMS(dirname, getuid(), getgid(), 0700); | ||
821 | #ifdef HAVE_GCOV | ||
822 | __gcov_flush(); | ||
823 | #endif | ||
824 | _exit(0); | ||
825 | } | ||
826 | // wait for the child to finish | ||
827 | waitpid(child, NULL, 0); | ||
828 | if (stat(dirname, &s) == -1) { | ||
829 | fprintf(stderr, "Error: cannot create ~/.firejail directory\n"); | ||
830 | exit(1); | ||
831 | } | ||
832 | fs_logger2("create", dirname); | ||
833 | } | ||
834 | free(dirname); | ||
835 | |||
836 | // check overlay directory | ||
837 | if (asprintf(&dirname, "%s/.firejail/%s", cfg.homedir, subdirname) == -1) | ||
838 | errExit("asprintf"); | ||
839 | if (lstat(dirname, &s) == 0) { | ||
840 | if (!S_ISDIR(s.st_mode)) { | ||
841 | if (S_ISLNK(s.st_mode)) | ||
842 | fprintf(stderr, "Error: %s is a symbolic link\n", dirname); | ||
843 | else | ||
844 | fprintf(stderr, "Error: %s is not a directory\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"); | ||
853 | exit(1); | ||
854 | } | ||
855 | } | ||
856 | |||
857 | return dirname; | ||
858 | } | ||
859 | |||
860 | |||
861 | |||
862 | // mount overlayfs on top of / directory | ||
863 | // mounting an overlay and chrooting into it: | ||
864 | // | ||
865 | // Old Ubuntu kernel | ||
866 | // # cd ~ | ||
867 | // # mkdir -p overlay/root | ||
868 | // # mkdir -p overlay/diff | ||
869 | // # mount -t overlayfs -o lowerdir=/,upperdir=/root/overlay/diff overlayfs /root/overlay/root | ||
870 | // # chroot /root/overlay/root | ||
871 | // to shutdown, first exit the chroot and then unmount the overlay | ||
872 | // # exit | ||
873 | // # umount /root/overlay/root | ||
874 | // | ||
875 | // Kernels 3.18+ | ||
876 | // # cd ~ | ||
877 | // # mkdir -p overlay/root | ||
878 | // # mkdir -p overlay/diff | ||
879 | // # mkdir -p overlay/work | ||
880 | // # mount -t overlay -o lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work overlay /root/overlay/root | ||
881 | // # cat /etc/mtab | grep overlay | ||
882 | // /root/overlay /root/overlay/root overlay rw,relatime,lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work 0 0 | ||
883 | // # chroot /root/overlay/root | ||
884 | // to shutdown, first exit the chroot and then unmount the overlay | ||
885 | // # exit | ||
886 | // # umount /root/overlay/root | ||
887 | |||
888 | |||
889 | // to do: fix the code below; also, it might work without /dev, but consider keeping /dev/shm; add locking mechanism for overlay-clean | ||
890 | #include <sys/utsname.h> | ||
891 | void fs_overlayfs(void) { | ||
892 | struct stat s; | ||
893 | |||
894 | // check kernel version | ||
895 | struct utsname u; | ||
896 | int rv = uname(&u); | ||
897 | if (rv != 0) | ||
898 | errExit("uname"); | ||
899 | int major; | ||
900 | int minor; | ||
901 | if (2 != sscanf(u.release, "%d.%d", &major, &minor)) { | ||
902 | fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version); | ||
903 | exit(1); | ||
904 | } | ||
905 | |||
906 | if (arg_debug) | ||
907 | printf("Linux kernel version %d.%d\n", major, minor); | ||
908 | int oldkernel = 0; | ||
909 | if (major < 3) { | ||
910 | fprintf(stderr, "Error: minimum kernel version required 3.x\n"); | ||
911 | exit(1); | ||
912 | } | ||
913 | if (major == 3 && minor < 18) | ||
914 | oldkernel = 1; | ||
915 | |||
916 | char *oroot = RUN_OVERLAY_ROOT; | ||
917 | mkdir_attr(oroot, 0755, 0, 0); | ||
918 | |||
919 | // set base for working and diff directories | ||
920 | char *basedir = RUN_MNT_DIR; | ||
921 | int basefd = -1; | ||
922 | |||
923 | if (arg_overlay_keep) { | ||
924 | basedir = cfg.overlay_dir; | ||
925 | assert(basedir); | ||
926 | // get a file descriptor for ~/.firejail, fails if there is any symlink | ||
927 | char *firejail; | ||
928 | if (asprintf(&firejail, "%s/.firejail", cfg.homedir) == -1) | ||
929 | errExit("asprintf"); | ||
930 | int fd = safe_fd(firejail, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
931 | if (fd == -1) | ||
932 | errExit("safe_fd"); | ||
933 | free(firejail); | ||
934 | // create basedir if it doesn't exist | ||
935 | // the new directory will be owned by root | ||
936 | const char *dirname = gnu_basename(basedir); | ||
937 | if (mkdirat(fd, dirname, 0755) == -1 && errno != EEXIST) { | ||
938 | perror("mkdir"); | ||
939 | fprintf(stderr, "Error: cannot create overlay directory %s\n", basedir); | ||
940 | exit(1); | ||
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); | ||
953 | } | ||
954 | |||
955 | // confirm once more base is owned by root | ||
956 | if (fstat(basefd, &s) == -1) | ||
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 | } | ||
967 | |||
968 | // create diff and work directories inside base | ||
969 | // no need to check arg_overlay_reuse | ||
970 | char *odiff; | ||
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); | ||
978 | } | ||
979 | ASSERT_PERMS(odiff, 0, 0, 0755); | ||
980 | |||
981 | char *owork; | ||
982 | if (asprintf(&owork, "%s/owork", basedir) == -1) | ||
983 | errExit("asprintf"); | ||
984 | // the new directory will be owned by root | ||
985 | if (mkdirat(basefd, "owork", 0755) == -1 && errno != EEXIST) { | ||
986 | perror("mkdir"); | ||
987 | fprintf(stderr, "Error: cannot create overlay directory %s\n", owork); | ||
988 | exit(1); | ||
989 | } | ||
990 | ASSERT_PERMS(owork, 0, 0, 0755); | ||
991 | |||
992 | // mount overlayfs | ||
993 | if (arg_debug) | ||
994 | printf("Mounting OverlayFS\n"); | ||
995 | char *option; | ||
996 | if (oldkernel) { // old Ubuntu/OpenSUSE kernels | ||
997 | if (arg_overlay_keep) { | ||
998 | fprintf(stderr, "Error: option --overlay= not available for kernels older than 3.18\n"); | ||
999 | exit(1); | ||
1000 | } | ||
1001 | if (asprintf(&option, "lowerdir=/,upperdir=%s", odiff) == -1) | ||
1002 | errExit("asprintf"); | ||
1003 | if (mount("overlayfs", oroot, "overlayfs", MS_MGC_VAL, option) < 0) | ||
1004 | errExit("mounting overlayfs"); | ||
1005 | } | ||
1006 | else { // kernel 3.18 or newer | ||
1007 | if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s", odiff, owork) == -1) | ||
1008 | errExit("asprintf"); | ||
1009 | if (mount("overlay", oroot, "overlay", MS_MGC_VAL, option) < 0) | ||
1010 | errExit("mounting overlayfs"); | ||
1011 | |||
1012 | //*************************** | ||
1013 | // issue #263 start code | ||
1014 | // My setup has a separate mount point for /home. When the overlay is mounted, | ||
1015 | // the overlay does not contain the original /home contents. | ||
1016 | // I added code to create a second overlay for /home if the overlay home dir is empty and this seems to work | ||
1017 | // @dshmgh, Jan 2016 | ||
1018 | { | ||
1019 | char *overlayhome; | ||
1020 | struct stat s; | ||
1021 | char *hroot; | ||
1022 | char *hdiff; | ||
1023 | char *hwork; | ||
1024 | |||
1025 | // dons add debug | ||
1026 | if (arg_debug) printf ("DEBUG: chroot dirs are oroot %s odiff %s owork %s\n",oroot,odiff,owork); | ||
1027 | |||
1028 | // BEFORE NEXT, WE NEED TO TEST IF /home has any contents or do we need to mount it? | ||
1029 | // must create var for oroot/cfg.homedir | ||
1030 | if (asprintf(&overlayhome, "%s%s", oroot, cfg.homedir) == -1) | ||
1031 | errExit("asprintf"); | ||
1032 | if (arg_debug) printf ("DEBUG: overlayhome var holds ##%s##\n", overlayhome); | ||
1033 | |||
1034 | // if no homedir in overlay -- create another overlay for /home | ||
1035 | if (stat(cfg.homedir, &s) == 0 && stat(overlayhome, &s) == -1) { | ||
1036 | |||
1037 | // no need to check arg_overlay_reuse | ||
1038 | if (asprintf(&hdiff, "%s/hdiff", basedir) == -1) | ||
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); | ||
1047 | |||
1048 | // no need to check arg_overlay_reuse | ||
1049 | if (asprintf(&hwork, "%s/hwork", basedir) == -1) | ||
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); | ||
1056 | } | ||
1057 | ASSERT_PERMS(hwork, 0, 0, 0755); | ||
1058 | |||
1059 | // no homedir in overlay so now mount another overlay for /home | ||
1060 | if (asprintf(&hroot, "%s/home", oroot) == -1) | ||
1061 | errExit("asprintf"); | ||
1062 | if (asprintf(&option, "lowerdir=/home,upperdir=%s,workdir=%s", hdiff, hwork) == -1) | ||
1063 | errExit("asprintf"); | ||
1064 | if (mount("overlay", hroot, "overlay", MS_MGC_VAL, option) < 0) | ||
1065 | errExit("mounting overlayfs for mounted home directory"); | ||
1066 | |||
1067 | printf("OverlayFS for /home configured in %s directory\n", basedir); | ||
1068 | free(hroot); | ||
1069 | free(hdiff); | ||
1070 | free(hwork); | ||
1071 | |||
1072 | } // stat(overlayhome) | ||
1073 | free(overlayhome); | ||
1074 | } | ||
1075 | // issue #263 end code | ||
1076 | //*************************** | ||
1077 | } | ||
1078 | fmessage("OverlayFS configured in %s directory\n", basedir); | ||
1079 | close(basefd); | ||
1080 | |||
1081 | // /dev, /run and /tmp are not covered by the overlay | ||
1082 | // mount-bind dev directory | ||
1083 | if (arg_debug) | ||
1084 | printf("Mounting /dev\n"); | ||
1085 | char *dev; | ||
1086 | if (asprintf(&dev, "%s/dev", oroot) == -1) | ||
1087 | errExit("asprintf"); | ||
1088 | if (mount("/dev", dev, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
1089 | errExit("mounting /dev"); | ||
1090 | fs_logger("whitelist /dev"); | ||
1091 | |||
1092 | // mount-bind run directory | ||
1093 | if (arg_debug) | ||
1094 | printf("Mounting /run\n"); | ||
1095 | char *run; | ||
1096 | if (asprintf(&run, "%s/run", oroot) == -1) | ||
1097 | errExit("asprintf"); | ||
1098 | if (mount("/run", run, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
1099 | errExit("mounting /run"); | ||
1100 | fs_logger("whitelist /run"); | ||
1101 | |||
1102 | // mount-bind tmp directory | ||
1103 | if (arg_debug) | ||
1104 | printf("Mounting /tmp\n"); | ||
1105 | char *tmp; | ||
1106 | if (asprintf(&tmp, "%s/tmp", oroot) == -1) | ||
1107 | errExit("asprintf"); | ||
1108 | if (mount("/tmp", tmp, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
1109 | errExit("mounting /tmp"); | ||
1110 | fs_logger("whitelist /tmp"); | ||
1111 | |||
1112 | // chroot in the new filesystem | ||
1113 | #ifdef HAVE_GCOV | ||
1114 | __gcov_flush(); | ||
1115 | #endif | ||
1116 | if (chroot(oroot) == -1) | ||
1117 | errExit("chroot"); | ||
1118 | |||
1119 | // update /var directory in order to support multiple sandboxes running on the same root directory | ||
1120 | // if (!arg_private_dev) | ||
1121 | // fs_dev_shm(); | ||
1122 | fs_var_lock(); | ||
1123 | if (!arg_keep_var_tmp) | ||
1124 | fs_var_tmp(); | ||
1125 | if (!arg_writable_var_log) | ||
1126 | fs_var_log(); | ||
1127 | fs_var_lib(); | ||
1128 | fs_var_cache(); | ||
1129 | fs_var_utmp(); | ||
1130 | fs_machineid(); | ||
1131 | |||
1132 | // don't leak user information | ||
1133 | restrict_users(); | ||
1134 | |||
1135 | // when starting as root, firejail config is not disabled; | ||
1136 | if (getuid() != 0) | ||
1137 | disable_config(); | ||
1138 | |||
1139 | // cleanup and exit | ||
1140 | free(option); | ||
1141 | free(odiff); | ||
1142 | free(owork); | ||
1143 | free(dev); | ||
1144 | free(run); | ||
1145 | free(tmp); | ||
1146 | } | ||
1147 | #endif | ||
1148 | |||
1149 | |||
1150 | #ifdef HAVE_CHROOT | ||
1151 | // exit if error | ||
1152 | void fs_check_chroot_dir(const char *rootdir) { | ||
1153 | EUID_ASSERT(); | ||
1154 | assert(rootdir); | ||
1155 | struct stat s; | ||
1156 | int fd = -1; | ||
1157 | int parentfd = -1; | ||
1158 | |||
1159 | char *overlay; | ||
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"); | ||
1164 | exit(1); | ||
1165 | } | ||
1166 | free(overlay); | ||
1167 | |||
1168 | // fails if there is any symlink or if rootdir is not a directory | ||
1169 | parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
1170 | if (parentfd == -1) { | ||
1171 | fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir); | ||
1172 | exit(1); | ||
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"); | ||
1178 | if (s.st_uid != 0) { | ||
1179 | fprintf(stderr, "Error: chroot directory should be owned by root\n"); | ||
1180 | exit(1); | ||
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 | } | ||
1186 | |||
1187 | // check /dev | ||
1188 | fd = openat(parentfd, "dev", O_PATH|O_CLOEXEC); | ||
1189 | if (fd == -1) { | ||
1190 | fprintf(stderr, "Error: cannot open /dev in chroot directory\n"); | ||
1191 | exit(1); | ||
1192 | } | ||
1193 | if (fstat(fd, &s) == -1) | ||
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"); | ||
1197 | exit(1); | ||
1198 | } | ||
1199 | close(fd); | ||
1200 | |||
1201 | // check /var/tmp | ||
1202 | fd = openat(parentfd, "var/tmp", O_PATH|O_CLOEXEC); | ||
1203 | if (fd == -1) { | ||
1204 | fprintf(stderr, "Error: cannot open /var/tmp in chroot directory\n"); | ||
1205 | exit(1); | ||
1206 | } | ||
1207 | if (fstat(fd, &s) == -1) | ||
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"); | ||
1211 | exit(1); | ||
1212 | } | ||
1213 | close(fd); | ||
1214 | |||
1215 | // check /proc | ||
1216 | fd = openat(parentfd, "proc", O_PATH|O_CLOEXEC); | ||
1217 | if (fd == -1) { | ||
1218 | fprintf(stderr, "Error: cannot open /proc in chroot directory\n"); | ||
1219 | exit(1); | ||
1220 | } | ||
1221 | if (fstat(fd, &s) == -1) | ||
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"); | ||
1225 | exit(1); | ||
1226 | } | ||
1227 | close(fd); | ||
1228 | |||
1229 | // check /tmp | ||
1230 | fd = openat(parentfd, "tmp", O_PATH|O_CLOEXEC); | ||
1231 | if (fd == -1) { | ||
1232 | fprintf(stderr, "Error: cannot open /tmp in chroot directory\n"); | ||
1233 | exit(1); | ||
1234 | } | ||
1235 | if (fstat(fd, &s) == -1) | ||
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"); | ||
1239 | exit(1); | ||
1240 | } | ||
1241 | close(fd); | ||
1242 | |||
1243 | // check /etc | ||
1244 | fd = openat(parentfd, "etc", O_PATH|O_CLOEXEC); | ||
1245 | if (fd == -1) { | ||
1246 | fprintf(stderr, "Error: cannot open /etc in chroot directory\n"); | ||
1247 | exit(1); | ||
1248 | } | ||
1249 | if (fstat(fd, &s) == -1) | ||
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"); | ||
1253 | exit(1); | ||
1254 | } | ||
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); | ||
1260 | |||
1261 | // there should be no checking on <chrootdir>/etc/resolv.conf | ||
1262 | // the file is replaced with the real /etc/resolv.conf anyway | ||
1263 | #if 0 | ||
1264 | if (asprintf(&name, "%s/etc/resolv.conf", rootdir) == -1) | ||
1265 | errExit("asprintf"); | ||
1266 | if (stat(name, &s) == 0) { | ||
1267 | if (s.st_uid != 0) { | ||
1268 | fprintf(stderr, "Error: chroot /etc/resolv.conf should be owned by root\n"); | ||
1269 | exit(1); | ||
1270 | } | ||
1271 | } | ||
1272 | else { | ||
1273 | fprintf(stderr, "Error: chroot /etc/resolv.conf not found\n"); | ||
1274 | exit(1); | ||
1275 | } | ||
1276 | // on Arch /etc/resolv.conf could be a symlink to /run/systemd/resolve/resolv.conf | ||
1277 | // on Ubuntu 17.04 /etc/resolv.conf could be a symlink to /run/resolveconf/resolv.conf | ||
1278 | if (is_link(name)) { | ||
1279 | // check the link points in chroot | ||
1280 | char *rname = realpath(name, NULL); | ||
1281 | if (!rname || strncmp(rname, rootdir, strlen(rootdir)) != 0) { | ||
1282 | fprintf(stderr, "Error: chroot /etc/resolv.conf is pointing outside chroot\n"); | ||
1283 | exit(1); | ||
1284 | } | ||
1285 | } | ||
1286 | free(name); | ||
1287 | #endif | ||
1288 | |||
1289 | // check x11 socket directory | ||
1290 | if (getenv("FIREJAIL_X11")) { | ||
1291 | fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_CLOEXEC); | ||
1292 | if (fd == -1) { | ||
1293 | fprintf(stderr, "Error: cannot open /tmp/.X11-unix in chroot directory\n"); | ||
1294 | exit(1); | ||
1295 | } | ||
1296 | if (fstat(fd, &s) == -1) | ||
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"); | ||
1300 | exit(1); | ||
1301 | } | ||
1302 | close(fd); | ||
1303 | } | ||
1304 | |||
1305 | close(parentfd); | ||
1306 | } | ||
1307 | |||
1308 | // chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf | ||
1309 | void fs_chroot(const char *rootdir) { | ||
1310 | assert(rootdir); | ||
1311 | |||
1312 | // mount-bind a /dev in rootdir | ||
1313 | char *newdev; | ||
1314 | if (asprintf(&newdev, "%s/dev", rootdir) == -1) | ||
1315 | errExit("asprintf"); | ||
1316 | if (arg_debug) | ||
1317 | printf("Mounting /dev on %s\n", newdev); | ||
1318 | if (mount("/dev", newdev, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
1319 | errExit("mounting /dev"); | ||
1320 | free(newdev); | ||
1321 | |||
1322 | // x11 | ||
1323 | if (getenv("FIREJAIL_X11")) { | ||
1324 | char *newx11; | ||
1325 | if (asprintf(&newx11, "%s/tmp/.X11-unix", rootdir) == -1) | ||
1326 | errExit("asprintf"); | ||
1327 | if (arg_debug) | ||
1328 | printf("Mounting /tmp/.X11-unix on %s\n", newx11); | ||
1329 | if (mount("/tmp/.X11-unix", newx11, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
1330 | errExit("mounting /tmp/.X11-unix"); | ||
1331 | free(newx11); | ||
1332 | } | ||
1333 | |||
1334 | // some older distros don't have a /run directory | ||
1335 | // create one by default | ||
1336 | char *rundir; | ||
1337 | if (asprintf(&rundir, "%s/run", rootdir) == -1) | ||
1338 | errExit("asprintf"); | ||
1339 | struct stat s; | ||
1340 | if (lstat(rundir, &s) == 0) { | ||
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); | ||
1359 | } | ||
1360 | free(rundir); | ||
1361 | |||
1362 | // create /run/firejail directory in chroot | ||
1363 | if (asprintf(&rundir, "%s/run/firejail", rootdir) == -1) | ||
1364 | errExit("asprintf"); | ||
1365 | if (mkdir(rundir, 0755) == -1 && errno != EEXIST) | ||
1366 | errExit("mkdir"); | ||
1367 | ASSERT_PERMS(rundir, 0, 0, 0755); | ||
1368 | free(rundir); | ||
1369 | |||
1370 | // create /run/firejail/mnt directory in chroot and mount the current one | ||
1371 | if (asprintf(&rundir, "%s%s", rootdir, RUN_MNT_DIR) == -1) | ||
1372 | errExit("asprintf"); | ||
1373 | if (mkdir(rundir, 0755) == -1 && errno != EEXIST) | ||
1374 | errExit("mkdir"); | ||
1375 | ASSERT_PERMS(rundir, 0, 0, 0755); | ||
1376 | if (mount(RUN_MNT_DIR, rundir, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
1377 | errExit("mount bind"); | ||
1378 | free(rundir); | ||
1379 | |||
1380 | // copy /etc/resolv.conf in chroot directory | ||
1381 | char *fname; | ||
1382 | if (asprintf(&fname, "%s/etc/resolv.conf", rootdir) == -1) | ||
1383 | errExit("asprintf"); | ||
1384 | if (arg_debug) | ||
1385 | printf("Updating /etc/resolv.conf in %s\n", fname); | ||
1386 | unlink(fname); | ||
1387 | if (copy_file("/etc/resolv.conf", fname, 0, 0, 0644) == -1) // root needed | ||
1388 | fwarning("/etc/resolv.conf not initialized\n"); | ||
1389 | free(fname); | ||
1390 | |||
1391 | // chroot into the new directory | ||
1392 | #ifdef HAVE_GCOV | ||
1393 | __gcov_flush(); | ||
1394 | #endif | ||
1395 | // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay | ||
1396 | // and chroot into this new directory | ||
1397 | if (arg_debug) | ||
1398 | printf("Chrooting into %s\n", rootdir); | ||
1399 | char *oroot = RUN_OVERLAY_ROOT; | ||
1400 | if (mkdir(oroot, 0755) == -1) | ||
1401 | errExit("mkdir"); | ||
1402 | if (mount(rootdir, oroot, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
1403 | errExit("mounting rootdir oroot"); | ||
1404 | if (chroot(oroot) < 0) | ||
1405 | errExit("chroot"); | ||
1406 | |||
1407 | // create all other /run/firejail files and directories | ||
1408 | preproc_build_firejail_dir(); | ||
1409 | |||
1410 | // update /var directory in order to support multiple sandboxes running on the same root directory | ||
1411 | // if (!arg_private_dev) | ||
1412 | // fs_dev_shm(); | ||
1413 | fs_var_lock(); | ||
1414 | if (!arg_keep_var_tmp) | ||
1415 | fs_var_tmp(); | ||
1416 | if (!arg_writable_var_log) | ||
1417 | fs_var_log(); | ||
1418 | |||
1419 | fs_var_lib(); | ||
1420 | fs_var_cache(); | ||
1421 | fs_var_utmp(); | ||
1422 | fs_machineid(); | ||
1423 | |||
1424 | // don't leak user information | ||
1425 | restrict_users(); | ||
1426 | |||
1427 | // when starting as root, firejail config is not disabled; | ||
1428 | if (getuid() != 0) | ||
1429 | disable_config(); | ||
1430 | } | ||
1431 | #endif | ||
1432 | #endif // LTS | ||
1433 | 783 | ||
1434 | // this function is called from sandbox.c before blacklist/whitelist functions | 784 | // this function is called from sandbox.c before blacklist/whitelist functions |
1435 | void fs_private_tmp(void) { | 785 | void fs_private_tmp(void) { |
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c index 8e8739436..fc6ac525e 100644 --- a/src/firejail/fs_etc.c +++ b/src/firejail/fs_etc.c | |||
@@ -74,115 +74,3 @@ void fs_machineid(void) { | |||
74 | } | 74 | } |
75 | } | 75 | } |
76 | 76 | ||
77 | #ifndef LTS | ||
78 | // return 0 if file not found, 1 if found | ||
79 | static int check_dir_or_file(const char *fname) { | ||
80 | assert(fname); | ||
81 | |||
82 | struct stat s; | ||
83 | if (stat(fname, &s) == -1) { | ||
84 | if (arg_debug) | ||
85 | fwarning("file %s not found.\n", fname); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | // read access | ||
90 | if (access(fname, R_OK) == -1) | ||
91 | goto errexit; | ||
92 | |||
93 | // dir or regular file | ||
94 | if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode) || !is_link(fname)) | ||
95 | return 1; // normal exit | ||
96 | |||
97 | errexit: | ||
98 | fprintf(stderr, "Error: invalid file type, %s.\n", fname); | ||
99 | exit(1); | ||
100 | } | ||
101 | |||
102 | static void duplicate(const char *fname, const char *private_dir, const char *private_run_dir) { | ||
103 | assert(fname); | ||
104 | |||
105 | if (*fname == '~' || strchr(fname, '/') || strcmp(fname, "..") == 0) { | ||
106 | fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname); | ||
107 | exit(1); | ||
108 | } | ||
109 | invalid_filename(fname, 0); // no globbing | ||
110 | |||
111 | char *src; | ||
112 | if (asprintf(&src, "%s/%s", private_dir, fname) == -1) | ||
113 | errExit("asprintf"); | ||
114 | if (check_dir_or_file(src) == 0) { | ||
115 | fwarning("skipping %s for private %s\n", fname, private_dir); | ||
116 | free(src); | ||
117 | return; | ||
118 | } | ||
119 | |||
120 | if (arg_debug) | ||
121 | printf("copying %s to private %s\n", src, private_dir); | ||
122 | |||
123 | struct stat s; | ||
124 | if (stat(src, &s) == 0 && S_ISDIR(s.st_mode)) { | ||
125 | // create the directory in RUN_ETC_DIR | ||
126 | char *dirname; | ||
127 | if (asprintf(&dirname, "%s/%s", private_run_dir, fname) == -1) | ||
128 | errExit("asprintf"); | ||
129 | create_empty_dir_as_root(dirname, s.st_mode); | ||
130 | sbox_run(SBOX_ROOT| SBOX_SECCOMP, 3, PATH_FCOPY, src, dirname); | ||
131 | free(dirname); | ||
132 | } | ||
133 | else | ||
134 | sbox_run(SBOX_ROOT| SBOX_SECCOMP, 3, PATH_FCOPY, src, private_run_dir); | ||
135 | |||
136 | fs_logger2("clone", src); | ||
137 | free(src); | ||
138 | } | ||
139 | |||
140 | |||
141 | void fs_private_dir_list(const char *private_dir, const char *private_run_dir, const char *private_list) { | ||
142 | assert(private_dir); | ||
143 | assert(private_run_dir); | ||
144 | assert(private_list); | ||
145 | |||
146 | timetrace_start(); | ||
147 | |||
148 | // create /run/firejail/mnt/etc directory | ||
149 | mkdir_attr(private_run_dir, 0755, 0, 0); | ||
150 | fs_logger2("tmpfs", private_dir); | ||
151 | |||
152 | fs_logger_print(); // save the current log | ||
153 | |||
154 | |||
155 | // copy the list of files in the new etc directory | ||
156 | // using a new child process with root privileges | ||
157 | if (*private_list != '\0') { | ||
158 | if (arg_debug) | ||
159 | printf("Copying files in the new %s directory:\n", private_dir); | ||
160 | |||
161 | // copy the list of files in the new home directory | ||
162 | char *dlist = strdup(private_list); | ||
163 | if (!dlist) | ||
164 | errExit("strdup"); | ||
165 | |||
166 | |||
167 | char *ptr = strtok(dlist, ","); | ||
168 | if (!ptr) { | ||
169 | fprintf(stderr, "Error: invalid private %s argument\n", private_dir); | ||
170 | exit(1); | ||
171 | } | ||
172 | duplicate(ptr, private_dir, private_run_dir); | ||
173 | |||
174 | while ((ptr = strtok(NULL, ",")) != NULL) | ||
175 | duplicate(ptr, private_dir, private_run_dir); | ||
176 | free(dlist); | ||
177 | fs_logger_print(); | ||
178 | } | ||
179 | |||
180 | if (arg_debug) | ||
181 | printf("Mount-bind %s on top of %s\n", private_run_dir, private_dir); | ||
182 | if (mount(private_run_dir, private_dir, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
183 | errExit("mount bind"); | ||
184 | fs_logger2("mount", private_dir); | ||
185 | |||
186 | fmessage("Private %s installed in %0.2f ms\n", private_dir, timetrace_end()); | ||
187 | } | ||
188 | #endif | ||
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 866b750b0..1ac03e4dc 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -366,175 +366,3 @@ void fs_check_private_dir(void) { | |||
366 | } | 366 | } |
367 | } | 367 | } |
368 | 368 | ||
369 | //*********************************************************************************** | ||
370 | // --private-home | ||
371 | //*********************************************************************************** | ||
372 | #ifndef LTS | ||
373 | static char *check_dir_or_file(const char *name) { | ||
374 | assert(name); | ||
375 | |||
376 | // basic checks | ||
377 | invalid_filename(name, 0); // no globbing | ||
378 | if (arg_debug) | ||
379 | printf("Private home: checking %s\n", name); | ||
380 | |||
381 | // expand home directory | ||
382 | char *fname = expand_home(name, cfg.homedir); | ||
383 | assert(fname); | ||
384 | |||
385 | // If it doesn't start with '/', it must be relative to homedir | ||
386 | if (fname[0] != '/') { | ||
387 | char* tmp; | ||
388 | if (asprintf(&tmp, "%s/%s", cfg.homedir, fname) == -1) | ||
389 | errExit("asprintf"); | ||
390 | free(fname); | ||
391 | fname = tmp; | ||
392 | } | ||
393 | |||
394 | // we allow only files in user home directory or symbolic links to files or directories owned by the user | ||
395 | struct stat s; | ||
396 | if (lstat(fname, &s) == 0 && S_ISLNK(s.st_mode)) { | ||
397 | if (stat(fname, &s) == 0) { | ||
398 | if (s.st_uid != getuid()) { | ||
399 | fprintf(stderr, "Error: symbolic link %s to file or directory not owned by the user\n", fname); | ||
400 | exit(1); | ||
401 | } | ||
402 | return fname; | ||
403 | } | ||
404 | else // dangling link | ||
405 | goto errexit; | ||
406 | } | ||
407 | else { | ||
408 | // check the file is in user home directory, a full home directory is not allowed | ||
409 | char *rname = realpath(fname, NULL); | ||
410 | if (!rname || | ||
411 | strncmp(rname, cfg.homedir, strlen(cfg.homedir)) != 0 || | ||
412 | strcmp(rname, cfg.homedir) == 0) | ||
413 | goto errexit; | ||
414 | |||
415 | // only top files and directories in user home are allowed | ||
416 | char *ptr = rname + strlen(cfg.homedir); | ||
417 | if (*ptr != '/') | ||
418 | goto errexit; | ||
419 | ptr = strchr(++ptr, '/'); | ||
420 | if (ptr) { | ||
421 | fprintf(stderr, "Error: only top files and directories in user home are allowed\n"); | ||
422 | exit(1); | ||
423 | } | ||
424 | free(fname); | ||
425 | return rname; | ||
426 | } | ||
427 | |||
428 | errexit: | ||
429 | fprintf(stderr, "Error: invalid file %s\n", name); | ||
430 | exit(1); | ||
431 | } | ||
432 | |||
433 | static void duplicate(char *name) { | ||
434 | char *fname = check_dir_or_file(name); | ||
435 | |||
436 | if (arg_debug) | ||
437 | printf("Private home: duplicating %s\n", fname); | ||
438 | assert(strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0); | ||
439 | |||
440 | struct stat s; | ||
441 | if (lstat(fname, &s) == -1) { | ||
442 | free(fname); | ||
443 | return; | ||
444 | } | ||
445 | else if (S_ISDIR(s.st_mode)) { | ||
446 | // create the directory in RUN_HOME_DIR | ||
447 | char *name; | ||
448 | char *ptr = strrchr(fname, '/'); | ||
449 | ptr++; | ||
450 | if (asprintf(&name, "%s/%s", RUN_HOME_DIR, ptr) == -1) | ||
451 | errExit("asprintf"); | ||
452 | mkdir_attr(name, 0755, getuid(), getgid()); | ||
453 | sbox_run(SBOX_USER| SBOX_CAPS_NONE | SBOX_SECCOMP, 3, PATH_FCOPY, fname, name); | ||
454 | free(name); | ||
455 | } | ||
456 | else | ||
457 | sbox_run(SBOX_USER| SBOX_CAPS_NONE | SBOX_SECCOMP, 3, PATH_FCOPY, fname, RUN_HOME_DIR); | ||
458 | fs_logger2("clone", fname); | ||
459 | fs_logger_print(); // save the current log | ||
460 | |||
461 | free(fname); | ||
462 | } | ||
463 | |||
464 | // private mode (--private-home=list): | ||
465 | // mount homedir on top of /home/user, | ||
466 | // tmpfs on top of /root in nonroot mode, | ||
467 | // tmpfs on top of /tmp in root mode, | ||
468 | // set skel files, | ||
469 | // restore .Xauthority | ||
470 | void fs_private_home_list(void) { | ||
471 | timetrace_start(); | ||
472 | |||
473 | char *homedir = cfg.homedir; | ||
474 | char *private_list = cfg.home_private_keep; | ||
475 | assert(homedir); | ||
476 | assert(private_list); | ||
477 | |||
478 | int xflag = store_xauthority(); | ||
479 | int aflag = store_asoundrc(); | ||
480 | |||
481 | uid_t uid = getuid(); | ||
482 | gid_t gid = getgid(); | ||
483 | |||
484 | // create /run/firejail/mnt/home directory | ||
485 | mkdir_attr(RUN_HOME_DIR, 0755, uid, gid); | ||
486 | fs_logger_print(); // save the current log | ||
487 | |||
488 | if (arg_debug) | ||
489 | printf("Copying files in the new home:\n"); | ||
490 | |||
491 | // copy the list of files in the new home directory | ||
492 | char *dlist = strdup(cfg.home_private_keep); | ||
493 | if (!dlist) | ||
494 | errExit("strdup"); | ||
495 | |||
496 | char *ptr = strtok(dlist, ","); | ||
497 | if (!ptr) { | ||
498 | fprintf(stderr, "Error: invalid private-home argument\n"); | ||
499 | exit(1); | ||
500 | } | ||
501 | duplicate(ptr); | ||
502 | while ((ptr = strtok(NULL, ",")) != NULL) | ||
503 | duplicate(ptr); | ||
504 | |||
505 | fs_logger_print(); // save the current log | ||
506 | free(dlist); | ||
507 | |||
508 | if (arg_debug) | ||
509 | printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir); | ||
510 | |||
511 | if (mount(RUN_HOME_DIR, homedir, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
512 | errExit("mount bind"); | ||
513 | fs_logger2("tmpfs", homedir); | ||
514 | |||
515 | if (uid != 0) { | ||
516 | // mask /root | ||
517 | if (arg_debug) | ||
518 | printf("Mounting a new /root directory\n"); | ||
519 | if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) | ||
520 | errExit("mounting home directory"); | ||
521 | } | ||
522 | else { | ||
523 | // mask /home | ||
524 | if (arg_debug) | ||
525 | printf("Mounting a new /home directory\n"); | ||
526 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | ||
527 | errExit("mounting home directory"); | ||
528 | } | ||
529 | |||
530 | skel(homedir, uid, gid); | ||
531 | if (xflag) | ||
532 | copy_xauthority(); | ||
533 | if (aflag) | ||
534 | copy_asoundrc(); | ||
535 | |||
536 | if (!arg_quiet) | ||
537 | fprintf(stderr, "Home directory installed in %0.2f ms\n", timetrace_end()); | ||
538 | |||
539 | } | ||
540 | #endif //LTS \ No newline at end of file | ||
diff --git a/src/firejail/fs_trace.c b/src/firejail/fs_trace.c index e1320433c..2668c8429 100644 --- a/src/firejail/fs_trace.c +++ b/src/firejail/fs_trace.c | |||
@@ -53,15 +53,6 @@ void fs_trace(void) { | |||
53 | errExit("fopen"); | 53 | errExit("fopen"); |
54 | const char *prefix = LIBDIR "/firejail"; | 54 | const char *prefix = LIBDIR "/firejail"; |
55 | 55 | ||
56 | #ifndef LTS | ||
57 | if (arg_trace) { | ||
58 | fprintf(fp, "%s/libtrace.so\n", prefix); | ||
59 | } | ||
60 | else if (arg_tracelog) { | ||
61 | fprintf(fp, "%s/libtracelog.so\n", prefix); | ||
62 | fmessage("Blacklist violations are logged to syslog\n"); | ||
63 | } | ||
64 | #endif | ||
65 | if (arg_seccomp_postexec) { | 56 | if (arg_seccomp_postexec) { |
66 | fprintf(fp, "%s/libpostexecseccomp.so\n", prefix); | 57 | fprintf(fp, "%s/libpostexecseccomp.so\n", prefix); |
67 | fmessage("Post-exec seccomp protector enabled\n"); | 58 | fmessage("Post-exec seccomp protector enabled\n"); |
diff --git a/src/firejail/join.c b/src/firejail/join.c index bf421d5d1..852796c20 100644 --- a/src/firejail/join.c +++ b/src/firejail/join.c | |||
@@ -121,21 +121,6 @@ static void extract_cpu(pid_t pid) { | |||
121 | free(fname); | 121 | free(fname); |
122 | } | 122 | } |
123 | 123 | ||
124 | #ifndef LTS | ||
125 | static void extract_cgroup(pid_t pid) { | ||
126 | char *fname; | ||
127 | if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_CGROUP_CFG) == -1) | ||
128 | errExit("asprintf"); | ||
129 | |||
130 | struct stat s; | ||
131 | if (stat(fname, &s) == -1) | ||
132 | return; | ||
133 | |||
134 | // there is a cgroup file CGROUP_CFG, load it! | ||
135 | load_cgroup(fname); | ||
136 | free(fname); | ||
137 | } | ||
138 | #endif | ||
139 | 124 | ||
140 | static void extract_caps_seccomp(pid_t pid) { | 125 | static void extract_caps_seccomp(pid_t pid) { |
141 | // open stat file | 126 | // open stat file |
@@ -289,19 +274,10 @@ void join(pid_t pid, int argc, char **argv, int index) { | |||
289 | if (getuid() != 0) { | 274 | if (getuid() != 0) { |
290 | extract_caps_seccomp(pid); | 275 | extract_caps_seccomp(pid); |
291 | extract_cpu(pid); | 276 | extract_cpu(pid); |
292 | #ifndef LTS | ||
293 | extract_cgroup(pid); | ||
294 | #endif | ||
295 | extract_nogroups(pid); | 277 | extract_nogroups(pid); |
296 | extract_user_namespace(pid); | 278 | extract_user_namespace(pid); |
297 | } | 279 | } |
298 | 280 | ||
299 | #ifndef LTS | ||
300 | // set cgroup | ||
301 | if (cfg.cgroup) // not available for uid 0 | ||
302 | set_cgroup(cfg.cgroup); | ||
303 | #endif | ||
304 | |||
305 | // get umask, it will be set by start_application() | 281 | // get umask, it will be set by start_application() |
306 | extract_umask(pid); | 282 | extract_umask(pid); |
307 | 283 | ||
diff --git a/src/firejail/main.c b/src/firejail/main.c index d35d8c207..6709d6a6c 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -66,10 +66,6 @@ int arg_caps_drop_all = 0; // drop all capabilities | |||
66 | int arg_caps_keep = 0; // keep list | 66 | int arg_caps_keep = 0; // keep list |
67 | char *arg_caps_list = NULL; // optional caps list | 67 | char *arg_caps_list = NULL; // optional caps list |
68 | 68 | ||
69 | #ifndef LTS | ||
70 | int arg_trace = 0; // syscall tracing support | ||
71 | int arg_tracelog = 0; // blacklist tracing support | ||
72 | #endif | ||
73 | int arg_rlimit_cpu = 0; // rlimit max cpu time | 69 | int arg_rlimit_cpu = 0; // rlimit max cpu time |
74 | int arg_rlimit_nofile = 0; // rlimit nofile | 70 | int arg_rlimit_nofile = 0; // rlimit nofile |
75 | int arg_rlimit_nproc = 0; // rlimit nproc | 71 | int arg_rlimit_nproc = 0; // rlimit nproc |
@@ -344,42 +340,6 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
344 | exit(0); | 340 | exit(0); |
345 | } | 341 | } |
346 | #endif | 342 | #endif |
347 | #ifndef LTS | ||
348 | #ifdef HAVE_X11 | ||
349 | else if (strcmp(argv[i], "--x11") == 0) { | ||
350 | if (checkcfg(CFG_X11)) { | ||
351 | x11_start(argc, argv); | ||
352 | exit(0); | ||
353 | } | ||
354 | else | ||
355 | exit_err_feature("x11"); | ||
356 | } | ||
357 | else if (strcmp(argv[i], "--x11=xpra") == 0) { | ||
358 | if (checkcfg(CFG_X11)) { | ||
359 | x11_start_xpra(argc, argv); | ||
360 | exit(0); | ||
361 | } | ||
362 | else | ||
363 | exit_err_feature("x11"); | ||
364 | } | ||
365 | else if (strcmp(argv[i], "--x11=xephyr") == 0) { | ||
366 | if (checkcfg(CFG_X11)) { | ||
367 | x11_start_xephyr(argc, argv); | ||
368 | exit(0); | ||
369 | } | ||
370 | else | ||
371 | exit_err_feature("x11"); | ||
372 | } | ||
373 | else if (strcmp(argv[i], "--x11=xvfb") == 0) { | ||
374 | if (checkcfg(CFG_X11)) { | ||
375 | x11_start_xvfb(argc, argv); | ||
376 | exit(0); | ||
377 | } | ||
378 | else | ||
379 | exit_err_feature("x11"); | ||
380 | } | ||
381 | #endif | ||
382 | #endif // LTS | ||
383 | #ifdef HAVE_NETWORK | 343 | #ifdef HAVE_NETWORK |
384 | else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { | 344 | else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { |
385 | if (checkcfg(CFG_NETWORK)) { | 345 | if (checkcfg(CFG_NETWORK)) { |
@@ -598,88 +558,6 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
598 | exit_err_feature("networking"); | 558 | exit_err_feature("networking"); |
599 | } | 559 | } |
600 | #endif | 560 | #endif |
601 | #ifndef LTS | ||
602 | #ifdef HAVE_FILE_TRANSFER | ||
603 | else if (strncmp(argv[i], "--get=", 6) == 0) { | ||
604 | if (checkcfg(CFG_FILE_TRANSFER)) { | ||
605 | logargs(argc, argv); | ||
606 | |||
607 | // verify path | ||
608 | if ((i + 2) != argc) { | ||
609 | fprintf(stderr, "Error: invalid --get option, path expected\n"); | ||
610 | exit(1); | ||
611 | } | ||
612 | char *path = argv[i + 1]; | ||
613 | invalid_filename(path, 0); // no globbing | ||
614 | if (strstr(path, "..")) { | ||
615 | fprintf(stderr, "Error: invalid file name %s\n", path); | ||
616 | exit(1); | ||
617 | } | ||
618 | |||
619 | // get file | ||
620 | pid_t pid = require_pid(argv[i] + 6); | ||
621 | sandboxfs(SANDBOX_FS_GET, pid, path, NULL); | ||
622 | exit(0); | ||
623 | } | ||
624 | else | ||
625 | exit_err_feature("file transfer"); | ||
626 | } | ||
627 | else if (strncmp(argv[i], "--put=", 6) == 0) { | ||
628 | if (checkcfg(CFG_FILE_TRANSFER)) { | ||
629 | logargs(argc, argv); | ||
630 | |||
631 | // verify path | ||
632 | if ((i + 3) != argc) { | ||
633 | fprintf(stderr, "Error: invalid --put option, 2 paths expected\n"); | ||
634 | exit(1); | ||
635 | } | ||
636 | char *path1 = argv[i + 1]; | ||
637 | invalid_filename(path1, 0); // no globbing | ||
638 | if (strstr(path1, "..")) { | ||
639 | fprintf(stderr, "Error: invalid file name %s\n", path1); | ||
640 | exit(1); | ||
641 | } | ||
642 | char *path2 = argv[i + 2]; | ||
643 | invalid_filename(path2, 0); // no globbing | ||
644 | if (strstr(path2, "..")) { | ||
645 | fprintf(stderr, "Error: invalid file name %s\n", path2); | ||
646 | exit(1); | ||
647 | } | ||
648 | |||
649 | // get file | ||
650 | pid_t pid = require_pid(argv[i] + 6); | ||
651 | sandboxfs(SANDBOX_FS_PUT, pid, path1, path2); | ||
652 | exit(0); | ||
653 | } | ||
654 | else | ||
655 | exit_err_feature("file transfer"); | ||
656 | } | ||
657 | else if (strncmp(argv[i], "--ls=", 5) == 0) { | ||
658 | if (checkcfg(CFG_FILE_TRANSFER)) { | ||
659 | logargs(argc, argv); | ||
660 | |||
661 | // verify path | ||
662 | if ((i + 2) != argc) { | ||
663 | fprintf(stderr, "Error: invalid --ls option, path expected\n"); | ||
664 | exit(1); | ||
665 | } | ||
666 | char *path = argv[i + 1]; | ||
667 | invalid_filename(path, 0); // no globbing | ||
668 | if (strstr(path, "..")) { | ||
669 | fprintf(stderr, "Error: invalid file name %s\n", path); | ||
670 | exit(1); | ||
671 | } | ||
672 | |||
673 | // list directory contents | ||
674 | pid_t pid = require_pid(argv[i] + 5); | ||
675 | sandboxfs(SANDBOX_FS_LS, pid, path, NULL); | ||
676 | exit(0); | ||
677 | } | ||
678 | else | ||
679 | exit_err_feature("file transfer"); | ||
680 | } | ||
681 | #endif | ||
682 | #endif | ||
683 | else if (strncmp(argv[i], "--join=", 7) == 0) { | 561 | else if (strncmp(argv[i], "--join=", 7) == 0) { |
684 | if (checkcfg(CFG_JOIN) || getuid() == 0) { | 562 | if (checkcfg(CFG_JOIN) || getuid() == 0) { |
685 | logargs(argc, argv); | 563 | logargs(argc, argv); |
@@ -831,29 +709,6 @@ static int check_arg(int argc, char **argv, const char *argument, int strict) { | |||
831 | return found; | 709 | return found; |
832 | } | 710 | } |
833 | 711 | ||
834 | #ifndef LTS | ||
835 | static void run_builder(int argc, char **argv) { | ||
836 | EUID_ASSERT(); | ||
837 | (void) argc; | ||
838 | |||
839 | // drop privileges | ||
840 | EUID_ROOT(); | ||
841 | if (setgid(getgid()) < 0) | ||
842 | errExit("setgid/getgid"); | ||
843 | if (setuid(getuid()) < 0) | ||
844 | errExit("setuid/getuid"); | ||
845 | |||
846 | assert(getenv("LD_PRELOAD") == NULL); | ||
847 | umask(orig_umask); | ||
848 | |||
849 | argv[0] = LIBDIR "/firejail/fbuilder"; | ||
850 | execvp(argv[0], argv); | ||
851 | |||
852 | perror("execvp"); | ||
853 | exit(1); | ||
854 | } | ||
855 | #endif // LTS | ||
856 | |||
857 | //******************************************* | 712 | //******************************************* |
858 | // Main program | 713 | // Main program |
859 | //******************************************* | 714 | //******************************************* |
@@ -930,11 +785,6 @@ int main(int argc, char **argv) { | |||
930 | profile_add(cmd); | 785 | profile_add(cmd); |
931 | } | 786 | } |
932 | 787 | ||
933 | #ifndef LTS | ||
934 | // profile builder | ||
935 | if (check_arg(argc, argv, "--build", 0)) // supports both --build and --build=filename | ||
936 | run_builder(argc, argv); // this function will not return | ||
937 | #endif // LTS | ||
938 | // check argv[0] symlink wrapper if this is not a login shell | 788 | // check argv[0] symlink wrapper if this is not a login shell |
939 | if (*argv[0] != '-') | 789 | if (*argv[0] != '-') |
940 | run_symlink(argc, argv, 0); // if symlink detected, this function will not return | 790 | run_symlink(argc, argv, 0); // if symlink detected, this function will not return |
@@ -967,113 +817,6 @@ int main(int argc, char **argv) { | |||
967 | delete_run_files(sandbox_pid); | 817 | delete_run_files(sandbox_pid); |
968 | EUID_USER(); | 818 | EUID_USER(); |
969 | 819 | ||
970 | #ifndef LTS | ||
971 | //check if the parent is sshd daemon | ||
972 | int parent_sshd = 0; | ||
973 | { | ||
974 | pid_t ppid = getppid(); | ||
975 | EUID_ROOT(); | ||
976 | char *comm = pid_proc_comm(ppid); | ||
977 | EUID_USER(); | ||
978 | if (comm) { | ||
979 | if (strcmp(comm, "sshd") == 0) { | ||
980 | arg_quiet = 1; | ||
981 | parent_sshd = 1; | ||
982 | |||
983 | #ifdef DEBUG_RESTRICTED_SHELL | ||
984 | {EUID_ROOT(); | ||
985 | FILE *fp = fopen("/firelog", "w"); | ||
986 | if (fp) { | ||
987 | int i; | ||
988 | fprintf(fp, "argc %d: ", argc); | ||
989 | for (i = 0; i < argc; i++) | ||
990 | fprintf(fp, "#%s# ", argv[i]); | ||
991 | fprintf(fp, "\n"); | ||
992 | fclose(fp); | ||
993 | } | ||
994 | EUID_USER();} | ||
995 | #endif | ||
996 | // run sftp and scp directly without any sandboxing | ||
997 | // regular login has argv[0] == "-firejail" | ||
998 | if (*argv[0] != '-') { | ||
999 | if (strcmp(argv[1], "-c") == 0 && argc > 2) { | ||
1000 | if (strcmp(argv[2], "/usr/lib/openssh/sftp-server") == 0 || | ||
1001 | strncmp(argv[2], "scp ", 4) == 0) { | ||
1002 | #ifdef DEBUG_RESTRICTED_SHELL | ||
1003 | {EUID_ROOT(); | ||
1004 | FILE *fp = fopen("/firelog", "a"); | ||
1005 | if (fp) { | ||
1006 | fprintf(fp, "run without a sandbox\n"); | ||
1007 | fclose(fp); | ||
1008 | } | ||
1009 | EUID_USER();} | ||
1010 | #endif | ||
1011 | |||
1012 | drop_privs(1); | ||
1013 | umask(orig_umask); | ||
1014 | int rv = system(argv[2]); | ||
1015 | exit(rv); | ||
1016 | } | ||
1017 | } | ||
1018 | } | ||
1019 | } | ||
1020 | free(comm); | ||
1021 | } | ||
1022 | } | ||
1023 | EUID_ASSERT(); | ||
1024 | |||
1025 | // is this a login shell, or a command passed by sshd, insert command line options from /etc/firejail/login.users | ||
1026 | if (*argv[0] == '-' || parent_sshd) { | ||
1027 | if (argc == 1) | ||
1028 | login_shell = 1; | ||
1029 | fullargc = restricted_shell(cfg.username); | ||
1030 | if (fullargc) { | ||
1031 | |||
1032 | #ifdef DEBUG_RESTRICTED_SHELL | ||
1033 | {EUID_ROOT(); | ||
1034 | FILE *fp = fopen("/firelog", "a"); | ||
1035 | if (fp) { | ||
1036 | fprintf(fp, "fullargc %d: ", fullargc); | ||
1037 | int i; | ||
1038 | for (i = 0; i < fullargc; i++) | ||
1039 | fprintf(fp, "#%s# ", fullargv[i]); | ||
1040 | fprintf(fp, "\n"); | ||
1041 | fclose(fp); | ||
1042 | } | ||
1043 | EUID_USER();} | ||
1044 | #endif | ||
1045 | |||
1046 | int j; | ||
1047 | for (i = 1, j = fullargc; i < argc && j < MAX_ARGS; i++, j++, fullargc++) | ||
1048 | fullargv[j] = argv[i]; | ||
1049 | |||
1050 | // replace argc/argv with fullargc/fullargv | ||
1051 | argv = fullargv; | ||
1052 | argc = j; | ||
1053 | |||
1054 | #ifdef DEBUG_RESTRICTED_SHELL | ||
1055 | {EUID_ROOT(); | ||
1056 | FILE *fp = fopen("/firelog", "a"); | ||
1057 | if (fp) { | ||
1058 | fprintf(fp, "argc %d: ", argc); | ||
1059 | int i; | ||
1060 | for (i = 0; i < argc; i++) | ||
1061 | fprintf(fp, "#%s# ", argv[i]); | ||
1062 | fprintf(fp, "\n"); | ||
1063 | fclose(fp); | ||
1064 | } | ||
1065 | EUID_USER();} | ||
1066 | #endif | ||
1067 | } | ||
1068 | } | ||
1069 | else { | ||
1070 | // check --output option and execute it; | ||
1071 | check_output(argc, argv); // the function will not return if --output or --output-stderr option was found | ||
1072 | } | ||
1073 | #endif // LTS | ||
1074 | EUID_ASSERT(); | ||
1075 | |||
1076 | |||
1077 | // check for force-nonewprivs in /etc/firejail/firejail.config file | 820 | // check for force-nonewprivs in /etc/firejail/firejail.config file |
1078 | if (checkcfg(CFG_FORCE_NONEWPRIVS)) | 821 | if (checkcfg(CFG_FORCE_NONEWPRIVS)) |
1079 | arg_nonewprivs = 1; | 822 | arg_nonewprivs = 1; |
@@ -1224,42 +967,6 @@ int main(int argc, char **argv) { | |||
1224 | } | 967 | } |
1225 | 968 | ||
1226 | 969 | ||
1227 | #ifndef LTS | ||
1228 | else if (strcmp(argv[i], "--trace") == 0) | ||
1229 | arg_trace = 1; | ||
1230 | else if (strcmp(argv[i], "--tracelog") == 0) | ||
1231 | arg_tracelog = 1; | ||
1232 | else if (strncmp(argv[i], "--rlimit-cpu=", 13) == 0) { | ||
1233 | check_unsigned(argv[i] + 13, "Error: invalid rlimit"); | ||
1234 | sscanf(argv[i] + 13, "%llu", &cfg.rlimit_cpu); | ||
1235 | arg_rlimit_cpu = 1; | ||
1236 | } | ||
1237 | else if (strncmp(argv[i], "--rlimit-nofile=", 16) == 0) { | ||
1238 | check_unsigned(argv[i] + 16, "Error: invalid rlimit"); | ||
1239 | sscanf(argv[i] + 16, "%llu", &cfg.rlimit_nofile); | ||
1240 | arg_rlimit_nofile = 1; | ||
1241 | } | ||
1242 | else if (strncmp(argv[i], "--rlimit-nproc=", 15) == 0) { | ||
1243 | check_unsigned(argv[i] + 15, "Error: invalid rlimit"); | ||
1244 | sscanf(argv[i] + 15, "%llu", &cfg.rlimit_nproc); | ||
1245 | arg_rlimit_nproc = 1; | ||
1246 | } | ||
1247 | else if (strncmp(argv[i], "--rlimit-fsize=", 15) == 0) { | ||
1248 | check_unsigned(argv[i] + 15, "Error: invalid rlimit"); | ||
1249 | sscanf(argv[i] + 15, "%llu", &cfg.rlimit_fsize); | ||
1250 | arg_rlimit_fsize = 1; | ||
1251 | } | ||
1252 | else if (strncmp(argv[i], "--rlimit-sigpending=", 20) == 0) { | ||
1253 | check_unsigned(argv[i] + 20, "Error: invalid rlimit"); | ||
1254 | sscanf(argv[i] + 20, "%llu", &cfg.rlimit_sigpending); | ||
1255 | arg_rlimit_sigpending = 1; | ||
1256 | } | ||
1257 | else if (strncmp(argv[i], "--rlimit-as=", 12) == 0) { | ||
1258 | check_unsigned(argv[i] + 12, "Error: invalid rlimit"); | ||
1259 | sscanf(argv[i] + 12, "%llu", &cfg.rlimit_as); | ||
1260 | arg_rlimit_as = 1; | ||
1261 | } | ||
1262 | #endif | ||
1263 | else if (strncmp(argv[i], "--ipc-namespace", 15) == 0) | 970 | else if (strncmp(argv[i], "--ipc-namespace", 15) == 0) |
1264 | arg_ipc = 1; | 971 | arg_ipc = 1; |
1265 | else if (strncmp(argv[i], "--cpu=", 6) == 0) | 972 | else if (strncmp(argv[i], "--cpu=", 6) == 0) |
@@ -1270,20 +977,6 @@ int main(int argc, char **argv) { | |||
1270 | cfg.nice = 0; | 977 | cfg.nice = 0; |
1271 | arg_nice = 1; | 978 | arg_nice = 1; |
1272 | } | 979 | } |
1273 | #ifndef LTS | ||
1274 | else if (strncmp(argv[i], "--cgroup=", 9) == 0) { | ||
1275 | if (option_cgroup) { | ||
1276 | fprintf(stderr, "Error: only a cgroup can be defined\n"); | ||
1277 | exit(1); | ||
1278 | } | ||
1279 | |||
1280 | option_cgroup = 1; | ||
1281 | cfg.cgroup = strdup(argv[i] + 9); | ||
1282 | if (!cfg.cgroup) | ||
1283 | errExit("strdup"); | ||
1284 | set_cgroup(cfg.cgroup); | ||
1285 | } | ||
1286 | #endif | ||
1287 | //************************************* | 980 | //************************************* |
1288 | // filesystem | 981 | // filesystem |
1289 | //************************************* | 982 | //************************************* |
@@ -1375,95 +1068,6 @@ int main(int argc, char **argv) { | |||
1375 | } | 1068 | } |
1376 | else if (strcmp(argv[i], "--disable-mnt") == 0) | 1069 | else if (strcmp(argv[i], "--disable-mnt") == 0) |
1377 | arg_disable_mnt = 1; | 1070 | arg_disable_mnt = 1; |
1378 | #ifndef LTS | ||
1379 | #ifdef HAVE_OVERLAYFS | ||
1380 | else if (strcmp(argv[i], "--overlay") == 0) { | ||
1381 | if (checkcfg(CFG_OVERLAYFS)) { | ||
1382 | if (arg_overlay) { | ||
1383 | fprintf(stderr, "Error: only one overlay command is allowed\n"); | ||
1384 | exit(1); | ||
1385 | } | ||
1386 | |||
1387 | if (cfg.chrootdir) { | ||
1388 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); | ||
1389 | exit(1); | ||
1390 | } | ||
1391 | struct stat s; | ||
1392 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
1393 | fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); | ||
1394 | exit(1); | ||
1395 | } | ||
1396 | arg_overlay = 1; | ||
1397 | arg_overlay_keep = 1; | ||
1398 | |||
1399 | char *subdirname; | ||
1400 | if (asprintf(&subdirname, "%d", getpid()) == -1) | ||
1401 | errExit("asprintf"); | ||
1402 | cfg.overlay_dir = fs_check_overlay_dir(subdirname, arg_overlay_reuse); | ||
1403 | |||
1404 | free(subdirname); | ||
1405 | } | ||
1406 | else | ||
1407 | exit_err_feature("overlayfs"); | ||
1408 | } | ||
1409 | else if (strncmp(argv[i], "--overlay-named=", 16) == 0) { | ||
1410 | if (checkcfg(CFG_OVERLAYFS)) { | ||
1411 | if (arg_overlay) { | ||
1412 | fprintf(stderr, "Error: only one overlay command is allowed\n"); | ||
1413 | exit(1); | ||
1414 | } | ||
1415 | if (cfg.chrootdir) { | ||
1416 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); | ||
1417 | exit(1); | ||
1418 | } | ||
1419 | struct stat s; | ||
1420 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
1421 | fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); | ||
1422 | exit(1); | ||
1423 | } | ||
1424 | arg_overlay = 1; | ||
1425 | arg_overlay_keep = 1; | ||
1426 | arg_overlay_reuse = 1; | ||
1427 | |||
1428 | char *subdirname = argv[i] + 16; | ||
1429 | if (*subdirname == '\0') { | ||
1430 | fprintf(stderr, "Error: invalid overlay option\n"); | ||
1431 | exit(1); | ||
1432 | } | ||
1433 | |||
1434 | // check name | ||
1435 | invalid_filename(subdirname, 0); // no globbing | ||
1436 | if (strstr(subdirname, "..") || strstr(subdirname, "/")) { | ||
1437 | fprintf(stderr, "Error: invalid overlay name\n"); | ||
1438 | exit(1); | ||
1439 | } | ||
1440 | cfg.overlay_dir = fs_check_overlay_dir(subdirname, arg_overlay_reuse); | ||
1441 | } | ||
1442 | else | ||
1443 | exit_err_feature("overlayfs"); | ||
1444 | } | ||
1445 | else if (strcmp(argv[i], "--overlay-tmpfs") == 0) { | ||
1446 | if (checkcfg(CFG_OVERLAYFS)) { | ||
1447 | if (arg_overlay) { | ||
1448 | fprintf(stderr, "Error: only one overlay command is allowed\n"); | ||
1449 | exit(1); | ||
1450 | } | ||
1451 | if (cfg.chrootdir) { | ||
1452 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); | ||
1453 | exit(1); | ||
1454 | } | ||
1455 | struct stat s; | ||
1456 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
1457 | fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); | ||
1458 | exit(1); | ||
1459 | } | ||
1460 | arg_overlay = 1; | ||
1461 | } | ||
1462 | else | ||
1463 | exit_err_feature("overlayfs"); | ||
1464 | } | ||
1465 | #endif | ||
1466 | #endif //LTS | ||
1467 | else if (strncmp(argv[i], "--profile=", 10) == 0) { | 1071 | else if (strncmp(argv[i], "--profile=", 10) == 0) { |
1468 | // multiple profile files are allowed! | 1072 | // multiple profile files are allowed! |
1469 | 1073 | ||
@@ -1494,55 +1098,6 @@ int main(int argc, char **argv) { | |||
1494 | } | 1098 | } |
1495 | profile_add_ignore(argv[i] + 9); | 1099 | profile_add_ignore(argv[i] + 9); |
1496 | } | 1100 | } |
1497 | #ifndef LTS | ||
1498 | #ifdef HAVE_CHROOT | ||
1499 | else if (strncmp(argv[i], "--chroot=", 9) == 0) { | ||
1500 | if (checkcfg(CFG_CHROOT)) { | ||
1501 | if (arg_overlay) { | ||
1502 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); | ||
1503 | exit(1); | ||
1504 | } | ||
1505 | |||
1506 | struct stat s; | ||
1507 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
1508 | fprintf(stderr, "Error: --chroot option is not available on Grsecurity systems\n"); | ||
1509 | exit(1); | ||
1510 | } | ||
1511 | |||
1512 | |||
1513 | invalid_filename(argv[i] + 9, 0); // no globbing | ||
1514 | |||
1515 | // extract chroot dirname | ||
1516 | cfg.chrootdir = argv[i] + 9; | ||
1517 | // if the directory starts with ~, expand the home directory | ||
1518 | if (*cfg.chrootdir == '~') { | ||
1519 | char *tmp; | ||
1520 | if (asprintf(&tmp, "%s%s", cfg.homedir, cfg.chrootdir + 1) == -1) | ||
1521 | errExit("asprintf"); | ||
1522 | cfg.chrootdir = tmp; | ||
1523 | } | ||
1524 | |||
1525 | if (strstr(cfg.chrootdir, "..") || is_link(cfg.chrootdir)) { | ||
1526 | fprintf(stderr, "Error: invalid chroot directory %s\n", cfg.chrootdir); | ||
1527 | return 1; | ||
1528 | } | ||
1529 | |||
1530 | // check chroot dirname exists, don't allow "--chroot=/" | ||
1531 | char *rpath = realpath(cfg.chrootdir, NULL); | ||
1532 | if (rpath == NULL || strcmp(rpath, "/") == 0) { | ||
1533 | fprintf(stderr, "Error: invalid chroot directory\n"); | ||
1534 | exit(1); | ||
1535 | } | ||
1536 | cfg.chrootdir = rpath; | ||
1537 | |||
1538 | // check chroot directory structure | ||
1539 | fs_check_chroot_dir(cfg.chrootdir); | ||
1540 | } | ||
1541 | else | ||
1542 | exit_err_feature("chroot"); | ||
1543 | } | ||
1544 | #endif | ||
1545 | #endif // LTS | ||
1546 | else if (strcmp(argv[i], "--writable-etc") == 0) { | 1101 | else if (strcmp(argv[i], "--writable-etc") == 0) { |
1547 | if (cfg.etc_private_keep) { | 1102 | if (cfg.etc_private_keep) { |
1548 | fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); | 1103 | fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); |
@@ -1589,112 +1144,12 @@ int main(int argc, char **argv) { | |||
1589 | } | 1144 | } |
1590 | arg_private = 1; | 1145 | arg_private = 1; |
1591 | } | 1146 | } |
1592 | #ifndef LTS | ||
1593 | #ifdef HAVE_PRIVATE_HOME | ||
1594 | else if (strncmp(argv[i], "--private-home=", 15) == 0) { | ||
1595 | if (checkcfg(CFG_PRIVATE_HOME)) { | ||
1596 | if (cfg.home_private) { | ||
1597 | fprintf(stderr, "Error: a private home directory was already defined with --private option.\n"); | ||
1598 | exit(1); | ||
1599 | } | ||
1600 | |||
1601 | // extract private home dirname | ||
1602 | if (*(argv[i] + 15) == '\0') { | ||
1603 | fprintf(stderr, "Error: invalid private-home option\n"); | ||
1604 | exit(1); | ||
1605 | } | ||
1606 | if (cfg.home_private_keep) { | ||
1607 | if ( asprintf(&cfg.home_private_keep, "%s,%s", cfg.home_private_keep, argv[i] + 15) < 0 ) | ||
1608 | errExit("asprintf"); | ||
1609 | } else | ||
1610 | cfg.home_private_keep = argv[i] + 15; | ||
1611 | arg_private = 1; | ||
1612 | } | ||
1613 | else | ||
1614 | exit_err_feature("private-home"); | ||
1615 | } | ||
1616 | #endif | ||
1617 | #endif //LTS | ||
1618 | else if (strcmp(argv[i], "--private-dev") == 0) { | 1147 | else if (strcmp(argv[i], "--private-dev") == 0) { |
1619 | arg_private_dev = 1; | 1148 | arg_private_dev = 1; |
1620 | } | 1149 | } |
1621 | else if (strcmp(argv[i], "--keep-dev-shm") == 0) { | 1150 | else if (strcmp(argv[i], "--keep-dev-shm") == 0) { |
1622 | arg_keep_dev_shm = 1; | 1151 | arg_keep_dev_shm = 1; |
1623 | } | 1152 | } |
1624 | #ifndef LTS | ||
1625 | else if (strncmp(argv[i], "--private-etc=", 14) == 0) { | ||
1626 | if (arg_writable_etc) { | ||
1627 | fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); | ||
1628 | exit(1); | ||
1629 | } | ||
1630 | |||
1631 | // extract private etc list | ||
1632 | if (*(argv[i] + 14) == '\0') { | ||
1633 | fprintf(stderr, "Error: invalid private-etc option\n"); | ||
1634 | exit(1); | ||
1635 | } | ||
1636 | if (cfg.etc_private_keep) { | ||
1637 | if ( asprintf(&cfg.etc_private_keep, "%s,%s", cfg.etc_private_keep, argv[i] + 14) < 0 ) | ||
1638 | errExit("asprintf"); | ||
1639 | } else | ||
1640 | cfg.etc_private_keep = argv[i] + 14; | ||
1641 | arg_private_etc = 1; | ||
1642 | } | ||
1643 | else if (strncmp(argv[i], "--private-opt=", 14) == 0) { | ||
1644 | // extract private opt list | ||
1645 | if (*(argv[i] + 14) == '\0') { | ||
1646 | fprintf(stderr, "Error: invalid private-opt option\n"); | ||
1647 | exit(1); | ||
1648 | } | ||
1649 | if (cfg.opt_private_keep) { | ||
1650 | if ( asprintf(&cfg.opt_private_keep, "%s,%s", cfg.opt_private_keep, argv[i] + 14) < 0 ) | ||
1651 | errExit("asprintf"); | ||
1652 | } else | ||
1653 | cfg.opt_private_keep = argv[i] + 14; | ||
1654 | arg_private_opt = 1; | ||
1655 | } | ||
1656 | else if (strncmp(argv[i], "--private-srv=", 14) == 0) { | ||
1657 | // extract private srv list | ||
1658 | if (*(argv[i] + 14) == '\0') { | ||
1659 | fprintf(stderr, "Error: invalid private-srv option\n"); | ||
1660 | exit(1); | ||
1661 | } | ||
1662 | if (cfg.srv_private_keep) { | ||
1663 | if ( asprintf(&cfg.srv_private_keep, "%s,%s", cfg.srv_private_keep, argv[i] + 14) < 0 ) | ||
1664 | errExit("asprintf"); | ||
1665 | } else | ||
1666 | cfg.srv_private_keep = argv[i] + 14; | ||
1667 | arg_private_srv = 1; | ||
1668 | } | ||
1669 | else if (strncmp(argv[i], "--private-bin=", 14) == 0) { | ||
1670 | // extract private bin list | ||
1671 | if (*(argv[i] + 14) == '\0') { | ||
1672 | fprintf(stderr, "Error: invalid private-bin option\n"); | ||
1673 | exit(1); | ||
1674 | } | ||
1675 | if (cfg.bin_private_keep) { | ||
1676 | if ( asprintf(&cfg.bin_private_keep, "%s,%s", cfg.bin_private_keep, argv[i] + 14) < 0 ) | ||
1677 | errExit("asprintf"); | ||
1678 | } else | ||
1679 | cfg.bin_private_keep = argv[i] + 14; | ||
1680 | arg_private_bin = 1; | ||
1681 | } | ||
1682 | else if (strncmp(argv[i], "--private-lib", 13) == 0) { | ||
1683 | if (checkcfg(CFG_PRIVATE_LIB)) { | ||
1684 | // extract private lib list (if any) | ||
1685 | if (argv[i][13] == '=') { | ||
1686 | if (cfg.lib_private_keep) { | ||
1687 | if (argv[i][14] != '\0' && asprintf(&cfg.lib_private_keep, "%s,%s", cfg.lib_private_keep, argv[i] + 14) < 0) | ||
1688 | errExit("asprintf"); | ||
1689 | } else | ||
1690 | cfg.lib_private_keep = argv[i] + 14; | ||
1691 | } | ||
1692 | arg_private_lib = 1; | ||
1693 | } | ||
1694 | else | ||
1695 | exit_err_feature("private-lib"); | ||
1696 | } | ||
1697 | #endif // LTS | ||
1698 | else if (strcmp(argv[i], "--private-tmp") == 0) { | 1153 | else if (strcmp(argv[i], "--private-tmp") == 0) { |
1699 | arg_private_tmp = 1; | 1154 | arg_private_tmp = 1; |
1700 | } | 1155 | } |
@@ -2110,28 +1565,6 @@ int main(int argc, char **argv) { | |||
2110 | //************************************* | 1565 | //************************************* |
2111 | else if (strncmp(argv[i], "--timeout=", 10) == 0) | 1566 | else if (strncmp(argv[i], "--timeout=", 10) == 0) |
2112 | cfg.timeout = extract_timeout(argv[i] + 10); | 1567 | cfg.timeout = extract_timeout(argv[i] + 10); |
2113 | #ifndef LTS | ||
2114 | else if (strcmp(argv[i], "--audit") == 0) { | ||
2115 | arg_audit_prog = LIBDIR "/firejail/faudit"; | ||
2116 | arg_audit = 1; | ||
2117 | } | ||
2118 | else if (strncmp(argv[i], "--audit=", 8) == 0) { | ||
2119 | if (strlen(argv[i] + 8) == 0) { | ||
2120 | fprintf(stderr, "Error: invalid audit program\n"); | ||
2121 | exit(1); | ||
2122 | } | ||
2123 | arg_audit_prog = strdup(argv[i] + 8); | ||
2124 | if (!arg_audit_prog) | ||
2125 | errExit("strdup"); | ||
2126 | |||
2127 | struct stat s; | ||
2128 | if (stat(arg_audit_prog, &s) != 0) { | ||
2129 | fprintf(stderr, "Error: cannot find the audit program %s\n", arg_audit_prog); | ||
2130 | exit(1); | ||
2131 | } | ||
2132 | arg_audit = 1; | ||
2133 | } | ||
2134 | #endif // LTS | ||
2135 | else if (strcmp(argv[i], "--appimage") == 0) | 1568 | else if (strcmp(argv[i], "--appimage") == 0) |
2136 | arg_appimage = 1; | 1569 | arg_appimage = 1; |
2137 | else if (strcmp(argv[i], "--shell=none") == 0) { | 1570 | else if (strcmp(argv[i], "--shell=none") == 0) { |
@@ -2255,12 +1688,6 @@ int main(int argc, char **argv) { | |||
2255 | exit(1); | 1688 | exit(1); |
2256 | } | 1689 | } |
2257 | 1690 | ||
2258 | #ifndef LTS | ||
2259 | // check trace configuration | ||
2260 | if (arg_trace && arg_tracelog) { | ||
2261 | fwarning("--trace and --tracelog are mutually exclusive; --tracelog disabled\n"); | ||
2262 | } | ||
2263 | #endif | ||
2264 | // check user namespace (--noroot) options | 1691 | // check user namespace (--noroot) options |
2265 | if (arg_noroot) { | 1692 | if (arg_noroot) { |
2266 | if (arg_overlay) { | 1693 | if (arg_overlay) { |
@@ -2370,11 +1797,6 @@ int main(int argc, char **argv) { | |||
2370 | } | 1797 | } |
2371 | EUID_ASSERT(); | 1798 | EUID_ASSERT(); |
2372 | 1799 | ||
2373 | #ifndef LTS | ||
2374 | // block X11 sockets | ||
2375 | if (arg_x11_block) | ||
2376 | x11_block(); | ||
2377 | #endif //LTS | ||
2378 | // check network configuration options - it will exit if anything went wrong | 1800 | // check network configuration options - it will exit if anything went wrong |
2379 | net_check_cfg(); | 1801 | net_check_cfg(); |
2380 | 1802 | ||
@@ -2429,11 +1851,6 @@ int main(int argc, char **argv) { | |||
2429 | } | 1851 | } |
2430 | if (cfg.name) | 1852 | if (cfg.name) |
2431 | set_name_run_file(sandbox_pid); | 1853 | set_name_run_file(sandbox_pid); |
2432 | #ifndef LTS | ||
2433 | int display = x11_display(); | ||
2434 | if (display > 0) | ||
2435 | set_x11_run_file(sandbox_pid, display); | ||
2436 | #endif | ||
2437 | if (lockfd_directory != -1) { | 1854 | if (lockfd_directory != -1) { |
2438 | flock(lockfd_directory, LOCK_UN); | 1855 | flock(lockfd_directory, LOCK_UN); |
2439 | close(lockfd_directory); | 1856 | close(lockfd_directory); |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 5f6707e4b..bce96727d 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -197,33 +197,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
197 | arg_shell_none = 1; | 197 | arg_shell_none = 1; |
198 | return 0; | 198 | return 0; |
199 | } | 199 | } |
200 | #ifndef LTS | ||
201 | else if (strcmp(ptr, "tracelog") == 0) { | ||
202 | arg_tracelog = 1; | ||
203 | return 0; | ||
204 | } | ||
205 | #endif | ||
206 | else if (strcmp(ptr, "private") == 0) { | 200 | else if (strcmp(ptr, "private") == 0) { |
207 | arg_private = 1; | 201 | arg_private = 1; |
208 | return 0; | 202 | return 0; |
209 | } | 203 | } |
210 | #ifndef LTS | ||
211 | if (strncmp(ptr, "private-home ", 13) == 0) { | ||
212 | #ifdef HAVE_PRIVATE_HOME | ||
213 | if (checkcfg(CFG_PRIVATE_HOME)) { | ||
214 | if (cfg.home_private_keep) { | ||
215 | if ( asprintf(&cfg.home_private_keep, "%s,%s", cfg.home_private_keep, ptr + 13) < 0 ) | ||
216 | errExit("asprintf"); | ||
217 | } else | ||
218 | cfg.home_private_keep = ptr + 13; | ||
219 | arg_private = 1; | ||
220 | } | ||
221 | else | ||
222 | warning_feature_disabled("private-home"); | ||
223 | #endif | ||
224 | return 0; | ||
225 | } | ||
226 | #endif //LTS | ||
227 | else if (strcmp(ptr, "allusers") == 0) { | 204 | else if (strcmp(ptr, "allusers") == 0) { |
228 | arg_allusers = 1; | 205 | arg_allusers = 1; |
229 | return 0; | 206 | return 0; |
@@ -749,14 +726,6 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
749 | return 0; | 726 | return 0; |
750 | } | 727 | } |
751 | 728 | ||
752 | #ifndef LTS | ||
753 | // cgroup | ||
754 | if (strncmp(ptr, "cgroup ", 7) == 0) { | ||
755 | set_cgroup(ptr + 7); | ||
756 | return 0; | ||
757 | } | ||
758 | #endif | ||
759 | |||
760 | // writable-etc | 729 | // writable-etc |
761 | if (strcmp(ptr, "writable-etc") == 0) { | 730 | if (strcmp(ptr, "writable-etc") == 0) { |
762 | if (cfg.etc_private_keep) { | 731 | if (cfg.etc_private_keep) { |
@@ -804,253 +773,6 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
804 | return 0; | 773 | return 0; |
805 | } | 774 | } |
806 | 775 | ||
807 | #ifndef LTS | ||
808 | if (strcmp(ptr, "x11 xephyr") == 0) { | ||
809 | #ifdef HAVE_X11 | ||
810 | if (checkcfg(CFG_X11)) { | ||
811 | char *x11env = getenv("FIREJAIL_X11"); | ||
812 | if (x11env && strcmp(x11env, "yes") == 0) { | ||
813 | return 0; | ||
814 | } | ||
815 | else { | ||
816 | // start x11 | ||
817 | x11_start_xephyr(cfg.original_argc, cfg.original_argv); | ||
818 | exit(0); | ||
819 | } | ||
820 | } | ||
821 | else | ||
822 | warning_feature_disabled("x11"); | ||
823 | #endif | ||
824 | return 0; | ||
825 | } | ||
826 | |||
827 | if (strcmp(ptr, "x11 xorg") == 0) { | ||
828 | #ifdef HAVE_X11 | ||
829 | if (checkcfg(CFG_X11)) | ||
830 | arg_x11_xorg = 1; | ||
831 | else | ||
832 | warning_feature_disabled("x11"); | ||
833 | #endif | ||
834 | return 0; | ||
835 | } | ||
836 | |||
837 | if (strcmp(ptr, "x11 xpra") == 0) { | ||
838 | #ifdef HAVE_X11 | ||
839 | if (checkcfg(CFG_X11)) { | ||
840 | char *x11env = getenv("FIREJAIL_X11"); | ||
841 | if (x11env && strcmp(x11env, "yes") == 0) { | ||
842 | return 0; | ||
843 | } | ||
844 | else { | ||
845 | // start x11 | ||
846 | x11_start_xpra(cfg.original_argc, cfg.original_argv); | ||
847 | exit(0); | ||
848 | } | ||
849 | } | ||
850 | else | ||
851 | warning_feature_disabled("x11"); | ||
852 | #endif | ||
853 | return 0; | ||
854 | } | ||
855 | |||
856 | if (strcmp(ptr, "x11 xvfb") == 0) { | ||
857 | #ifdef HAVE_X11 | ||
858 | if (checkcfg(CFG_X11)) { | ||
859 | char *x11env = getenv("FIREJAIL_X11"); | ||
860 | if (x11env && strcmp(x11env, "yes") == 0) { | ||
861 | return 0; | ||
862 | } | ||
863 | else { | ||
864 | // start x11 | ||
865 | x11_start_xvfb(cfg.original_argc, cfg.original_argv); | ||
866 | exit(0); | ||
867 | } | ||
868 | } | ||
869 | else | ||
870 | warning_feature_disabled("x11"); | ||
871 | #endif | ||
872 | return 0; | ||
873 | } | ||
874 | |||
875 | if (strcmp(ptr, "x11") == 0) { | ||
876 | #ifdef HAVE_X11 | ||
877 | if (checkcfg(CFG_X11)) { | ||
878 | char *x11env = getenv("FIREJAIL_X11"); | ||
879 | if (x11env && strcmp(x11env, "yes") == 0) { | ||
880 | return 0; | ||
881 | } | ||
882 | else { | ||
883 | // start x11 | ||
884 | x11_start(cfg.original_argc, cfg.original_argv); | ||
885 | exit(0); | ||
886 | } | ||
887 | } | ||
888 | else | ||
889 | warning_feature_disabled("x11"); | ||
890 | #endif | ||
891 | return 0; | ||
892 | } | ||
893 | |||
894 | // private /etc list of files and directories | ||
895 | if (strncmp(ptr, "private-etc ", 12) == 0) { | ||
896 | if (arg_writable_etc) { | ||
897 | fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); | ||
898 | exit(1); | ||
899 | } | ||
900 | if (cfg.etc_private_keep) { | ||
901 | if ( asprintf(&cfg.etc_private_keep, "%s,%s", cfg.etc_private_keep, ptr + 12) < 0 ) | ||
902 | errExit("asprintf"); | ||
903 | } else { | ||
904 | cfg.etc_private_keep = ptr + 12; | ||
905 | } | ||
906 | arg_private_etc = 1; | ||
907 | |||
908 | return 0; | ||
909 | } | ||
910 | |||
911 | // private /opt list of files and directories | ||
912 | if (strncmp(ptr, "private-opt ", 12) == 0) { | ||
913 | if (cfg.opt_private_keep) { | ||
914 | if ( asprintf(&cfg.opt_private_keep, "%s,%s", cfg.opt_private_keep, ptr + 12) < 0 ) | ||
915 | errExit("asprintf"); | ||
916 | } else { | ||
917 | cfg.opt_private_keep = ptr + 12; | ||
918 | } | ||
919 | arg_private_opt = 1; | ||
920 | |||
921 | return 0; | ||
922 | } | ||
923 | |||
924 | // private /srv list of files and directories | ||
925 | if (strncmp(ptr, "private-srv ", 12) == 0) { | ||
926 | if (cfg.srv_private_keep) { | ||
927 | if ( asprintf(&cfg.srv_private_keep, "%s,%s", cfg.srv_private_keep, ptr + 12) < 0 ) | ||
928 | errExit("asprintf"); | ||
929 | } else { | ||
930 | cfg.srv_private_keep = ptr + 12; | ||
931 | } | ||
932 | arg_private_srv = 1; | ||
933 | |||
934 | return 0; | ||
935 | } | ||
936 | |||
937 | // private /bin list of files | ||
938 | if (strncmp(ptr, "private-bin ", 12) == 0) { | ||
939 | if (cfg.bin_private_keep) { | ||
940 | if ( asprintf(&cfg.bin_private_keep, "%s,%s", cfg.bin_private_keep, ptr + 12) < 0 ) | ||
941 | errExit("asprintf"); | ||
942 | } else { | ||
943 | cfg.bin_private_keep = ptr + 12; | ||
944 | } | ||
945 | arg_private_bin = 1; | ||
946 | return 0; | ||
947 | } | ||
948 | |||
949 | // private /lib list of files | ||
950 | if (strncmp(ptr, "private-lib", 11) == 0) { | ||
951 | if (checkcfg(CFG_PRIVATE_LIB)) { | ||
952 | if (ptr[11] == ' ') { | ||
953 | if (cfg.lib_private_keep) { | ||
954 | if (ptr[12] != '\0' && asprintf(&cfg.lib_private_keep, "%s,%s", cfg.lib_private_keep, ptr + 12) < 0) | ||
955 | errExit("asprintf"); | ||
956 | } else { | ||
957 | cfg.lib_private_keep = ptr + 12; | ||
958 | } | ||
959 | } | ||
960 | arg_private_lib = 1; | ||
961 | } | ||
962 | else | ||
963 | warning_feature_disabled("private-lib"); | ||
964 | return 0; | ||
965 | } | ||
966 | |||
967 | |||
968 | #ifdef HAVE_OVERLAYFS | ||
969 | if (strncmp(ptr, "overlay-named ", 14) == 0) { | ||
970 | if (checkcfg(CFG_OVERLAYFS)) { | ||
971 | if (arg_overlay) { | ||
972 | fprintf(stderr, "Error: only one overlay command is allowed\n"); | ||
973 | exit(1); | ||
974 | } | ||
975 | if (cfg.chrootdir) { | ||
976 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); | ||
977 | exit(1); | ||
978 | } | ||
979 | struct stat s; | ||
980 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
981 | fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); | ||
982 | exit(1); | ||
983 | } | ||
984 | arg_overlay = 1; | ||
985 | arg_overlay_keep = 1; | ||
986 | arg_overlay_reuse = 1; | ||
987 | |||
988 | char *subdirname = ptr + 14; | ||
989 | if (*subdirname == '\0') { | ||
990 | fprintf(stderr, "Error: invalid overlay option\n"); | ||
991 | exit(1); | ||
992 | } | ||
993 | |||
994 | // check name | ||
995 | invalid_filename(subdirname, 0); // no globbing | ||
996 | if (strstr(subdirname, "..") || strstr(subdirname, "/")) { | ||
997 | fprintf(stderr, "Error: invalid overlay name\n"); | ||
998 | exit(1); | ||
999 | } | ||
1000 | cfg.overlay_dir = fs_check_overlay_dir(subdirname, arg_overlay_reuse); | ||
1001 | } | ||
1002 | |||
1003 | return 0; | ||
1004 | } else if (strcmp(ptr, "overlay-tmpfs") == 0) { | ||
1005 | if (checkcfg(CFG_OVERLAYFS)) { | ||
1006 | if (arg_overlay) { | ||
1007 | fprintf(stderr, "Error: only one overlay command is allowed\n"); | ||
1008 | exit(1); | ||
1009 | } | ||
1010 | if (cfg.chrootdir) { | ||
1011 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); | ||
1012 | exit(1); | ||
1013 | } | ||
1014 | struct stat s; | ||
1015 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
1016 | fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); | ||
1017 | exit(1); | ||
1018 | } | ||
1019 | arg_overlay = 1; | ||
1020 | |||
1021 | return 0; | ||
1022 | } | ||
1023 | } else if (strcmp(ptr, "overlay") == 0) { | ||
1024 | if (checkcfg(CFG_OVERLAYFS)) { | ||
1025 | if (arg_overlay) { | ||
1026 | fprintf(stderr, "Error: only one overlay command is allowed\n"); | ||
1027 | exit(1); | ||
1028 | } | ||
1029 | if (cfg.chrootdir) { | ||
1030 | fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); | ||
1031 | exit(1); | ||
1032 | } | ||
1033 | struct stat s; | ||
1034 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
1035 | fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); | ||
1036 | exit(1); | ||
1037 | } | ||
1038 | arg_overlay = 1; | ||
1039 | arg_overlay_keep = 1; | ||
1040 | |||
1041 | char *subdirname; | ||
1042 | if (asprintf(&subdirname, "%d", getpid()) == -1) | ||
1043 | errExit("asprintf"); | ||
1044 | cfg.overlay_dir = fs_check_overlay_dir(subdirname, arg_overlay_reuse); | ||
1045 | |||
1046 | free(subdirname); | ||
1047 | |||
1048 | return 0; | ||
1049 | } | ||
1050 | } | ||
1051 | #endif | ||
1052 | #endif // LTS | ||
1053 | |||
1054 | // filesystem bind | 776 | // filesystem bind |
1055 | if (strncmp(ptr, "bind ", 5) == 0) { | 777 | if (strncmp(ptr, "bind ", 5) == 0) { |
1056 | if (checkcfg(CFG_BIND)) { | 778 | if (checkcfg(CFG_BIND)) { |
@@ -1088,47 +810,6 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
1088 | return 0; | 810 | return 0; |
1089 | } | 811 | } |
1090 | 812 | ||
1091 | #ifndef LTS | ||
1092 | // rlimit | ||
1093 | if (strncmp(ptr, "rlimit", 6) == 0) { | ||
1094 | if (strncmp(ptr, "rlimit-nofile ", 14) == 0) { | ||
1095 | check_unsigned(ptr + 14, "Error: invalid rlimit in profile file: "); | ||
1096 | sscanf(ptr + 14, "%llu", &cfg.rlimit_nofile); | ||
1097 | arg_rlimit_nofile = 1; | ||
1098 | } | ||
1099 | else if (strncmp(ptr, "rlimit-cpu ", 11) == 0) { | ||
1100 | check_unsigned(ptr + 11, "Error: invalid rlimit in profile file: "); | ||
1101 | sscanf(ptr + 11, "%llu", &cfg.rlimit_cpu); | ||
1102 | arg_rlimit_cpu = 1; | ||
1103 | } | ||
1104 | else if (strncmp(ptr, "rlimit-nproc ", 13) == 0) { | ||
1105 | check_unsigned(ptr + 13, "Error: invalid rlimit in profile file: "); | ||
1106 | sscanf(ptr + 13, "%llu", &cfg.rlimit_nproc); | ||
1107 | arg_rlimit_nproc = 1; | ||
1108 | } | ||
1109 | else if (strncmp(ptr, "rlimit-fsize ", 13) == 0) { | ||
1110 | check_unsigned(ptr + 13, "Error: invalid rlimit in profile file: "); | ||
1111 | sscanf(ptr + 13, "%llu", &cfg.rlimit_fsize); | ||
1112 | arg_rlimit_fsize = 1; | ||
1113 | } | ||
1114 | else if (strncmp(ptr, "rlimit-sigpending ", 18) == 0) { | ||
1115 | check_unsigned(ptr + 18, "Error: invalid rlimit in profile file: "); | ||
1116 | sscanf(ptr + 18, "%llu", &cfg.rlimit_sigpending); | ||
1117 | arg_rlimit_sigpending = 1; | ||
1118 | } | ||
1119 | else if (strncmp(ptr, "rlimit-as ", 10) == 0) { | ||
1120 | check_unsigned(ptr + 10, "Error: invalid rlimit in profile file: "); | ||
1121 | sscanf(ptr + 10, "%llu", &cfg.rlimit_as); | ||
1122 | arg_rlimit_as = 1; | ||
1123 | } | ||
1124 | else { | ||
1125 | fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); | ||
1126 | exit(1); | ||
1127 | } | ||
1128 | |||
1129 | return 0; | ||
1130 | } | ||
1131 | #endif | ||
1132 | 813 | ||
1133 | if (strncmp(ptr, "timeout ", 8) == 0) { | 814 | if (strncmp(ptr, "timeout ", 8) == 0) { |
1134 | cfg.timeout = extract_timeout(ptr +8); | 815 | cfg.timeout = extract_timeout(ptr +8); |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index e0bc0e02f..483681779 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -400,30 +400,6 @@ void start_application(int no_sandbox, FILE *fp) { | |||
400 | printf("LD_PRELOAD=%s\n", getenv("LD_PRELOAD")); | 400 | printf("LD_PRELOAD=%s\n", getenv("LD_PRELOAD")); |
401 | } | 401 | } |
402 | 402 | ||
403 | //**************************************** | ||
404 | // audit | ||
405 | //**************************************** | ||
406 | #ifndef LTS | ||
407 | if (arg_audit) { | ||
408 | assert(arg_audit_prog); | ||
409 | |||
410 | if (fp) { | ||
411 | fprintf(fp, "ready\n"); | ||
412 | fclose(fp); | ||
413 | } | ||
414 | #ifdef HAVE_GCOV | ||
415 | __gcov_dump(); | ||
416 | #endif | ||
417 | #ifdef HAVE_SECCOMP | ||
418 | seccomp_install_filters(); | ||
419 | #endif | ||
420 | execl(arg_audit_prog, arg_audit_prog, NULL); | ||
421 | } | ||
422 | //**************************************** | ||
423 | // start the program without using a shell | ||
424 | //**************************************** | ||
425 | else | ||
426 | #endif // LTS | ||
427 | if (arg_shell_none) { | 403 | if (arg_shell_none) { |
428 | if (arg_debug) { | 404 | if (arg_debug) { |
429 | int i; | 405 | int i; |
@@ -747,20 +723,12 @@ int sandbox(void* sandbox_arg) { | |||
747 | #endif | 723 | #endif |
748 | 724 | ||
749 | // need ld.so.preload if tracing or seccomp with any non-default lists | 725 | // need ld.so.preload if tracing or seccomp with any non-default lists |
750 | #ifndef LTS | ||
751 | bool need_preload = arg_trace || arg_tracelog || arg_seccomp_postexec; | ||
752 | #else | ||
753 | bool need_preload = arg_seccomp_postexec; | 726 | bool need_preload = arg_seccomp_postexec; |
754 | #endif | ||
755 | // for --appimage, --chroot and --overlay* we replace the seccomp filter with the default one | 727 | // for --appimage, --chroot and --overlay* we replace the seccomp filter with the default one |
756 | // we also drop all capabilities | 728 | // we also drop all capabilities |
757 | if (getuid() != 0 && (arg_appimage || cfg.chrootdir || arg_overlay)) { | 729 | if (getuid() != 0 && (arg_appimage || cfg.chrootdir || arg_overlay)) { |
758 | enforce_filters(); | 730 | enforce_filters(); |
759 | #ifdef LTS | ||
760 | need_preload = 0; | 731 | need_preload = 0; |
761 | #else | ||
762 | need_preload = arg_trace || arg_tracelog; | ||
763 | #endif | ||
764 | arg_seccomp = 1; | 732 | arg_seccomp = 1; |
765 | } | 733 | } |
766 | // trace pre-install | 734 | // trace pre-install |
@@ -774,26 +742,7 @@ int sandbox(void* sandbox_arg) { | |||
774 | //**************************** | 742 | //**************************** |
775 | // configure filesystem | 743 | // configure filesystem |
776 | //**************************** | 744 | //**************************** |
777 | #ifndef LTS | 745 | fs_basic_fs(); |
778 | #ifdef HAVE_CHROOT | ||
779 | if (cfg.chrootdir) { | ||
780 | fs_chroot(cfg.chrootdir); | ||
781 | |||
782 | //**************************** | ||
783 | // trace pre-install, this time inside chroot | ||
784 | //**************************** | ||
785 | if (need_preload) | ||
786 | fs_trace_preload(); | ||
787 | } | ||
788 | else | ||
789 | #endif | ||
790 | #ifdef HAVE_OVERLAYFS | ||
791 | if (arg_overlay) | ||
792 | fs_overlayfs(); | ||
793 | else | ||
794 | #endif | ||
795 | #endif // LTS | ||
796 | fs_basic_fs(); | ||
797 | 746 | ||
798 | //**************************** | 747 | //**************************** |
799 | // private mode | 748 | // private mode |
@@ -807,16 +756,6 @@ int sandbox(void* sandbox_arg) { | |||
807 | else | 756 | else |
808 | fs_private_homedir(); | 757 | fs_private_homedir(); |
809 | } | 758 | } |
810 | #ifndef LTS | ||
811 | else if (cfg.home_private_keep) { // --private-home= | ||
812 | if (cfg.chrootdir) | ||
813 | fwarning("private-home= feature is disabled in chroot\n"); | ||
814 | else if (arg_overlay) | ||
815 | fwarning("private-home= feature is disabled in overlay\n"); | ||
816 | else | ||
817 | fs_private_home_list(); | ||
818 | } | ||
819 | #endif //LTS | ||
820 | else // --private | 759 | else // --private |
821 | fs_private(); | 760 | fs_private(); |
822 | } | 761 | } |
@@ -824,71 +763,6 @@ int sandbox(void* sandbox_arg) { | |||
824 | if (arg_private_dev) | 763 | if (arg_private_dev) |
825 | fs_private_dev(); | 764 | fs_private_dev(); |
826 | 765 | ||
827 | #ifndef LTS | ||
828 | if (arg_private_etc) { | ||
829 | if (cfg.chrootdir) | ||
830 | fwarning("private-etc feature is disabled in chroot\n"); | ||
831 | else if (arg_overlay) | ||
832 | fwarning("private-etc feature is disabled in overlay\n"); | ||
833 | else { | ||
834 | fs_private_dir_list("/etc", RUN_ETC_DIR, cfg.etc_private_keep); | ||
835 | // create /etc/ld.so.preload file again | ||
836 | if (need_preload) | ||
837 | fs_trace_preload(); | ||
838 | } | ||
839 | } | ||
840 | |||
841 | if (arg_private_opt) { | ||
842 | if (cfg.chrootdir) | ||
843 | fwarning("private-opt feature is disabled in chroot\n"); | ||
844 | else if (arg_overlay) | ||
845 | fwarning("private-opt feature is disabled in overlay\n"); | ||
846 | else { | ||
847 | fs_private_dir_list("/opt", RUN_OPT_DIR, cfg.opt_private_keep); | ||
848 | } | ||
849 | } | ||
850 | |||
851 | if (arg_private_srv) { | ||
852 | if (cfg.chrootdir) | ||
853 | fwarning("private-srv feature is disabled in chroot\n"); | ||
854 | else if (arg_overlay) | ||
855 | fwarning("private-srv feature is disabled in overlay\n"); | ||
856 | else { | ||
857 | fs_private_dir_list("/srv", RUN_SRV_DIR, cfg.srv_private_keep); | ||
858 | } | ||
859 | } | ||
860 | |||
861 | // private-bin is disabled for appimages | ||
862 | if (arg_private_bin && !arg_appimage) { | ||
863 | if (cfg.chrootdir) | ||
864 | fwarning("private-bin feature is disabled in chroot\n"); | ||
865 | else if (arg_overlay) | ||
866 | fwarning("private-bin feature is disabled in overlay\n"); | ||
867 | else { | ||
868 | // for --x11=xorg we need to add xauth command | ||
869 | if (arg_x11_xorg) { | ||
870 | EUID_USER(); | ||
871 | char *tmp; | ||
872 | if (asprintf(&tmp, "%s,xauth", cfg.bin_private_keep) == -1) | ||
873 | errExit("asprintf"); | ||
874 | cfg.bin_private_keep = tmp; | ||
875 | EUID_ROOT(); | ||
876 | } | ||
877 | fs_private_bin_list(); | ||
878 | } | ||
879 | } | ||
880 | |||
881 | // private-lib is disabled for appimages | ||
882 | if (arg_private_lib && !arg_appimage) { | ||
883 | if (cfg.chrootdir) | ||
884 | fwarning("private-lib feature is disabled in chroot\n"); | ||
885 | else if (arg_overlay) | ||
886 | fwarning("private-lib feature is disabled in overlay\n"); | ||
887 | else { | ||
888 | fs_private_lib(); | ||
889 | } | ||
890 | } | ||
891 | #endif // LTS | ||
892 | 766 | ||
893 | if (arg_private_cache) { | 767 | if (arg_private_cache) { |
894 | if (cfg.chrootdir) | 768 | if (cfg.chrootdir) |
@@ -1037,22 +911,11 @@ int sandbox(void* sandbox_arg) { | |||
1037 | } | 911 | } |
1038 | } | 912 | } |
1039 | 913 | ||
1040 | #ifndef LTS | ||
1041 | // clean /tmp/.X11-unix sockets | ||
1042 | fs_x11(); | ||
1043 | if (arg_x11_xorg) | ||
1044 | x11_xorg(); | ||
1045 | #endif //LTS | ||
1046 | |||
1047 | //**************************** | 914 | //**************************** |
1048 | // set security filters | 915 | // set security filters |
1049 | //**************************** | 916 | //**************************** |
1050 | // set capabilities | 917 | // set capabilities |
1051 | set_caps(); | 918 | set_caps(); |
1052 | #ifndef LTS | ||
1053 | // set rlimits | ||
1054 | set_rlimits(); | ||
1055 | #endif //LTS | ||
1056 | // set cpu affinity | 919 | // set cpu affinity |
1057 | if (cfg.cpus) { | 920 | if (cfg.cpus) { |
1058 | save_cpu(); // save cpu affinity mask to CPU_CFG file | 921 | save_cpu(); // save cpu affinity mask to CPU_CFG file |
@@ -1061,12 +924,6 @@ int sandbox(void* sandbox_arg) { | |||
1061 | EUID_ROOT(); | 924 | EUID_ROOT(); |
1062 | } | 925 | } |
1063 | 926 | ||
1064 | #ifndef LTS | ||
1065 | // save cgroup in CGROUP_CFG file | ||
1066 | if (cfg.cgroup) | ||
1067 | save_cgroup(); | ||
1068 | #endif | ||
1069 | |||
1070 | // set seccomp | 927 | // set seccomp |
1071 | #ifdef HAVE_SECCOMP | 928 | #ifdef HAVE_SECCOMP |
1072 | // install protocol filter | 929 | // install protocol filter |