aboutsummaryrefslogtreecommitdiffstats
path: root/src/fnettrace-sni/main.c
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@protonmail.com>2022-04-08 08:49:51 -0400
committerLibravatar netblue30 <netblue30@protonmail.com>2022-04-08 08:49:51 -0400
commitf861764dfa4841a5743b49f33df923bfe75a7f84 (patch)
treef27e8b4a84479557ba0901985944c2ce4ce28b84 /src/fnettrace-sni/main.c
parentMerge branch 'master' of ssh://github.com/netblue30/firejail (diff)
downloadfirejail-f861764dfa4841a5743b49f33df923bfe75a7f84.tar.gz
firejail-f861764dfa4841a5743b49f33df923bfe75a7f84.tar.zst
firejail-f861764dfa4841a5743b49f33df923bfe75a7f84.zip
nettrace dns and sni
Diffstat (limited to 'src/fnettrace-sni/main.c')
-rw-r--r--src/fnettrace-sni/main.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/src/fnettrace-sni/main.c b/src/fnettrace-sni/main.c
new file mode 100644
index 000000000..ffcb3f28a
--- /dev/null
+++ b/src/fnettrace-sni/main.c
@@ -0,0 +1,207 @@
1/*
2 * Copyright (C) 2014-2022 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 "fnettrace_sni.h"
21#include <sys/ioctl.h>
22#include <time.h>
23#include <linux/filter.h>
24#include <linux/if_ether.h>
25#define MAX_BUF_SIZE (64 * 1024)
26
27// pkt - start of TLS layer
28static void print_tls(uint32_t ip_dest, unsigned char *pkt, unsigned len) {
29 assert(pkt);
30
31 char ip[30];
32 sprintf(ip, "%d.%d.%d.%d", PRINT_IP(ip_dest));
33 time_t seconds = time(NULL);
34 struct tm *t = localtime(&seconds);
35
36 // expecting a handshake packet and client hello
37 if (pkt[0] != 0x16 || pkt[5] != 0x01)
38 goto errout;
39
40
41 // look for server name indication
42 unsigned char *ptr = pkt;
43 int i = 0;
44 char *name = NULL;
45 while (i < (len - 20)) {
46 // 3 zeros and 3 matching length fields
47 if (*ptr == 0 && *(ptr + 1) == 0 && (*(ptr + 2) == 0 || *(ptr + 2) == 1) && *(ptr + 6) == 0) {
48 uint16_t len1;
49 memcpy(&len1, ptr + 2, 2);
50 len1 = ntohs(len1);
51
52 uint16_t len2;
53 memcpy(&len2, ptr + 4, 2);
54 len2 = ntohs(len2);
55
56 uint16_t len3;
57 memcpy(&len3, ptr + 7, 2);
58 len3 = ntohs(len3);
59
60 if (len1 == (len2 + 2) && len1 == (len3 + 5)) {
61 *(ptr + 9 + len3) = 0;
62 name = ptr + 9;
63 break;
64 }
65 }
66 ptr++;
67 i++;
68 }
69
70 if (name)
71 printf("%02d:%02d:%02d %15s %s\n", t->tm_hour, t->tm_min, t->tm_sec, ip, name);
72 else
73 goto nosni;
74 return;
75
76errout:
77 printf("%02d:%02d:%02d %15s Error: invalid TLS packet\n", t->tm_hour, t->tm_min, t->tm_sec, ip);
78 return;
79
80nosni:
81 printf("%02d:%02d:%02d %15s no SNI\n", t->tm_hour, t->tm_min, t->tm_sec, ip);
82 return;
83}
84
85// https://www.kernel.org/doc/html/latest/networking/filter.html
86static void custom_bpf(int sock) {
87 struct sock_filter code[] = {
88 // sudo tcpdump "tcp port 443 and (tcp[((tcp[12] & 0xf0) >>2)] = 0x16) && (tcp[((tcp[12] & 0xf0) >>2)+5] = 0x01)" -dd
89 { 0x28, 0, 0, 0x0000000c },
90 { 0x15, 27, 0, 0x000086dd },
91 { 0x15, 0, 26, 0x00000800 },
92 { 0x30, 0, 0, 0x00000017 },
93 { 0x15, 0, 24, 0x00000006 },
94 { 0x28, 0, 0, 0x00000014 },
95 { 0x45, 22, 0, 0x00001fff },
96 { 0xb1, 0, 0, 0x0000000e },
97 { 0x48, 0, 0, 0x0000000e },
98 { 0x15, 2, 0, 0x000001bb },
99 { 0x48, 0, 0, 0x00000010 },
100 { 0x15, 0, 17, 0x000001bb },
101 { 0x50, 0, 0, 0x0000001a },
102 { 0x54, 0, 0, 0x000000f0 },
103 { 0x74, 0, 0, 0x00000002 },
104 { 0xc, 0, 0, 0x00000000 },
105 { 0x7, 0, 0, 0x00000000 },
106 { 0x50, 0, 0, 0x0000000e },
107 { 0x15, 0, 10, 0x00000016 },
108 { 0xb1, 0, 0, 0x0000000e },
109 { 0x50, 0, 0, 0x0000001a },
110 { 0x54, 0, 0, 0x000000f0 },
111 { 0x74, 0, 0, 0x00000002 },
112 { 0x4, 0, 0, 0x00000005 },
113 { 0xc, 0, 0, 0x00000000 },
114 { 0x7, 0, 0, 0x00000000 },
115 { 0x50, 0, 0, 0x0000000e },
116 { 0x15, 0, 1, 0x00000001 },
117 { 0x6, 0, 0, 0x00040000 },
118 { 0x6, 0, 0, 0x00000000 },
119 };
120
121 struct sock_fprog bpf = {
122 .len = (unsigned short) sizeof(code) / sizeof(code[0]),
123 .filter = code,
124 };
125
126 int rv = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
127 if (rv < 0) {
128 fprintf(stderr, "Error: cannot attach BPF filter\n");
129 exit(1);
130 }
131}
132
133static void run_trace(void) {
134 // grab all Ethernet packets and use a custom BPF filter to get only UDP from source port 53
135 int s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
136 if (s < 0)
137 errExit("socket");
138 custom_bpf(s);
139
140 unsigned char buf[MAX_BUF_SIZE];
141 while (1) {
142 fd_set rfds;
143 FD_ZERO(&rfds);
144 FD_SET(s, &rfds);
145 struct timeval tv;
146 tv.tv_sec = 1;
147 tv.tv_usec = 0;
148 int rv = select(s + 1, &rfds, NULL, NULL, &tv);
149 if (rv < 0)
150 errExit("select");
151 else if (rv == 0)
152 continue;
153 unsigned bytes = recvfrom(s, buf, MAX_BUF_SIZE, 0, NULL, NULL);
154
155 if (bytes >= (14 + 20 + 20)) { // size of MAC + IP + TCP headers
156 uint8_t ip_hlen = (buf[14] & 0x0f) * 4;
157 uint16_t port_dest;
158 memcpy(&port_dest, buf + 14 + ip_hlen + 2, 2);
159 port_dest = ntohs(port_dest);
160 uint8_t protocol = buf[14 + 9];
161 uint32_t ip_dest;
162 memcpy(&ip_dest, buf + 14 + 16, 4);
163 ip_dest = ntohl(ip_dest);
164 uint8_t tcp_hlen = (buf[14 + ip_hlen + 12] & 0xf0) >> 2;
165
166 // if TLS packet, extract SNI
167 if (port_dest == 443 && protocol == 6) // TCP protocol
168 print_tls(ip_dest, buf + 14 + ip_hlen + tcp_hlen, bytes - 14 - ip_hlen - tcp_hlen); // IP and TCP header len
169 }
170 }
171
172 close(s);
173}
174
175
176static void usage(void) {
177 printf("Usage: fnettrace-sni [OPTIONS]\n");
178 printf("Options:\n");
179 printf(" --help, -? - this help screen\n");
180 printf("\n");
181}
182
183int main(int argc, char **argv) {
184 int i;
185
186 for (i = 1; i < argc; i++) {
187 if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") == 0) {
188 usage();
189 return 0;
190 }
191 else {
192 fprintf(stderr, "Error: invalid argument\n");
193 return 1;
194 }
195 }
196
197 if (getuid() != 0) {
198 fprintf(stderr, "Error: you need to be root to run this program\n");
199 return 1;
200 }
201
202 time_t now = time(NULL);
203 printf("SNI trace for %s\n", ctime(&now));
204 run_trace();
205
206 return 0;
207}