aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README9
-rw-r--r--src/fcopy/main.c534
-rw-r--r--src/firejail/fs_bin.c2
-rw-r--r--src/firejail/x11.c536
-rwxr-xr-xtest/fcopy/cmdline.exp8
-rwxr-xr-xtest/fs/private-home.exp2
6 files changed, 583 insertions, 508 deletions
diff --git a/README b/README
index 52c5f7dd2..b12ad7b59 100644
--- a/README
+++ b/README
@@ -104,7 +104,14 @@ valoq (https://github.com/valoq)
104Zack Weinberg (https://github.com/zackw) 104Zack Weinberg (https://github.com/zackw)
105 - removed libconnect 105 - removed libconnect
106 - fixed memory corruption in noblacklist processing 106 - fixed memory corruption in noblacklist processing
107 - rework DISPLAY environment parsing, rework masking X11 sockets in /tmp/.X11-unix directory 107 - rework DISPLAY environment parsing
108 - rework masking X11 sockets in /tmp/.X11-unix directory
109 - rework xpra and xephyr detection
110 - rework abstract X11 socket detection
111 - rework X11 display number assignment
112 - rework X11 xorg processing
113 - rework fcopy, --follow-link support in fcopy
114 - follow link support in --private-bin
108Igor Bukanov (https://github.com/ibukanov) 115Igor Bukanov (https://github.com/ibukanov)
109 - found/fiixed privilege escalation in --hosts-file option 116 - found/fiixed privilege escalation in --hosts-file option
110Cat (https://github.com/ecat3) 117Cat (https://github.com/ecat3)
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
index 43fc8fc99..9f19b6dd8 100644
--- a/src/fcopy/main.c
+++ b/src/fcopy/main.c
@@ -21,6 +21,9 @@
21#include "../include/common.h" 21#include "../include/common.h"
22#include <fcntl.h> 22#include <fcntl.h>
23#include <ftw.h> 23#include <ftw.h>
24#include <errno.h>
25
26static int arg_follow_link = 0;
24 27
25 28
26#define COPY_LIMIT (500 * 1024 *1024) 29#define COPY_LIMIT (500 * 1024 *1024)
@@ -34,245 +37,244 @@ static char *inpath = NULL;
34 37
35// modified version of the function from util.c 38// modified version of the function from util.c
36static void copy_file(const char *srcname, const char *destname, mode_t mode, uid_t uid, gid_t gid) { 39static void copy_file(const char *srcname, const char *destname, mode_t mode, uid_t uid, gid_t gid) {
37 assert(srcname); 40 assert(srcname);
38 assert(destname); 41 assert(destname);
39 mode &= 07777; 42 mode &= 07777;
40 43
41 // open source 44 // open source
42 int src = open(srcname, O_RDONLY); 45 int src = open(srcname, O_RDONLY);
43 if (src < 0) { 46 if (src < 0) {
44 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", srcname); 47 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", srcname);
45 return; 48 return;
46 } 49 }
47 50
48 // open destination 51 // open destination
49 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, 0755); 52 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, 0755);
50 if (dst < 0) { 53 if (dst < 0) {
51 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", destname); 54 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", destname);
52 close(src); 55 close(src);
53 return; 56 return;
54 } 57 }
55 58
56 // copy 59 // copy
57 ssize_t len; 60 ssize_t len;
58 static const int BUFLEN = 1024; 61 static const int BUFLEN = 1024;
59 unsigned char buf[BUFLEN]; 62 unsigned char buf[BUFLEN];
60 while ((len = read(src, buf, BUFLEN)) > 0) { 63 while ((len = read(src, buf, BUFLEN)) > 0) {
61 int done = 0; 64 int done = 0;
62 while (done != len) { 65 while (done != len) {
63 int rv = write(dst, buf + done, len - done); 66 int rv = write(dst, buf + done, len - done);
64 if (rv == -1) 67 if (rv == -1)
65 goto errexit; 68 goto errexit;
66 done += rv; 69 done += rv;
67 } 70 }
68 } 71 }
69 fflush(0); 72 fflush(0);
70 73
71 if (fchown(dst, uid, gid) == -1) 74 if (fchown(dst, uid, gid) == -1)
72 goto errexit; 75 goto errexit;
73 if (fchmod(dst, mode) == -1) 76 if (fchmod(dst, mode) == -1)
74 goto errexit; 77 goto errexit;
75 78
76 close(src); 79 close(src);
77 close(dst); 80 close(dst);
78 81
79 return; 82 return;
80 83
81errexit: 84errexit:
82 close(src); 85 close(src);
83 close(dst); 86 close(dst);
84 unlink(destname); 87 unlink(destname);
85 fprintf(stderr, "Warning fcopy: cannot copy %s\n", destname); 88 fprintf(stderr, "Warning fcopy: cannot copy %s\n", destname);
86} 89}
87 90
88 91
89 92
90// modified version of the function in firejail/util.c 93// modified version of the function in firejail/util.c
91static void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid) { 94static void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid) {
92 assert(fname); 95 assert(fname);
93 mode &= 07777; 96 mode &= 07777;
94 97
95 if (mkdir(fname, mode) == -1 || 98 if (mkdir(fname, mode) == -1 ||
96 chmod(fname, mode) == -1) { 99 chmod(fname, mode) == -1) {
97 fprintf(stderr, "Error fcopy: failed to create %s directory\n", fname); 100 fprintf(stderr, "Error fcopy: failed to create %s directory\n", fname);
98 errExit("mkdir/chmod"); 101 errExit("mkdir/chmod");
99 } 102 }
100 if (chown(fname, uid, gid)) 103 if (chown(fname, uid, gid))
101 fprintf(stderr, "Warning fcopy: failed to change ownership of %s\n", fname); 104 fprintf(stderr, "Warning fcopy: failed to change ownership of %s\n", fname);
102} 105}
103 106
104void copy_link(const char *target, const char *linkpath, mode_t mode, uid_t uid, gid_t gid) { 107void copy_link(const char *target, const char *linkpath, mode_t mode, uid_t uid, gid_t gid) {
105 (void) mode; 108 (void) mode;
106 (void) uid; 109 (void) uid;
107 (void) gid; 110 (void) gid;
108 char *rp = realpath(target, NULL); 111 char *rp = realpath(target, NULL);
109 if (rp) { 112 if (rp) {
110 if (symlink(rp, linkpath) == -1) 113 if (symlink(rp, linkpath) == -1)
111 goto errout; 114 goto errout;
112 free(rp); 115 free(rp);
113 } 116 }
114 else 117 else
115 goto errout; 118 goto errout;
116 119
117 return; 120 return;
118errout: 121errout:
119 fprintf(stderr, "Warning fcopy: cannot create symbolic link %s\n", target); 122 fprintf(stderr, "Warning fcopy: cannot create symbolic link %s\n", target);
120} 123}
121 124
122static int first = 1; 125static int first = 1;
123static int fs_copydir(const char *infname, const struct stat *st, int ftype, struct FTW *sftw) { 126static int fs_copydir(const char *infname, const struct stat *st, int ftype, struct FTW *sftw) {
124 (void) st; 127 (void) st;
125 (void) sftw; 128 (void) sftw;
126 assert(infname); 129 assert(infname);
127 assert(*infname != '\0'); 130 assert(*infname != '\0');
128 assert(outpath); 131 assert(outpath);
129 assert(*outpath != '\0'); 132 assert(*outpath != '\0');
130 assert(inpath); 133 assert(inpath);
131 134
132 // check size limit 135 // check size limit
133 if (size_limit_reached) 136 if (size_limit_reached)
134 return 0; 137 return 0;
135 138
136 139
137 char *outfname; 140 char *outfname;
138 if (asprintf(&outfname, "%s%s", outpath, infname + strlen(inpath)) == -1) 141 if (asprintf(&outfname, "%s%s", outpath, infname + strlen(inpath)) == -1)
139 errExit("asprintf"); 142 errExit("asprintf");
140 143
141//printf("outpaht %s\n", outpath); 144 // don't copy it if we already have the file
142//printf("inpath %s\n", inpath); 145 struct stat s;
143//printf("infname %s\n", infname); 146 if (stat(outfname, &s) == 0) {
144//printf("outfname %s\n\n", outfname); 147 if (first)
145 148 first = 0;
146 // don't copy it if we already have the file 149 else
147 struct stat s; 150 fprintf(stderr, "Warning fcopy: skipping %s, file already present\n", infname);
148 if (stat(outfname, &s) == 0) { 151 free(outfname);
149 if (first) 152 return 0;
150 first = 0; 153 }
151 else 154
152 fprintf(stderr, "Warning fcopy: skipping %s, file already present\n", infname); 155 // extract mode and ownership
153 free(outfname); 156 if (stat(infname, &s) != 0) {
154 return 0; 157 fprintf(stderr, "Warning fcopy: skipping %s, cannot find inode\n", infname);
155 } 158 free(outfname);
156 159 return 0;
157 // extract mode and ownership 160 }
158 if (stat(infname, &s) != 0) { 161 uid_t uid = s.st_uid;
159 fprintf(stderr, "Warning fcopy: skipping %s, cannot find inode\n", infname); 162 gid_t gid = s.st_gid;
160 free(outfname); 163 mode_t mode = s.st_mode;
161 return 0; 164
162 } 165 // recalculate size
163 uid_t uid = s.st_uid; 166 if ((s.st_size + size_cnt) > COPY_LIMIT) {
164 gid_t gid = s.st_gid; 167 fprintf(stderr, "Error fcopy: size limit of %dMB reached\n", (COPY_LIMIT / 1024) / 1024);
165 mode_t mode = s.st_mode; 168 size_limit_reached = 1;
166 169 free(outfname);
167 // recalculate size 170 return 0;
168 if ((s.st_size + size_cnt) > COPY_LIMIT) { 171 }
169 fprintf(stderr, "Error fcopy: size limit of %dMB reached\n", (COPY_LIMIT / 1024) / 1024); 172
170 size_limit_reached = 1; 173 file_cnt++;
171 free(outfname); 174 size_cnt += s.st_size;
172 return 0; 175
173 } 176 if(ftype == FTW_F) {
174 177 copy_file(infname, outfname, mode, uid, gid);
175 file_cnt++; 178 }
176 size_cnt += s.st_size; 179 else if (ftype == FTW_D) {
177 180 mkdir_attr(outfname, mode, uid, gid);
178 if(ftype == FTW_F) { 181 }
179 copy_file(infname, outfname, mode, uid, gid); 182 else if (ftype == FTW_SL) {
180 } 183 copy_link(infname, outfname, mode, uid, gid);
181 else if (ftype == FTW_D) { 184 }
182 mkdir_attr(outfname, mode, uid, gid); 185
183 } 186 return(0);
184 else if (ftype == FTW_SL) {
185 copy_link(infname, outfname, mode, uid, gid);
186 }
187
188 return(0);
189} 187}
190 188
191static char *check(const char *src) { 189static char *check(const char *src) {
192 struct stat s; 190 struct stat s;
193 char *rsrc = realpath(src, NULL); 191 char *rsrc = realpath(src, NULL);
194 if (!rsrc || stat(rsrc, &s) == -1) 192 if (!rsrc || stat(rsrc, &s) == -1)
195 goto errexit; 193 goto errexit;
196 194
197 // check uid 195 // check uid
198 if (s.st_uid != getuid() || s.st_gid != getgid()) 196 if (s.st_uid != getuid() || s.st_gid != getgid())
199 goto errexit; 197 goto errexit;
200 198
201 // dir, link, regular file 199 // dir, link, regular file
202 if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode) || S_ISLNK(s.st_mode)) 200 if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode) || S_ISLNK(s.st_mode))
203 return rsrc; // normal exit from the function 201 return rsrc; // normal exit from the function
204 202
205errexit: 203errexit:
206 fprintf(stderr, "Error fcopy: invalid file %s\n", src); 204 fprintf(stderr, "Error fcopy: invalid file %s\n", src);
207 exit(1); 205 exit(1);
208} 206}
209 207
210static void duplicate_dir(const char *src, const char *dest, struct stat *s) { 208static void duplicate_dir(const char *src, const char *dest, struct stat *s) {
211 (void) s; 209 (void) s;
212 char *rsrc = check(src); 210 char *rsrc = check(src);
213 char *rdest = check(dest); 211 char *rdest = check(dest);
214 inpath = rsrc; 212 inpath = rsrc;
215 outpath = rdest; 213 outpath = rdest;
216 214
217 // walk 215 // walk
218 if(nftw(rsrc, fs_copydir, 1, FTW_PHYS) != 0) { 216 if(nftw(rsrc, fs_copydir, 1, FTW_PHYS) != 0) {
219 fprintf(stderr, "Error: unable to copy file\n"); 217 fprintf(stderr, "Error: unable to copy file\n");
220 exit(1); 218 exit(1);
221 } 219 }
222 220
223 free(rsrc); 221 free(rsrc);
224 free(rdest); 222 free(rdest);
225} 223}
226 224
227static void duplicate_file(const char *src, const char *dest, struct stat *s) { 225static void duplicate_file(const char *src, const char *dest, struct stat *s) {
228 char *rsrc = check(src); 226 char *rsrc = check(src);
229 char *rdest = check(dest); 227 char *rdest = check(dest);
230 uid_t uid = s->st_uid; 228 uid_t uid = s->st_uid;
231 gid_t gid = s->st_gid; 229 gid_t gid = s->st_gid;
232 mode_t mode = s->st_mode; 230 mode_t mode = s->st_mode;
233 231
234 // build destination file name 232 // build destination file name
235 char *name; 233 char *name;
236 char *ptr = strrchr(rsrc, '/'); 234 char *ptr = (arg_follow_link)? strrchr(src, '/'): strrchr(rsrc, '/');
237 ptr++; 235 ptr++;
238 if (asprintf(&name, "%s/%s", rdest, ptr) == -1) 236 if (asprintf(&name, "%s/%s", rdest, ptr) == -1)
239 errExit("asprintf"); 237 errExit("asprintf");
240 238
241 // copy 239 // copy
242 copy_file(rsrc, name, mode, uid, gid); 240 copy_file(rsrc, name, mode, uid, gid);
243 241
244 free(name); 242 free(name);
245 free(rsrc); 243 free(rsrc);
246 free(rdest); 244 free(rdest);
247} 245}
248 246
249static void duplicate_link(const char *src, const char *dest, struct stat *s) { 247static void duplicate_link(const char *src, const char *dest, struct stat *s) {
250 char *rsrc = check(src); // we drop the result and use the original name 248 char *rsrc = check(src); // we drop the result and use the original name
251 char *rdest = check(dest); 249 char *rdest = check(dest);
252 uid_t uid = s->st_uid; 250 uid_t uid = s->st_uid;
253 gid_t gid = s->st_gid; 251 gid_t gid = s->st_gid;
254 mode_t mode = s->st_mode; 252 mode_t mode = s->st_mode;
255 253
256 // build destination file name 254 // build destination file name
257 char *name; 255 char *name;
258// char *ptr = strrchr(rsrc, '/'); 256// char *ptr = strrchr(rsrc, '/');
259 char *ptr = strrchr(src, '/'); 257 char *ptr = strrchr(src, '/');
260 ptr++; 258 ptr++;
261 if (asprintf(&name, "%s/%s", rdest, ptr) == -1) 259 if (asprintf(&name, "%s/%s", rdest, ptr) == -1)
262 errExit("asprintf"); 260 errExit("asprintf");
263 261
264 // copy 262 // copy
265 copy_link(rsrc, name, mode, uid, gid); 263 copy_link(rsrc, name, mode, uid, gid);
266 264
267 free(name); 265 free(name);
268 free(rsrc); 266 free(rsrc);
269 free(rdest); 267 free(rdest);
270} 268}
271 269
272static void usage(void) { 270static void usage(void) {
273 printf("Usage: fcopy src dest\n"); 271 fputs("Usage: fcopy [--follow-link] src dest\n"
274 printf("Copy src file in dest directory. If src is a directory, copy all the files in\n"); 272 "\n"
275 printf("src recoursively. If the destination directory does not exist, it will be created.\n"); 273 "Copy SRC to DEST/SRC. SRC may be a file, directory, or symbolic link.\n"
274 "If SRC is a directory it is copied recursively. If it is a symlink,\n"
275 "the link itself is duplicated, unless --follow-link is given,\n"
276 "in which case the destination of the link is copied.\n"
277 "DEST must already exist and must be a directory.\n", stderr);
276} 278}
277 279
278int main(int argc, char **argv) { 280int main(int argc, char **argv) {
@@ -285,56 +287,70 @@ for (i = 0; i < argc; i++)
285printf("\n"); 287printf("\n");
286} 288}
287#endif 289#endif
288 if (argc != 3) { 290 char *src;
289 fprintf(stderr, "Error fcopy: files missing\n"); 291 char *dest;
290 usage(); 292
291 exit(1); 293 if (argc == 3) {
292 } 294 src = argv[1];
293 295 dest = argv[2];
294 // check the two files; remove ending / 296 arg_follow_link = 0;
295 char *src = argv[1]; 297 }
296 int len = strlen(src); 298 else if (argc == 4 && !strcmp(argv[1], "--follow-link")) {
297 if (src[len - 1] == '/') 299 src = argv[2];
298 src[len - 1] = '\0'; 300 dest = argv[3];
299 if (strcspn(src, "\\*&!?\"'<>%^(){}[];,") != (size_t)len) { 301 arg_follow_link = 1;
300 fprintf(stderr, "Error fcopy: invalid file name %s\n", src); 302 }
301 exit(1); 303 else {
302 } 304 fprintf(stderr, "Error: arguments missing\n");
303 305 usage();
304 char *dest = argv[2]; 306 exit(1);
305 len = strlen(dest); 307 }
306 if (dest[len - 1] == '/') 308
307 dest[len - 1] = '\0'; 309 // check the two files; remove ending /
308 if (strcspn(dest, "\\*&!?\"'<>%^(){}[];,~") != (size_t)len) { 310 int len = strlen(src);
309 fprintf(stderr, "Error fcopy: invalid file name %s\n", dest); 311 if (src[len - 1] == '/')
310 exit(1); 312 src[len - 1] = '\0';
311 } 313 if (strcspn(src, "\\*&!?\"'<>%^(){}[];,") != (size_t)len) {
312 314 fprintf(stderr, "Error fcopy: invalid source file name %s\n", src);
313 315 exit(1);
314 // the destination should be a directory; 316 }
315 struct stat s; 317
316 if (stat(dest, &s) == -1 || 318 len = strlen(dest);
317 !S_ISDIR(s.st_mode)) { 319 if (dest[len - 1] == '/')
318 fprintf(stderr, "Error fcopy: invalid destination directory\n"); 320 dest[len - 1] = '\0';
319 exit(1); 321 if (strcspn(dest, "\\*&!?\"'<>%^(){}[];,~") != (size_t)len) {
320 } 322 fprintf(stderr, "Error fcopy: invalid dest file name %s\n", dest);
321 323 exit(1);
322 // copy files 324 }
323 if (lstat(src, &s) == -1) { 325
324 fprintf(stderr, "Error fcopy: cannot find source file\n"); 326
325 exit(1); 327 // the destination should be a directory;
326 } 328 struct stat s;
327 329 if (stat(dest, &s) == -1) {
328 if (S_ISDIR(s.st_mode)) 330 fprintf(stderr, "Error fcopy: dest dir %s: %s\n", dest, strerror(errno));
329 duplicate_dir(src, dest, &s); 331 exit(1);
330 else if (S_ISREG(s.st_mode)) 332 }
331 duplicate_file(src, dest, &s); 333 if (!S_ISDIR(s.st_mode)) {
332 else if (S_ISLNK(s.st_mode)) 334 fprintf(stderr, "Error fcopy: dest %s is not a directory\n", dest);
333 duplicate_link(src, dest, &s); 335 exit(1);
334 else { 336 }
335 fprintf(stderr, "Error fcopy: source file unsupported\n"); 337
336 exit(1); 338 // copy files
337 } 339 if ((arg_follow_link ? stat : lstat)(src, &s) == -1) {
338 340 fprintf(stderr, "Error fcopy: src %s: %s\n", src, strerror(errno));
339 return 0; 341 exit(1);
342 }
343
344 if (S_ISDIR(s.st_mode))
345 duplicate_dir(src, dest, &s);
346 else if (S_ISREG(s.st_mode))
347 duplicate_file(src, dest, &s);
348 else if (S_ISLNK(s.st_mode))
349 duplicate_link(src, dest, &s);
350 else {
351 fprintf(stderr, "Error fcopy: src %s is an unsupported type of file\n", src);
352 exit(1);
353 }
354
355 return 0;
340} 356}
diff --git a/src/firejail/fs_bin.c b/src/firejail/fs_bin.c
index 547978b47..3473fca4c 100644
--- a/src/firejail/fs_bin.c
+++ b/src/firejail/fs_bin.c
@@ -111,7 +111,7 @@ static void duplicate(char *fname) {
111 errExit("asprintf"); 111 errExit("asprintf");
112 112
113 // copy the file 113 // copy the file
114 sbox_run(SBOX_ROOT| SBOX_SECCOMP, 3, PATH_FCOPY, full_path, RUN_BIN_DIR); 114 sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", full_path, RUN_BIN_DIR);
115 fs_logger2("clone", fname); 115 fs_logger2("clone", fname);
116 free(full_path); 116 free(full_path);
117} 117}
diff --git a/src/firejail/x11.c b/src/firejail/x11.c
index b551a2d2a..0fa789ff1 100644
--- a/src/firejail/x11.c
+++ b/src/firejail/x11.c
@@ -20,6 +20,8 @@
20#include "firejail.h" 20#include "firejail.h"
21#include <sys/types.h> 21#include <sys/types.h>
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <sys/socket.h>
24#include <sys/un.h>
23#include <fcntl.h> 25#include <fcntl.h>
24#include <unistd.h> 26#include <unistd.h>
25#include <signal.h> 27#include <signal.h>
@@ -31,80 +33,6 @@
31#include <limits.h> 33#include <limits.h>
32int mask_x11_abstract_socket = 0; 34int mask_x11_abstract_socket = 0;
33 35
34#ifdef HAVE_X11
35// return 1 if xpra is installed on the system
36static int x11_check_xpra(void) {
37 struct stat s;
38
39 // check xpra
40 if (stat("/usr/bin/xpra", &s) == -1)
41 return 0;
42
43 return 1;
44}
45
46// return 1 if xephyr is installed on the system
47static int x11_check_xephyr(void) {
48 struct stat s;
49
50 // check xephyr
51 if (stat("/usr/bin/Xephyr", &s) == -1)
52 return 0;
53
54 return 1;
55}
56
57// check for X11 abstract sockets
58static int x11_abstract_sockets_present(void) {
59 char *path;
60
61 EUID_ROOT(); // grsecurity fix
62 FILE *fp = fopen("/proc/net/unix", "r");
63 EUID_USER();
64
65 if (!fp)
66 errExit("fopen");
67
68 while (fscanf(fp, "%*s %*s %*s %*s %*s %*s %*s %ms\n", &path) != EOF) {
69 if (path && strncmp(path, "@/tmp/.X11-unix/", 16) == 0) {
70 free(path);
71 fclose(fp);
72 return 1;
73 }
74 }
75
76 free(path);
77 fclose(fp);
78
79 return 0;
80}
81
82static int random_display_number(void) {
83 int i;
84 int found = 1;
85 int display;
86 for (i = 0; i < 100; i++) {
87 display = rand() % 1024;
88 if (display < 10)
89 continue;
90 char *fname;
91 if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1)
92 errExit("asprintf");
93 struct stat s;
94 if (stat(fname, &s) == -1) {
95 found = 1;
96 break;
97 }
98 }
99 if (!found) {
100 fprintf(stderr, "Error: cannot pick up a random X11 display number, exiting...\n");
101 exit(1);
102 }
103
104 return display;
105}
106#endif
107
108 36
109// Parse the DISPLAY environment variable and return a display number. 37// Parse the DISPLAY environment variable and return a display number.
110// Returns -1 if DISPLAY is not set, or is set to anything other than :ddd. 38// Returns -1 if DISPLAY is not set, or is set to anything other than :ddd.
@@ -145,116 +73,132 @@ int x11_display(void) {
145 return (int)display; 73 return (int)display;
146} 74}
147 75
148void fs_x11(void) {
149#ifdef HAVE_X11
150 int display = x11_display();
151 if (display <= 0)
152 return;
153 76
154 char *x11file; 77#ifdef HAVE_X11
155 if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1) 78// check for X11 abstract sockets
156 errExit("asprintf"); 79static int x11_abstract_sockets_present(void) {
157 struct stat x11stat;
158 if (stat(x11file, &x11stat) == -1 || !S_ISSOCK(x11stat.st_mode)) {
159 free(x11file);
160 return;
161 }
162
163 if (arg_debug || arg_debug_whitelists)
164 fprintf(stderr, "Masking all X11 sockets except %s\n", x11file);
165
166 // Move the real /tmp/.X11-unix to a scratch location
167 // so we can still access x11file after we mount a
168 // tmpfs over /tmp/.X11-unix.
169 int rv = mkdir(RUN_WHITELIST_X11_DIR, 0700);
170 if (rv == -1)
171 errExit("mkdir");
172 if (set_perms(RUN_WHITELIST_X11_DIR, 0, 0, 0700))
173 errExit("set_perms");
174
175 if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, 0, MS_BIND|MS_REC, 0) < 0)
176 errExit("mount bind");
177
178 // This directory must be mode 1777, or Xlib will barf.
179 if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs",
180 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC,
181 "mode=1777,uid=0,gid=0") < 0)
182 errExit("mounting tmpfs on /tmp/.X11-unix");
183 fs_logger("tmpfs /tmp/.X11-unix");
184 80
185 // create an empty file which will have the desired socket bind-mounted over it 81 EUID_ROOT(); // grsecurity fix
186 int fd = open(x11file, O_RDWR|O_CREAT|O_EXCL, x11stat.st_mode & ~S_IFMT); 82 FILE *fp = fopen("/proc/net/unix", "r");
187 if (fd < 0) 83 if (!fp)
188 errExit(x11file); 84 errExit("fopen");
189 if (fchown(fd, x11stat.st_uid, x11stat.st_gid)) 85 EUID_USER();
190 errExit("fchown");
191 close(fd);
192 86
193 // do the mount 87 char *linebuf = 0;
194 char *wx11file; 88 size_t bufsz = 0;
195 if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1) 89 int found = 0;
196 errExit("asprintf"); 90 errno = 0;
197 if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0)
198 errExit("mount bind");
199 fs_logger2("whitelist", x11file);
200 91
201 free(x11file); 92 for (;;) {
202 free(wx11file); 93 if (getline(&linebuf, &bufsz, fp) == -1) {
94 if (errno)
95 errExit("getline");
96 break;
97 }
98 // The last space-separated field in 'linebuf' is the
99 // pathname of the socket. Abstract sockets' pathnames
100 // all begin with '@/', normal ones begin with '/'.
101 char *p = strrchr(linebuf, ' ');
102 if (!p) {
103 fputs("error parsing /proc/net/unix\n", stderr);
104 exit(1);
105 }
106 if (strncmp(p+1, "@/tmp/.X11-unix/", 16) == 0) {
107 found = 1;
108 break;
109 }
110 }
203 111
204 // block access to RUN_WHITELIST_X11_DIR 112 free(linebuf);
205 if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, 0, MS_BIND, 0) < 0) 113 fclose(fp);
206 errExit("mount"); 114 return found;
207 fs_logger2("blacklist", RUN_WHITELIST_X11_DIR); 115}
208 116
117// Choose a random, unallocated display number. This has an inherent
118// and unavoidable TOCTOU race, since we cannot create either the
119// socket or a lockfile ourselves.
120static int random_display_number(void) {
121 int display;
122 int found = 0;
123 int i;
209 124
210#if 0 125 struct sockaddr_un sa;
211 // keep a copy of real /tmp/.X11-unix directory in WHITELIST_TMP_DIR 126 // The -1 here is because we need space to inject a
212 int rv = mkdir(RUN_WHITELIST_X11_DIR, 1777); 127 // leading nul byte.
213 if (rv == -1) 128 int sun_pathmax = (int)(sizeof sa.sun_path - 1);
214 errExit("mkdir"); 129 assert((size_t)sun_pathmax == sizeof sa.sun_path - 1);
215 if (set_perms(RUN_WHITELIST_X11_DIR, 0, 0, 1777)) 130 int sun_pathlen;
216 errExit("set_perms");
217 131
218 if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) 132 int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
219 errExit("mount bind"); 133 if (sockfd == -1)
134 errExit("socket");
220 135
221 // mount tmpfs on /tmp/.X11-unix 136 for (i = 0; i < 100; i++) {
222 if (arg_debug || arg_debug_whitelists) 137 // We try display numbers in the range 21 through 1000.
223 printf("Mounting tmpfs on /tmp/.X11-unix directory\n"); 138 // Normal X servers typically use displays in the 0-10 range;
224 if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) 139 // ssh's X11 forwarding uses 10-20, and login screens
225 errExit("mounting tmpfs on /tmp"); 140 // (e.g. gdm3) may use displays above 1000.
226 fs_logger("tmpfs /tmp/.X11-unix"); 141 display = rand() % 979 + 21;
142
143 // The display number might be claimed by a server listening
144 // in _either_ the normal or the abstract namespace; they
145 // don't necessarily do both. The easiest way to check is
146 // to try to connect, both ways.
147 memset(&sa, 0, sizeof sa);
148 sa.sun_family = AF_UNIX;
149 sun_pathlen = snprintf(sa.sun_path, sun_pathmax,
150 "/tmp/.X11-unix/X%d", display);
151 if (sun_pathlen >= sun_pathmax) {
152 fprintf(stderr, "sun_path too small for display :%d"
153 " (only %d bytes usable)\n", display, sun_pathmax);
154 exit(1);
155 }
156
157 if (connect(sockfd, (struct sockaddr *)&sa,
158 offsetof(struct sockaddr_un, sun_path) + sun_pathlen + 1) == 0) {
159 close(sockfd);
160 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
161 if (sockfd == -1)
162 errExit("socket");
163 continue;
164 }
165 if (errno != ECONNREFUSED && errno != ENOENT)
166 errExit("connect");
167
168 // Name not claimed in the normal namespace; now try it
169 // in the abstract namespace. Note that abstract-namespace
170 // names are NOT nul-terminated; they extend to the length
171 // specified as the third argument to 'connect'.
172 memmove(sa.sun_path + 1, sa.sun_path, sun_pathlen + 1);
173 sa.sun_path[0] = '\0';
174 if (connect(sockfd, (struct sockaddr *)&sa,
175 offsetof(struct sockaddr_un, sun_path) + 1 + sun_pathlen) == 0) {
176 close(sockfd);
177 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
178 if (sockfd == -1)
179 errExit("socket");
180 continue;
181 }
182 if (errno != ECONNREFUSED && errno != ENOENT)
183 errExit("connect");
184
185 // This display number is unclaimed. Of course, it could
186 // be claimed before we get around to doing it...
187 found = 1;
188 break;
189 }
190 close(sockfd);
227 191
228 // create an empty file 192 if (!found) {
229 /* coverity[toctou] */ 193 fputs("Error: cannot find an unallocated X11 display number, "
230 FILE *fp = fopen(x11file, "w"); 194 "exiting...\n", stderr);
231 if (!fp) {
232 fprintf(stderr, "Error: cannot create empty file in x11 directory\n");
233 exit(1); 195 exit(1);
234 } 196 }
235 // set file properties 197 return display;
236 SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); 198}
237 fclose(fp);
238
239 // mount
240 char *wx11file;
241 if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1)
242 errExit("asprintf");
243 if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0)
244 errExit("mount bind");
245 fs_logger2("whitelist", x11file);
246
247 free(x11file);
248 free(wx11file);
249
250 // block access to RUN_WHITELIST_X11_DIR
251 if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, "none", MS_BIND, "mode=400,gid=0") == -1)
252 errExit("mount");
253 fs_logger2("blacklist", RUN_WHITELIST_X11_DIR);
254#endif 199#endif
255 200
256#endif 201
257}
258 202
259 203
260#ifdef HAVE_X11 204#ifdef HAVE_X11
@@ -277,7 +221,7 @@ void x11_start_xephyr(int argc, char **argv) {
277 drop_privs(0); 221 drop_privs(0);
278 222
279 // check xephyr 223 // check xephyr
280 if (x11_check_xephyr() == 0) { 224 if (!program_in_path("Xephyr")) {
281 fprintf(stderr, "\nError: Xephyr program was not found in /usr/bin directory, please install it:\n"); 225 fprintf(stderr, "\nError: Xephyr program was not found in /usr/bin directory, please install it:\n");
282 fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xserver-xephyr\n"); 226 fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xserver-xephyr\n");
283 fprintf(stderr, " Arch: sudo pacman -S xorg-server-xephyr\n"); 227 fprintf(stderr, " Arch: sudo pacman -S xorg-server-xephyr\n");
@@ -477,7 +421,7 @@ void x11_start_xpra(int argc, char **argv) {
477 drop_privs(0); 421 drop_privs(0);
478 422
479 // check xpra 423 // check xpra
480 if (x11_check_xpra() == 0) { 424 if (!program_in_path("xpra")) {
481 fprintf(stderr, "\nError: Xpra program was not found in /usr/bin directory, please install it:\n"); 425 fprintf(stderr, "\nError: Xpra program was not found in /usr/bin directory, please install it:\n");
482 fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xpra\n"); 426 fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xpra\n");
483 exit(0); 427 exit(0);
@@ -670,9 +614,9 @@ void x11_start(int argc, char **argv) {
670 } 614 }
671 615
672 // check xpra 616 // check xpra
673 if (x11_check_xpra() == 1) 617 if (program_in_path("xpra"))
674 x11_start_xpra(argc, argv); 618 x11_start_xpra(argc, argv);
675 else if (x11_check_xephyr() == 1) 619 else if (program_in_path("Xephyr"))
676 x11_start_xephyr(argc, argv); 620 x11_start_xephyr(argc, argv);
677 else { 621 else {
678 fprintf(stderr, "\nError: Xpra or Xephyr not found in /usr/bin directory, please install one of them:\n"); 622 fprintf(stderr, "\nError: Xpra or Xephyr not found in /usr/bin directory, please install one of them:\n");
@@ -684,58 +628,39 @@ void x11_start(int argc, char **argv) {
684 628
685#endif 629#endif
686 630
687void x11_block(void) { 631// Porting notes:
632//
633// 1. merge #1100 from zackw:
634// Attempting to run xauth -f directly on a file in /run/firejail/mnt/ directory fails on Debian 8
635// with this message:
636// xauth: timeout in locking authority file /run/firejail/mnt/sec.Xauthority-Qt5Mu4
637// Failed to create untrusted X cookie: xauth: exit 1
638// For this reason we run xauth on a file in a tmpfs filesystem mounted on /tmp. This was
639// a partial merge.
640//
641// 2. Since we cannot deal with the TOCTOU condition when mounting .Xauthority in user home
642// directory, we need to make sure /usr/bin/xauth executable is the real thing, and not
643// something picked up on $PATH.
644//
645void x11_xorg(void) {
688#ifdef HAVE_X11 646#ifdef HAVE_X11
689 mask_x11_abstract_socket = 1;
690 647
691 // check abstract socket presence and network namespace options 648 // check xauth utility is present in the system
692 if ((!arg_nonetwork && !cfg.bridge0.configured && !cfg.interface0.configured) 649 struct stat s;
693 && x11_abstract_sockets_present()) { 650 if (stat("/usr/bin/xauth", &s) == -1) {
694 fprintf(stderr, "ERROR: --x11=none specified, but abstract X11 socket still accessible.\n" 651 fprintf(stderr, "Error: xauth utility not found in PATH. Please install it:\n"
695 "Additional setup required. To block abstract X11 socket you can either:\n" 652 " Debian/Ubuntu/Mint: sudo apt-get install xauth\n");
696 " * use network namespace in firejail (--net=none, --net=...)\n"
697 " * add \"-nolisten local\" to xserver options\n"
698 " (eg. to your display manager config, or /etc/X11/xinit/xserverrc)\n");
699 exit(1); 653 exit(1);
700 } 654 }
701 655 if (s.st_uid != 0 && s.st_gid != 0) {
702 // blacklist sockets 656 fprintf(stderr, "Error: invalid /usr/bin/xauth executable\n");
703 profile_check_line("blacklist /tmp/.X11-unix", 0, NULL); 657 exit(1);
704 profile_add(strdup("blacklist /tmp/.X11-unix"));
705
706 // blacklist .Xauthority
707 profile_check_line("blacklist ${HOME}/.Xauthority", 0, NULL);
708 profile_add(strdup("blacklist ${HOME}/.Xauthority"));
709 char *xauthority = getenv("XAUTHORITY");
710 if (xauthority) {
711 char *line;
712 if (asprintf(&line, "blacklist %s", xauthority) == -1)
713 errExit("asprintf");
714 profile_check_line(line, 0, NULL);
715 profile_add(line);
716 }
717
718 // clear environment
719 env_store("DISPLAY", RMENV);
720 env_store("XAUTHORITY", RMENV);
721#endif
722}
723
724void x11_xorg(void) {
725#ifdef HAVE_X11
726 // destination - create an empty ~/.Xauthotrity file if it doesn't exist already, and use it as a mount point
727 char *dest;
728 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1)
729 errExit("asprintf");
730 struct stat s;
731 if (stat(dest, &s) == -1) {
732 // create an .Xauthority file
733 touch_file_as_user(dest, getuid(), getgid(), 0600);
734 } 658 }
735 659
736 // check xauth utility is present in the system 660 // get DISPLAY env
737 if (stat("/usr/bin/xauth", &s) == -1) { 661 char *display = getenv("DISPLAY");
738 fprintf(stderr, "Error: cannot find /usr/bin/xauth executable\n"); 662 if (!display) {
663 fputs("Error: --x11=xorg requires an 'outer' X11 server to use.\n", stderr);
739 exit(1); 664 exit(1);
740 } 665 }
741 666
@@ -743,7 +668,9 @@ void x11_xorg(void) {
743 if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) 668 if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
744 errExit("mounting /tmp"); 669 errExit("mounting /tmp");
745 670
746 // create a temporary .Xauthority file 671 // create the temporary .Xauthority file
672 if (arg_debug)
673 printf("Generating a new .Xauthority file\n");
747 char tmpfname[] = "/tmp/.tmpXauth-XXXXXX"; 674 char tmpfname[] = "/tmp/.tmpXauth-XXXXXX";
748 int fd = mkstemp(tmpfname); 675 int fd = mkstemp(tmpfname);
749 if (fd == -1) { 676 if (fd == -1) {
@@ -758,38 +685,48 @@ void x11_xorg(void) {
758 if (child < 0) 685 if (child < 0)
759 errExit("fork"); 686 errExit("fork");
760 if (child == 0) { 687 if (child == 0) {
761 // generate the new .Xauthority file using xauth utility
762 if (arg_debug)
763 printf("Generating a new .Xauthority file\n");
764 drop_privs(1); 688 drop_privs(1);
765
766 char *display = getenv("DISPLAY");
767 if (!display)
768 display = ":0.0";
769
770 clearenv(); 689 clearenv();
771 execlp("/usr/bin/xauth", "/usr/bin/xauth", "-f", tmpfname,
772 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL);
773
774#ifdef HAVE_GCOV 690#ifdef HAVE_GCOV
775 __gcov_flush(); 691 __gcov_flush();
776#endif 692#endif
777 _exit(0); 693 execlp("/usr/bin/xauth", "/usr/bin/xauth", "-f", tmpfname,
694 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL);
695
696 _exit(127);
697 }
698
699 // wait for the xauth process to finish
700 int status;
701 if (waitpid(child, &status, 0) != child)
702 errExit("waitpid");
703 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
704 /* success */
705 } else if (WIFEXITED(status)) {
706 fprintf(stderr, "Failed to create untrusted X cookie: xauth: exit %d\n",
707 WEXITSTATUS(status));
708 exit(1);
709 } else if (WIFSIGNALED(status)) {
710 fprintf(stderr, "Failed to create untrusted X cookie: xauth: %s\n",
711 strsignal(WTERMSIG(status)));
712 exit(1);
713 } else {
714 fprintf(stderr, "Failed to create untrusted X cookie: "
715 "xauth: un-decodable exit status %04x\n", status);
716 exit(1);
778 } 717 }
779 718
780 // wait for the child to finish 719 // ensure the file has the correct permissions and move it
781 waitpid(child, NULL, 0); 720 // into the correct location.
782
783 // check the file was created and set mode and ownership
784 if (stat(tmpfname, &s) == -1) { 721 if (stat(tmpfname, &s) == -1) {
785 fprintf(stderr, "Error: cannot create the new .Xauthority file\n"); 722 fprintf(stderr, "Error: .Xauthority file was mpt created\n");
786 exit(1); 723 exit(1);
787 } 724 }
788 if (set_perms(tmpfname, getuid(), getgid(), 0600)) 725 if (set_perms(tmpfname, getuid(), getgid(), 0600))
789 errExit("set_perms"); 726 errExit("set_perms");
790 727
791 // move the temporary file in RUN_XAUTHORITY_SEC_FILE in order to have it deleted 728 // move the temporary file in RUN_XAUTHORITY_SEC_FILE in order to have it deleted
792 // automatically when the sandbox is closed 729 // automatically when the sandbox is closed (rename doesn't work)
793 if (copy_file(tmpfname, RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600)) { // root needed 730 if (copy_file(tmpfname, RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600)) { // root needed
794 fprintf(stderr, "Error: cannot create the new .Xauthority file\n"); 731 fprintf(stderr, "Error: cannot create the new .Xauthority file\n");
795 exit(1); 732 exit(1);
@@ -798,17 +735,132 @@ void x11_xorg(void) {
798 errExit("set_perms"); 735 errExit("set_perms");
799 /* coverity[toctou] */ 736 /* coverity[toctou] */
800 unlink(tmpfname); 737 unlink(tmpfname);
738 umount("/tmp");
801 739
740
741 // Ensure there is already a file in the usual location, so that bind-mount below will work.
742 // todo: fix TOCTOU races, currently managed by imposing /usr/bin/xauth as executable
743 char *dest;
744 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1)
745 errExit("asprintf");
746 if (stat(dest, &s) == -1) {
747 // create an .Xauthority file
748 touch_file_as_user(dest, getuid(), getgid(), 0600);
749 }
750 if (is_link(dest)) {
751 fprintf(stderr, "Error: .Xauthority is a symbolic link\n");
752 exit(1);
753 }
754
802 // mount 755 // mount
803 if (mount(RUN_XAUTHORITY_SEC_FILE, dest, "none", MS_BIND, "mode=0600") == -1) { 756 if (mount(RUN_XAUTHORITY_SEC_FILE, dest, "none", MS_BIND, "mode=0600") == -1) {
804 fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); 757 fprintf(stderr, "Error: cannot mount the new .Xauthority file\n");
805 exit(1); 758 exit(1);
806 } 759 }
760 // just in case...
807 if (set_perms(dest, getuid(), getgid(), 0600)) 761 if (set_perms(dest, getuid(), getgid(), 0600))
808 errExit("set_perms"); 762 errExit("set_perms");
809 free(dest); 763 free(dest);
810
811 // unmount /tmp
812 umount("/tmp");
813#endif 764#endif
814} 765}
766
767void fs_x11(void) {
768#ifdef HAVE_X11
769 int display = x11_display();
770 if (display <= 0)
771 return;
772
773 char *x11file;
774 if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1)
775 errExit("asprintf");
776 struct stat x11stat;
777 if (stat(x11file, &x11stat) == -1 || !S_ISSOCK(x11stat.st_mode)) {
778 free(x11file);
779 return;
780 }
781
782 if (arg_debug || arg_debug_whitelists)
783 fprintf(stderr, "Masking all X11 sockets except %s\n", x11file);
784
785 // Move the real /tmp/.X11-unix to a scratch location
786 // so we can still access x11file after we mount a
787 // tmpfs over /tmp/.X11-unix.
788 int rv = mkdir(RUN_WHITELIST_X11_DIR, 0700);
789 if (rv == -1)
790 errExit("mkdir");
791 if (set_perms(RUN_WHITELIST_X11_DIR, 0, 0, 0700))
792 errExit("set_perms");
793
794 if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, 0, MS_BIND|MS_REC, 0) < 0)
795 errExit("mount bind");
796
797 // This directory must be mode 1777, or Xlib will barf.
798 if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs",
799 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC,
800 "mode=1777,uid=0,gid=0") < 0)
801 errExit("mounting tmpfs on /tmp/.X11-unix");
802 fs_logger("tmpfs /tmp/.X11-unix");
803
804 // create an empty file which will have the desired socket bind-mounted over it
805 int fd = open(x11file, O_RDWR|O_CREAT|O_EXCL, x11stat.st_mode & ~S_IFMT);
806 if (fd < 0)
807 errExit(x11file);
808 if (fchown(fd, x11stat.st_uid, x11stat.st_gid))
809 errExit("fchown");
810 close(fd);
811
812 // do the mount
813 char *wx11file;
814 if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1)
815 errExit("asprintf");
816 if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0)
817 errExit("mount bind");
818 fs_logger2("whitelist", x11file);
819
820 free(x11file);
821 free(wx11file);
822
823 // block access to RUN_WHITELIST_X11_DIR
824 if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, 0, MS_BIND, 0) < 0)
825 errExit("mount");
826 fs_logger2("blacklist", RUN_WHITELIST_X11_DIR);
827#endif
828}
829
830void x11_block(void) {
831#ifdef HAVE_X11
832 mask_x11_abstract_socket = 1;
833
834 // check abstract socket presence and network namespace options
835 if ((!arg_nonetwork && !cfg.bridge0.configured && !cfg.interface0.configured)
836 && x11_abstract_sockets_present()) {
837 fprintf(stderr, "ERROR: --x11=none specified, but abstract X11 socket still accessible.\n"
838 "Additional setup required. To block abstract X11 socket you can either:\n"
839 " * use network namespace in firejail (--net=none, --net=...)\n"
840 " * add \"-nolisten local\" to xserver options\n"
841 " (eg. to your display manager config, or /etc/X11/xinit/xserverrc)\n");
842 exit(1);
843 }
844
845 // blacklist sockets
846 profile_check_line("blacklist /tmp/.X11-unix", 0, NULL);
847 profile_add(strdup("blacklist /tmp/.X11-unix"));
848
849 // blacklist .Xauthority
850 profile_check_line("blacklist ${HOME}/.Xauthority", 0, NULL);
851 profile_add(strdup("blacklist ${HOME}/.Xauthority"));
852 char *xauthority = getenv("XAUTHORITY");
853 if (xauthority) {
854 char *line;
855 if (asprintf(&line, "blacklist %s", xauthority) == -1)
856 errExit("asprintf");
857 profile_check_line(line, 0, NULL);
858 profile_add(line);
859 }
860
861 // clear environment
862 env_store("DISPLAY", RMENV);
863 env_store("XAUTHORITY", RMENV);
864#endif
865}
866
diff --git a/test/fcopy/cmdline.exp b/test/fcopy/cmdline.exp
index 3ea33b01b..10dd8da58 100755
--- a/test/fcopy/cmdline.exp
+++ b/test/fcopy/cmdline.exp
@@ -10,7 +10,7 @@ match_max 100000
10send -- "/usr/lib/firejail/fcopy\r" 10send -- "/usr/lib/firejail/fcopy\r"
11expect { 11expect {
12 timeout {puts "TESTING ERROR 0\n";exit} 12 timeout {puts "TESTING ERROR 0\n";exit}
13 "files missing" 13 "arguments missing"
14} 14}
15expect { 15expect {
16 timeout {puts "TESTING ERROR 1\n";exit} 16 timeout {puts "TESTING ERROR 1\n";exit}
@@ -21,7 +21,7 @@ after 100
21send -- "/usr/lib/firejail/fcopy foo\r" 21send -- "/usr/lib/firejail/fcopy foo\r"
22expect { 22expect {
23 timeout {puts "TESTING ERROR 2\n";exit} 23 timeout {puts "TESTING ERROR 2\n";exit}
24 "files missing" 24 "arguments missing"
25} 25}
26expect { 26expect {
27 timeout {puts "TESTING ERROR 3\n";exit} 27 timeout {puts "TESTING ERROR 3\n";exit}
@@ -32,14 +32,14 @@ after 100
32send -- "/usr/lib/firejail/fcopy f%oo1 foo2\r" 32send -- "/usr/lib/firejail/fcopy f%oo1 foo2\r"
33expect { 33expect {
34 timeout {puts "TESTING ERROR 4\n";exit} 34 timeout {puts "TESTING ERROR 4\n";exit}
35 "invalid file name" 35 "invalid source file name"
36} 36}
37after 100 37after 100
38 38
39send -- "/usr/lib/firejail/fcopy foo1 f,oo2\r" 39send -- "/usr/lib/firejail/fcopy foo1 f,oo2\r"
40expect { 40expect {
41 timeout {puts "TESTING ERROR 5\n";exit} 41 timeout {puts "TESTING ERROR 5\n";exit}
42 "invalid file name" 42 "invalid dest file name"
43} 43}
44after 100 44after 100
45 45
diff --git a/test/fs/private-home.exp b/test/fs/private-home.exp
index f2f30914d..259eb4f9e 100755
--- a/test/fs/private-home.exp
+++ b/test/fs/private-home.exp
@@ -89,7 +89,7 @@ expect {
89 "Child process initialized" 89 "Child process initialized"
90} 90}
91after 100 91after 100
92send -- "file file ~/_firejail_test_link2\r" 92send -- "file ~/_firejail_test_link2\r"
93expect { 93expect {
94 timeout {puts "TESTING ERROR 11\n";exit} 94 timeout {puts "TESTING ERROR 11\n";exit}
95 "broken symbolic link" 95 "broken symbolic link"