aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kris7topher@gmail.com>2020-01-28 16:59:49 +0100
committerLibravatar GitHub <noreply@github.com>2020-01-28 16:59:49 +0100
commit93cecca281c42fe7b96605b612f3c206d528cae2 (patch)
treecca6801d42fab82ab09f6e8d334c5d96ebdd86ce
parentclarify dropping python2 support in meld.profile (#3167) (diff)
parentDo not declare variable in for loop (diff)
downloadfirejail-93cecca281c42fe7b96605b612f3c206d528cae2.tar.gz
firejail-93cecca281c42fe7b96605b612f3c206d528cae2.tar.zst
firejail-93cecca281c42fe7b96605b612f3c206d528cae2.zip
Merge pull request #3190 from kris7t/dhcp-client
DHCP client code quality fixes
-rw-r--r--src/firejail/dhcp.c205
-rw-r--r--src/firejail/firejail.h4
-rw-r--r--src/firejail/fs_hostname.c6
-rw-r--r--src/firejail/main.c24
-rw-r--r--src/firejail/network_main.c8
-rw-r--r--src/firejail/profile.c26
-rw-r--r--src/firejail/sandbox.c8
-rw-r--r--src/firejail/sbox.c47
-rw-r--r--src/firejail/usage.c2
-rw-r--r--src/fnet/interface.c194
-rw-r--r--src/include/rundefs.h11
-rw-r--r--src/man/firejail-profile.txt53
-rw-r--r--src/man/firejail.txt49
13 files changed, 376 insertions, 261 deletions
diff --git a/src/firejail/dhcp.c b/src/firejail/dhcp.c
index f7d417d3d..7593a47f2 100644
--- a/src/firejail/dhcp.c
+++ b/src/firejail/dhcp.c
@@ -29,130 +29,133 @@ pid_t dhclient4_pid = 0;
29pid_t dhclient6_pid = 0; 29pid_t dhclient6_pid = 0;
30 30
31typedef struct { 31typedef struct {
32 char *version_arg; 32 char *version_arg;
33 char *pid_file; 33 char *pid_file;
34 char *leases_file; 34 char *leases_file;
35 uint8_t generate_duid; 35 uint8_t generate_duid;
36 char *duid_leases_file; 36 char *duid_leases_file;
37 pid_t *pid; 37 pid_t *pid;
38 ptrdiff_t arg_offset; 38 ptrdiff_t arg_offset;
39} Dhclient; 39} Dhclient;
40 40
41static const Dhclient dhclient4 = { .version_arg = "-4", 41static const Dhclient dhclient4 = {
42 .pid_file = RUN_DHCLIENT_4_PID_FILE, 42 .version_arg = "-4",
43 .leases_file = RUN_DHCLIENT_4_LEASES_FILE, 43 .pid_file = RUN_DHCLIENT_4_PID_FILE,
44 .generate_duid = 1, 44 .leases_file = RUN_DHCLIENT_4_LEASES_FILE,
45 .pid = &dhclient4_pid, 45 .generate_duid = 1,
46 .arg_offset = offsetof(Bridge, arg_ip_dhcp) 46 .pid = &dhclient4_pid,
47 .arg_offset = offsetof(Bridge, arg_ip_dhcp)
47}; 48};
48 49
49static const Dhclient dhclient6 = { .version_arg = "-6", 50static const Dhclient dhclient6 = {
50 .pid_file = RUN_DHCLIENT_6_PID_FILE, 51 .version_arg = "-6",
51 .leases_file = RUN_DHCLIENT_6_LEASES_FILE, 52 .pid_file = RUN_DHCLIENT_6_PID_FILE,
52 .duid_leases_file = RUN_DHCLIENT_4_LEASES_FILE, 53 .leases_file = RUN_DHCLIENT_6_LEASES_FILE,
53 .pid = &dhclient6_pid, 54 .duid_leases_file = RUN_DHCLIENT_4_LEASES_FILE,
54 .arg_offset = offsetof(Bridge, arg_ip6_dhcp) 55 .pid = &dhclient6_pid,
56 .arg_offset = offsetof(Bridge, arg_ip6_dhcp)
55}; 57};
56 58
57static void dhcp_run_dhclient(const Dhclient *client) { 59static void dhcp_run_dhclient(const Dhclient *client) {
58 char *argv[256] = { "dhclient", 60 char *argv[256] = {
59 client->version_arg, 61 "dhclient",
60 "-pf", client->pid_file, 62 client->version_arg,
61 "-lf", client->leases_file, 63 "-pf", client->pid_file,
62 }; 64 "-lf", client->leases_file,
63 int i = 6; 65 };
64 if (client->generate_duid) 66 int i = 6;
65 argv[i++] = "-i"; 67 if (client->generate_duid)
66 if (client->duid_leases_file) { 68 argv[i++] = "-i";
67 argv[i++] = "-df"; 69 if (client->duid_leases_file) {
68 argv[i++] = client->duid_leases_file; 70 argv[i++] = "-df";
69 } 71 argv[i++] = client->duid_leases_file;
70 if (arg_debug) 72 }
71 argv[i++] = "-v"; 73 if (arg_debug)
72 if (*(uint8_t *) ((char *) &cfg.bridge0 + client->arg_offset)) 74 argv[i++] = "-v";
73 argv[i++] = cfg.bridge0.devsandbox; 75 if (*(uint8_t *)((char *)&cfg.bridge0 + client->arg_offset))
74 if (*(uint8_t *) ((char *) &cfg.bridge1 + client->arg_offset)) 76 argv[i++] = cfg.bridge0.devsandbox;
75 argv[i++] = cfg.bridge1.devsandbox; 77 if (*(uint8_t *)((char *)&cfg.bridge1 + client->arg_offset))
76 if (*(uint8_t *) ((char *) &cfg.bridge2 + client->arg_offset)) 78 argv[i++] = cfg.bridge1.devsandbox;
77 argv[i++] = cfg.bridge2.devsandbox; 79 if (*(uint8_t *)((char *)&cfg.bridge2 + client->arg_offset))
78 if (*(uint8_t *) ((char *) &cfg.bridge3 + client->arg_offset)) 80 argv[i++] = cfg.bridge2.devsandbox;
79 argv[i++] = cfg.bridge3.devsandbox; 81 if (*(uint8_t *)((char *)&cfg.bridge3 + client->arg_offset))
82 argv[i++] = cfg.bridge3.devsandbox;
80 83
81 sbox_run_v(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_CAPS_NET_SERVICE | SBOX_SECCOMP, argv); 84 sbox_run_v(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_CAPS_NET_SERVICE | SBOX_SECCOMP, argv);
82} 85}
83 86
84static pid_t dhcp_read_pidfile(const Dhclient *client) { 87static pid_t dhcp_read_pidfile(const Dhclient *client) {
85 // We have to run dhclient as a forking daemon (not pass the -d option), 88 // 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. 89 // 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, 90 // However, try to be extra paranoid with race conditions,
88 // because dhclient only writes the daemon pid into the pidfile 91 // because dhclient only writes the daemon pid into the pidfile
89 // after its parent process has exited. 92 // after its parent process has exited.
90 int tries = 0; 93 int tries = 0;
91 pid_t found = 0; 94 pid_t found = 0;
92 while (found == 0 && tries < 10) { 95 while (found == 0 && tries < 10) {
93 if (tries >= 1) 96 if (tries >= 1)
94 usleep(100000); 97 usleep(100000);
95 FILE *pidfile = fopen(client->pid_file, "r"); 98 FILE *pidfile = fopen(client->pid_file, "r");
96 if (pidfile) { 99 if (pidfile) {
97 long pid; 100 long pid;
98 if (fscanf(pidfile, "%ld", &pid) == 1) { 101 if (fscanf(pidfile, "%ld", &pid) == 1) {
99 char *pidname = pid_proc_comm((pid_t) pid); 102 char *pidname = pid_proc_comm((pid_t) pid);
100 if (pidname && strcmp(pidname, "dhclient") == 0) 103 if (pidname && strcmp(pidname, "dhclient") == 0)
101 found = (pid_t) pid; 104 found = (pid_t) pid;
102 } 105 }
103 fclose(pidfile); 106 fclose(pidfile);
104 } 107 }
105 ++tries; 108 ++tries;
106 } 109 }
107 if (found == 0) { 110 if (found == 0) {
108 fprintf(stderr, "Error: Cannot get dhclient %s PID from %s\n", 111 fprintf(stderr, "Error: Cannot get dhclient %s PID from %s\n",
109 client->version_arg, client->pid_file); 112 client->version_arg, client->pid_file);
110 exit(1); 113 exit(1);
111 } 114 }
112 return found; 115 return found;
113} 116}
114 117
115static void dhcp_start_dhclient(const Dhclient *client) { 118static void dhcp_start_dhclient(const Dhclient *client) {
116 dhcp_run_dhclient(client); 119 dhcp_run_dhclient(client);
117 *(client->pid) = dhcp_read_pidfile(client); 120 *(client->pid) = dhcp_read_pidfile(client);
118} 121}
119 122
120static void dhcp_waitll(const char *ifname) { 123static void dhcp_waitll(const char *ifname) {
121 sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, PATH_FNET, "waitll", ifname); 124 sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, PATH_FNET, "waitll", ifname);
122} 125}
123 126
124static void dhcp_waitll_all() { 127static void dhcp_waitll_all() {
125 if (cfg.bridge0.arg_ip6_dhcp) 128 if (cfg.bridge0.arg_ip6_dhcp)
126 dhcp_waitll(cfg.bridge0.devsandbox); 129 dhcp_waitll(cfg.bridge0.devsandbox);
127 if (cfg.bridge1.arg_ip6_dhcp) 130 if (cfg.bridge1.arg_ip6_dhcp)
128 dhcp_waitll(cfg.bridge1.devsandbox); 131 dhcp_waitll(cfg.bridge1.devsandbox);
129 if (cfg.bridge2.arg_ip6_dhcp) 132 if (cfg.bridge2.arg_ip6_dhcp)
130 dhcp_waitll(cfg.bridge2.devsandbox); 133 dhcp_waitll(cfg.bridge2.devsandbox);
131 if (cfg.bridge3.arg_ip6_dhcp) 134 if (cfg.bridge3.arg_ip6_dhcp)
132 dhcp_waitll(cfg.bridge3.devsandbox); 135 dhcp_waitll(cfg.bridge3.devsandbox);
133} 136}
134 137
135void dhcp_start(void) { 138void dhcp_start(void) {
136 if (!any_dhcp()) 139 if (!any_dhcp())
137 return; 140 return;
138 141
139 EUID_ROOT(); 142 EUID_ROOT();
140 if (mkdir(RUN_DHCLIENT_DIR, 0700)) 143 if (mkdir(RUN_DHCLIENT_DIR, 0700))
141 errExit("mkdir"); 144 errExit("mkdir");
142 145
143 if (any_ip_dhcp()) { 146 if (any_ip_dhcp()) {
144 dhcp_start_dhclient(&dhclient4); 147 dhcp_start_dhclient(&dhclient4);
145 if (arg_debug) 148 if (arg_debug)
146 printf("Running dhclient -4 in the background as pid %ld\n", (long) dhclient4_pid); 149 printf("Running dhclient -4 in the background as pid %ld\n", (long) dhclient4_pid);
147 } 150 }
148 if (any_ip6_dhcp()) { 151 if (any_ip6_dhcp()) {
149 dhcp_waitll_all(); 152 dhcp_waitll_all();
150 dhcp_start_dhclient(&dhclient6); 153 dhcp_start_dhclient(&dhclient6);
151 if (arg_debug) 154 if (arg_debug)
152 printf("Running dhclient -6 in the background as pid %ld\n", (long) dhclient6_pid); 155 printf("Running dhclient -6 in the background as pid %ld\n", (long) dhclient6_pid);
153 if (dhclient4_pid == dhclient6_pid) { 156 if (dhclient4_pid == dhclient6_pid) {
154 fprintf(stderr, "Error: dhclient -4 and -6 have the same PID: %ld\n", (long) dhclient4_pid); 157 fprintf(stderr, "Error: dhclient -4 and -6 have the same PID: %ld\n", (long) dhclient4_pid);
155 exit(1); 158 exit(1);
156 } 159 }
157 } 160 }
158} 161}
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 8f90152e6..4dc580a5e 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -103,8 +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; 106 uint8_t arg_ip_dhcp;
107 uint8_t arg_ip6_dhcp; 107 uint8_t arg_ip6_dhcp;
108 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, ...)
109 uint8_t configured; 109 uint8_t configured;
110 uint8_t scan; // set by --scan 110 uint8_t scan; // set by --scan
diff --git a/src/firejail/fs_hostname.c b/src/firejail/fs_hostname.c
index 15e9f9ca9..fd5e1bbd3 100644
--- a/src/firejail/fs_hostname.c
+++ b/src/firejail/fs_hostname.c
@@ -171,10 +171,10 @@ void fs_resolvconf(void) {
171 } 171 }
172 172
173 if (cfg.dns1) { 173 if (cfg.dns1) {
174 if (any_dhcp()) 174 if (any_dhcp())
175 fwarning("network setup uses DHCP, nameservers will likely be overwritten\n"); 175 fwarning("network setup uses DHCP, nameservers will likely be overwritten\n");
176 fprintf(fp, "nameserver %s\n", cfg.dns1); 176 fprintf(fp, "nameserver %s\n", cfg.dns1);
177 } 177 }
178 if (cfg.dns2) 178 if (cfg.dns2)
179 fprintf(fp, "nameserver %s\n", cfg.dns2); 179 fprintf(fp, "nameserver %s\n", cfg.dns2);
180 if (cfg.dns3) 180 if (cfg.dns3)
diff --git a/src/firejail/main.c b/src/firejail/main.c
index d2784ed2c..78717ab41 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -2193,18 +2193,18 @@ int main(int argc, char **argv) {
2193 } 2193 }
2194 2194
2195 // configure this IP address for the last bridge defined 2195 // configure this IP address for the last bridge defined
2196 if (strcmp(argv[i] + 6, "dhcp") == 0) 2196 if (strcmp(argv[i] + 6, "dhcp") == 0)
2197 br->arg_ip6_dhcp = 1; 2197 br->arg_ip6_dhcp = 1;
2198 else { 2198 else {
2199 if (check_ip46_address(argv[i] + 6) == 0) { 2199 if (check_ip46_address(argv[i] + 6) == 0) {
2200 fprintf(stderr, "Error: invalid IPv6 address\n"); 2200 fprintf(stderr, "Error: invalid IPv6 address\n");
2201 exit(1); 2201 exit(1);
2202 } 2202 }
2203 2203
2204 br->ip6sandbox = strdup(argv[i] + 6); 2204 br->ip6sandbox = strdup(argv[i] + 6);
2205 if (br->ip6sandbox == NULL) 2205 if (br->ip6sandbox == NULL)
2206 errExit("strdup"); 2206 errExit("strdup");
2207 } 2207 }
2208 } 2208 }
2209 else 2209 else
2210 exit_err_feature("networking"); 2210 exit_err_feature("networking");
diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c
index b5ea63360..85896e528 100644
--- a/src/firejail/network_main.c
+++ b/src/firejail/network_main.c
@@ -246,10 +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, 249 // if the first network has no assigned address,
250 // do not try to set up a gateway, because it will fail 250 // do not try to set up a gateway, because it will fail
251 if (cfg.bridge0.arg_ip_none) 251 if (cfg.bridge0.arg_ip_none)
252 return; 252 return;
253 // first network is a regular bridge 253 // first network is a regular bridge
254 if (cfg.bridge0.macvlan == 0) 254 if (cfg.bridge0.macvlan == 0)
255 cfg.defaultgw = cfg.bridge0.ip; 255 cfg.defaultgw = cfg.bridge0.ip;
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index b77d03d3f..969209869 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -701,19 +701,19 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
701 exit(1); 701 exit(1);
702 } 702 }
703 703
704 // configure this IP address for the last bridge defined 704 // configure this IP address for the last bridge defined
705 if (strcmp(ptr + 4, "dhcp") == 0) 705 if (strcmp(ptr + 4, "dhcp") == 0)
706 br->arg_ip6_dhcp = 1; 706 br->arg_ip6_dhcp = 1;
707 else { 707 else {
708 if (check_ip46_address(ptr + 4) == 0) { 708 if (check_ip46_address(ptr + 4) == 0) {
709 fprintf(stderr, "Error: invalid IPv6 address\n"); 709 fprintf(stderr, "Error: invalid IPv6 address\n");
710 exit(1); 710 exit(1);
711 } 711 }
712 712
713 br->ip6sandbox = strdup(ptr + 4); 713 br->ip6sandbox = strdup(ptr + 4);
714 if (br->ip6sandbox == NULL) 714 if (br->ip6sandbox == NULL)
715 errExit("strdup"); 715 errExit("strdup");
716 } 716 }
717 } 717 }
718 else 718 else
719 warning_feature_disabled("networking"); 719 warning_feature_disabled("networking");
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 58c30ec34..96ad30bed 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -337,8 +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) 340 if ((pid_t) pid == dhclient4_pid || (pid_t) pid == dhclient6_pid)
341 continue; 341 continue;
342 342
343 // todo: make this generic 343 // todo: make this generic
344 // 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
@@ -995,7 +995,7 @@ int sandbox(void* sandbox_arg) {
995 fs_dev_disable_dvd(); 995 fs_dev_disable_dvd();
996 996
997 if (arg_nou2f) 997 if (arg_nou2f)
998 fs_dev_disable_u2f(); 998 fs_dev_disable_u2f();
999 999
1000 if (arg_novideo) 1000 if (arg_novideo)
1001 fs_dev_disable_video(); 1001 fs_dev_disable_video();
@@ -1020,7 +1020,7 @@ int sandbox(void* sandbox_arg) {
1020 //**************************** 1020 //****************************
1021 // start dhcp client 1021 // start dhcp client
1022 //**************************** 1022 //****************************
1023 dhcp_start(); 1023 dhcp_start();
1024 1024
1025 //**************************** 1025 //****************************
1026 // set application environment 1026 // set application environment
diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c
index 1ead5a449..203c0fc03 100644
--- a/src/firejail/sbox.c
+++ b/src/firejail/sbox.c
@@ -128,11 +128,11 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) {
128 128
129 if (arg_debug) { 129 if (arg_debug) {
130 printf("sbox run: "); 130 printf("sbox run: ");
131 int i = 0; 131 int i = 0;
132 while (arg[i]) { 132 while (arg[i]) {
133 printf("%s ", arg[i]); 133 printf("%s ", arg[i]);
134 i++; 134 i++;
135 } 135 }
136 printf("\n"); 136 printf("\n");
137 } 137 }
138 138
@@ -182,7 +182,8 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) {
182 182
183 // close all other file descriptors 183 // close all other file descriptors
184 int max = 20; // getdtablesize() is overkill for a firejail process 184 int max = 20; // getdtablesize() is overkill for a firejail process
185 for (int i = 3; i < max; i++) 185 int i = 3;
186 for (i = 3; i < max; i++)
186 close(i); // close open files 187 close(i); // close open files
187 188
188 umask(027); 189 umask(027);
@@ -191,33 +192,33 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) {
191 if (filtermask & SBOX_CAPS_NONE) { 192 if (filtermask & SBOX_CAPS_NONE) {
192 caps_drop_all(); 193 caps_drop_all();
193 } else { 194 } else {
194 uint64_t set = 0; 195 uint64_t set = 0;
195 if (filtermask & SBOX_CAPS_NETWORK) { 196 if (filtermask & SBOX_CAPS_NETWORK) {
196#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files 197#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files
197 set |= ((uint64_t) 1) << CAP_NET_ADMIN; 198 set |= ((uint64_t) 1) << CAP_NET_ADMIN;
198 set |= ((uint64_t) 1) << CAP_NET_RAW; 199 set |= ((uint64_t) 1) << CAP_NET_RAW;
199#endif 200#endif
200 } 201 }
201 if (filtermask & SBOX_CAPS_HIDEPID) { 202 if (filtermask & SBOX_CAPS_HIDEPID) {
202#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files 203#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files
203 set |= ((uint64_t) 1) << CAP_SYS_PTRACE; 204 set |= ((uint64_t) 1) << CAP_SYS_PTRACE;
204 set |= ((uint64_t) 1) << CAP_SYS_PACCT; 205 set |= ((uint64_t) 1) << CAP_SYS_PACCT;
205#endif 206#endif
206 } 207 }
207 if (filtermask & SBOX_CAPS_NET_SERVICE) { 208 if (filtermask & SBOX_CAPS_NET_SERVICE) {
208#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files 209#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_BIND_SERVICE;
210 set |= ((uint64_t) 1) << CAP_NET_BROADCAST; 211 set |= ((uint64_t) 1) << CAP_NET_BROADCAST;
211#endif 212#endif
212 } 213 }
213 if (set != 0) { // some SBOX_CAPS_ flag was specified, drop all other capabilities 214 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#ifndef HAVE_GCOV // the following filter will prevent GCOV from saving info in .gcda files
215 caps_set(set); 216 caps_set(set);
216#endif 217#endif
217 } 218 }
218 } 219 }
219 220
220 if (filtermask & SBOX_SECCOMP) { 221 if (filtermask & SBOX_SECCOMP) {
221 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { 222 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
222 perror("prctl(NO_NEW_PRIVS)"); 223 perror("prctl(NO_NEW_PRIVS)");
223 } 224 }
diff --git a/src/firejail/usage.c b/src/firejail/usage.c
index c8dc39a04..52d4f7c03 100644
--- a/src/firejail/usage.c
+++ b/src/firejail/usage.c
@@ -82,7 +82,9 @@ static char *usage_str =
82 " --interface=name - move interface in sandbox.\n" 82 " --interface=name - move interface in sandbox.\n"
83 " --ip=address - set interface IP address.\n" 83 " --ip=address - set interface IP address.\n"
84 " --ip=none - no IP address and no default gateway are configured.\n" 84 " --ip=none - no IP address and no default gateway are configured.\n"
85 " --ip=dhcp - acquire IP address by running dhclient.\n"
85 " --ip6=address - set interface IPv6 address.\n" 86 " --ip6=address - set interface IPv6 address.\n"
87 " --ip6=dhcp - acquire IPv6 address by running dhclient.\n"
86 " --iprange=address,address - configure an IP address in this range.\n" 88 " --iprange=address,address - configure an IP address in this range.\n"
87#endif 89#endif
88 " --ipc-namespace - enable a new IPC namespace.\n" 90 " --ipc-namespace - enable a new IPC namespace.\n"
diff --git a/src/fnet/interface.c b/src/fnet/interface.c
index 3b44b70e3..62df0930e 100644
--- a/src/fnet/interface.c
+++ b/src/fnet/interface.c
@@ -374,81 +374,83 @@ void net_if_ip6(const char *ifname, const char *addr6) {
374} 374}
375 375
376static int net_netlink_address_tentative(struct nlmsghdr *current_header) { 376static int net_netlink_address_tentative(struct nlmsghdr *current_header) {
377 struct ifaddrmsg *msg = NLMSG_DATA(current_header); 377 struct ifaddrmsg *msg = NLMSG_DATA(current_header);
378 struct rtattr *rta = IFA_RTA(msg); 378 int has_flags = 0;
379 size_t msg_len = IFA_PAYLOAD(current_header); 379#ifdef IFA_FLAGS
380 int has_flags = 0; 380 struct rtattr *rta = IFA_RTA(msg);
381 while (RTA_OK(rta, msg_len)) { 381 size_t msg_len = IFA_PAYLOAD(current_header);
382 if (rta->rta_type == IFA_FLAGS) { 382 while (RTA_OK(rta, msg_len)) {
383 has_flags = 1; 383 if (rta->rta_type == IFA_FLAGS) {
384 uint32_t *flags = RTA_DATA(rta); 384 has_flags = 1;
385 if (*flags & IFA_F_TENTATIVE) 385 uint32_t *flags = RTA_DATA(rta);
386 return 1; 386 if (*flags & IFA_F_TENTATIVE)
387 } 387 return 1;
388 rta = RTA_NEXT(rta, msg_len); 388 }
389 } 389 rta = RTA_NEXT(rta, msg_len);
390 // According to <linux/if_addr.h>, if an IFA_FLAGS attribute is present, 390 }
391 // the field ifa_flags should be ignored. 391#endif
392 return !has_flags && (msg->ifa_flags & IFA_F_TENTATIVE); 392 // According to <linux/if_addr.h>, if an IFA_FLAGS attribute is present,
393 // the field ifa_flags should be ignored.
394 return !has_flags && (msg->ifa_flags & IFA_F_TENTATIVE);
393} 395}
394 396
395static int net_netlink_if_has_ll(int sock, int index) { 397static int net_netlink_if_has_ll(int sock, uint32_t index) {
396 struct { 398 struct {
397 struct nlmsghdr header; 399 struct nlmsghdr header;
398 struct ifaddrmsg message; 400 struct ifaddrmsg message;
399 } req; 401 } req;
400 memset(&req, 0, sizeof(req)); 402 memset(&req, 0, sizeof(req));
401 req.header.nlmsg_len = NLMSG_LENGTH(sizeof(req.message)); 403 req.header.nlmsg_len = NLMSG_LENGTH(sizeof(req.message));
402 req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 404 req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
403 req.header.nlmsg_type = RTM_GETADDR; 405 req.header.nlmsg_type = RTM_GETADDR;
404 req.message.ifa_family = AF_INET6; 406 req.message.ifa_family = AF_INET6;
405 if (send(sock, &req, req.header.nlmsg_len, 0) != req.header.nlmsg_len) 407 if (send(sock, &req, req.header.nlmsg_len, 0) != req.header.nlmsg_len)
406 errExit("send"); 408 errExit("send");
407 409
408 int found = 0; 410 int found = 0;
409 int all_parts_processed = 0; 411 int all_parts_processed = 0;
410 while (!all_parts_processed) { 412 while (!all_parts_processed) {
411 char buf[16384]; 413 char buf[16384];
412 ssize_t len = recv(sock, buf, sizeof(buf), 0); 414 ssize_t len = recv(sock, buf, sizeof(buf), 0);
413 if (len < 0) 415 if (len < 0)
414 errExit("recv"); 416 errExit("recv");
415 if (len < sizeof(struct nlmsghdr)) { 417 if (len < (ssize_t) sizeof(struct nlmsghdr)) {
416 fprintf(stderr, "Received incomplete netlink message\n"); 418 fprintf(stderr, "Received incomplete netlink message\n");
417 exit(1); 419 exit(1);
418 } 420 }
419 421
420 struct nlmsghdr *current_header = (struct nlmsghdr *) buf; 422 struct nlmsghdr *current_header = (struct nlmsghdr *) buf;
421 while (NLMSG_OK(current_header, len)) { 423 while (NLMSG_OK(current_header, len)) {
422 switch (current_header->nlmsg_type) { 424 switch (current_header->nlmsg_type) {
423 case RTM_NEWADDR: { 425 case RTM_NEWADDR: {
424 struct ifaddrmsg *msg = NLMSG_DATA(current_header); 426 struct ifaddrmsg *msg = NLMSG_DATA(current_header);
425 if (!found && msg->ifa_index == index && msg->ifa_scope == RT_SCOPE_LINK && 427 if (!found && msg->ifa_index == index && msg->ifa_scope == RT_SCOPE_LINK &&
426 !net_netlink_address_tentative(current_header)) 428 !net_netlink_address_tentative(current_header))
427 found = 1; 429 found = 1;
428 } 430 }
429 break; 431 break;
430 case NLMSG_NOOP: 432 case NLMSG_NOOP:
431 break; 433 break;
432 case NLMSG_DONE: 434 case NLMSG_DONE:
433 all_parts_processed = 1; 435 all_parts_processed = 1;
434 break; 436 break;
435 case NLMSG_ERROR: { 437 case NLMSG_ERROR: {
436 struct nlmsgerr *err = NLMSG_DATA(current_header); 438 struct nlmsgerr *err = NLMSG_DATA(current_header);
437 fprintf(stderr, "Netlink error: %d\n", err->error); 439 fprintf(stderr, "Netlink error: %d\n", err->error);
438 exit(1); 440 exit(1);
439 } 441 }
440 break; 442 break;
441 default: 443 default:
442 fprintf(stderr, "Unknown netlink message type: %u\n", current_header->nlmsg_type); 444 fprintf(stderr, "Unknown netlink message type: %u\n", current_header->nlmsg_type);
443 exit(1); 445 exit(1);
444 break; 446 break;
445 } 447 }
446 448
447 current_header = NLMSG_NEXT(current_header, len); 449 current_header = NLMSG_NEXT(current_header, len);
448 } 450 }
449 } 451 }
450 452
451 return found; 453 return found;
452} 454}
453 455
454// wait for a link-local IPv6 address for DHCPv6 456// wait for a link-local IPv6 address for DHCPv6
@@ -468,27 +470,31 @@ void net_if_waitll(const char *ifname) {
468 perror("ioctl SIOGIFINDEX"); 470 perror("ioctl SIOGIFINDEX");
469 exit(1); 471 exit(1);
470 } 472 }
471 close(inet6_sock); 473 close(inet6_sock);
472 int index = ifr.ifr_ifindex; 474 if (ifr.ifr_ifindex < 0) {
475 fprintf(stderr, "Error fnet: interface index is negative\n");
476 exit(1);
477 }
478 uint32_t index = (uint32_t) ifr.ifr_ifindex;
473 479
474 // poll for link-local address 480 // poll for link-local address
475 int netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 481 int netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
476 if (netlink_sock < 0) 482 if (netlink_sock < 0)
477 errExit("socket"); 483 errExit("socket");
478 int tries = 0; 484 int tries = 0;
479 int found = 0; 485 int found = 0;
480 while (tries < 60 && !found) { 486 while (tries < 60 && !found) {
481 if (tries >= 1) 487 if (tries >= 1)
482 usleep(500000); 488 usleep(500000);
483 489
484 found = net_netlink_if_has_ll(netlink_sock, index); 490 found = net_netlink_if_has_ll(netlink_sock, index);
485 491
486 tries++; 492 tries++;
487 } 493 }
488 close(netlink_sock); 494 close(netlink_sock);
489 495
490 if (!found) { 496 if (!found) {
491 fprintf(stderr, "Waiting for link-local IPv6 address of %s timed out\n", ifname); 497 fprintf(stderr, "Waiting for link-local IPv6 address of %s timed out\n", ifname);
492 exit(1); 498 exit(1);
493 } 499 }
494} 500}
diff --git a/src/include/rundefs.h b/src/include/rundefs.h
index b33d1f8c1..7f9c68be2 100644
--- a/src/include/rundefs.h
+++ b/src/include/rundefs.h
@@ -49,11 +49,12 @@
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" 52#define RUN_DHCLIENT_DIR RUN_MNT_DIR "/dhclient"
53#define RUN_DHCLIENT_4_LEASES_FILE RUN_DHCLIENT_DIR "/dhclient.leases" 53#define RUN_DHCLIENT_4_LEASES_FILE RUN_DHCLIENT_DIR "/dhclient.leases"
54#define RUN_DHCLIENT_6_LEASES_FILE RUN_DHCLIENT_DIR "/dhclient6.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" 55#define RUN_DHCLIENT_4_LEASES_FILE RUN_DHCLIENT_DIR "/dhclient.leases"
56#define RUN_DHCLIENT_6_PID_FILE RUN_DHCLIENT_DIR "/dhclient6.pid" 56#define RUN_DHCLIENT_4_PID_FILE RUN_DHCLIENT_DIR "/dhclient.pid"
57#define RUN_DHCLIENT_6_PID_FILE RUN_DHCLIENT_DIR "/dhclient6.pid"
57 58
58#define RUN_SECCOMP_DIR RUN_MNT_DIR "/seccomp" 59#define RUN_SECCOMP_DIR RUN_MNT_DIR "/seccomp"
59#define RUN_SECCOMP_LIST RUN_SECCOMP_DIR "/seccomp.list" // list of seccomp files installed 60#define RUN_SECCOMP_LIST RUN_SECCOMP_DIR "/seccomp.list" // list of seccomp files installed
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt
index 719a80c2c..84aed41a4 100644
--- a/src/man/firejail-profile.txt
+++ b/src/man/firejail-profile.txt
@@ -582,6 +582,33 @@ net eth0
582ip none 582ip none
583 583
584.TP 584.TP
585\fBip dhcp
586Acquire an IP address and default gateway for the last interface defined by a
587net command, as well as set the DNS servers according to the DHCP response.
588This command requires the ISC dhclient DHCP client to be installed and will start
589it automatically inside the sandbox.
590.br
591
592.br
593Example:
594.br
595net br0
596.br
597ip dhcp
598.br
599
600.br
601This command should not be used in conjunction with the dns command if the
602DHCP server is set to configure DNS servers for the clients, because the
603manually specified DNS servers will be overwritten.
604
605.br
606The DHCP client will NOT release the DHCP lease when the sandbox terminates.
607If your DHCP server requires leases to be explicitly released, consider running
608a DHCP client and releasing the lease manually in conjunction with the
609net none command.
610
611.TP
585\fBip6 address 612\fBip6 address
586Assign IPv6 addresses to the last network interface defined by a net command. 613Assign IPv6 addresses to the last network interface defined by a net command.
587.br 614.br
@@ -594,6 +621,32 @@ net eth0
594ip6 2001:0db8:0:f101::1/64 621ip6 2001:0db8:0:f101::1/64
595 622
596.TP 623.TP
624\fBip6 dhcp
625Acquire an IPv6 address and default gateway for the last interface defined by a
626net command, as well as set the DNS servers according to the DHCP response.
627This command requires the ISC dhclient DHCP client to be installed and will start
628it automatically inside the sandbox.
629.br
630
631.br
632Example:
633.br
634net br0
635.br
636ip6 dhcp
637.br
638
639.br
640This command should not be used in conjunction with the dns command if the
641DHCP server is set to configure DNS servers for the clients, because the
642manually specified DNS servers will be overwritten.
643
644.br
645The DHCP client will NOT release the DHCP lease when the sandbox terminates.
646If your DHCP server requires leases to be explicitly released, consider running
647a DHCP client and releasing the lease manually.
648
649.TP
597\fBiprange address,address 650\fBiprange address,address
598Assign an IP address in the provided range to the last network 651Assign an IP address in the provided range to the last network
599interface defined by a net command. A default gateway is assigned by default. 652interface defined by a net command. A default gateway is assigned by default.
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index e5abcdcf5..4b1134686 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -567,6 +567,31 @@ If the corresponding interface doesn't have an IP address configured, this
567option is enabled by default. 567option is enabled by default.
568 568
569.TP 569.TP
570\fB\-\-ip=dhcp
571Acquire an IP address and default gateway for the last interface defined by a
572\-\-net option, as well as set the DNS servers according to the DHCP response.
573This option requires the ISC dhclient DHCP client to be installed and will start
574it automatically inside the sandbox.
575.br
576
577.br
578Example:
579.br
580$ firejail \-\-net=br0 \-\-ip=dhcp
581.br
582
583.br
584This option should not be used in conjunction with the \-\-dns option if the
585DHCP server is set to configure DNS servers for the clients, because the
586manually specified DNS servers will be overwritten.
587
588.br
589The DHCP client will NOT release the DHCP lease when the sandbox terminates.
590If your DHCP server requires leases to be explicitly released, consider running
591a DHCP client and releasing the lease manually in conjunction with the
592\-\-net=none option.
593
594.TP
570\fB\-\-ip6=address 595\fB\-\-ip6=address
571Assign IPv6 addresses to the last network interface defined by a \-\-net option. 596Assign IPv6 addresses to the last network interface defined by a \-\-net option.
572.br 597.br
@@ -579,6 +604,30 @@ $ firejail \-\-net=eth0 \-\-ip6=2001:0db8:0:f101::1/64 firefox
579Note: you don't need this option if you obtain your ip6 address from router via SLAAC (your ip6 address and default route will be configured by kernel automatically). 604Note: you don't need this option if you obtain your ip6 address from router via SLAAC (your ip6 address and default route will be configured by kernel automatically).
580 605
581.TP 606.TP
607\fB\-\-ip6=dhcp
608Acquire an IPv6 address and default gateway for the last interface defined by a
609\-\-net option, as well as set the DNS servers according to the DHCP response.
610This option requires the ISC dhclient DHCP client to be installed and will start
611it automatically inside the sandbox.
612.br
613
614.br
615Example:
616.br
617$ firejail \-\-net=br0 \-\-ip6=dhcp
618.br
619
620.br
621This option should not be used in conjunction with the \-\-dns option if the
622DHCP server is set to configure DNS servers for the clients, because the
623manually specified DNS servers will be overwritten.
624
625.br
626The DHCP client will NOT release the DHCP lease when the sandbox terminates.
627If your DHCP server requires leases to be explicitly released, consider running
628a DHCP client and releasing the lease manually.
629
630.TP
582\fB\-\-iprange=address,address 631\fB\-\-iprange=address,address
583Assign an IP address in the provided range to the last network interface defined by a \-\-net option. A 632Assign an IP address in the provided range to the last network interface defined by a \-\-net option. A
584default gateway is assigned by default. 633default gateway is assigned by default.