diff options
author | netblue30 <netblue30@yahoo.com> | 2020-01-07 11:35:05 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-07 11:35:05 -0500 |
commit | 94d018cec4daa4d409877640277156d2038628ed (patch) | |
tree | 482de240dc24d0c781a46e0220469ee9c19acd07 | |
parent | hexchat: comment machine-id (diff) | |
parent | Wait for link-local address for DHCPv6 (diff) | |
download | firejail-94d018cec4daa4d409877640277156d2038628ed.tar.gz firejail-94d018cec4daa4d409877640277156d2038628ed.tar.zst firejail-94d018cec4daa4d409877640277156d2038628ed.zip |
Merge pull request #3102 from kris7t/dhcp-client
DHCP client support
-rw-r--r-- | src/firejail/dhcp.c | 158 | ||||
-rw-r--r-- | src/firejail/firejail.h | 27 | ||||
-rw-r--r-- | src/firejail/fs_hostname.c | 10 | ||||
-rw-r--r-- | src/firejail/main.c | 27 | ||||
-rw-r--r-- | src/firejail/network_main.c | 4 | ||||
-rw-r--r-- | src/firejail/profile.c | 30 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 7 | ||||
-rw-r--r-- | src/firejail/sbox.c | 58 | ||||
-rw-r--r-- | src/fnet/fnet.h | 1 | ||||
-rw-r--r-- | src/fnet/interface.c | 122 | ||||
-rw-r--r-- | src/fnet/main.c | 4 | ||||
-rw-r--r-- | src/include/rundefs.h | 5 |
12 files changed, 410 insertions, 43 deletions
diff --git a/src/firejail/dhcp.c b/src/firejail/dhcp.c new file mode 100644 index 000000000..7ce9a2b18 --- /dev/null +++ b/src/firejail/dhcp.c | |||
@@ -0,0 +1,158 @@ | |||
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 | |||
28 | pid_t dhclient4_pid = 0; | ||
29 | pid_t dhclient6_pid = 0; | ||
30 | |||
31 | typedef 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 | |||
41 | static 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 | |||
49 | static 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 | |||
57 | static 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 | |||
84 | static 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 | |||
115 | static void dhcp_start_dhclient(const Dhclient *client) { | ||
116 | dhcp_run_dhclient(client); | ||
117 | *(client->pid) = dhcp_read_pidfile(client); | ||
118 | } | ||
119 | |||
120 | static void dhcp_waitll(const char *ifname) { | ||
121 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, PATH_FNET, "waitll", ifname); | ||
122 | } | ||
123 | |||
124 | static void dhcp_waitll_all() { | ||
125 | if (cfg.bridge0.arg_ip6_dhcp) | ||
126 | dhcp_waitll(cfg.bridge0.devsandbox); | ||
127 | if (cfg.bridge1.arg_ip6_dhcp) | ||
128 | dhcp_waitll(cfg.bridge1.devsandbox); | ||
129 | if (cfg.bridge2.arg_ip6_dhcp) | ||
130 | dhcp_waitll(cfg.bridge2.devsandbox); | ||
131 | if (cfg.bridge3.arg_ip6_dhcp) | ||
132 | dhcp_waitll(cfg.bridge3.devsandbox); | ||
133 | } | ||
134 | |||
135 | void dhcp_start(void) { | ||
136 | if (!any_dhcp()) | ||
137 | return; | ||
138 | |||
139 | EUID_ROOT(); | ||
140 | if (mkdir(RUN_DHCLIENT_DIR, 0700)) | ||
141 | errExit("mkdir"); | ||
142 | |||
143 | if (any_ip_dhcp()) { | ||
144 | dhcp_start_dhclient(&dhclient4); | ||
145 | if (arg_debug) | ||
146 | printf("Running dhclient -4 in the background as pid %ld\n", (long) dhclient4_pid); | ||
147 | } | ||
148 | if (any_ip6_dhcp()) { | ||
149 | dhcp_waitll_all(); | ||
150 | dhcp_start_dhclient(&dhclient6); | ||
151 | if (arg_debug) | ||
152 | printf("Running dhclient -6 in the background as pid %ld\n", (long) dhclient6_pid); | ||
153 | if (dhclient4_pid == dhclient6_pid) { | ||
154 | fprintf(stderr, "Error: dhclient -4 and -6 have the same PID: %ld\n", (long) dhclient4_pid); | ||
155 | exit(1); | ||
156 | } | ||
157 | } | ||
158 | } | ||
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 03bcbda46..4beae587e 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -103,6 +103,8 @@ typedef struct bridge_t { | |||
103 | 103 | ||
104 | // flags | 104 | // flags |
105 | uint8_t arg_ip_none; // --ip=none | 105 | uint8_t arg_ip_none; // --ip=none |
106 | uint8_t arg_ip_dhcp; | ||
107 | uint8_t arg_ip6_dhcp; | ||
106 | uint8_t macvlan; // set by --net=eth0 (or eth1, ...); reset by --net=br0 (or br1, ...) | 108 | uint8_t macvlan; // set by --net=eth0 (or eth1, ...); reset by --net=br0 (or br1, ...) |
107 | uint8_t configured; | 109 | uint8_t configured; |
108 | uint8_t scan; // set by --scan | 110 | uint8_t scan; // set by --scan |
@@ -237,6 +239,24 @@ static inline int any_interface_configured(void) { | |||
237 | return 0; | 239 | return 0; |
238 | } | 240 | } |
239 | 241 | ||
242 | static inline int any_ip_dhcp(void) { | ||
243 | if (cfg.bridge0.arg_ip_dhcp || cfg.bridge1.arg_ip_dhcp || cfg.bridge2.arg_ip_dhcp || cfg.bridge3.arg_ip_dhcp) | ||
244 | return 1; | ||
245 | else | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static inline int any_ip6_dhcp(void) { | ||
250 | if (cfg.bridge0.arg_ip6_dhcp || cfg.bridge1.arg_ip6_dhcp || cfg.bridge2.arg_ip6_dhcp || cfg.bridge3.arg_ip6_dhcp) | ||
251 | return 1; | ||
252 | else | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static inline int any_dhcp(void) { | ||
257 | return any_ip_dhcp() || any_ip6_dhcp(); | ||
258 | } | ||
259 | |||
240 | extern int arg_private; // mount private /home | 260 | extern int arg_private; // mount private /home |
241 | extern int arg_private_cache; // private home/.cache | 261 | extern int arg_private_cache; // private home/.cache |
242 | extern int arg_debug; // print debug messages | 262 | extern int arg_debug; // print debug messages |
@@ -792,9 +812,11 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc, | |||
792 | #define SBOX_ALLOW_STDIN (1 << 5) // don't close stdin | 812 | #define SBOX_ALLOW_STDIN (1 << 5) // don't close stdin |
793 | #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 |
794 | #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 | ||
795 | 816 | ||
796 | // run sbox | 817 | // run sbox |
797 | int sbox_run(unsigned filter, int num, ...); | 818 | int sbox_run(unsigned filter, int num, ...); |
819 | int sbox_run_v(unsigned filter, char * const arg[]); | ||
798 | 820 | ||
799 | // run_files.c | 821 | // run_files.c |
800 | void delete_run_files(pid_t pid); | 822 | void delete_run_files(pid_t pid); |
@@ -806,4 +828,9 @@ void set_profile_run_file(pid_t pid, const char *fname); | |||
806 | // dbus.c | 828 | // dbus.c |
807 | void dbus_disable(void); | 829 | void dbus_disable(void); |
808 | 830 | ||
831 | // dhcp.c | ||
832 | extern pid_t dhclient4_pid; | ||
833 | extern pid_t dhclient6_pid; | ||
834 | void dhcp_start(void); | ||
835 | |||
809 | #endif | 836 | #endif |
diff --git a/src/firejail/fs_hostname.c b/src/firejail/fs_hostname.c index 9da01b24c..60c65746b 100644 --- a/src/firejail/fs_hostname.c +++ b/src/firejail/fs_hostname.c | |||
@@ -89,7 +89,7 @@ errexit: | |||
89 | } | 89 | } |
90 | 90 | ||
91 | void fs_resolvconf(void) { | 91 | void fs_resolvconf(void) { |
92 | if (cfg.dns1 == NULL) | 92 | if (cfg.dns1 == NULL && !any_dhcp()) |
93 | return; | 93 | return; |
94 | 94 | ||
95 | if (arg_debug) | 95 | if (arg_debug) |
@@ -108,7 +108,8 @@ void fs_resolvconf(void) { | |||
108 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) | 108 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) |
109 | continue; | 109 | continue; |
110 | // for resolv.conf we create a brand new file | 110 | // for resolv.conf we create a brand new file |
111 | if (strcmp(entry->d_name, "resolv.conf") == 0) | 111 | if (strcmp(entry->d_name, "resolv.conf") == 0 || |
112 | strcmp(entry->d_name, "resolv.conf.dhclient-new") == 0) | ||
112 | continue; | 113 | continue; |
113 | // printf("linking %s\n", entry->d_name); | 114 | // printf("linking %s\n", entry->d_name); |
114 | 115 | ||
@@ -169,8 +170,11 @@ void fs_resolvconf(void) { | |||
169 | exit(1); | 170 | exit(1); |
170 | } | 171 | } |
171 | 172 | ||
172 | if (cfg.dns1) | 173 | if (cfg.dns1) { |
174 | if (any_dhcp()) | ||
175 | fwarning("network setup uses DHCP, nameservers will likely be overwritten\n"); | ||
173 | fprintf(fp, "nameserver %s\n", cfg.dns1); | 176 | fprintf(fp, "nameserver %s\n", cfg.dns1); |
177 | } | ||
174 | if (cfg.dns2) | 178 | if (cfg.dns2) |
175 | fprintf(fp, "nameserver %s\n", cfg.dns2); | 179 | fprintf(fp, "nameserver %s\n", cfg.dns2); |
176 | if (cfg.dns3) | 180 | if (cfg.dns3) |
diff --git a/src/firejail/main.c b/src/firejail/main.c index 179f8ddf9..0b9ebc482 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -2144,7 +2144,10 @@ int main(int argc, char **argv) { | |||
2144 | // configure this IP address for the last bridge defined | 2144 | // configure this IP address for the last bridge defined |
2145 | if (strcmp(argv[i] + 5, "none") == 0) | 2145 | if (strcmp(argv[i] + 5, "none") == 0) |
2146 | br->arg_ip_none = 1; | 2146 | br->arg_ip_none = 1; |
2147 | else { | 2147 | else if (strcmp(argv[i] + 5, "dhcp") == 0) { |
2148 | br->arg_ip_none = 1; | ||
2149 | br->arg_ip_dhcp = 1; | ||
2150 | } else { | ||
2148 | if (atoip(argv[i] + 5, &br->ipsandbox)) { | 2151 | if (atoip(argv[i] + 5, &br->ipsandbox)) { |
2149 | fprintf(stderr, "Error: invalid IP address\n"); | 2152 | fprintf(stderr, "Error: invalid IP address\n"); |
2150 | exit(1); | 2153 | exit(1); |
@@ -2184,20 +2187,24 @@ int main(int argc, char **argv) { | |||
2184 | fprintf(stderr, "Error: no network device configured\n"); | 2187 | fprintf(stderr, "Error: no network device configured\n"); |
2185 | exit(1); | 2188 | exit(1); |
2186 | } | 2189 | } |
2187 | if (br->ip6sandbox) { | 2190 | if (br->arg_ip6_dhcp || br->ip6sandbox) { |
2188 | fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); | 2191 | fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); |
2189 | exit(1); | 2192 | exit(1); |
2190 | } | 2193 | } |
2191 | 2194 | ||
2192 | // configure this IP address for the last bridge defined | 2195 | // configure this IP address for the last bridge defined |
2193 | if (check_ip46_address(argv[i] + 6) == 0) { | 2196 | if (strcmp(argv[i] + 6, "dhcp") == 0) |
2194 | fprintf(stderr, "Error: invalid IPv6 address\n"); | 2197 | br->arg_ip6_dhcp = 1; |
2195 | exit(1); | 2198 | else { |
2196 | } | 2199 | if (check_ip46_address(argv[i] + 6) == 0) { |
2197 | 2200 | fprintf(stderr, "Error: invalid IPv6 address\n"); | |
2198 | br->ip6sandbox = strdup(argv[i] + 6); | 2201 | exit(1); |
2199 | if (br->ip6sandbox == NULL) | 2202 | } |
2200 | errExit("strdup"); | 2203 | |
2204 | br->ip6sandbox = strdup(argv[i] + 6); | ||
2205 | if (br->ip6sandbox == NULL) | ||
2206 | errExit("strdup"); | ||
2207 | } | ||
2201 | } | 2208 | } |
2202 | else | 2209 | else |
2203 | exit_err_feature("networking"); | 2210 | exit_err_feature("networking"); |
diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c index 6800bde8d..ad297efc7 100644 --- a/src/firejail/network_main.c +++ b/src/firejail/network_main.c | |||
@@ -246,6 +246,10 @@ void net_check_cfg(void) { | |||
246 | if (cfg.defaultgw) | 246 | if (cfg.defaultgw) |
247 | check_default_gw(cfg.defaultgw); | 247 | check_default_gw(cfg.defaultgw); |
248 | else { | 248 | else { |
249 | // if the first network has no assigned address, | ||
250 | // do not try to set up a gateway, because it will fail | ||
251 | if (cfg.bridge0.arg_ip_none) | ||
252 | return; | ||
249 | // first network is a regular bridge | 253 | // first network is a regular bridge |
250 | if (cfg.bridge0.macvlan == 0) | 254 | if (cfg.bridge0.macvlan == 0) |
251 | cfg.defaultgw = cfg.bridge0.ip; | 255 | cfg.defaultgw = cfg.bridge0.ip; |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 9a724331b..959678501 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -672,7 +672,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
672 | // configure this IP address for the last bridge defined | 672 | // configure this IP address for the last bridge defined |
673 | if (strcmp(ptr + 3, "none") == 0) | 673 | if (strcmp(ptr + 3, "none") == 0) |
674 | br->arg_ip_none = 1; | 674 | br->arg_ip_none = 1; |
675 | else { | 675 | else if (strcmp(ptr + 3, "dhcp") == 0) { |
676 | br->arg_ip_none = 1; | ||
677 | br->arg_ip_dhcp = 1; | ||
678 | } else { | ||
676 | if (atoip(ptr + 3, &br->ipsandbox)) { | 679 | if (atoip(ptr + 3, &br->ipsandbox)) { |
677 | fprintf(stderr, "Error: invalid IP address\n"); | 680 | fprintf(stderr, "Error: invalid IP address\n"); |
678 | exit(1); | 681 | exit(1); |
@@ -693,21 +696,24 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
693 | fprintf(stderr, "Error: no network device configured\n"); | 696 | fprintf(stderr, "Error: no network device configured\n"); |
694 | exit(1); | 697 | exit(1); |
695 | } | 698 | } |
696 | if (br->ip6sandbox) { | 699 | if (br->arg_ip6_dhcp || br->ip6sandbox) { |
697 | fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); | 700 | fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); |
698 | exit(1); | 701 | exit(1); |
699 | } | 702 | } |
700 | 703 | ||
701 | // configure this IP address for the last bridge defined | 704 | // configure this IP address for the last bridge defined |
702 | if (check_ip46_address(ptr + 4) == 0) { | 705 | if (strcmp(ptr + 4, "dhcp") == 0) |
703 | fprintf(stderr, "Error: invalid IPv6 address\n"); | 706 | br->arg_ip6_dhcp = 1; |
704 | exit(1); | 707 | else { |
705 | } | 708 | if (check_ip46_address(ptr + 4) == 0) { |
706 | 709 | fprintf(stderr, "Error: invalid IPv6 address\n"); | |
707 | br->ip6sandbox = strdup(ptr + 4); | 710 | exit(1); |
708 | if (br->ip6sandbox == NULL) | 711 | } |
709 | errExit("strdup"); | 712 | |
710 | 713 | br->ip6sandbox = strdup(ptr + 4); | |
714 | if (br->ip6sandbox == NULL) | ||
715 | errExit("strdup"); | ||
716 | } | ||
711 | } | 717 | } |
712 | else | 718 | else |
713 | warning_feature_disabled("networking"); | 719 | warning_feature_disabled("networking"); |
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/firejail/sbox.c b/src/firejail/sbox.c index e5739ecb5..a1e65cd3c 100644 --- a/src/firejail/sbox.c +++ b/src/firejail/sbox.c | |||
@@ -105,23 +105,34 @@ static struct sock_fprog prog = { | |||
105 | }; | 105 | }; |
106 | 106 | ||
107 | int sbox_run(unsigned filtermask, int num, ...) { | 107 | int sbox_run(unsigned filtermask, int num, ...) { |
108 | EUID_ROOT(); | ||
109 | |||
110 | int i; | ||
111 | va_list valist; | 108 | va_list valist; |
112 | va_start(valist, num); | 109 | va_start(valist, num); |
113 | 110 | ||
114 | // build argument list | 111 | // build argument list |
115 | char *arg[num + 1]; | 112 | char **arg = malloc((num + 1) * sizeof(char *)); |
113 | int i; | ||
116 | for (i = 0; i < num; i++) | 114 | for (i = 0; i < num; i++) |
117 | arg[i] = va_arg(valist, char*); | 115 | arg[i] = va_arg(valist, char*); |
118 | arg[i] = NULL; | 116 | arg[i] = NULL; |
119 | va_end(valist); | 117 | va_end(valist); |
120 | 118 | ||
119 | int status = sbox_run_v(filtermask, arg); | ||
120 | |||
121 | free(arg); | ||
122 | |||
123 | return status; | ||
124 | } | ||
125 | |||
126 | int sbox_run_v(unsigned filtermask, char * const arg[]) { | ||
127 | EUID_ROOT(); | ||
128 | |||
121 | if (arg_debug) { | 129 | if (arg_debug) { |
122 | printf("sbox run: "); | 130 | printf("sbox run: "); |
123 | for (i = 0; i <= num; i++) | 131 | int i = 0; |
132 | while (arg[i]) { | ||
124 | printf("%s ", arg[i]); | 133 | printf("%s ", arg[i]); |
134 | i++; | ||
135 | } | ||
125 | printf("\n"); | 136 | printf("\n"); |
126 | } | 137 | } |
127 | 138 | ||
@@ -171,7 +182,7 @@ int sbox_run(unsigned filtermask, int num, ...) { | |||
171 | 182 | ||
172 | // close all other file descriptors | 183 | // close all other file descriptors |
173 | int max = 20; // getdtablesize() is overkill for a firejail process | 184 | int max = 20; // getdtablesize() is overkill for a firejail process |
174 | for (i = 3; i < max; i++) | 185 | for (int i = 3; i < max; i++) |
175 | close(i); // close open files | 186 | close(i); // close open files |
176 | 187 | ||
177 | umask(027); | 188 | umask(027); |
@@ -179,23 +190,34 @@ int sbox_run(unsigned filtermask, int num, ...) { | |||
179 | // apply filters | 190 | // apply filters |
180 | if (filtermask & SBOX_CAPS_NONE) { | 191 | if (filtermask & SBOX_CAPS_NONE) { |
181 | caps_drop_all(); | 192 | caps_drop_all(); |
182 | } | 193 | } else { |
183 | else if (filtermask & SBOX_CAPS_NETWORK) { | 194 | uint64_t set = 0; |
195 | if (filtermask & SBOX_CAPS_NETWORK) { | ||
184 | #ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files | 196 | #ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files |
185 | uint64_t set = ((uint64_t) 1) << CAP_NET_ADMIN; | 197 | set |= ((uint64_t) 1) << CAP_NET_ADMIN; |
186 | set |= ((uint64_t) 1) << CAP_NET_RAW; | 198 | set |= ((uint64_t) 1) << CAP_NET_RAW; |
187 | caps_set(set); | ||
188 | #endif | 199 | #endif |
189 | } | 200 | } |
190 | else if (filtermask & SBOX_CAPS_HIDEPID) { | 201 | if (filtermask & SBOX_CAPS_HIDEPID) { |
191 | #ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files | 202 | #ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files |
192 | uint64_t set = ((uint64_t) 1) << CAP_SYS_PTRACE; | 203 | set |= ((uint64_t) 1) << CAP_SYS_PTRACE; |
193 | set |= ((uint64_t) 1) << CAP_SYS_PACCT; | 204 | set |= ((uint64_t) 1) << CAP_SYS_PACCT; |
194 | caps_set(set); | ||
195 | #endif | 205 | #endif |
196 | } | 206 | } |
207 | if (filtermask & SBOX_CAPS_NET_SERVICE) { | ||
208 | #ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files | ||
209 | set |= ((uint64_t) 1) << CAP_NET_BIND_SERVICE; | ||
210 | set |= ((uint64_t) 1) << CAP_NET_BROADCAST; | ||
211 | #endif | ||
212 | } | ||
213 | if (set != 0) { // some SBOX_CAPS_ flag was specified, drop all other capabilities | ||
214 | #ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files | ||
215 | caps_set(set); | ||
216 | #endif | ||
217 | } | ||
218 | } | ||
197 | 219 | ||
198 | if (filtermask & SBOX_SECCOMP) { | 220 | if (filtermask & SBOX_SECCOMP) { |
199 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | 221 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { |
200 | perror("prctl(NO_NEW_PRIVS)"); | 222 | perror("prctl(NO_NEW_PRIVS)"); |
201 | } | 223 | } |
diff --git a/src/fnet/fnet.h b/src/fnet/fnet.h index 4900967f7..4d0d62b39 100644 --- a/src/fnet/fnet.h +++ b/src/fnet/fnet.h | |||
@@ -47,6 +47,7 @@ int net_get_mac(const char *ifname, unsigned char mac[6]); | |||
47 | void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); | 47 | void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); |
48 | int net_if_mac(const char *ifname, const unsigned char mac[6]); | 48 | int net_if_mac(const char *ifname, const unsigned char mac[6]); |
49 | void net_if_ip6(const char *ifname, const char *addr6); | 49 | void net_if_ip6(const char *ifname, const char *addr6); |
50 | void net_if_waitll(const char *ifname); | ||
50 | 51 | ||
51 | 52 | ||
52 | // arp.c | 53 | // arp.c |
diff --git a/src/fnet/interface.c b/src/fnet/interface.c index 7e7cceeed..1c07ec8f7 100644 --- a/src/fnet/interface.c +++ b/src/fnet/interface.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include <net/if_arp.h> | 28 | #include <net/if_arp.h> |
29 | #include <net/route.h> | 29 | #include <net/route.h> |
30 | #include <linux/if_bridge.h> | 30 | #include <linux/if_bridge.h> |
31 | #include <linux/netlink.h> | ||
32 | #include <linux/rtnetlink.h> | ||
31 | 33 | ||
32 | static void check_if_name(const char *ifname) { | 34 | static void check_if_name(const char *ifname) { |
33 | if (strlen(ifname) > IFNAMSIZ) { | 35 | if (strlen(ifname) > IFNAMSIZ) { |
@@ -370,3 +372,123 @@ void net_if_ip6(const char *ifname, const char *addr6) { | |||
370 | 372 | ||
371 | close(sock); | 373 | close(sock); |
372 | } | 374 | } |
375 | |||
376 | static int net_netlink_address_tentative(struct nlmsghdr *current_header) { | ||
377 | struct ifaddrmsg *msg = NLMSG_DATA(current_header); | ||
378 | struct rtattr *rta = IFA_RTA(msg); | ||
379 | size_t msg_len = IFA_PAYLOAD(current_header); | ||
380 | int has_flags = 0; | ||
381 | while (RTA_OK(rta, msg_len)) { | ||
382 | if (rta->rta_type == IFA_FLAGS) { | ||
383 | has_flags = 1; | ||
384 | uint32_t *flags = RTA_DATA(rta); | ||
385 | if (*flags & IFA_F_TENTATIVE) | ||
386 | return 1; | ||
387 | } | ||
388 | rta = RTA_NEXT(rta, msg_len); | ||
389 | } | ||
390 | // According to <linux/if_addr.h>, if an IFA_FLAGS attribute is present, | ||
391 | // the field ifa_flags should be ignored. | ||
392 | return !has_flags && (msg->ifa_flags & IFA_F_TENTATIVE); | ||
393 | } | ||
394 | |||
395 | static int net_netlink_if_has_ll(int sock, int index) { | ||
396 | struct { | ||
397 | struct nlmsghdr header; | ||
398 | struct ifaddrmsg message; | ||
399 | } req; | ||
400 | memset(&req, 0, sizeof(req)); | ||
401 | req.header.nlmsg_len = NLMSG_LENGTH(sizeof(req.message)); | ||
402 | req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; | ||
403 | req.header.nlmsg_type = RTM_GETADDR; | ||
404 | req.message.ifa_family = AF_INET6; | ||
405 | if (send(sock, &req, req.header.nlmsg_len, 0) != req.header.nlmsg_len) | ||
406 | errExit("send"); | ||
407 | |||
408 | int found = 0; | ||
409 | int all_parts_processed = 0; | ||
410 | while (!all_parts_processed) { | ||
411 | char buf[16384]; | ||
412 | ssize_t len = recv(sock, buf, sizeof(buf), 0); | ||
413 | if (len < 0) | ||
414 | errExit("recv"); | ||
415 | if (len < sizeof(struct nlmsghdr)) { | ||
416 | fprintf(stderr, "Received incomplete netlink message\n"); | ||
417 | exit(1); | ||
418 | } | ||
419 | |||
420 | struct nlmsghdr *current_header = (struct nlmsghdr *) buf; | ||
421 | while (NLMSG_OK(current_header, len)) { | ||
422 | switch (current_header->nlmsg_type) { | ||
423 | case RTM_NEWADDR: { | ||
424 | struct ifaddrmsg *msg = NLMSG_DATA(current_header); | ||
425 | if (!found && msg->ifa_index == index && msg->ifa_scope == RT_SCOPE_LINK && | ||
426 | !net_netlink_address_tentative(current_header)) | ||
427 | found = 1; | ||
428 | } | ||
429 | break; | ||
430 | case NLMSG_NOOP: | ||
431 | break; | ||
432 | case NLMSG_DONE: | ||
433 | all_parts_processed = 1; | ||
434 | break; | ||
435 | case NLMSG_ERROR: { | ||
436 | struct nlmsgerr *err = NLMSG_DATA(current_header); | ||
437 | fprintf(stderr, "Netlink error: %d\n", err->error); | ||
438 | exit(1); | ||
439 | } | ||
440 | break; | ||
441 | default: | ||
442 | fprintf(stderr, "Unknown netlink message type: %u\n", current_header->nlmsg_type); | ||
443 | exit(1); | ||
444 | break; | ||
445 | } | ||
446 | |||
447 | current_header = NLMSG_NEXT(current_header, len); | ||
448 | } | ||
449 | } | ||
450 | |||
451 | return found; | ||
452 | } | ||
453 | |||
454 | // wait for a link-local IPv6 address for DHCPv6 | ||
455 | // ex: firejail --net=br0 --ip6=dhcp | ||
456 | void net_if_waitll(const char *ifname) { | ||
457 | // find interface index | ||
458 | int inet6_sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); | ||
459 | if (inet6_sock < 0) { | ||
460 | fprintf(stderr, "Error fnet: IPv6 is not supported on this system\n"); | ||
461 | exit(1); | ||
462 | } | ||
463 | struct ifreq ifr; | ||
464 | memset(&ifr, 0, sizeof(ifr)); | ||
465 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); | ||
466 | ifr.ifr_addr.sa_family = AF_INET; | ||
467 | if (ioctl(inet6_sock, SIOGIFINDEX, &ifr) < 0) { | ||
468 | perror("ioctl SIOGIFINDEX"); | ||
469 | exit(1); | ||
470 | } | ||
471 | close(inet6_sock); | ||
472 | int index = ifr.ifr_ifindex; | ||
473 | |||
474 | // poll for link-local address | ||
475 | int netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | ||
476 | if (netlink_sock < 0) | ||
477 | errExit("socket"); | ||
478 | int tries = 0; | ||
479 | int found = 0; | ||
480 | while (tries < 60 && !found) { | ||
481 | if (tries >= 1) | ||
482 | usleep(500000); | ||
483 | |||
484 | found = net_netlink_if_has_ll(netlink_sock, index); | ||
485 | |||
486 | tries++; | ||
487 | } | ||
488 | close(netlink_sock); | ||
489 | |||
490 | if (!found) { | ||
491 | fprintf(stderr, "Waiting for link-local IPv6 address of %s timed out\n", ifname); | ||
492 | exit(1); | ||
493 | } | ||
494 | } | ||
diff --git a/src/fnet/main.c b/src/fnet/main.c index 890f842f6..3ef500b5e 100644 --- a/src/fnet/main.c +++ b/src/fnet/main.c | |||
@@ -47,6 +47,7 @@ static void usage(void) { | |||
47 | printf("\tfnet config mac addr\n"); | 47 | printf("\tfnet config mac addr\n"); |
48 | printf("\tfnet config ipv6 dev ip\n"); | 48 | printf("\tfnet config ipv6 dev ip\n"); |
49 | printf("\tfnet ifup dev\n"); | 49 | printf("\tfnet ifup dev\n"); |
50 | printf("\tfnet waitll dev\n"); | ||
50 | } | 51 | } |
51 | 52 | ||
52 | int main(int argc, char **argv) { | 53 | int main(int argc, char **argv) { |
@@ -141,6 +142,9 @@ printf("\n"); | |||
141 | else if (argc == 5 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "ipv6") == 0) { | 142 | else if (argc == 5 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "ipv6") == 0) { |
142 | net_if_ip6(argv[3], argv[4]); | 143 | net_if_ip6(argv[3], argv[4]); |
143 | } | 144 | } |
145 | else if (argc == 3 && strcmp(argv[1], "waitll") == 0) { | ||
146 | net_if_waitll(argv[2]); | ||
147 | } | ||
144 | else { | 148 | else { |
145 | fprintf(stderr, "Error fnet: invalid arguments\n"); | 149 | fprintf(stderr, "Error fnet: invalid arguments\n"); |
146 | return 1; | 150 | return 1; |
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 |