From 729b1251cd1783a0bc72a96ebc5aba455ccb375f Mon Sep 17 00:00:00 2001 From: netblue30 Date: Mon, 24 Oct 2022 08:35:01 -0400 Subject: --icmptrace --- src/firejail/main.c | 38 ++++++- src/fnettrace-icmp/Makefile | 17 +++ src/fnettrace-icmp/fnettrace_icmp.h | 34 ++++++ src/fnettrace-icmp/main.c | 215 ++++++++++++++++++++++++++++++++++++ src/man/firejail.txt | 40 ++++++- 5 files changed, 337 insertions(+), 7 deletions(-) create mode 100644 src/fnettrace-icmp/Makefile create mode 100644 src/fnettrace-icmp/fnettrace_icmp.h create mode 100644 src/fnettrace-icmp/main.c (limited to 'src') diff --git a/src/firejail/main.c b/src/firejail/main.c index fe80c5e2e..ed5b4901b 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -450,13 +450,13 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { exit_err_feature("networking"); exit(0); } - else if (strncmp(argv[i], "--dnstrace=", 15) == 0) { + else if (strncmp(argv[i], "--dnstrace=", 11) == 0) { if (checkcfg(CFG_NETWORK)) { if (getuid() != 0) { fprintf(stderr, "Error: --dnstrace is only available to root user\n"); exit(1); } - pid_t pid = require_pid(argv[i] + 15); + pid_t pid = require_pid(argv[i] + 11); netfilter_trace(pid, LIBDIR "/firejail/fnettrace-dns"); } else @@ -475,19 +475,49 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { exit_err_feature("networking"); exit(0); } - else if (strncmp(argv[i], "--snitrace=", 15) == 0) { + else if (strncmp(argv[i], "--snitrace=", 11) == 0) { if (checkcfg(CFG_NETWORK)) { if (getuid() != 0) { fprintf(stderr, "Error: --snitrace is only available to root user\n"); exit(1); } - pid_t pid = require_pid(argv[i] + 15); + pid_t pid = require_pid(argv[i] + 11); netfilter_trace(pid, LIBDIR "/firejail/fnettrace-sni"); } else exit_err_feature("networking"); exit(0); } + + + else if (strcmp(argv[i], "--icmptrace") == 0) { + if (checkcfg(CFG_NETWORK)) { + if (getuid() != 0) { + fprintf(stderr, "Error: --icmptrace is only available to root user\n"); + exit(1); + } + netfilter_trace(0, LIBDIR "/firejail/fnettrace-icmp"); + } + else + exit_err_feature("networking"); + exit(0); + } + else if (strncmp(argv[i], "--icmptrace=", 12) == 0) { + if (checkcfg(CFG_NETWORK)) { + if (getuid() != 0) { + fprintf(stderr, "Error: -icmptrace is only available to root user\n"); + exit(1); + } + pid_t pid = require_pid(argv[i] + 12); + netfilter_trace(pid, LIBDIR "/firejail/fnettrace-icmp"); + } + else + exit_err_feature("networking"); + exit(0); + } + + + else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { if (checkcfg(CFG_NETWORK)) { logargs(argc, argv); diff --git a/src/fnettrace-icmp/Makefile b/src/fnettrace-icmp/Makefile new file mode 100644 index 000000000..4dfdc891a --- /dev/null +++ b/src/fnettrace-icmp/Makefile @@ -0,0 +1,17 @@ +.PHONY: all +all: fnettrace-icmp + +ROOT = ../.. +include $(ROOT)/src/common.mk + +%.o : %.c $(H_FILE_LIST) $(ROOT)/config.mk + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ + +fnettrace-icmp: $(OBJS) $(ROOT)/config.mk + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) + +.PHONY: clean +clean:; rm -fr *.o fnettrace-icmp *.gcov *.gcda *.gcno *.plist + +.PHONY: distclean +distclean: clean diff --git a/src/fnettrace-icmp/fnettrace_icmp.h b/src/fnettrace-icmp/fnettrace_icmp.h new file mode 100644 index 000000000..790a3ce7f --- /dev/null +++ b/src/fnettrace-icmp/fnettrace_icmp.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014-2022 Firejail Authors + * + * This file is part of firejail project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#ifndef FNETTRACE_SNI_H +#define FNETTRACE_SNI_H + +#include "../include/common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif \ No newline at end of file diff --git a/src/fnettrace-icmp/main.c b/src/fnettrace-icmp/main.c new file mode 100644 index 000000000..47d61a326 --- /dev/null +++ b/src/fnettrace-icmp/main.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2014-2022 Firejail Authors + * + * This file is part of firejail project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "fnettrace_icmp.h" +#include +#include +#include +#include +#define MAX_BUF_SIZE (64 * 1024) + +char *type_description[19] = { + "Echo reply", + "unassigned", + "unassigned", + "Destination unreachable", + "Source quench", + "Redirect message", + "unassigned", + "unassigned", + "Echo request", + "Router advertisement", + "Router solicitation" + "Time exceeded", + "Bad IP header", + "Timestamp", + "Timestamp replay", + "Information request", + "Information reply", + "Address mask request", + "Address mask reply" +}; + +char *code_dest_unreachable[16] = { + "Network unreachable", + "Host unreachable", + "Protocol unreachable", + "Port unreachable", + "Fragmentation required, and DF flag set", + "Source route failed", + "Network unknown", + "Host unknown", + "Source host isolated", + "Network administratively prohibited", + "Host administratively prohibited", + "Network unreachable for ToS", + "Host unreachable for ToS", + "Communication administratively prohibited", + "Host Precedence Violation", + "Precedence cutoff in effect " +}; + +char *code_redirect_message[4] = { + "Datagram for the Network", + "Datagram for the Host", + "Datagram for the ToS & network", + "Datagram for the ToS & host " +}; + +char *code_time_exceeded[2] = { + "TTL expired in transit", + "Fragment reassembly time exceeded " +}; + +char *code_bad_ip_header[3] = { + "Pointer indicates the error", + "Missing a required option", + "Bad length" +}; + +static void print_icmp(uint32_t ip_dest, uint32_t ip_src, uint8_t type, uint8_t code, unsigned icmp_bytes) { + char type_number[10]; + char *type_ptr = type_number; + if (type < 19) + type_ptr = type_description[type]; + else + sprintf(type_number, "%u", type); + + char code_number[10]; + char *code_ptr = code_number; + if (type ==3 && code < 16) + code_ptr = code_dest_unreachable[code]; + else if (type == 5 && code < 4) + code_ptr = code_redirect_message[code]; + else if (type == 11 && code < 2) + code_ptr = code_time_exceeded[code]; + else if (type == 12 && code < 3) + code_ptr = code_bad_ip_header[code]; + else + sprintf(code_number, "%u", code); + + time_t seconds = time(NULL); + struct tm *t = localtime(&seconds); + printf("%02d:%02d:%02d %d.%d.%d.%d -> %d.%d.%d.%d - %u bytes - %s/%s\n", + t->tm_hour, t->tm_min, t->tm_sec, + PRINT_IP(ip_src), + PRINT_IP(ip_dest), + icmp_bytes, + type_ptr, + code_ptr); +} + +// https://www.kernel.org/doc/html/latest/networking/filter.html +static void custom_bpf(int sock) { + struct sock_filter code[] = { + // sudo tcpdump "icmp" -dd + { 0x28, 0, 0, 0x0000000c }, + { 0x15, 0, 3, 0x00000800 }, + { 0x30, 0, 0, 0x00000017 }, + { 0x15, 0, 1, 0x00000001 }, + { 0x6, 0, 0, 0x00040000 }, + { 0x6, 0, 0, 0x00000000 }, + }; + + struct sock_fprog bpf = { + .len = (unsigned short) sizeof(code) / sizeof(code[0]), + .filter = code, + }; + + int rv = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)); + if (rv < 0) { + fprintf(stderr, "Error: cannot attach BPF filter\n"); + exit(1); + } +} + +static void run_trace(void) { + // grab all Ethernet packets and use a custom BPF filter to get TLS/SNI packets + int s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (s < 0) + errExit("socket"); + custom_bpf(s); + + unsigned char buf[MAX_BUF_SIZE]; + while (1) { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(s, &rfds); + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + int rv = select(s + 1, &rfds, NULL, NULL, &tv); + if (rv < 0) + errExit("select"); + else if (rv == 0) + continue; + unsigned bytes = recvfrom(s, buf, MAX_BUF_SIZE, 0, NULL, NULL); + + if (bytes >= (14 + 20 + 2)) { // size of MAC + IP + ICMP code and type fields + uint8_t ip_hlen = (buf[14] & 0x0f) * 4; + uint8_t type = *(buf + 14 +ip_hlen); + uint8_t code = *(buf + 14 + ip_hlen + 1); + + uint32_t ip_dest; + memcpy(&ip_dest, buf + 14 + 16, 4); + ip_dest = ntohl(ip_dest); + uint32_t ip_src; + memcpy(&ip_src, buf + 14 + 12, 4); + ip_src = ntohl(ip_src); + + print_icmp(ip_dest, ip_src, type, code, bytes); + } + } + + close(s); +} + + +static void usage(void) { + printf("Usage: fnettrace-icmp [OPTIONS]\n"); + printf("Options:\n"); + printf(" --help, -? - this help screen\n"); + printf("\n"); +} + +int main(int argc, char **argv) { + int i; + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") == 0) { + usage(); + return 0; + } + else { + fprintf(stderr, "Error: invalid argument\n"); + return 1; + } + } + + if (getuid() != 0) { + fprintf(stderr, "Error: you need to be root to run this program\n"); + return 1; + } + + time_t now = time(NULL); + printf("ICMP trace for %s\n", ctime(&now)); + run_trace(); + + return 0; +} diff --git a/src/man/firejail.txt b/src/man/firejail.txt index a7e418981..b4be1cd62 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -791,7 +791,9 @@ Without a name/pid, Firejail will monitor the main system network namespace. .br .br -$ sudo firejail --dnstrace=browser +Example: +.br +$ sudo firejail --dnstrace .br 11:31:43 9.9.9.9 linux.com (type 1) .br @@ -917,6 +919,34 @@ $ firejail --ignore=seccomp --ignore=caps firefox $ firejail \-\-ignore="net eth0" firefox #endif +#ifdef HAVE_NETWORK +.TP +\fB\-\-icmptrace[=name|pid] +Monitor ICMP traffic. The sandbox can be specified by name or pid. Only networked sandboxes +created with \-\-net are supported. This option is only available when running the sandbox as root. +.br + +.br +Without a name/pid, Firejail will monitor the main system network namespace. +.br + +.br +Example +.br +$ sudo firejail --icmptrace +.br +20:53:54 192.168.1.60 -> 142.250.65.174 - 98 bytes - Echo request/0 +.br +20:53:54 142.250.65.174 -> 192.168.1.60 - 98 bytes - Echo reply/0 +.br +20:53:55 192.168.1.60 -> 142.250.65.174 - 98 bytes - Echo request/0 +.br +20:53:55 142.250.65.174 -> 192.168.1.60 - 98 bytes - Echo reply/0 +.br +20:53:55 192.168.1.60 -> 1.1.1.1 - 154 bytes - Destination unreachable/Port unreachable +.br +#endif + .TP \fB\-\-\include=file.profile Include a profile file before the regular profiles are used. @@ -1597,7 +1627,9 @@ Without a name/pid, Firejail will monitor the main system network namespace. .br .br -$ sudo firejail --nettrace=browser +Example: +.br +$ sudo firejail --nettrace .br 95 KB/s geoip 457, IP database 4436 .br @@ -2791,7 +2823,9 @@ Without a name/pid, Firejail will monitor the main system network namespace. .br .br -$ sudo firejail --snitrace=browser +Example: +.br +$ sudo firejail --snitrace .br 07:49:51 23.185.0.3 linux.com .br -- cgit v1.2.3-70-g09d2