aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/firejail/x11.c173
-rw-r--r--src/include/rundefs.h5
2 files changed, 72 insertions, 106 deletions
diff --git a/src/firejail/x11.c b/src/firejail/x11.c
index 98ac184d9..1ecb688af 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.
1151void x11_xorg(void) { 1133void 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
diff --git a/src/include/rundefs.h b/src/include/rundefs.h
index f8bcdec52..d56623907 100644
--- a/src/include/rundefs.h
+++ b/src/include/rundefs.h
@@ -99,8 +99,9 @@
99#define RUN_WHITELIST_SHARE_DIR RUN_MNT_DIR "/orig-share" 99#define RUN_WHITELIST_SHARE_DIR RUN_MNT_DIR "/orig-share"
100#define RUN_WHITELIST_MODULE_DIR RUN_MNT_DIR "/orig-module" 100#define RUN_WHITELIST_MODULE_DIR RUN_MNT_DIR "/orig-module"
101 101
102#define RUN_XAUTHORITY_FILE RUN_MNT_DIR "/.Xauthority" 102#define RUN_XAUTHORITY_FILE RUN_MNT_DIR "/.Xauthority" // private options
103#define RUN_XAUTHORITY_SEC_FILE RUN_MNT_DIR "/sec.Xauthority" 103#define RUN_XAUTH_FILE RUN_MNT_DIR "/xauth" // x11=xorg
104#define RUN_XAUTHORITY_SEC_DIR RUN_MNT_DIR "/.sec.Xauthority" // x11=xorg
104#define RUN_ASOUNDRC_FILE RUN_MNT_DIR "/.asoundrc" 105#define RUN_ASOUNDRC_FILE RUN_MNT_DIR "/.asoundrc"
105#define RUN_HOSTNAME_FILE RUN_MNT_DIR "/hostname" 106#define RUN_HOSTNAME_FILE RUN_MNT_DIR "/hostname"
106#define RUN_HOSTS_FILE RUN_MNT_DIR "/hosts" 107#define RUN_HOSTS_FILE RUN_MNT_DIR "/hosts"