From 78a0feb5a681283dbf1239bf3ef777673eb2203b Mon Sep 17 00:00:00 2001 From: M66B Date: Mon, 28 Aug 2023 10:43:44 +0200 Subject: [PATCH] Added SNI decoding --- app/CMakeLists.txt | 1 + app/src/main/jni/netguard/ip.c | 14 +++ app/src/main/jni/netguard/netguard.h | 12 ++ app/src/main/jni/netguard/tls.c | 175 +++++++++++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 app/src/main/jni/netguard/tls.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index ff27ce06..dca80a1e 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -9,6 +9,7 @@ add_library( netguard src/main/jni/netguard/tcp.c src/main/jni/netguard/udp.c src/main/jni/netguard/icmp.c + src/main/jni/netguard/tls.c src/main/jni/netguard/dns.c src/main/jni/netguard/dhcp.c src/main/jni/netguard/pcap.c diff --git a/app/src/main/jni/netguard/ip.c b/app/src/main/jni/netguard/ip.c index c87d727e..c319721f 100644 --- a/app/src/main/jni/netguard/ip.c +++ b/app/src/main/jni/netguard/ip.c @@ -292,6 +292,20 @@ void handle_ip(const struct arguments *args, uid = get_uid_q(args, version, protocol, source, sport, dest, dport); } + // Get server name + if (protocol == IPPROTO_TCP) { + const struct tcphdr *tcphdr = (struct tcphdr *) payload; + const uint8_t tcpoptlen = (uint8_t) ((tcphdr->doff - 5) * 4); + const uint8_t *data = payload + sizeof(struct tcphdr) + tcpoptlen; + const uint16_t datalen = (const uint16_t) (length - (data - pkt)); + + char server_name[TLS_SNI_LENGTH + 1]; + if (get_sni(data, datalen, server_name)) { + log_android(ANDROID_LOG_INFO, "TLS server name: %s", server_name); + dns_resolved(args, server_name, server_name, dest, -1); + } + } + log_android(ANDROID_LOG_DEBUG, "Packet v%d %s/%u > %s/%u proto %d flags %s uid %d", version, source, sport, dest, dport, protocol, flags, uid); diff --git a/app/src/main/jni/netguard/netguard.h b/app/src/main/jni/netguard/netguard.h index 2edd4676..47c6336b 100644 --- a/app/src/main/jni/netguard/netguard.h +++ b/app/src/main/jni/netguard/netguard.h @@ -253,6 +253,18 @@ typedef struct pcaprec_hdr_s { #define LINKTYPE_RAW 101 +// TLS + +#define TLS_SNI_LENGTH 255 +#define TLS_HANDSHAKE_RECORD 22 +#define TLS_MESSAGE_CLIENTHELLO 1 +#define TLS_EXTENSION_TYPE_SERVER_NAME 0 + +int get_sni( + const uint8_t *data, + const uint16_t datalen, + char *server_name); + // DNS #define DNS_QCLASS_IN 1 diff --git a/app/src/main/jni/netguard/tls.c b/app/src/main/jni/netguard/tls.c new file mode 100644 index 00000000..fea5d459 --- /dev/null +++ b/app/src/main/jni/netguard/tls.c @@ -0,0 +1,175 @@ +/* + This file is part of NetGuard. + + NetGuard 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 3 of the License, or + (at your option) any later version. + + NetGuard 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 NetGuard. If not, see . + + Copyright 2015-2019 by Marcel Bokhorst (M66B) +*/ + +#include "netguard.h" + +int get_sni( + const uint8_t *data, + const uint16_t datalen, + char *server_name) { + if (datalen < 6) { + log_android(ANDROID_LOG_DEBUG, "TLS header too short"); + return 0; + } + + uint8_t content_type = (uint8_t) data[0]; + uint8_t major_version = (uint8_t) data[1]; + uint8_t minor_version = (uint8_t) data[2]; + uint16_t content_length = ((uint16_t) data[3] << 8) | data[4]; + uint8_t message_type = (uint8_t) data[5]; + + // https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2 + if (content_type != TLS_HANDSHAKE_RECORD || + major_version < 0x03 || + 5 + content_length != datalen || + message_type != TLS_MESSAGE_CLIENTHELLO) { + log_android(ANDROID_LOG_DEBUG, "TLS content %d version %d length %d/%d type %d", + content_type, major_version, 5 + content_length, datalen, message_type); + return 0; + } + + log_android(ANDROID_LOG_DEBUG, "TLS client hello version %d.%d", + major_version, minor_version); + + uint8_t index = 6 + // header above + 3 + // client hello length + 2 + // client hello protocol version + 32; // random value + + // Session ID + if (index >= datalen) { + log_android(ANDROID_LOG_WARN, "TLS session ID %d/%d", index, datalen); + return 0; + } + + log_android(ANDROID_LOG_DEBUG, "TLS hello version %d.%d", + data[9], data[10]); + + uint8_t session_len = data[index]; + index += 1 + session_len; + + // Cipher suites + if (index + 1 >= datalen) { + log_android(ANDROID_LOG_WARN, "TLS cipher suites %d/%d", index + 1, datalen); + return 0; + } + uint16_t suites_len = ((uint16_t) data[index] << 8) | data[index + 1]; + index += 2 + suites_len; + + // Compression method + if (index >= datalen) { + log_android(ANDROID_LOG_WARN, "TLS compression %d/%d", index, datalen); + return 0; + } + uint8_t compression_len = data[index]; + index += 1 + compression_len; + + // Extensions length + if (index + 1 >= datalen) { + log_android(ANDROID_LOG_WARN, "TLS extensions %d/%d", index + 1, datalen); + return 0; + } + uint16_t edatalen = ((uint16_t) data[index] << 8) | data[index + 1]; + index += 2; + + if (edatalen == 0 || index + edatalen != datalen) { + log_android(ANDROID_LOG_WARN, "TLS extensions(2) len=%d %d/%d", + edatalen, index + edatalen, datalen); + return 0; + } + + uint16_t eindex = 0; + const uint8_t *edata = data + index; + while (eindex < edatalen) { + if (eindex + 1 >= edatalen) { + log_android(ANDROID_LOG_WARN, "TLS ext_type %d/%d", eindex + 1, edatalen); + return 0; + } + uint16_t ext_type = ((uint16_t) edata[eindex] << 8) | edata[eindex + 1]; + eindex += 2; + log_android(ANDROID_LOG_DEBUG, "TLS ext_type=%d", ext_type); + + if (eindex + 1 >= edatalen) { + log_android(ANDROID_LOG_WARN, "TLS ext_len %d/%d", eindex + 1, edatalen); + return 0; + } + uint16_t ext_len = ((uint16_t) edata[eindex] << 8) | edata[eindex + 1]; + eindex += 2; + + if (eindex + ext_len > edatalen) { + log_android(ANDROID_LOG_WARN, "TLS ext_len(2) %d/%d", eindex + ext_len, edatalen); + return 0; + } + + // https://datatracker.ietf.org/doc/html/rfc6066 + if (ext_type == TLS_EXTENSION_TYPE_SERVER_NAME) { + if (eindex + 1 >= edatalen) { + log_android(ANDROID_LOG_WARN, "TLS sni_len %d/%d", eindex + 1, edatalen); + return 0; + } + uint16_t sni_len = ((uint16_t) edata[eindex] << 8) | edata[eindex + 1]; + eindex += 2; + log_android(ANDROID_LOG_DEBUG, "TLS sni_len=%d", sni_len); + + + if (eindex + sni_len >= edatalen) { + log_android(ANDROID_LOG_WARN, "TLS sni_len(2) len=%d %d/%d", + sni_len, eindex + sni_len, edatalen); + return 0; + } + + if (eindex >= edatalen) { + log_android(ANDROID_LOG_WARN, "TLS sni_type %d/%d", eindex, edatalen); + return 0; + } + uint8_t sni_type = edata[eindex++]; + if (sni_type != 0) { + log_android(ANDROID_LOG_WARN, "TLS sni_type=%d", sni_type); + return 0; + } + + if (eindex + 1 >= edatalen) { + log_android(ANDROID_LOG_WARN, "TLS name_len %d/%d", eindex + 1, edatalen); + return 0; + } + uint16_t name_len = ((uint16_t) edata[eindex] << 8) | edata[eindex + 1]; + eindex += 2; + + if (eindex + name_len >= edatalen) { + log_android(ANDROID_LOG_WARN, "TLS name_len(2) len=%d %d/%d", + name_len, eindex + name_len, edatalen); + return 0; + } + if (name_len >= TLS_SNI_LENGTH) { + log_android(ANDROID_LOG_WARN, "TLS name_len(3) %d/%d", + name_len, TLS_SNI_LENGTH); + return 0; + } + + memcpy(server_name, edata + eindex, name_len); + server_name[name_len] = 0; + + return 1; + } + + eindex += ext_len; + } + + return 0; +}