aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Nardi <12729895+IvanNardi@users.noreply.github.com>2021-03-02 21:15:40 +0100
committerGitHub <noreply@github.com>2021-03-02 21:15:40 +0100
commit4c00ff89dfa64f1026c2f1d267dc081a86b45243 (patch)
tree42a219182bf7d40a7ab17fb9c856d4ab22432657
parent2b71a329c18026cb56d938db6f14014aaf682abd (diff)
DTLS: improve support (#1146)
* DTLS: add some pcap tests * DTLS: fix parsing of Client/Server Helllo message * DTLS: add parsing of server certificates
-rw-r--r--src/lib/ndpi_main.c1
-rw-r--r--src/lib/ndpi_utils.c1
-rw-r--r--src/lib/protocols/tls.c139
-rw-r--r--tests/pcap/dtls2.pcapbin0 -> 5495 bytes
-rw-r--r--tests/pcap/dtls_certificate_fragments.pcapbin0 -> 6322 bytes
-rw-r--r--tests/pcap/dtls_session_id_and_coockie_both.pcapbin0 -> 692 bytes
-rw-r--r--tests/result/dtls2.pcap.out8
-rw-r--r--tests/result/dtls_certificate_fragments.pcap.out8
-rw-r--r--tests/result/dtls_session_id_and_coockie_both.pcap.out8
9 files changed, 127 insertions, 38 deletions
diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c
index 5afdc4695..5d23ec514 100644
--- a/src/lib/ndpi_main.c
+++ b/src/lib/ndpi_main.c
@@ -6972,6 +6972,7 @@ uint8_t ndpi_connection_tracking(struct ndpi_detection_module_struct *ndpi_str,
switch(proto) {
case NDPI_PROTOCOL_TLS:
+ case NDPI_PROTOCOL_DTLS:
if(flow->l4.tcp.tls.certificate_processed) return(0);
if(flow->l4.tcp.tls.num_tls_blocks <= ndpi_str->num_tls_blocks_to_follow) {
diff --git a/src/lib/ndpi_utils.c b/src/lib/ndpi_utils.c
index d50e8ccae..75f7b903d 100644
--- a/src/lib/ndpi_utils.c
+++ b/src/lib/ndpi_utils.c
@@ -1290,6 +1290,7 @@ int ndpi_dpi2json(struct ndpi_detection_module_struct *ndpi_struct,
break;
case NDPI_PROTOCOL_TLS:
+ case NDPI_PROTOCOL_DTLS:
if(flow->protos.tls_quic_stun.tls_quic.ssl_version) {
char notBefore[32], notAfter[32];
struct tm a, b, *before = NULL, *after = NULL;
diff --git a/src/lib/protocols/tls.c b/src/lib/protocols/tls.c
index 5e3ae65b1..f9d96f55c 100644
--- a/src/lib/protocols/tls.c
+++ b/src/lib/protocols/tls.c
@@ -546,8 +546,9 @@ static void processCertificateElements(struct ndpi_detection_module_struct *ndpi
int processCertificate(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow) {
struct ndpi_packet_struct *packet = &flow->packet;
+ int is_dtls = packet->udp ? 1 : 0;
u_int32_t certificates_length, length = (packet->payload[1] << 16) + (packet->payload[2] << 8) + packet->payload[3];
- u_int16_t certificates_offset = 7;
+ u_int32_t certificates_offset = 7 + (is_dtls ? 8 : 0);
u_int8_t num_certificates_found = 0;
SHA1_CTX srv_cert_fingerprint_ctx ;
@@ -559,14 +560,16 @@ int processCertificate(struct ndpi_detection_module_struct *ndpi_struct,
packet->payload[3], packet->payload[4], packet->payload[5]);
#endif
- if((packet->payload_packet_len != (length + 4)) || (packet->payload[1] != 0x0)) {
+ if((packet->payload_packet_len != (length + 4 + (is_dtls ? 8 : 0))) || (packet->payload[1] != 0x0)) {
NDPI_SET_BIT(flow->risk, NDPI_MALFORMED_PACKET);
return(-1); /* Invalid length */
}
- certificates_length = (packet->payload[4] << 16) + (packet->payload[5] << 8) + packet->payload[6];
+ certificates_length = (packet->payload[certificates_offset - 3] << 16) +
+ (packet->payload[certificates_offset - 2] << 8) +
+ packet->payload[certificates_offset - 1];
- if((packet->payload[4] != 0x0) || ((certificates_length+3) != length)) {
+ if((packet->payload[certificates_offset - 3] != 0x0) || ((certificates_length+3) != length)) {
NDPI_SET_BIT(flow->risk, NDPI_MALFORMED_PACKET);
return(-2); /* Invalid length */
}
@@ -578,7 +581,7 @@ int processCertificate(struct ndpi_detection_module_struct *ndpi_struct,
/* Invalid lenght */
if((certificate_len == 0)
|| (packet->payload[certificates_offset] != 0x0)
- || ((certificates_offset+certificate_len) > (4+certificates_length))) {
+ || ((certificates_offset+certificate_len) > (4+certificates_length+(is_dtls ? 8 : 0)))) {
#ifdef DEBUG_TLS
printf("[TLS] Invalid length [certificate_len: %u][certificates_offset: %u][%u vs %u]\n",
certificate_len, certificates_offset,
@@ -667,6 +670,7 @@ int processCertificate(struct ndpi_detection_module_struct *ndpi_struct,
static int processTLSBlock(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow) {
struct ndpi_packet_struct *packet = &flow->packet;
+ int ret;
switch(packet->payload[0] /* block type */) {
case 0x01: /* Client Hello */
@@ -691,7 +695,12 @@ static int processTLSBlock(struct ndpi_detection_module_struct *ndpi_struct,
/* Important: populate the tls union fields only after
* ndpi_int_tls_add_connection has been called */
if(flow->l4.tcp.tls.hello_processed) {
- processCertificate(ndpi_struct, flow);
+ ret = processCertificate(ndpi_struct, flow);
+ if (ret != 1) {
+#ifdef DEBUG_TLS
+ printf("[TLS] Error processing certificate: %d\n", ret);
+#endif
+ }
flow->l4.tcp.tls.certificate_processed = 1;
}
break;
@@ -861,50 +870,97 @@ static int ndpi_search_tls_tcp(struct ndpi_detection_module_struct *ndpi_struct,
static int ndpi_search_tls_udp(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow) {
struct ndpi_packet_struct *packet = &flow->packet;
- // u_int8_t handshake_type;
u_int32_t handshake_len;
- u_int16_t p_len;
+ u_int16_t p_len, processed;
const u_int8_t *p;
+ u_int8_t no_dtls = 0, change_cipher_found = 0;
#ifdef DEBUG_TLS
printf("[TLS] %s()\n", __FUNCTION__);
#endif
- /* Consider only specific SSL packets (handshake) */
- if((packet->payload_packet_len < 17)
- || (packet->payload[0] != 0x16)
- || (packet->payload[1] != 0xfe) /* We ignore old DTLS versions */
- || ((packet->payload[2] != 0xff) && (packet->payload[2] != 0xfd))
- || ((ntohs(*((u_int16_t*)&packet->payload[11]))+13) != packet->payload_packet_len)
- ) {
- no_dtls:
+ /* Overwriting packet payload */
+ p = packet->payload, p_len = packet->payload_packet_len; /* Backup */
+
+ /* Split the element in blocks */
+ processed = 0;
+ while(processed + 13 < p_len) {
+ u_int32_t block_len;
+ const u_int8_t *block = (const u_int8_t *)&p[processed];
+ if((block[0] != 0x16 && block[0] != 0x14) || /* Handshake, change-cipher-spec */
+ (block[1] != 0xfe) || /* We ignore old DTLS versions */
+ ((block[2] != 0xff) && (block[2] != 0xfd))) {
+#ifdef DEBUG_TLS
+ printf("[TLS] DTLS invalid block 0x%x or old version 0x%x-0x%x-0x%x\n",
+ block[0], block[1], block[2], block[3]);
+#endif
+ no_dtls = 1;
+ break;
+ }
+ block_len = ntohs(*((u_int16_t*)&block[11]));
+#ifdef DEBUG_TLS
+ printf("[TLS] DTLS block len: %d\n", block_len);
+#endif
+ if (block_len == 0 || (processed + block_len + 12 >= p_len)) {
+#ifdef DEBUG_TLS
+ printf("[TLS] DTLS invalid block len %d (processed %d, p_len %d)\n",
+ block_len, processed, p_len);
+#endif
+ no_dtls = 1;
+ break;
+ }
+ /* We process only handshake msgs */
+ if(block[0] == 0x16) {
+ if (processed + block_len + 13 > p_len) {
+#ifdef DEBUG_TLS
+ printf("[TLS] DTLS invalid len %d %d %d\n", processed, block_len, p_len);
+#endif
+ no_dtls = 1;
+ break;
+ }
+ /* TODO: handle (certificate) fragments */
+ handshake_len = (block[14] << 16) + (block[15] << 8) + block[16];
+ if((handshake_len + 12) != block_len) {
+#ifdef DEBUG_TLS
+ printf("[TLS] DTLS invalid handshake_len %d, %d)\n",
+ handshake_len, block_len);
+#endif
+ no_dtls = 1;
+ break;
+ }
+ packet->payload = &block[13];
+ packet->payload_packet_len = block_len;
+ processTLSBlock(ndpi_struct, flow);
+ } else {
+ /* Change-cipher-spec: any subsequent block might be encrypted */
#ifdef DEBUG_TLS
- printf("[TLS] No DTLS found\n");
+ printf("[TLS] Change-cipher-spec\n");
#endif
+ change_cipher_found = 1;
+ processed += block_len + 13;
+ break;
+ }
- NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
- return(0); /* Giveup */
+ processed += block_len + 13;
+ }
+ if(processed != p_len) {
+#ifdef DEBUG_TLS
+ printf("[TLS] DTLS invalid processed len %d/%d (%d)\n", processed, p_len, change_cipher_found);
+#endif
+ if(!change_cipher_found)
+ no_dtls = 1;
}
-
- // handshake_type = packet->payload[13];
- handshake_len = (packet->payload[14] << 16) + (packet->payload[15] << 8) + packet->payload[16];
-
- if((handshake_len+25) != packet->payload_packet_len)
- goto no_dtls;
-
- /* Overwriting packet payload */
- p = packet->payload, p_len = packet->payload_packet_len; /* Backup */
- packet->payload = &packet->payload[13], packet->payload_packet_len -= 13;
-
- processTLSBlock(ndpi_struct, flow);
packet->payload = p;
packet->payload_packet_len = p_len; /* Restore */
- ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_DTLS);
-
- return(1); /* Keep working */
+ if(no_dtls || change_cipher_found) {
+ NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
+ return(0); /* That's all */
+ } else {
+ return(1); /* Keep working */
+ }
}
/* **************************************** */
@@ -1023,7 +1079,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
https://networkengineering.stackexchange.com/questions/55752/why-does-wireshark-show-version-tls-1-2-here-instead-of-tls-1-3
*/
if(packet->udp)
- offset += 1;
+ offset += session_id_len + 1;
else {
if(tls_version < 0x7F15 /* TLS 1.3 lacks of session id */)
offset += session_id_len+1;
@@ -1127,6 +1183,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
#endif
} else if(handshake_type == 0x01 /* Client Hello */) {
u_int16_t cipher_len, cipher_offset;
+ u_int8_t cookie_len = 0;
if((session_id_len+base_offset+3) > packet->payload_packet_len)
return(0); /* Not found */
@@ -1135,8 +1192,14 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
cipher_len = packet->payload[session_id_len+base_offset+2] + (packet->payload[session_id_len+base_offset+1] << 8);
cipher_offset = base_offset + session_id_len + 3;
} else {
- cipher_len = ntohs(*((u_int16_t*)&packet->payload[base_offset+2]));
- cipher_offset = base_offset+4;
+ cookie_len = packet->payload[base_offset+session_id_len+1];
+#ifdef DEBUG_TLS
+ printf("[JA3] Client: DTLS cookie len %d\n", cookie_len);
+#endif
+ if((session_id_len+base_offset+cookie_len+4) > packet->payload_packet_len)
+ return(0); /* Not found */
+ cipher_len = ntohs(*((u_int16_t*)&packet->payload[base_offset+session_id_len+cookie_len+2]));
+ cipher_offset = base_offset + session_id_len + cookie_len + 4;
}
#ifdef DEBUG_TLS
@@ -1175,7 +1238,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
#endif
}
- offset = base_offset + session_id_len + cipher_len + 2;
+ offset = base_offset + session_id_len + cookie_len + cipher_len + 2;
if(offset < total_len) {
u_int16_t compression_len;
diff --git a/tests/pcap/dtls2.pcap b/tests/pcap/dtls2.pcap
new file mode 100644
index 000000000..68be6c38d
--- /dev/null
+++ b/tests/pcap/dtls2.pcap
Binary files differ
diff --git a/tests/pcap/dtls_certificate_fragments.pcap b/tests/pcap/dtls_certificate_fragments.pcap
new file mode 100644
index 000000000..5551b5cab
--- /dev/null
+++ b/tests/pcap/dtls_certificate_fragments.pcap
Binary files differ
diff --git a/tests/pcap/dtls_session_id_and_coockie_both.pcap b/tests/pcap/dtls_session_id_and_coockie_both.pcap
new file mode 100644
index 000000000..932f96e44
--- /dev/null
+++ b/tests/pcap/dtls_session_id_and_coockie_both.pcap
Binary files differ
diff --git a/tests/result/dtls2.pcap.out b/tests/result/dtls2.pcap.out
new file mode 100644
index 000000000..cf26153f5
--- /dev/null
+++ b/tests/result/dtls2.pcap.out
@@ -0,0 +1,8 @@
+DTLS 30 4991 1
+
+JA3 Host Stats:
+ IP Address # JA3C
+ 1 61.68.110.153 1
+
+
+ 1 UDP 61.68.110.153:53045 <-> 212.32.214.39:61457 [proto: 30/DTLS][cat: Web/5][14 pkts/2246 bytes <-> 16 pkts/2745 bytes][Goodput ratio: 74/75][382.15 sec][bytes ratio: -0.100 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 2/241 27857/28359 60550/60551 26256/25033][Pkt Len c2s/s2c min/avg/max/stddev: 123/102 160/172 325/867 46/180][Risk: ** Weak TLS cipher **** TLS (probably) not carrying HTTPS **** SNI TLS extension was missing **][DTLSv1.0][JA3C: 1b45c913a0c0fde5f263502e65999485][JA3S: 749bd1edea60396ffaa65213b7971718 (WEAK)][Issuer: C=US][Subject: C=US, CN=*.relay.ros.rockstargames.com][Validity: 2014-09-12 21:31:19 - 2037-02-15 21:31:19][Cipher: TLS_RSA_WITH_AES_256_CBC_SHA][PLAIN TEXT (140912213119Z)][Plen Bins: 0,3,43,46,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
diff --git a/tests/result/dtls_certificate_fragments.pcap.out b/tests/result/dtls_certificate_fragments.pcap.out
new file mode 100644
index 000000000..e9461e6c9
--- /dev/null
+++ b/tests/result/dtls_certificate_fragments.pcap.out
@@ -0,0 +1,8 @@
+DTLS 20 5978 1
+
+JA3 Host Stats:
+ IP Address # JA3C
+ 1 10.186.198.149 1
+
+
+ 1 UDP 10.186.198.149:39347 <-> 35.210.59.134:44443 [proto: 30/DTLS][cat: Web/5][11 pkts/2624 bytes <-> 9 pkts/3354 bytes][Goodput ratio: 82/89][2.92 sec][bytes ratio: -0.122 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/0 324/76 2179/186 659/75][Pkt Len c2s/s2c min/avg/max/stddev: 167/90 239/373 416/1454 97/388][Risk: ** Weak TLS cipher **** TLS (probably) not carrying HTTPS **** SNI TLS extension was missing **][DTLSv1.0][JA3C: 3c3d129780d0066cd8936a6291a8d44f][JA3S: d45798bc098cd930de7eb2f5f866e994 (WEAK)][Cipher: TLS_RSA_WITH_AES_256_CBC_SHA][PLAIN TEXT (Opera Software ASA1)][Plen Bins: 0,5,0,35,5,10,10,0,10,10,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0]
diff --git a/tests/result/dtls_session_id_and_coockie_both.pcap.out b/tests/result/dtls_session_id_and_coockie_both.pcap.out
new file mode 100644
index 000000000..d729967ea
--- /dev/null
+++ b/tests/result/dtls_session_id_and_coockie_both.pcap.out
@@ -0,0 +1,8 @@
+DTLS 4 604 1
+
+JA3 Host Stats:
+ IP Address # JA3C
+ 1 185.196.113.239 1
+
+
+ 1 UDP 185.196.113.239:50257 <-> 223.116.105.247:44443 [proto: 30/DTLS][cat: Web/5][2 pkts/302 bytes <-> 2 pkts/302 bytes][Goodput ratio: 72/72][0.06 sec][Risk: ** TLS (probably) not carrying HTTPS **** SNI TLS extension was missing **][DTLSv1.2][JA3C: e15c510766789ed8f49de0e37951c1da][JA3S: a1d48eca741e476d8ee735578a26bdbd][Cipher: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384][Plen Bins: 0,25,0,50,0,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]