aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/bandwidth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/firejail/bandwidth.c')
-rw-r--r--src/firejail/bandwidth.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/src/firejail/bandwidth.c b/src/firejail/bandwidth.c
new file mode 100644
index 000000000..25dd7a0fb
--- /dev/null
+++ b/src/firejail/bandwidth.c
@@ -0,0 +1,483 @@
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#define _GNU_SOURCE
21#include <stdio.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <net/if.h>
26#include "firejail.h"
27
28//***********************************
29// interface bandwidth linked list
30//***********************************
31typedef struct ifbw_t {
32 struct ifbw_t *next;
33 char *txt;
34} IFBW;
35IFBW *ifbw = NULL;
36
37
38#if 0
39static void ifbw_print(void) {
40 IFBW *ptr = ifbw;
41 while (ptr) {
42 printf("#%s#\n", ptr->txt);
43 ptr = ptr->next;
44 }
45}
46#endif
47
48static void ifbw_add(IFBW *ptr) {
49 assert(ptr);
50
51 if (ifbw != NULL)
52 ptr->next = ifbw;
53 ifbw = ptr;
54}
55
56
57IFBW *ifbw_find(const char *dev) {
58 assert(dev);
59 int len = strlen(dev);
60 assert(len);
61
62 if (ifbw == NULL)
63 return NULL;
64
65 IFBW *ptr = ifbw;
66 while (ptr) {
67 if (strncmp(ptr->txt, dev, len) == 0 && ptr->txt[len] == ':')
68 return ptr;
69 ptr = ptr->next;
70 }
71
72 return NULL;
73}
74
75void ifbw_remove(IFBW *r) {
76 if (ifbw == NULL)
77 return;
78
79 // remove the first element
80 if (ifbw == r) {
81 ifbw = ifbw->next;
82 return;
83 }
84
85 // walk the list
86 IFBW *ptr = ifbw->next;
87 IFBW *prev = ifbw;
88 while (ptr) {
89 if (ptr == r) {
90 prev->next = ptr->next;
91 return;
92 }
93
94 prev = ptr;
95 ptr = ptr->next;
96 }
97
98 return;
99}
100
101int fibw_count(viod) {
102 int rv = 0;
103 IFBW *ptr = ifbw;
104
105 while (ptr) {
106 rv++;
107 ptr = ptr->next;
108 }
109
110 return rv;
111}
112
113
114//***********************************
115// shm file handling
116//***********************************
117void shm_create_firejail_dir(void) {
118 struct stat s;
119 if (stat("/dev/shm/firejail", &s) == -1) {
120 /* coverity[toctou] */
121 if (mkdir("/dev/shm/firejail", 0777) == -1)
122 errExit("mkdir");
123 if (chown("/dev/shm/firejail", 0, 0) == -1)
124 errExit("chown");
125 }
126 else { // check /dev/shm/firejail directory belongs to root end exit if doesn't!
127 if (s.st_uid != 0 || s.st_gid != 0) {
128 fprintf(stderr, "Error: non-root %s directory, exiting...\n", "/dev/shm/firejail");
129 exit(1);
130 }
131 }
132}
133
134static void shm_create_bandwidth_file(pid_t pid) {
135 char *fname;
136 if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1)
137 errExit("asprintf");
138
139 // if the file already exists, do nothing
140 struct stat s;
141 if (stat(fname, &s) == 0) {
142 free(fname);
143 return;
144 }
145
146 // create an empty file and set mod and ownership
147 /* coverity[toctou] */
148 FILE *fp = fopen(fname, "w");
149 if (fp) {
150 fclose(fp);
151
152 /* coverity[toctou] */
153 if (chmod(fname, 0644) == -1)
154 errExit("chmod");
155 /* coverity[toctou] */
156 if (chown(fname, 0, 0) == -1)
157 errExit("chown");
158 }
159 else {
160 fprintf(stderr, "Error: cannot create bandwidth file in /dev/shm/firejail directory\n");
161 exit(1);
162 }
163
164 free(fname);
165}
166
167// delete shm bandwidth file
168void bandwidth_shm_del_file(pid_t pid) {
169 char *fname;
170 if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1)
171 errExit("asprintf");
172 unlink(fname);
173 free(fname);
174}
175
176void network_shm_del_file(pid_t pid) {
177 char *fname;
178 if (asprintf(&fname, "/dev/shm/firejail/%d-netmap", (int) pid) == -1)
179 errExit("asprintf");
180 unlink(fname);
181 free(fname);
182}
183
184void network_shm_set_file(pid_t pid) {
185 char *fname;
186 if (asprintf(&fname, "/dev/shm/firejail/%d-netmap", (int) pid) == -1)
187 errExit("asprintf");
188
189 // create an empty file and set mod and ownership
190 FILE *fp = fopen(fname, "w");
191 if (fp) {
192 if (cfg.bridge0.configured)
193 fprintf(fp, "%s:%s\n", cfg.bridge0.dev, cfg.bridge0.devsandbox);
194 if (cfg.bridge1.configured)
195 fprintf(fp, "%s:%s\n", cfg.bridge1.dev, cfg.bridge1.devsandbox);
196 if (cfg.bridge2.configured)
197 fprintf(fp, "%s:%s\n", cfg.bridge2.dev, cfg.bridge2.devsandbox);
198 if (cfg.bridge3.configured)
199 fprintf(fp, "%s:%s\n", cfg.bridge3.dev, cfg.bridge3.devsandbox);
200 fclose(fp);
201
202 if (chmod(fname, 0644) == -1)
203 errExit("chmod");
204 if (chown(fname, 0, 0) == -1)
205 errExit("chown");
206 }
207 else {
208 fprintf(stderr, "Error: cannot create network map file in /dev/shm/firejail directory\n");
209 exit(1);
210 }
211
212 free(fname);
213}
214
215
216void shm_read_bandwidth_file(pid_t pid) {
217 assert(ifbw == NULL);
218
219 char *fname;
220 if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1)
221 errExit("asprintf");
222
223 FILE *fp = fopen(fname, "r");
224 if (fp) {
225 char buf[1024];
226 while (fgets(buf, 1024,fp)) {
227 // remove '\n'
228 char *ptr = strchr(buf, '\n');
229 if (ptr)
230 *ptr = '\0';
231 if (strlen(buf) == 0)
232 continue;
233
234 // create a new IFBW entry
235 IFBW *ifbw_new = malloc(sizeof(IFBW));
236 if (!ifbw_new)
237 errExit("malloc");
238 memset(ifbw_new, 0, sizeof(IFBW));
239 ifbw_new->txt = strdup(buf);
240 if (!ifbw_new->txt)
241 errExit("strdup");
242
243 // add it to the linked list
244 ifbw_add(ifbw_new);
245 }
246
247 fclose(fp);
248 }
249}
250
251void shm_write_bandwidth_file(pid_t pid) {
252 if (ifbw == NULL)
253 return; // nothing to do
254
255 char *fname;
256 if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1)
257 errExit("asprintf");
258
259 FILE *fp = fopen(fname, "w");
260 if (fp) {
261 IFBW *ptr = ifbw;
262 while (ptr) {
263 fprintf(fp, "%s\n", ptr->txt);
264 ptr = ptr->next;
265 }
266 fclose(fp);
267 }
268 else {
269 fprintf(stderr, "Error: cannot write bandwidht file %s\n", fname);
270 exit(1);
271 }
272}
273
274//***********************************
275// add or remove interfaces
276//***********************************
277
278// remove interface from shm file
279void bandwidth_shm_remove(pid_t pid, const char *dev) {
280 // create bandwidth directory & file in case they are not in the filesystem yet
281 shm_create_firejail_dir();
282 shm_create_bandwidth_file(pid);
283
284 // read bandwidth file
285 shm_read_bandwidth_file(pid);
286
287 // find the element and remove it
288 IFBW *elem = ifbw_find(dev);
289 if (elem) {
290 ifbw_remove(elem);
291 shm_write_bandwidth_file(pid) ;
292 }
293
294 // remove the file if there are no entries in the list
295 if (ifbw == NULL) {
296 bandwidth_shm_del_file(pid);
297 }
298}
299
300// add interface to shm file
301void bandwidth_shm_set(pid_t pid, const char *dev, int down, int up) {
302 // create bandwidth directory & file in case they are not in the filesystem yet
303 shm_create_firejail_dir();
304 shm_create_bandwidth_file(pid);
305
306 // create the new text entry
307 char *txt;
308 if (asprintf(&txt, "%s: RX %dKB/s, TX %dKB/s", dev, down, up) == -1)
309 errExit("asprintf");
310
311 // read bandwidth file
312 shm_read_bandwidth_file(pid);
313
314 // look for an existing entry and replace the text
315 IFBW *ptr = ifbw_find(dev);
316 if (ptr) {
317 assert(ptr->txt);
318 free(ptr->txt);
319 ptr->txt = txt;
320 }
321 // ... or add a new entry
322 else {
323 IFBW *ifbw_new = malloc(sizeof(IFBW));
324 if (!ifbw_new)
325 errExit("malloc");
326 memset(ifbw_new, 0, sizeof(IFBW));
327 ifbw_new->txt = txt;
328
329 // add it to the linked list
330 ifbw_add(ifbw_new);
331 }
332 shm_write_bandwidth_file(pid) ;
333}
334
335
336//***********************************
337// command execution
338//***********************************
339void bandwidth_name(const char *name, const char *command, const char *dev, int down, int up) {
340 if (!name || strlen(name) == 0) {
341 fprintf(stderr, "Error: invalid sandbox name\n");
342 exit(1);
343 }
344 pid_t pid;
345 if (name2pid(name, &pid)) {
346 fprintf(stderr, "Error: cannot find sandbox %s\n", name);
347 exit(1);
348 }
349
350 bandwidth_pid(pid, command, dev, down, up);
351}
352
353void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, int up) {
354 //************************
355 // verify sandbox
356 //************************
357 char *comm = pid_proc_comm(pid);
358 if (!comm) {
359 fprintf(stderr, "Error: cannot find sandbox\n");
360 exit(1);
361 }
362
363 // remove \n and check for firejail sandbox
364 char *ptr = strchr(comm, '\n');
365 if (ptr)
366 *ptr = '\0';
367 if (strcmp(comm, "firejail") != 0) {
368 fprintf(stderr, "Error: cannot find sandbox\n");
369 exit(1);
370 }
371 free(comm);
372
373 // check network namespace
374 char *cmd = pid_proc_cmdline(pid);
375 if (!cmd || strstr(cmd, "--net") == NULL) {
376 fprintf(stderr, "Error: the sandbox doesn't use a new network namespace\n");
377 exit(1);
378 }
379 free(cmd);
380
381
382 //************************
383 // join the network namespace
384 //************************
385 pid_t child;
386 if (find_child(pid, &child) == -1) {
387 fprintf(stderr, "Error: cannot join the network namespace\n");
388 exit(1);
389 }
390 if (join_namespace(child, "net")) {
391 fprintf(stderr, "Error: cannot join the network namespace\n");
392 exit(1);
393 }
394
395 // set shm file
396 if (strcmp(command, "set") == 0)
397 bandwidth_shm_set(pid, dev, down, up);
398 else if (strcmp(command, "clear") == 0)
399 bandwidth_shm_remove(pid, dev);
400
401 //************************
402 // build command
403 //************************
404 char *devname = NULL;
405 if (dev) {
406 // read shm network map file
407 char *fname;
408 if (asprintf(&fname, "/dev/shm/firejail/%d-netmap", (int) pid) == -1)
409 errExit("asprintf");
410 FILE *fp = fopen(fname, "r");
411 if (!fp) {
412 fprintf(stderr, "Error: cannot read netowk map filel %s\n", fname);
413 exit(1);
414 }
415
416 char buf[1024];
417 int len = strlen(dev);
418 while (fgets(buf, 1024, fp)) {
419 // remove '\n'
420 char *ptr = strchr(buf, '\n');
421 if (ptr)
422 *ptr = '\0';
423 if (*buf == '\0')
424 break;
425
426 if (strncmp(buf, dev, len) == 0 && buf[len] == ':') {
427 devname = strdup(buf + len + 1);
428 if (!devname)
429 errExit("strdup");
430 // check device in namespace
431 if (if_nametoindex(devname) == 0) {
432 fprintf(stderr, "Error: cannot find network device %s\n", devname);
433 exit(1);
434 }
435 break;
436 }
437 }
438 free(fname);
439 fclose(fp);
440 }
441
442 // build fshaper.sh command
443 cmd = NULL;
444 if (devname) {
445 if (strcmp(command, "set") == 0) {
446 if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s %s %d %d",
447 PREFIX, command, devname, down, up) == -1)
448 errExit("asprintf");
449 }
450 else {
451 if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s %s",
452 PREFIX, command, devname) == -1)
453 errExit("asprintf");
454 }
455 }
456 else {
457 if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s", PREFIX, command) == -1)
458 errExit("asprintf");
459 }
460 assert(cmd);
461
462 // wipe out environment variables
463 environ = NULL;
464
465 //************************
466 // build command
467 //************************
468 // elevate privileges
469 if (setreuid(0, 0))
470 errExit("setreuid");
471 if (setregid(0, 0))
472 errExit("setregid");
473
474 char *arg[4];
475 arg[0] = "/bin/bash";
476 arg[1] = "-c";
477 arg[2] = cmd;
478 arg[3] = NULL;
479 execvp("/bin/bash", arg);
480
481 // it will never get here
482 exit(0);
483}