mirror of
https://github.com/ValdikSS/GoodbyeDPI.git
synced 2024-12-31 07:04:21 +00:00
Improved tls parsing
This commit is contained in:
parent
4a82fd442d
commit
12e5d77375
143
src/goodbyedpi.c
143
src/goodbyedpi.c
@ -19,6 +19,7 @@
|
|||||||
#include "ttltrack.h"
|
#include "ttltrack.h"
|
||||||
#include "blackwhitelist.h"
|
#include "blackwhitelist.h"
|
||||||
#include "fakepackets.h"
|
#include "fakepackets.h"
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
// My mingw installation does not load inet_pton definition for some reason
|
// My mingw installation does not load inet_pton definition for some reason
|
||||||
WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pAddr);
|
WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pAddr);
|
||||||
@ -387,48 +388,136 @@ static int find_header_and_get_info(const char *pktdata, unsigned int pktlen,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int contains_tls_record_with_client_hello(const char* pktdata, unsigned int pktlen) {
|
||||||
|
// check min header sizes
|
||||||
|
if (pktlen < TLS_RECORD_HEADER_SIZE + TLS_MESSAGE_HEADER_SIZE) {
|
||||||
|
debug("Packet too small for TLS record and message header\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
// check content type handshake
|
||||||
|
if (pktdata[0] != TLS_CONTENT_TYPE_HANDSHAKE) {
|
||||||
|
debug("Packet does not contain TLS handshake\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
// check if record header matches any TLS version
|
||||||
|
if (
|
||||||
|
// TLS 1.0 Version is also used for backwards compatibility
|
||||||
|
memcmp(pktdata+1, TLS_1_0_VERSION, TLS_VERSION_LENGTH) == 1 &&
|
||||||
|
memcmp(pktdata+1, TLS_1_1_VERSION, TLS_VERSION_LENGTH) == 1 &&
|
||||||
|
memcmp(pktdata+1, TLS_1_2_AND_1_3_VERSION, TLS_VERSION_LENGTH) == 1) {
|
||||||
|
debug("Packet does not contain TLS 1.0, 1.1, 1.2 or 1.3\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
// check if handshake message contains client hello
|
||||||
|
if (pktdata[5] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
|
||||||
|
debug("Packet does not contain TLS client hello\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
// looks good otherwise
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Very crude Server Name Indication (TLS ClientHello hostname) extractor.
|
* Extracts the (first) hostname in the SNI of the given packet. Assumes that the packet is a TLS Client Hello.
|
||||||
*/
|
*/
|
||||||
static int extract_sni(const char *pktdata, unsigned int pktlen,
|
static int extract_sni(const char *pktdata, unsigned int pktlen,
|
||||||
char **hostnameaddr, unsigned int *hostnamelen) {
|
char **hostnameaddr, unsigned int *hostnamelen) {
|
||||||
unsigned int ptr = 0;
|
|
||||||
unsigned const char *d = (unsigned const char *)pktdata;
|
|
||||||
unsigned const char *hnaddr = 0;
|
|
||||||
int hnlen = 0;
|
|
||||||
|
|
||||||
while (ptr + 8 < pktlen) {
|
unsigned const char *d = (unsigned const char *)pktdata;
|
||||||
/* Search for specific Extensions sequence */
|
unsigned const char *host_name_address = 0;
|
||||||
if (d[ptr] == '\0' && d[ptr+1] == '\0' && d[ptr+2] == '\0' &&
|
unsigned int host_name_length = 0;
|
||||||
d[ptr+4] == '\0' && d[ptr+6] == '\0' && d[ptr+7] == '\0' &&
|
|
||||||
/* Check Extension length, Server Name list length
|
// offset for first length_field to parse
|
||||||
* and Server Name length relations
|
unsigned int p = TLS_RECORD_HEADER_SIZE + TLS_MESSAGE_HEADER_SIZE + TLS_VERSION_LENGTH + TLS_RANDOM_LENGTH;
|
||||||
*/
|
|
||||||
d[ptr+3] - d[ptr+5] == 2 && d[ptr+5] - d[ptr+8] == 3)
|
if (p + TLS_SESSION_ID_LENGTH >= pktlen) {
|
||||||
{
|
debug("Packet too small for header\n");
|
||||||
if (ptr + 8 + d[ptr+8] > pktlen) {
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
hnaddr = &d[ptr+9];
|
|
||||||
hnlen = d[ptr+8];
|
// skip session id
|
||||||
|
unsigned int session_id_length = d[p];
|
||||||
|
p += (session_id_length + TLS_SESSION_ID_LENGTH);
|
||||||
|
|
||||||
|
if (p + TLS_CIPHER_SUITES_LENGTH >= pktlen) {
|
||||||
|
debug("Packet too small for session id\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip cipher suites
|
||||||
|
unsigned int cipher_suites_length = (unsigned int)(d[p] << 8) + d[p+1];
|
||||||
|
p += (cipher_suites_length + TLS_CIPHER_SUITES_LENGTH);
|
||||||
|
|
||||||
|
if (p + TLS_COMPRESSION_METHODS_LENGTH >= pktlen) {
|
||||||
|
debug("Packet too small for cipher suites\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip compression methods
|
||||||
|
unsigned int compression_methods_length = d[p];
|
||||||
|
p += (compression_methods_length + TLS_COMPRESSION_METHODS_LENGTH);
|
||||||
|
|
||||||
|
if (p + TLS_EXTENSIONS_LENGTH >= pktlen) {
|
||||||
|
debug("Packet too small for compression methods\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int extensions_length = (unsigned int)(d[p] << 8) + d[p+1];
|
||||||
|
p += TLS_EXTENSIONS_LENGTH;
|
||||||
|
|
||||||
|
while (p + TLS_EXTENSION_TYPE_LENGTH + TLS_EXTENSION_LENGTH_LENGTH < min(pktlen, p + extensions_length)) {
|
||||||
|
|
||||||
|
if (memcmp(d+p, TLS_EXTENSION_TYPE_SERVER_NAME, TLS_EXTENSION_TYPE_LENGTH) == 0) {
|
||||||
|
// finally extract sni
|
||||||
|
p += TLS_EXTENSION_TYPE_LENGTH + TLS_EXTENSION_LENGTH_LENGTH;
|
||||||
|
|
||||||
|
if (p + TLS_SNI_HEADER_LENGTH >= pktlen) {
|
||||||
|
debug("Packet too small for extension header\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
unsigned int host_name_type = d[p+TLS_EXTENSION_TYPE_LENGTH];
|
||||||
|
|
||||||
|
if (host_name_type != TLS_SNI_HOST_NAME_TYPE) {
|
||||||
|
debug("SNI type is not host name\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
host_name_length = (unsigned int)(d[p + TLS_EXTENSION_TYPE_LENGTH + 1] << 8) + d[p + TLS_EXTENSION_TYPE_LENGTH + 2];
|
||||||
|
p += TLS_SNI_HEADER_LENGTH;
|
||||||
|
|
||||||
|
if (p + host_name_length >= pktlen) {
|
||||||
|
debug("Packet too small for host name\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
host_name_address = &d[p];
|
||||||
/* Limit hostname size up to 253 bytes */
|
/* Limit hostname size up to 253 bytes */
|
||||||
if (hnlen < 3 || hnlen > HOST_MAXLEN) {
|
if (host_name_length < 3) {
|
||||||
|
debug("Host name too short\n");
|
||||||
|
return FALSE;
|
||||||
|
} else if (host_name_length > HOST_MAXLEN) {
|
||||||
|
debug("Host name too long\n");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
/* Validate that hostname has only ascii lowercase characters */
|
|
||||||
for (int i=0; i<hnlen; i++) {
|
*hostnameaddr = (char*)host_name_address;
|
||||||
|
*hostnamelen = host_name_length;
|
||||||
|
|
||||||
|
// TODO: is this necessary? if yes: why? domain names are case insensitive
|
||||||
|
/* for (int i=0; i<hnlen; i++) {
|
||||||
if (!( (hnaddr[i] >= '0' && hnaddr[i] <= '9') ||
|
if (!( (hnaddr[i] >= '0' && hnaddr[i] <= '9') ||
|
||||||
(hnaddr[i] >= 'a' && hnaddr[i] <= 'z') ||
|
(hnaddr[i] >= 'a' && hnaddr[i] <= 'z') ||
|
||||||
hnaddr[i] == '.' || hnaddr[i] == '-'))
|
hnaddr[i] == '.' || hnaddr[i] == '-'))
|
||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
*hostnameaddr = (char*)hnaddr;
|
|
||||||
*hostnamelen = (unsigned int)hnlen;
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
} else {
|
||||||
|
// skip extension
|
||||||
|
unsigned int extension_length = (unsigned int)(d[p+TLS_EXTENSION_TYPE_LENGTH] << 8) + d[p+TLS_EXTENSION_TYPE_LENGTH+1];
|
||||||
|
p += TLS_EXTENSION_TYPE_LENGTH + TLS_EXTENSION_LENGTH_LENGTH + extension_length;
|
||||||
}
|
}
|
||||||
ptr++;
|
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -1128,8 +1217,10 @@ int main(int argc, char *argv[]) {
|
|||||||
* In case of Window Size fragmentation=2, we'll receive only 2 byte packet.
|
* In case of Window Size fragmentation=2, we'll receive only 2 byte packet.
|
||||||
* But if the packet is more than 2 bytes, check ClientHello byte.
|
* But if the packet is more than 2 bytes, check ClientHello byte.
|
||||||
*/
|
*/
|
||||||
if ((packet_dataLen == 2 && memcmp(packet_data, "\x16\x03", 2) == 0) ||
|
if (
|
||||||
(packet_dataLen >= 3 && memcmp(packet_data, "\x16\x03\x01", 3) == 0))
|
// TODO: feedback on when this triggers and whether this is necessary, if we cannot check for sni do we want to circumvent?
|
||||||
|
(packet_dataLen == 2 && memcmp(packet_data, "\x16\x03", 2) == 0) ||
|
||||||
|
(packet_dataLen >= 3 && contains_tls_record_with_client_hello(packet_data, packet_dataLen)))
|
||||||
{
|
{
|
||||||
if (do_blacklist) {
|
if (do_blacklist) {
|
||||||
sni_ok = extract_sni(packet_data, packet_dataLen,
|
sni_ok = extract_sni(packet_data, packet_dataLen,
|
||||||
|
23
src/tls.h
Normal file
23
src/tls.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#define TLS_CONTENT_TYPE_HANDSHAKE 22
|
||||||
|
#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 1
|
||||||
|
#define TLS_SNI_HOST_NAME_TYPE 0
|
||||||
|
|
||||||
|
#define TLS_1_0_VERSION "\x03\x01"
|
||||||
|
#define TLS_1_1_VERSION "\x03\x02"
|
||||||
|
#define TLS_1_2_AND_1_3_VERSION "\x03\x03"
|
||||||
|
#define TLS_EXTENSION_TYPE_SERVER_NAME "\x00\x00"
|
||||||
|
|
||||||
|
#define TLS_CONTENT_TYPE_LENGTH 1
|
||||||
|
#define TLS_VERSION_LENGTH 2
|
||||||
|
#define TLS_RECORD_LENGTH_LENGTH 2
|
||||||
|
#define TLS_RANDOM_LENGTH 32
|
||||||
|
#define TLS_SESSION_ID_LENGTH 1
|
||||||
|
#define TLS_CIPHER_SUITES_LENGTH 2
|
||||||
|
#define TLS_COMPRESSION_METHODS_LENGTH 1
|
||||||
|
#define TLS_EXTENSIONS_LENGTH 2
|
||||||
|
#define TLS_EXTENSION_TYPE_LENGTH 2
|
||||||
|
#define TLS_EXTENSION_LENGTH_LENGTH 2
|
||||||
|
#define TLS_SNI_HEADER_LENGTH 5
|
||||||
|
|
||||||
|
#define TLS_RECORD_HEADER_SIZE TLS_CONTENT_TYPE_LENGTH + TLS_VERSION_LENGTH + TLS_RECORD_LENGTH_LENGTH
|
||||||
|
#define TLS_MESSAGE_HEADER_SIZE 4
|
Loading…
Reference in New Issue
Block a user