aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/sandbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/firejail/sandbox.c')
-rw-r--r--src/firejail/sandbox.c490
1 files changed, 490 insertions, 0 deletions
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
new file mode 100644
index 000000000..3f5fb51fa
--- /dev/null
+++ b/src/firejail/sandbox.c
@@ -0,0 +1,490 @@
1/*
2 * Copyright (C) 2014, 2015 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20
21#include "firejail.h"
22#include <sys/mount.h>
23#include <sys/wait.h>
24#include <sys/stat.h>
25#include <sys/prctl.h>
26#include <sys/time.h>
27#include <sys/resource.h>
28
29#include <sched.h>
30#ifndef CLONE_NEWUSER
31#define CLONE_NEWUSER 0x10000000
32#endif
33
34static void set_caps(void) {
35 if (arg_caps_drop_all)
36 caps_drop_all();
37 else if (arg_caps_drop)
38 caps_drop_list(arg_caps_list);
39 else if (arg_caps_keep)
40 caps_keep_list(arg_caps_list);
41 else if (arg_caps_default_filter)
42 caps_default_filter();
43}
44
45void save_nogroups(void) {
46 if (arg_nogroups == 0)
47 return;
48
49 char *fname;
50 if (asprintf(&fname, "%s/groups", MNT_DIR) == -1)
51 errExit("asprintf");
52 FILE *fp = fopen(fname, "w");
53 if (fp) {
54 fprintf(fp, "\n");
55 fclose(fp);
56 if (chown(fname, 0, 0) < 0)
57 errExit("chown");
58 }
59 else {
60 fprintf(stderr, "Error: cannot save nogroups state\n");
61 free(fname);
62 exit(1);
63 }
64
65 free(fname);
66}
67
68static void sandbox_if_up(Bridge *br) {
69 assert(br);
70 if (!br->configured)
71 return;
72
73 char *dev = br->devsandbox;
74 net_if_up(dev);
75
76 if (br->arg_ip_none == 1); // do nothing
77 else if (br->arg_ip_none == 0 && br->macvlan == 0) {
78 if (br->ipsandbox == br->ip) {
79 fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev);
80 exit(1);
81 }
82
83 // just assign the address
84 assert(br->ipsandbox);
85 if (arg_debug)
86 printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev);
87 net_if_ip(dev, br->ipsandbox, br->mask);
88 net_if_up(dev);
89 }
90 else if (br->arg_ip_none == 0 && br->macvlan == 1) {
91 // reassign the macvlan address
92 if (br->ipsandbox == 0)
93 // ip address assigned by arp-scan for a macvlan device
94 br->ipsandbox = arp_assign(dev, br); //br->ip, br->mask);
95 else {
96 if (br->ipsandbox == br->ip) {
97 fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev);
98 exit(1);
99 }
100
101 uint32_t rv = arp_check(dev, br->ipsandbox, br->ip);
102 if (rv) {
103 fprintf(stderr, "Error: the address %d.%d.%d.%d is already in use.\n", PRINT_IP(br->ipsandbox));
104 exit(1);
105 }
106 }
107
108 if (arg_debug)
109 printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev);
110 net_if_ip(dev, br->ipsandbox, br->mask);
111 net_if_up(dev);
112 }
113}
114
115static void chk_chroot(void) {
116 // if we are starting firejail inside some other container technology, we don't care about this
117 char *mycont = getenv("container");
118 if (mycont)
119 return;
120
121 // check if this is a regular chroot
122 struct stat s;
123 if (stat("/", &s) == 0) {
124 if (s.st_ino != 2)
125 return;
126 }
127
128 fprintf(stderr, "Error: cannot mount filesystem as slave\n");
129 exit(1);
130}
131
132int sandbox(void* sandbox_arg) {
133 pid_t child_pid = getpid();
134 if (arg_debug)
135 printf("Initializing child process\n");
136
137 // close each end of the unused pipes
138 close(parent_to_child_fds[1]);
139 close(child_to_parent_fds[0]);
140
141 // wait for parent to do base setup
142 wait_for_other(parent_to_child_fds[0]);
143
144 if (arg_debug && child_pid == 1)
145 printf("PID namespace installed\n");
146
147 //****************************
148 // set hostname
149 //****************************
150 if (cfg.hostname) {
151 if (sethostname(cfg.hostname, strlen(cfg.hostname)) < 0)
152 errExit("sethostname");
153 }
154
155 //****************************
156 // mount namespace
157 //****************************
158 // mount events are not forwarded between the host the sandbox
159 if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) {
160 chk_chroot();
161 }
162
163 //****************************
164 // netfilter
165 //****************************
166 if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter
167 netfilter(arg_netfilter_file);
168 }
169
170 //****************************
171 // trace pre-install
172 //****************************
173 if (arg_trace)
174 fs_trace_preload();
175
176 //****************************
177 // configure filesystem
178 //****************************
179#ifdef HAVE_CHROOT
180 if (cfg.chrootdir) {
181 fs_chroot(cfg.chrootdir);
182 // force caps and seccomp if not started as root
183 if (getuid() != 0) {
184 // force default seccomp inside the chroot, no keep or drop list
185 // the list build on top of the default drop list is kept intact
186 arg_seccomp = 1;
187 if (arg_seccomp_list_drop) {
188 free(arg_seccomp_list_drop);
189 arg_seccomp_list_drop = NULL;
190 }
191 if (arg_seccomp_list_keep) {
192 free(arg_seccomp_list_keep);
193 arg_seccomp_list_keep = NULL;
194 }
195
196 // disable all capabilities
197 if (arg_caps_default_filter || arg_caps_list)
198 fprintf(stderr, "Warning: all capabilities disabled for a regular user during chroot\n");
199 arg_caps_drop_all = 1;
200
201 // drop all supplementary groups; /etc/group file inside chroot
202 // is controlled by a regular usr
203 arg_nogroups = 1;
204 printf("Dropping all Linux capabilities and enforcing default seccomp filter\n");
205 }
206
207 //****************************
208 // trace pre-install, this time inside chroot
209 //****************************
210 if (arg_trace)
211 fs_trace_preload();
212 }
213 else
214#endif
215 if (arg_overlay)
216 fs_overlayfs();
217 else
218 fs_basic_fs();
219
220
221 //****************************
222 // set hostname in /etc/hostname
223 //****************************
224 if (cfg.hostname) {
225 fs_hostname(cfg.hostname);
226 }
227
228 //****************************
229 // apply the profile file
230 //****************************
231 if (cfg.profile)
232 fs_blacklist(cfg.homedir);
233
234 //****************************
235 // private mode
236 //****************************
237 if (arg_private) {
238 if (cfg.home_private) // --private=
239 fs_private_homedir();
240 else if (cfg.home_private_keep) // --private.keep=
241 fs_private_home_list();
242 else // --private
243 fs_private();
244 }
245
246 if (arg_private_dev)
247 fs_private_dev();
248
249 //****************************
250 // install trace
251 //****************************
252 if (arg_trace)
253 fs_trace();
254
255 //****************************
256 // update /proc, /dev, /boot directorymy
257 //****************************
258 fs_proc_sys_dev_boot();
259
260 //****************************
261 // networking
262 //****************************
263 if (arg_nonetwork) {
264 net_if_up("lo");
265 }
266 else if (any_bridge_configured()) {
267 // configure lo and eth0...eth3
268 net_if_up("lo");
269
270 if (mac_not_zero(cfg.bridge0.macsandbox))
271 net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox);
272 sandbox_if_up(&cfg.bridge0);
273
274 if (mac_not_zero(cfg.bridge1.macsandbox))
275 net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox);
276 sandbox_if_up(&cfg.bridge1);
277
278 if (mac_not_zero(cfg.bridge2.macsandbox))
279 net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox);
280 sandbox_if_up(&cfg.bridge2);
281
282 if (mac_not_zero(cfg.bridge3.macsandbox))
283 net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox);
284 sandbox_if_up(&cfg.bridge3);
285
286 // add a default route
287 if (cfg.defaultgw) {
288 // set the default route
289 if (net_add_route(0, 0, cfg.defaultgw))
290 fprintf(stderr, "Warning: cannot configure default route\n");
291 }
292
293 if (arg_debug)
294 printf("Network namespace enabled\n");
295 }
296
297 // if any dns server is configured, it is time to set it now
298 fs_resolvconf();
299
300 // print network configuration
301 if (any_bridge_configured() || cfg.defaultgw || cfg.dns1) {
302 printf("\n");
303 if (any_bridge_configured())
304 net_ifprint();
305 if (cfg.defaultgw != 0)
306 printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw));
307 if (cfg.dns1 != 0)
308 printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1));
309 if (cfg.dns2 != 0)
310 printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2));
311 if (cfg.dns3 != 0)
312 printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3));
313 printf("\n");
314 }
315
316
317
318 //****************************
319 // start executable
320 //****************************
321 prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died
322 int cwd = 0;
323 if (cfg.cwd) {
324 if (chdir(cfg.cwd) == 0)
325 cwd = 1;
326 }
327
328 if (!cwd) {
329 if (chdir("/") < 0)
330 errExit("chdir");
331 if (cfg.homedir) {
332 struct stat s;
333 if (stat(cfg.homedir, &s) == 0) {
334 /* coverity[toctou] */
335 if (chdir(cfg.homedir) < 0)
336 errExit("chdir");
337 }
338 }
339 }
340
341 // set environment
342 // fix qt 4.8
343 if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0)
344 errExit("setenv");
345 if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc,
346 errExit("setenv");
347 if (arg_zsh && setenv("SHELL", "/usr/bin/zsh", 1) < 0)
348 errExit("setenv");
349 if (arg_csh && setenv("SHELL", "/bin/csh", 1) < 0)
350 errExit("setenv");
351 if (cfg.shell && setenv("SHELL", cfg.shell, 1) < 0)
352 errExit("setenv");
353 // set prompt color to green
354 //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] '
355 if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0)
356 errExit("setenv");
357
358
359 // set capabilities
360 if (!arg_noroot)
361 set_caps();
362
363 // set rlimits
364 set_rlimits();
365
366 // set seccomp
367#ifdef HAVE_SECCOMP
368 // if a keep list is available, disregard the drop list
369 if (arg_seccomp == 1) {
370 if (arg_seccomp_list_keep)
371 seccomp_filter_keep(); // this will also save the fmyilter to MNT_DIR/seccomp file
372 else
373 seccomp_filter_drop(); // this will also save the filter to MNT_DIR/seccomp file
374 }
375#endif
376
377 // set cpu affinity
378 if (cfg.cpus) {
379 save_cpu(); // save cpu affinity mask to MNT_DIR/cpu file
380 set_cpu_affinity();
381 }
382
383 // save cgroup in MNT_DIR/cgroup file
384 if (cfg.cgroup)
385 save_cgroup();
386
387 //****************************************
388 // drop privileges or create a new user namespace
389 //****************************************
390 save_nogroups();
391 if (arg_noroot) {
392 int rv = unshare(CLONE_NEWUSER);
393 if (rv == -1) {
394 fprintf(stderr, "Warning: cannot mount a new user namespace\n");
395 perror("unshare");
396 drop_privs(arg_nogroups);
397 }
398 }
399 else
400 drop_privs(arg_nogroups);
401
402 // notify parent that new user namespace has been created so a proper
403 // UID/GID map can be setup
404 notify_other(child_to_parent_fds[1]);
405 close(child_to_parent_fds[1]);
406
407 // wait for parent to finish setting up a proper UID/GID map
408 wait_for_other(parent_to_child_fds[0]);
409 close(parent_to_child_fds[0]);
410
411 // somehow, the new user namespace resets capabilities;
412 // we need to do them again
413 if (arg_noroot) {
414 set_caps();
415 if (arg_debug)
416 printf("User namespace (noroot) installed\n");
417 }
418
419
420 //****************************************
421 // start the program without using a shell
422 //****************************************
423 if (arg_shell_none) {
424 if (arg_debug) {
425 int i;
426 for (i = cfg.original_program_index; i < cfg.original_argc; i++) {
427 if (cfg.original_argv[i] == NULL)
428 break;
429 printf("execvp argument %d: %s\n", i - cfg.original_program_index, cfg.original_argv[i]);
430 }
431 }
432
433 if (!arg_command)
434 printf("Child process initialized\n");
435 execvp(cfg.original_argv[cfg.original_program_index], &cfg.original_argv[cfg.original_program_index + 1]);
436 }
437 //****************************************
438 // start the program using a shell
439 //****************************************
440 else {
441 // choose the shell requested by the user, or use bash as default
442 char *sh;
443 if (cfg.shell)
444 sh = cfg.shell;
445 else if (arg_zsh)
446 sh = "/usr/bin/zsh";
447 else if (arg_csh)
448 sh = "/bin/csh";
449 else
450 sh = "/bin/bash";
451
452 char *arg[5];
453 int index = 0;
454 arg[index++] = sh;
455 arg[index++] = "-c";
456 assert(cfg.command_line);
457 if (arg_debug)
458 printf("Starting %s\n", cfg.command_line);
459 if (arg_doubledash)
460 arg[index++] = "--";
461 arg[index++] = cfg.command_line;
462 arg[index] = NULL;
463 assert(index < 5);
464
465 if (arg_debug) {
466 char *msg;
467 if (asprintf(&msg, "sandbox %d, execvp into %s", sandbox_pid, cfg.command_line) == -1)
468 errExit("asprintf");
469 logmsg(msg);
470 free(msg);
471 }
472
473 if (arg_debug) {
474 int i;
475 for (i = 0; i < 5; i++) {
476 if (arg[i] == NULL)
477 break;
478 printf("execvp argument %d: %s\n", i, arg[i]);
479 }
480 }
481
482 if (!arg_command)
483 printf("Child process initialized\n");
484 execvp(sh, arg);
485 }
486
487
488 perror("execvp");
489 return 0;
490}