aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@yahoo.com>2020-08-22 07:45:58 -0400
committerLibravatar netblue30 <netblue30@yahoo.com>2020-08-22 07:45:58 -0400
commit07472ed702f416f8c684de8baff3b32761ebc32a (patch)
tree4bfeb5e53e2ae379dd30a80421ff29cdf1293a09 /src/firejail
parentcleaning up POSTMORTEM code (diff)
parentMerge pull request #3572 from smitsohu/dumpable (diff)
downloadfirejail-07472ed702f416f8c684de8baff3b32761ebc32a.tar.gz
firejail-07472ed702f416f8c684de8baff3b32761ebc32a.tar.zst
firejail-07472ed702f416f8c684de8baff3b32761ebc32a.zip
Merge branch 'master' of https://github.com/netblue30/firejail
Diffstat (limited to 'src/firejail')
-rw-r--r--src/firejail/firejail.h3
-rw-r--r--src/firejail/ls.c169
-rw-r--r--src/firejail/main.c35
-rw-r--r--src/firejail/sandbox.c40
-rw-r--r--src/firejail/sbox.c1
-rw-r--r--src/firejail/usage.c3
-rw-r--r--src/firejail/x11.c173
7 files changed, 231 insertions, 193 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index c98f80d13..49d19e33d 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -728,10 +728,13 @@ void x11_xorg(void);
728// ls.c 728// ls.c
729enum { 729enum {
730 SANDBOX_FS_LS = 0, 730 SANDBOX_FS_LS = 0,
731 SANDBOX_FS_CAT,
731 SANDBOX_FS_GET, 732 SANDBOX_FS_GET,
732 SANDBOX_FS_PUT, 733 SANDBOX_FS_PUT,
733 SANDBOX_FS_MAX // this should always be the last entry 734 SANDBOX_FS_MAX // this should always be the last entry
734}; 735};
736void ls(const char *path);
737void cat(const char *path);
735void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) __attribute__((noreturn)); 738void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) __attribute__((noreturn));
736 739
737// checkcfg.c 740// checkcfg.c
diff --git a/src/firejail/ls.c b/src/firejail/ls.c
index aa33d838b..4d0a001b6 100644
--- a/src/firejail/ls.c
+++ b/src/firejail/ls.c
@@ -34,18 +34,12 @@
34static uid_t c_uid = 0; 34static uid_t c_uid = 0;
35static char *c_uid_name = NULL; 35static char *c_uid_name = NULL;
36 36
37static void print_file_or_dir(const char *path, const char *fname, int separator) { 37static void print_file_or_dir(const char *path, const char *fname) {
38 assert(fname); 38 assert(fname);
39 39
40 char *name; 40 char *name;
41 if (separator) { 41 if (asprintf(&name, "%s/%s", path, fname) == -1)
42 if (asprintf(&name, "%s/%s", path, fname) == -1) 42 errExit("asprintf");
43 errExit("asprintf");
44 }
45 else {
46 if (asprintf(&name, "%s%s", path, fname) == -1)
47 errExit("asprintf");
48 }
49 43
50 struct stat s; 44 struct stat s;
51 if (stat(name, &s) == -1) { 45 if (stat(name, &s) == -1) {
@@ -178,13 +172,81 @@ static void print_directory(const char *path) {
178 errExit("scandir"); 172 errExit("scandir");
179 else { 173 else {
180 for (i = 0; i < n; i++) { 174 for (i = 0; i < n; i++) {
181 print_file_or_dir(path, namelist[i]->d_name, 0); 175 print_file_or_dir(path, namelist[i]->d_name);
182 free(namelist[i]); 176 free(namelist[i]);
183 } 177 }
184 } 178 }
185 free(namelist); 179 free(namelist);
186} 180}
187 181
182void ls(const char *path) {
183 EUID_ASSERT();
184 assert(path);
185
186 char *rp = realpath(path, NULL);
187 if (!rp || access(rp, R_OK) == -1) {
188 fprintf(stderr, "Error: cannot access %s\n", path);
189 exit(1);
190 }
191 if (arg_debug)
192 printf("ls %s\n", rp);
193
194 // list directory contents
195 struct stat s;
196 if (stat(rp, &s) == -1) {
197 fprintf(stderr, "Error: cannot access %s\n", rp);
198 exit(1);
199 }
200 if (S_ISDIR(s.st_mode))
201 print_directory(rp);
202 else {
203 char *split = strrchr(rp, '/');
204 if (split) {
205 *split = '\0';
206 char *rp2 = split + 1;
207 if (arg_debug)
208 printf("path %s, file %s\n", rp, rp2);
209 print_file_or_dir(rp, rp2);
210 }
211 }
212 free(rp);
213}
214
215void cat(const char *path) {
216 EUID_ASSERT();
217 assert(path);
218
219 if (arg_debug)
220 printf("cat %s\n", path);
221 FILE *fp = fopen(path, "r");
222 if (!fp) {
223 fprintf(stderr, "Error: cannot read %s\n", path);
224 exit(1);
225 }
226 int fd = fileno(fp);
227 if (fd == -1)
228 errExit("fileno");
229 struct stat s;
230 if (fstat(fd, &s) == -1)
231 errExit("fstat");
232 if (!S_ISREG(s.st_mode)) {
233 fprintf(stderr, "Error: %s is not a regular file\n", path);
234 exit(1);
235 }
236 bool tty = isatty(STDOUT_FILENO);
237
238 int c;
239 while ((c = fgetc(fp)) != EOF) {
240 // file is untrusted
241 // replace control characters when printing to a terminal
242 if (tty && c != '\t' && c != '\n' && iscntrl((unsigned char) c))
243 c = '?';
244 fputc(c, stdout);
245 }
246 fflush(stdout);
247 fclose(fp);
248}
249
188char *expand_path(const char *path) { 250char *expand_path(const char *path) {
189 char *fname = NULL; 251 char *fname = NULL;
190 if (*path == '/') { 252 if (*path == '/') {
@@ -219,14 +281,14 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
219 check_join_permission(pid); 281 check_join_permission(pid);
220 282
221 // expand paths 283 // expand paths
222 char *fname1 = expand_path(path1);; 284 char *fname1 = expand_path(path1);
223 char *fname2 = NULL; 285 char *fname2 = NULL;
224 if (path2 != NULL) { 286 if (path2 != NULL) {
225 fname2 = expand_path(path2); 287 fname2 = expand_path(path2);
226 } 288 }
227 if (arg_debug) { 289 if (arg_debug) {
228 printf("file1 %s\n", fname1); 290 printf("file1 %s\n", fname1);
229 printf("file2 %s\n", fname2); 291 printf("file2 %s\n", fname2 ? fname2 : "(null)");
230 } 292 }
231 293
232 // sandbox root directory 294 // sandbox root directory
@@ -234,57 +296,36 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
234 if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) 296 if (asprintf(&rootdir, "/proc/%d/root", pid) == -1)
235 errExit("asprintf"); 297 errExit("asprintf");
236 298
237 if (op == SANDBOX_FS_LS) { 299 if (op == SANDBOX_FS_LS || op == SANDBOX_FS_CAT) {
238 EUID_ROOT(); 300 pid_t child = fork();
239 // chroot 301 if (child < 0)
240 if (chroot(rootdir) < 0) 302 errExit("fork");
241 errExit("chroot"); 303 if (child == 0) {
242 if (chdir("/") < 0) 304 EUID_ROOT();
243 errExit("chdir"); 305 // chroot
244 306 if (chroot(rootdir) < 0)
245 // drop privileges 307 errExit("chroot");
246 drop_privs(0); 308 if (chdir("/") < 0)
247 309 errExit("chdir");
248 // check access
249 if (access(fname1, R_OK) == -1) {
250 fprintf(stderr, "Error: Cannot access %s\n", fname1);
251 exit(1);
252 }
253 /* coverity[toctou] */
254 char *rp = realpath(fname1, NULL);
255 if (!rp) {
256 fprintf(stderr, "Error: Cannot access %s\n", fname1);
257 exit(1);
258 }
259 if (arg_debug)
260 printf("realpath %s\n", rp);
261
262 310
263 // list directory contents 311 // drop privileges
264 struct stat s; 312 drop_privs(0);
265 if (stat(rp, &s) == -1) {
266 fprintf(stderr, "Error: Cannot access %s\n", rp);
267 exit(1);
268 }
269 if (S_ISDIR(s.st_mode)) {
270 char *dir;
271 if (asprintf(&dir, "%s/", rp) == -1)
272 errExit("asprintf");
273 313
274 print_directory(dir); 314 if (op == SANDBOX_FS_LS)
275 free(dir); 315 ls(fname1);
276 } 316 else
277 else { 317 cat(fname1);
278 char *split = strrchr(rp, '/'); 318#ifdef HAVE_GCOV
279 if (split) { 319 __gcov_flush();
280 *split = '\0'; 320#endif
281 char *rp2 = split + 1; 321 _exit(0);
282 if (arg_debug)
283 printf("path %s, file %s\n", rp, rp2);
284 print_file_or_dir(rp, rp2, 1);
285 }
286 } 322 }
287 free(rp); 323 // wait for the child to finish
324 int status = 0;
325 waitpid(child, &status, 0);
326 if (WIFEXITED(status) && WEXITSTATUS(status) == 0);
327 else
328 exit(1);
288 } 329 }
289 330
290 // get file from sandbox and store it in the current directory 331 // get file from sandbox and store it in the current directory
@@ -303,10 +344,12 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
303 // create a user-owned temporary file in /run/firejail directory 344 // create a user-owned temporary file in /run/firejail directory
304 char tmp_fname[] = "/run/firejail/tmpget-XXXXXX"; 345 char tmp_fname[] = "/run/firejail/tmpget-XXXXXX";
305 int fd = mkstemp(tmp_fname); 346 int fd = mkstemp(tmp_fname);
306 if (fd != -1) { 347 if (fd == -1) {
307 SET_PERMS_FD(fd, getuid(), getgid(), 0600); 348 fprintf(stderr, "Error: cannot create temporary file %s\n", tmp_fname);
308 close(fd); 349 exit(1);
309 } 350 }
351 SET_PERMS_FD(fd, getuid(), getgid(), 0600);
352 close(fd);
310 353
311 // copy the source file into the temporary file - we need to chroot 354 // copy the source file into the temporary file - we need to chroot
312 pid_t child = fork(); 355 pid_t child = fork();
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 4c98210f5..072651c4d 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -789,6 +789,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
789 } 789 }
790 790
791 // list directory contents 791 // list directory contents
792 if (!arg_debug)
793 arg_quiet = 1;
792 pid_t pid = require_pid(argv[i] + 5); 794 pid_t pid = require_pid(argv[i] + 5);
793 sandboxfs(SANDBOX_FS_LS, pid, path, NULL); 795 sandboxfs(SANDBOX_FS_LS, pid, path, NULL);
794 exit(0); 796 exit(0);
@@ -796,6 +798,35 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
796 else 798 else
797 exit_err_feature("file transfer"); 799 exit_err_feature("file transfer");
798 } 800 }
801 else if (strncmp(argv[i], "--cat=", 6) == 0) {
802 if (checkcfg(CFG_FILE_TRANSFER)) {
803 logargs(argc, argv);
804 if (arg_private_cwd) {
805 fprintf(stderr, "Error: --cat and --private-cwd options are mutually exclusive\n");
806 exit(1);
807 }
808
809 if ((i + 2) != argc) {
810 fprintf(stderr, "Error: invalid --cat option, path expected\n");
811 exit(1);
812 }
813 char *path = argv[i + 1];
814 invalid_filename(path, 0); // no globbing
815 if (strstr(path, "..")) {
816 fprintf(stderr, "Error: invalid file name %s\n", path);
817 exit(1);
818 }
819
820 // write file contents to stdout
821 if (!arg_debug)
822 arg_quiet = 1;
823 pid_t pid = require_pid(argv[i] + 6);
824 sandboxfs(SANDBOX_FS_CAT, pid, path, NULL);
825 exit(0);
826 }
827 else
828 exit_err_feature("file transfer");
829 }
799#endif 830#endif
800 else if (strncmp(argv[i], "--join=", 7) == 0) { 831 else if (strncmp(argv[i], "--join=", 7) == 0) {
801 if (checkcfg(CFG_JOIN) || getuid() == 0) { 832 if (checkcfg(CFG_JOIN) || getuid() == 0) {
@@ -1252,6 +1283,10 @@ int main(int argc, char **argv, char **envp) {
1252 } 1283 }
1253 EUID_ASSERT(); 1284 EUID_ASSERT();
1254 1285
1286#ifdef WARN_DUMPABLE
1287 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 1 && getuid())
1288 fprintf(stderr, "Error: Firejail is dumpable\n");
1289#endif
1255 1290
1256 // check for force-nonewprivs in /etc/firejail/firejail.config file 1291 // check for force-nonewprivs in /etc/firejail/firejail.config file
1257 if (checkcfg(CFG_FORCE_NONEWPRIVS)) 1292 if (checkcfg(CFG_FORCE_NONEWPRIVS))
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index e42d35be5..81d535762 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -140,6 +140,20 @@ void set_apparmor(void) {
140} 140}
141#endif 141#endif
142 142
143#ifdef HAVE_SECCOMP
144void seccomp_debug(void) {
145 if (arg_debug == 0)
146 return;
147
148 EUID_USER();
149 printf("Seccomp directory:\n");
150 ls(RUN_SECCOMP_DIR);
151 printf("Active seccomp files:\n");
152 cat(RUN_SECCOMP_LIST);
153 EUID_ROOT();
154}
155#endif
156
143static void save_nogroups(void) { 157static void save_nogroups(void) {
144 if (arg_nogroups == 0) 158 if (arg_nogroups == 0)
145 return; 159 return;
@@ -197,32 +211,6 @@ static FILE *create_ready_for_join_file(void) {
197 } 211 }
198} 212}
199 213
200#ifdef HAVE_SECCOMP
201static void seccomp_debug(void) {
202 if (arg_debug == 0)
203 return;
204
205 pid_t child = fork();
206 if (child < 0)
207 errExit("fork");
208 if (child == 0) {
209 // dropping privs before calling system(3)
210 drop_privs(1);
211 printf("Seccomp directory:\n");
212 int rv = system("ls -l " RUN_SECCOMP_DIR);
213 (void) rv;
214 printf("Active seccomp files:\n");
215 rv = system("cat " RUN_SECCOMP_LIST);
216 (void) rv;
217#ifdef HAVE_GCOV
218 __gcov_flush();
219#endif
220 _exit(0);
221 }
222 waitpid(child, NULL, 0);
223}
224#endif
225
226static void sandbox_if_up(Bridge *br) { 214static void sandbox_if_up(Bridge *br) {
227 assert(br); 215 assert(br);
228 if (!br->configured) 216 if (!br->configured)
diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c
index 57c21ce78..a92d62940 100644
--- a/src/firejail/sbox.c
+++ b/src/firejail/sbox.c
@@ -48,6 +48,7 @@ static int __attribute__((noreturn)) sbox_do_exec_v(unsigned filtermask, char *
48 if (cfg.seccomp_error_action) 48 if (cfg.seccomp_error_action)
49 if (asprintf(&new_environment[env_index++], "FIREJAIL_SECCOMP_ERROR_ACTION=%s", cfg.seccomp_error_action) == -1) 49 if (asprintf(&new_environment[env_index++], "FIREJAIL_SECCOMP_ERROR_ACTION=%s", cfg.seccomp_error_action) == -1)
50 errExit("asprintf"); 50 errExit("asprintf");
51 new_environment[env_index++] = "FIREJAIL_PLUGIN="; // always set
51 52
52 if (filtermask & SBOX_STDIN_FROM_FILE) { 53 if (filtermask & SBOX_STDIN_FROM_FILE) {
53 int fd; 54 int fd;
diff --git a/src/firejail/usage.c b/src/firejail/usage.c
index 73c9a6a8b..2390706f2 100644
--- a/src/firejail/usage.c
+++ b/src/firejail/usage.c
@@ -47,6 +47,9 @@ static char *usage_str =
47 " --caps.drop=capability,capability - blacklist capabilities filter.\n" 47 " --caps.drop=capability,capability - blacklist capabilities filter.\n"
48 " --caps.keep=capability,capability - whitelist capabilities filter.\n" 48 " --caps.keep=capability,capability - whitelist capabilities filter.\n"
49 " --caps.print=name|pid - print the caps filter.\n" 49 " --caps.print=name|pid - print the caps filter.\n"
50#ifdef HAVE_FILE_TRANSFER
51 " --cat=name|pid filename - print content of file from sandbox container.\n"
52#endif
50 " --cgroup=tasks-file - place the sandbox in the specified control group.\n" 53 " --cgroup=tasks-file - place the sandbox in the specified control group.\n"
51#ifdef HAVE_CHROOT 54#ifdef HAVE_CHROOT
52 " --chroot=dirname - chroot into directory.\n" 55 " --chroot=dirname - chroot into directory.\n"
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.
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