diff options
Diffstat (limited to 'src/firejail/network_main.c')
-rw-r--r-- | src/firejail/network_main.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c new file mode 100644 index 000000000..c2459b0cd --- /dev/null +++ b/src/firejail/network_main.c | |||
@@ -0,0 +1,268 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #include "firejail.h" | ||
22 | #include <sys/types.h> | ||
23 | #include <sys/stat.h> | ||
24 | #include <unistd.h> | ||
25 | #include <net/if.h> | ||
26 | |||
27 | // configure bridge structure | ||
28 | // - extract ip address and mask from the bridge interface | ||
29 | void net_configure_bridge(Bridge *br, char *dev_name) { | ||
30 | assert(br); | ||
31 | assert(dev_name); | ||
32 | |||
33 | br->dev = dev_name; | ||
34 | |||
35 | // check the bridge device exists | ||
36 | char sysbridge[30 + strlen(br->dev)]; | ||
37 | sprintf(sysbridge, "/sys/class/net/%s/bridge", br->dev); | ||
38 | struct stat s; | ||
39 | int rv = stat(sysbridge, &s); | ||
40 | if (rv == 0) { | ||
41 | // this is a bridge device | ||
42 | br->macvlan = 0; | ||
43 | } | ||
44 | else { | ||
45 | // is this a regular Ethernet interface | ||
46 | if (if_nametoindex(br->dev) > 0) { | ||
47 | br->macvlan = 1; | ||
48 | char *newname; | ||
49 | if (asprintf(&newname, "%s-%u", br->devsandbox, getpid()) == -1) | ||
50 | errExit("asprintf"); | ||
51 | br->devsandbox = newname; | ||
52 | } | ||
53 | else { | ||
54 | fprintf(stderr, "Error: cannot find network device %s\n", br->dev); | ||
55 | exit(1); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | if (net_get_if_addr(br->dev, &br->ip, &br->mask, br->mac)) { | ||
60 | fprintf(stderr, "Error: interface %s is not configured\n", br->dev); | ||
61 | exit(1); | ||
62 | } | ||
63 | if (arg_debug) { | ||
64 | if (br->macvlan == 0) | ||
65 | printf("Bridge device %s at %d.%d.%d.%d/%d\n", | ||
66 | br->dev, PRINT_IP(br->ip), mask2bits(br->mask)); | ||
67 | else | ||
68 | printf("macvlan parent device %s at %d.%d.%d.%d/%d\n", | ||
69 | br->dev, PRINT_IP(br->ip), mask2bits(br->mask)); | ||
70 | } | ||
71 | |||
72 | uint32_t range = ~br->mask + 1; // the number of potential addresses | ||
73 | // this software is not supported for /31 networks | ||
74 | if (range < 4) { | ||
75 | fprintf(stderr, "Error: the software is not supported for /31 networks\n"); | ||
76 | exit(1); | ||
77 | } | ||
78 | br->configured = 1; | ||
79 | } | ||
80 | |||
81 | |||
82 | void net_configure_sandbox_ip(Bridge *br) { | ||
83 | assert(br); | ||
84 | if (br->configured == 0) | ||
85 | return; | ||
86 | |||
87 | if (br->arg_ip_none) | ||
88 | br->ipsandbox = 0; | ||
89 | else if (br->ipsandbox) { | ||
90 | // check network range | ||
91 | char *rv = in_netrange(br->ipsandbox, br->ip, br->mask); | ||
92 | if (rv) { | ||
93 | fprintf(stderr, "%s", rv); | ||
94 | exit(1); | ||
95 | } | ||
96 | // send an ARP request and check if there is anybody on this IP address | ||
97 | if (arp_check(br->dev, br->ipsandbox, br->ip)) { | ||
98 | fprintf(stderr, "Error: IP address %d.%d.%d.%d is already in use\n", PRINT_IP(br->ipsandbox)); | ||
99 | exit(1); | ||
100 | } | ||
101 | } | ||
102 | else | ||
103 | // ip address assigned by arp-scan for a bridge device | ||
104 | br->ipsandbox = arp_assign(br->dev, br); //br->ip, br->mask); | ||
105 | } | ||
106 | |||
107 | |||
108 | // create a veth pair | ||
109 | // - br - bridge device | ||
110 | // - ifname - interface name in sandbox namespace | ||
111 | // - child - child process running the namespace | ||
112 | |||
113 | void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child) { | ||
114 | assert(br); | ||
115 | if (br->configured == 0) | ||
116 | return; | ||
117 | |||
118 | // create a veth pair | ||
119 | char *dev; | ||
120 | if (asprintf(&dev, "veth%u%s", getpid(), ifname) < 0) | ||
121 | errExit("asprintf"); | ||
122 | net_create_veth(dev, ifname, child); | ||
123 | |||
124 | // bring up the interface | ||
125 | net_if_up(dev); | ||
126 | |||
127 | // add interface to the bridge | ||
128 | net_bridge_add_interface(br->dev, dev); | ||
129 | |||
130 | char *msg; | ||
131 | if (asprintf(&msg, "%d.%d.%d.%d address assigned to sandbox", PRINT_IP(br->ipsandbox)) == -1) | ||
132 | errExit("asprintf"); | ||
133 | logmsg(msg); | ||
134 | fflush(0); | ||
135 | free(msg); | ||
136 | } | ||
137 | |||
138 | // the default address should be in the range of at least on of the bridge devices | ||
139 | void check_default_gw(uint32_t defaultgw) { | ||
140 | assert(defaultgw); | ||
141 | |||
142 | if (cfg.bridge0.configured) { | ||
143 | char *rv = in_netrange(defaultgw, cfg.bridge0.ip, cfg.bridge0.mask); | ||
144 | if (rv == 0) | ||
145 | return; | ||
146 | } | ||
147 | if (cfg.bridge1.configured) { | ||
148 | char *rv = in_netrange(defaultgw, cfg.bridge1.ip, cfg.bridge1.mask); | ||
149 | if (rv == 0) | ||
150 | return; | ||
151 | } | ||
152 | if (cfg.bridge2.configured) { | ||
153 | char *rv = in_netrange(defaultgw, cfg.bridge2.ip, cfg.bridge2.mask); | ||
154 | if (rv == 0) | ||
155 | return; | ||
156 | } | ||
157 | if (cfg.bridge3.configured) { | ||
158 | char *rv = in_netrange(defaultgw, cfg.bridge3.ip, cfg.bridge3.mask); | ||
159 | if (rv == 0) | ||
160 | return; | ||
161 | } | ||
162 | |||
163 | fprintf(stderr, "Error: default gateway %d.%d.%d.%d is not in the range of any network\n", PRINT_IP(defaultgw)); | ||
164 | exit(1); | ||
165 | } | ||
166 | |||
167 | void net_check_cfg(void) { | ||
168 | int net_configured = 0; | ||
169 | if (cfg.bridge0.configured) | ||
170 | net_configured++; | ||
171 | if (cfg.bridge1.configured) | ||
172 | net_configured++; | ||
173 | if (cfg.bridge2.configured) | ||
174 | net_configured++; | ||
175 | if (cfg.bridge3.configured) | ||
176 | net_configured++; | ||
177 | |||
178 | // --defaultgw requires a network | ||
179 | if (cfg.defaultgw && net_configured == 0) { | ||
180 | fprintf(stderr, "Error: option --defaultgw requires at least one network to be configured\n"); | ||
181 | exit(1); | ||
182 | } | ||
183 | |||
184 | if (net_configured == 0) // nothing to check | ||
185 | return; | ||
186 | |||
187 | // --net=none | ||
188 | if (arg_nonetwork && net_configured) { | ||
189 | fprintf(stderr, "Error: --net and --net=none are mutually exclusive\n"); | ||
190 | exit(1); | ||
191 | } | ||
192 | |||
193 | // check default gateway address or assign one | ||
194 | assert(cfg.bridge0.configured); | ||
195 | if (cfg.defaultgw) | ||
196 | check_default_gw(cfg.defaultgw); | ||
197 | else { | ||
198 | // first network is a regular bridge | ||
199 | if (cfg.bridge0.macvlan == 0) | ||
200 | cfg.defaultgw = cfg.bridge0.ip; | ||
201 | // first network is a mac device | ||
202 | else { | ||
203 | // get the host default gw | ||
204 | uint32_t gw = network_get_defaultgw(); | ||
205 | // check the gateway is network range | ||
206 | if (in_netrange(gw, cfg.bridge0.ip, cfg.bridge0.mask)) | ||
207 | gw = 0; | ||
208 | cfg.defaultgw = gw; | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | |||
213 | |||
214 | |||
215 | void net_dns_print_name(const char *name) { | ||
216 | if (!name || strlen(name) == 0) { | ||
217 | fprintf(stderr, "Error: invalid sandbox name\n"); | ||
218 | exit(1); | ||
219 | } | ||
220 | pid_t pid; | ||
221 | if (name2pid(name, &pid)) { | ||
222 | fprintf(stderr, "Error: cannot find sandbox %s\n", name); | ||
223 | exit(1); | ||
224 | } | ||
225 | |||
226 | net_dns_print(pid); | ||
227 | } | ||
228 | |||
229 | #define MAXBUF 4096 | ||
230 | void net_dns_print(pid_t pid) { | ||
231 | // drop privileges - will not be able to read /etc/resolv.conf for --noroot option | ||
232 | // drop_privs(1); | ||
233 | |||
234 | // if the pid is that of a firejail process, use the pid of the first child process | ||
235 | char *comm = pid_proc_comm(pid); | ||
236 | if (comm) { | ||
237 | // remove \n | ||
238 | char *ptr = strchr(comm, '\n'); | ||
239 | if (ptr) | ||
240 | *ptr = '\0'; | ||
241 | if (strcmp(comm, "firejail") == 0) { | ||
242 | pid_t child; | ||
243 | if (find_child(pid, &child) == 0) { | ||
244 | pid = child; | ||
245 | } | ||
246 | } | ||
247 | free(comm); | ||
248 | } | ||
249 | |||
250 | char *fname; | ||
251 | if (asprintf(&fname, "/proc/%d/root/etc/resolv.conf", pid) == -1) | ||
252 | errExit("asprintf"); | ||
253 | |||
254 | // access /etc/resolv.conf | ||
255 | FILE *fp = fopen(fname, "r"); | ||
256 | if (!fp) { | ||
257 | fprintf(stderr, "Error: cannot access /etc/resolv.conf\n"); | ||
258 | exit(1); | ||
259 | } | ||
260 | |||
261 | char buf[MAXBUF]; | ||
262 | while (fgets(buf, MAXBUF, fp)) | ||
263 | printf("%s", buf); | ||
264 | printf("\n"); | ||
265 | fclose(fp); | ||
266 | free(fname); | ||
267 | exit(0); | ||
268 | } | ||