aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.in5
-rwxr-xr-xconfigure3
-rw-r--r--configure.ac2
-rw-r--r--src/firejail/firejail.h2
-rw-r--r--src/firejail/main.c42
-rw-r--r--src/firejail/netfilter.c85
-rw-r--r--src/fnettrace/Makefile.in17
-rw-r--r--src/fnettrace/fnettrace.h64
-rw-r--r--src/fnettrace/main.c433
9 files changed, 629 insertions, 24 deletions
diff --git a/Makefile.in b/Makefile.in
index 31c2442ed..4422cf8a9 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -27,12 +27,13 @@ COMPLETIONDIRS = src/zsh_completion src/bash_completion
27all: all_items mydirs $(MAN_TARGET) filters 27all: all_items mydirs $(MAN_TARGET) filters
28APPS = src/firecfg/firecfg src/firejail/firejail src/firemon/firemon src/profstats/profstats src/jailcheck/jailcheck 28APPS = src/firecfg/firecfg src/firejail/firejail src/firemon/firemon src/profstats/profstats src/jailcheck/jailcheck
29SBOX_APPS = src/fbuilder/fbuilder src/ftee/ftee src/fids/fids 29SBOX_APPS = src/fbuilder/fbuilder src/ftee/ftee src/fids/fids
30SBOX_APPS_NON_DUMPABLE = src/fcopy/fcopy src/fldd/fldd src/fnet/fnet src/fnetfilter/fnetfilter src/profstats/profstats 30SBOX_APPS_NON_DUMPABLE = src/fcopy/fcopy src/fldd/fldd src/fnet/fnet src/fnetfilter/fnetfilter
31SBOX_APPS_NON_DUMPABLE += src/fsec-optimize/fsec-optimize src/fsec-print/fsec-print src/fseccomp/fseccomp
32SBOX_APPS_NON_DUMPABLE += src/fnettrace/fnettrace
31MYDIRS = src/lib $(MAN_SRC) $(COMPLETIONDIRS) 33MYDIRS = src/lib $(MAN_SRC) $(COMPLETIONDIRS)
32MYLIBS = src/libpostexecseccomp/libpostexecseccomp.so src/libtrace/libtrace.so src/libtracelog/libtracelog.so 34MYLIBS = src/libpostexecseccomp/libpostexecseccomp.so src/libtrace/libtrace.so src/libtracelog/libtracelog.so
33COMPLETIONS = src/zsh_completion/_firejail src/bash_completion/firejail.bash_completion 35COMPLETIONS = src/zsh_completion/_firejail src/bash_completion/firejail.bash_completion
34MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5 firejail-users.5 jailcheck.1 36MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5 firejail-users.5 jailcheck.1
35SBOX_APPS_NON_DUMPABLE += src/fsec-optimize/fsec-optimize src/fsec-print/fsec-print src/fseccomp/fseccomp
36SECCOMP_FILTERS = seccomp seccomp.debug seccomp.32 seccomp.block_secondary seccomp.mdwx seccomp.mdwx.32 37SECCOMP_FILTERS = seccomp seccomp.debug seccomp.32 seccomp.block_secondary seccomp.mdwx seccomp.mdwx.32
37ALL_ITEMS = $(APPS) $(SBOX_APPS) $(SBOX_APPS_NON_DUMPABLE) $(MYLIBS) 38ALL_ITEMS = $(APPS) $(SBOX_APPS) $(SBOX_APPS_NON_DUMPABLE) $(MYLIBS)
38 39
diff --git a/configure b/configure
index da886a541..a8c9a1d96 100755
--- a/configure
+++ b/configure
@@ -4271,7 +4271,7 @@ fi
4271 4271
4272ac_config_files="$ac_config_files mkdeb.sh" 4272ac_config_files="$ac_config_files mkdeb.sh"
4273 4273
4274ac_config_files="$ac_config_files Makefile src/common.mk src/lib/Makefile src/fcopy/Makefile src/fnet/Makefile src/firejail/Makefile src/fnetfilter/Makefile src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/fbuilder/Makefile src/fsec-print/Makefile src/ftee/Makefile src/fseccomp/Makefile src/fldd/Makefile src/libpostexecseccomp/Makefile src/fsec-optimize/Makefile src/profstats/Makefile src/man/Makefile src/zsh_completion/Makefile src/bash_completion/Makefile test/Makefile src/jailcheck/Makefile src/fids/Makefile" 4274ac_config_files="$ac_config_files Makefile src/common.mk src/lib/Makefile src/fcopy/Makefile src/fnet/Makefile src/firejail/Makefile src/fnetfilter/Makefile src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/fbuilder/Makefile src/fsec-print/Makefile src/ftee/Makefile src/fseccomp/Makefile src/fldd/Makefile src/libpostexecseccomp/Makefile src/fsec-optimize/Makefile src/profstats/Makefile src/man/Makefile src/zsh_completion/Makefile src/bash_completion/Makefile test/Makefile src/jailcheck/Makefile src/fids/Makefile src/fnettrace/Makefile"
4275 4275
4276cat >confcache <<\_ACEOF 4276cat >confcache <<\_ACEOF
4277# This file is a shell script that caches the results of configure 4277# This file is a shell script that caches the results of configure
@@ -5006,6 +5006,7 @@ do
5006 "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; 5006 "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
5007 "src/jailcheck/Makefile") CONFIG_FILES="$CONFIG_FILES src/jailcheck/Makefile" ;; 5007 "src/jailcheck/Makefile") CONFIG_FILES="$CONFIG_FILES src/jailcheck/Makefile" ;;
5008 "src/fids/Makefile") CONFIG_FILES="$CONFIG_FILES src/fids/Makefile" ;; 5008 "src/fids/Makefile") CONFIG_FILES="$CONFIG_FILES src/fids/Makefile" ;;
5009 "src/fnettrace/Makefile") CONFIG_FILES="$CONFIG_FILES src/fnettrace/Makefile" ;;
5009 5010
5010 *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; 5011 *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
5011 esac 5012 esac
diff --git a/configure.ac b/configure.ac
index bf501506d..232d49e1e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -272,7 +272,7 @@ AC_CONFIG_FILES([Makefile src/common.mk src/lib/Makefile src/fcopy/Makefile src/
272src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/fbuilder/Makefile src/fsec-print/Makefile \ 272src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/fbuilder/Makefile src/fsec-print/Makefile \
273src/ftee/Makefile src/fseccomp/Makefile src/fldd/Makefile src/libpostexecseccomp/Makefile src/fsec-optimize/Makefile \ 273src/ftee/Makefile src/fseccomp/Makefile src/fldd/Makefile src/libpostexecseccomp/Makefile src/fsec-optimize/Makefile \
274src/profstats/Makefile src/man/Makefile src/zsh_completion/Makefile src/bash_completion/Makefile test/Makefile \ 274src/profstats/Makefile src/man/Makefile src/zsh_completion/Makefile src/bash_completion/Makefile test/Makefile \
275src/jailcheck/Makefile src/fids/Makefile]) 275src/jailcheck/Makefile src/fids/Makefile src/fnettrace/Makefile])
276AC_OUTPUT 276AC_OUTPUT
277 277
278cat <<EOF 278cat <<EOF
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index c04a644f4..bc4cfe3fc 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -658,6 +658,8 @@ void set_cgroup(const char *fname, pid_t pid);
658void check_output(int argc, char **argv); 658void check_output(int argc, char **argv);
659 659
660// netfilter.c 660// netfilter.c
661void netfilter_netlock(pid_t pid);
662void netfilter_trace(pid_t pid);
661void check_netfilter_file(const char *fname); 663void check_netfilter_file(const char *fname);
662void netfilter(const char *fname); 664void netfilter(const char *fname);
663void netfilter6(const char *fname); 665void netfilter6(const char *fname);
diff --git a/src/firejail/main.c b/src/firejail/main.c
index e49280ab1..3b12f7ca1 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -408,6 +408,10 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
408 } 408 }
409#endif 409#endif
410#ifdef HAVE_NETWORK 410#ifdef HAVE_NETWORK
411 else if (strncmp(argv[i], "--nettrace=", 11) == 0) {
412 pid_t pid = require_pid(argv[i] + 11);
413 netfilter_trace(pid);
414 }
411 else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { 415 else if (strncmp(argv[i], "--bandwidth=", 12) == 0) {
412 if (checkcfg(CFG_NETWORK)) { 416 if (checkcfg(CFG_NETWORK)) {
413 logargs(argc, argv); 417 logargs(argc, argv);
@@ -990,8 +994,10 @@ int main(int argc, char **argv, char **envp) {
990 int option_cgroup = 0; 994 int option_cgroup = 0;
991 int custom_profile = 0; // custom profile loaded 995 int custom_profile = 0; // custom profile loaded
992 int arg_caps_cmdline = 0; // caps requested on command line (used to break out of --chroot) 996 int arg_caps_cmdline = 0; // caps requested on command line (used to break out of --chroot)
997 int arg_netlock = 0;
993 char **ptr; 998 char **ptr;
994 999
1000
995 // sanitize the umask 1001 // sanitize the umask
996 orig_umask = umask(022); 1002 orig_umask = umask(022);
997 1003
@@ -2288,6 +2294,12 @@ int main(int argc, char **argv, char **envp) {
2288 //************************************* 2294 //*************************************
2289 // network 2295 // network
2290 //************************************* 2296 //*************************************
2297 else if (strcmp(argv[i], "--netlock") == 0)
2298 arg_netlock = 1;
2299 else if (strncmp(argv[i], "--netlock=", 10) == 0) {
2300 pid_t pid = require_pid(argv[i] + 10);
2301 netfilter_netlock(pid);
2302 }
2291 else if (strcmp(argv[i], "--net=none") == 0) { 2303 else if (strcmp(argv[i], "--net=none") == 0) {
2292 arg_nonetwork = 1; 2304 arg_nonetwork = 1;
2293 cfg.bridge0.configured = 0; 2305 cfg.bridge0.configured = 0;
@@ -3220,6 +3232,16 @@ int main(int argc, char **argv, char **envp) {
3220 } 3232 }
3221 EUID_USER(); 3233 EUID_USER();
3222 3234
3235 // lock netfilter firewall
3236 if (arg_netlock) {
3237 char *cmd;
3238 if (asprintf(&cmd, "firejail --netlock=%d&", getpid()) == -1)
3239 errExit("asprintf");
3240 int rv = system(cmd);
3241 (void) rv;
3242 free(cmd);
3243 }
3244
3223 int status = 0; 3245 int status = 0;
3224 //***************************** 3246 //*****************************
3225 // following code is signal-safe 3247 // following code is signal-safe
@@ -3237,26 +3259,6 @@ int main(int argc, char **argv, char **envp) {
3237 // end of signal-safe code 3259 // end of signal-safe code
3238 //***************************** 3260 //*****************************
3239 3261
3240#if 0
3241// at this point the sandbox was closed and we are on our way out
3242// it would make sense to move this before waitpid above to free some memory
3243// crash for now as of issue #3662 from dhcp code
3244 // free globals
3245 if (cfg.profile) {
3246 ProfileEntry *prf = cfg.profile;
3247 while (prf != NULL) {
3248 ProfileEntry *next = prf->next;
3249printf("data #%s#\n", prf->data);
3250 if (prf->data)
3251 free(prf->data);
3252printf("link #%s#\n", prf->link);
3253 if (prf->link)
3254 free(prf->link);
3255 free(prf);
3256 prf = next;
3257 }
3258 }
3259#endif
3260 3262
3261 3263
3262 if (WIFEXITED(status)){ 3264 if (WIFEXITED(status)){
diff --git a/src/firejail/netfilter.c b/src/firejail/netfilter.c
index fc79dddec..f412950f2 100644
--- a/src/firejail/netfilter.c
+++ b/src/firejail/netfilter.c
@@ -24,6 +24,91 @@
24#include <sys/wait.h> 24#include <sys/wait.h>
25#include <fcntl.h> 25#include <fcntl.h>
26 26
27void netfilter_netlock(pid_t pid) {
28 EUID_ASSERT();
29
30 // give the sandbox a chance to start up before entering the network namespace
31 sleep(1);
32 enter_network_namespace(pid);
33
34 char *flog;
35 if (asprintf(&flog, "/run/firejail/network/%d-netlock", getpid()) == -1)
36 errExit("asprintf");
37 FILE *fp = fopen(flog, "w");
38 if (!fp)
39 errExit("fopen");
40 fclose(fp);
41
42 // try to find a X terminal
43 char *terminal = NULL;
44 if (access("/usr/bin/lxterminal", X_OK) == 0)
45 terminal = "/usr/bin/lxterminal";
46 else if (access("/usr/bin/xterm", X_OK) == 0)
47 terminal = "/usr/bin/xterm";
48 else if (access("/usr/bin/xfce4-terminal", X_OK) == 0)
49 terminal = "/usr/bin/xfce4-terminal";
50 else if (access("/usr/bin/konsole", X_OK) == 0)
51 terminal = "/usr/bin/konsole";
52// problem: newer gnome-terminal versions don't support -e command line option???
53// else if (access("/usr/bin/gnome-terminal", X_OK) == 0)
54// terminal = "/usr/bin/gnome-terminal";
55
56 if (terminal) {
57 pid_t p = fork();
58 if (p == -1)
59 ; // run without terminal logger
60 else if (p == 0) { // child
61 drop_privs(0);
62
63 char *cmd;
64 if (asprintf(&cmd, "%s -e \"tail -f %s\"", terminal, flog) == -1)
65 errExit("asprintf");
66 int rv = system(cmd);
67 (void) rv;
68 exit(0);
69 }
70 }
71
72 char *cmd;
73 if (asprintf(&cmd, "%s/firejail/fnettrace --netfilter --log=%s", LIBDIR, flog) == -1)
74 errExit("asprintf");
75 free(flog);
76
77 //************************
78 // build command
79 //************************
80 char *arg[4];
81 arg[0] = "/bin/sh";
82 arg[1] = "-c";
83 arg[2] = cmd;
84 arg[3] = NULL;
85 clearenv();
86 sbox_exec_v(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, arg);
87 // it will never get here!!
88}
89
90void netfilter_trace(pid_t pid) {
91 EUID_ASSERT();
92
93 enter_network_namespace(pid);
94 char *cmd;
95 if (asprintf(&cmd, "%s/firejail/fnettrace", LIBDIR) == -1)
96 errExit("asprintf");
97
98 //************************
99 // build command
100 //************************
101 char *arg[4];
102 arg[0] = "/bin/sh";
103 arg[1] = "-c";
104 arg[2] = cmd;
105 arg[3] = NULL;
106
107 clearenv();
108 sbox_exec_v(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, arg);
109 // it will never get here!!
110}
111
27void check_netfilter_file(const char *fname) { 112void check_netfilter_file(const char *fname) {
28 EUID_ASSERT(); 113 EUID_ASSERT();
29 114
diff --git a/src/fnettrace/Makefile.in b/src/fnettrace/Makefile.in
new file mode 100644
index 000000000..755ddcc3a
--- /dev/null
+++ b/src/fnettrace/Makefile.in
@@ -0,0 +1,17 @@
1.PHONY: all
2all: fnettrace
3
4include ../common.mk
5
6%.o : %.c $(H_FILE_LIST)
7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
8
9fnettrace: $(OBJS)
10 $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS)
11
12.PHONY: clean
13clean:; rm -fr *.o fnettrace *.gcov *.gcda *.gcno *.plist
14
15.PHONY: distclean
16distclean: clean
17 rm -fr Makefile
diff --git a/src/fnettrace/fnettrace.h b/src/fnettrace/fnettrace.h
new file mode 100644
index 000000000..9c34e17ca
--- /dev/null
+++ b/src/fnettrace/fnettrace.h
@@ -0,0 +1,64 @@
1#ifndef FNETTRACE_H
2#define FNETTRACE_H
3
4#include "../include/common.h"
5#include <unistd.h>
6#include <sys/stat.h>
7#include <sys/socket.h>
8#include <netinet/in.h>
9#include <time.h>
10#include <stdarg.h>
11
12//#define NETLOCK_INTERVAL 60
13#define NETLOCK_INTERVAL 60
14#define DISPLAY_INTERVAL 3
15
16void logprintf(char* fmt, ...);
17
18static inline void ansi_topleft(int tolog) {
19 char str[] = {0x1b, '[', '1', ';', '1', 'H', '\0'};
20 if (tolog)
21 logprintf("%s", str);
22 else
23 printf("%s", str);
24 fflush(0);
25}
26
27static inline void ansi_clrscr(int tolog) {
28 ansi_topleft(tolog);
29 char str[] = {0x1b, '[', '0', 'J', '\0'};
30 if (tolog)
31 logprintf("%s", str);
32 else
33 printf("%s", str);
34 fflush(0);
35}
36
37static inline void ansi_linestart(int tolog) {
38 char str[] = {0x1b, '[', '0', 'G', '\0'};
39 if (tolog)
40 logprintf("%s", str);
41 else
42 printf("%s", str);
43 fflush(0);
44}
45
46static inline void ansi_clrline(int tolog) {
47 ansi_linestart(tolog);
48 char str[] = {0x1b, '[', '0', 'K', '\0'};
49 if (tolog)
50 logprintf("%s", str);
51 else
52 printf("%s", str);
53 fflush(0);
54}
55
56static inline uint8_t hash(uint32_t ip) {
57 uint8_t *ptr = (uint8_t *) &ip;
58 // simple byte xor
59 return *ptr ^ *(ptr + 1) ^ *(ptr + 2) ^ *(ptr + 3);
60}
61
62
63
64#endif \ No newline at end of file
diff --git a/src/fnettrace/main.c b/src/fnettrace/main.c
new file mode 100644
index 000000000..f036d0c9e
--- /dev/null
+++ b/src/fnettrace/main.c
@@ -0,0 +1,433 @@
1#include "fnettrace.h"
2#define MAX_BUF_SIZE (64 * 1024)
3
4static int arg_netfilter = 0;
5static char *arg_log = NULL;
6
7typedef struct hlist_t {
8 struct hlist_t *next;
9 uint32_t ip_src;
10 uint32_t ip_dst;
11 uint16_t port_src;
12 uint64_t bytes;
13 int instance;
14#define MAX_TTL 20 // 20 * DISPLAY_INTERVAL = 1 minute
15 short ttl;
16 uint8_t protocol;
17} HList;
18
19#define HMAX 256
20HList *htable[HMAX] = {NULL};
21static int htable_empty = 1;
22
23static void hlist_add(uint32_t ip_src, uint32_t ip_dst, uint8_t protocol, uint16_t port_src, uint64_t bytes) {
24 uint8_t h = hash(ip_src);
25 htable_empty = 0;
26
27 // find
28 int instance = 0;
29 HList *ptr = htable[h];
30 while (ptr) {
31 if (ptr->ip_src == ip_src) {
32 instance++;
33 if (ptr->ip_dst == ip_dst && ptr->port_src == port_src && ptr->protocol == protocol) {
34 ptr->bytes += bytes;
35 ptr->ttl = MAX_TTL;
36 return;
37 }
38 }
39 ptr = ptr->next;
40 }
41
42 HList *hnew = malloc(sizeof(HList));
43 hnew->ip_src = ip_src;
44 hnew->ip_dst = ip_dst;
45 hnew->port_src = port_src;
46 hnew->protocol = protocol;
47 hnew->next = NULL;
48 hnew->bytes = bytes;
49 hnew->ttl = MAX_TTL;
50 hnew->instance = instance + 1;
51 if (htable[h] == NULL)
52 htable[h] = hnew;
53 else {
54 hnew->next = htable[h];
55 htable[h] = hnew;
56 }
57
58 ansi_clrline(1);
59 logprintf(" %u.%u.%u.%u\n", PRINT_IP(hnew->ip_src));
60}
61
62// remove entries with a ttl <= 0
63static void hlist_clean_ttl() {
64 if (htable_empty)
65 return;
66
67 int i;
68 for (i = 0; i < HMAX; i++) {
69 HList *ptr = htable[i];
70 HList *parent = NULL;
71 while (ptr) {
72 if (--ptr->ttl <= 0) {
73 HList *tmp = ptr;
74 ptr = ptr->next;
75 if (parent)
76 parent->next = ptr;
77 else
78 htable[i] = ptr;
79 free(tmp);
80 }
81 else {
82 parent = ptr;
83 ptr = ptr->next;
84 }
85 }
86 }
87}
88
89static void hlist_print() {
90 ansi_clrscr(0);
91 if (htable_empty)
92 return;
93 if (arg_netfilter)
94 printf("\n\n");
95 static int clear_cnt = 0;
96
97 int i;
98 int cnt = 0;
99 int cnt_printed = 0;
100 for (i = 0; i < HMAX; i++) {
101 HList *ptr = htable[i];
102 while (ptr) {
103 if (ptr->bytes) {
104 cnt_printed++;
105 char ip_src[30];
106 sprintf(ip_src, "%u.%u.%u.%u:%u", PRINT_IP(ptr->ip_src), ptr->port_src);
107 char ip_dst[30];
108 sprintf(ip_dst, "%u.%u.%u.%u", PRINT_IP(ptr->ip_dst));
109 printf("%-25s => %-25s\t%s:",
110 ip_src,
111 ip_dst,
112 (ptr->protocol == 6)? "TCP": "UDP");
113
114 if (ptr->bytes > (DISPLAY_INTERVAL * 1024 * 2)) // > 2 KB/second
115 printf(" %lu KB/sec\n",
116 ptr->bytes / (DISPLAY_INTERVAL * 1024));
117 else
118 printf(" %lu B/sec\n",
119 ptr->bytes / DISPLAY_INTERVAL);
120 ptr->bytes = 0;
121 }
122
123 ptr = ptr->next;
124 cnt++;
125 }
126 }
127
128 if (cnt_printed < 7) {
129 for (i = 0; i < 7 - cnt_printed; i++)
130 printf("\n");
131 }
132
133 if (!arg_netfilter) {
134 printf("(%d %s in the last one minute)\n", cnt, (cnt == 1)? "stream": "streams");
135 hlist_clean_ttl();
136 }
137}
138
139static void run_trace(void) {
140 logprintf("accumulating traffic for %d seconds...\n", NETLOCK_INTERVAL);
141
142 // trace only rx ipv4 tcp and upd
143 int s1 = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
144 int s2 = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
145 if (s1 < 0 || s2 < 0)
146 errExit("socket");
147
148 unsigned start = time(NULL);
149 unsigned last_print_traces = 0;
150 unsigned last_print_remaining = 0;
151 unsigned char buf[MAX_BUF_SIZE];
152 int progress_cnt = 0;
153 while (1) {
154 unsigned end = time(NULL);
155 if (arg_netfilter && end - start >= NETLOCK_INTERVAL) {
156 ansi_clrline(1);
157 break;
158 }
159 if (end % DISPLAY_INTERVAL == 1 && last_print_traces != end) { // first print after 1 second
160 hlist_print();
161 last_print_traces = end;
162 }
163 if (arg_netfilter && last_print_remaining != end) {
164 ansi_clrline(1);
165 int secs = NETLOCK_INTERVAL - (end - start);
166 logprintf("%d %s remaining ", secs, (secs == 1)? "second": "seconds");
167 last_print_remaining = end;
168 }
169
170 fd_set rfds;
171 FD_ZERO(&rfds);
172 FD_SET(s1, &rfds);
173 FD_SET(s2, &rfds);
174 int maxfd = (s1 > s2) ? s1 : s2;
175 maxfd++;
176 struct timeval tv;
177 tv.tv_sec = 1;
178 tv.tv_usec = 0;
179 int rv = select(maxfd, &rfds, NULL, NULL, &tv);
180 if (rv < 0)
181 errExit("select");
182 else if (rv == 0)
183 continue;
184
185
186
187 int sock = (FD_ISSET(s1, &rfds)) ? s1 : s2;
188
189 unsigned char buf[MAX_BUF_SIZE];
190 unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL);
191 if (bytes >= 20) { // size of IP header
192 // filter out loopback traffic
193 if (buf[12] != 127) {
194 uint32_t ip_src;
195 memcpy(&ip_src, buf + 12, 4);
196 ip_src = ntohl(ip_src);
197
198 uint32_t ip_dst;
199 memcpy(&ip_dst, buf + 16, 4);
200 ip_dst = ntohl(ip_dst);
201
202 uint8_t hlen = (buf[0] & 0x0f) * 4;
203 uint16_t port_src;
204 memcpy(&port_src, buf + hlen, 2);
205 port_src = ntohs(port_src);
206
207 hlist_add(ip_src, ip_dst, buf[9], port_src, (uint64_t) bytes);
208 }
209 }
210 }
211
212 close(s1);
213 close(s2);
214}
215
216static char *filter_start =
217"*filter\n"
218":INPUT DROP [0:0]\n"
219":FORWARD DROP [0:0]\n"
220":OUTPUT DROP [0:0]\n";
221
222// return 1 if error
223static int print_filter(FILE *fp) {
224 if (htable_empty)
225 return 1;
226 fprintf(fp, "%s\n", filter_start);
227 fprintf(fp, "-A INPUT -s 127.0.0.0/8 -j ACCEPT\n");
228 fprintf(fp, "-A OUTPUT -d 127.0.0.0/8 -j ACCEPT\n");
229 fprintf(fp, "\n");
230
231 int i;
232 for (i = 0; i < HMAX; i++) {
233 HList *ptr = htable[i];
234 while (ptr) {
235 if (ptr->instance == 1) {
236 char *protocol = (ptr->protocol == 6)? "tcp": "udp";
237 fprintf(fp, "-A INPUT -s %u.%u.%u.%u -sport %u -p %s -j ACCEPT\n",
238 PRINT_IP(ptr->ip_src),
239 ptr->port_src,
240 protocol);
241 fprintf(fp, "-A OUTPUT -d %u.%u.%u.%u -dport %u -p %s -j ACCEPT\n",
242 PRINT_IP(ptr->ip_src),
243 ptr->port_src,
244 protocol);
245 fprintf(fp, "\n");
246 }
247 ptr = ptr->next;
248 }
249 }
250 fprintf(fp, "COMMIT\n");
251
252 return 0;
253}
254
255static char *flush_rules[] = {
256 "-P INPUT ACCEPT",
257 "-P FORWARD ACCEPT",
258 "-P OUTPUT ACCEPT",
259 "-F",
260 "-X",
261 "-t nat -F",
262 "-t nat -X",
263 "-t mangle -F",
264 "-t mangle -X",
265 "iptables -t raw -F",
266 "-t raw -X",
267 NULL
268};
269
270static void flush_netfilter(void) {
271 // find iptables command
272 struct stat s;
273 char *iptables = NULL;
274 if (stat("/sbin/iptables", &s) == 0)
275 iptables = "/sbin/iptables";
276 else if (stat("/usr/sbin/iptables", &s) == 0)
277 iptables = "/usr/sbin/iptables";
278 if (iptables == NULL) {
279 fprintf(stderr, "Error: iptables command not found, netfilter not configured\n");
280 exit(1);
281 }
282
283 int i = 0;
284 while (flush_rules[i]) {
285 char *cmd;
286 if (asprintf(&cmd, "%s %s", iptables, flush_rules[i]) == -1)
287 errExit("asprintf");
288 int rv = system(cmd);
289 (void) rv;
290 free(cmd);
291 i++;
292 }
293}
294
295static void deploy_netfilter(void) {
296 int rv;
297 char *cmd;
298
299 // create temporary file
300 char fname[] = "/tmp/firejail-XXXXXX";
301 int fd = mkstemp(fname);
302 if (fd == -1) {
303 fprintf(stderr, "Error: cannot create temporary configuration file\n");
304 exit(1);
305 }
306
307 FILE* fp = fdopen(fd, "w");
308 if (!fp) {
309 rv = unlink(fname);
310 (void) rv;
311 fprintf(stderr, "Error: cannot create temporary configuration file\n");
312 exit(1);
313 }
314 print_filter(fp);
315 fclose(fp);
316
317 if (arg_log) {
318 logprintf("\n");
319 logprintf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
320 if (asprintf(&cmd, "cat %s >> %s", fname, arg_log) == -1)
321 errExit("asprintf");
322 rv = system(cmd);
323 (void) rv;
324 free(cmd);
325 logprintf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
326 }
327
328 // find iptables command
329 struct stat s;
330 char *iptables = NULL;
331 char *iptables_restore = NULL;
332 if (stat("/sbin/iptables", &s) == 0) {
333 iptables = "/sbin/iptables";
334 iptables_restore = "/sbin/iptables-restore";
335 }
336 else if (stat("/usr/sbin/iptables", &s) == 0) {
337 iptables = "/usr/sbin/iptables";
338 iptables_restore = "/usr/sbin/iptables-restore";
339 }
340 if (iptables == NULL || iptables_restore == NULL) {
341 fprintf(stderr, "Error: iptables command not found, netfilter not configured\n");
342 rv = unlink(fname);
343 (void) rv;
344 exit(1);
345 }
346
347 // configuring
348 if (asprintf(&cmd, "%s %s", iptables_restore, fname) == -1)
349 errExit("asprintf");
350 rv = system(cmd);
351 if (rv)
352 fprintf(stdout, "Warning: possible netfilter problem!");
353 free(cmd);
354
355 sleep(1);
356 if (asprintf(&cmd, "%s %s", iptables_restore, fname) == -1)
357 errExit("asprintf");
358 rv = system(cmd);
359 free(cmd);
360
361 printf("Current firewall configuration:\n\n");
362 if (asprintf(&cmd, "%s -vL -n", iptables) == -1)
363 errExit("asprintf");
364 rv = system(cmd);
365
366 rv = unlink(fname);
367 (void) rv;
368 logprintf("\nfirewall deployed\n");
369}
370
371void logprintf(char* fmt, ...) {
372 if (!arg_log)
373 return;
374
375 FILE *fp = fopen(arg_log, "a");
376 if (fp) { // disregard if error
377 va_list args;
378 va_start(args,fmt);
379 vfprintf(fp, fmt, args);
380 va_end(args);
381 fclose(fp);
382 }
383}
384
385static void usage(void) {
386 printf("Usage: fnetlock [OPTIONS]\n");
387 printf("Options:\n");
388 printf(" --help, -? - this help screen\n");
389 printf(" --netfilter - build the firewall rules and commit them.\n");
390 printf(" --log=filename - logfile\n");
391 printf("\n");
392}
393
394int main(int argc, char **argv) {
395 int i;
396 printf("\n\n");
397
398 if (getuid() != 0) {
399 fprintf(stderr, "Error: you need to be root to run this program\n");
400 return 1;
401 }
402
403 for (i = 1; i < argc; i++) {
404 if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") == 0) {
405 usage();
406 return 0;
407 }
408 else if (strcmp(argv[i], "--netfilter") == 0)
409 arg_netfilter = 1;
410 else if (strncmp(argv[i], "--log=", 6) == 0)
411 arg_log = argv[i] + 6;
412 else {
413 fprintf(stderr, "Error: invalid argument\n");
414 return 1;
415 }
416 }
417
418 if (arg_netfilter) {
419 logprintf("starting network lockdown\n");
420 flush_netfilter();
421 }
422
423 ansi_clrscr(0);
424 run_trace();
425 if (arg_netfilter) {
426 deploy_netfilter();
427 sleep(3);
428 if (arg_log)
429 unlink(arg_log);
430 }
431
432 return 0;
433}