aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@yahoo.com>2020-01-07 11:35:05 -0500
committerLibravatar GitHub <noreply@github.com>2020-01-07 11:35:05 -0500
commit94d018cec4daa4d409877640277156d2038628ed (patch)
tree482de240dc24d0c781a46e0220469ee9c19acd07 /src
parenthexchat: comment machine-id (diff)
parentWait for link-local address for DHCPv6 (diff)
downloadfirejail-94d018cec4daa4d409877640277156d2038628ed.tar.gz
firejail-94d018cec4daa4d409877640277156d2038628ed.tar.zst
firejail-94d018cec4daa4d409877640277156d2038628ed.zip
Merge pull request #3102 from kris7t/dhcp-client
DHCP client support
Diffstat (limited to 'src')
-rw-r--r--src/firejail/dhcp.c158
-rw-r--r--src/firejail/firejail.h27
-rw-r--r--src/firejail/fs_hostname.c10
-rw-r--r--src/firejail/main.c27
-rw-r--r--src/firejail/network_main.c4
-rw-r--r--src/firejail/profile.c30
-rw-r--r--src/firejail/sandbox.c7
-rw-r--r--src/firejail/sbox.c58
-rw-r--r--src/fnet/fnet.h1
-rw-r--r--src/fnet/interface.c122
-rw-r--r--src/fnet/main.c4
-rw-r--r--src/include/rundefs.h5
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
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
120static void dhcp_waitll(const char *ifname) {
121 sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, PATH_FNET, "waitll", ifname);
122}
123
124static 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
135void 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
242static 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
249static 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
256static inline int any_dhcp(void) {
257 return any_ip_dhcp() || any_ip6_dhcp();
258}
259
240extern int arg_private; // mount private /home 260extern int arg_private; // mount private /home
241extern int arg_private_cache; // private home/.cache 261extern int arg_private_cache; // private home/.cache
242extern int arg_debug; // print debug messages 262extern 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
797int sbox_run(unsigned filter, int num, ...); 818int sbox_run(unsigned filter, int num, ...);
819int sbox_run_v(unsigned filter, char * const arg[]);
798 820
799// run_files.c 821// run_files.c
800void delete_run_files(pid_t pid); 822void 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
807void dbus_disable(void); 829void dbus_disable(void);
808 830
831// dhcp.c
832extern pid_t dhclient4_pid;
833extern pid_t dhclient6_pid;
834void 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
91void fs_resolvconf(void) { 91void 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
107int sbox_run(unsigned filtermask, int num, ...) { 107int 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
126int 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]);
47void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); 47void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu);
48int net_if_mac(const char *ifname, const unsigned char mac[6]); 48int net_if_mac(const char *ifname, const unsigned char mac[6]);
49void net_if_ip6(const char *ifname, const char *addr6); 49void net_if_ip6(const char *ifname, const char *addr6);
50void 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
32static void check_if_name(const char *ifname) { 34static 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
376static 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
395static 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
456void 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
52int main(int argc, char **argv) { 53int 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