Browse Source

tls: respect tls record length

pull/172/head
bol-van 1 year ago
parent
commit
a33848b212
  1. BIN
      binaries/aarch64/nfqws
  2. BIN
      binaries/aarch64/tpws
  3. BIN
      binaries/arm/nfqws
  4. BIN
      binaries/arm/tpws
  5. BIN
      binaries/freebsd-x64/dvtws
  6. BIN
      binaries/freebsd-x64/tpws
  7. BIN
      binaries/mac64/tpws
  8. BIN
      binaries/mips32r1-lsb/nfqws
  9. BIN
      binaries/mips32r1-lsb/tpws
  10. BIN
      binaries/mips32r1-msb/nfqws
  11. BIN
      binaries/mips32r1-msb/tpws
  12. BIN
      binaries/mips64r2-msb/nfqws
  13. BIN
      binaries/mips64r2-msb/tpws
  14. BIN
      binaries/ppc/nfqws
  15. BIN
      binaries/ppc/tpws
  16. BIN
      binaries/x86/nfqws
  17. BIN
      binaries/x86/tpws
  18. BIN
      binaries/x86_64/nfqws
  19. BIN
      binaries/x86_64/tpws
  20. BIN
      binaries/x86_64/tpws_wsl.tgz
  21. 3
      nfq/protocol.c
  22. 107
      nfq/strpool.c
  23. 29
      nfq/strpool.h
  24. 127
      tpws/protocol.c
  25. 11
      tpws/protocol.h
  26. 107
      tpws/strpool.c
  27. 29
      tpws/strpool.h
  28. 6
      tpws/tamper.c

BIN
binaries/aarch64/nfqws

Binary file not shown.

BIN
binaries/aarch64/tpws

Binary file not shown.

BIN
binaries/arm/nfqws

Binary file not shown.

BIN
binaries/arm/tpws

Binary file not shown.

BIN
binaries/freebsd-x64/dvtws

Binary file not shown.

BIN
binaries/freebsd-x64/tpws

Binary file not shown.

BIN
binaries/mac64/tpws

Binary file not shown.

BIN
binaries/mips32r1-lsb/nfqws

Binary file not shown.

BIN
binaries/mips32r1-lsb/tpws

Binary file not shown.

BIN
binaries/mips32r1-msb/nfqws

Binary file not shown.

BIN
binaries/mips32r1-msb/tpws

Binary file not shown.

BIN
binaries/mips64r2-msb/nfqws

Binary file not shown.

BIN
binaries/mips64r2-msb/tpws

Binary file not shown.

BIN
binaries/ppc/nfqws

Binary file not shown.

BIN
binaries/ppc/tpws

Binary file not shown.

BIN
binaries/x86/nfqws

Binary file not shown.

BIN
binaries/x86/tpws

Binary file not shown.

BIN
binaries/x86_64/nfqws

Binary file not shown.

BIN
binaries/x86_64/tpws

Binary file not shown.

BIN
binaries/x86_64/tpws_wsl.tgz

Binary file not shown.

3
nfq/protocol.c

@ -200,7 +200,10 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
// u8 ContentType: Handshake // u8 ContentType: Handshake
// u16 Version: TLS1.0 // u16 Version: TLS1.0
// u16 Length // u16 Length
size_t reclen;
if (!IsTLSClientHello(data, len, bPartialIsOK)) return false; if (!IsTLSClientHello(data, len, bPartialIsOK)) return false;
reclen=TLSRecordLen(data);
if (reclen<len) len=reclen; // correct len if it has more data than the first tls record has
return TLSFindExtInHandshake(data + 5, len - 5, type, ext, len_ext, bPartialIsOK); return TLSFindExtInHandshake(data + 5, len - 5, type, ext, len_ext, bPartialIsOK);
} }
static bool TLSExtractHostFromExt(const uint8_t *ext, size_t elen, char *host, size_t len_host) static bool TLSExtractHostFromExt(const uint8_t *ext, size_t elen, char *host, size_t len_host)

107
nfq/strpool.c

@ -0,0 +1,107 @@
#define _GNU_SOURCE
#include "strpool.h"
#include <string.h>
#include <stdlib.h>
#undef uthash_nonfatal_oom
#define uthash_nonfatal_oom(elt) ut_oom_recover(elt)
static bool oom = false;
static void ut_oom_recover(strpool *elem)
{
oom = true;
}
// for zero terminated strings
bool StrPoolAddStr(strpool **pp, const char *s)
{
strpool *elem;
if (!(elem = (strpool*)malloc(sizeof(strpool))))
return false;
if (!(elem->str = strdup(s)))
{
free(elem);
return false;
}
oom = false;
HASH_ADD_KEYPTR(hh, *pp, elem->str, strlen(elem->str), elem);
if (oom)
{
free(elem->str);
free(elem);
return false;
}
return true;
}
// for not zero terminated strings
bool StrPoolAddStrLen(strpool **pp, const char *s, size_t slen)
{
strpool *elem;
if (!(elem = (strpool*)malloc(sizeof(strpool))))
return false;
if (!(elem->str = malloc(slen + 1)))
{
free(elem);
return false;
}
memcpy(elem->str, s, slen);
elem->str[slen] = 0;
oom = false;
HASH_ADD_KEYPTR(hh, *pp, elem->str, strlen(elem->str), elem);
if (oom)
{
free(elem->str);
free(elem);
return false;
}
return true;
}
bool StrPoolCheckStr(strpool *p, const char *s)
{
strpool *elem;
HASH_FIND_STR(p, s, elem);
return elem != NULL;
}
void StrPoolDestroy(strpool **p)
{
strpool *elem, *tmp;
HASH_ITER(hh, *p, elem, tmp) {
free(elem->str);
HASH_DEL(*p, elem);
free(elem);
}
*p = NULL;
}
bool strlist_add(struct str_list_head *head, const char *filename)
{
struct str_list *entry = malloc(sizeof(struct str_list));
if (!entry) return false;
entry->str = strdup(filename);
if (!entry->str)
{
free(entry);
return false;
}
LIST_INSERT_HEAD(head, entry, next);
return true;
}
static void strlist_entry_destroy(struct str_list *entry)
{
if (entry->str) free(entry->str);
free(entry);
}
void strlist_destroy(struct str_list_head *head)
{
struct str_list *entry;
while ((entry = LIST_FIRST(head)))
{
LIST_REMOVE(entry, next);
strlist_entry_destroy(entry);
}
}

29
nfq/strpool.h

@ -0,0 +1,29 @@
#pragma once
#include <stdbool.h>
#include <ctype.h>
#include <sys/queue.h>
//#define HASH_BLOOM 20
#define HASH_NONFATAL_OOM 1
#define HASH_FUNCTION HASH_BER
#include "uthash.h"
typedef struct strpool {
char *str; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} strpool;
void StrPoolDestroy(strpool **p);
bool StrPoolAddStr(strpool **pp,const char *s);
bool StrPoolAddStrLen(strpool **pp,const char *s,size_t slen);
bool StrPoolCheckStr(strpool *p,const char *s);
struct str_list {
char *str;
LIST_ENTRY(str_list) next;
};
LIST_HEAD(str_list_head, str_list);
bool strlist_add(struct str_list_head *head, const char *filename);
void strlist_destroy(struct str_list_head *head);

127
tpws/protocol.c

@ -110,17 +110,27 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
bool IsTLSClientHello(const uint8_t *data, size_t len) uint16_t TLSRecordDataLen(const uint8_t *data)
{ {
return len>=6 && data[0]==0x16 && data[1]==0x03 && data[2]>=0x01 && data[2]<=0x03 && data[5]==0x01 && (pntoh16(data+3)+5)<=len; return pntoh16(data + 3);
} }
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext) size_t TLSRecordLen(const uint8_t *data)
{
return TLSRecordDataLen(data) + 5;
}
bool IsTLSRecordFull(const uint8_t *data, size_t len)
{
return TLSRecordLen(data)<=len;
}
bool IsTLSClientHello(const uint8_t *data, size_t len, bool bPartialIsOK)
{
return len >= 6 && data[0] == 0x16 && data[1] == 0x03 && data[2] >= 0x01 && data[2] <= 0x03 && data[5] == 0x01 && (bPartialIsOK || TLSRecordLen(data) <= len);
}
// bPartialIsOK=true - accept partial packets not containing the whole TLS message
bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK)
{ {
// +0 // +0
// u8 ContentType: Handshake
// u16 Version: TLS1.0
// u16 Length
// +5
// u8 HandshakeType: ClientHello // u8 HandshakeType: ClientHello
// u24 Length // u24 Length
// u16 Version // u16 Version
@ -133,35 +143,46 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
// <CompressionMethods> // <CompressionMethods>
// u16 ExtensionsLength // u16 ExtensionsLength
size_t l,ll; size_t l, ll;
l = 1+2+2+1+3+2+32; l = 1 + 3 + 2 + 32;
// SessionIDLength // SessionIDLength
if (len<(l+1)) return false; if (len < (l + 1)) return false;
ll = data[6]<<16 | data[7]<<8 | data[8]; // HandshakeProtocol length if (!bPartialIsOK)
if (len<(ll+9)) return false; {
l += data[l]+1; ll = data[1] << 16 | data[2] << 8 | data[3]; // HandshakeProtocol length
if (len < (ll + 4)) return false;
}
l += data[l] + 1;
// CipherSuitesLength // CipherSuitesLength
if (len<(l+2)) return false; if (len < (l + 2)) return false;
l += pntoh16(data+l)+2; l += pntoh16(data + l) + 2;
// CompressionMethodsLength // CompressionMethodsLength
if (len<(l+1)) return false; if (len < (l + 1)) return false;
l += data[l]+1; l += data[l] + 1;
// ExtensionsLength // ExtensionsLength
if (len<(l+2)) return false; if (len < (l + 2)) return false;
data+=l; len-=l; data += l; len -= l;
l=pntoh16(data); l = pntoh16(data);
data+=2; len-=2; data += 2; len -= 2;
if (len<l) return false;
if (bPartialIsOK)
{
if (len < l) l = len;
}
else
{
if (len < l) return false;
}
while(l>=4) while (l >= 4)
{ {
uint16_t etype=pntoh16(data); uint16_t etype = pntoh16(data);
size_t elen=pntoh16(data+2); size_t elen = pntoh16(data + 2);
data+=4; l-=4; data += 4; l -= 4;
if (l<elen) break; if (l < elen) break;
if (etype==type) if (etype == type)
{ {
if (ext && len_ext) if (ext && len_ext)
{ {
@ -170,29 +191,53 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
} }
return true; return true;
} }
data+=elen; l-=elen; data += elen; l -= elen;
} }
return false; return false;
} }
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host) bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK)
{
// +0
// u8 ContentType: Handshake
// u16 Version: TLS1.0
// u16 Length
size_t reclen;
if (!IsTLSClientHello(data, len, bPartialIsOK)) return false;
reclen=TLSRecordLen(data);
if (reclen<len) len=reclen; // correct len if it has more data than the first tls record has
return TLSFindExtInHandshake(data + 5, len - 5, type, ext, len_ext, bPartialIsOK);
}
static bool TLSExtractHostFromExt(const uint8_t *ext, size_t elen, char *host, size_t len_host)
{ {
const uint8_t *ext;
size_t elen;
if (!TLSFindExt(data,len,0,&ext,&elen)) return false;
// u16 data+0 - name list length // u16 data+0 - name list length
// u8 data+2 - server name type. 0=host_name // u8 data+2 - server name type. 0=host_name
// u16 data+3 - server name length // u16 data+3 - server name length
if (elen<5 || ext[2]!=0) return false; if (elen < 5 || ext[2] != 0) return false;
size_t slen = pntoh16(ext+3); size_t slen = pntoh16(ext + 3);
ext+=5; elen-=5; ext += 5; elen -= 5;
if (slen<elen) return false; if (slen < elen) return false;
if (ext && len_host) if (ext && len_host)
{ {
if (slen>=len_host) slen=len_host-1; if (slen >= len_host) slen = len_host - 1;
for(size_t i=0;i<slen;i++) host[i]=tolower(ext[i]); for (size_t i = 0; i < slen; i++) host[i] = tolower(ext[i]);
host[slen]=0; host[slen] = 0;
} }
return true; return true;
} }
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK)
{
const uint8_t *ext;
size_t elen;
if (!TLSFindExt(data, len, 0, &ext, &elen, bPartialIsOK)) return false;
return TLSExtractHostFromExt(ext, elen, host, len_host);
}
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK)
{
const uint8_t *ext;
size_t elen;
if (!TLSFindExtInHandshake(data, len, 0, &ext, &elen, bPartialIsOK)) return false;
return TLSExtractHostFromExt(ext, elen, host, len_host);
}

11
tpws/protocol.h

@ -15,6 +15,11 @@ int HttpReplyCode(const uint8_t *data, size_t len);
// must be pre-checked by IsHttpReply // must be pre-checked by IsHttpReply
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host); bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host);
bool IsTLSClientHello(const uint8_t *data, size_t len); uint16_t TLSRecordDataLen(const uint8_t *data);
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext); size_t TLSRecordLen(const uint8_t *data);
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host); bool IsTLSRecordFull(const uint8_t *data, size_t len);
bool IsTLSClientHello(const uint8_t *data, size_t len, bool bPartialIsOK);
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK);
bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK);
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);

107
tpws/strpool.c

@ -0,0 +1,107 @@
#define _GNU_SOURCE
#include "strpool.h"
#include <string.h>
#include <stdlib.h>
#undef uthash_nonfatal_oom
#define uthash_nonfatal_oom(elt) ut_oom_recover(elt)
static bool oom = false;
static void ut_oom_recover(strpool *elem)
{
oom = true;
}
// for zero terminated strings
bool StrPoolAddStr(strpool **pp, const char *s)
{
strpool *elem;
if (!(elem = (strpool*)malloc(sizeof(strpool))))
return false;
if (!(elem->str = strdup(s)))
{
free(elem);
return false;
}
oom = false;
HASH_ADD_KEYPTR(hh, *pp, elem->str, strlen(elem->str), elem);
if (oom)
{
free(elem->str);
free(elem);
return false;
}
return true;
}
// for not zero terminated strings
bool StrPoolAddStrLen(strpool **pp, const char *s, size_t slen)
{
strpool *elem;
if (!(elem = (strpool*)malloc(sizeof(strpool))))
return false;
if (!(elem->str = malloc(slen + 1)))
{
free(elem);
return false;
}
memcpy(elem->str, s, slen);
elem->str[slen] = 0;
oom = false;
HASH_ADD_KEYPTR(hh, *pp, elem->str, strlen(elem->str), elem);
if (oom)
{
free(elem->str);
free(elem);
return false;
}
return true;
}
bool StrPoolCheckStr(strpool *p, const char *s)
{
strpool *elem;
HASH_FIND_STR(p, s, elem);
return elem != NULL;
}
void StrPoolDestroy(strpool **p)
{
strpool *elem, *tmp;
HASH_ITER(hh, *p, elem, tmp) {
free(elem->str);
HASH_DEL(*p, elem);
free(elem);
}
*p = NULL;
}
bool strlist_add(struct str_list_head *head, const char *filename)
{
struct str_list *entry = malloc(sizeof(struct str_list));
if (!entry) return false;
entry->str = strdup(filename);
if (!entry->str)
{
free(entry);
return false;
}
LIST_INSERT_HEAD(head, entry, next);
return true;
}
static void strlist_entry_destroy(struct str_list *entry)
{
if (entry->str) free(entry->str);
free(entry);
}
void strlist_destroy(struct str_list_head *head)
{
struct str_list *entry;
while ((entry = LIST_FIRST(head)))
{
LIST_REMOVE(entry, next);
strlist_entry_destroy(entry);
}
}

29
tpws/strpool.h

@ -0,0 +1,29 @@
#pragma once
#include <stdbool.h>
#include <ctype.h>
#include <sys/queue.h>
//#define HASH_BLOOM 20
#define HASH_NONFATAL_OOM 1
#define HASH_FUNCTION HASH_BER
#include "uthash.h"
typedef struct strpool {
char *str; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} strpool;
void StrPoolDestroy(strpool **p);
bool StrPoolAddStr(strpool **pp,const char *s);
bool StrPoolAddStrLen(strpool **pp,const char *s,size_t slen);
bool StrPoolCheckStr(strpool *p,const char *s);
struct str_list {
char *str;
LIST_ENTRY(str_list) next;
};
LIST_HEAD(str_list_head, str_list);
bool strlist_add(struct str_list_head *head, const char *filename);
void strlist_destroy(struct str_list_head *head);

6
tpws/tamper.c

@ -215,7 +215,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
VPRINT("Not acting on this request") VPRINT("Not acting on this request")
} }
} }
else if (IsTLSClientHello(segment,*size)) else if (IsTLSClientHello(segment,*size,false))
{ {
size_t tpos=0,elen; size_t tpos=0,elen;
const uint8_t *ext; const uint8_t *ext;
@ -224,7 +224,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
VPRINT("packet contains TLS ClientHello") VPRINT("packet contains TLS ClientHello")
// we need host only if hostlist is present // we need host only if hostlist is present
if ((params.hostlist || params.hostlist_exclude) && TLSHelloExtractHost((uint8_t*)segment,*size,Host,sizeof(Host))) if ((params.hostlist || params.hostlist_exclude) && TLSHelloExtractHost((uint8_t*)segment,*size,Host,sizeof(Host),false))
{ {
VPRINT("hostname: %s",Host) VPRINT("hostname: %s",Host)
bHaveHost = true; bHaveHost = true;
@ -239,7 +239,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
switch(params.tlsrec) switch(params.tlsrec)
{ {
case tlsrec_sni: case tlsrec_sni:
if (TLSFindExt(segment,*size,0,&ext,&elen)) if (TLSFindExt(segment,*size,0,&ext,&elen,false))
tpos = ext-segment+1; // between typical 1st and 2nd char of hostname tpos = ext-segment+1; // between typical 1st and 2nd char of hostname
break; break;
case tlsrec_pos: case tlsrec_pos:

Loading…
Cancel
Save