diff options
author | smitsohu <smitsohu@gmail.com> | 2020-08-17 16:40:52 +0200 |
---|---|---|
committer | smitsohu <smitsohu@gmail.com> | 2020-08-17 16:40:52 +0200 |
commit | 7d0800682ab3a74e3d463836cd2ca5cd697d542c (patch) | |
tree | c1099688e259a1d03ffc633778de2ce836f03db4 | |
parent | hardening: run plugins with dumpable flag cleared (diff) | |
download | firejail-7d0800682ab3a74e3d463836cd2ca5cd697d542c.tar.gz firejail-7d0800682ab3a74e3d463836cd2ca5cd697d542c.tar.zst firejail-7d0800682ab3a74e3d463836cd2ca5cd697d542c.zip |
various x11 xorg enhancements
1) copy xauth binary into the sandbox and set mode to 0711, so it runs
with cleared dumpable flag for unprivileged users
2) run xauth in an sbox sandbox
3) generate Xauthority file in runtime directory instead of /tmp;
this way xauth is able to connect to the X11 socket even if the
abstract socket doesn't exist, for example because a new network
namespace was instantiated
-rw-r--r-- | src/firejail/x11.c | 173 | ||||
-rw-r--r-- | src/include/rundefs.h | 5 |
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. | ||
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 | ||
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" |