diff options
Diffstat (limited to 'src')
31 files changed, 1209 insertions, 461 deletions
diff --git a/src/firejail/Makefile.in b/src/firejail/Makefile.in index fce460906..c99b6c30c 100644 --- a/src/firejail/Makefile.in +++ b/src/firejail/Makefile.in | |||
@@ -30,11 +30,11 @@ BINOBJS = $(foreach file, $(OBJS), $file) | |||
30 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | 30 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security |
31 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread | 31 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread |
32 | 32 | ||
33 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h | 33 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/pid.h ../include/seccomp.h ../include/syscall.h |
34 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | 34 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ |
35 | 35 | ||
36 | firejail: $(OBJS) ../lib/libnetlink.o ../lib/common.o | 36 | firejail: $(OBJS) ../lib/libnetlink.o ../lib/common.o |
37 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS) | 37 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS) |
38 | 38 | ||
39 | clean:; rm -f *.o firejail firejail.1 firejail.1.gz | 39 | clean:; rm -f *.o firejail firejail.1 firejail.1.gz |
40 | 40 | ||
diff --git a/src/firejail/appimage_size.c b/src/firejail/appimage_size.c index c8b3d28c5..3f5c3150c 100644 --- a/src/firejail/appimage_size.c +++ b/src/firejail/appimage_size.c | |||
@@ -1,4 +1,23 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2014-2016 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 | /* | ||
2 | Compile with: | 21 | Compile with: |
3 | gcc elfsize.c -o elfsize | 22 | gcc elfsize.c -o elfsize |
4 | Example: | 23 | Example: |
@@ -9,7 +28,6 @@ Size of section headers e_shentsize 64 | |||
9 | Number of section headers e_shnum 29 | 28 | Number of section headers e_shnum 29 |
10 | e_shoff + ( e_shentsize * e_shnum ) = 126584 | 29 | e_shoff + ( e_shentsize * e_shnum ) = 126584 |
11 | */ | 30 | */ |
12 | |||
13 | #include <elf.h> | 31 | #include <elf.h> |
14 | #include <byteswap.h> | 32 | #include <byteswap.h> |
15 | #include <stdio.h> | 33 | #include <stdio.h> |
@@ -23,7 +41,6 @@ e_shoff + ( e_shentsize * e_shnum ) = 126584 | |||
23 | typedef Elf32_Nhdr Elf_Nhdr; | 41 | typedef Elf32_Nhdr Elf_Nhdr; |
24 | 42 | ||
25 | static Elf64_Ehdr ehdr; | 43 | static Elf64_Ehdr ehdr; |
26 | static Elf64_Phdr *phdr; | ||
27 | 44 | ||
28 | #if __BYTE_ORDER == __LITTLE_ENDIAN | 45 | #if __BYTE_ORDER == __LITTLE_ENDIAN |
29 | #define ELFDATANATIVE ELFDATA2LSB | 46 | #define ELFDATANATIVE ELFDATA2LSB |
@@ -57,7 +74,7 @@ static uint64_t file64_to_cpu(uint64_t val) { | |||
57 | // return 0 if error | 74 | // return 0 if error |
58 | static long unsigned int read_elf32(int fd) { | 75 | static long unsigned int read_elf32(int fd) { |
59 | Elf32_Ehdr ehdr32; | 76 | Elf32_Ehdr ehdr32; |
60 | ssize_t ret, i; | 77 | ssize_t ret; |
61 | 78 | ||
62 | ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); | 79 | ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); |
63 | if (ret < 0 || (size_t)ret != sizeof(ehdr)) | 80 | if (ret < 0 || (size_t)ret != sizeof(ehdr)) |
@@ -74,7 +91,7 @@ static long unsigned int read_elf32(int fd) { | |||
74 | // return 0 if error | 91 | // return 0 if error |
75 | static long unsigned int read_elf64(int fd) { | 92 | static long unsigned int read_elf64(int fd) { |
76 | Elf64_Ehdr ehdr64; | 93 | Elf64_Ehdr ehdr64; |
77 | ssize_t ret, i; | 94 | ssize_t ret; |
78 | 95 | ||
79 | ret = pread(fd, &ehdr64, sizeof(ehdr64), 0); | 96 | ret = pread(fd, &ehdr64, sizeof(ehdr64), 0); |
80 | if (ret < 0 || (size_t)ret != sizeof(ehdr)) | 97 | if (ret < 0 || (size_t)ret != sizeof(ehdr)) |
diff --git a/src/firejail/errno.c b/src/firejail/errno.c index c493dfa09..03f10bb14 100644 --- a/src/firejail/errno.c +++ b/src/firejail/errno.c | |||
@@ -206,15 +206,4 @@ char *errno_find_nr(int nr) { | |||
206 | return "unknown"; | 206 | return "unknown"; |
207 | } | 207 | } |
208 | 208 | ||
209 | void errno_print(void) { | ||
210 | EUID_ASSERT(); | ||
211 | |||
212 | int i; | ||
213 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | ||
214 | for (i = 0; i < elems; i++) { | ||
215 | printf("%d\t- %s\n", errnolist[i].nr, errnolist[i].name); | ||
216 | } | ||
217 | printf("\n"); | ||
218 | } | ||
219 | |||
220 | #endif // HAVE_SECCOMP | 209 | #endif // HAVE_SECCOMP |
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 9a9bb1ae7..749656f8b 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -25,6 +25,7 @@ | |||
25 | // debug restricted shell | 25 | // debug restricted shell |
26 | //#define DEBUG_RESTRICTED_SHELL | 26 | //#define DEBUG_RESTRICTED_SHELL |
27 | 27 | ||
28 | |||
28 | // filesystem | 29 | // filesystem |
29 | #define RUN_FIREJAIL_BASEDIR "/run" | 30 | #define RUN_FIREJAIL_BASEDIR "/run" |
30 | #define RUN_FIREJAIL_DIR "/run/firejail" | 31 | #define RUN_FIREJAIL_DIR "/run/firejail" |
@@ -38,6 +39,7 @@ | |||
38 | #define RUN_RO_FILE "/run/firejail/firejail.ro.file" | 39 | #define RUN_RO_FILE "/run/firejail/firejail.ro.file" |
39 | #define RUN_MNT_DIR "/run/firejail/mnt" // a tmpfs is mounted on this directory before any of the files below are created | 40 | #define RUN_MNT_DIR "/run/firejail/mnt" // a tmpfs is mounted on this directory before any of the files below are created |
40 | #define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" | 41 | #define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" |
42 | #define RUN_SECCOMP_PROTOCOL "/run/firejail/mnt/seccomp.protocol" | ||
41 | #define RUN_CGROUP_CFG "/run/firejail/mnt/cgroup" | 43 | #define RUN_CGROUP_CFG "/run/firejail/mnt/cgroup" |
42 | #define RUN_CPU_CFG "/run/firejail/mnt/cpu" | 44 | #define RUN_CPU_CFG "/run/firejail/mnt/cpu" |
43 | #define RUN_GROUPS_CFG "/run/firejail/mnt/groups" | 45 | #define RUN_GROUPS_CFG "/run/firejail/mnt/groups" |
@@ -362,7 +364,6 @@ void net_if_ip6(const char *ifname, const char *addr6); | |||
362 | int net_get_if_addr(const char *bridge, uint32_t *ip, uint32_t *mask, uint8_t mac[6], int *mtu); | 364 | int net_get_if_addr(const char *bridge, uint32_t *ip, uint32_t *mask, uint8_t mac[6], int *mtu); |
363 | int net_add_route(uint32_t dest, uint32_t mask, uint32_t gw); | 365 | int net_add_route(uint32_t dest, uint32_t mask, uint32_t gw); |
364 | void net_ifprint(void); | 366 | void net_ifprint(void); |
365 | void net_bridge_add_interface(const char *bridge, const char *dev); | ||
366 | uint32_t network_get_defaultgw(void); | 367 | uint32_t network_get_defaultgw(void); |
367 | int net_config_mac(const char *ifname, const unsigned char mac[6]); | 368 | int net_config_mac(const char *ifname, const unsigned char mac[6]); |
368 | int net_get_mac(const char *ifname, unsigned char mac[6]); | 369 | int net_get_mac(const char *ifname, unsigned char mac[6]); |
@@ -432,11 +433,6 @@ uint32_t arp_assign(const char *dev, Bridge *br); | |||
432 | // scan interface (--scan option) | 433 | // scan interface (--scan option) |
433 | void arp_scan(const char *dev, uint32_t srcaddr, uint32_t srcmask); | 434 | void arp_scan(const char *dev, uint32_t srcaddr, uint32_t srcmask); |
434 | 435 | ||
435 | // veth.c | ||
436 | int net_create_veth(const char *dev, const char *nsdev, unsigned pid); | ||
437 | int net_create_macvlan(const char *dev, const char *parent, unsigned pid); | ||
438 | int net_move_interface(const char *dev, unsigned pid); | ||
439 | |||
440 | // util.c | 436 | // util.c |
441 | void drop_privs(int nogroups); | 437 | void drop_privs(int nogroups); |
442 | int mkpath_as_root(const char* path); | 438 | int mkpath_as_root(const char* path); |
@@ -519,8 +515,6 @@ void caps_print_filter_name(const char *name); | |||
519 | const char *syscall_find_nr(int nr); | 515 | const char *syscall_find_nr(int nr); |
520 | // return -1 if error, 0 if no error | 516 | // return -1 if error, 0 if no error |
521 | int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg), int arg); | 517 | int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg), int arg); |
522 | // print all available syscallsseccomp | ||
523 | void syscall_print(void); | ||
524 | 518 | ||
525 | // fs_trace.c | 519 | // fs_trace.c |
526 | void fs_trace_preload(void); | 520 | void fs_trace_preload(void); |
@@ -603,7 +597,7 @@ void protocol_list(); | |||
603 | void protocol_print_filter_name(const char *name); | 597 | void protocol_print_filter_name(const char *name); |
604 | void protocol_print_filter(pid_t pid); | 598 | void protocol_print_filter(pid_t pid); |
605 | void protocol_store(const char *prlist); | 599 | void protocol_store(const char *prlist); |
606 | void protocol_filter(void); | 600 | void protocol_filter(const char *fname); |
607 | void protocol_filter_save(void); | 601 | void protocol_filter_save(void); |
608 | void protocol_filter_load(const char *fname); | 602 | void protocol_filter_load(const char *fname); |
609 | 603 | ||
@@ -687,6 +681,19 @@ long unsigned int appimage2_size(const char *fname); | |||
687 | // cmdline.c | 681 | // cmdline.c |
688 | void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index); | 682 | void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index); |
689 | 683 | ||
684 | // sbox.c | ||
685 | // programs | ||
686 | #define PATH_FNET (LIBDIR "/firejail/fnet") | ||
687 | #define PATH_FIREMON (PREFIX "/bin/firemon") | ||
688 | #define PATH_FSECCOMP (LIBDIR "/firejail/fseccomp") | ||
689 | // bitmapped filters for sbox_run | ||
690 | #define SBOX_ROOT 1 | ||
691 | #define SBOX_USER 2 | ||
692 | #define SBOX_CAPS 4 | ||
693 | #define SBOX_SECCOMP 8 | ||
694 | // run sbox | ||
695 | int sbox_run(unsigned filter, int num, ...); | ||
696 | |||
690 | 697 | ||
691 | #endif | 698 | #endif |
692 | 699 | ||
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 6c566bd90..572b08205 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -754,8 +754,8 @@ void fs_basic_fs(void) { | |||
754 | fs_rdonly("/usr"); | 754 | fs_rdonly("/usr"); |
755 | 755 | ||
756 | // update /var directory in order to support multiple sandboxes running on the same root directory | 756 | // update /var directory in order to support multiple sandboxes running on the same root directory |
757 | if (!arg_private_dev) | 757 | // if (!arg_private_dev) |
758 | fs_dev_shm(); | 758 | // fs_dev_shm(); |
759 | fs_var_lock(); | 759 | fs_var_lock(); |
760 | fs_var_tmp(); | 760 | fs_var_tmp(); |
761 | fs_var_log(); | 761 | fs_var_log(); |
@@ -1061,8 +1061,8 @@ void fs_overlayfs(void) { | |||
1061 | errExit("chroot"); | 1061 | errExit("chroot"); |
1062 | 1062 | ||
1063 | // update /var directory in order to support multiple sandboxes running on the same root directory | 1063 | // update /var directory in order to support multiple sandboxes running on the same root directory |
1064 | if (!arg_private_dev) | 1064 | // if (!arg_private_dev) |
1065 | fs_dev_shm(); | 1065 | // fs_dev_shm(); |
1066 | fs_var_lock(); | 1066 | fs_var_lock(); |
1067 | fs_var_tmp(); | 1067 | fs_var_tmp(); |
1068 | fs_var_log(); | 1068 | fs_var_log(); |
@@ -1233,8 +1233,8 @@ void fs_chroot(const char *rootdir) { | |||
1233 | 1233 | ||
1234 | if (checkcfg(CFG_CHROOT_DESKTOP)) { | 1234 | if (checkcfg(CFG_CHROOT_DESKTOP)) { |
1235 | // update /var directory in order to support multiple sandboxes running on the same root directory | 1235 | // update /var directory in order to support multiple sandboxes running on the same root directory |
1236 | if (!arg_private_dev) | 1236 | // if (!arg_private_dev) |
1237 | fs_dev_shm(); | 1237 | // fs_dev_shm(); |
1238 | fs_var_lock(); | 1238 | fs_var_lock(); |
1239 | fs_var_tmp(); | 1239 | fs_var_tmp(); |
1240 | fs_var_log(); | 1240 | fs_var_log(); |
diff --git a/src/firejail/fs_dev.c b/src/firejail/fs_dev.c index aa5f7c28b..0186c6f82 100644 --- a/src/firejail/fs_dev.c +++ b/src/firejail/fs_dev.c | |||
@@ -244,6 +244,7 @@ void fs_private_dev(void){ | |||
244 | } | 244 | } |
245 | 245 | ||
246 | 246 | ||
247 | #if 0 | ||
247 | void fs_dev_shm(void) { | 248 | void fs_dev_shm(void) { |
248 | uid_t uid = getuid(); // set a new shm only if we started as root | 249 | uid_t uid = getuid(); // set a new shm only if we started as root |
249 | if (uid) | 250 | if (uid) |
@@ -282,6 +283,7 @@ void fs_dev_shm(void) { | |||
282 | 283 | ||
283 | } | 284 | } |
284 | } | 285 | } |
286 | #endif | ||
285 | 287 | ||
286 | static void disable_file_or_dir(const char *fname) { | 288 | static void disable_file_or_dir(const char *fname) { |
287 | if (arg_debug) | 289 | if (arg_debug) |
diff --git a/src/firejail/join.c b/src/firejail/join.c index ea44019ca..9b5fba24d 100644 --- a/src/firejail/join.c +++ b/src/firejail/join.c | |||
@@ -296,7 +296,7 @@ void join(pid_t pid, int argc, char **argv, int index) { | |||
296 | if (getuid() != 0) | 296 | if (getuid() != 0) |
297 | protocol_filter_load(RUN_PROTOCOL_CFG); | 297 | protocol_filter_load(RUN_PROTOCOL_CFG); |
298 | if (cfg.protocol) { // not available for uid 0 | 298 | if (cfg.protocol) { // not available for uid 0 |
299 | protocol_filter(); | 299 | protocol_filter(RUN_SECCOMP_PROTOCOL); |
300 | } | 300 | } |
301 | 301 | ||
302 | // set seccomp filter | 302 | // set seccomp filter |
diff --git a/src/firejail/list.c b/src/firejail/list.c deleted file mode 100644 index d093a1f85..000000000 --- a/src/firejail/list.c +++ /dev/null | |||
@@ -1,101 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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/stat.h> | ||
23 | |||
24 | static void set_privileges(void) { | ||
25 | struct stat s; | ||
26 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { | ||
27 | EUID_ROOT(); | ||
28 | |||
29 | // elevate privileges | ||
30 | if (setreuid(0, 0)) | ||
31 | errExit("setreuid"); | ||
32 | if (setregid(0, 0)) | ||
33 | errExit("setregid"); | ||
34 | } | ||
35 | else | ||
36 | drop_privs(1); | ||
37 | } | ||
38 | |||
39 | static char *get_firemon_path(const char *cmd) { | ||
40 | assert(cmd); | ||
41 | |||
42 | // start the argv[0] program in a new sandbox | ||
43 | char *firemon; | ||
44 | if (asprintf(&firemon, "%s/bin/firemon %s", PREFIX, cmd) == -1) | ||
45 | errExit("asprintf"); | ||
46 | |||
47 | return firemon; | ||
48 | } | ||
49 | |||
50 | void top(void) { | ||
51 | EUID_ASSERT(); | ||
52 | drop_privs(1); | ||
53 | char *cmd = get_firemon_path("--top"); | ||
54 | |||
55 | char *arg[4]; | ||
56 | arg[0] = "bash"; | ||
57 | arg[1] = "-c"; | ||
58 | arg[2] = cmd; | ||
59 | arg[3] = NULL; | ||
60 | execvp("/bin/bash", arg); | ||
61 | } | ||
62 | |||
63 | void netstats(void) { | ||
64 | EUID_ASSERT(); | ||
65 | set_privileges(); | ||
66 | char *cmd = get_firemon_path("--netstats"); | ||
67 | |||
68 | char *arg[4]; | ||
69 | arg[0] = "bash"; | ||
70 | arg[1] = "-c"; | ||
71 | arg[2] = cmd; | ||
72 | arg[3] = NULL; | ||
73 | execvp("/bin/bash", arg); | ||
74 | } | ||
75 | |||
76 | void list(void) { | ||
77 | EUID_ASSERT(); | ||
78 | drop_privs(1); | ||
79 | char *cmd = get_firemon_path("--list"); | ||
80 | |||
81 | char *arg[4]; | ||
82 | arg[0] = "bash"; | ||
83 | arg[1] = "-c"; | ||
84 | arg[2] = cmd; | ||
85 | arg[3] = NULL; | ||
86 | execvp("/bin/bash", arg); | ||
87 | } | ||
88 | |||
89 | void tree(void) { | ||
90 | EUID_ASSERT(); | ||
91 | drop_privs(1); | ||
92 | char *cmd = get_firemon_path("--tree"); | ||
93 | |||
94 | char *arg[4]; | ||
95 | arg[0] = "bash"; | ||
96 | arg[1] = "-c"; | ||
97 | arg[2] = cmd; | ||
98 | arg[3] = NULL; | ||
99 | execvp("/bin/bash", arg); | ||
100 | } | ||
101 | |||
diff --git a/src/firejail/main.c b/src/firejail/main.c index b5a97c71e..e210ceb31 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -54,9 +54,9 @@ Config cfg; // configuration | |||
54 | int arg_private = 0; // mount private /home and /tmp directoryu | 54 | int arg_private = 0; // mount private /home and /tmp directoryu |
55 | int arg_private_template = 0; // mount private /home using a template | 55 | int arg_private_template = 0; // mount private /home using a template |
56 | int arg_debug = 0; // print debug messages | 56 | int arg_debug = 0; // print debug messages |
57 | int arg_debug_check_filename; // print debug messages for filename checking | 57 | int arg_debug_check_filename = 0; // print debug messages for filename checking |
58 | int arg_debug_blacklists; // print debug messages for blacklists | 58 | int arg_debug_blacklists = 0; // print debug messages for blacklists |
59 | int arg_debug_whitelists; // print debug messages for whitelists | 59 | int arg_debug_whitelists = 0; // print debug messages for whitelists |
60 | int arg_nonetwork = 0; // --net=none | 60 | int arg_nonetwork = 0; // --net=none |
61 | int arg_command = 0; // -c | 61 | int arg_command = 0; // -c |
62 | int arg_overlay = 0; // overlay option | 62 | int arg_overlay = 0; // overlay option |
@@ -404,8 +404,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
404 | #ifdef HAVE_SECCOMP | 404 | #ifdef HAVE_SECCOMP |
405 | else if (strcmp(argv[i], "--debug-syscalls") == 0) { | 405 | else if (strcmp(argv[i], "--debug-syscalls") == 0) { |
406 | if (checkcfg(CFG_SECCOMP)) { | 406 | if (checkcfg(CFG_SECCOMP)) { |
407 | syscall_print(); | 407 | int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-syscalls"); |
408 | exit(0); | 408 | exit(rv); |
409 | } | 409 | } |
410 | else { | 410 | else { |
411 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | 411 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); |
@@ -414,7 +414,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
414 | } | 414 | } |
415 | else if (strcmp(argv[i], "--debug-errnos") == 0) { | 415 | else if (strcmp(argv[i], "--debug-errnos") == 0) { |
416 | if (checkcfg(CFG_SECCOMP)) { | 416 | if (checkcfg(CFG_SECCOMP)) { |
417 | errno_print(); | 417 | int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-errnos"); |
418 | exit(rv); | ||
418 | } | 419 | } |
419 | else { | 420 | else { |
420 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | 421 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); |
@@ -438,8 +439,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
438 | exit(0); | 439 | exit(0); |
439 | } | 440 | } |
440 | else if (strcmp(argv[i], "--debug-protocols") == 0) { | 441 | else if (strcmp(argv[i], "--debug-protocols") == 0) { |
441 | protocol_list(); | 442 | int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-protocols"); |
442 | exit(0); | 443 | exit(rv); |
443 | } | 444 | } |
444 | else if (strncmp(argv[i], "--protocol.print=", 17) == 0) { | 445 | else if (strncmp(argv[i], "--protocol.print=", 17) == 0) { |
445 | if (checkcfg(CFG_SECCOMP)) { | 446 | if (checkcfg(CFG_SECCOMP)) { |
@@ -498,27 +499,32 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
498 | exit(0); | 499 | exit(0); |
499 | } | 500 | } |
500 | else if (strcmp(argv[i], "--list") == 0) { | 501 | else if (strcmp(argv[i], "--list") == 0) { |
501 | list(); | 502 | int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--list"); |
502 | exit(0); | 503 | exit(rv); |
503 | } | 504 | } |
504 | else if (strcmp(argv[i], "--tree") == 0) { | 505 | else if (strcmp(argv[i], "--tree") == 0) { |
505 | tree(); | 506 | int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--tree"); |
506 | exit(0); | 507 | exit(rv); |
507 | } | 508 | } |
508 | else if (strcmp(argv[i], "--top") == 0) { | 509 | else if (strcmp(argv[i], "--top") == 0) { |
509 | top(); | 510 | int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--top"); |
510 | exit(0); | 511 | exit(rv); |
511 | } | 512 | } |
512 | #ifdef HAVE_NETWORK | 513 | #ifdef HAVE_NETWORK |
513 | else if (strcmp(argv[i], "--netstats") == 0) { | 514 | else if (strcmp(argv[i], "--netstats") == 0) { |
514 | if (checkcfg(CFG_NETWORK)) { | 515 | if (checkcfg(CFG_NETWORK)) { |
515 | netstats(); | 516 | struct stat s; |
517 | int rv; | ||
518 | if (stat("/proc/sys/kernel/grsecurity", &s) == 0) | ||
519 | rv = sbox_run(SBOX_ROOT | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); | ||
520 | else | ||
521 | rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); | ||
522 | exit(rv); | ||
516 | } | 523 | } |
517 | else { | 524 | else { |
518 | fprintf(stderr, "Error: networking features are disabled in Firejail configuration file\n"); | 525 | fprintf(stderr, "Error: networking features are disabled in Firejail configuration file\n"); |
519 | exit(1); | 526 | exit(1); |
520 | } | 527 | } |
521 | exit(0); | ||
522 | } | 528 | } |
523 | #endif | 529 | #endif |
524 | #ifdef HAVE_FILE_TRANSFER | 530 | #ifdef HAVE_FILE_TRANSFER |
@@ -1112,7 +1118,16 @@ int main(int argc, char **argv) { | |||
1112 | #ifdef HAVE_SECCOMP | 1118 | #ifdef HAVE_SECCOMP |
1113 | else if (strncmp(argv[i], "--protocol=", 11) == 0) { | 1119 | else if (strncmp(argv[i], "--protocol=", 11) == 0) { |
1114 | if (checkcfg(CFG_SECCOMP)) { | 1120 | if (checkcfg(CFG_SECCOMP)) { |
1115 | protocol_store(argv[i] + 11); | 1121 | if (cfg.protocol) { |
1122 | if (!arg_quiet) | ||
1123 | fprintf(stderr, "Warning: a protocol list is present, the new list \"%s\" will not be installed\n", argv[i] + 11); | ||
1124 | } | ||
1125 | else { | ||
1126 | // store list | ||
1127 | cfg.protocol = strdup(argv[i] + 11); | ||
1128 | if (!cfg.protocol) | ||
1129 | errExit("strdup"); | ||
1130 | } | ||
1116 | } | 1131 | } |
1117 | else { | 1132 | else { |
1118 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); | 1133 | fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n"); |
@@ -1605,6 +1620,14 @@ int main(int argc, char **argv) { | |||
1605 | return 1; | 1620 | return 1; |
1606 | } | 1621 | } |
1607 | 1622 | ||
1623 | // don't allow "--chroot=/" | ||
1624 | char *rpath = realpath(cfg.chrootdir, NULL); | ||
1625 | if (rpath == NULL || strcmp(rpath, "/") == 0) { | ||
1626 | fprintf(stderr, "Error: invalid chroot directory\n"); | ||
1627 | exit(1); | ||
1628 | } | ||
1629 | free(rpath); | ||
1630 | |||
1608 | // check chroot directory structure | 1631 | // check chroot directory structure |
1609 | if (fs_check_chroot_dir(cfg.chrootdir)) { | 1632 | if (fs_check_chroot_dir(cfg.chrootdir)) { |
1610 | fprintf(stderr, "Error: invalid chroot\n"); | 1633 | fprintf(stderr, "Error: invalid chroot\n"); |
diff --git a/src/firejail/network.c b/src/firejail/network.c index 4473ef099..ac0d86559 100644 --- a/src/firejail/network.c +++ b/src/firejail/network.c | |||
@@ -431,52 +431,6 @@ int net_add_route(uint32_t ip, uint32_t mask, uint32_t gw) { | |||
431 | } | 431 | } |
432 | 432 | ||
433 | 433 | ||
434 | // add a veth device to a bridge | ||
435 | void net_bridge_add_interface(const char *bridge, const char *dev) { | ||
436 | if (strlen(bridge) > IFNAMSIZ) { | ||
437 | fprintf(stderr, "Error: invalid network device name %s\n", bridge); | ||
438 | exit(1); | ||
439 | } | ||
440 | |||
441 | // somehow adding the interface to the bridge resets MTU on bridge device!!! | ||
442 | // workaround: restore MTU on the bridge device | ||
443 | // todo: put a real fix in | ||
444 | int mtu1 = net_get_mtu(bridge); | ||
445 | |||
446 | struct ifreq ifr; | ||
447 | int err; | ||
448 | int ifindex = if_nametoindex(dev); | ||
449 | |||
450 | if (ifindex <= 0) | ||
451 | errExit("if_nametoindex"); | ||
452 | |||
453 | int sock; | ||
454 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) | ||
455 | errExit("socket"); | ||
456 | |||
457 | memset(&ifr, 0, sizeof(ifr)); | ||
458 | strncpy(ifr.ifr_name, bridge, IFNAMSIZ); | ||
459 | #ifdef SIOCBRADDIF | ||
460 | ifr.ifr_ifindex = ifindex; | ||
461 | err = ioctl(sock, SIOCBRADDIF, &ifr); | ||
462 | if (err < 0) | ||
463 | #endif | ||
464 | { | ||
465 | unsigned long args[4] = { BRCTL_ADD_IF, ifindex, 0, 0 }; | ||
466 | |||
467 | ifr.ifr_data = (char *) args; | ||
468 | err = ioctl(sock, SIOCDEVPRIVATE, &ifr); | ||
469 | } | ||
470 | (void) err; | ||
471 | close(sock); | ||
472 | |||
473 | int mtu2 = net_get_mtu(bridge); | ||
474 | if (mtu1 != mtu2) { | ||
475 | if (arg_debug) | ||
476 | printf("Restoring MTU for %s\n", bridge); | ||
477 | net_set_mtu(bridge, mtu1); | ||
478 | } | ||
479 | } | ||
480 | 434 | ||
481 | #define BUFSIZE 1024 | 435 | #define BUFSIZE 1024 |
482 | uint32_t network_get_defaultgw(void) { | 436 | uint32_t network_get_defaultgw(void) { |
diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c index 907b84642..35d6dd214 100644 --- a/src/firejail/network_main.c +++ b/src/firejail/network_main.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <sys/stat.h> | 23 | #include <sys/stat.h> |
24 | #include <unistd.h> | 24 | #include <unistd.h> |
25 | #include <net/if.h> | 25 | #include <net/if.h> |
26 | #include <stdarg.h> | ||
26 | 27 | ||
27 | // configure bridge structure | 28 | // configure bridge structure |
28 | // - extract ip address and mask from the bridge interface | 29 | // - extract ip address and mask from the bridge interface |
@@ -127,13 +128,12 @@ void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child) { | |||
127 | else | 128 | else |
128 | dev = br->veth_name; | 129 | dev = br->veth_name; |
129 | 130 | ||
130 | net_create_veth(dev, ifname, child); | 131 | // net_create_veth(dev, ifname, child); |
131 | 132 | char *cstr; | |
132 | // add interface to the bridge | 133 | if (asprintf(&cstr, "%d", child) == -1) |
133 | net_bridge_add_interface(br->dev, dev); | 134 | errExit("asprintf"); |
134 | 135 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 7, PATH_FNET, "create", "veth", dev, ifname, br->dev, cstr); | |
135 | // bring up the interface | 136 | free(cstr); |
136 | net_if_up(dev); | ||
137 | 137 | ||
138 | char *msg; | 138 | char *msg; |
139 | if (asprintf(&msg, "%d.%d.%d.%d address assigned to sandbox", PRINT_IP(br->ipsandbox)) == -1) | 139 | if (asprintf(&msg, "%d.%d.%d.%d address assigned to sandbox", PRINT_IP(br->ipsandbox)) == -1) |
@@ -290,47 +290,61 @@ void net_dns_print(pid_t pid) { | |||
290 | } | 290 | } |
291 | 291 | ||
292 | void network_main(pid_t child) { | 292 | void network_main(pid_t child) { |
293 | char *cstr; | ||
294 | if (asprintf(&cstr, "%d", child) == -1) | ||
295 | errExit("asprintf"); | ||
296 | |||
293 | // create veth pair or macvlan device | 297 | // create veth pair or macvlan device |
294 | if (cfg.bridge0.configured) { | 298 | if (cfg.bridge0.configured) { |
295 | if (cfg.bridge0.macvlan == 0) { | 299 | if (cfg.bridge0.macvlan == 0) { |
296 | net_configure_veth_pair(&cfg.bridge0, "eth0", child); | 300 | net_configure_veth_pair(&cfg.bridge0, "eth0", child); |
297 | } | 301 | } |
298 | else | 302 | else |
299 | net_create_macvlan(cfg.bridge0.devsandbox, cfg.bridge0.dev, child); | 303 | // net_create_macvlan(cfg.bridge0.devsandbox, cfg.bridge0.dev, child); |
304 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge0.devsandbox, cfg.bridge0.dev, cstr); | ||
300 | } | 305 | } |
301 | 306 | ||
302 | if (cfg.bridge1.configured) { | 307 | if (cfg.bridge1.configured) { |
303 | if (cfg.bridge1.macvlan == 0) | 308 | if (cfg.bridge1.macvlan == 0) |
304 | net_configure_veth_pair(&cfg.bridge1, "eth1", child); | 309 | net_configure_veth_pair(&cfg.bridge1, "eth1", child); |
305 | else | 310 | else |
306 | net_create_macvlan(cfg.bridge1.devsandbox, cfg.bridge1.dev, child); | 311 | // net_create_macvlan(cfg.bridge1.devsandbox, cfg.bridge1.dev, child); |
312 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge1.devsandbox, cfg.bridge1.dev, cstr); | ||
307 | } | 313 | } |
308 | 314 | ||
309 | if (cfg.bridge2.configured) { | 315 | if (cfg.bridge2.configured) { |
310 | if (cfg.bridge2.macvlan == 0) | 316 | if (cfg.bridge2.macvlan == 0) |
311 | net_configure_veth_pair(&cfg.bridge2, "eth2", child); | 317 | net_configure_veth_pair(&cfg.bridge2, "eth2", child); |
312 | else | 318 | else |
313 | net_create_macvlan(cfg.bridge2.devsandbox, cfg.bridge2.dev, child); | 319 | // net_create_macvlan(cfg.bridge2.devsandbox, cfg.bridge2.dev, child); |
320 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge2.devsandbox, cfg.bridge2.dev, cstr); | ||
314 | } | 321 | } |
315 | 322 | ||
316 | if (cfg.bridge3.configured) { | 323 | if (cfg.bridge3.configured) { |
317 | if (cfg.bridge3.macvlan == 0) | 324 | if (cfg.bridge3.macvlan == 0) |
318 | net_configure_veth_pair(&cfg.bridge3, "eth3", child); | 325 | net_configure_veth_pair(&cfg.bridge3, "eth3", child); |
319 | else | 326 | else |
320 | net_create_macvlan(cfg.bridge3.devsandbox, cfg.bridge3.dev, child); | 327 | // net_create_macvlan(cfg.bridge3.devsandbox, cfg.bridge3.dev, child); |
328 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge3.devsandbox, cfg.bridge3.dev, cstr); | ||
321 | } | 329 | } |
322 | 330 | ||
323 | // move interfaces in sandbox | 331 | // move interfaces in sandbox |
324 | if (cfg.interface0.configured) { | 332 | if (cfg.interface0.configured) { |
325 | net_move_interface(cfg.interface0.dev, child); | 333 | // net_move_interface(cfg.interface0.dev, child); |
334 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface0.dev, cstr); | ||
326 | } | 335 | } |
327 | if (cfg.interface1.configured) { | 336 | if (cfg.interface1.configured) { |
328 | net_move_interface(cfg.interface1.dev, child); | 337 | // net_move_interface(cfg.interface1.dev, child); |
338 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface1.dev, cstr); | ||
329 | } | 339 | } |
330 | if (cfg.interface2.configured) { | 340 | if (cfg.interface2.configured) { |
331 | net_move_interface(cfg.interface2.dev, child); | 341 | // net_move_interface(cfg.interface2.dev, child); |
342 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); | ||
332 | } | 343 | } |
333 | if (cfg.interface3.configured) { | 344 | if (cfg.interface3.configured) { |
334 | net_move_interface(cfg.interface3.dev, child); | 345 | // net_move_interface(cfg.interface3.dev, child); |
346 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); | ||
335 | } | 347 | } |
348 | |||
349 | free(cstr); | ||
336 | } | 350 | } |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index e5c35a89d..f7d5e87e6 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -497,8 +497,18 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
497 | 497 | ||
498 | if (strncmp(ptr, "protocol ", 9) == 0) { | 498 | if (strncmp(ptr, "protocol ", 9) == 0) { |
499 | #ifdef HAVE_SECCOMP | 499 | #ifdef HAVE_SECCOMP |
500 | if (checkcfg(CFG_SECCOMP)) | 500 | if (checkcfg(CFG_SECCOMP)) { |
501 | protocol_store(ptr + 9); | 501 | if (cfg.protocol) { |
502 | if (!arg_quiet) | ||
503 | fprintf(stderr, "Warning: a protocol list is present, the new list \"%s\" will not be installed\n", ptr + 9); | ||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | // store list | ||
508 | cfg.protocol = strdup(ptr + 9); | ||
509 | if (!cfg.protocol) | ||
510 | errExit("strdup"); | ||
511 | } | ||
502 | else | 512 | else |
503 | fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); | 513 | fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); |
504 | #endif | 514 | #endif |
diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c index 1ef5bf13d..43f30e30a 100644 --- a/src/firejail/protocol.c +++ b/src/firejail/protocol.c | |||
@@ -18,241 +18,44 @@ | |||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | /* | ||
22 | struct sock_filter filter[] = { | ||
23 | VALIDATE_ARCHITECTURE, | ||
24 | EXAMINE_SYSCALL, | ||
25 | ONLY(SYS_socket), | ||
26 | EXAMINE_ARGUMENT(0), // allow only AF_INET and AF_INET6, drop everything else | ||
27 | WHITELIST(AF_INET), | ||
28 | WHITELIST(AF_INET6), | ||
29 | WHITELIST(AF_PACKET), | ||
30 | RETURN_ERRNO(ENOTSUP) | ||
31 | }; | ||
32 | struct sock_fprog prog = { | ||
33 | .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), | ||
34 | .filter = filter, | ||
35 | }; | ||
36 | |||
37 | |||
38 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
39 | perror("prctl(NO_NEW_PRIVS)"); | ||
40 | return 1; | ||
41 | } | ||
42 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | ||
43 | perror("prctl"); | ||
44 | return 1; | ||
45 | } | ||
46 | */ | ||
47 | |||
48 | #ifdef HAVE_SECCOMP | 21 | #ifdef HAVE_SECCOMP |
49 | #include "firejail.h" | 22 | #include "firejail.h" |
50 | #include "seccomp.h" | 23 | #include "../include/seccomp.h" |
51 | #include <sys/types.h> | ||
52 | #include <sys/socket.h> | ||
53 | |||
54 | static char *protocol[] = { | ||
55 | "unix", | ||
56 | "inet", | ||
57 | "inet6", | ||
58 | "netlink", | ||
59 | "packet", | ||
60 | NULL | ||
61 | }; | ||
62 | |||
63 | static struct sock_filter protocol_filter_command[] = { | ||
64 | WHITELIST(AF_UNIX), | ||
65 | WHITELIST(AF_INET), | ||
66 | WHITELIST(AF_INET6), | ||
67 | WHITELIST(AF_NETLINK), | ||
68 | WHITELIST(AF_PACKET) | ||
69 | }; | ||
70 | // Note: protocol[] and protocol_filter_command are synchronized | ||
71 | |||
72 | // command length | ||
73 | struct sock_filter whitelist[] = { | ||
74 | WHITELIST(AF_UNIX) | ||
75 | }; | ||
76 | unsigned whitelist_len = sizeof(whitelist) / sizeof(struct sock_filter); | ||
77 | |||
78 | 24 | ||
79 | 25 | // install protocol filter | |
80 | static int is_protocol(const char *p) { | 26 | void protocol_filter(const char *fname) { |
81 | int i = 0; | ||
82 | while (protocol[i] != NULL) { | ||
83 | if (strcmp(protocol[i], p) == 0) | ||
84 | return 1; | ||
85 | i++; | ||
86 | } | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static struct sock_filter *find_protocol_domain(const char *p) { | ||
92 | int i = 0; | ||
93 | while (protocol[i] != NULL) { | ||
94 | if (strcmp(protocol[i], p) == 0) | ||
95 | return &protocol_filter_command[i * whitelist_len]; | ||
96 | i++; | ||
97 | } | ||
98 | |||
99 | return NULL; | ||
100 | } | ||
101 | |||
102 | // --debug-protocols | ||
103 | void protocol_list(void) { | ||
104 | EUID_ASSERT(); | ||
105 | |||
106 | #ifndef SYS_socket | 27 | #ifndef SYS_socket |
107 | fprintf(stderr, "Warning: --protocol not supported on this platform\n"); | 28 | if (arg_debug) |
29 | printf("No support for --protocol on this platform\n"); | ||
108 | return; | 30 | return; |
109 | #endif | 31 | #else |
110 | 32 | assert(fname); | |
111 | int i = 0; | ||
112 | while (protocol[i] != NULL) { | ||
113 | printf("%s, ", protocol[i]); | ||
114 | i++; | ||
115 | } | ||
116 | printf("\n"); | ||
117 | } | ||
118 | |||
119 | 33 | ||
120 | // check protocol list and store it in cfg structure | 34 | // check file |
121 | void protocol_store(const char *prlist) { | 35 | struct stat s; |
122 | EUID_ASSERT(); | 36 | if (stat(fname, &s) == -1) { |
123 | assert(prlist); | 37 | fprintf(stderr, "Error: cannot read protocol filter file\n"); |
124 | 38 | exit(1); | |
125 | if (cfg.protocol && !arg_quiet) { | ||
126 | fprintf(stderr, "Warning: a protocol list is present, the new list \"%s\" will not be installed\n", prlist); | ||
127 | return; | ||
128 | } | 39 | } |
129 | 40 | int size = s.st_size; | |
130 | // temporary list | ||
131 | char *tmplist = strdup(prlist); | ||
132 | if (!tmplist) | ||
133 | errExit("strdup"); | ||
134 | |||
135 | // check list | ||
136 | char *token = strtok(tmplist, ","); | ||
137 | if (!token) | ||
138 | goto errout; | ||
139 | |||
140 | while (token) { | ||
141 | if (!is_protocol(token)) | ||
142 | goto errout; | ||
143 | token = strtok(NULL, ","); | ||
144 | } | ||
145 | free(tmplist); | ||
146 | |||
147 | // store list | ||
148 | cfg.protocol = strdup(prlist); | ||
149 | if (!cfg.protocol) | ||
150 | errExit("strdup"); | ||
151 | return; | ||
152 | |||
153 | errout: | ||
154 | fprintf(stderr, "Error: invalid protocol list\n"); | ||
155 | exit(1); | ||
156 | } | ||
157 | 41 | ||
158 | // install protocol filter | 42 | // read filter |
159 | void protocol_filter(void) { | ||
160 | assert(cfg.protocol); | ||
161 | if (arg_debug) | ||
162 | printf("Set protocol filter: %s\n", cfg.protocol); | ||
163 | |||
164 | #ifndef SYS_socket | ||
165 | (void) find_protocol_domain; | ||
166 | fprintf(stderr, "Warning: --protocol not supported on this platform\n"); | ||
167 | return; | ||
168 | #else | ||
169 | // build the filter | ||
170 | struct sock_filter filter[32]; // big enough | 43 | struct sock_filter filter[32]; // big enough |
171 | memset(&filter[0], 0, sizeof(filter)); | 44 | memset(&filter[0], 0, sizeof(filter)); |
172 | uint8_t *ptr = (uint8_t *) &filter[0]; | 45 | int src = open(fname, O_RDONLY); |
173 | 46 | int rd = 0; | |
174 | // header | 47 | while (rd < size) { |
175 | struct sock_filter filter_start[] = { | 48 | int rv = read(src, (unsigned char *) filter + rd, size - rd); |
176 | VALIDATE_ARCHITECTURE, | 49 | if (rv == -1) { |
177 | EXAMINE_SYSCALL, | 50 | fprintf(stderr, "Error: cannot read %s file\n", fname); |
178 | ONLY(SYS_socket), | 51 | exit(1); |
179 | EXAMINE_ARGUMENT(0) | 52 | } |
180 | }; | 53 | rd += rv; |
181 | memcpy(ptr, &filter_start[0], sizeof(filter_start)); | ||
182 | ptr += sizeof(filter_start); | ||
183 | |||
184 | #if 0 | ||
185 | printf("entries %u\n", (unsigned) (sizeof(filter_start) / sizeof(struct sock_filter))); | ||
186 | { | ||
187 | unsigned j; | ||
188 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
189 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
190 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
191 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
192 | printf("%02x, ", (*ptr2) & 0xff); | ||
193 | } | ||
194 | printf("\n"); | ||
195 | } | ||
196 | printf("whitelist_len %u, struct sock_filter len %u\n", whitelist_len, (unsigned) sizeof(struct sock_filter)); | ||
197 | #endif | ||
198 | |||
199 | |||
200 | // parse list and add commands | ||
201 | char *tmplist = strdup(cfg.protocol); | ||
202 | if (!tmplist) | ||
203 | errExit("strdup"); | ||
204 | char *token = strtok(tmplist, ","); | ||
205 | if (!token) | ||
206 | errExit("strtok"); | ||
207 | |||
208 | while (token) { | ||
209 | struct sock_filter *domain = find_protocol_domain(token); | ||
210 | assert(domain); | ||
211 | memcpy(ptr, domain, whitelist_len * sizeof(struct sock_filter)); | ||
212 | ptr += whitelist_len * sizeof(struct sock_filter); | ||
213 | token = strtok(NULL, ","); | ||
214 | |||
215 | #if 0 | ||
216 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
217 | { | ||
218 | unsigned j; | ||
219 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
220 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
221 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
222 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
223 | printf("%02x, ", (*ptr2) & 0xff); | ||
224 | } | ||
225 | printf("\n"); | ||
226 | } | ||
227 | #endif | ||
228 | |||
229 | |||
230 | } | ||
231 | free(tmplist); | ||
232 | |||
233 | // add end of filter | ||
234 | struct sock_filter filter_end[] = { | ||
235 | RETURN_ERRNO(ENOTSUP) | ||
236 | }; | ||
237 | memcpy(ptr, &filter_end[0], sizeof(filter_end)); | ||
238 | ptr += sizeof(filter_end); | ||
239 | |||
240 | #if 0 | ||
241 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
242 | { | ||
243 | unsigned j; | ||
244 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
245 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
246 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
247 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
248 | printf("%02x, ", (*ptr2) & 0xff); | ||
249 | } | 54 | } |
250 | printf("\n"); | 55 | close(src); |
251 | } | ||
252 | #endif | ||
253 | 56 | ||
254 | // install filter | 57 | // install filter |
255 | unsigned short entries = (unsigned short) ((uintptr_t) ptr - (uintptr_t) (filter)) / (unsigned) sizeof(struct sock_filter); | 58 | unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); |
256 | struct sock_fprog prog = { | 59 | struct sock_fprog prog = { |
257 | .len = entries, | 60 | .len = entries, |
258 | .filter = filter, | 61 | .filter = filter, |
@@ -262,7 +65,7 @@ printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (uns | |||
262 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | 65 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); |
263 | return; | 66 | return; |
264 | } | 67 | } |
265 | #endif // SYS_socket | 68 | #endif |
266 | } | 69 | } |
267 | 70 | ||
268 | void protocol_filter_save(void) { | 71 | void protocol_filter_save(void) { |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index f5cca7494..7a63461ef 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -819,8 +819,24 @@ int sandbox(void* sandbox_arg) { | |||
819 | #ifdef HAVE_SECCOMP | 819 | #ifdef HAVE_SECCOMP |
820 | // install protocol filter | 820 | // install protocol filter |
821 | if (cfg.protocol) { | 821 | if (cfg.protocol) { |
822 | protocol_filter(); // install filter | 822 | if (arg_debug) |
823 | protocol_filter_save(); // save filter in PROTOCOL_CFG | 823 | printf("Set protocol filter: %s\n", cfg.protocol); |
824 | // as root, create RUN_SECCOMP_PROTOCOL file | ||
825 | // this is where fseccomp program will store the protocol filter | ||
826 | int dst = open(RUN_SECCOMP_PROTOCOL, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
827 | if (dst == -1) | ||
828 | errExit("open"); | ||
829 | close(dst); | ||
830 | if (chown(RUN_SECCOMP_PROTOCOL, getuid(), getgid()) == -1) | ||
831 | errExit("chown"); | ||
832 | |||
833 | // build the seccomp filter as a regular user | ||
834 | int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 5, | ||
835 | PATH_FSECCOMP, "protocol", "build", cfg.protocol, RUN_SECCOMP_PROTOCOL); | ||
836 | if (rv) | ||
837 | exit(rv); | ||
838 | protocol_filter(RUN_SECCOMP_PROTOCOL); // install filter | ||
839 | protocol_filter_save(); // save filter in RUN_PROTOCOL_CFG | ||
824 | } | 840 | } |
825 | 841 | ||
826 | // if a keep list is available, disregard the drop list | 842 | // if a keep list is available, disregard the drop list |
diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c new file mode 100644 index 000000000..d1225c3bc --- /dev/null +++ b/src/firejail/sbox.c | |||
@@ -0,0 +1,181 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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/stat.h> | ||
23 | #include <unistd.h> | ||
24 | #include <net/if.h> | ||
25 | #include <stdarg.h> | ||
26 | #include <sys/wait.h> | ||
27 | #include "../include/seccomp.h" | ||
28 | |||
29 | static struct sock_filter filter[] = { | ||
30 | VALIDATE_ARCHITECTURE, | ||
31 | EXAMINE_SYSCALL, | ||
32 | |||
33 | #if defined(__x86_64__) | ||
34 | #define X32_SYSCALL_BIT 0x40000000 | ||
35 | // handle X32 ABI | ||
36 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), | ||
37 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), | ||
38 | RETURN_ERRNO(EPERM), | ||
39 | #endif | ||
40 | |||
41 | // syscall list | ||
42 | #ifdef SYS_mount | ||
43 | BLACKLIST(SYS_mount), // mount/unmount filesystems | ||
44 | #endif | ||
45 | #ifdef SYS_umount2 | ||
46 | BLACKLIST(SYS_umount2), | ||
47 | #endif | ||
48 | #ifdef SYS_ptrace | ||
49 | BLACKLIST(SYS_ptrace), // trace processes | ||
50 | #endif | ||
51 | #ifdef SYS_kexec_file_load | ||
52 | BLACKLIST(SYS_kexec_file_load), | ||
53 | #endif | ||
54 | #ifdef SYS_kexec_load | ||
55 | BLACKLIST(SYS_kexec_load), // loading a different kernel | ||
56 | #endif | ||
57 | #ifdef SYS_name_to_handle_at | ||
58 | BLACKLIST(SYS_name_to_handle_at), | ||
59 | #endif | ||
60 | #ifdef SYS_open_by_handle_at | ||
61 | BLACKLIST(SYS_open_by_handle_at), // open by handle | ||
62 | #endif | ||
63 | #ifdef SYS_init_module | ||
64 | BLACKLIST(SYS_init_module), // kernel module handling | ||
65 | #endif | ||
66 | #ifdef SYS_finit_module // introduced in 2013 | ||
67 | BLACKLIST(SYS_finit_module), | ||
68 | #endif | ||
69 | #ifdef SYS_create_module | ||
70 | BLACKLIST(SYS_create_module), | ||
71 | #endif | ||
72 | #ifdef SYS_delete_module | ||
73 | BLACKLIST(SYS_delete_module), | ||
74 | #endif | ||
75 | #ifdef SYS_iopl | ||
76 | BLACKLIST(SYS_iopl), // io permissions | ||
77 | #endif | ||
78 | #ifdef SYS_ioperm | ||
79 | BLACKLIST(SYS_ioperm), | ||
80 | #endif | ||
81 | #ifdef SYS_iopl | ||
82 | BLACKLIST(SYS_iopl), // io permissions | ||
83 | #endif | ||
84 | #ifdef SYS_ioprio_set | ||
85 | BLACKLIST(SYS_ioprio_set), | ||
86 | #endif | ||
87 | #ifdef SYS_ni_syscall // new io permissions call on arm devices | ||
88 | BLACKLIST(SYS_ni_syscall), | ||
89 | #endif | ||
90 | #ifdef SYS_swapon | ||
91 | BLACKLIST(SYS_swapon), // swap on/off | ||
92 | #endif | ||
93 | #ifdef SYS_swapoff | ||
94 | BLACKLIST(SYS_swapoff), | ||
95 | #endif | ||
96 | #ifdef SYS_syslog | ||
97 | BLACKLIST(SYS_syslog), // kernel printk control | ||
98 | #endif | ||
99 | RETURN_ALLOW | ||
100 | }; | ||
101 | |||
102 | static struct sock_fprog prog = { | ||
103 | .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), | ||
104 | .filter = filter, | ||
105 | }; | ||
106 | |||
107 | typedef struct sbox_config { | ||
108 | char *name; | ||
109 | char *path; | ||
110 | unsigned filters; | ||
111 | } SboxConfig; | ||
112 | |||
113 | |||
114 | int sbox_run(unsigned filter, int num, ...) { | ||
115 | EUID_ROOT(); | ||
116 | |||
117 | int i; | ||
118 | va_list valist; | ||
119 | va_start(valist, num); | ||
120 | |||
121 | // build argument list | ||
122 | char *arg[num + 1]; | ||
123 | for (i = 0; i < num; i++) | ||
124 | arg[i] = va_arg(valist, char*); | ||
125 | arg[i] = NULL; | ||
126 | va_end(valist); | ||
127 | |||
128 | //#if 0 | ||
129 | { | ||
130 | int i; | ||
131 | for (i = 0; i <= num; i++) | ||
132 | printf("#%s# ", arg[i]); | ||
133 | printf("\n"); | ||
134 | } | ||
135 | //#endif | ||
136 | pid_t child = fork(); | ||
137 | if (child < 0) | ||
138 | errExit("fork"); | ||
139 | if (child == 0) { | ||
140 | // apply filters | ||
141 | if (filter & SBOX_CAPS) | ||
142 | caps_drop_all(); | ||
143 | |||
144 | if (filter & SBOX_SECCOMP) { | ||
145 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
146 | perror("prctl(NO_NEW_PRIVS)"); | ||
147 | } | ||
148 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | ||
149 | perror("prctl(PR_SET_SECCOMP)"); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | if (filter & SBOX_ROOT) { | ||
154 | // elevate privileges in order to get grsecurity working | ||
155 | if (setreuid(0, 0)) | ||
156 | errExit("setreuid"); | ||
157 | if (setregid(0, 0)) | ||
158 | errExit("setregid"); | ||
159 | } | ||
160 | else if (filter & SBOX_USER) | ||
161 | drop_privs(1); | ||
162 | |||
163 | if (arg[0]) // get rid of scan-build warning | ||
164 | execvp(arg[0], arg); | ||
165 | else | ||
166 | assert(0); | ||
167 | perror("execl"); | ||
168 | _exit(1); | ||
169 | } | ||
170 | |||
171 | int status; | ||
172 | if (waitpid(child, &status, 0) == -1 ) { | ||
173 | errExit("waitpid"); | ||
174 | } | ||
175 | if (WIFEXITED(status) && status != 0) { | ||
176 | fprintf(stderr, "Error: failed to run %s\n", arg[0]); | ||
177 | exit(1); | ||
178 | } | ||
179 | |||
180 | return status; | ||
181 | } | ||
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 549359d94..09862ec20 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c | |||
@@ -20,7 +20,7 @@ | |||
20 | 20 | ||
21 | #ifdef HAVE_SECCOMP | 21 | #ifdef HAVE_SECCOMP |
22 | #include "firejail.h" | 22 | #include "firejail.h" |
23 | #include "seccomp.h" | 23 | #include "../include/seccomp.h" |
24 | 24 | ||
25 | #define SECSIZE 128 // initial filter size | 25 | #define SECSIZE 128 // initial filter size |
26 | static struct sock_filter *sfilter = NULL; | 26 | static struct sock_filter *sfilter = NULL; |
diff --git a/src/firejail/syscall.c b/src/firejail/syscall.c index 985cc8bb8..f405f23c8 100644 --- a/src/firejail/syscall.c +++ b/src/firejail/syscall.c | |||
@@ -31,7 +31,7 @@ static SyscallEntry syslist[] = { | |||
31 | // | 31 | // |
32 | // code generated using tools/extract-syscall | 32 | // code generated using tools/extract-syscall |
33 | // | 33 | // |
34 | #include "syscall.h" | 34 | #include "../include/syscall.h" |
35 | // | 35 | // |
36 | // end of generated code | 36 | // end of generated code |
37 | // | 37 | // |
@@ -102,15 +102,4 @@ int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg) | |||
102 | return 0; | 102 | return 0; |
103 | } | 103 | } |
104 | 104 | ||
105 | void syscall_print(void) { | ||
106 | EUID_ASSERT(); | ||
107 | |||
108 | int i; | ||
109 | int elems = sizeof(syslist) / sizeof(syslist[0]); | ||
110 | for (i = 0; i < elems; i++) { | ||
111 | printf("%d\t- %s\n", syslist[i].nr, syslist[i].name); | ||
112 | } | ||
113 | printf("\n"); | ||
114 | } | ||
115 | |||
116 | #endif // HAVE_SECCOMP | 105 | #endif // HAVE_SECCOMP |
diff --git a/src/firejail/util.c b/src/firejail/util.c index f38b02fd0..4b2e09953 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -171,11 +171,17 @@ void logerr(const char *msg) { | |||
171 | } | 171 | } |
172 | 172 | ||
173 | 173 | ||
174 | // return -1 if error, 0 if no error | 174 | // return -1 if error, 0 if no error; if destname already exists, return error |
175 | int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) { | 175 | int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) { |
176 | assert(srcname); | 176 | assert(srcname); |
177 | assert(destname); | 177 | assert(destname); |
178 | 178 | ||
179 | struct stat s; | ||
180 | if (stat(destname, &s) == 0) { | ||
181 | fprintf(stderr, "Error: file %s already exists\n", destname); | ||
182 | return -1; | ||
183 | } | ||
184 | |||
179 | // open source | 185 | // open source |
180 | int src = open(srcname, O_RDONLY); | 186 | int src = open(srcname, O_RDONLY); |
181 | if (src < 0) { | 187 | if (src < 0) { |
diff --git a/src/fnet/Makefile.in b/src/fnet/Makefile.in new file mode 100644 index 000000000..1bfb4c68d --- /dev/null +++ b/src/fnet/Makefile.in | |||
@@ -0,0 +1,43 @@ | |||
1 | all: fnet | ||
2 | |||
3 | prefix=@prefix@ | ||
4 | exec_prefix=@exec_prefix@ | ||
5 | libdir=@libdir@ | ||
6 | sysconfdir=@sysconfdir@ | ||
7 | |||
8 | VERSION=@PACKAGE_VERSION@ | ||
9 | NAME=@PACKAGE_NAME@ | ||
10 | HAVE_SECCOMP_H=@HAVE_SECCOMP_H@ | ||
11 | HAVE_SECCOMP=@HAVE_SECCOMP@ | ||
12 | HAVE_CHROOT=@HAVE_CHROOT@ | ||
13 | HAVE_BIND=@HAVE_BIND@ | ||
14 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ | ||
15 | HAVE_NETWORK=@HAVE_NETWORK@ | ||
16 | HAVE_USERNS=@HAVE_USERNS@ | ||
17 | HAVE_X11=@HAVE_X11@ | ||
18 | HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ | ||
19 | HAVE_WHITELIST=@HAVE_WHITELIST@ | ||
20 | HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ | ||
21 | HAVE_APPARMOR=@HAVE_APPARMOR@ | ||
22 | HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ | ||
23 | HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@ | ||
24 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
25 | |||
26 | H_FILE_LIST = $(sort $(wildcard *.[h])) | ||
27 | C_FILE_LIST = $(sort $(wildcard *.c)) | ||
28 | OBJS = $(C_FILE_LIST:.c=.o) | ||
29 | BINOBJS = $(foreach file, $(OBJS), $file) | ||
30 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | ||
31 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread | ||
32 | |||
33 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h | ||
34 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | ||
35 | |||
36 | fnet: $(OBJS) ../lib/libnetlink.o ../lib/common.o | ||
37 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS) | ||
38 | |||
39 | clean:; rm -f *.o fnet | ||
40 | |||
41 | distclean: clean | ||
42 | rm -fr Makefile | ||
43 | |||
diff --git a/src/fnet/fnet.h b/src/fnet/fnet.h new file mode 100644 index 000000000..58efbbed5 --- /dev/null +++ b/src/fnet/fnet.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | #ifndef FNET_H | ||
21 | #define FNET_H | ||
22 | |||
23 | #include <stdio.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <string.h> | ||
26 | #include <assert.h> | ||
27 | #include "../include/common.h" | ||
28 | |||
29 | // veth.c | ||
30 | int net_create_veth(const char *dev, const char *nsdev, unsigned pid); | ||
31 | int net_create_macvlan(const char *dev, const char *parent, unsigned pid); | ||
32 | int net_move_interface(const char *dev, unsigned pid); | ||
33 | |||
34 | // interface.c | ||
35 | void net_bridge_add_interface(const char *bridge, const char *dev); | ||
36 | void net_if_up(const char *ifname); | ||
37 | int net_get_mtu(const char *ifname); | ||
38 | void net_set_mtu(const char *ifname, int mtu); | ||
39 | |||
40 | #endif | ||
diff --git a/src/fnet/interface.c b/src/fnet/interface.c new file mode 100644 index 000000000..b1903dd46 --- /dev/null +++ b/src/fnet/interface.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | |||
21 | #include "fnet.h" | ||
22 | #include <arpa/inet.h> | ||
23 | #include <sys/socket.h> | ||
24 | #include <sys/ioctl.h> | ||
25 | #include <netdb.h> | ||
26 | #include <ifaddrs.h> | ||
27 | #include <net/if.h> | ||
28 | #include <net/if_arp.h> | ||
29 | #include <net/route.h> | ||
30 | #include <linux/if_bridge.h> | ||
31 | |||
32 | // add a veth device to a bridge | ||
33 | void net_bridge_add_interface(const char *bridge, const char *dev) { | ||
34 | if (strlen(bridge) > IFNAMSIZ) { | ||
35 | fprintf(stderr, "Error fnet: invalid network device name %s\n", bridge); | ||
36 | exit(1); | ||
37 | } | ||
38 | |||
39 | // somehow adding the interface to the bridge resets MTU on bridge device!!! | ||
40 | // workaround: restore MTU on the bridge device | ||
41 | // todo: put a real fix in | ||
42 | int mtu1 = net_get_mtu(bridge); | ||
43 | |||
44 | struct ifreq ifr; | ||
45 | int err; | ||
46 | int ifindex = if_nametoindex(dev); | ||
47 | |||
48 | if (ifindex <= 0) | ||
49 | errExit("if_nametoindex"); | ||
50 | |||
51 | int sock; | ||
52 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) | ||
53 | errExit("socket"); | ||
54 | |||
55 | memset(&ifr, 0, sizeof(ifr)); | ||
56 | strncpy(ifr.ifr_name, bridge, IFNAMSIZ); | ||
57 | #ifdef SIOCBRADDIF | ||
58 | ifr.ifr_ifindex = ifindex; | ||
59 | err = ioctl(sock, SIOCBRADDIF, &ifr); | ||
60 | if (err < 0) | ||
61 | #endif | ||
62 | { | ||
63 | unsigned long args[4] = { BRCTL_ADD_IF, ifindex, 0, 0 }; | ||
64 | |||
65 | ifr.ifr_data = (char *) args; | ||
66 | err = ioctl(sock, SIOCDEVPRIVATE, &ifr); | ||
67 | } | ||
68 | (void) err; | ||
69 | close(sock); | ||
70 | |||
71 | int mtu2 = net_get_mtu(bridge); | ||
72 | if (mtu1 != mtu2) { | ||
73 | net_set_mtu(bridge, mtu1); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | |||
78 | // bring interface up | ||
79 | void net_if_up(const char *ifname) { | ||
80 | if (strlen(ifname) > IFNAMSIZ) { | ||
81 | fprintf(stderr, "Error fnet: invalid network device name %s\n", ifname); | ||
82 | exit(1); | ||
83 | } | ||
84 | |||
85 | int sock = socket(AF_INET,SOCK_DGRAM,0); | ||
86 | if (sock < 0) | ||
87 | errExit("socket"); | ||
88 | |||
89 | // get the existing interface flags | ||
90 | struct ifreq ifr; | ||
91 | memset(&ifr, 0, sizeof(ifr)); | ||
92 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
93 | ifr.ifr_addr.sa_family = AF_INET; | ||
94 | |||
95 | // read the existing flags | ||
96 | if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { | ||
97 | close(sock); | ||
98 | printf("Error fnet: cannot bring up interface %s\n", ifname); | ||
99 | errExit("ioctl"); | ||
100 | } | ||
101 | |||
102 | ifr.ifr_flags |= IFF_UP; | ||
103 | |||
104 | // set the new flags | ||
105 | if (ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0) { | ||
106 | close(sock); | ||
107 | printf("Error fnet: cannot bring up interface %s\n", ifname); | ||
108 | errExit("ioctl"); | ||
109 | } | ||
110 | |||
111 | // checking | ||
112 | // read the existing flags | ||
113 | if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { | ||
114 | close(sock); | ||
115 | printf("Error fnet: cannot bring up interface %s\n", ifname); | ||
116 | errExit("ioctl"); | ||
117 | } | ||
118 | |||
119 | // wait not more than 500ms for the interface to come up | ||
120 | int cnt = 0; | ||
121 | while (cnt < 50) { | ||
122 | usleep(10000); // sleep 10ms | ||
123 | |||
124 | // read the existing flags | ||
125 | if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { | ||
126 | close(sock); | ||
127 | printf("Error fnet: cannot bring up interface %s\n", ifname); | ||
128 | errExit("ioctl"); | ||
129 | } | ||
130 | if (ifr.ifr_flags & IFF_RUNNING) | ||
131 | break; | ||
132 | cnt++; | ||
133 | } | ||
134 | |||
135 | close(sock); | ||
136 | } | ||
137 | |||
138 | int net_get_mtu(const char *ifname) { | ||
139 | int mtu = 0; | ||
140 | if (strlen(ifname) > IFNAMSIZ) { | ||
141 | fprintf(stderr, "Error fnet: invalid network device name %s\n", ifname); | ||
142 | exit(1); | ||
143 | } | ||
144 | |||
145 | int s; | ||
146 | struct ifreq ifr; | ||
147 | |||
148 | if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) | ||
149 | errExit("socket"); | ||
150 | |||
151 | memset(&ifr, 0, sizeof(ifr)); | ||
152 | ifr.ifr_addr.sa_family = AF_INET; | ||
153 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
154 | if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == 0) | ||
155 | mtu = ifr.ifr_mtu; | ||
156 | close(s); | ||
157 | |||
158 | |||
159 | return mtu; | ||
160 | } | ||
161 | |||
162 | void net_set_mtu(const char *ifname, int mtu) { | ||
163 | if (strlen(ifname) > IFNAMSIZ) { | ||
164 | fprintf(stderr, "Error fnet: invalid network device name %s\n", ifname); | ||
165 | exit(1); | ||
166 | } | ||
167 | |||
168 | int s; | ||
169 | struct ifreq ifr; | ||
170 | |||
171 | if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) | ||
172 | errExit("socket"); | ||
173 | |||
174 | memset(&ifr, 0, sizeof(ifr)); | ||
175 | ifr.ifr_addr.sa_family = AF_INET; | ||
176 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | ||
177 | ifr.ifr_mtu = mtu; | ||
178 | if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) != 0) | ||
179 | fprintf(stderr, "Warning fnet: cannot set mtu for interface %s\n", ifname); | ||
180 | close(s); | ||
181 | } | ||
182 | |||
183 | |||
diff --git a/src/fnet/main.c b/src/fnet/main.c new file mode 100644 index 000000000..f17287cb9 --- /dev/null +++ b/src/fnet/main.c | |||
@@ -0,0 +1,70 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fnet.h" | ||
21 | |||
22 | static void usage(void) { | ||
23 | printf("Usage:\n"); | ||
24 | printf("\tfnet create veth dev1 dev2 bridge child\n"); | ||
25 | printf("\tfnet create macvlan dev parent child\n"); | ||
26 | printf("\tfnet moveif dev proc\n"); | ||
27 | } | ||
28 | |||
29 | int main(int argc, char **argv) { | ||
30 | #if 0 | ||
31 | { | ||
32 | system("cat /proc/self/status"); | ||
33 | int i; | ||
34 | for (i = 0; i < argc; i++) | ||
35 | printf("*%s* ", argv[i]); | ||
36 | printf("\n"); | ||
37 | } | ||
38 | #endif | ||
39 | if (argc < 2) | ||
40 | return 1; | ||
41 | |||
42 | |||
43 | |||
44 | if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { | ||
45 | usage(); | ||
46 | return 0; | ||
47 | } | ||
48 | else if (argc == 7 && strcmp(argv[1], "create") == 0 && strcmp(argv[2], "veth") == 0) { | ||
49 | // create veth pair and move one end in the the namespace | ||
50 | net_create_veth(argv[3], argv[4], atoi(argv[6])); | ||
51 | |||
52 | // connect the ohter veth end to the bridge ... | ||
53 | net_bridge_add_interface(argv[5], argv[3]); | ||
54 | |||
55 | // ... and bring it up | ||
56 | net_if_up(argv[3]); | ||
57 | } | ||
58 | else if (argc == 6 && strcmp(argv[1], "create") == 0 && strcmp(argv[2], "macvlan") == 0) { | ||
59 | net_create_macvlan(argv[3], argv[4], atoi(argv[5])); | ||
60 | } | ||
61 | else if (argc == 4 && strcmp(argv[1], "moveif") == 0) { | ||
62 | net_move_interface(argv[2], atoi(argv[3])); | ||
63 | } | ||
64 | else { | ||
65 | fprintf(stderr, "Error fnet: invalid arguments\n"); | ||
66 | return 1; | ||
67 | } | ||
68 | |||
69 | return 0; | ||
70 | } | ||
diff --git a/src/firejail/veth.c b/src/fnet/veth.c index df3c1d1f9..d06bc9256 100644 --- a/src/firejail/veth.c +++ b/src/fnet/veth.c | |||
@@ -45,7 +45,7 @@ | |||
45 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 45 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
46 | */ | 46 | */ |
47 | 47 | ||
48 | #include "firejail.h" | 48 | #include "fnet.h" |
49 | #include "../include/libnetlink.h" | 49 | #include "../include/libnetlink.h" |
50 | #include <linux/veth.h> | 50 | #include <linux/veth.h> |
51 | #include <net/if.h> | 51 | #include <net/if.h> |
@@ -63,8 +63,6 @@ int net_create_veth(const char *dev, const char *nsdev, unsigned pid) { | |||
63 | int len; | 63 | int len; |
64 | struct iplink_req req; | 64 | struct iplink_req req; |
65 | 65 | ||
66 | if (arg_debug) | ||
67 | printf("create veth %s/%s/%u\n", dev, nsdev, pid); | ||
68 | assert(dev); | 66 | assert(dev); |
69 | assert(nsdev); | 67 | assert(nsdev); |
70 | assert(pid); | 68 | assert(pid); |
@@ -120,8 +118,6 @@ int net_create_veth(const char *dev, const char *nsdev, unsigned pid) { | |||
120 | int net_create_macvlan(const char *dev, const char *parent, unsigned pid) { | 118 | int net_create_macvlan(const char *dev, const char *parent, unsigned pid) { |
121 | int len; | 119 | int len; |
122 | struct iplink_req req; | 120 | struct iplink_req req; |
123 | if (arg_debug) | ||
124 | printf("create macvlan %s, parent %s\n", dev, parent); | ||
125 | assert(dev); | 121 | assert(dev); |
126 | assert(parent); | 122 | assert(parent); |
127 | 123 | ||
@@ -184,8 +180,6 @@ int net_create_macvlan(const char *dev, const char *parent, unsigned pid) { | |||
184 | // when the interface is moved, netlink does not preserve interface configuration | 180 | // when the interface is moved, netlink does not preserve interface configuration |
185 | int net_move_interface(const char *dev, unsigned pid) { | 181 | int net_move_interface(const char *dev, unsigned pid) { |
186 | struct iplink_req req; | 182 | struct iplink_req req; |
187 | if (arg_debug) | ||
188 | printf("move device %s inside the namespace\n", dev); | ||
189 | assert(dev); | 183 | assert(dev); |
190 | 184 | ||
191 | if (rtnl_open(&rth, 0) < 0) { | 185 | if (rtnl_open(&rth, 0) < 0) { |
diff --git a/src/fseccomp/Makefile.in b/src/fseccomp/Makefile.in new file mode 100644 index 000000000..e7edd1b8f --- /dev/null +++ b/src/fseccomp/Makefile.in | |||
@@ -0,0 +1,43 @@ | |||
1 | all: fseccomp | ||
2 | |||
3 | prefix=@prefix@ | ||
4 | exec_prefix=@exec_prefix@ | ||
5 | libdir=@libdir@ | ||
6 | sysconfdir=@sysconfdir@ | ||
7 | |||
8 | VERSION=@PACKAGE_VERSION@ | ||
9 | NAME=@PACKAGE_NAME@ | ||
10 | HAVE_SECCOMP_H=@HAVE_SECCOMP_H@ | ||
11 | HAVE_SECCOMP=@HAVE_SECCOMP@ | ||
12 | HAVE_CHROOT=@HAVE_CHROOT@ | ||
13 | HAVE_BIND=@HAVE_BIND@ | ||
14 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ | ||
15 | HAVE_NETWORK=@HAVE_NETWORK@ | ||
16 | HAVE_USERNS=@HAVE_USERNS@ | ||
17 | HAVE_X11=@HAVE_X11@ | ||
18 | HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ | ||
19 | HAVE_WHITELIST=@HAVE_WHITELIST@ | ||
20 | HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ | ||
21 | HAVE_APPARMOR=@HAVE_APPARMOR@ | ||
22 | HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ | ||
23 | HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@ | ||
24 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
25 | |||
26 | H_FILE_LIST = $(sort $(wildcard *.[h])) | ||
27 | C_FILE_LIST = $(sort $(wildcard *.c)) | ||
28 | OBJS = $(C_FILE_LIST:.c=.o) | ||
29 | BINOBJS = $(foreach file, $(OBJS), $file) | ||
30 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | ||
31 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread | ||
32 | |||
33 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h ../include/syscall.h | ||
34 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | ||
35 | |||
36 | fseccomp: $(OBJS) ../lib/libnetlink.o ../lib/common.o | ||
37 | $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS) | ||
38 | |||
39 | clean:; rm -f *.o fseccomp | ||
40 | |||
41 | distclean: clean | ||
42 | rm -fr Makefile | ||
43 | |||
diff --git a/src/fseccomp/errno.c b/src/fseccomp/errno.c new file mode 100644 index 000000000..625f484bd --- /dev/null +++ b/src/fseccomp/errno.c | |||
@@ -0,0 +1,161 @@ | |||
1 | #include "fseccomp.h" | ||
2 | |||
3 | #include <errno.h> | ||
4 | //#include <attr/xattr.h> | ||
5 | |||
6 | typedef struct { | ||
7 | char *name; | ||
8 | int nr; | ||
9 | } ErrnoEntry; | ||
10 | |||
11 | static ErrnoEntry errnolist[] = { | ||
12 | // | ||
13 | // code generated using tools/extract-errnos | ||
14 | // | ||
15 | {"EPERM", EPERM}, | ||
16 | {"ENOENT", ENOENT}, | ||
17 | {"ESRCH", ESRCH}, | ||
18 | {"EINTR", EINTR}, | ||
19 | {"EIO", EIO}, | ||
20 | {"ENXIO", ENXIO}, | ||
21 | {"E2BIG", E2BIG}, | ||
22 | {"ENOEXEC", ENOEXEC}, | ||
23 | {"EBADF", EBADF}, | ||
24 | {"ECHILD", ECHILD}, | ||
25 | {"EAGAIN", EAGAIN}, | ||
26 | {"ENOMEM", ENOMEM}, | ||
27 | {"EACCES", EACCES}, | ||
28 | {"EFAULT", EFAULT}, | ||
29 | {"ENOTBLK", ENOTBLK}, | ||
30 | {"EBUSY", EBUSY}, | ||
31 | {"EEXIST", EEXIST}, | ||
32 | {"EXDEV", EXDEV}, | ||
33 | {"ENODEV", ENODEV}, | ||
34 | {"ENOTDIR", ENOTDIR}, | ||
35 | {"EISDIR", EISDIR}, | ||
36 | {"EINVAL", EINVAL}, | ||
37 | {"ENFILE", ENFILE}, | ||
38 | {"EMFILE", EMFILE}, | ||
39 | {"ENOTTY", ENOTTY}, | ||
40 | {"ETXTBSY", ETXTBSY}, | ||
41 | {"EFBIG", EFBIG}, | ||
42 | {"ENOSPC", ENOSPC}, | ||
43 | {"ESPIPE", ESPIPE}, | ||
44 | {"EROFS", EROFS}, | ||
45 | {"EMLINK", EMLINK}, | ||
46 | {"EPIPE", EPIPE}, | ||
47 | {"EDOM", EDOM}, | ||
48 | {"ERANGE", ERANGE}, | ||
49 | {"EDEADLK", EDEADLK}, | ||
50 | {"ENAMETOOLONG", ENAMETOOLONG}, | ||
51 | {"ENOLCK", ENOLCK}, | ||
52 | {"ENOSYS", ENOSYS}, | ||
53 | {"ENOTEMPTY", ENOTEMPTY}, | ||
54 | {"ELOOP", ELOOP}, | ||
55 | {"EWOULDBLOCK", EWOULDBLOCK}, | ||
56 | {"ENOMSG", ENOMSG}, | ||
57 | {"EIDRM", EIDRM}, | ||
58 | {"ECHRNG", ECHRNG}, | ||
59 | {"EL2NSYNC", EL2NSYNC}, | ||
60 | {"EL3HLT", EL3HLT}, | ||
61 | {"EL3RST", EL3RST}, | ||
62 | {"ELNRNG", ELNRNG}, | ||
63 | {"EUNATCH", EUNATCH}, | ||
64 | {"ENOCSI", ENOCSI}, | ||
65 | {"EL2HLT", EL2HLT}, | ||
66 | {"EBADE", EBADE}, | ||
67 | {"EBADR", EBADR}, | ||
68 | {"EXFULL", EXFULL}, | ||
69 | {"ENOANO", ENOANO}, | ||
70 | {"EBADRQC", EBADRQC}, | ||
71 | {"EBADSLT", EBADSLT}, | ||
72 | {"EDEADLOCK", EDEADLOCK}, | ||
73 | {"EBFONT", EBFONT}, | ||
74 | {"ENOSTR", ENOSTR}, | ||
75 | {"ENODATA", ENODATA}, | ||
76 | {"ETIME", ETIME}, | ||
77 | {"ENOSR", ENOSR}, | ||
78 | {"ENONET", ENONET}, | ||
79 | {"ENOPKG", ENOPKG}, | ||
80 | {"EREMOTE", EREMOTE}, | ||
81 | {"ENOLINK", ENOLINK}, | ||
82 | {"EADV", EADV}, | ||
83 | {"ESRMNT", ESRMNT}, | ||
84 | {"ECOMM", ECOMM}, | ||
85 | {"EPROTO", EPROTO}, | ||
86 | {"EMULTIHOP", EMULTIHOP}, | ||
87 | {"EDOTDOT", EDOTDOT}, | ||
88 | {"EBADMSG", EBADMSG}, | ||
89 | {"EOVERFLOW", EOVERFLOW}, | ||
90 | {"ENOTUNIQ", ENOTUNIQ}, | ||
91 | {"EBADFD", EBADFD}, | ||
92 | {"EREMCHG", EREMCHG}, | ||
93 | {"ELIBACC", ELIBACC}, | ||
94 | {"ELIBBAD", ELIBBAD}, | ||
95 | {"ELIBSCN", ELIBSCN}, | ||
96 | {"ELIBMAX", ELIBMAX}, | ||
97 | {"ELIBEXEC", ELIBEXEC}, | ||
98 | {"EILSEQ", EILSEQ}, | ||
99 | {"ERESTART", ERESTART}, | ||
100 | {"ESTRPIPE", ESTRPIPE}, | ||
101 | {"EUSERS", EUSERS}, | ||
102 | {"ENOTSOCK", ENOTSOCK}, | ||
103 | {"EDESTADDRREQ", EDESTADDRREQ}, | ||
104 | {"EMSGSIZE", EMSGSIZE}, | ||
105 | {"EPROTOTYPE", EPROTOTYPE}, | ||
106 | {"ENOPROTOOPT", ENOPROTOOPT}, | ||
107 | {"EPROTONOSUPPORT", EPROTONOSUPPORT}, | ||
108 | {"ESOCKTNOSUPPORT", ESOCKTNOSUPPORT}, | ||
109 | {"EOPNOTSUPP", EOPNOTSUPP}, | ||
110 | {"EPFNOSUPPORT", EPFNOSUPPORT}, | ||
111 | {"EAFNOSUPPORT", EAFNOSUPPORT}, | ||
112 | {"EADDRINUSE", EADDRINUSE}, | ||
113 | {"EADDRNOTAVAIL", EADDRNOTAVAIL}, | ||
114 | {"ENETDOWN", ENETDOWN}, | ||
115 | {"ENETUNREACH", ENETUNREACH}, | ||
116 | {"ENETRESET", ENETRESET}, | ||
117 | {"ECONNABORTED", ECONNABORTED}, | ||
118 | {"ECONNRESET", ECONNRESET}, | ||
119 | {"ENOBUFS", ENOBUFS}, | ||
120 | {"EISCONN", EISCONN}, | ||
121 | {"ENOTCONN", ENOTCONN}, | ||
122 | {"ESHUTDOWN", ESHUTDOWN}, | ||
123 | {"ETOOMANYREFS", ETOOMANYREFS}, | ||
124 | {"ETIMEDOUT", ETIMEDOUT}, | ||
125 | {"ECONNREFUSED", ECONNREFUSED}, | ||
126 | {"EHOSTDOWN", EHOSTDOWN}, | ||
127 | {"EHOSTUNREACH", EHOSTUNREACH}, | ||
128 | {"EALREADY", EALREADY}, | ||
129 | {"EINPROGRESS", EINPROGRESS}, | ||
130 | {"ESTALE", ESTALE}, | ||
131 | {"EUCLEAN", EUCLEAN}, | ||
132 | {"ENOTNAM", ENOTNAM}, | ||
133 | {"ENAVAIL", ENAVAIL}, | ||
134 | {"EISNAM", EISNAM}, | ||
135 | {"EREMOTEIO", EREMOTEIO}, | ||
136 | {"EDQUOT", EDQUOT}, | ||
137 | {"ENOMEDIUM", ENOMEDIUM}, | ||
138 | {"EMEDIUMTYPE", EMEDIUMTYPE}, | ||
139 | {"ECANCELED", ECANCELED}, | ||
140 | {"ENOKEY", ENOKEY}, | ||
141 | {"EKEYEXPIRED", EKEYEXPIRED}, | ||
142 | {"EKEYREVOKED", EKEYREVOKED}, | ||
143 | {"EKEYREJECTED", EKEYREJECTED}, | ||
144 | {"EOWNERDEAD", EOWNERDEAD}, | ||
145 | {"ENOTRECOVERABLE", ENOTRECOVERABLE}, | ||
146 | {"ERFKILL", ERFKILL}, | ||
147 | {"EHWPOISON", EHWPOISON}, | ||
148 | {"ENOTSUP", ENOTSUP}, | ||
149 | #ifdef ENOATTR | ||
150 | {"ENOATTR", ENOATTR}, | ||
151 | #endif | ||
152 | }; | ||
153 | |||
154 | void errno_print(void) { | ||
155 | int i; | ||
156 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | ||
157 | for (i = 0; i < elems; i++) { | ||
158 | printf("%d\t- %s\n", errnolist[i].nr, errnolist[i].name); | ||
159 | } | ||
160 | printf("\n"); | ||
161 | } | ||
diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h new file mode 100644 index 000000000..57757ea6c --- /dev/null +++ b/src/fseccomp/fseccomp.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #ifndef FSECCOMP_H | ||
2 | #define FSECCOMP_H | ||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | #include <assert.h> | ||
7 | #include "../include/common.h" | ||
8 | |||
9 | // syscall.c | ||
10 | void syscall_print(void); | ||
11 | |||
12 | // errno.c | ||
13 | void errno_print(void); | ||
14 | |||
15 | // protocol.c | ||
16 | void protocol_print(void); | ||
17 | void protocol_build_filter(const char *prlist, const char *fname); | ||
18 | #endif | ||
diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c new file mode 100644 index 000000000..59d426a78 --- /dev/null +++ b/src/fseccomp/main.c | |||
@@ -0,0 +1,42 @@ | |||
1 | #include "fseccomp.h" | ||
2 | |||
3 | static void usage(void) { | ||
4 | printf("Usage:\n"); | ||
5 | printf("\tfseccomp debug-syscalls\n"); | ||
6 | printf("\tfseccomp debug-errnos\n"); | ||
7 | printf("\tfseccomp debug-protocols\n"); | ||
8 | printf("\tfseccomp protocol build list file\n"); | ||
9 | } | ||
10 | |||
11 | int main(int argc, char **argv) { | ||
12 | //#if 0 | ||
13 | { | ||
14 | //system("cat /proc/self/status"); | ||
15 | int i; | ||
16 | for (i = 0; i < argc; i++) | ||
17 | printf("*%s* ", argv[i]); | ||
18 | printf("\n"); | ||
19 | } | ||
20 | //#endif | ||
21 | if (argc < 2) | ||
22 | return 1; | ||
23 | |||
24 | if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { | ||
25 | usage(); | ||
26 | return 0; | ||
27 | } | ||
28 | else if (argc == 2 && strcmp(argv[1], "debug-syscalls") == 0) | ||
29 | syscall_print(); | ||
30 | else if (argc == 2 && strcmp(argv[1], "debug-errnos") == 0) | ||
31 | errno_print(); | ||
32 | else if (argc == 2 && strcmp(argv[1], "debug-protocols") == 0) | ||
33 | protocol_print(); | ||
34 | else if (argc == 5 && strcmp(argv[1], "protocol") == 0 && strcmp(argv[2], "build") == 0) | ||
35 | protocol_build_filter(argv[3], argv[4]); | ||
36 | else { | ||
37 | fprintf(stderr, "Error fseccomp: invalid arguments\n"); | ||
38 | return 1; | ||
39 | } | ||
40 | |||
41 | return 0; | ||
42 | } \ No newline at end of file | ||
diff --git a/src/fseccomp/protocol.c b/src/fseccomp/protocol.c new file mode 100644 index 000000000..38c5f9d88 --- /dev/null +++ b/src/fseccomp/protocol.c | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | |||
21 | /* | ||
22 | struct sock_filter filter[] = { | ||
23 | VALIDATE_ARCHITECTURE, | ||
24 | EXAMINE_SYSCALL, | ||
25 | ONLY(SYS_socket), | ||
26 | EXAMINE_ARGUMENT(0), // allow only AF_INET and AF_INET6, drop everything else | ||
27 | WHITELIST(AF_INET), | ||
28 | WHITELIST(AF_INET6), | ||
29 | WHITELIST(AF_PACKET), | ||
30 | RETURN_ERRNO(ENOTSUP) | ||
31 | }; | ||
32 | struct sock_fprog prog = { | ||
33 | .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), | ||
34 | .filter = filter, | ||
35 | }; | ||
36 | |||
37 | |||
38 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
39 | perror("prctl(NO_NEW_PRIVS)"); | ||
40 | return 1; | ||
41 | } | ||
42 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | ||
43 | perror("prctl"); | ||
44 | return 1; | ||
45 | } | ||
46 | */ | ||
47 | |||
48 | #include "fseccomp.h" | ||
49 | #include "../include/seccomp.h" | ||
50 | #include <sys/syscall.h> | ||
51 | #include <sys/types.h> | ||
52 | #include <sys/socket.h> | ||
53 | |||
54 | static char *protocol[] = { | ||
55 | "unix", | ||
56 | "inet", | ||
57 | "inet6", | ||
58 | "netlink", | ||
59 | "packet", | ||
60 | NULL | ||
61 | }; | ||
62 | |||
63 | static struct sock_filter protocol_filter_command[] = { | ||
64 | WHITELIST(AF_UNIX), | ||
65 | WHITELIST(AF_INET), | ||
66 | WHITELIST(AF_INET6), | ||
67 | WHITELIST(AF_NETLINK), | ||
68 | WHITELIST(AF_PACKET) | ||
69 | }; | ||
70 | // Note: protocol[] and protocol_filter_command are synchronized | ||
71 | |||
72 | // command length | ||
73 | struct sock_filter whitelist[] = { | ||
74 | WHITELIST(AF_UNIX) | ||
75 | }; | ||
76 | unsigned whitelist_len = sizeof(whitelist) / sizeof(struct sock_filter); | ||
77 | |||
78 | static struct sock_filter *find_protocol_domain(const char *p) { | ||
79 | int i = 0; | ||
80 | while (protocol[i] != NULL) { | ||
81 | if (strcmp(protocol[i], p) == 0) | ||
82 | return &protocol_filter_command[i * whitelist_len]; | ||
83 | i++; | ||
84 | } | ||
85 | |||
86 | return NULL; | ||
87 | } | ||
88 | |||
89 | |||
90 | void protocol_print(void) { | ||
91 | #ifndef SYS_socket | ||
92 | fprintf(stderr, "Warning fseccomp: firejail --protocol not supported on this platform\n"); | ||
93 | return; | ||
94 | #endif | ||
95 | |||
96 | int i = 0; | ||
97 | while (protocol[i] != NULL) { | ||
98 | printf("%s, ", protocol[i]); | ||
99 | i++; | ||
100 | } | ||
101 | printf("\n"); | ||
102 | } | ||
103 | |||
104 | // install protocol filter | ||
105 | void protocol_build_filter(const char *prlist, const char *fname) { | ||
106 | assert(prlist); | ||
107 | assert(fname); | ||
108 | |||
109 | #ifndef SYS_socket | ||
110 | fprintf(stderr, "Warning: --protocol not supported on this platform\n"); | ||
111 | return; | ||
112 | #else | ||
113 | // build the filter | ||
114 | struct sock_filter filter[32]; // big enough | ||
115 | memset(&filter[0], 0, sizeof(filter)); | ||
116 | uint8_t *ptr = (uint8_t *) &filter[0]; | ||
117 | |||
118 | // header | ||
119 | struct sock_filter filter_start[] = { | ||
120 | VALIDATE_ARCHITECTURE, | ||
121 | EXAMINE_SYSCALL, | ||
122 | ONLY(SYS_socket), | ||
123 | EXAMINE_ARGUMENT(0) | ||
124 | }; | ||
125 | memcpy(ptr, &filter_start[0], sizeof(filter_start)); | ||
126 | ptr += sizeof(filter_start); | ||
127 | |||
128 | #if 0 | ||
129 | printf("entries %u\n", (unsigned) (sizeof(filter_start) / sizeof(struct sock_filter))); | ||
130 | { | ||
131 | unsigned j; | ||
132 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
133 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
134 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
135 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
136 | printf("%02x, ", (*ptr2) & 0xff); | ||
137 | } | ||
138 | printf("\n"); | ||
139 | } | ||
140 | printf("whitelist_len %u, struct sock_filter len %u\n", whitelist_len, (unsigned) sizeof(struct sock_filter)); | ||
141 | #endif | ||
142 | |||
143 | |||
144 | // parse list and add commands | ||
145 | char *tmplist = strdup(prlist); | ||
146 | if (!tmplist) | ||
147 | errExit("strdup"); | ||
148 | char *token = strtok(tmplist, ","); | ||
149 | if (!token) | ||
150 | errExit("strtok"); | ||
151 | |||
152 | while (token) { | ||
153 | struct sock_filter *domain = find_protocol_domain(token); | ||
154 | if (domain == NULL) { | ||
155 | fprintf(stderr, "Error fseccomp: %s is not a valid protocol\n", token); | ||
156 | exit(1); | ||
157 | } | ||
158 | memcpy(ptr, domain, whitelist_len * sizeof(struct sock_filter)); | ||
159 | ptr += whitelist_len * sizeof(struct sock_filter); | ||
160 | token = strtok(NULL, ","); | ||
161 | |||
162 | #if 0 | ||
163 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
164 | { | ||
165 | unsigned j; | ||
166 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
167 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
168 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
169 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
170 | printf("%02x, ", (*ptr2) & 0xff); | ||
171 | } | ||
172 | printf("\n"); | ||
173 | } | ||
174 | #endif | ||
175 | |||
176 | |||
177 | } | ||
178 | free(tmplist); | ||
179 | |||
180 | // add end of filter | ||
181 | struct sock_filter filter_end[] = { | ||
182 | RETURN_ERRNO(ENOTSUP) | ||
183 | }; | ||
184 | memcpy(ptr, &filter_end[0], sizeof(filter_end)); | ||
185 | ptr += sizeof(filter_end); | ||
186 | |||
187 | #if 0 | ||
188 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
189 | { | ||
190 | unsigned j; | ||
191 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
192 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
193 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
194 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
195 | printf("%02x, ", (*ptr2) & 0xff); | ||
196 | } | ||
197 | printf("\n"); | ||
198 | } | ||
199 | #endif | ||
200 | // save filter to file | ||
201 | int dst = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
202 | if (dst < 0) { | ||
203 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
204 | exit(1); | ||
205 | } | ||
206 | |||
207 | int size = (int) ((uintptr_t) ptr - (uintptr_t) (filter)); | ||
208 | int written = 0; | ||
209 | while (written < size) { | ||
210 | int rv = write(dst, (unsigned char *) filter + written, size - written); | ||
211 | if (rv == -1) { | ||
212 | fprintf(stderr, "Error fseccomp: cannot write %s file\n", fname); | ||
213 | exit(1); | ||
214 | } | ||
215 | written += rv; | ||
216 | } | ||
217 | close(dst); | ||
218 | #endif // SYS_socket | ||
219 | } | ||
diff --git a/src/fseccomp/syscall.c b/src/fseccomp/syscall.c new file mode 100644 index 000000000..c67d45598 --- /dev/null +++ b/src/fseccomp/syscall.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #include "fseccomp.h" | ||
2 | #include <sys/syscall.h> | ||
3 | |||
4 | typedef struct { | ||
5 | char *name; | ||
6 | int nr; | ||
7 | } SyscallEntry; | ||
8 | |||
9 | static SyscallEntry syslist[] = { | ||
10 | // | ||
11 | // code generated using tools/extract-syscall | ||
12 | // | ||
13 | #include "../include/syscall.h" | ||
14 | // | ||
15 | // end of generated code | ||
16 | // | ||
17 | }; // end of syslist | ||
18 | |||
19 | void syscall_print(void) { | ||
20 | int i; | ||
21 | int elems = sizeof(syslist) / sizeof(syslist[0]); | ||
22 | for (i = 0; i < elems; i++) { | ||
23 | printf("%d\t- %s\n", syslist[i].nr, syslist[i].name); | ||
24 | } | ||
25 | printf("\n"); | ||
26 | } | ||
diff --git a/src/firejail/seccomp.h b/src/include/seccomp.h index 7d646dd9e..7d646dd9e 100644 --- a/src/firejail/seccomp.h +++ b/src/include/seccomp.h | |||
diff --git a/src/firejail/syscall.h b/src/include/syscall.h index 68d4b5736..9a29779c9 100644 --- a/src/firejail/syscall.h +++ b/src/include/syscall.h | |||
@@ -20,7 +20,6 @@ | |||
20 | 20 | ||
21 | // content extracted from /bits/syscall.h file form glibc 2.22 | 21 | // content extracted from /bits/syscall.h file form glibc 2.22 |
22 | // using ../tools/extract_syscall tool | 22 | // using ../tools/extract_syscall tool |
23 | |||
24 | #if !defined __x86_64__ | 23 | #if !defined __x86_64__ |
25 | #ifdef SYS__llseek | 24 | #ifdef SYS__llseek |
26 | #ifdef __NR__llseek | 25 | #ifdef __NR__llseek |