diff options
author | 2021-12-28 11:04:59 -0500 | |
---|---|---|
committer | 2021-12-28 11:04:59 -0500 | |
commit | 54b28a0b6392f9921fad98ba9e22470c900efb03 (patch) | |
tree | b84db698ff24816e8c67b9002de860a2fccaeb0f | |
parent | Merge pull request #4802 from jose1711/clipgrab_fix (diff) | |
download | firejail-54b28a0b6392f9921fad98ba9e22470c900efb03.tar.gz firejail-54b28a0b6392f9921fad98ba9e22470c900efb03.tar.zst firejail-54b28a0b6392f9921fad98ba9e22470c900efb03.zip |
nettrace/netlock
-rw-r--r-- | Makefile.in | 5 | ||||
-rwxr-xr-x | configure | 3 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | src/firejail/firejail.h | 2 | ||||
-rw-r--r-- | src/firejail/main.c | 42 | ||||
-rw-r--r-- | src/firejail/netfilter.c | 85 | ||||
-rw-r--r-- | src/fnettrace/Makefile.in | 17 | ||||
-rw-r--r-- | src/fnettrace/fnettrace.h | 64 | ||||
-rw-r--r-- | src/fnettrace/main.c | 433 |
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 | |||
27 | all: all_items mydirs $(MAN_TARGET) filters | 27 | all: all_items mydirs $(MAN_TARGET) filters |
28 | APPS = src/firecfg/firecfg src/firejail/firejail src/firemon/firemon src/profstats/profstats src/jailcheck/jailcheck | 28 | APPS = src/firecfg/firecfg src/firejail/firejail src/firemon/firemon src/profstats/profstats src/jailcheck/jailcheck |
29 | SBOX_APPS = src/fbuilder/fbuilder src/ftee/ftee src/fids/fids | 29 | SBOX_APPS = src/fbuilder/fbuilder src/ftee/ftee src/fids/fids |
30 | SBOX_APPS_NON_DUMPABLE = src/fcopy/fcopy src/fldd/fldd src/fnet/fnet src/fnetfilter/fnetfilter src/profstats/profstats | 30 | SBOX_APPS_NON_DUMPABLE = src/fcopy/fcopy src/fldd/fldd src/fnet/fnet src/fnetfilter/fnetfilter |
31 | SBOX_APPS_NON_DUMPABLE += src/fsec-optimize/fsec-optimize src/fsec-print/fsec-print src/fseccomp/fseccomp | ||
32 | SBOX_APPS_NON_DUMPABLE += src/fnettrace/fnettrace | ||
31 | MYDIRS = src/lib $(MAN_SRC) $(COMPLETIONDIRS) | 33 | MYDIRS = src/lib $(MAN_SRC) $(COMPLETIONDIRS) |
32 | MYLIBS = src/libpostexecseccomp/libpostexecseccomp.so src/libtrace/libtrace.so src/libtracelog/libtracelog.so | 34 | MYLIBS = src/libpostexecseccomp/libpostexecseccomp.so src/libtrace/libtrace.so src/libtracelog/libtracelog.so |
33 | COMPLETIONS = src/zsh_completion/_firejail src/bash_completion/firejail.bash_completion | 35 | COMPLETIONS = src/zsh_completion/_firejail src/bash_completion/firejail.bash_completion |
34 | MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5 firejail-users.5 jailcheck.1 | 36 | MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5 firejail-users.5 jailcheck.1 |
35 | SBOX_APPS_NON_DUMPABLE += src/fsec-optimize/fsec-optimize src/fsec-print/fsec-print src/fseccomp/fseccomp | ||
36 | SECCOMP_FILTERS = seccomp seccomp.debug seccomp.32 seccomp.block_secondary seccomp.mdwx seccomp.mdwx.32 | 37 | SECCOMP_FILTERS = seccomp seccomp.debug seccomp.32 seccomp.block_secondary seccomp.mdwx seccomp.mdwx.32 |
37 | ALL_ITEMS = $(APPS) $(SBOX_APPS) $(SBOX_APPS_NON_DUMPABLE) $(MYLIBS) | 38 | ALL_ITEMS = $(APPS) $(SBOX_APPS) $(SBOX_APPS_NON_DUMPABLE) $(MYLIBS) |
38 | 39 | ||
@@ -4271,7 +4271,7 @@ fi | |||
4271 | 4271 | ||
4272 | ac_config_files="$ac_config_files mkdeb.sh" | 4272 | ac_config_files="$ac_config_files mkdeb.sh" |
4273 | 4273 | ||
4274 | ac_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" | 4274 | ac_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 | ||
4276 | cat >confcache <<\_ACEOF | 4276 | cat >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/ | |||
272 | src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/fbuilder/Makefile src/fsec-print/Makefile \ | 272 | src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/fbuilder/Makefile src/fsec-print/Makefile \ |
273 | src/ftee/Makefile src/fseccomp/Makefile src/fldd/Makefile src/libpostexecseccomp/Makefile src/fsec-optimize/Makefile \ | 273 | src/ftee/Makefile src/fseccomp/Makefile src/fldd/Makefile src/libpostexecseccomp/Makefile src/fsec-optimize/Makefile \ |
274 | src/profstats/Makefile src/man/Makefile src/zsh_completion/Makefile src/bash_completion/Makefile test/Makefile \ | 274 | src/profstats/Makefile src/man/Makefile src/zsh_completion/Makefile src/bash_completion/Makefile test/Makefile \ |
275 | src/jailcheck/Makefile src/fids/Makefile]) | 275 | src/jailcheck/Makefile src/fids/Makefile src/fnettrace/Makefile]) |
276 | AC_OUTPUT | 276 | AC_OUTPUT |
277 | 277 | ||
278 | cat <<EOF | 278 | cat <<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); | |||
658 | void check_output(int argc, char **argv); | 658 | void check_output(int argc, char **argv); |
659 | 659 | ||
660 | // netfilter.c | 660 | // netfilter.c |
661 | void netfilter_netlock(pid_t pid); | ||
662 | void netfilter_trace(pid_t pid); | ||
661 | void check_netfilter_file(const char *fname); | 663 | void check_netfilter_file(const char *fname); |
662 | void netfilter(const char *fname); | 664 | void netfilter(const char *fname); |
663 | void netfilter6(const char *fname); | 665 | void 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; | ||
3249 | printf("data #%s#\n", prf->data); | ||
3250 | if (prf->data) | ||
3251 | free(prf->data); | ||
3252 | printf("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 | ||
27 | void 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 | |||
90 | void 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 | |||
27 | void check_netfilter_file(const char *fname) { | 112 | void 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 | ||
2 | all: fnettrace | ||
3 | |||
4 | include ../common.mk | ||
5 | |||
6 | %.o : %.c $(H_FILE_LIST) | ||
7 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ | ||
8 | |||
9 | fnettrace: $(OBJS) | ||
10 | $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) | ||
11 | |||
12 | .PHONY: clean | ||
13 | clean:; rm -fr *.o fnettrace *.gcov *.gcda *.gcno *.plist | ||
14 | |||
15 | .PHONY: distclean | ||
16 | distclean: 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 | |||
16 | void logprintf(char* fmt, ...); | ||
17 | |||
18 | static 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 | |||
27 | static 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 | |||
37 | static 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 | |||
46 | static 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 | |||
56 | static 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 | |||
4 | static int arg_netfilter = 0; | ||
5 | static char *arg_log = NULL; | ||
6 | |||
7 | typedef 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 | ||
20 | HList *htable[HMAX] = {NULL}; | ||
21 | static int htable_empty = 1; | ||
22 | |||
23 | static 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 | ||
63 | static 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 | |||
89 | static 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 | |||
139 | static 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 | |||
216 | static 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 | ||
223 | static 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 | |||
255 | static 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 | |||
270 | static 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 | |||
295 | static 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 | |||
371 | void 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 | |||
385 | static 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 | |||
394 | int 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 | } | ||