aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/x11.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/firejail/x11.c')
-rw-r--r--src/firejail/x11.c522
1 files changed, 261 insertions, 261 deletions
diff --git a/src/firejail/x11.c b/src/firejail/x11.c
index f66848b7c..b72b46f0d 100644
--- a/src/firejail/x11.c
+++ b/src/firejail/x11.c
@@ -35,40 +35,41 @@
35// Parse the DISPLAY environment variable and return a display number. 35// Parse the DISPLAY environment variable and return a display number.
36// Returns -1 if DISPLAY is not set, or is set to anything other than :ddd. 36// Returns -1 if DISPLAY is not set, or is set to anything other than :ddd.
37int x11_display(void) { 37int x11_display(void) {
38 const char *display_str = getenv("DISPLAY"); 38 const char *display_str = getenv("DISPLAY");
39 char *endp; 39 char *endp;
40 unsigned long display; 40 unsigned long display;
41 41
42 if (!display_str) { 42 if (!display_str) {
43 if (arg_debug) 43 if (arg_debug)
44 fputs("DISPLAY is not set\n", stderr); 44 fputs("DISPLAY is not set\n", stderr);
45 return -1; 45 return -1;
46 } 46 }
47 47
48 if (display_str[0] != ':' || display_str[1] < '0' || display_str[1] > '9') { 48 if (display_str[0] != ':' || display_str[1] < '0' || display_str[1] > '9') {
49 if (arg_debug) 49 if (arg_debug)
50 fprintf(stderr, "unsupported DISPLAY form '%s'\n", display_str); 50 fprintf(stderr, "unsupported DISPLAY form '%s'\n", display_str);
51 return -1; 51 return -1;
52 } 52 }
53 53
54 errno = 0; 54 errno = 0;
55 display = strtoul(display_str+1, &endp, 10); 55 display = strtoul(display_str+1, &endp, 10);
56 if (endp == display_str+1 || (*endp != '\0' && *endp != '.')) { // handling DISPLAY=:0 and also :0.0 56 // handling DISPLAY=:0 and also :0.0
57 if (arg_debug) 57 if (endp == display_str+1 || (*endp != '\0' && *endp != '.')) {
58 fprintf(stderr, "unsupported DISPLAY form '%s'\n", display_str); 58 if (arg_debug)
59 return -1; 59 fprintf(stderr, "unsupported DISPLAY form '%s'\n", display_str);
60 } 60 return -1;
61 if (errno || display > (unsigned long)INT_MAX) { 61 }
62 if (arg_debug) 62 if (errno || display > (unsigned long)INT_MAX) {
63 fprintf(stderr, "display number %s is outside the valid range\n", 63 if (arg_debug)
64 display_str+1); 64 fprintf(stderr, "display number %s is outside the valid range\n",
65 return -1; 65 display_str+1);
66 } 66 return -1;
67 67 }
68 if (arg_debug) 68
69 fprintf(stderr, "DISPLAY=%s parsed as %lu\n", display_str, display); 69 if (arg_debug)
70 70 fprintf(stderr, "DISPLAY=%s parsed as %lu\n", display_str, display);
71 return (int)display; 71
72 return (int)display;
72} 73}
73 74
74 75
@@ -76,34 +77,34 @@ int x11_display(void) {
76// check for X11 abstract sockets 77// check for X11 abstract sockets
77static int x11_abstract_sockets_present(void) { 78static int x11_abstract_sockets_present(void) {
78 79
79 EUID_ROOT(); // grsecurity fix 80 EUID_ROOT(); // grsecurity fix
80 FILE *fp = fopen("/proc/net/unix", "r"); 81 FILE *fp = fopen("/proc/net/unix", "r");
81 if (!fp) 82 if (!fp)
82 errExit("fopen"); 83 errExit("fopen");
83 EUID_USER(); 84 EUID_USER();
84 85
85 char *linebuf = 0; 86 char *linebuf = 0;
86 size_t bufsz = 0; 87 size_t bufsz = 0;
87 int found = 0; 88 int found = 0;
88 errno = 0; 89 errno = 0;
89 90
90 for (;;) { 91 for (;;) {
91 if (getline(&linebuf, &bufsz, fp) == -1) { 92 if (getline(&linebuf, &bufsz, fp) == -1) {
92 if (errno) 93 if (errno)
93 errExit("getline"); 94 errExit("getline");
94 break; 95 break;
95 } 96 }
96 // The last space-separated field in 'linebuf' is the 97 // The last space-separated field in 'linebuf' is the
97 // pathname of the socket. Abstract sockets' pathnames 98 // pathname of the socket. Abstract sockets' pathnames
98 // all begin with '@/', normal ones begin with '/'. 99 // all begin with '@/', normal ones begin with '/'.
99 char *p = strrchr(linebuf, ' '); 100 char *p = strrchr(linebuf, ' ');
100 if (!p) { 101 if (!p) {
101 fputs("error parsing /proc/net/unix\n", stderr); 102 fputs("error parsing /proc/net/unix\n", stderr);
102 exit(1); 103 exit(1);
103 } 104 }
104 if (strncmp(p+1, "@/tmp/.X11-unix/", 16) == 0) { 105 if (strncmp(p+1, "@/tmp/.X11-unix/", 16) == 0) {
105 found = 1; 106 found = 1;
106 break; 107 break;
107 } 108 }
108 } 109 }
109 110
@@ -112,102 +113,99 @@ static int x11_abstract_sockets_present(void) {
112 return found; 113 return found;
113} 114}
114 115
116
115// Choose a random, unallocated display number. This has an inherent 117// Choose a random, unallocated display number. This has an inherent
116// and unavoidable TOCTOU race, since we cannot create either the 118// and unavoidable TOCTOU race, since we cannot create either the
117// socket or a lockfile ourselves. 119// socket or a lockfile ourselves.
118static int random_display_number(void) { 120static int random_display_number(void) {
119 int display; 121 int display;
120 int found = 0; 122 int found = 0;
121 int i; 123 int i;
122 124
123 struct sockaddr_un sa; 125 struct sockaddr_un sa;
124 // The -1 here is because we need space to inject a 126 // The -1 here is because we need space to inject a
125 // leading nul byte. 127 // leading nul byte.
126 int sun_pathmax = (int)(sizeof sa.sun_path - 1); 128 int sun_pathmax = (int)(sizeof sa.sun_path - 1);
127 assert((size_t)sun_pathmax == sizeof sa.sun_path - 1); 129 assert((size_t)sun_pathmax == sizeof sa.sun_path - 1);
128 int sun_pathlen; 130 int sun_pathlen;
129 131
130 int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 132 int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
131 if (sockfd == -1) 133 if (sockfd == -1)
132 errExit("socket"); 134 errExit("socket");
133 135
134 for (i = 0; i < 100; i++) { 136 for (i = 0; i < 100; i++) {
135 // We try display numbers in the range 21 through 1000. 137 // We try display numbers in the range 21 through 1000.
136 // Normal X servers typically use displays in the 0-10 range; 138 // Normal X servers typically use displays in the 0-10 range;
137 // ssh's X11 forwarding uses 10-20, and login screens 139 // ssh's X11 forwarding uses 10-20, and login screens
138 // (e.g. gdm3) may use displays above 1000. 140 // (e.g. gdm3) may use displays above 1000.
139 display = rand() % 979 + 21; 141 display = rand() % 979 + 21;
140 142
141 // The display number might be claimed by a server listening 143 // The display number might be claimed by a server listening
142 // in _either_ the normal or the abstract namespace; they 144 // in _either_ the normal or the abstract namespace; they
143 // don't necessarily do both. The easiest way to check is 145 // don't necessarily do both. The easiest way to check is
144 // to try to connect, both ways. 146 // to try to connect, both ways.
145 memset(&sa, 0, sizeof sa); 147 memset(&sa, 0, sizeof sa);
146 sa.sun_family = AF_UNIX; 148 sa.sun_family = AF_UNIX;
147 sun_pathlen = snprintf(sa.sun_path, sun_pathmax, 149 sun_pathlen = snprintf(sa.sun_path, sun_pathmax,
148 "/tmp/.X11-unix/X%d", display); 150 "/tmp/.X11-unix/X%d", display);
149 if (sun_pathlen >= sun_pathmax) { 151 if (sun_pathlen >= sun_pathmax) {
150 fprintf(stderr, "sun_path too small for display :%d" 152 fprintf(stderr, "sun_path too small for display :%d"
151 " (only %d bytes usable)\n", display, sun_pathmax); 153 " (only %d bytes usable)\n", display, sun_pathmax);
152 exit(1); 154 exit(1);
153 } 155 }
154 156
155 if (connect(sockfd, (struct sockaddr *)&sa, 157 if (connect(sockfd, (struct sockaddr *)&sa,
156 offsetof(struct sockaddr_un, sun_path) + sun_pathlen + 1) == 0) { 158 offsetof(struct sockaddr_un, sun_path) + sun_pathlen + 1) == 0) {
157 close(sockfd); 159 close(sockfd);
158 sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 160 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
159 if (sockfd == -1) 161 if (sockfd == -1)
160 errExit("socket"); 162 errExit("socket");
161 continue; 163 continue;
162 } 164 }
163 if (errno != ECONNREFUSED && errno != ENOENT) 165 if (errno != ECONNREFUSED && errno != ENOENT)
164 errExit("connect"); 166 errExit("connect");
165 167
166 // Name not claimed in the normal namespace; now try it 168 // Name not claimed in the normal namespace; now try it
167 // in the abstract namespace. Note that abstract-namespace 169 // in the abstract namespace. Note that abstract-namespace
168 // names are NOT nul-terminated; they extend to the length 170 // names are NOT nul-terminated; they extend to the length
169 // specified as the third argument to 'connect'. 171 // specified as the third argument to 'connect'.
170 memmove(sa.sun_path + 1, sa.sun_path, sun_pathlen + 1); 172 memmove(sa.sun_path + 1, sa.sun_path, sun_pathlen + 1);
171 sa.sun_path[0] = '\0'; 173 sa.sun_path[0] = '\0';
172 if (connect(sockfd, (struct sockaddr *)&sa, 174 if (connect(sockfd, (struct sockaddr *)&sa,
173 offsetof(struct sockaddr_un, sun_path) + 1 + sun_pathlen) == 0) { 175 offsetof(struct sockaddr_un, sun_path) + 1 + sun_pathlen) == 0) {
174 close(sockfd); 176 close(sockfd);
175 sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 177 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
176 if (sockfd == -1) 178 if (sockfd == -1)
177 errExit("socket"); 179 errExit("socket");
178 continue; 180 continue;
179 } 181 }
180 if (errno != ECONNREFUSED && errno != ENOENT) 182 if (errno != ECONNREFUSED && errno != ENOENT)
181 errExit("connect"); 183 errExit("connect");
182 184
183 // This display number is unclaimed. Of course, it could 185 // This display number is unclaimed. Of course, it could
184 // be claimed before we get around to doing it... 186 // be claimed before we get around to doing it...
185 found = 1; 187 found = 1;
186 break; 188 break;
187 } 189 }
188 close(sockfd); 190 close(sockfd);
189 191
190 if (!found) { 192 if (!found) {
191 fputs("Error: cannot find an unallocated X11 display number, " 193 fputs("Error: cannot find an unallocated X11 display number, "
192 "exiting...\n", stderr); 194 "exiting...\n", stderr);
193 exit(1); 195 exit(1);
194 } 196 }
195 return display; 197 return display;
196} 198}
197#endif 199#endif
198 200
199
200
201
202#ifdef HAVE_X11 201#ifdef HAVE_X11
203
204void x11_start_xvfb(int argc, char **argv) { 202void x11_start_xvfb(int argc, char **argv) {
205 EUID_ASSERT(); 203 EUID_ASSERT();
206 int i; 204 int i;
207 struct stat s; 205 struct stat s;
208 pid_t jail = 0; 206 pid_t jail = 0;
209 pid_t server = 0; 207 pid_t server = 0;
210 208
211 setenv("FIREJAIL_X11", "yes", 1); 209 setenv("FIREJAIL_X11", "yes", 1);
212 210
213 // mever try to run X servers as root!!! 211 // mever try to run X servers as root!!!
@@ -219,12 +217,12 @@ void x11_start_xvfb(int argc, char **argv) {
219 217
220 // check xephyr 218 // check xephyr
221 if (!program_in_path("Xvfb")) { 219 if (!program_in_path("Xvfb")) {
222 fprintf(stderr, "\nError: xvfb program was not found in /usr/bin directory, please install it:\n"); 220 fprintf(stderr, "\nError: Xvfb program was not found in /usr/bin directory, please install it:\n");
223 fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xvfb\n"); 221 fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xvfb\n");
224 fprintf(stderr, " Arch: sudo pacman -S xorg-server-xvfb\n"); 222 fprintf(stderr, " Arch: sudo pacman -S xorg-server-xvfb\n");
225 exit(0); 223 exit(0);
226 } 224 }
227 225
228 int display = random_display_number(); 226 int display = random_display_number();
229 char *display_str; 227 char *display_str;
230 if (asprintf(&display_str, ":%d", display) == -1) 228 if (asprintf(&display_str, ":%d", display) == -1)
@@ -232,10 +230,12 @@ void x11_start_xvfb(int argc, char **argv) {
232 230
233 assert(xvfb_screen); 231 assert(xvfb_screen);
234 232
235 char *server_argv[256] = { "Xvfb", display_str, "-screen", "0", xvfb_screen }; // rest initialyzed to NULL 233 char *server_argv[256] = { // rest initialyzed to NULL
234 "Xvfb", display_str, "-screen", "0", xvfb_screen
235 };
236 unsigned pos = 0; 236 unsigned pos = 0;
237 while (server_argv[pos] != NULL) pos++; 237 while (server_argv[pos] != NULL) pos++;
238 assert(xvfb_extra_params); // should be "" if empty 238 assert(xvfb_extra_params); // should be "" if empty
239 239
240 // parse xvfb_extra_params 240 // parse xvfb_extra_params
241 // very basic quoting support 241 // very basic quoting support
@@ -248,11 +248,13 @@ void x11_start_xvfb(int argc, char **argv) {
248 for (i = 0; i < (int) strlen(xvfb_extra_params); i++) { 248 for (i = 0; i < (int) strlen(xvfb_extra_params); i++) {
249 if (temp[i] == '\"') { 249 if (temp[i] == '\"') {
250 dquote = !dquote; 250 dquote = !dquote;
251 if (dquote) temp[i] = '\0'; // replace closing quote by \0 251 // replace closing quote by \0
252 if (dquote) temp[i] = '\0';
252 } 253 }
253 if (temp[i] == '\'') { 254 if (temp[i] == '\'') {
254 squote = !squote; 255 squote = !squote;
255 if (squote) temp[i] = '\0'; // replace closing quote by \0 256 // replace closing quote by \0
257 if (squote) temp[i] = '\0';
256 } 258 }
257 if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0'; 259 if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0';
258 if (dquote && squote) { 260 if (dquote && squote) {
@@ -274,12 +276,12 @@ void x11_start_xvfb(int argc, char **argv) {
274 else if (temp[i] == '\0' && temp[i+1] != '\0') server_argv[pos++] = temp + i + 1; 276 else if (temp[i] == '\0' && temp[i+1] != '\0') server_argv[pos++] = temp + i + 1;
275 } 277 }
276 } 278 }
277 279
278 server_argv[pos++] = NULL; 280 server_argv[pos++] = NULL;
279 281
280 assert(pos < (sizeof(server_argv)/sizeof(*server_argv))); // no overrun 282 assert(pos < (sizeof(server_argv)/sizeof(*server_argv))); // no overrun
281 assert(server_argv[pos-1] == NULL); // last element is null 283 assert(server_argv[pos-1] == NULL); // last element is null
282 284
283 if (arg_debug) { 285 if (arg_debug) {
284 size_t i = 0; 286 size_t i = 0;
285 printf("xvfb server:"); 287 printf("xvfb server:");
@@ -294,20 +296,14 @@ void x11_start_xvfb(int argc, char **argv) {
294 char *jail_argv[argc+2]; 296 char *jail_argv[argc+2];
295 int j = 0; 297 int j = 0;
296 for (i = 0; i < argc; i++) { 298 for (i = 0; i < argc; i++) {
297 if (strcmp(argv[i], "--x11") == 0) 299 if (strncmp(argv[i], "--x11", 5) == 0)
298 continue;
299 if (strcmp(argv[i], "--x11=xpra") == 0)
300 continue;
301 if (strcmp(argv[i], "--x11=xephyr") == 0)
302 continue;
303 if (strcmp(argv[i], "--x11=xvfb") == 0)
304 continue; 300 continue;
305 jail_argv[j] = argv[i]; 301 jail_argv[j] = argv[i];
306 j++; 302 j++;
307 } 303 }
308 jail_argv[j] = NULL; 304 jail_argv[j] = NULL;
309 305
310 assert(j < argc+2); // no overrun 306 assert(j < argc+2); // no overrun
311 307
312 if (arg_debug) { 308 if (arg_debug) {
313 size_t i = 0; 309 size_t i = 0;
@@ -318,7 +314,7 @@ void x11_start_xvfb(int argc, char **argv) {
318 } 314 }
319 putchar('\n'); 315 putchar('\n');
320 } 316 }
321 317
322 server = fork(); 318 server = fork();
323 if (server < 0) 319 if (server < 0)
324 errExit("fork"); 320 errExit("fork");
@@ -327,7 +323,7 @@ void x11_start_xvfb(int argc, char **argv) {
327 printf("Starting xvfb...\n"); 323 printf("Starting xvfb...\n");
328 324
329 // running without privileges - see drop_privs call above 325 // running without privileges - see drop_privs call above
330 assert(getenv("LD_PRELOAD") == NULL); 326 assert(getenv("LD_PRELOAD") == NULL);
331 execvp(server_argv[0], server_argv); 327 execvp(server_argv[0], server_argv);
332 perror("execvp"); 328 perror("execvp");
333 _exit(1); 329 _exit(1);
@@ -347,17 +343,17 @@ void x11_start_xvfb(int argc, char **argv) {
347 if (stat(fname, &s) == 0) 343 if (stat(fname, &s) == 0)
348 break; 344 break;
349 }; 345 };
350 346
351 if (n == 10) { 347 if (n == 10) {
352 fprintf(stderr, "Error: failed to start xephyr\n"); 348 fprintf(stderr, "Error: failed to start xephyr\n");
353 exit(1); 349 exit(1);
354 } 350 }
355 free(fname); 351 free(fname);
356 352
357 if (arg_debug) { 353 if (arg_debug) {
358 printf("X11 sockets: "); fflush(0); 354 printf("X11 sockets: "); fflush(0);
359 int rv = system("ls /tmp/.X11-unix"); 355 int rv = system("ls /tmp/.X11-unix");
360 (void) rv; 356 (void) rv;
361 } 357 }
362 358
363 setenv("DISPLAY", display_str, 1); 359 setenv("DISPLAY", display_str, 1);
@@ -370,7 +366,7 @@ void x11_start_xvfb(int argc, char **argv) {
370 printf("\n*** Attaching to Xvfb display %d ***\n\n", display); 366 printf("\n*** Attaching to Xvfb display %d ***\n\n", display);
371 367
372 // running without privileges - see drop_privs call above 368 // running without privileges - see drop_privs call above
373 assert(getenv("LD_PRELOAD") == NULL); 369 assert(getenv("LD_PRELOAD") == NULL);
374 execvp(jail_argv[0], jail_argv); 370 execvp(jail_argv[0], jail_argv);
375 perror("execvp"); 371 perror("execvp");
376 _exit(1); 372 _exit(1);
@@ -386,20 +382,20 @@ void x11_start_xvfb(int argc, char **argv) {
386 // see which process terminated and kill other 382 // see which process terminated and kill other
387 if (pid == server) { 383 if (pid == server) {
388 kill(jail, SIGTERM); 384 kill(jail, SIGTERM);
389 } else if (pid == jail) { 385 }
386 else if (pid == jail) {
390 kill(server, SIGTERM); 387 kill(server, SIGTERM);
391 } 388 }
392 389
393 // without this closing Xephyr window may mess your terminal: 390 // without this closing Xephyr window may mess your terminal:
394 // "monitoring" process will release terminal before 391 // "monitoring" process will release terminal before
395 // jail process ends and releases terminal 392 // jail process ends and releases terminal
396 wait(NULL); // fulneral 393 wait(NULL); // fulneral
397 394
398 exit(0); 395 exit(0);
399} 396}
400 397
401 398
402
403//$ Xephyr -ac -br -noreset -screen 800x600 :22 & 399//$ Xephyr -ac -br -noreset -screen 800x600 :22 &
404//$ DISPLAY=:22 firejail --net=eth0 --blacklist=/tmp/.X11-unix/x0 firefox 400//$ DISPLAY=:22 firejail --net=eth0 --blacklist=/tmp/.X11-unix/x0 firefox
405void x11_start_xephyr(int argc, char **argv) { 401void x11_start_xephyr(int argc, char **argv) {
@@ -408,7 +404,7 @@ void x11_start_xephyr(int argc, char **argv) {
408 struct stat s; 404 struct stat s;
409 pid_t jail = 0; 405 pid_t jail = 0;
410 pid_t server = 0; 406 pid_t server = 0;
411 407
412 setenv("FIREJAIL_X11", "yes", 1); 408 setenv("FIREJAIL_X11", "yes", 1);
413 409
414 // unfortunately, xephyr does a number of weird things when started by root user!!! 410 // unfortunately, xephyr does a number of weird things when started by root user!!!
@@ -425,14 +421,16 @@ void x11_start_xephyr(int argc, char **argv) {
425 fprintf(stderr, " Arch: sudo pacman -S xorg-server-xephyr\n"); 421 fprintf(stderr, " Arch: sudo pacman -S xorg-server-xephyr\n");
426 exit(0); 422 exit(0);
427 } 423 }
428 424
429 int display = random_display_number(); 425 int display = random_display_number();
430 char *display_str; 426 char *display_str;
431 if (asprintf(&display_str, ":%d", display) == -1) 427 if (asprintf(&display_str, ":%d", display) == -1)
432 errExit("asprintf"); 428 errExit("asprintf");
433 429
434 assert(xephyr_screen); 430 assert(xephyr_screen);
435 char *server_argv[256] = { "Xephyr", "-ac", "-br", "-noreset", "-screen", xephyr_screen }; // rest initialyzed to NULL 431 char *server_argv[256] = { // rest initialyzed to NULL
432 "Xephyr", "-ac", "-br", "-noreset", "-screen", xephyr_screen
433 };
436 unsigned pos = 0; 434 unsigned pos = 0;
437 while (server_argv[pos] != NULL) pos++; 435 while (server_argv[pos] != NULL) pos++;
438 if (checkcfg(CFG_XEPHYR_WINDOW_TITLE)) { 436 if (checkcfg(CFG_XEPHYR_WINDOW_TITLE)) {
@@ -440,7 +438,7 @@ void x11_start_xephyr(int argc, char **argv) {
440 server_argv[pos++] = "firejail x11 sandbox"; 438 server_argv[pos++] = "firejail x11 sandbox";
441 } 439 }
442 440
443 assert(xephyr_extra_params); // should be "" if empty 441 assert(xephyr_extra_params); // should be "" if empty
444 442
445 // parse xephyr_extra_params 443 // parse xephyr_extra_params
446 // very basic quoting support 444 // very basic quoting support
@@ -453,11 +451,13 @@ void x11_start_xephyr(int argc, char **argv) {
453 for (i = 0; i < (int) strlen(xephyr_extra_params); i++) { 451 for (i = 0; i < (int) strlen(xephyr_extra_params); i++) {
454 if (temp[i] == '\"') { 452 if (temp[i] == '\"') {
455 dquote = !dquote; 453 dquote = !dquote;
456 if (dquote) temp[i] = '\0'; // replace closing quote by \0 454 // replace closing quote by \0
455 if (dquote) temp[i] = '\0';
457 } 456 }
458 if (temp[i] == '\'') { 457 if (temp[i] == '\'') {
459 squote = !squote; 458 squote = !squote;
460 if (squote) temp[i] = '\0'; // replace closing quote by \0 459 // replace closing quote by \0
460 if (squote) temp[i] = '\0';
461 } 461 }
462 if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0'; 462 if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0';
463 if (dquote && squote) { 463 if (dquote && squote) {
@@ -479,13 +479,14 @@ void x11_start_xephyr(int argc, char **argv) {
479 else if (temp[i] == '\0' && temp[i+1] != '\0') server_argv[pos++] = temp + i + 1; 479 else if (temp[i] == '\0' && temp[i+1] != '\0') server_argv[pos++] = temp + i + 1;
480 } 480 }
481 } 481 }
482 482
483 server_argv[pos++] = display_str; 483 server_argv[pos++] = display_str;
484 server_argv[pos++] = NULL; 484 server_argv[pos++] = NULL;
485 485
486 assert(pos < (sizeof(server_argv)/sizeof(*server_argv))); // no overrun 486 // no overrun
487 assert(server_argv[pos-1] == NULL); // last element is null 487 assert(pos < (sizeof(server_argv)/sizeof(*server_argv)));
488 488 assert(server_argv[pos-1] == NULL); // last element is null
489
489 if (arg_debug) { 490 if (arg_debug) {
490 size_t i = 0; 491 size_t i = 0;
491 printf("xephyr server:"); 492 printf("xephyr server:");
@@ -500,18 +501,14 @@ void x11_start_xephyr(int argc, char **argv) {
500 char *jail_argv[argc+2]; 501 char *jail_argv[argc+2];
501 int j = 0; 502 int j = 0;
502 for (i = 0; i < argc; i++) { 503 for (i = 0; i < argc; i++) {
503 if (strcmp(argv[i], "--x11") == 0) 504 if (strncmp(argv[i], "--x11", 5) == 0)
504 continue;
505 if (strcmp(argv[i], "--x11=xpra") == 0)
506 continue;
507 if (strcmp(argv[i], "--x11=xephyr") == 0)
508 continue; 505 continue;
509 jail_argv[j] = argv[i]; 506 jail_argv[j] = argv[i];
510 j++; 507 j++;
511 } 508 }
512 jail_argv[j] = NULL; 509 jail_argv[j] = NULL;
513 510
514 assert(j < argc+2); // no overrun 511 assert(j < argc+2); // no overrun
515 512
516 if (arg_debug) { 513 if (arg_debug) {
517 size_t i = 0; 514 size_t i = 0;
@@ -522,7 +519,7 @@ void x11_start_xephyr(int argc, char **argv) {
522 } 519 }
523 putchar('\n'); 520 putchar('\n');
524 } 521 }
525 522
526 server = fork(); 523 server = fork();
527 if (server < 0) 524 if (server < 0)
528 errExit("fork"); 525 errExit("fork");
@@ -531,7 +528,7 @@ void x11_start_xephyr(int argc, char **argv) {
531 printf("Starting xephyr...\n"); 528 printf("Starting xephyr...\n");
532 529
533 // running without privileges - see drop_privs call above 530 // running without privileges - see drop_privs call above
534 assert(getenv("LD_PRELOAD") == NULL); 531 assert(getenv("LD_PRELOAD") == NULL);
535 execvp(server_argv[0], server_argv); 532 execvp(server_argv[0], server_argv);
536 perror("execvp"); 533 perror("execvp");
537 _exit(1); 534 _exit(1);
@@ -551,17 +548,17 @@ void x11_start_xephyr(int argc, char **argv) {
551 if (stat(fname, &s) == 0) 548 if (stat(fname, &s) == 0)
552 break; 549 break;
553 }; 550 };
554 551
555 if (n == 10) { 552 if (n == 10) {
556 fprintf(stderr, "Error: failed to start xephyr\n"); 553 fprintf(stderr, "Error: failed to start xephyr\n");
557 exit(1); 554 exit(1);
558 } 555 }
559 free(fname); 556 free(fname);
560 557
561 if (arg_debug) { 558 if (arg_debug) {
562 printf("X11 sockets: "); fflush(0); 559 printf("X11 sockets: "); fflush(0);
563 int rv = system("ls /tmp/.X11-unix"); 560 int rv = system("ls /tmp/.X11-unix");
564 (void) rv; 561 (void) rv;
565 } 562 }
566 563
567 setenv("DISPLAY", display_str, 1); 564 setenv("DISPLAY", display_str, 1);
@@ -574,7 +571,7 @@ void x11_start_xephyr(int argc, char **argv) {
574 printf("\n*** Attaching to Xephyr display %d ***\n\n", display); 571 printf("\n*** Attaching to Xephyr display %d ***\n\n", display);
575 572
576 // running without privileges - see drop_privs call above 573 // running without privileges - see drop_privs call above
577 assert(getenv("LD_PRELOAD") == NULL); 574 assert(getenv("LD_PRELOAD") == NULL);
578 execvp(jail_argv[0], jail_argv); 575 execvp(jail_argv[0], jail_argv);
579 perror("execvp"); 576 perror("execvp");
580 _exit(1); 577 _exit(1);
@@ -590,25 +587,27 @@ void x11_start_xephyr(int argc, char **argv) {
590 // see which process terminated and kill other 587 // see which process terminated and kill other
591 if (pid == server) { 588 if (pid == server) {
592 kill(jail, SIGTERM); 589 kill(jail, SIGTERM);
593 } else if (pid == jail) { 590 }
591 else if (pid == jail) {
594 kill(server, SIGTERM); 592 kill(server, SIGTERM);
595 } 593 }
596 594
597 // without this closing Xephyr window may mess your terminal: 595 // without this closing Xephyr window may mess your terminal:
598 // "monitoring" process will release terminal before 596 // "monitoring" process will release terminal before
599 // jail process ends and releases terminal 597 // jail process ends and releases terminal
600 wait(NULL); // fulneral 598 wait(NULL); // fulneral
601 599
602 exit(0); 600 exit(0);
603} 601}
604 602
603
605void x11_start_xpra(int argc, char **argv) { 604void x11_start_xpra(int argc, char **argv) {
606 EUID_ASSERT(); 605 EUID_ASSERT();
607 int i; 606 int i;
608 struct stat s; 607 struct stat s;
609 pid_t client = 0; 608 pid_t client = 0;
610 pid_t server = 0; 609 pid_t server = 0;
611 610
612 setenv("FIREJAIL_X11", "yes", 1); 611 setenv("FIREJAIL_X11", "yes", 1);
613 612
614 // unfortunately, xpra does a number of weird things when started by root user!!! 613 // unfortunately, xpra does a number of weird things when started by root user!!!
@@ -624,7 +623,7 @@ void x11_start_xpra(int argc, char **argv) {
624 fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xpra\n"); 623 fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xpra\n");
625 exit(0); 624 exit(0);
626 } 625 }
627 626
628 int display = random_display_number(); 627 int display = random_display_number();
629 char *display_str; 628 char *display_str;
630 if (asprintf(&display_str, ":%d", display) == -1) 629 if (asprintf(&display_str, ":%d", display) == -1)
@@ -653,9 +652,9 @@ void x11_start_xpra(int argc, char **argv) {
653 dup2(fd_null,1); 652 dup2(fd_null,1);
654 dup2(fd_null,2); 653 dup2(fd_null,2);
655 } 654 }
656 655
657 // running without privileges - see drop_privs call above 656 // running without privileges - see drop_privs call above
658 assert(getenv("LD_PRELOAD") == NULL); 657 assert(getenv("LD_PRELOAD") == NULL);
659 execvp(server_argv[0], server_argv); 658 execvp(server_argv[0], server_argv);
660 perror("execvp"); 659 perror("execvp");
661 _exit(1); 660 _exit(1);
@@ -675,18 +674,18 @@ void x11_start_xpra(int argc, char **argv) {
675 if (stat(fname, &s) == 0) 674 if (stat(fname, &s) == 0)
676 break; 675 break;
677 } 676 }
678 677
679 if (n == 10) { 678 if (n == 10) {
680 fprintf(stderr, "Error: failed to start xpra\n"); 679 fprintf(stderr, "Error: failed to start xpra\n");
681 exit(1); 680 exit(1);
682 } 681 }
683 free(fname); 682 free(fname);
684 683
685 if (arg_debug) { 684 if (arg_debug) {
686 printf("X11 sockets: "); fflush(0); 685 printf("X11 sockets: "); fflush(0);
687 int rv = system("ls /tmp/.X11-unix"); 686 int rv = system("ls /tmp/.X11-unix");
688 (void) rv; 687 (void) rv;
689 } 688 }
690 689
691 // build attach command 690 // build attach command
692 char *attach_argv[] = { "xpra", "--title=\"firejail x11 sandbox\"", "attach", display_str, NULL }; 691 char *attach_argv[] = { "xpra", "--title=\"firejail x11 sandbox\"", "attach", display_str, NULL };
@@ -706,7 +705,7 @@ void x11_start_xpra(int argc, char **argv) {
706 printf("\n*** Attaching to xpra display %d ***\n\n", display); 705 printf("\n*** Attaching to xpra display %d ***\n\n", display);
707 706
708 // running without privileges - see drop_privs call above 707 // running without privileges - see drop_privs call above
709 assert(getenv("LD_PRELOAD") == NULL); 708 assert(getenv("LD_PRELOAD") == NULL);
710 execvp(attach_argv[0], attach_argv); 709 execvp(attach_argv[0], attach_argv);
711 perror("execvp"); 710 perror("execvp");
712 _exit(1); 711 _exit(1);
@@ -718,11 +717,7 @@ void x11_start_xpra(int argc, char **argv) {
718 char *firejail_argv[argc+2]; 717 char *firejail_argv[argc+2];
719 int pos = 0; 718 int pos = 0;
720 for (i = 0; i < argc; i++) { 719 for (i = 0; i < argc; i++) {
721 if (strcmp(argv[i], "--x11") == 0) 720 if (strncmp(argv[i], "--x11", 5) == 0)
722 continue;
723 if (strcmp(argv[i], "--x11=xpra") == 0)
724 continue;
725 if (strcmp(argv[i], "--x11=xephyr") == 0)
726 continue; 721 continue;
727 firejail_argv[pos] = argv[i]; 722 firejail_argv[pos] = argv[i];
728 pos++; 723 pos++;
@@ -738,8 +733,8 @@ void x11_start_xpra(int argc, char **argv) {
738 errExit("fork"); 733 errExit("fork");
739 if (jail == 0) { 734 if (jail == 0) {
740 // running without privileges - see drop_privs call above 735 // running without privileges - see drop_privs call above
741 assert(getenv("LD_PRELOAD") == NULL); 736 assert(getenv("LD_PRELOAD") == NULL);
742 if (firejail_argv[0]) // shut up llvm scan-build 737 if (firejail_argv[0]) // shut up llvm scan-build
743 execvp(firejail_argv[0], firejail_argv); 738 execvp(firejail_argv[0], firejail_argv);
744 perror("execvp"); 739 perror("execvp");
745 exit(1); 740 exit(1);
@@ -748,8 +743,8 @@ void x11_start_xpra(int argc, char **argv) {
748 if (!arg_quiet) 743 if (!arg_quiet)
749 printf("Xpra server pid %d, xpra client pid %d, jail %d\n", server, client, jail); 744 printf("Xpra server pid %d, xpra client pid %d, jail %d\n", server, client, jail);
750 745
751 sleep(1); // let jail start 746 sleep(1); // adding a delay in order to let the server start
752 747
753 // wait for jail or server to end 748 // wait for jail or server to end
754 while (1) { 749 while (1) {
755 pid_t pid = wait(NULL); 750 pid_t pid = wait(NULL);
@@ -766,7 +761,7 @@ void x11_start_xpra(int argc, char **argv) {
766 dup2(fd_null,2); 761 dup2(fd_null,2);
767 } 762 }
768 // running without privileges - see drop_privs call above 763 // running without privileges - see drop_privs call above
769 assert(getenv("LD_PRELOAD") == NULL); 764 assert(getenv("LD_PRELOAD") == NULL);
770 execvp(stop_argv[0], stop_argv); 765 execvp(stop_argv[0], stop_argv);
771 perror("execvp"); 766 perror("execvp");
772 _exit(1); 767 _exit(1);
@@ -786,7 +781,7 @@ void x11_start_xpra(int argc, char **argv) {
786 else 781 else
787 printf("xpra server successfully stopped in %d secs\n", n); 782 printf("xpra server successfully stopped in %d secs\n", n);
788 } 783 }
789 784
790 // kill xpra server and xpra client 785 // kill xpra server and xpra client
791 kill(client, SIGTERM); 786 kill(client, SIGTERM);
792 kill(server, SIGTERM); 787 kill(server, SIGTERM);
@@ -802,6 +797,7 @@ void x11_start_xpra(int argc, char **argv) {
802 } 797 }
803} 798}
804 799
800
805void x11_start(int argc, char **argv) { 801void x11_start(int argc, char **argv) {
806 EUID_ASSERT(); 802 EUID_ASSERT();
807 803
@@ -826,7 +822,7 @@ void x11_start(int argc, char **argv) {
826#endif 822#endif
827 823
828// Porting notes: 824// Porting notes:
829// 825//
830// 1. merge #1100 from zackw: 826// 1. merge #1100 from zackw:
831// Attempting to run xauth -f directly on a file in /run/firejail/mnt/ directory fails on Debian 8 827// Attempting to run xauth -f directly on a file in /run/firejail/mnt/ directory fails on Debian 8
832// with this message: 828// with this message:
@@ -851,7 +847,7 @@ void x11_xorg(void) {
851 struct stat s; 847 struct stat s;
852 if (stat("/usr/bin/xauth", &s) == -1) { 848 if (stat("/usr/bin/xauth", &s) == -1) {
853 fprintf(stderr, "Error: xauth utility not found in PATH. Please install it:\n" 849 fprintf(stderr, "Error: xauth utility not found in PATH. Please install it:\n"
854 " Debian/Ubuntu/Mint: sudo apt-get install xauth\n"); 850 " Debian/Ubuntu/Mint: sudo apt-get install xauth\n");
855 exit(1); 851 exit(1);
856 } 852 }
857 if (s.st_uid != 0 && s.st_gid != 0) { 853 if (s.st_uid != 0 && s.st_gid != 0) {
@@ -893,8 +889,8 @@ void x11_xorg(void) {
893 __gcov_flush(); 889 __gcov_flush();
894#endif 890#endif
895 execlp("/usr/bin/xauth", "/usr/bin/xauth", "-v", "-f", tmpfname, 891 execlp("/usr/bin/xauth", "/usr/bin/xauth", "-v", "-f", tmpfname,
896 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL); 892 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL);
897 893
898 _exit(127); 894 _exit(127);
899 } 895 }
900 896
@@ -903,16 +899,19 @@ void x11_xorg(void) {
903 if (waitpid(child, &status, 0) != child) 899 if (waitpid(child, &status, 0) != child)
904 errExit("waitpid"); 900 errExit("waitpid");
905 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 901 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
906 /* success */ 902 /* success */
907 } else if (WIFEXITED(status)) { 903 }
904 else if (WIFEXITED(status)) {
908 fprintf(stderr, "Failed to create untrusted X cookie: xauth: exit %d\n", 905 fprintf(stderr, "Failed to create untrusted X cookie: xauth: exit %d\n",
909 WEXITSTATUS(status)); 906 WEXITSTATUS(status));
910 exit(1); 907 exit(1);
911 } else if (WIFSIGNALED(status)) { 908 }
909 else if (WIFSIGNALED(status)) {
912 fprintf(stderr, "Failed to create untrusted X cookie: xauth: %s\n", 910 fprintf(stderr, "Failed to create untrusted X cookie: xauth: %s\n",
913 strsignal(WTERMSIG(status))); 911 strsignal(WTERMSIG(status)));
914 exit(1); 912 exit(1);
915 } else { 913 }
914 else {
916 fprintf(stderr, "Failed to create untrusted X cookie: " 915 fprintf(stderr, "Failed to create untrusted X cookie: "
917 "xauth: un-decodable exit status %04x\n", status); 916 "xauth: un-decodable exit status %04x\n", status);
918 exit(1); 917 exit(1);
@@ -926,10 +925,11 @@ void x11_xorg(void) {
926 } 925 }
927 if (set_perms(tmpfname, getuid(), getgid(), 0600)) 926 if (set_perms(tmpfname, getuid(), getgid(), 0600))
928 errExit("set_perms"); 927 errExit("set_perms");
929 928
930 // move the temporary file in RUN_XAUTHORITY_SEC_FILE in order to have it deleted 929 // move the temporary file in RUN_XAUTHORITY_SEC_FILE in order to have it deleted
931 // automatically when the sandbox is closed (rename doesn't work) 930 // automatically when the sandbox is closed (rename doesn't work)
932 if (copy_file(tmpfname, RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600)) { // root needed 931 // root needed
932 if (copy_file(tmpfname, RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600)) {
933 fprintf(stderr, "Error: cannot create the new .Xauthority file\n"); 933 fprintf(stderr, "Error: cannot create the new .Xauthority file\n");
934 exit(1); 934 exit(1);
935 } 935 }
@@ -938,7 +938,6 @@ void x11_xorg(void) {
938 /* coverity[toctou] */ 938 /* coverity[toctou] */
939 unlink(tmpfname); 939 unlink(tmpfname);
940 umount("/tmp"); 940 umount("/tmp");
941
942 941
943 // Ensure there is already a file in the usual location, so that bind-mount below will work. 942 // Ensure there is already a file in the usual location, so that bind-mount below will work.
944 // todo: fix TOCTOU races, currently managed by imposing /usr/bin/xauth as executable 943 // todo: fix TOCTOU races, currently managed by imposing /usr/bin/xauth as executable
@@ -963,9 +962,10 @@ void x11_xorg(void) {
963 if (set_perms(dest, getuid(), getgid(), 0600)) 962 if (set_perms(dest, getuid(), getgid(), 0600))
964 errExit("set_perms"); 963 errExit("set_perms");
965 free(dest); 964 free(dest);
966#endif 965#endif
967} 966}
968 967
968
969void fs_x11(void) { 969void fs_x11(void) {
970#ifdef HAVE_X11 970#ifdef HAVE_X11
971 int display = x11_display(); 971 int display = x11_display();
@@ -984,61 +984,62 @@ void fs_x11(void) {
984 if (arg_debug || arg_debug_whitelists) 984 if (arg_debug || arg_debug_whitelists)
985 fprintf(stderr, "Masking all X11 sockets except %s\n", x11file); 985 fprintf(stderr, "Masking all X11 sockets except %s\n", x11file);
986 986
987 // Move the real /tmp/.X11-unix to a scratch location 987 // Move the real /tmp/.X11-unix to a scratch location
988 // so we can still access x11file after we mount a 988 // so we can still access x11file after we mount a
989 // tmpfs over /tmp/.X11-unix. 989 // tmpfs over /tmp/.X11-unix.
990 int rv = mkdir(RUN_WHITELIST_X11_DIR, 0700); 990 int rv = mkdir(RUN_WHITELIST_X11_DIR, 0700);
991 if (rv == -1) 991 if (rv == -1)
992 errExit("mkdir"); 992 errExit("mkdir");
993 if (set_perms(RUN_WHITELIST_X11_DIR, 0, 0, 0700)) 993 if (set_perms(RUN_WHITELIST_X11_DIR, 0, 0, 0700))
994 errExit("set_perms"); 994 errExit("set_perms");
995 995
996 if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, 0, MS_BIND|MS_REC, 0) < 0) 996 if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, 0, MS_BIND|MS_REC, 0) < 0)
997 errExit("mount bind"); 997 errExit("mount bind");
998 998
999 // This directory must be mode 1777, or Xlib will barf. 999 // This directory must be mode 1777, or Xlib will barf.
1000 if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs", 1000 if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs",
1001 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, 1001 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC,
1002 "mode=1777,uid=0,gid=0") < 0) 1002 "mode=1777,uid=0,gid=0") < 0)
1003 errExit("mounting tmpfs on /tmp/.X11-unix"); 1003 errExit("mounting tmpfs on /tmp/.X11-unix");
1004 fs_logger("tmpfs /tmp/.X11-unix"); 1004 fs_logger("tmpfs /tmp/.X11-unix");
1005 1005
1006 // create an empty file which will have the desired socket bind-mounted over it 1006 // create an empty file which will have the desired socket bind-mounted over it
1007 int fd = open(x11file, O_RDWR|O_CREAT|O_EXCL, x11stat.st_mode & ~S_IFMT); 1007 int fd = open(x11file, O_RDWR|O_CREAT|O_EXCL, x11stat.st_mode & ~S_IFMT);
1008 if (fd < 0) 1008 if (fd < 0)
1009 errExit(x11file); 1009 errExit(x11file);
1010 if (fchown(fd, x11stat.st_uid, x11stat.st_gid)) 1010 if (fchown(fd, x11stat.st_uid, x11stat.st_gid))
1011 errExit("fchown"); 1011 errExit("fchown");
1012 close(fd); 1012 close(fd);
1013 1013
1014 // do the mount 1014 // do the mount
1015 char *wx11file; 1015 char *wx11file;
1016 if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1) 1016 if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1)
1017 errExit("asprintf"); 1017 errExit("asprintf");
1018 if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0) 1018 if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0)
1019 errExit("mount bind"); 1019 errExit("mount bind");
1020 fs_logger2("whitelist", x11file); 1020 fs_logger2("whitelist", x11file);
1021 1021
1022 free(x11file); 1022 free(x11file);
1023 free(wx11file); 1023 free(wx11file);
1024 1024
1025 // block access to RUN_WHITELIST_X11_DIR 1025 // block access to RUN_WHITELIST_X11_DIR
1026 if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, 0, MS_BIND, 0) < 0) 1026 if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, 0, MS_BIND, 0) < 0)
1027 errExit("mount"); 1027 errExit("mount");
1028 fs_logger2("blacklist", RUN_WHITELIST_X11_DIR); 1028 fs_logger2("blacklist", RUN_WHITELIST_X11_DIR);
1029#endif 1029#endif
1030} 1030}
1031 1031
1032
1032void x11_block(void) { 1033void x11_block(void) {
1033#ifdef HAVE_X11 1034#ifdef HAVE_X11
1034 // check abstract socket presence and network namespace options 1035 // check abstract socket presence and network namespace options
1035 if ((!arg_nonetwork && !cfg.bridge0.configured && !cfg.interface0.configured) 1036 if ((!arg_nonetwork && !cfg.bridge0.configured && !cfg.interface0.configured)
1036 && x11_abstract_sockets_present()) { 1037 && x11_abstract_sockets_present()) {
1037 fprintf(stderr, "ERROR: --x11=none specified, but abstract X11 socket still accessible.\n" 1038 fprintf(stderr, "ERROR: --x11=none specified, but abstract X11 socket still accessible.\n"
1038 "Additional setup required. To block abstract X11 socket you can either:\n" 1039 "Additional setup required. To block abstract X11 socket you can either:\n"
1039 " * use network namespace in firejail (--net=none, --net=...)\n" 1040 " * use network namespace in firejail (--net=none, --net=...)\n"
1040 " * add \"-nolisten local\" to xserver options\n" 1041 " * add \"-nolisten local\" to xserver options\n"
1041 " (eg. to your display manager config, or /etc/X11/xinit/xserverrc)\n"); 1042 " (eg. to your display manager config, or /etc/X11/xinit/xserverrc)\n");
1042 exit(1); 1043 exit(1);
1043 } 1044 }
1044 1045
@@ -1063,4 +1064,3 @@ void x11_block(void) {
1063 env_store("XAUTHORITY", RMENV); 1064 env_store("XAUTHORITY", RMENV);
1064#endif 1065#endif
1065} 1066}
1066