aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README1
-rw-r--r--README.md37
-rw-r--r--RELNOTES1
-rw-r--r--src/firejail/caps.c6
-rw-r--r--src/firejail/usage.c1
-rw-r--r--src/firejail/x11.c522
-rw-r--r--src/man/firejail-profile.txt7
-rw-r--r--src/man/firejail.txt70
8 files changed, 376 insertions, 269 deletions
diff --git a/README b/README
index 71e329792..277d686ac 100644
--- a/README
+++ b/README
@@ -126,6 +126,7 @@ Zack Weinberg (https://github.com/zackw)
126 - rework fcopy, --follow-link support in fcopy 126 - rework fcopy, --follow-link support in fcopy
127 - follow link support in --private-bin 127 - follow link support in --private-bin
128 - wait_for_other function rewrite 128 - wait_for_other function rewrite
129 - xvfb X11 server support
129Austin S. Hemmelgarn (https://github.com/Ferroin) 130Austin S. Hemmelgarn (https://github.com/Ferroin)
130 - unbound profile update 131 - unbound profile update
131Igor Bukanov (https://github.com/ibukanov) 132Igor Bukanov (https://github.com/ibukanov)
diff --git a/README.md b/README.md
index 687877c73..20d624243 100644
--- a/README.md
+++ b/README.md
@@ -145,6 +145,43 @@ Added AppImage type 2 support, and support for passing command line arguments to
145 145
146 $ firejail --git-uninstall 146 $ firejail --git-uninstall
147 147
148 --x11=xvfb
149 Start Xvfb X11 server and attach the sandbox to this server.
150 Xvfb, short for X virtual framebuffer, performs all graphical
151 operations in memory without showing any screen output. Xvfb is
152 mainly used for remote access and software testing on headless
153 servers.
154
155 On Debian platforms Xvfb is installed with the command sudo apt-
156 get install xvfb. This feature is not available when running as
157 root.
158
159 Example: remote VNC access
160
161 On the server we start a sandbox using Xvfb and openbox window
162 manager. The default size of Xvfb screen is 800x600 - it can be
163 changed in /etc/firejail/firejail.config (xvfb-screen). Some
164 sort of networking (--net) is required in order to isolate the
165 abstract sockets used by other X servers.
166
167 $ firejail --net=none --x11=xvfb openbox
168
169 *** Attaching to Xvfb display 792 ***
170
171 Reading profile /etc/firejail/openbox.profile
172 Reading profile /etc/firejail/disable-common.inc
173 Reading profile /etc/firejail/disable-common.local
174 Parent pid 5400, child pid 5401
175
176 On the server we also start a VNC server and attach it to the
177 display handled by our Xvfb server (792).
178
179 $ x11vnc -display :792
180
181 On the client machine we start a VNC viewer and use it to con‐
182 nect to our server:
183
184 $ vncviewer
148 185
149````` 186`````
150## New Profiles 187## New Profiles
diff --git a/RELNOTES b/RELNOTES
index 54078875b..80bede268 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -31,6 +31,7 @@ firejail (0.9.45) baseline; urgency=low
31 * feature: added a number o Python scripts for handling sandboxes 31 * feature: added a number o Python scripts for handling sandboxes
32 * feature: allow local customization using .local files under /etc/firejail 32 * feature: allow local customization using .local files under /etc/firejail
33 * feature: follow-symlink-as-user runtime config option in /etc/firejail/firejail.config 33 * feature: follow-symlink-as-user runtime config option in /etc/firejail/firejail.config
34 * feature: xvfb X11 server support (--x11=xvfb)
34 * new profiles: xiphos, Tor Browser Bundle, display (imagemagik), Wire, 35 * new profiles: xiphos, Tor Browser Bundle, display (imagemagik), Wire,
35 * new profiles: mumble, zoom, Guayadeque, qemu, keypass2, xed, pluma, 36 * new profiles: mumble, zoom, Guayadeque, qemu, keypass2, xed, pluma,
36 * new profiles: Cryptocat, Bless, Gnome 2048, Gnome Calculator, 37 * new profiles: Cryptocat, Bless, Gnome 2048, Gnome Calculator,
diff --git a/src/firejail/caps.c b/src/firejail/caps.c
index 521187e3a..30693f7a0 100644
--- a/src/firejail/caps.c
+++ b/src/firejail/caps.c
@@ -183,8 +183,10 @@ static int caps_find_name(const char *name) {
183// return 1 if error, 0 if OK 183// return 1 if error, 0 if OK
184void caps_check_list(const char *clist, void (*callback)(int)) { 184void caps_check_list(const char *clist, void (*callback)(int)) {
185 // don't allow empty lists 185 // don't allow empty lists
186 if (clist == NULL || *clist == '\0') 186 if (clist == NULL || *clist == '\0') {
187 goto errexit; 187 fprintf(stderr, "Error: empty capabilities list\n");
188 exit(1);
189 }
188 190
189 // work on a copy of the string 191 // work on a copy of the string
190 char *str = strdup(clist); 192 char *str = strdup(clist);
diff --git a/src/firejail/usage.c b/src/firejail/usage.c
index ae3993aec..4d2054a72 100644
--- a/src/firejail/usage.c
+++ b/src/firejail/usage.c
@@ -205,6 +205,7 @@ void usage(void) {
205 printf(" --x11=xephyr - enable Xephyr X11 server. The window size is 800x600.\n"); 205 printf(" --x11=xephyr - enable Xephyr X11 server. The window size is 800x600.\n");
206 printf(" --x11=xorg - enable X11 security extension.\n"); 206 printf(" --x11=xorg - enable X11 security extension.\n");
207 printf(" --x11=xpra - enable Xpra X11 server.\n"); 207 printf(" --x11=xpra - enable Xpra X11 server.\n");
208 printf(" --x11=xvfb - enable Xvfb X11 server.\n");
208 printf(" --zsh - use /usr/bin/zsh as default shell.\n"); 209 printf(" --zsh - use /usr/bin/zsh as default shell.\n");
209 printf("\n"); 210 printf("\n");
210 printf("Examples:\n"); 211 printf("Examples:\n");
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
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt
index aa1aec567..d60d48072 100644
--- a/src/man/firejail-profile.txt
+++ b/src/man/firejail-profile.txt
@@ -310,13 +310,16 @@ Remove DISPLAY and XAUTHORITY environment variables.
310Stop with error message if X11 abstract socket will be accessible in jail. 310Stop with error message if X11 abstract socket will be accessible in jail.
311.TP 311.TP
312\fBx11 xephyr 312\fBx11 xephyr
313Enable X11 sandboxing with xephyr. 313Enable X11 sandboxing with Xephyr server.
314.TP 314.TP
315\fBx11 xorg 315\fBx11 xorg
316Enable X11 sandboxing with X11 security extension. 316Enable X11 sandboxing with X11 security extension.
317.TP 317.TP
318\fBx11 xpra 318\fBx11 xpra
319Enable X11 sandboxing with xpra. 319Enable X11 sandboxing with Xpra server.
320.TP
321\fBx11 xvfb
322Enable X11 sandboxing with Xvfb server.
320 323
321.SH Resource limits, CPU affinity, Control Groups 324.SH Resource limits, CPU affinity, Control Groups
322These profile entries define the limits on system resources (rlimits) for the processes inside the sandbox. 325These profile entries define the limits on system resources (rlimits) for the processes inside the sandbox.
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index f978661dc..2b6069a7a 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -1772,17 +1772,17 @@ $ sudo firejail --writable-var-log
1772 1772
1773.TP 1773.TP
1774\fB\-\-x11 1774\fB\-\-x11
1775Sandbox the application using Xpra, Xephyr or Xorg security extension. 1775Sandbox the application using Xpra, Xephyr, Xvfb or Xorg security extension.
1776The sandbox will prevents screenshot and keylogger applications started inside the sandbox from accessing 1776The sandbox will prevents screenshot and keylogger applications started inside the sandbox from accessing
1777clients running outside the sandbox. 1777clients running outside the sandbox.
1778Firejail will try first Xpra, and if Xpra is not installed on the system, it will try to find Xephyr. 1778Firejail will try first Xpra, and if Xpra is not installed on the system, it will try to find Xephyr.
1779If all fails, Firejail will not attempt to use X11 security extension. 1779If all fails, Firejail will not attempt to use Xvfb or X11 security extension.
1780.br 1780.br
1781 1781
1782.br 1782.br
1783Xpra and Xephyr modes require a network namespace to be instantiated in order to disable 1783Xpra, Xephyr and Xvfb modes require a network namespace to be instantiated in order to disable
1784X11 abstract Unix socket. If this is not possible, the user can disable the abstract socket 1784X11 abstract Unix socket. If this is not possible, the user can disable the abstract socket
1785by adding "-nolisten local" on Xorg command line. 1785by adding "-nolisten local" on Xorg command line at system level.
1786.br 1786.br
1787 1787
1788.br 1788.br
@@ -1859,6 +1859,68 @@ Example:
1859.br 1859.br
1860$ firejail \-\-x11=xpra --net=eth0 firefox 1860$ firejail \-\-x11=xpra --net=eth0 firefox
1861 1861
1862
1863.TP
1864\fB\-\-x11=xvfb
1865Start Xvfb X11 server and attach the sandbox to this server.
1866Xvfb, short for X virtual framebuffer, performs all graphical operations in memory
1867without showing any screen output. Xvfb is mainly used for remote access and software
1868testing on headless servers.
1869.br
1870
1871.br
1872On Debian platforms Xvfb is installed with the command \fBsudo apt-get install xvfb\fR.
1873This feature is not available when running as root.
1874.br
1875
1876.br
1877Example: remote VNC access
1878.br
1879
1880.br
1881On the server we start a sandbox using Xvfb and openbox
1882window manager. The default size of Xvfb screen is 800x600 - it can be changed
1883in /etc/firejail/firejail.config (xvfb-screen). Some sort of networking (--net) is required
1884in order to isolate the abstract sockets used by other X servers.
1885.br
1886
1887.br
1888$ firejail --net=none --x11=xvfb openbox
1889.br
1890
1891.br
1892*** Attaching to Xvfb display 792 ***
1893.br
1894
1895.br
1896Reading profile /etc/firejail/openbox.profile
1897.br
1898Reading profile /etc/firejail/disable-common.inc
1899.br
1900Reading profile /etc/firejail/disable-common.local
1901.br
1902Parent pid 5400, child pid 5401
1903.br
1904
1905.br
1906On the server we also start a VNC server and attach it to the display handled by our
1907Xvfb server (792).
1908.br
1909
1910.br
1911$ x11vnc -display :792
1912.br
1913
1914.br
1915On the client machine we start a VNC viewer and use it to connect to our server:
1916.br
1917
1918.br
1919$ vncviewer
1920.br
1921
1922
1923
1862.TP 1924.TP
1863\fB\-\-zsh 1925\fB\-\-zsh
1864Use /usr/bin/zsh as default user shell. 1926Use /usr/bin/zsh as default user shell.