diff options
author | netblue30 <netblue30@yahoo.com> | 2017-03-03 10:46:05 -0500 |
---|---|---|
committer | netblue30 <netblue30@yahoo.com> | 2017-03-03 10:46:05 -0500 |
commit | dd5881a2860ddb9abb1e60b7de550f4cdac60524 (patch) | |
tree | be54f040ed6dc04628a21323a6d31856ffc9303c | |
parent | merge #1100 from zackw: xvfb support (diff) | |
download | firejail-dd5881a2860ddb9abb1e60b7de550f4cdac60524.tar.gz firejail-dd5881a2860ddb9abb1e60b7de550f4cdac60524.tar.zst firejail-dd5881a2860ddb9abb1e60b7de550f4cdac60524.zip |
xvfb X11 server support (--x11=xvfb)
-rw-r--r-- | README | 1 | ||||
-rw-r--r-- | README.md | 37 | ||||
-rw-r--r-- | RELNOTES | 1 | ||||
-rw-r--r-- | src/firejail/caps.c | 6 | ||||
-rw-r--r-- | src/firejail/usage.c | 1 | ||||
-rw-r--r-- | src/firejail/x11.c | 522 | ||||
-rw-r--r-- | src/man/firejail-profile.txt | 7 | ||||
-rw-r--r-- | src/man/firejail.txt | 70 |
8 files changed, 376 insertions, 269 deletions
@@ -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 | ||
129 | Austin S. Hemmelgarn (https://github.com/Ferroin) | 130 | Austin S. Hemmelgarn (https://github.com/Ferroin) |
130 | - unbound profile update | 131 | - unbound profile update |
131 | Igor Bukanov (https://github.com/ibukanov) | 132 | Igor Bukanov (https://github.com/ibukanov) |
@@ -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 |
@@ -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 |
184 | void caps_check_list(const char *clist, void (*callback)(int)) { | 184 | void 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. |
37 | int x11_display(void) { | 37 | int 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 |
77 | static int x11_abstract_sockets_present(void) { | 78 | static 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. |
118 | static int random_display_number(void) { | 120 | static 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 | |||
204 | void x11_start_xvfb(int argc, char **argv) { | 202 | void 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 |
405 | void x11_start_xephyr(int argc, char **argv) { | 401 | void 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 | |||
605 | void x11_start_xpra(int argc, char **argv) { | 604 | void 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 | |||
805 | void x11_start(int argc, char **argv) { | 801 | void 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 | |||
969 | void fs_x11(void) { | 969 | void 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 | |||
1032 | void x11_block(void) { | 1033 | void 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. | |||
310 | Stop with error message if X11 abstract socket will be accessible in jail. | 310 | Stop with error message if X11 abstract socket will be accessible in jail. |
311 | .TP | 311 | .TP |
312 | \fBx11 xephyr | 312 | \fBx11 xephyr |
313 | Enable X11 sandboxing with xephyr. | 313 | Enable X11 sandboxing with Xephyr server. |
314 | .TP | 314 | .TP |
315 | \fBx11 xorg | 315 | \fBx11 xorg |
316 | Enable X11 sandboxing with X11 security extension. | 316 | Enable X11 sandboxing with X11 security extension. |
317 | .TP | 317 | .TP |
318 | \fBx11 xpra | 318 | \fBx11 xpra |
319 | Enable X11 sandboxing with xpra. | 319 | Enable X11 sandboxing with Xpra server. |
320 | .TP | ||
321 | \fBx11 xvfb | ||
322 | Enable X11 sandboxing with Xvfb server. | ||
320 | 323 | ||
321 | .SH Resource limits, CPU affinity, Control Groups | 324 | .SH Resource limits, CPU affinity, Control Groups |
322 | These profile entries define the limits on system resources (rlimits) for the processes inside the sandbox. | 325 | These 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 |
1775 | Sandbox the application using Xpra, Xephyr or Xorg security extension. | 1775 | Sandbox the application using Xpra, Xephyr, Xvfb or Xorg security extension. |
1776 | The sandbox will prevents screenshot and keylogger applications started inside the sandbox from accessing | 1776 | The sandbox will prevents screenshot and keylogger applications started inside the sandbox from accessing |
1777 | clients running outside the sandbox. | 1777 | clients running outside the sandbox. |
1778 | Firejail will try first Xpra, and if Xpra is not installed on the system, it will try to find Xephyr. | 1778 | Firejail will try first Xpra, and if Xpra is not installed on the system, it will try to find Xephyr. |
1779 | If all fails, Firejail will not attempt to use X11 security extension. | 1779 | If all fails, Firejail will not attempt to use Xvfb or X11 security extension. |
1780 | .br | 1780 | .br |
1781 | 1781 | ||
1782 | .br | 1782 | .br |
1783 | Xpra and Xephyr modes require a network namespace to be instantiated in order to disable | 1783 | Xpra, Xephyr and Xvfb modes require a network namespace to be instantiated in order to disable |
1784 | X11 abstract Unix socket. If this is not possible, the user can disable the abstract socket | 1784 | X11 abstract Unix socket. If this is not possible, the user can disable the abstract socket |
1785 | by adding "-nolisten local" on Xorg command line. | 1785 | by 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 | ||
1865 | Start Xvfb X11 server and attach the sandbox to this server. | ||
1866 | Xvfb, short for X virtual framebuffer, performs all graphical operations in memory | ||
1867 | without showing any screen output. Xvfb is mainly used for remote access and software | ||
1868 | testing on headless servers. | ||
1869 | .br | ||
1870 | |||
1871 | .br | ||
1872 | On Debian platforms Xvfb is installed with the command \fBsudo apt-get install xvfb\fR. | ||
1873 | This feature is not available when running as root. | ||
1874 | .br | ||
1875 | |||
1876 | .br | ||
1877 | Example: remote VNC access | ||
1878 | .br | ||
1879 | |||
1880 | .br | ||
1881 | On the server we start a sandbox using Xvfb and openbox | ||
1882 | window manager. The default size of Xvfb screen is 800x600 - it can be changed | ||
1883 | in /etc/firejail/firejail.config (xvfb-screen). Some sort of networking (--net) is required | ||
1884 | in 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 | ||
1896 | Reading profile /etc/firejail/openbox.profile | ||
1897 | .br | ||
1898 | Reading profile /etc/firejail/disable-common.inc | ||
1899 | .br | ||
1900 | Reading profile /etc/firejail/disable-common.local | ||
1901 | .br | ||
1902 | Parent pid 5400, child pid 5401 | ||
1903 | .br | ||
1904 | |||
1905 | .br | ||
1906 | On the server we also start a VNC server and attach it to the display handled by our | ||
1907 | Xvfb server (792). | ||
1908 | .br | ||
1909 | |||
1910 | .br | ||
1911 | $ x11vnc -display :792 | ||
1912 | .br | ||
1913 | |||
1914 | .br | ||
1915 | On 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 |
1864 | Use /usr/bin/zsh as default user shell. | 1926 | Use /usr/bin/zsh as default user shell. |