aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kris7topher@gmail.com>2019-12-30 00:04:04 +0100
committerLibravatar Kristóf Marussy <kris7topher@gmail.com>2019-12-30 16:56:47 +0100
commitce3c1988578f6b18488a91132d355cf13a37e522 (patch)
tree3898a7fe71c84eb2ef1374b67404d760c637d5de /src
parentAdd capability filter for network services, additive filter (diff)
downloadfirejail-ce3c1988578f6b18488a91132d355cf13a37e522.tar.gz
firejail-ce3c1988578f6b18488a91132d355cf13a37e522.tar.zst
firejail-ce3c1988578f6b18488a91132d355cf13a37e522.zip
Run dhclient inside the sandbox
* In order to ensure that network interfaces are already configured when the sandboxed launches, we run dhclient in forking mode (no -d switch), which makes the dhclient command exit when it successfully acquired a lease. The dhclient daemon process keeps running in the background. * We read the pid file for dhclient to find out the pid of the daemon process. Because dhclient only writes the pid file in the child process potentially after the forking parent process exits, there is some handling for possible race conditions. * All lease files and pid files are under /run/firejail/dhclient/ * The v4 and v6 dhclient has a separate lease as recommended. * The v4 client is set to generate a DUID, which is also used by the v6 client so that the server can associate the two leases if needed. * /etc/resolv.conf is created in the sandbox just like with the --dns option, by mirroring /etc. When DHCP is used, /etc/resolv.conf is normally empty so that dhclient can overwrite it the nameservers from the DHCP server. Current limitations: * The dhclient processes in the background are not terminated properly (by SIGTERM or dhclient -x), nor is the DHCP lease released (by dclient -r). The reason for this is that firejail drops all capabilities and privileges before the application in the sandbox is launched, which makes it impossible to launch dhclient to release the lease or kill the dhclient processes still running with the effective user id of root. Instead the dhclient daemons die with the sandbox. According to the dhclient man page, releasing the lease is not required by the DHCP specification, so this is not a problem, however some ISPs may require releasing leases. A possible workaround would be to fork another process upon sandbox initialization that invokes dhclient -r when the sandbox is ready to exit. This would require communication with the main firejail process through a pipe, while keeping and required privileges. As this would add some complexity but the benefits have limited applicability (compatibility with esoteric DHCP server configurations), I chose not to implement this. * When only an IPv6 address is requested, the interface may possible not have a link-local address when we run dhclient. This causes dhclient -6 fail, since DHCPv6 uses link-local addressing instead of layer 2 addressing, see e.g., https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=783387 In a future commit, waiting for a link-local address will be added.
Diffstat (limited to 'src')
-rw-r--r--src/firejail/dhcp.c142
-rw-r--r--src/firejail/firejail.h6
-rw-r--r--src/firejail/sandbox.c7
-rw-r--r--src/include/rundefs.h5
4 files changed, 160 insertions, 0 deletions
diff --git a/src/firejail/dhcp.c b/src/firejail/dhcp.c
new file mode 100644
index 000000000..c9bbb4d8f
--- /dev/null
+++ b/src/firejail/dhcp.c
@@ -0,0 +1,142 @@
1/*
2 * Copyright (C) 2014-2019 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#include "firejail.h"
21#include <sys/types.h>
22#include <sys/wait.h>
23#include <errno.h>
24#include <stddef.h>
25#include <stdio.h>
26#include <string.h>
27
28pid_t dhclient4_pid = 0;
29pid_t dhclient6_pid = 0;
30
31typedef struct {
32 char *version_arg;
33 char *pid_file;
34 char *leases_file;
35 uint8_t generate_duid;
36 char *duid_leases_file;
37 pid_t *pid;
38 ptrdiff_t arg_offset;
39} Dhclient;
40
41static const Dhclient dhclient4 = { .version_arg = "-4",
42 .pid_file = RUN_DHCLIENT_4_PID_FILE,
43 .leases_file = RUN_DHCLIENT_4_LEASES_FILE,
44 .generate_duid = 1,
45 .pid = &dhclient4_pid,
46 .arg_offset = offsetof(Bridge, arg_ip_dhcp)
47};
48
49static const Dhclient dhclient6 = { .version_arg = "-6",
50 .pid_file = RUN_DHCLIENT_6_PID_FILE,
51 .leases_file = RUN_DHCLIENT_6_LEASES_FILE,
52 .duid_leases_file = RUN_DHCLIENT_4_LEASES_FILE,
53 .pid = &dhclient6_pid,
54 .arg_offset = offsetof(Bridge, arg_ip6_dhcp)
55};
56
57static void dhcp_run_dhclient(const Dhclient *client) {
58 char *argv[256] = { "dhclient",
59 client->version_arg,
60 "-pf", client->pid_file,
61 "-lf", client->leases_file,
62 };
63 int i = 6;
64 if (client->generate_duid)
65 argv[i++] = "-i";
66 if (client->duid_leases_file) {
67 argv[i++] = "-df";
68 argv[i++] = client->duid_leases_file;
69 }
70 if (arg_debug)
71 argv[i++] = "-v";
72 if (*(uint8_t *) ((char *) &cfg.bridge0 + client->arg_offset))
73 argv[i++] = cfg.bridge0.devsandbox;
74 if (*(uint8_t *) ((char *) &cfg.bridge1 + client->arg_offset))
75 argv[i++] = cfg.bridge1.devsandbox;
76 if (*(uint8_t *) ((char *) &cfg.bridge2 + client->arg_offset))
77 argv[i++] = cfg.bridge2.devsandbox;
78 if (*(uint8_t *) ((char *) &cfg.bridge3 + client->arg_offset))
79 argv[i++] = cfg.bridge3.devsandbox;
80
81 sbox_run_v(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_CAPS_NET_SERVICE | SBOX_SECCOMP, argv);
82}
83
84static pid_t dhcp_read_pidfile(const Dhclient *client) {
85 // We have to run dhclient as a forking daemon (not pass the -d option),
86 // because we want to be notified of a successful DHCP lease by the parent process exit.
87 // However, try to be extra paranoid with race conditions,
88 // because dhclient only writes the daemon pid into the pidfile
89 // after its parent process has exited.
90 int tries = 0;
91 pid_t found = 0;
92 while (found == 0 && tries < 10) {
93 if (tries >= 1)
94 usleep(100000);
95 FILE *pidfile = fopen(client->pid_file, "r");
96 if (pidfile) {
97 long pid;
98 if (fscanf(pidfile, "%ld", &pid) == 1) {
99 char *pidname = pid_proc_comm((pid_t) pid);
100 if (pidname && strcmp(pidname, "dhclient") == 0)
101 found = (pid_t) pid;
102 }
103 fclose(pidfile);
104 }
105 ++tries;
106 }
107 if (found == 0) {
108 fprintf(stderr, "Error: Cannot get dhclient %s PID from %s\n",
109 client->version_arg, client->pid_file);
110 exit(1);
111 }
112 return found;
113}
114
115static void dhcp_start_dhclient(const Dhclient *client) {
116 dhcp_run_dhclient(client);
117 *(client->pid) = dhcp_read_pidfile(client);
118}
119
120void dhcp_start(void) {
121 if (!any_dhcp())
122 return;
123
124 EUID_ROOT();
125 if (mkdir(RUN_DHCLIENT_DIR, 0700))
126 errExit("mkdir");
127
128 if (any_ip_dhcp()) {
129 dhcp_start_dhclient(&dhclient4);
130 if (arg_debug)
131 printf("Running dhclient -4 in the background as pid %ld\n", (long) dhclient4_pid);
132 }
133 if (any_ip6_dhcp()) {
134 dhcp_start_dhclient(&dhclient6);
135 if (arg_debug)
136 printf("Running dhclient -6 in the background as pid %ld\n", (long) dhclient6_pid);
137 if (dhclient4_pid == dhclient6_pid) {
138 fprintf(stderr, "Error: dhclient -4 and -6 have the same PID: %ld\n", (long) dhclient4_pid);
139 exit(1);
140 }
141 }
142}
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 0311968c3..4beae587e 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -812,6 +812,7 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc,
812#define SBOX_ALLOW_STDIN (1 << 5) // don't close stdin 812#define SBOX_ALLOW_STDIN (1 << 5) // don't close stdin
813#define SBOX_STDIN_FROM_FILE (1 << 6) // open file and redirect it to stdin 813#define SBOX_STDIN_FROM_FILE (1 << 6) // open file and redirect it to stdin
814#define SBOX_CAPS_HIDEPID (1 << 7) // hidepid caps filter for running firemon 814#define SBOX_CAPS_HIDEPID (1 << 7) // hidepid caps filter for running firemon
815#define SBOX_CAPS_NET_SERVICE (1 << 8) // caps filter for programs running network services
815 816
816// run sbox 817// run sbox
817int sbox_run(unsigned filter, int num, ...); 818int sbox_run(unsigned filter, int num, ...);
@@ -827,4 +828,9 @@ void set_profile_run_file(pid_t pid, const char *fname);
827// dbus.c 828// dbus.c
828void dbus_disable(void); 829void dbus_disable(void);
829 830
831// dhcp.c
832extern pid_t dhclient4_pid;
833extern pid_t dhclient6_pid;
834void dhcp_start(void);
835
830#endif 836#endif
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 995e98f9f..bcd812ab2 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -337,6 +337,8 @@ static int monitor_application(pid_t app_pid) {
337 continue; 337 continue;
338 if (pid == 1) 338 if (pid == 1)
339 continue; 339 continue;
340 if (pid == dhclient4_pid || pid == dhclient6_pid)
341 continue;
340 342
341 // todo: make this generic 343 // todo: make this generic
342 // Dillo browser leaves a dpid process running, we need to shut it down 344 // Dillo browser leaves a dpid process running, we need to shut it down
@@ -1016,6 +1018,11 @@ int sandbox(void* sandbox_arg) {
1016 fs_logger_change_owner(); 1018 fs_logger_change_owner();
1017 1019
1018 //**************************** 1020 //****************************
1021 // start dhcp client
1022 //****************************
1023 dhcp_start();
1024
1025 //****************************
1019 // set application environment 1026 // set application environment
1020 //**************************** 1027 //****************************
1021 EUID_USER(); 1028 EUID_USER();
diff --git a/src/include/rundefs.h b/src/include/rundefs.h
index df135b9ca..8119f31e9 100644
--- a/src/include/rundefs.h
+++ b/src/include/rundefs.h
@@ -49,6 +49,11 @@
49#define RUN_LIB_DIR RUN_MNT_DIR "/lib" 49#define RUN_LIB_DIR RUN_MNT_DIR "/lib"
50#define RUN_LIB_FILE RUN_MNT_DIR "/libfiles" 50#define RUN_LIB_FILE RUN_MNT_DIR "/libfiles"
51#define RUN_DNS_ETC RUN_MNT_DIR "/dns-etc" 51#define RUN_DNS_ETC RUN_MNT_DIR "/dns-etc"
52#define RUN_DHCLIENT_DIR RUN_MNT_DIR "/dhclient"
53#define RUN_DHCLIENT_4_LEASES_FILE RUN_DHCLIENT_DIR "/dhclient.leases"
54#define RUN_DHCLIENT_6_LEASES_FILE RUN_DHCLIENT_DIR "/dhclient6.leases"
55#define RUN_DHCLIENT_4_PID_FILE RUN_DHCLIENT_DIR "/dhclient.pid"
56#define RUN_DHCLIENT_6_PID_FILE RUN_DHCLIENT_DIR "/dhclient6.pid"
52 57
53#define RUN_SECCOMP_DIR RUN_MNT_DIR "/seccomp" 58#define RUN_SECCOMP_DIR RUN_MNT_DIR "/seccomp"
54#define RUN_SECCOMP_LIST RUN_SECCOMP_DIR "/seccomp.list" // list of seccomp files installed 59#define RUN_SECCOMP_LIST RUN_SECCOMP_DIR "/seccomp.list" // list of seccomp files installed