Skip to content

Commit

Permalink
TLS: add support for DTLS (over STUN) over TCP
Browse files Browse the repository at this point in the history
TODO: TCP reassembler on top of UDP reassembler

See: ntop#2414
  • Loading branch information
IvanNardi committed May 29, 2024
1 parent 81e42b7 commit 3c14145
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 61 deletions.
99 changes: 47 additions & 52 deletions src/lib/protocols/stun.c
Original file line number Diff line number Diff line change
Expand Up @@ -698,65 +698,60 @@ static int stun_search_again(struct ndpi_detection_module_struct *ndpi_struct,
positives. In that case, the TLS dissector doesn't set the master protocol, so we
need to rollback to the current state */

if(packet->tcp) {
/* TODO: TLS code assumes that DTLS is only over UDP */
NDPI_LOG_DBG(ndpi_struct, "Ignoring DTLS over TCP\n");
if(flow->tls_quic.certificate_processed == 1) {
NDPI_LOG_DBG(ndpi_struct, "Interesting DTLS stuff already processed. Ignoring\n");
} else {
if(flow->tls_quic.certificate_processed == 1) {
NDPI_LOG_DBG(ndpi_struct, "Interesting DTLS stuff already processed. Ignoring\n");
} else {
NDPI_LOG_DBG(ndpi_struct, "Switch to DTLS (%d/%d)\n",
flow->detected_protocol_stack[0], flow->detected_protocol_stack[1]);

if(flow->stun.maybe_dtls == 0) {
/* First DTLS packet of the flow */
first_dtls_pkt = 1;

/* We might need to rollback this change... */
old_proto_stack[0] = flow->detected_protocol_stack[0];
old_proto_stack[1] = flow->detected_protocol_stack[1];

/* TODO: right way? It is a bit scary... do we need to reset something else too? */
reset_detected_protocol(flow);
/* We keep the category related to STUN traffic */
/* STUN often triggers this risk; clear it. TODO: clear other risks? */
ndpi_unset_risk(flow, NDPI_KNOWN_PROTOCOL_ON_NON_STANDARD_PORT);

/* Give room for DTLS handshake, where we might have
retransmissions and fragments */
flow->max_extra_packets_to_check = ndpi_min(255, (int)flow->max_extra_packets_to_check + 10);
flow->stun.maybe_dtls = 1;
}

switch_to_tls(ndpi_struct, flow, first_dtls_pkt);
NDPI_LOG_DBG(ndpi_struct, "Switch to DTLS (%d/%d)\n",
flow->detected_protocol_stack[0], flow->detected_protocol_stack[1]);

if(flow->stun.maybe_dtls == 0) {
/* First DTLS packet of the flow */
first_dtls_pkt = 1;

/* We might need to rollback this change... */
old_proto_stack[0] = flow->detected_protocol_stack[0];
old_proto_stack[1] = flow->detected_protocol_stack[1];

/* TODO: right way? It is a bit scary... do we need to reset something else too? */
reset_detected_protocol(flow);
/* We keep the category related to STUN traffic */
/* STUN often triggers this risk; clear it. TODO: clear other risks? */
ndpi_unset_risk(flow, NDPI_KNOWN_PROTOCOL_ON_NON_STANDARD_PORT);

/* Give room for DTLS handshake, where we might have
retransmissions and fragments */
flow->max_extra_packets_to_check = ndpi_min(255, (int)flow->max_extra_packets_to_check + 10);
flow->stun.maybe_dtls = 1;
}

if(first_dtls_pkt &&
flow->detected_protocol_stack[0] == NDPI_PROTOCOL_DTLS &&
flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN &&
old_proto_stack[0] != NDPI_PROTOCOL_UNKNOWN &&
old_proto_stack[0] != NDPI_PROTOCOL_STUN) {
NDPI_LOG_DBG(ndpi_struct, "Keeping old subclassification %d\n", old_proto_stack[0]);
ndpi_int_stun_add_connection(ndpi_struct, flow,
old_proto_stack[0] == NDPI_PROTOCOL_RTP ? NDPI_PROTOCOL_SRTP : old_proto_stack[0],
__get_master(flow));
}
switch_to_tls(ndpi_struct, flow, first_dtls_pkt);

/* If this is not a real DTLS packet, we need to restore the old state */
if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN &&
first_dtls_pkt) {
NDPI_LOG_DBG(ndpi_struct, "Switch to TLS failed. Rollback to old classification\n");
if(first_dtls_pkt &&
flow->detected_protocol_stack[0] == NDPI_PROTOCOL_DTLS &&
flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN &&
old_proto_stack[0] != NDPI_PROTOCOL_UNKNOWN &&
old_proto_stack[0] != NDPI_PROTOCOL_STUN) {
NDPI_LOG_DBG(ndpi_struct, "Keeping old subclassification %d\n", old_proto_stack[0]);
ndpi_int_stun_add_connection(ndpi_struct, flow,
old_proto_stack[0] == NDPI_PROTOCOL_RTP ? NDPI_PROTOCOL_SRTP : old_proto_stack[0],
__get_master(flow));
}

ndpi_set_detected_protocol(ndpi_struct, flow,
old_proto_stack[0], old_proto_stack[1],
NDPI_CONFIDENCE_DPI);
/* If this is not a real DTLS packet, we need to restore the old state */
if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN &&
first_dtls_pkt) {
NDPI_LOG_DBG(ndpi_struct, "Switch to TLS failed. Rollback to old classification\n");

flow->stun.maybe_dtls = 0;
flow->max_extra_packets_to_check -= 10;
}
ndpi_set_detected_protocol(ndpi_struct, flow,
old_proto_stack[0], old_proto_stack[1],
NDPI_CONFIDENCE_DPI);

NDPI_LOG_DBG(ndpi_struct, "(%d/%d)\n",
flow->detected_protocol_stack[0], flow->detected_protocol_stack[1]);
flow->stun.maybe_dtls = 0;
flow->max_extra_packets_to_check -= 10;
}

NDPI_LOG_DBG(ndpi_struct, "(%d/%d)\n",
flow->detected_protocol_stack[0], flow->detected_protocol_stack[1]);
}
}
} else if(first_byte <= 79) {
Expand Down
17 changes: 10 additions & 7 deletions src/lib/protocols/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ static u_int32_t ndpi_tls_refine_master_protocol(struct ndpi_detection_module_st
u_int16_t sport = ntohs(packet->tcp->source);
u_int16_t dport = ntohs(packet->tcp->dest);

if((sport == 465) || (dport == 465) || (sport == 587) || (dport == 587))
if(flow->stun.maybe_dtls)
protocol = NDPI_PROTOCOL_DTLS;
else if((sport == 465) || (dport == 465) || (sport == 587) || (dport == 587))
protocol = NDPI_PROTOCOL_MAIL_SMTPS;
else if((sport == 993) || (dport == 993) || (flow->l4.tcp.mail_imap_starttls))
protocol = NDPI_PROTOCOL_MAIL_IMAPS;
Expand Down Expand Up @@ -770,7 +772,7 @@ void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct
int processCertificate(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow) {
struct ndpi_packet_struct *packet = &ndpi_struct->packet;
int is_dtls = packet->udp ? 1 : 0;
int is_dtls = packet->udp || flow->stun.maybe_dtls; /* No certificate with QUIC */
u_int32_t certificates_length, length = (packet->payload[1] << 16) + (packet->payload[2] << 8) + packet->payload[3];
u_int32_t certificates_offset = 7 + (is_dtls ? 8 : 0);
u_int8_t num_certificates_found = 0;
Expand Down Expand Up @@ -899,6 +901,7 @@ static int processTLSBlock(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow) {
struct ndpi_packet_struct *packet = &ndpi_struct->packet;
int ret;
int is_dtls = packet->udp || flow->stun.maybe_dtls;

#ifdef DEBUG_TLS
printf("[TLS] Processing block %u\n", packet->payload[0]);
Expand All @@ -918,11 +921,11 @@ static int processTLSBlock(struct ndpi_detection_module_struct *ndpi_struct,
(packet->payload[0] == 0x01) ? "Client" : "Server");
#endif

if((packet->tcp && flow->protos.tls_quic.ssl_version >= 0x0304 /* TLS 1.3 */)
if((!is_dtls && flow->protos.tls_quic.ssl_version >= 0x0304 /* TLS 1.3 */)
&& (packet->payload[0] == 0x02 /* Server Hello */)) {
flow->tls_quic.certificate_processed = 1; /* No Certificate with TLS 1.3+ */
}
if((packet->udp && flow->protos.tls_quic.ssl_version == 0xFEFC /* DTLS 1.3 */)
if((is_dtls && flow->protos.tls_quic.ssl_version == 0xFEFC /* DTLS 1.3 */)
&& (packet->payload[0] == 0x02 /* Server Hello */)) {
flow->tls_quic.certificate_processed = 1; /* No Certificate with DTLS 1.3+ */
}
Expand Down Expand Up @@ -1655,7 +1658,7 @@ static void ndpi_compute_ja4(struct ndpi_flow_struct *flow,
u_int16_t tls_handshake_version = ja->client.tls_handshake_version;
char * const ja_str = &flow->protos.tls_quic.ja4_client[0];
const u_int16_t ja_max_len = sizeof(flow->protos.tls_quic.ja4_client);
bool is_dtls = (flow->l4_proto == IPPROTO_UDP) && (quic_version == 0);
bool is_dtls = ((flow->l4_proto == IPPROTO_UDP) && (quic_version == 0)) || flow->stun.maybe_dtls;
/*
Compute JA4 TLS/QUIC client
Expand Down Expand Up @@ -1815,7 +1818,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
u_int16_t total_len;
u_int8_t handshake_type;
bool is_quic = (quic_version != 0);
bool is_dtls = packet->udp && (!is_quic);
bool is_dtls = (packet->udp && !is_quic) || flow->stun.maybe_dtls;

#ifdef DEBUG_TLS
printf("TLS %s() called\n", __FUNCTION__);
Expand Down Expand Up @@ -2964,7 +2967,7 @@ static void ndpi_search_tls_wrapper(struct ndpi_detection_module_struct *ndpi_st
flow->protos.tls_quic.ssl_version);
#endif

if(packet->udp != NULL)
if(packet->udp != NULL || flow->stun.maybe_dtls)
ndpi_search_tls_udp(ndpi_struct, flow);
else
ndpi_search_tls_tcp(ndpi_struct, flow);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Num dissector calls: 13 (6.50 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/0/0 (insert/search/found)
LRU cache stun: 42/0/0 (insert/search/found)
LRU cache tls_cert: 0/1/0 (insert/search/found)
LRU cache tls_cert: 0/2/0 (insert/search/found)
LRU cache mining: 0/0/0 (insert/search/found)
LRU cache msteams: 0/0/0 (insert/search/found)
LRU cache stun_zoom: 0/0/0 (insert/search/found)
Expand All @@ -28,7 +28,8 @@ Acceptable 102 26347 2
JA3 Host Stats:
IP Address # JA3C
1 192.168.12.156 1
2 192.168.12.182 1


1 TCP 192.168.12.182:50221 <-> 142.250.82.249:3478 [proto: 338.404/SRTP.GoogleCall][IP: 126/Google][Stream Content: Audio][Encrypted][Confidence: DPI][DPI packets: 63][cat: VoIP/10][28 pkts/3492 bytes <-> 35 pkts/14442 bytes][Goodput ratio: 56/87][0.89 sec][Hostname/SNI: turn.l.google.com][bytes ratio: -0.611 (Download)][IAT c2s/s2c min/avg/max/stddev: 0/0 11/13 55/55 17/18][Pkt Len c2s/s2c min/avg/max/stddev: 54/54 125/413 250/1162 71/442][Mapped IP/Port: 10.13.0.62:15530][Peer IP/Port: 10.13.0.50:1259][Relayed IP/Port: 10.13.0.62:15530][PLAIN TEXT (Lvsrdelc)][Plen Bins: 2,2,12,15,21,10,2,0,0,0,5,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,0]
1 TCP 192.168.12.182:50221 <-> 142.250.82.249:3478 [proto: 30.404/DTLS.GoogleCall][IP: 126/Google][Stream Content: Audio][Encrypted][Confidence: DPI][DPI packets: 63][cat: VoIP/10][28 pkts/3492 bytes <-> 35 pkts/14442 bytes][Goodput ratio: 56/87][0.89 sec][Hostname/SNI: turn.l.google.com][bytes ratio: -0.611 (Download)][IAT c2s/s2c min/avg/max/stddev: 0/0 11/13 55/55 17/18][Pkt Len c2s/s2c min/avg/max/stddev: 54/54 125/413 250/1162 71/442][Mapped IP/Port: 10.13.0.62:15530][Peer IP/Port: 10.13.0.50:1259][Relayed IP/Port: 10.13.0.62:15530][Risk: ** TLS Cert About To Expire **][Risk Score: 50][Risk Info: 16/Mar/2024 12:47:23 - 16/Apr/2024 12:47:23][DTLSv1.2][JA3C: c14667d7da3e6f7a7ab5519ef78c2452][JA4: dd2d110700_c45550529adf_d9dd6182da81][JA3S: 717ecda0d920dc848680e6da69fb0468][Issuer: CN=WebRTC][Subject: CN=WebRTC][Certificate SHA-1: 33:D9:F2:88:62:62:B0:C4:A1:20:72:CA:BF:CF:E7:69:A0:9E:0F:94][Validity: 2024-03-16 12:47:23 - 2024-04-16 12:47:23][Cipher: TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256][PLAIN TEXT (Lvsrdelc)][Plen Bins: 2,2,12,15,21,10,2,0,0,0,5,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,0]
2 UDP 192.168.12.156:37967 <-> 142.250.82.76:19305 [proto: 30.404/DTLS.GoogleCall][IP: 126/Google][Stream Content: Audio][Encrypted][Confidence: DPI][DPI packets: 39][cat: VoIP/10][25 pkts/4202 bytes <-> 14 pkts/4211 bytes][Goodput ratio: 75/86][0.88 sec][bytes ratio: -0.001 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/0 37/35 203/107 47/36][Pkt Len c2s/s2c min/avg/max/stddev: 103/82 168/301 587/1245 125/320][Mapped IP/Port: 93.35.171.3:61536][DTLSv1.2][JA3C: c14667d7da3e6f7a7ab5519ef78c2452][JA4: dd2d110700_c45550529adf_d9dd6182da81][JA3S: 1f5d6a6d0bc5d514dd84d13e6283d309][Issuer: CN=hangouts][Subject: CN=hangouts][Certificate SHA-1: AF:DD:BF:F5:59:23:0C:D1:B0:9F:B1:04:2E:89:DF:4C:1B:AB:BE:CC][Validity: 2022-11-30 17:35:18 - 2023-12-01 17:35:18][Cipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256][PLAIN TEXT (ShSURJhNF)][Plen Bins: 0,5,47,30,2,0,0,0,0,0,0,0,0,2,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0]

0 comments on commit 3c14145

Please sign in to comment.