aboutsummaryrefslogtreecommitdiffstats
path: root/src/fnetlock/main.c
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@protonmail.com>2023-07-25 10:22:59 -0400
committerLibravatar netblue30 <netblue30@protonmail.com>2023-07-25 10:22:59 -0400
commit8e4b847cdd9203227c003152d1ea4a2dd0f91664 (patch)
treeaec337dd5faa357606ac06874f2168db6afe520e /src/fnetlock/main.c
parentcleanup (diff)
downloadfirejail-8e4b847cdd9203227c003152d1ea4a2dd0f91664.tar.gz
firejail-8e4b847cdd9203227c003152d1ea4a2dd0f91664.tar.zst
firejail-8e4b847cdd9203227c003152d1ea4a2dd0f91664.zip
split nettrace executable ^Cto netrace and netlock
Diffstat (limited to 'src/fnetlock/main.c')
-rw-r--r--src/fnetlock/main.c392
1 files changed, 392 insertions, 0 deletions
diff --git a/src/fnetlock/main.c b/src/fnetlock/main.c
new file mode 100644
index 000000000..b76be029d
--- /dev/null
+++ b/src/fnetlock/main.c
@@ -0,0 +1,392 @@
1/*
2 * Copyright (C) 2014-2023 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 "fnetlock.h"
21#include <limits.h>
22#include <sys/ioctl.h>
23#include <sys/prctl.h>
24#include <signal.h>
25#define MAX_BUF_SIZE (64 * 1024)
26
27static int arg_tail = 0;
28static char *arg_log = NULL;
29
30//*****************************************************************
31// traffic trace storage - hash table for fast access + linked list for display purposes
32//*****************************************************************
33typedef struct hnode_t {
34 struct hnode_t *hnext; // used for hash table and unused linked list
35 struct hnode_t *dnext; // used to display streams on the screen
36 uint32_t ip_src;
37 uint16_t port_src;
38 uint8_t protocol;
39
40 // the firewall is build based on source address, and in the linked list
41 // we could have elements with the same address but different ports
42 uint8_t ip_instance;
43} HNode;
44
45// hash table
46#define HMAX 256
47HNode *htable[HMAX] = {NULL};
48static int have_traffic = 0;
49
50// using protocol 0 and port 0 for ICMP
51static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src) {
52 uint8_t h = hash(ip_src);
53 int ip_instance = 0;
54 HNode *ptr = htable[h];
55 while (ptr) {
56 if (ptr->ip_src == ip_src) {
57 ip_instance++;
58 if (ptr->port_src == port_src && ptr->protocol == protocol)
59 return;
60 }
61 ptr = ptr->hnext;
62 }
63
64 logprintf("netlock: adding %d.%d.%d.%d\n", PRINT_IP(ip_src));
65 have_traffic = 1;
66 HNode *hnew = malloc(sizeof(HNode));
67 assert(hnew);
68 hnew->ip_src = ip_src;
69 hnew->port_src = port_src;
70 hnew->protocol = protocol;
71 hnew->hnext = NULL;
72 hnew->ip_instance = ip_instance + 1;
73 if (htable[h] == NULL)
74 htable[h] = hnew;
75 else {
76 hnew->hnext = htable[h];
77 htable[h] = hnew;
78 }
79}
80
81
82
83
84// trace rx traffic coming in
85static void run_trace(void) {
86 logprintf("netlock: accumulating traffic for %d seconds\n", NETLOCK_INTERVAL);
87
88 // trace only rx ipv4 tcp and upd
89 int s1 = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
90 int s2 = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
91 if (s1 < 0 || s2 < 0)
92 errExit("socket");
93
94
95 unsigned start = time(NULL);
96 unsigned char buf[MAX_BUF_SIZE];
97 unsigned bw = 0; // bandwidth calculations
98
99 int printed = 0;
100 while (1) {
101 unsigned runtime = time(NULL) - start;
102 if ( runtime >= NETLOCK_INTERVAL)
103 break;
104 if (runtime % 10 == 0) {
105 if (!printed)
106 logprintf("netlock: %u seconds remaining\n", NETLOCK_INTERVAL - runtime);
107 printed = 1;
108 }
109 else
110 printed = 0;
111
112 fd_set rfds;
113 FD_ZERO(&rfds);
114 FD_SET(s1, &rfds);
115 FD_SET(s2, &rfds);
116 int maxfd = (s1 > s2) ? s1 : s2;
117 maxfd++;
118
119 struct timeval tv;
120 tv.tv_sec = 1;
121 tv.tv_usec = 0;
122
123 int rv = select(maxfd, &rfds, NULL, NULL, &tv);
124 if (rv < 0)
125 errExit("select");
126 else if (rv == 0)
127 continue;
128
129
130 // rx tcp traffic by default
131 int sock = s1;
132
133 if (FD_ISSET(s2, &rfds))
134 sock = s2;
135
136 unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL);
137 if (bytes >= 20) { // size of IP header
138#ifdef DEBUG
139 {
140 uint32_t ip_src;
141 memcpy(&ip_src, buf + 12, 4);
142 ip_src = ntohl(ip_src);
143
144 uint32_t ip_dst;
145 memcpy(&ip_dst, buf + 16, 4);
146 ip_dst = ntohl(ip_dst);
147 printf("%d.%d.%d.%d -> %d.%d.%d.%d, %u bytes\n", PRINT_IP(ip_src), PRINT_IP(ip_dst), bytes);
148 }
149#endif
150 // filter out loopback traffic
151 if (buf[12] != 127 && buf[16] != 127) {
152 bw += bytes + 14; // assume a 14 byte Ethernet layer
153
154 uint32_t ip_src;
155 memcpy(&ip_src, buf + 12, 4);
156 ip_src = ntohl(ip_src);
157
158 uint8_t hlen = (buf[0] & 0x0f) * 4;
159 uint16_t port_src = 0;
160 memcpy(&port_src, buf + hlen, 2);
161 port_src = ntohs(port_src);
162
163 uint8_t protocol = buf[9];
164 hnode_add(ip_src, protocol, port_src);
165 }
166 }
167 }
168
169 close(s1);
170 close(s2);
171}
172
173static char *filter_start =
174 "*filter\n"
175 ":INPUT DROP [0:0]\n"
176 ":FORWARD DROP [0:0]\n"
177 ":OUTPUT DROP [0:0]\n";
178
179// return 1 if error
180static int print_filter(FILE *fp) {
181 fprintf(fp, "%s\n", filter_start);
182 fprintf(fp, "-A INPUT -s 127.0.0.0/8 -j ACCEPT\n");
183 fprintf(fp, "-A OUTPUT -d 127.0.0.0/8 -j ACCEPT\n");
184 fprintf(fp, "\n");
185
186 int i;
187 for (i = 0; i < HMAX; i++) {
188 HNode *ptr = htable[i];
189 while (ptr) {
190 // filter rules are targeting ip address, the port number is disregarded,
191 // so we look only at the first instance of an address
192 if (ptr->ip_instance == 1) {
193 char *protocol = (ptr->protocol == 6) ? "tcp" : "udp";
194 fprintf(fp, "-A INPUT -s %d.%d.%d.%d -p %s -j ACCEPT\n",
195 PRINT_IP(ptr->ip_src),
196 protocol);
197 fprintf(fp, "-A OUTPUT -d %d.%d.%d.%d -p %s -j ACCEPT\n",
198 PRINT_IP(ptr->ip_src),
199 protocol);
200 fprintf(fp, "\n");
201 }
202 ptr = ptr->hnext;
203 }
204 }
205 fprintf(fp, "COMMIT\n");
206
207 return 0;
208}
209
210static char *flush_rules[] = {
211 "-P INPUT ACCEPT",
212// "-P FORWARD DENY",
213 "-P OUTPUT ACCEPT",
214 "-F",
215 "-X",
216// "-t nat -F",
217// "-t nat -X",
218// "-t mangle -F",
219// "-t mangle -X",
220// "iptables -t raw -F",
221// "-t raw -X",
222 NULL
223};
224
225static void deploy_netfilter(void) {
226 int rv;
227 char *cmd;
228 int i;
229
230 if (have_traffic == 0) {
231 logprintf("Sorry, no network traffic was detected. The firewall was not configured.\n");
232 return;
233 }
234 // find iptables command
235 char *iptables = NULL;
236 char *iptables_restore = NULL;
237 if (access("/sbin/iptables", X_OK) == 0) {
238 iptables = "/sbin/iptables";
239 iptables_restore = "/sbin/iptables-restore";
240 }
241 else if (access("/usr/sbin/iptables", X_OK) == 0) {
242 iptables = "/usr/sbin/iptables";
243 iptables_restore = "/usr/sbin/iptables-restore";
244 }
245 if (iptables == NULL || iptables_restore == NULL) {
246 fprintf(stderr, "Error: iptables command not found, netfilter not configured\n");
247 exit(1);
248 }
249
250 // flush all netfilter rules
251 i = 0;
252 while (flush_rules[i]) {
253 char *cmd;
254 if (asprintf(&cmd, "%s %s", iptables, flush_rules[i]) == -1)
255 errExit("asprintf");
256 int rv = system(cmd);
257 (void) rv;
258 free(cmd);
259 i++;
260 }
261
262 // create temporary file
263 char fname[] = "/tmp/firejail-XXXXXX";
264 int fd = mkstemp(fname);
265 if (fd == -1) {
266 fprintf(stderr, "Error: cannot create temporary configuration file\n");
267 exit(1);
268 }
269
270 FILE *fp = fdopen(fd, "w");
271 if (!fp) {
272 rv = unlink(fname);
273 (void) rv;
274 fprintf(stderr, "Error: cannot create temporary configuration file\n");
275 exit(1);
276 }
277 print_filter(fp);
278 fclose(fp);
279
280 logprintf("\n\n");
281 logprintf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
282 if (asprintf(&cmd, "cat %s >> %s", fname, arg_log) == -1)
283 errExit("asprintf");
284 rv = system(cmd);
285 (void) rv;
286 free(cmd);
287
288 if (asprintf(&cmd, "cat %s", fname) == -1)
289 errExit("asprintf");
290 rv = system(cmd);
291 (void) rv;
292 free(cmd);
293 logprintf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
294
295
296 // configuring
297 if (asprintf(&cmd, "%s %s", iptables_restore, fname) == -1)
298 errExit("asprintf");
299 rv = system(cmd);
300 if (rv)
301 fprintf(stdout, "Warning: possible netfilter problem!");
302 free(cmd);
303
304 rv = unlink(fname);
305 (void) rv;
306 logprintf("\nnetlock: firewall deployed\n");
307}
308
309void logprintf(char *fmt, ...) {
310 if (!arg_log)
311 return;
312
313 FILE *fp = fopen(arg_log, "a");
314 if (fp) { // disregard if error
315 va_list args;
316 va_start(args, fmt);
317 vfprintf(fp, fmt, args);
318 va_end(args);
319 fclose(fp);
320 }
321
322 va_list args;
323 va_start(args, fmt);
324 vfprintf(stdout, fmt, args);
325 va_end(args);
326}
327
328static const char *const usage_str =
329 "Usage: fnettrace [OPTIONS]\n"
330 "Options:\n"
331 " --help, -? - this help screen\n"
332 " --log=filename - netlocker logfile\n"
333 " --tail - \"tail -f\" functionality\n";
334
335static void usage(void) {
336 puts(usage_str);
337}
338
339int main(int argc, char **argv) {
340 int i;
341
342 for (i = 1; i < argc; i++) {
343 if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") == 0) {
344 usage();
345 return 0;
346 }
347 else if (strcmp(argv[i], "--tail") == 0)
348 arg_tail = 1;
349 else if (strncmp(argv[i], "--log=", 6) == 0)
350 arg_log = argv[i] + 6;
351 else {
352 fprintf(stderr, "Error: invalid argument\n");
353 return 1;
354 }
355 }
356
357 // tail
358 if (arg_tail) {
359 if (!arg_log) {
360 fprintf(stderr, "Error: no log file\n");
361 usage();
362 exit(1);
363 }
364
365 tail(arg_log);
366 sleep(5);
367 exit(0);
368 }
369
370 if (getuid() != 0) {
371 fprintf(stderr, "Error: you need to be root to run this program\n");
372 return 1;
373 }
374
375 // kill the process if the parent died
376 prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
377
378 logprintf("netlock: starting network lockdown\n");
379 run_trace();
380
381 // TCP path MTU discovery will not work properly since the firewall drops all ICMP packets
382 // Instead, we use iPacketization Layer PMTUD (RFC 4821) support in Linux kernel
383 int rv = system("echo 1 > /proc/sys/net/ipv4/tcp_mtu_probing");
384 (void) rv;
385
386 deploy_netfilter();
387 sleep(3);
388 if (arg_log)
389 unlink(arg_log);
390
391 return 0;
392}