diff options
Diffstat (limited to 'src/firejail/x11.c')
-rw-r--r-- | src/firejail/x11.c | 173 |
1 files changed, 69 insertions, 104 deletions
diff --git a/src/firejail/x11.c b/src/firejail/x11.c index ba54ca376..e10abad4e 100644 --- a/src/firejail/x11.c +++ b/src/firejail/x11.c | |||
@@ -34,7 +34,7 @@ | |||
34 | 34 | ||
35 | #include <fcntl.h> | 35 | #include <fcntl.h> |
36 | #ifndef O_PATH | 36 | #ifndef O_PATH |
37 | # define O_PATH 010000000 | 37 | #define O_PATH 010000000 |
38 | #endif | 38 | #endif |
39 | 39 | ||
40 | 40 | ||
@@ -1129,28 +1129,17 @@ void x11_start(int argc, char **argv) { | |||
1129 | } | 1129 | } |
1130 | #endif | 1130 | #endif |
1131 | 1131 | ||
1132 | // Porting notes: | 1132 | |
1133 | // | ||
1134 | // 1. merge #1100 from zackw: | ||
1135 | // Attempting to run xauth -f directly on a file in /run/firejail/mnt/ directory fails on Debian 8 | ||
1136 | // with this message: | ||
1137 | // xauth: timeout in locking authority file /run/firejail/mnt/sec.Xauthority-Qt5Mu4 | ||
1138 | // Failed to create untrusted X cookie: xauth: exit 1 | ||
1139 | // For this reason we run xauth on a file in a tmpfs filesystem mounted on /tmp. This was | ||
1140 | // a partial merge. | ||
1141 | // | ||
1142 | // 2. Since we cannot deal with the TOCTOU condition when mounting .Xauthority in user home | ||
1143 | // directory, we need to make sure /usr/bin/xauth executable is the real thing, and not | ||
1144 | // something picked up on $PATH. | ||
1145 | // | ||
1146 | // 3. If for any reason xauth command fails, we exit the sandbox. On Debian 8 this happens | ||
1147 | // when using a network namespace. Somehow, xauth tries to connect to the abstract socket, | ||
1148 | // and it fails because of the network namespace - it should try to connect to the regular | ||
1149 | // Unix socket! If we ignore the fail condition, the program will be started on X server without | ||
1150 | // the security extension loaded. | ||
1151 | void x11_xorg(void) { | 1133 | void x11_xorg(void) { |
1152 | #ifdef HAVE_X11 | 1134 | #ifdef HAVE_X11 |
1153 | 1135 | ||
1136 | // get DISPLAY env | ||
1137 | char *display = getenv("DISPLAY"); | ||
1138 | if (!display) { | ||
1139 | fputs("Error: --x11=xorg requires an 'outer' X11 server to use.\n", stderr); | ||
1140 | exit(1); | ||
1141 | } | ||
1142 | |||
1154 | // check xauth utility is present in the system | 1143 | // check xauth utility is present in the system |
1155 | struct stat s; | 1144 | struct stat s; |
1156 | if (stat("/usr/bin/xauth", &s) == -1) { | 1145 | if (stat("/usr/bin/xauth", &s) == -1) { |
@@ -1160,26 +1149,27 @@ void x11_xorg(void) { | |||
1160 | fprintf(stderr, " Fedora: sudo dnf install xorg-x11-xauth\n"); | 1149 | fprintf(stderr, " Fedora: sudo dnf install xorg-x11-xauth\n"); |
1161 | exit(1); | 1150 | exit(1); |
1162 | } | 1151 | } |
1163 | if (s.st_uid != 0 && s.st_gid != 0) { | 1152 | if ((s.st_uid != 0 && s.st_gid != 0) || (s.st_mode & S_IWOTH)) { |
1164 | fprintf(stderr, "Error: invalid /usr/bin/xauth executable\n"); | 1153 | fprintf(stderr, "Error: invalid /usr/bin/xauth executable\n"); |
1165 | exit(1); | 1154 | exit(1); |
1166 | } | 1155 | } |
1167 | 1156 | if (s.st_size > 1024 * 1024) { | |
1168 | // get DISPLAY env | 1157 | fprintf(stderr, "Error: /usr/bin/xauth executable is too large\n"); |
1169 | char *display = getenv("DISPLAY"); | ||
1170 | if (!display) { | ||
1171 | fputs("Error: --x11=xorg requires an 'outer' X11 server to use.\n", stderr); | ||
1172 | exit(1); | 1158 | exit(1); |
1173 | } | 1159 | } |
1174 | 1160 | // copy /usr/bin/xauth in the sandbox and set mode to 0711 | |
1175 | // temporarily mount a tempfs on top of /tmp directory | 1161 | // users are not able to trace the running xauth this way |
1176 | if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=1777,gid=0") < 0) | ||
1177 | errExit("mounting /tmp"); | ||
1178 | |||
1179 | // create the temporary .Xauthority file | ||
1180 | if (arg_debug) | 1162 | if (arg_debug) |
1181 | printf("Generating a new .Xauthority file\n"); | 1163 | printf("Copying /usr/bin/xauth to %s\n", RUN_XAUTH_FILE); |
1182 | char tmpfname[] = "/tmp/.tmpXauth-XXXXXX"; | 1164 | if (copy_file("/usr/bin/xauth", RUN_XAUTH_FILE, 0, 0, 0711)) { |
1165 | fprintf(stderr, "Error: cannot copy /usr/bin/xauth executable\n"); | ||
1166 | exit(1); | ||
1167 | } | ||
1168 | |||
1169 | fmessage("Generating a new .Xauthority file\n"); | ||
1170 | mkdir_attr(RUN_XAUTHORITY_SEC_DIR, 0700, getuid(), getgid()); | ||
1171 | // create new Xauthority file in RUN_XAUTHORITY_SEC_DIR | ||
1172 | char tmpfname[] = RUN_XAUTHORITY_SEC_DIR "/.Xauth-XXXXXX"; | ||
1183 | int fd = mkstemp(tmpfname); | 1173 | int fd = mkstemp(tmpfname); |
1184 | if (fd == -1) { | 1174 | if (fd == -1) { |
1185 | fprintf(stderr, "Error: cannot create .Xauthority file\n"); | 1175 | fprintf(stderr, "Error: cannot create .Xauthority file\n"); |
@@ -1189,64 +1179,17 @@ void x11_xorg(void) { | |||
1189 | errExit("chown"); | 1179 | errExit("chown"); |
1190 | close(fd); | 1180 | close(fd); |
1191 | 1181 | ||
1192 | pid_t child = fork(); | 1182 | // run xauth |
1193 | if (child < 0) | ||
1194 | errExit("fork"); | ||
1195 | if (child == 0) { | ||
1196 | drop_privs(1); | ||
1197 | clearenv(); | ||
1198 | #ifdef HAVE_GCOV | ||
1199 | __gcov_flush(); | ||
1200 | #endif | ||
1201 | if (arg_debug) { | ||
1202 | execlp("/usr/bin/xauth", "/usr/bin/xauth", "-v", "-f", tmpfname, | ||
1203 | "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL); | ||
1204 | } | ||
1205 | else { | ||
1206 | execlp("/usr/bin/xauth", "/usr/bin/xauth", "-f", tmpfname, | ||
1207 | "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL); | ||
1208 | } | ||
1209 | |||
1210 | _exit(127); | ||
1211 | } | ||
1212 | |||
1213 | // wait for the xauth process to finish | ||
1214 | int status; | ||
1215 | if (waitpid(child, &status, 0) != child) | ||
1216 | errExit("waitpid"); | ||
1217 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { | ||
1218 | /* success */ | ||
1219 | } | ||
1220 | else if (WIFEXITED(status)) { | ||
1221 | fprintf(stderr, "Failed to create untrusted X cookie: xauth: exit %d\n", | ||
1222 | WEXITSTATUS(status)); | ||
1223 | exit(1); | ||
1224 | } | ||
1225 | else if (WIFSIGNALED(status)) { | ||
1226 | fprintf(stderr, "Failed to create untrusted X cookie: xauth: %s\n", | ||
1227 | strsignal(WTERMSIG(status))); | ||
1228 | exit(1); | ||
1229 | } | ||
1230 | else { | ||
1231 | fprintf(stderr, "Failed to create untrusted X cookie: " | ||
1232 | "xauth: un-decodable exit status %04x\n", status); | ||
1233 | exit(1); | ||
1234 | } | ||
1235 | |||
1236 | // move the temporary file in RUN_XAUTHORITY_SEC_FILE in order to have it deleted | ||
1237 | // automatically when the sandbox is closed (rename doesn't work) | ||
1238 | if (arg_debug) | 1183 | if (arg_debug) |
1239 | printf("Copying the new .Xauthority file\n"); | 1184 | sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 8, RUN_XAUTH_FILE, "-v", "-f", tmpfname, |
1240 | copy_file_from_user_to_root(tmpfname, RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600); | 1185 | "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted"); |
1241 | 1186 | else | |
1242 | /* coverity[toctou] */ | 1187 | sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7, RUN_XAUTH_FILE, "-f", tmpfname, |
1243 | unlink(tmpfname); | 1188 | "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted"); |
1244 | umount("/tmp"); | 1189 | // remove xauth copy |
1245 | 1190 | unlink(RUN_XAUTH_FILE); | |
1246 | // mount RUN_XAUTHORITY_SEC_FILE noexec, nodev, nosuid | ||
1247 | fs_remount(RUN_XAUTHORITY_SEC_FILE, MOUNT_NOEXEC, 0); | ||
1248 | 1191 | ||
1249 | // Ensure there is already a file in the usual location, so that bind-mount below will work. | 1192 | // ensure there is already a file ~/.Xauthority, so that bind-mount below will work. |
1250 | char *dest; | 1193 | char *dest; |
1251 | if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) | 1194 | if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) |
1252 | errExit("asprintf"); | 1195 | errExit("asprintf"); |
@@ -1257,13 +1200,12 @@ void x11_xorg(void) { | |||
1257 | exit(1); | 1200 | exit(1); |
1258 | } | 1201 | } |
1259 | } | 1202 | } |
1260 | 1203 | // get a file descriptor for ~/.Xauthority | |
1261 | // get a file descriptor for .Xauthority | 1204 | int dst = safe_fd(dest, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
1262 | fd = safe_fd(dest, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 1205 | if (dst == -1) |
1263 | if (fd == -1) | ||
1264 | errExit("safe_fd"); | 1206 | errExit("safe_fd"); |
1265 | // check if the actual mount destination is a user owned regular file | 1207 | // check if the actual mount destination is a user owned regular file |
1266 | if (fstat(fd, &s) == -1) | 1208 | if (fstat(dst, &s) == -1) |
1267 | errExit("fstat"); | 1209 | errExit("fstat"); |
1268 | if (!S_ISREG(s.st_mode) || s.st_uid != getuid()) { | 1210 | if (!S_ISREG(s.st_mode) || s.st_uid != getuid()) { |
1269 | if (S_ISLNK(s.st_mode)) | 1211 | if (S_ISLNK(s.st_mode)) |
@@ -1274,31 +1216,49 @@ void x11_xorg(void) { | |||
1274 | } | 1216 | } |
1275 | // preserve a read-only mount | 1217 | // preserve a read-only mount |
1276 | struct statvfs vfs; | 1218 | struct statvfs vfs; |
1277 | if (fstatvfs(fd, &vfs) == -1) | 1219 | if (fstatvfs(dst, &vfs) == -1) |
1278 | errExit("fstatvfs"); | 1220 | errExit("fstatvfs"); |
1279 | if ((vfs.f_flag & MS_RDONLY) == MS_RDONLY) | 1221 | if ((vfs.f_flag & MS_RDONLY) == MS_RDONLY) |
1280 | fs_remount(RUN_XAUTHORITY_SEC_FILE, MOUNT_READONLY, 0); | 1222 | fs_remount(RUN_XAUTHORITY_SEC_DIR, MOUNT_READONLY, 0); |
1223 | |||
1224 | // always mounting the new Xauthority file noexec,nodev,nosuid | ||
1225 | fs_remount(RUN_XAUTHORITY_SEC_DIR, MOUNT_NOEXEC, 0); | ||
1226 | |||
1227 | // get a file descriptor for the new Xauthority file | ||
1228 | int src = safe_fd(tmpfname, O_PATH|O_NOFOLLOW|O_CLOEXEC); | ||
1229 | if (src == -1) | ||
1230 | errExit("safe_fd"); | ||
1231 | if (fstat(src, &s) == -1) | ||
1232 | errExit("fstat"); | ||
1233 | if (!S_ISREG(s.st_mode)) { | ||
1234 | errno = EPERM; | ||
1235 | errExit("mounting Xauthority file"); | ||
1236 | } | ||
1281 | 1237 | ||
1282 | // mount via the link in /proc/self/fd | 1238 | // mount via the link in /proc/self/fd |
1283 | if (arg_debug) | 1239 | if (arg_debug) |
1284 | printf("Mounting %s on %s\n", RUN_XAUTHORITY_SEC_FILE, dest); | 1240 | printf("Mounting %s on %s\n", tmpfname, dest); |
1285 | char *proc; | 1241 | char *proc_src, *proc_dst; |
1286 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 1242 | if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1) |
1243 | errExit("asprintf"); | ||
1244 | if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1) | ||
1287 | errExit("asprintf"); | 1245 | errExit("asprintf"); |
1288 | if (mount(RUN_XAUTHORITY_SEC_FILE, proc, "none", MS_BIND, "mode=0600") == -1) { | 1246 | if (mount(proc_src, proc_dst, NULL, MS_BIND, NULL) == -1) { |
1289 | fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); | 1247 | fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); |
1290 | exit(1); | 1248 | exit(1); |
1291 | } | 1249 | } |
1292 | free(proc); | ||
1293 | close(fd); | ||
1294 | // check /proc/self/mountinfo to confirm the mount is ok | 1250 | // check /proc/self/mountinfo to confirm the mount is ok |
1295 | MountData *mptr = get_last_mount(); | 1251 | MountData *mptr = get_last_mount(); |
1296 | if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) | 1252 | if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) |
1297 | errLogExit("invalid .Xauthority mount"); | 1253 | errLogExit("invalid .Xauthority mount"); |
1254 | free(proc_src); | ||
1255 | free(proc_dst); | ||
1256 | close(src); | ||
1257 | close(dst); | ||
1298 | 1258 | ||
1299 | ASSERT_PERMS(dest, getuid(), getgid(), 0600); | 1259 | ASSERT_PERMS(dest, getuid(), getgid(), 0600); |
1300 | 1260 | ||
1301 | // blacklist .Xauthority file if it is not masked already | 1261 | // blacklist user .Xauthority file if it is not masked already |
1302 | char *envar = getenv("XAUTHORITY"); | 1262 | char *envar = getenv("XAUTHORITY"); |
1303 | if (envar) { | 1263 | if (envar) { |
1304 | char *rp = realpath(envar, NULL); | 1264 | char *rp = realpath(envar, NULL); |
@@ -1312,6 +1272,11 @@ void x11_xorg(void) { | |||
1312 | if (setenv("XAUTHORITY", dest, 1) < 0) | 1272 | if (setenv("XAUTHORITY", dest, 1) < 0) |
1313 | errExit("setenv"); | 1273 | errExit("setenv"); |
1314 | free(dest); | 1274 | free(dest); |
1275 | |||
1276 | // mask RUN_XAUTHORITY_SEC_DIR | ||
1277 | if (mount("tmpfs", RUN_XAUTHORITY_SEC_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1278 | errExit("mounting tmpfs"); | ||
1279 | fs_logger2("tmpfs", RUN_XAUTHORITY_SEC_DIR); | ||
1315 | #endif | 1280 | #endif |
1316 | } | 1281 | } |
1317 | 1282 | ||