From 419160f351c35c9ccf1f4d4c148b68f846a828a6 Mon Sep 17 00:00:00 2001 From: Luca Date: Thu, 8 Aug 2019 12:45:27 +0200 Subject: Implemented DTLS support Renamed ssl to tls --- src/lib/ndpi_main.c | 48 +- src/lib/ndpi_utils.c | 18 +- src/lib/protocols/directconnect.c | 32 +- src/lib/protocols/mail_imap.c | 2 +- src/lib/protocols/msn.c | 12 +- src/lib/protocols/oscar.c | 2 +- src/lib/protocols/rtp.c | 2 +- src/lib/protocols/ssl.c | 1301 ---------------------------------- src/lib/protocols/stun.c | 34 +- src/lib/protocols/tls.c | 1384 +++++++++++++++++++++++++++++++++++++ src/lib/protocols/tor.c | 2 +- src/lib/protocols/yahoo.c | 2 +- 12 files changed, 1461 insertions(+), 1378 deletions(-) delete mode 100644 src/lib/protocols/ssl.c create mode 100644 src/lib/protocols/tls.c (limited to 'src/lib') diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c index c59201c9a..b039f585e 100644 --- a/src/lib/ndpi_main.c +++ b/src/lib/ndpi_main.c @@ -1097,10 +1097,10 @@ static void ndpi_init_protocol_defaults(struct ndpi_detection_module_struct *ndp ndpi_build_default_ports(ports_a, 0, 0, 0, 0, 0) /* TCP */, ndpi_build_default_ports(ports_b, 0, 0, 0, 0, 0) /* UDP */); - custom_master[0] = NDPI_PROTOCOL_SSL, custom_master[1] = NDPI_PROTOCOL_UNKNOWN; - ndpi_set_proto_defaults(ndpi_mod, NDPI_PROTOCOL_ACCEPTABLE, NDPI_PROTOCOL_SSL_NO_CERT, + custom_master[0] = NDPI_PROTOCOL_TLS, custom_master[1] = NDPI_PROTOCOL_UNKNOWN; + ndpi_set_proto_defaults(ndpi_mod, NDPI_PROTOCOL_ACCEPTABLE, NDPI_PROTOCOL_TLS_NO_CERT, 1 /* can_have_a_subprotocol */, custom_master, - no_master, "SSL_No_Cert", NDPI_PROTOCOL_CATEGORY_WEB, + no_master, "TLS_No_Cert", NDPI_PROTOCOL_CATEGORY_WEB, ndpi_build_default_ports(ports_a, 0, 0, 0, 0, 0) /* TCP */, ndpi_build_default_ports(ports_b, 0, 0, 0, 0, 0) /* UDP */); ndpi_set_proto_defaults(ndpi_mod, NDPI_PROTOCOL_UNSAFE, NDPI_PROTOCOL_IRC, @@ -1241,10 +1241,10 @@ static void ndpi_init_protocol_defaults(struct ndpi_detection_module_struct *ndp ndpi_build_default_ports(ports_a, 0, 0, 0, 0, 0) /* TCP */, ndpi_build_default_ports(ports_b, 0, 0, 0, 0, 0) /* UDP */); - custom_master[0] = NDPI_PROTOCOL_SSL_NO_CERT, custom_master[1] = NDPI_PROTOCOL_UNKNOWN; - ndpi_set_proto_defaults(ndpi_mod, NDPI_PROTOCOL_SAFE, NDPI_PROTOCOL_SSL, + custom_master[0] = NDPI_PROTOCOL_TLS_NO_CERT, custom_master[1] = NDPI_PROTOCOL_UNKNOWN; + ndpi_set_proto_defaults(ndpi_mod, NDPI_PROTOCOL_SAFE, NDPI_PROTOCOL_TLS, 1 /* can_have_a_subprotocol */, no_master, - custom_master, "SSL", NDPI_PROTOCOL_CATEGORY_WEB, + custom_master, "TLS", NDPI_PROTOCOL_CATEGORY_WEB, ndpi_build_default_ports(ports_a, 443, 3001 /* ntop */, 0, 0, 0) /* TCP */, ndpi_build_default_ports(ports_b, 0, 0, 0, 0, 0) /* UDP */); ndpi_set_proto_defaults(ndpi_mod, NDPI_PROTOCOL_ACCEPTABLE, NDPI_PROTOCOL_SSH, @@ -2811,8 +2811,8 @@ void ndpi_set_protocol_detection_bitmask2(struct ndpi_detection_module_struct *n /* STARCRAFT */ init_starcraft_dissector(ndpi_struct, &a, detection_bitmask); - /* SSL */ - init_ssl_dissector(ndpi_struct, &a, detection_bitmask); + /* TLS */ + init_tls_dissector(ndpi_struct, &a, detection_bitmask); /* STUN */ init_stun_dissector(ndpi_struct, &a, detection_bitmask); @@ -4023,9 +4023,9 @@ ndpi_protocol ndpi_detection_giveup(struct ndpi_detection_module_struct *ndpi_st if(flow->guessed_protocol_id == NDPI_PROTOCOL_STUN) goto check_stun_export; - else if((flow->l4.tcp.ssl_seen_client_cert == 1) - && (flow->protos.stun_ssl.ssl.client_certificate[0] != '\0')) { - ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_SSL, NDPI_PROTOCOL_UNKNOWN); + else if((flow->l4.tcp.tls_seen_client_cert == 1) + && (flow->protos.stun_tls.tls.client_certificate[0] != '\0')) { + ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_TLS, NDPI_PROTOCOL_UNKNOWN); } else { ndpi_protocol ret_g = ndpi_get_partial_detection(ndpi_struct, flow); @@ -4038,8 +4038,8 @@ ndpi_protocol ndpi_detection_giveup(struct ndpi_detection_module_struct *ndpi_st if((flow->guessed_protocol_id == NDPI_PROTOCOL_UNKNOWN) && (flow->packet.l4_protocol == IPPROTO_TCP) - && (flow->l4.tcp.ssl_stage > 1)) - flow->guessed_protocol_id = NDPI_PROTOCOL_SSL_NO_CERT; + && (flow->l4.tcp.tls_stage > 1)) + flow->guessed_protocol_id = NDPI_PROTOCOL_TLS_NO_CERT; guessed_protocol_id = flow->guessed_protocol_id, guessed_host_protocol_id = flow->guessed_host_protocol_id; @@ -4061,8 +4061,8 @@ ndpi_protocol ndpi_detection_giveup(struct ndpi_detection_module_struct *ndpi_st if((guessed_protocol_id != NDPI_PROTOCOL_UNKNOWN) || (guessed_host_protocol_id != NDPI_PROTOCOL_UNKNOWN)) { if((guessed_protocol_id == 0) - && (flow->protos.stun_ssl.stun.num_binding_requests > 0) - && (flow->protos.stun_ssl.stun.num_processed_pkts > 0)) + && (flow->protos.stun_tls.stun.num_binding_requests > 0) + && (flow->protos.stun_tls.stun.num_processed_pkts > 0)) guessed_protocol_id = NDPI_PROTOCOL_STUN; @@ -4095,9 +4095,9 @@ ndpi_protocol ndpi_detection_giveup(struct ndpi_detection_module_struct *ndpi_st if((flow->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN) && (flow->guessed_protocol_id == NDPI_PROTOCOL_STUN)) { check_stun_export: - if(flow->protos.stun_ssl.stun.num_processed_pkts || flow->protos.stun_ssl.stun.num_udp_pkts) { - // if(/* (flow->protos.stun_ssl.stun.num_processed_pkts >= NDPI_MIN_NUM_STUN_DETECTION) */ - if(flow->protos.stun_ssl.stun.num_processed_pkts && flow->protos.stun_ssl.stun.is_skype) { + if(flow->protos.stun_tls.stun.num_processed_pkts || flow->protos.stun_tls.stun.num_udp_pkts) { + // if(/* (flow->protos.stun_tls.stun.num_processed_pkts >= NDPI_MIN_NUM_STUN_DETECTION) */ + if(flow->protos.stun_tls.stun.num_processed_pkts && flow->protos.stun_tls.stun.is_skype) { ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_SKYPE_CALL, NDPI_PROTOCOL_SKYPE); } else ndpi_set_detected_protocol(ndpi_struct, flow, @@ -4411,9 +4411,9 @@ void ndpi_fill_protocol_category(struct ndpi_detection_module_struct *ndpi_struc } } - if((flow->l4.tcp.ssl_seen_client_cert == 1) && (flow->protos.stun_ssl.ssl.client_certificate[0] != '\0')) { + if((flow->l4.tcp.tls_seen_client_cert == 1) && (flow->protos.stun_tls.tls.client_certificate[0] != '\0')) { unsigned long id; - int rc = ndpi_match_custom_category(ndpi_struct, (char *)flow->protos.stun_ssl.ssl.client_certificate, &id); + int rc = ndpi_match_custom_category(ndpi_struct, (char *)flow->protos.stun_tls.tls.client_certificate, &id); if(rc == 0) { flow->category = ret->category = (ndpi_protocol_category_t)id; @@ -4457,8 +4457,8 @@ ndpi_protocol ndpi_detection_process_packet(struct ndpi_detection_module_struct */ if(flow->check_extra_packets /* - && ((flow->detected_protocol_stack[0] == NDPI_PROTOCOL_SSL) - || (flow->detected_protocol_stack[1] == NDPI_PROTOCOL_SSL)) + && ((flow->detected_protocol_stack[0] == NDPI_PROTOCOL_TLS) + || (flow->detected_protocol_stack[1] == NDPI_PROTOCOL_TLS)) */ ) { ndpi_process_extra_packet(ndpi_struct, flow, packet, packetlen, current_tick_l, src, dst); @@ -4599,7 +4599,7 @@ ndpi_protocol ndpi_detection_process_packet(struct ndpi_detection_module_struct ret.master_protocol = NDPI_PROTOCOL_HTTP; break; case 443: - ret.master_protocol = NDPI_PROTOCOL_SSL; /* QUIC could also match */ + ret.master_protocol = NDPI_PROTOCOL_TLS; /* QUIC could also match */ break; } @@ -5589,7 +5589,7 @@ ndpi_protocol ndpi_guess_undetected_protocol(struct ndpi_detection_module_struct else { ret.app_protocol = rc; - if(rc == NDPI_PROTOCOL_SSL) + if(rc == NDPI_PROTOCOL_TLS) goto check_guessed_skype; else { ret.category = ndpi_get_proto_category(ndpi_struct, ret); diff --git a/src/lib/ndpi_utils.c b/src/lib/ndpi_utils.c index 654b9c730..af43f2bef 100644 --- a/src/lib/ndpi_utils.c +++ b/src/lib/ndpi_utils.c @@ -303,7 +303,7 @@ int strncasecmp(const char *s1, const char *s2, size_t n) { /* **************************************** */ -u_int8_t ndpi_is_safe_ssl_cipher(u_int32_t cipher) { +u_int8_t ndpi_is_safe_tls_cipher(u_int32_t cipher) { /* https://community.qualys.com/thread/18212-how-does-qualys-determine-the-server-cipher-suites */ /* INSECURE */ switch(cipher) { @@ -360,9 +360,9 @@ const char* ndpi_cipher2str(u_int32_t cipher) { case 0x000019: return("TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"); case 0x00001a: return("TLS_DH_anon_WITH_DES_CBC_SHA"); case 0x00001b: return("TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"); - case 0x00001c: return("SSL_FORTEZZA_KEA_WITH_NULL_SHA"); - case 0x00001d: return("SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA"); - /* case 0x00001e: return("SSL_FORTEZZA_KEA_WITH_RC4_128_SHA"); */ + case 0x00001c: return("TLS_FORTEZZA_KEA_WITH_NULL_SHA"); + case 0x00001d: return("TLS_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA"); + /* case 0x00001e: return("TLS_FORTEZZA_KEA_WITH_RC4_128_SHA"); */ case 0x00001E: return("TLS_KRB5_WITH_DES_CBC_SHA"); case 0x00001F: return("TLS_KRB5_WITH_3DES_EDE_CBC_SHA"); case 0x000020: return("TLS_KRB5_WITH_RC4_128_SHA"); @@ -576,10 +576,10 @@ const char* ndpi_cipher2str(u_int32_t cipher) { case 0x00E41D: return("TLS_DHE_PSK_WITH_SALSA20_SHA1"); case 0x00E41E: return("TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1"); case 0x00E41F: return("TLS_DHE_RSA_WITH_SALSA20_SHA1"); - case 0x00fefe: return("SSL_RSA_FIPS_WITH_DES_CBC_SHA"); - case 0x00feff: return("SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"); - case 0x00ffe0: return("SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"); - case 0x00ffe1: return("SSL_RSA_FIPS_WITH_DES_CBC_SHA"); + case 0x00fefe: return("TLS_RSA_FIPS_WITH_DES_CBC_SHA"); + case 0x00feff: return("TLS_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"); + case 0x00ffe0: return("TLS_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"); + case 0x00ffe1: return("TLS_RSA_FIPS_WITH_DES_CBC_SHA"); case 0x010080: return("SSL2_RC4_128_WITH_MD5"); case 0x020080: return("SSL2_RC4_128_EXPORT40_WITH_MD5"); case 0x030080: return("SSL2_RC2_128_CBC_WITH_MD5"); @@ -709,7 +709,7 @@ int ndpi_has_human_readeable_string(struct ndpi_detection_module_struct *ndpi_st /* ********************************** */ -char* ndpi_ssl_version2str(u_int16_t version) { +char* ndpi_tls_version2str(u_int16_t version) { static char v[8]; switch(version) { diff --git a/src/lib/protocols/directconnect.c b/src/lib/protocols/directconnect.c index 19582724d..969af69fc 100644 --- a/src/lib/protocols/directconnect.c +++ b/src/lib/protocols/directconnect.c @@ -130,13 +130,13 @@ static void ndpi_search_directconnect_tcp(struct ndpi_detection_module_struct *n if (flow->detected_protocol_stack[0] == NDPI_PROTOCOL_DIRECTCONNECT) { if (packet->payload_packet_len >= 40 && memcmp(&packet->payload[0], "BINF", 4) == 0) { - u_int16_t ssl_port = 0; - ssl_port = parse_binf_message(ndpi_struct, &packet->payload[4], packet->payload_packet_len - 4); - if (dst != NULL && ssl_port) { - dst->detected_directconnect_ssl_port = ssl_port; + u_int16_t tls_port = 0; + tls_port = parse_binf_message(ndpi_struct, &packet->payload[4], packet->payload_packet_len - 4); + if (dst != NULL && tls_port) { + dst->detected_directconnect_tls_port = tls_port; } - if (src != NULL && ssl_port) { - src->detected_directconnect_ssl_port = ssl_port; + if (src != NULL && tls_port) { + src->detected_directconnect_tls_port = tls_port; } @@ -145,14 +145,14 @@ static void ndpi_search_directconnect_tcp(struct ndpi_detection_module_struct *n && memcmp(&packet->payload[0], "DCTM", 4) == 0 && memcmp(&packet->payload[15], "ADCS", 4) == 0) { u_int16_t bytes_read = 0; if (dst != NULL) { - dst->detected_directconnect_ssl_port = + dst->detected_directconnect_tls_port = ntohs_ndpi_bytestream_to_number(&packet->payload[25], 5, &bytes_read); - NDPI_LOG_DBG2(ndpi_struct, "DC ssl port parsed %d\n", ntohs(dst->detected_directconnect_ssl_port)); + NDPI_LOG_DBG2(ndpi_struct, "DC ssl port parsed %d\n", ntohs(dst->detected_directconnect_tls_port)); } if (src != NULL) { - src->detected_directconnect_ssl_port = + src->detected_directconnect_tls_port = ntohs_ndpi_bytestream_to_number(&packet->payload[25], 5, &bytes_read); - NDPI_LOG_DBG2(ndpi_struct, "DC ssl port parsed %d\n", ntohs(src->detected_directconnect_ssl_port)); + NDPI_LOG_DBG2(ndpi_struct, "DC ssl port parsed %d\n", ntohs(src->detected_directconnect_tls_port)); } @@ -175,16 +175,16 @@ static void ndpi_search_directconnect_tcp(struct ndpi_detection_module_struct *n return; } } - if (src->detected_directconnect_ssl_port == packet->tcp->dest) { + if (src->detected_directconnect_tls_port == packet->tcp->dest) { if ((u_int32_t) (packet->tick_timestamp - src->directconnect_last_safe_access_time) < ndpi_struct->directconnect_connection_ip_tick_timeout) { src->directconnect_last_safe_access_time = packet->tick_timestamp; - NDPI_LOG_INFO(ndpi_struct, "found DC using port %d\n", ntohs(src->detected_directconnect_ssl_port)); + NDPI_LOG_INFO(ndpi_struct, "found DC using port %d\n", ntohs(src->detected_directconnect_tls_port)); ndpi_int_change_protocol(ndpi_struct, flow, NDPI_PROTOCOL_DIRECTCONNECT, NDPI_PROTOCOL_UNKNOWN); return; } else { - src->detected_directconnect_ssl_port = 0; + src->detected_directconnect_tls_port = 0; NDPI_LOG_DBG2(ndpi_struct, "resetting src port due to timeout\n"); return; } @@ -207,16 +207,16 @@ static void ndpi_search_directconnect_tcp(struct ndpi_detection_module_struct *n return; } } - if (dst->detected_directconnect_ssl_port == packet->tcp->dest) { + if (dst->detected_directconnect_tls_port == packet->tcp->dest) { if ((u_int32_t) (packet->tick_timestamp - dst->directconnect_last_safe_access_time) < ndpi_struct->directconnect_connection_ip_tick_timeout) { dst->directconnect_last_safe_access_time = packet->tick_timestamp; - NDPI_LOG_DBG(ndpi_struct, "found DC using port %d\n", ntohs(dst->detected_directconnect_ssl_port)); + NDPI_LOG_DBG(ndpi_struct, "found DC using port %d\n", ntohs(dst->detected_directconnect_tls_port)); ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_DIRECTCONNECT, NDPI_PROTOCOL_UNKNOWN); return; } else { - dst->detected_directconnect_ssl_port = 0; + dst->detected_directconnect_tls_port = 0; NDPI_LOG_DBG2(ndpi_struct, "resetting dst port due to timeout\n"); return; } diff --git a/src/lib/protocols/mail_imap.c b/src/lib/protocols/mail_imap.c index 65341cdc8..69d135943 100644 --- a/src/lib/protocols/mail_imap.c +++ b/src/lib/protocols/mail_imap.c @@ -48,7 +48,7 @@ void ndpi_search_mail_imap_tcp(struct ndpi_detection_module_struct *ndpi_struct, if (flow->l4.tcp.mail_imap_starttls == 2) { NDPI_LOG_DBG2(ndpi_struct, "starttls detected\n"); NDPI_ADD_PROTOCOL_TO_BITMASK(flow->excluded_protocol_bitmask, NDPI_PROTOCOL_MAIL_IMAP); - NDPI_DEL_PROTOCOL_FROM_BITMASK(flow->excluded_protocol_bitmask, NDPI_PROTOCOL_SSL); + NDPI_DEL_PROTOCOL_FROM_BITMASK(flow->excluded_protocol_bitmask, NDPI_PROTOCOL_TLS); return; } diff --git a/src/lib/protocols/msn.c b/src/lib/protocols/msn.c index 8ab45ad32..26d7557b1 100644 --- a/src/lib/protocols/msn.c +++ b/src/lib/protocols/msn.c @@ -62,7 +62,7 @@ static void ndpi_search_msn_tcp(struct ndpi_detection_module_struct *ndpi_struct u_int16_t plen; u_int16_t status = 0; - if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_SSL) { + if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_TLS) { NDPI_LOG_DBG2(ndpi_struct, "msn ssl ft test\n"); @@ -80,12 +80,12 @@ static void ndpi_search_msn_tcp(struct ndpi_detection_module_struct *ndpi_struct if(flow->packet_counter >= 5 && flow->packet_counter <= 10 && (get_u_int32_t(packet->payload, 0) == htonl(0x18000000) && get_u_int32_t(packet->payload, 4) == 0x00000000)) { - flow->l4.tcp.msn_ssl_ft++; + flow->l4.tcp.msn_tls_ft++; NDPI_LOG_DBG2(ndpi_struct, "increased msn ft ssl stage to: %u at packet nr: %u\n", - flow->l4.tcp.msn_ssl_ft, + flow->l4.tcp.msn_tls_ft, flow->packet_counter); - if (flow->l4.tcp.msn_ssl_ft == 2) { + if (flow->l4.tcp.msn_tls_ft == 2) { NDPI_LOG_INFO(ndpi_struct, "found MSN File Transfer, ifdef ssl 2.\n"); ndpi_int_msn_add_connection(ndpi_struct, flow); @@ -103,7 +103,7 @@ static void ndpi_search_msn_tcp(struct ndpi_detection_module_struct *ndpi_struct */ /* now we have a look at the first packet only. */ if(flow->packet_counter == 1 - || ((packet->detected_protocol_stack[0] == NDPI_PROTOCOL_SSL) + || ((packet->detected_protocol_stack[0] == NDPI_PROTOCOL_TLS) && flow->packet_counter <= 3) ) { @@ -497,7 +497,7 @@ void ndpi_search_msn(struct ndpi_detection_module_struct *ndpi_struct, struct nd // need to do the ceck when protocol == http too (POST /gateway ...) if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN || packet->detected_protocol_stack[0] == NDPI_PROTOCOL_HTTP - || packet->detected_protocol_stack[0] == NDPI_PROTOCOL_SSL + || packet->detected_protocol_stack[0] == NDPI_PROTOCOL_TLS || packet->detected_protocol_stack[0] == NDPI_PROTOCOL_STUN ) ndpi_search_msn_tcp(ndpi_struct, flow); diff --git a/src/lib/protocols/oscar.c b/src/lib/protocols/oscar.c index 010a620e9..1a848ea12 100644 --- a/src/lib/protocols/oscar.c +++ b/src/lib/protocols/oscar.c @@ -774,7 +774,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct NDPI_LOG_INFO(ndpi_struct, "found OSCAR PICTURE TRANSFER\n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); if (ntohs(packet->tcp->dest) == 443 || ntohs(packet->tcp->source) == 443) { - flow->oscar_ssl_voice_stage = 1; + flow->oscar_tls_voice_stage = 1; } return; diff --git a/src/lib/protocols/rtp.c b/src/lib/protocols/rtp.c index 90b73ab1e..6583b727a 100644 --- a/src/lib/protocols/rtp.c +++ b/src/lib/protocols/rtp.c @@ -78,7 +78,7 @@ static void ndpi_rtp_search(struct ndpi_detection_module_struct *ndpi_struct, const u_int8_t * payload, const u_int16_t payload_len) { NDPI_LOG_DBG(ndpi_struct, "search RTP\n"); - if((payload_len < 2) || flow->protos.stun_ssl.stun.num_binding_requests) { + if((payload_len < 2) || flow->protos.stun_tls.stun.num_binding_requests) { NDPI_EXCLUDE_PROTO(ndpi_struct, flow); return; } diff --git a/src/lib/protocols/ssl.c b/src/lib/protocols/ssl.c deleted file mode 100644 index b8a3a643a..000000000 --- a/src/lib/protocols/ssl.c +++ /dev/null @@ -1,1301 +0,0 @@ -/* - * ssl.c - * - * Copyright (C) 2016-18 - ntop.org - * - * This file is part of nDPI, an open source deep packet inspection - * library based on the OpenDPI and PACE technology by ipoque GmbH - * - * nDPI is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * nDPI 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with nDPI. If not, see . - * - */ - -#include "ndpi_protocol_ids.h" - -#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_SSL - -#include "ndpi_api.h" - -/* #define CERTIFICATE_DEBUG 1 */ - -#define NDPI_MAX_SSL_REQUEST_SIZE 10000 - -/* Skype.c */ -extern u_int8_t is_skype_flow(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow); - -/* **************************************** */ - -typedef struct MD5Context { - uint32_t buf[4]; - uint32_t bits[2]; - unsigned char in[64]; -} MD5_CTX; - -/* **************************************** */ - -static int is_big_endian(void) { - static const int n = 1; - return ((char *) &n)[0] == 0; -} - -static void byteReverse(unsigned char *buf, unsigned longs) { - // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN - if (is_big_endian()) { - do { - uint32_t t; - - t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | - ((unsigned) buf[1] << 8 | buf[0]); - * (uint32_t *) buf = t; - buf += 4; - } while (--longs); - } -} - -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -#define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) - -// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious -// initialization constants. -static void MD5Init(MD5_CTX *ctx) { - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) { - uint32_t a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - -static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) { - uint32_t t; - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) - ctx->bits[1]++; - ctx->bits[1] += len >> 29; - - t = (t >> 3) & 0x3f; - - if (t) { - unsigned char *p = (unsigned char *) ctx->in + t; - - t = 64 - t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32_t *) ctx->in); - buf += t; - len -= t; - } - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32_t *) ctx->in); - buf += 64; - len -= 64; - } - - memcpy(ctx->in, buf, len); -} - -static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) { - unsigned count; - unsigned char *p; - uint32_t *c = (uint32_t*)ctx->in; - - count = (ctx->bits[0] >> 3) & 0x3F; - - p = ctx->in + count; - *p++ = 0x80; - count = 64 - 1 - count; - if (count < 8) { - memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32_t *) ctx->in); - memset(ctx->in, 0, 56); - } else { - memset(p, 0, count - 8); - } - byteReverse(ctx->in, 14); - - c[14] = ctx->bits[0]; - c[15] = ctx->bits[1]; - - MD5Transform(ctx->buf, (uint32_t *) ctx->in); - byteReverse((unsigned char *) ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset((char *) ctx, 0, sizeof(*ctx)); -} - -/* **************************************** */ - -static u_int32_t ndpi_ssl_refine_master_protocol(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow, u_int32_t protocol) -{ - struct ndpi_packet_struct *packet = &flow->packet; - - if(((flow->l4.tcp.ssl_seen_client_cert == 1) && (flow->protos.stun_ssl.ssl.ja3_client[0] != '\0')) - || ((flow->l4.tcp.ssl_seen_server_cert == 1) && (flow->protos.stun_ssl.ssl.ja3_server[0] != '\0')) - // || (flow->host_server_name[0] != '\0') - ) - protocol = NDPI_PROTOCOL_SSL; - else - protocol = NDPI_PROTOCOL_SSL_NO_CERT; - - if(packet->tcp != NULL) { - switch(protocol) { - - case NDPI_PROTOCOL_SSL: - case NDPI_PROTOCOL_SSL_NO_CERT: - { - /* - In case of SSL there are probably sub-protocols - such as IMAPS that can be otherwise detected - */ - 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)) - protocol = NDPI_PROTOCOL_MAIL_SMTPS; - else if((sport == 993) || (dport == 993) - || (flow->l4.tcp.mail_imap_starttls) - ) protocol = NDPI_PROTOCOL_MAIL_IMAPS; - else if((sport == 995) || (dport == 995)) protocol = NDPI_PROTOCOL_MAIL_POPS; - } - break; - } - } - - return protocol; -} - -static void ndpi_int_ssl_add_connection(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow, u_int32_t protocol) -{ - if((protocol != NDPI_PROTOCOL_SSL) - && (protocol != NDPI_PROTOCOL_SSL_NO_CERT)) { - ndpi_set_detected_protocol(ndpi_struct, flow, protocol, NDPI_PROTOCOL_UNKNOWN); - } else { - protocol = ndpi_ssl_refine_master_protocol(ndpi_struct, flow, protocol); - ndpi_set_detected_protocol(ndpi_struct, flow, protocol, NDPI_PROTOCOL_UNKNOWN); - } -} - -/* Can't call libc functions from kernel space, define some stub instead */ - -#define ndpi_isalpha(ch) (((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z')) -#define ndpi_isdigit(ch) ((ch) >= '0' && (ch) <= '9') -#define ndpi_isspace(ch) (((ch) >= '\t' && (ch) <= '\r') || ((ch) == ' ')) -#define ndpi_isprint(ch) ((ch) >= 0x20 && (ch) <= 0x7e) -#define ndpi_ispunct(ch) (((ch) >= '!' && (ch) <= '/') || \ - ((ch) >= ':' && (ch) <= '@') || \ - ((ch) >= '[' && (ch) <= '`') || \ - ((ch) >= '{' && (ch) <= '~')) - -static void stripCertificateTrailer(char *buffer, int buffer_len) { - - int i, is_puny; - - // printf("->%s<-\n", buffer); - - for(i = 0; i < buffer_len; i++) { - // printf("%c [%d]\n", buffer[i], buffer[i]); - - if((buffer[i] != '.') - && (buffer[i] != '-') - && (buffer[i] != '_') - && (buffer[i] != '*') - && (!ndpi_isalpha(buffer[i])) - && (!ndpi_isdigit(buffer[i]))) { - buffer[i] = '\0'; - buffer_len = i; - break; - } - } - - /* check for punycode encoding */ - is_puny = ndpi_check_punycode_string(buffer, buffer_len); - - // not a punycode string - need more checks - if(is_puny == 0) { - - if(i > 0) i--; - - while(i > 0) { - if(!ndpi_isalpha(buffer[i])) { - buffer[i] = '\0'; - buffer_len = i; - i--; - } else - break; - } - - for(i = buffer_len; i > 0; i--) { - if(buffer[i] == '.') break; - else if(ndpi_isdigit(buffer[i])) - buffer[i] = '\0', buffer_len = i; - } - } - - /* Now all lowecase */ - for(i=0; i */ -int getSSLcertificate(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow, - char *buffer, int buffer_len) { - struct ndpi_packet_struct *packet = &flow->packet; - struct ja3_info ja3; - int i; - u_int8_t invalid_ja3 = 0; - u_int16_t pkt_ssl_version = (packet->payload[1] << 8) + packet->payload[2], ja3_str_len; - char ja3_str[JA3_STR_LEN]; - MD5_CTX ctx; - u_char md5_hash[16]; - - flow->protos.stun_ssl.ssl.ssl_version = pkt_ssl_version; - - memset(&ja3, 0, sizeof(ja3)); - -#ifdef CERTIFICATE_DEBUG - { - u_int16_t ssl_len = (packet->payload[3] << 8) + packet->payload[4]; - - printf("SSL Record [version: %u][len: %u]\n", pkt_ssl_version, ssl_len); - } -#endif - - /* - Nothing matched so far: let's decode the certificate with some heuristics - Patches courtesy of Denys Fedoryshchenko - */ - if(packet->payload[0] == 0x16 /* Handshake */) { - u_int16_t total_len = (packet->payload[3] << 8) + packet->payload[4] + 5 /* SSL Header */; - u_int8_t handshake_protocol = packet->payload[5]; /* handshake protocol a bit misleading, it is message type according TLS specs */ - - memset(buffer, 0, buffer_len); - - /* Truncate total len, search at least in incomplete packet */ - if(total_len > packet->payload_packet_len) - total_len = packet->payload_packet_len; - - /* At least "magic" 3 bytes, null for string end, otherwise no need to waste cpu cycles */ - if(total_len > 4) { -#ifdef CERTIFICATE_DEBUG - printf("SSL [len: %u][handshake_protocol: %02X]\n", packet->payload_packet_len, handshake_protocol); -#endif - - if((handshake_protocol == 0x02) - || (handshake_protocol == 0xb) /* Server Hello and Certificate message types are interesting for us */) { - u_int num_found = 0; - u_int16_t ssl_version = ntohs(*((u_int16_t*)&packet->payload[9])); - int i; - - ja3.ssl_version = ssl_version; - - if(handshake_protocol == 0x02) { - u_int16_t offset = 43, extension_len, j; - u_int8_t session_id_len = packet->payload[43]; - -#ifdef CERTIFICATE_DEBUG - printf("SSL Server Hello [version: 0x%04X]\n", ssl_version); -#endif - - /* - The server hello decides about the SSL version of this flow - https://networkengineering.stackexchange.com/questions/55752/why-does-wireshark-show-version-tls-1-2-here-instead-of-tls-1-3 - */ - flow->protos.stun_ssl.ssl.ssl_version = ssl_version; - - if(ssl_version < 0x7F15 /* TLS 1.3 lacks of session id */) - offset += session_id_len+1; - - ja3.num_cipher = 1, ja3.cipher[0] = ntohs(*((u_int16_t*)&packet->payload[offset])); - flow->protos.stun_ssl.ssl.server_unsafe_cipher = ndpi_is_safe_ssl_cipher(ja3.cipher[0]); - flow->protos.stun_ssl.ssl.server_cipher = ja3.cipher[0]; - -#ifdef CERTIFICATE_DEBUG - printf("SSL [server][session_id_len: %u][cipher: %04X]\n", session_id_len, ja3.cipher[0]); -#endif - - offset += 2 + 1; - extension_len = ntohs(*((u_int16_t*)&packet->payload[offset])); - -#ifdef CERTIFICATE_DEBUG - printf("SSL [server][extension_len: %u]\n", extension_len); -#endif - offset += 2; - - for(i=0; i= (packet->payload_packet_len+4)) break; - - id = ntohs(*((u_int16_t*)&packet->payload[offset])); - len = ntohs(*((u_int16_t*)&packet->payload[offset+2])); - - if(ja3.num_ssl_extension < MAX_NUM_JA3) - ja3.ssl_extension[ja3.num_ssl_extension++] = id; - -#ifdef CERTIFICATE_DEBUG - printf("SSL [server][extension_id: %u]\n", id); -#endif - - i += 4 + len, offset += 4 + len; - } - - ja3_str_len = snprintf(ja3_str, sizeof(ja3_str), "%u,", ja3.ssl_version); - - for(i=0; i 0) ? "-" : "", ja3.cipher[i]); - - ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); - - /* ********** */ - - for(i=0; i 0) ? "-" : "", ja3.ssl_extension[i]); - -#ifdef CERTIFICATE_DEBUG - printf("SSL [server] %s\n", ja3_str); -#endif - -#ifdef CERTIFICATE_DEBUG - printf("[JA3] Server: %s \n", ja3_str); -#endif - - MD5Init(&ctx); - MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str)); - MD5Final(md5_hash, &ctx); - - for(i=0, j=0; i<16; i++) - j += snprintf(&flow->protos.stun_ssl.ssl.ja3_server[j], - sizeof(flow->protos.stun_ssl.ssl.ja3_server)-j, "%02x", md5_hash[i]); - -#ifdef CERTIFICATE_DEBUG - printf("[JA3] Server: %s \n", flow->protos.stun_ssl.ssl.ja3_server); -#endif - - flow->l4.tcp.ssl_seen_server_cert = 1; - } else - flow->l4.tcp.ssl_seen_certificate = 1; - - /* Check after handshake protocol header (5 bytes) and message header (4 bytes) */ - for(i = 9; i < packet->payload_packet_len-3; i++) { - if(((packet->payload[i] == 0x04) && (packet->payload[i+1] == 0x03) && (packet->payload[i+2] == 0x0c)) - || ((packet->payload[i] == 0x04) && (packet->payload[i+1] == 0x03) && (packet->payload[i+2] == 0x13)) - || ((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x03))) { - u_int8_t server_len = packet->payload[i+3]; - - if(packet->payload[i] == 0x55) { - num_found++; - - if(num_found != 2) continue; - } - - if((server_len+i+3) < packet->payload_packet_len) { - char *server_name = (char*)&packet->payload[i+4]; - u_int8_t begin = 0, len, j, num_dots; - - while(begin < server_len) { - if(!ndpi_isprint(server_name[begin])) - begin++; - else - break; - } - - // len = ndpi_min(server_len-begin, buffer_len-1); - len = buffer_len-1; - strncpy(buffer, &server_name[begin], len); - buffer[len] = '\0'; - - /* We now have to check if this looks like an IP address or host name */ - for(j=0, num_dots = 0; j=1) break; - } - } - - if(num_dots >= 1) { - if(!ndpi_struct->disable_metadata_export) { - stripCertificateTrailer(buffer, buffer_len); - snprintf(flow->protos.stun_ssl.ssl.server_certificate, - sizeof(flow->protos.stun_ssl.ssl.server_certificate), "%s", buffer); - } - - return(1 /* Server Certificate */); - } - } - } - } - } else if(handshake_protocol == 0x01 /* Client Hello */) { - u_int offset, base_offset = 43; - - if(base_offset + 2 <= packet->payload_packet_len) { - u_int16_t session_id_len = packet->payload[base_offset]; - u_int16_t ssl_version = ntohs(*((u_int16_t*)&packet->payload[9])); - - ja3.ssl_version = ssl_version; - - if((session_id_len+base_offset+2) <= total_len) { - u_int16_t cipher_len = packet->payload[session_id_len+base_offset+2] + (packet->payload[session_id_len+base_offset+1] << 8); - u_int16_t cipher_offset = base_offset + session_id_len + 3; - -#ifdef CERTIFICATE_DEBUG - printf("Client SSL [client cipher_len: %u]\n", cipher_len); -#endif - - if((cipher_offset+cipher_len) <= total_len) { - for(i=0; ipayload[cipher_offset+i]; - -#ifdef CERTIFICATE_DEBUG - printf("Client SSL [cipher suite: %u] [%u/%u]\n", ntohs(*id), i, cipher_len); -#endif - if((*id == 0) || (packet->payload[cipher_offset+i] != packet->payload[cipher_offset+i+1])) { - /* - Skip GREASE [https://tools.ietf.org/id/draft-ietf-tls-grease-01.html] - https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967 - */ - - if(ja3.num_cipher < MAX_NUM_JA3) - ja3.cipher[ja3.num_cipher++] = ntohs(*id); - else { - invalid_ja3 = 1; -#ifdef CERTIFICATE_DEBUG - printf("Client SSL Invalid cipher %u\n", ja3.num_cipher); -#endif - } - } - - i += 2; - } - } else { - invalid_ja3 = 1; -#ifdef CERTIFICATE_DEBUG - printf("Client SSL Invalid len %u vs %u\n", (cipher_offset+cipher_len), total_len); -#endif - } - - offset = base_offset + session_id_len + cipher_len + 2; - - flow->l4.tcp.ssl_seen_client_cert = 1; - - if(offset < total_len) { - u_int16_t compression_len; - u_int16_t extensions_len; - - offset++; - compression_len = packet->payload[offset]; - offset++; - -#ifdef CERTIFICATE_DEBUG - printf("Client SSL [compression_len: %u]\n", compression_len); -#endif - - // offset += compression_len + 3; - offset += compression_len; - - if(offset < total_len) { - extensions_len = ntohs(*((u_int16_t*)&packet->payload[offset])); - offset += 2; - -#ifdef CERTIFICATE_DEBUG - printf("Client SSL [extensions_len: %u]\n", extensions_len); -#endif - - if((extensions_len+offset) <= total_len) { - /* Move to the first extension - Type is u_int to avoid possible overflow on extension_len addition */ - u_int extension_offset = 0; - u_int32_t j; - - while(extension_offset < extensions_len) { - u_int16_t extension_id, extension_len, extn_off = offset+extension_offset; - - extension_id = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset])); - extension_offset += 2; - - extension_len = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset])); - extension_offset += 2; - -#ifdef CERTIFICATE_DEBUG - printf("Client SSL [extension_id: %u][extension_len: %u]\n", extension_id, extension_len); -#endif - - if((extension_id == 0) || (packet->payload[extn_off] != packet->payload[extn_off+1])) { - /* Skip GREASE */ - - if(ja3.num_ssl_extension < MAX_NUM_JA3) - ja3.ssl_extension[ja3.num_ssl_extension++] = extension_id; - else { - invalid_ja3 = 1; -#ifdef CERTIFICATE_DEBUG - printf("Client SSL Invalid extensions %u\n", ja3.num_ssl_extension); -#endif - } - } - - if(extension_id == 0 /* server name */) { - u_int16_t len; - - len = (packet->payload[offset+extension_offset+3] << 8) + packet->payload[offset+extension_offset+4]; - len = (u_int)ndpi_min(len, buffer_len-1); - strncpy(buffer, (char*)&packet->payload[offset+extension_offset+5], len); - buffer[len] = '\0'; - - stripCertificateTrailer(buffer, buffer_len); - - if(!ndpi_struct->disable_metadata_export) { - snprintf(flow->protos.stun_ssl.ssl.client_certificate, - sizeof(flow->protos.stun_ssl.ssl.client_certificate), "%s", buffer); - } - } else if(extension_id == 10 /* supported groups */) { - u_int16_t s_offset = offset+extension_offset + 2; - -#ifdef CERTIFICATE_DEBUG - printf("Client SSL [EllipticCurveGroups: len=%u]\n", extension_len); -#endif - - if((s_offset+extension_len-2) <= total_len) { - for(i=0; ipayload[s_offset+i])); - -#ifdef CERTIFICATE_DEBUG - printf("Client SSL [EllipticCurve: %u]\n", s_group); -#endif - if((s_group == 0) || (packet->payload[s_offset+i] != packet->payload[s_offset+i+1])) { - /* Skip GREASE */ - if(ja3.num_elliptic_curve < MAX_NUM_JA3) - ja3.elliptic_curve[ja3.num_elliptic_curve++] = s_group; - else { - invalid_ja3 = 1; -#ifdef CERTIFICATE_DEBUG - printf("Client SSL Invalid num elliptic %u\n", ja3.num_elliptic_curve); -#endif - } - } - - i += 2; - } - } else { - invalid_ja3 = 1; -#ifdef CERTIFICATE_DEBUG - printf("Client SSL Invalid len %u vs %u\n", (s_offset+extension_len-1), total_len); -#endif - } - } else if(extension_id == 11 /* ec_point_formats groups */) { - u_int16_t s_offset = offset+extension_offset + 1; - -#ifdef CERTIFICATE_DEBUG - printf("Client SSL [EllipticCurveFormat: len=%u]\n", extension_len); -#endif - if((s_offset+extension_len) < total_len) { - for(i=0; ipayload[s_offset+i]; - -#ifdef CERTIFICATE_DEBUG - printf("Client SSL [EllipticCurveFormat: %u]\n", s_group); -#endif - - if(ja3.num_elliptic_curve_point_format < MAX_NUM_JA3) - ja3.elliptic_curve_point_format[ja3.num_elliptic_curve_point_format++] = s_group; - else { - invalid_ja3 = 1; -#ifdef CERTIFICATE_DEBUG - printf("Client SSL Invalid num elliptic %u\n", ja3.num_elliptic_curve_point_format); -#endif - } - } - } else { - invalid_ja3 = 1; -#ifdef CERTIFICATE_DEBUG - printf("Client SSL Invalid len %u vs %u\n", s_offset+extension_len, total_len); -#endif - } - } - - extension_offset += extension_len; - -#ifdef CERTIFICATE_DEBUG - printf("Client SSL [extension_offset/len: %u/%u]\n", extension_offset, extension_len); -#endif - } /* while */ - - if(!invalid_ja3) { - ja3_str_len = snprintf(ja3_str, sizeof(ja3_str), "%u,", ja3.ssl_version); - - for(i=0; i 0) ? "-" : "", ja3.cipher[i]); - } - - ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); - - /* ********** */ - - for(i=0; i 0) ? "-" : "", ja3.ssl_extension[i]); - - ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); - - /* ********** */ - - for(i=0; i 0) ? "-" : "", ja3.elliptic_curve[i]); - - ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); - - for(i=0; i 0) ? "-" : "", ja3.elliptic_curve_point_format[i]); - -#ifdef CERTIFICATE_DEBUG - printf("[JA3] Client: %s \n", ja3_str); -#endif - - MD5Init(&ctx); - MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str)); - MD5Final(md5_hash, &ctx); - - for(i=0, j=0; i<16; i++) - j += snprintf(&flow->protos.stun_ssl.ssl.ja3_client[j], - sizeof(flow->protos.stun_ssl.ssl.ja3_client)-j, "%02x", md5_hash[i]); - -#ifdef CERTIFICATE_DEBUG - printf("[JA3] Client: %s \n", flow->protos.stun_ssl.ssl.ja3_client); -#endif - } - - return(2 /* Client Certificate */); - } - } - } - } - } - } - } - } - - return(0); /* Not found */ -} - -void getSSLorganization(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow, - char *buffer, int buffer_len) { - struct ndpi_packet_struct *packet = &flow->packet; - - if(packet->payload[0] != 0x16 /* Handshake */) - return; - - u_int16_t total_len = (packet->payload[3] << 8) + packet->payload[4] + 5 /* SSL Header */; - u_int8_t handshake_protocol = packet->payload[5]; /* handshake protocol a bit misleading, it is message type according TLS specs */ - - if(handshake_protocol != 0x02 && handshake_protocol != 0xb /* Server Hello and Certificate message types are interesting for us */) - return; - - /* Truncate total len, search at least in incomplete packet */ - if(total_len > packet->payload_packet_len) - total_len = packet->payload_packet_len; - - memset(buffer, 0, buffer_len); - - /* Check after handshake protocol header (5 bytes) and message header (4 bytes) */ - u_int num_found = 0; - u_int i, j; - for(i = 9; i < packet->payload_packet_len-4; i++) { - /* Organization OID: 2.5.4.10 */ - if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x0a)) { - u_int8_t server_len = packet->payload[i+4]; - - num_found++; - /* what we want is subject certificate, so we bypass the issuer certificate */ - if(num_found != 2) continue; - - // packet is truncated... further inspection is not needed - if(i+4+server_len >= packet->payload_packet_len) { - break; - } - - char *server_org = (char*)&packet->payload[i+5]; - - u_int len = (u_int)ndpi_min(server_len, buffer_len-1); - strncpy(buffer, server_org, len); - buffer[len] = '\0'; - - // check if organization string are all printable - u_int8_t is_printable = 1; - for (j = 0; j < len; j++) { - if(!ndpi_isprint(buffer[j])) { - is_printable = 0; - break; - } - } - - if(is_printable == 1) { - snprintf(flow->protos.stun_ssl.ssl.server_organization, - sizeof(flow->protos.stun_ssl.ssl.server_organization), "%s", buffer); -#ifdef CERTIFICATE_DEBUG - printf("Certificate organization: %s\n", flow->protos.stun_ssl.ssl.server_organization); -#endif - } - } - } -} - - -int sslTryAndRetrieveServerCertificate(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { - struct ndpi_packet_struct *packet = &flow->packet; - - /* consider only specific SSL packets (handshake) */ - if((packet->payload_packet_len > 9) && (packet->payload[0] == 0x16)) { - char certificate[64]; - int rc; - - certificate[0] = '\0'; - rc = getSSLcertificate(ndpi_struct, flow, certificate, sizeof(certificate)); - packet->ssl_certificate_num_checks++; - - if(rc > 0) { - char organization[64]; - - // try fetch server organization once server certificate is found - organization[0] = '\0'; - getSSLorganization(ndpi_struct, flow, organization, sizeof(organization)); - - packet->ssl_certificate_detected++; - if((flow->l4.tcp.ssl_seen_server_cert == 1) && (flow->protos.stun_ssl.ssl.server_certificate[0] != '\0')) - /* 0 means we're done processing extra packets (since we found what we wanted) */ - return 0; - } - - /* Client hello, Server Hello, and certificate packets probably all checked in this case */ - if((packet->ssl_certificate_num_checks >= 3) - && (flow->l4.tcp.seen_syn) - && (flow->l4.tcp.seen_syn_ack) - && (flow->l4.tcp.seen_ack) /* We have seen the 3-way handshake */) - { - /* We're done processing extra packets since we've probably checked all possible cert packets */ - return 0; - } - } - /* 1 means keep looking for more packets */ - return 1; -} - -void sslInitExtraPacketProcessing(int caseNum, struct ndpi_flow_struct *flow) { - flow->check_extra_packets = 1; - /* 0 is the case for waiting for the server certificate */ - if(caseNum == 0) { - /* At most 7 packets should almost always be enough to find the server certificate if it's there */ - flow->max_extra_packets_to_check = 7; - flow->extra_packets_func = sslTryAndRetrieveServerCertificate; - } -} - -int sslDetectProtocolFromCertificate(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { - struct ndpi_packet_struct *packet = &flow->packet; - - if((packet->payload_packet_len > 9) - && (packet->payload[0] == 0x16 /* consider only specific SSL packets (handshake) */)) { - if((packet->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN) - || (packet->detected_protocol_stack[0] == NDPI_PROTOCOL_SSL)) { - char certificate[64]; - int rc; - - certificate[0] = '\0'; - rc = getSSLcertificate(ndpi_struct, flow, certificate, sizeof(certificate)); - packet->ssl_certificate_num_checks++; - - if(rc > 0) { - packet->ssl_certificate_detected++; -#ifdef CERTIFICATE_DEBUG - NDPI_LOG_DBG2(ndpi_struct, "***** [SSL] %s\n", certificate); -#endif - ndpi_protocol_match_result ret_match; - u_int32_t subproto = ndpi_match_host_subprotocol(ndpi_struct, flow, certificate, - strlen(certificate), - &ret_match, - NDPI_PROTOCOL_SSL); - - if(subproto != NDPI_PROTOCOL_UNKNOWN) { - /* If we've detected the subprotocol from client certificate but haven't had a chance - * to see the server certificate yet, set up extra packet processing to wait - * a few more packets. */ - if(((flow->l4.tcp.ssl_seen_client_cert == 1) && (flow->protos.stun_ssl.ssl.client_certificate[0] != '\0')) - && ((flow->l4.tcp.ssl_seen_server_cert != 1) && (flow->protos.stun_ssl.ssl.server_certificate[0] == '\0'))) { - sslInitExtraPacketProcessing(0, flow); - } - - ndpi_set_detected_protocol(ndpi_struct, flow, subproto, - ndpi_ssl_refine_master_protocol(ndpi_struct, flow, NDPI_PROTOCOL_SSL)); - return(rc); /* Fix courtesy of Gianluca Costa */ - } - - if(ndpi_is_ssl_tor(ndpi_struct, flow, certificate) != 0) - return(rc); - } - - if(((packet->ssl_certificate_num_checks >= 3) - && flow->l4.tcp.seen_syn - && flow->l4.tcp.seen_syn_ack - && flow->l4.tcp.seen_ack /* We have seen the 3-way handshake */) - || ((flow->l4.tcp.ssl_seen_certificate == 1) - && (flow->l4.tcp.ssl_seen_server_cert == 1) - && (flow->protos.stun_ssl.ssl.server_certificate[0] != '\0')) - /* || ((flow->l4.tcp.ssl_seen_client_cert == 1) && (flow->protos.stun_ssl.ssl.client_certificate[0] != '\0')) */ - ) { - ndpi_int_ssl_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_SSL); - } - } - } - return(0); -} - -static void ssl_mark_and_payload_search_for_other_protocols(struct ndpi_detection_module_struct - *ndpi_struct, struct ndpi_flow_struct *flow) -{ - struct ndpi_packet_struct *packet = &flow->packet; - u_int32_t a; - u_int32_t end; - - if(NDPI_COMPARE_PROTOCOL_TO_BITMASK(ndpi_struct->detection_bitmask, NDPI_PROTOCOL_UNENCRYPTED_JABBER) != 0) - goto check_for_ssl_payload; - - if(NDPI_COMPARE_PROTOCOL_TO_BITMASK(ndpi_struct->detection_bitmask, NDPI_PROTOCOL_OSCAR) != 0) - goto check_for_ssl_payload; - else - goto no_check_for_ssl_payload; - - check_for_ssl_payload: - end = packet->payload_packet_len - 20; - for (a = 5; a < end; a++) { - - if(packet->payload[a] == 't') { - if(memcmp(&packet->payload[a], "talk.google.com", 15) == 0) { - if(NDPI_COMPARE_PROTOCOL_TO_BITMASK - (ndpi_struct->detection_bitmask, NDPI_PROTOCOL_UNENCRYPTED_JABBER) != 0) { - NDPI_LOG_INFO(ndpi_struct, "found ssl jabber unencrypted\n"); - ndpi_int_ssl_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNENCRYPTED_JABBER); - return; - } - } - } - - if(packet->payload[a] == 'A' || packet->payload[a] == 'k' || packet->payload[a] == 'c' - || packet->payload[a] == 'h') { - if(((a + 19) < packet->payload_packet_len && memcmp(&packet->payload[a], "America Online Inc.", 19) == 0) - // || (end - c > 3 memcmp (&packet->payload[c],"AOL", 3) == 0 ) - // || (end - c > 7 && memcmp (&packet->payload[c], "AOL LLC", 7) == 0) - || ((a + 15) < packet->payload_packet_len && memcmp(&packet->payload[a], "kdc.uas.aol.com", 15) == 0) - || ((a + 14) < packet->payload_packet_len && memcmp(&packet->payload[a], "corehc@aol.net", 14) == 0) - || ((a + 41) < packet->payload_packet_len - && memcmp(&packet->payload[a], "http://crl.aol.com/AOLMSPKI/aolServerCert", 41) == 0) - || ((a + 28) < packet->payload_packet_len - && memcmp(&packet->payload[a], "http://ocsp.web.aol.com/ocsp", 28) == 0) - || ((a + 32) < packet->payload_packet_len - && memcmp(&packet->payload[a], "http://pki-info.aol.com/AOLMSPKI", 32) == 0)) { - NDPI_LOG_INFO(ndpi_struct, "found OSCAR SERVER SSL DETECTED\n"); - - if(flow->dst != NULL && packet->payload_packet_len > 75) { - memcpy(flow->dst->oscar_ssl_session_id, &packet->payload[44], 32); - flow->dst->oscar_ssl_session_id[32] = '\0'; - flow->dst->oscar_last_safe_access_time = packet->tick_timestamp; - } - - ndpi_int_ssl_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_OSCAR); - return; - } - } - - if(packet->payload[a] == 'm' || packet->payload[a] == 's') { - if((a + 21) < packet->payload_packet_len && - (memcmp(&packet->payload[a], "my.screenname.aol.com", 21) == 0 - || memcmp(&packet->payload[a], "sns-static.aolcdn.com", 21) == 0)) { - NDPI_LOG_DBG(ndpi_struct, "found OSCAR SERVER SSL DETECTED\n"); - ndpi_int_ssl_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_OSCAR); - return; - } - } - } - - no_check_for_ssl_payload: - if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN) { - NDPI_LOG_DBG(ndpi_struct, "found ssl connection\n"); - sslDetectProtocolFromCertificate(ndpi_struct, flow); - - if(!packet->ssl_certificate_detected - && (!(flow->l4.tcp.ssl_seen_client_cert && flow->l4.tcp.ssl_seen_server_cert))) { - /* SSL without certificate (Skype, Ultrasurf?) */ - NDPI_LOG_INFO(ndpi_struct, "found ssl NO_CERT\n"); - ndpi_int_ssl_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_SSL_NO_CERT); - } else if(packet->ssl_certificate_num_checks >= 3) { - NDPI_LOG_INFO(ndpi_struct, "found ssl\n"); - ndpi_int_ssl_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_SSL); - } - } -} - - -static u_int8_t ndpi_search_sslv3_direction1(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow) { - struct ndpi_packet_struct *packet = &flow->packet; - - if((packet->payload_packet_len >= 5) - && ((packet->payload[0] == 0x16) || packet->payload[0] == 0x17) - && (packet->payload[1] == 0x03) - && ((packet->payload[2] == 0x00) || (packet->payload[2] == 0x01) || - (packet->payload[2] == 0x02) || (packet->payload[2] == 0x03))) { - u_int32_t temp; - NDPI_LOG_DBG2(ndpi_struct, "search sslv3\n"); - // SSLv3 Record - if(packet->payload_packet_len >= 1300) { - return 1; - } - temp = ntohs(get_u_int16_t(packet->payload, 3)) + 5; - NDPI_LOG_DBG2(ndpi_struct, "temp = %u\n", temp); - if(packet->payload_packet_len == temp - || (temp < packet->payload_packet_len && packet->payload_packet_len > 500)) { - return 1; - } - - if(packet->payload_packet_len < temp && temp < 5000 && packet->payload_packet_len > 9) { - /* the server hello may be split into small packets */ - u_int32_t cert_start; - - NDPI_LOG_DBG2(ndpi_struct, - "maybe SSLv3 server hello split into smaller packets\n"); - - /* lets hope at least the server hello and the start of the certificate block are in the first packet */ - cert_start = ntohs(get_u_int16_t(packet->payload, 7)) + 5 + 4; - NDPI_LOG_DBG2(ndpi_struct, "suspected start of certificate: %u\n", - cert_start); - - if(cert_start < packet->payload_packet_len && packet->payload[cert_start] == 0x0b) { - NDPI_LOG_DBG2(ndpi_struct, - "found 0x0b at suspected start of certificate block\n"); - return 2; - } - } - - if((packet->payload_packet_len > temp) && (packet->payload_packet_len > 100)) { - /* the server hello may be split into small packets and the certificate has its own SSL Record - * so temp contains only the length for the first ServerHello block */ - u_int32_t cert_start; - - NDPI_LOG_DBG2(ndpi_struct, - "maybe SSLv3 server hello split into smaller packets but with seperate record for the certificate\n"); - - /* lets hope at least the server hello record and the start of the certificate record are in the first packet */ - cert_start = ntohs(get_u_int16_t(packet->payload, 7)) + 5 + 5 + 4; - NDPI_LOG_DBG2(ndpi_struct, "suspected start of certificate: %u\n", - cert_start); - - if(cert_start < packet->payload_packet_len && packet->payload[cert_start] == 0x0b) { - NDPI_LOG_DBG2(ndpi_struct, - "found 0x0b at suspected start of certificate block\n"); - return 2; - } - } - - - if(packet->payload_packet_len >= temp + 5 && (packet->payload[temp] == 0x14 || packet->payload[temp] == 0x16) - && packet->payload[temp + 1] == 0x03) { - u_int32_t temp2 = ntohs(get_u_int16_t(packet->payload, temp + 3)) + 5; - if(temp + temp2 > NDPI_MAX_SSL_REQUEST_SIZE) { - return 1; - } - temp += temp2; - NDPI_LOG_DBG2(ndpi_struct, "temp = %u\n", temp); - if(packet->payload_packet_len == temp) { - return 1; - } - if(packet->payload_packet_len >= temp + 5 && - packet->payload[temp] == 0x16 && packet->payload[temp + 1] == 0x03) { - temp2 = ntohs(get_u_int16_t(packet->payload, temp + 3)) + 5; - if(temp + temp2 > NDPI_MAX_SSL_REQUEST_SIZE) { - return 1; - } - temp += temp2; - NDPI_LOG_DBG2(ndpi_struct, "temp = %u\n", temp); - if(packet->payload_packet_len == temp) { - return 1; - } - if(packet->payload_packet_len >= temp + 5 && - packet->payload[temp] == 0x16 && packet->payload[temp + 1] == 0x03) { - temp2 = ntohs(get_u_int16_t(packet->payload, temp + 3)) + 5; - if(temp + temp2 > NDPI_MAX_SSL_REQUEST_SIZE) { - return 1; - } - temp += temp2; - NDPI_LOG_DBG2(ndpi_struct, "temp = %u\n", temp); - if(temp == packet->payload_packet_len) { - return 1; - } - } - } - } - } - - return 0; -} - -void ndpi_search_ssl_tcp(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) -{ - struct ndpi_packet_struct *packet = &flow->packet; - u_int8_t ret; - - if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_SSL) { - if(flow->l4.tcp.ssl_stage == 3 && packet->payload_packet_len > 20 && flow->packet_counter < 5) { - /* this should only happen, when we detected SSL with a packet that had parts of the certificate in subsequent packets - * so go on checking for certificate patterns for a couple more packets - */ - NDPI_LOG_DBG2(ndpi_struct, - "ssl flow but check another packet for patterns\n"); - ssl_mark_and_payload_search_for_other_protocols(ndpi_struct, flow); - if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_SSL) { - /* still ssl so check another packet */ - return; - } else { - /* protocol has changed so we are done */ - return; - } - } - return; - } - - NDPI_LOG_DBG(ndpi_struct, "search ssl\n"); - - /* Check if this is whatsapp first (this proto runs over port 443) */ - if((packet->payload_packet_len > 5) - && ((packet->payload[0] == 'W') - && (packet->payload[1] == 'A') - && (packet->payload[4] == 0) - && (packet->payload[2] <= 9) - && (packet->payload[3] <= 9))) { - ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_WHATSAPP, NDPI_PROTOCOL_UNKNOWN); - return; - } else if((packet->payload_packet_len == 4) - && (packet->payload[0] == 'W') - && (packet->payload[1] == 'A')) { - ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_WHATSAPP, NDPI_PROTOCOL_UNKNOWN); - return; - } else { - /* No whatsapp, let's try SSL */ - if(sslDetectProtocolFromCertificate(ndpi_struct, flow) > 0) - return; - } - - if(packet->payload_packet_len > 40 && flow->l4.tcp.ssl_stage == 0) { - NDPI_LOG_DBG2(ndpi_struct, "first ssl packet\n"); - // SSLv2 Record - if(packet->payload[2] == 0x01 && packet->payload[3] == 0x03 - && (packet->payload[4] == 0x00 || packet->payload[4] == 0x01 || packet->payload[4] == 0x02) - && (packet->payload_packet_len - packet->payload[1] == 2)) { - NDPI_LOG_DBG2(ndpi_struct, "sslv2 len match\n"); - flow->l4.tcp.ssl_stage = 1 + packet->packet_direction; - return; - } - - if(packet->payload[0] == 0x16 && packet->payload[1] == 0x03 - && (packet->payload[2] == 0x00 || packet->payload[2] == 0x01 || packet->payload[2] == 0x02) - && (packet->payload_packet_len - ntohs(get_u_int16_t(packet->payload, 3)) == 5)) { - // SSLv3 Record - NDPI_LOG_DBG2(ndpi_struct, "sslv3 len match\n"); - flow->l4.tcp.ssl_stage = 1 + packet->packet_direction; - return; - } - - // Application Data pkt - if(packet->payload[0] == 0x17 && packet->payload[1] == 0x03 - && (packet->payload[2] == 0x00 || packet->payload[2] == 0x01 || - packet->payload[2] == 0x02 || packet->payload[2] == 0x03)) { - if(packet->payload_packet_len - ntohs(get_u_int16_t(packet->payload, 3)) == 5) { - NDPI_LOG_DBG2(ndpi_struct, "TLS len match\n"); - flow->l4.tcp.ssl_stage = 1 + packet->packet_direction; - return; - } - } - } - - if(packet->payload_packet_len > 40 && - flow->l4.tcp.ssl_stage == 1 + packet->packet_direction - && flow->packet_direction_counter[packet->packet_direction] < 5) { - return; - } - - if(packet->payload_packet_len > 40 && flow->l4.tcp.ssl_stage == 2 - packet->packet_direction) { - NDPI_LOG_DBG2(ndpi_struct, "second ssl packet\n"); - // SSLv2 Record - if(packet->payload[2] == 0x01 && packet->payload[3] == 0x03 - && (packet->payload[4] == 0x00 || packet->payload[4] == 0x01 || packet->payload[4] == 0x02) - && (packet->payload_packet_len - 2) >= packet->payload[1]) { - NDPI_LOG_DBG2(ndpi_struct, "sslv2 server len match\n"); - ssl_mark_and_payload_search_for_other_protocols(ndpi_struct, flow); - return; - } - - ret = ndpi_search_sslv3_direction1(ndpi_struct, flow); - if(ret == 1) { - NDPI_LOG_DBG2(ndpi_struct, "sslv3 server len match\n"); - ssl_mark_and_payload_search_for_other_protocols(ndpi_struct, flow); - return; - } else if(ret == 2) { - NDPI_LOG_DBG2(ndpi_struct, - "sslv3 server len match with split packet -> check some more packets for SSL patterns\n"); - ssl_mark_and_payload_search_for_other_protocols(ndpi_struct, flow); - if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_SSL) { - flow->l4.tcp.ssl_stage = 3; - } - return; - } - - if(packet->payload_packet_len > 40 && flow->packet_direction_counter[packet->packet_direction] < 5) { - NDPI_LOG_DBG2(ndpi_struct, "need next packet\n"); - return; - } - } - - NDPI_EXCLUDE_PROTO(ndpi_struct, flow); - return; -} - - -void init_ssl_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask) -{ - ndpi_set_bitmask_protocol_detection("SSL", ndpi_struct, detection_bitmask, *id, - NDPI_PROTOCOL_SSL, - ndpi_search_ssl_tcp, - NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_WITH_PAYLOAD, - SAVE_DETECTION_BITMASK_AS_UNKNOWN, - ADD_TO_DETECTION_BITMASK); - - *id += 1; -} diff --git a/src/lib/protocols/stun.c b/src/lib/protocols/stun.c index bcf957340..a35ab1ad7 100644 --- a/src/lib/protocols/stun.c +++ b/src/lib/protocols/stun.c @@ -105,7 +105,7 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * if(payload_length < sizeof(struct stun_packet_header)) { /* This looks like an invlid packet */ - if(flow->protos.stun_ssl.stun.num_udp_pkts > 0) { + if(flow->protos.stun_tls.stun.num_udp_pkts > 0) { *is_whatsapp = 1; return(NDPI_IS_STUN); /* This is WhatsApp Voice */ } else @@ -125,7 +125,7 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * return(NDPI_IS_NOT_STUN); if(msg_type == 0x01 /* Binding Request */) { - flow->protos.stun_ssl.stun.num_binding_requests++; + flow->protos.stun_tls.stun.num_binding_requests++; if((msg_len == 0) && (flow->guessed_host_protocol_id == NDPI_PROTOCOL_GOOGLE)) { flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; } @@ -136,11 +136,11 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * return(NDPI_IS_NOT_STUN); } - flow->protos.stun_ssl.stun.num_udp_pkts++; + flow->protos.stun_tls.stun.num_udp_pkts++; /* printf("[msg_type: %04X][payload_length: %u][num_binding_request: %u]\n", - msg_type, payload_length, flow->protos.stun_ssl.stun.num_binding_requests); + msg_type, payload_length, flow->protos.stun_tls.stun.num_binding_requests); */ if(((payload[0] == 0x80) @@ -150,7 +150,7 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * return(NDPI_IS_STUN); /* This is WhatsApp Voice */ } else if((payload[0] == 0x90) && (((msg_len+11) == payload_length) /* WhatsApp Video */ - || (flow->protos.stun_ssl.stun.num_binding_requests >= 4))) { + || (flow->protos.stun_tls.stun.num_binding_requests >= 4))) { *is_whatsapp = 2; return(NDPI_IS_STUN); /* This is WhatsApp Video */ } @@ -211,7 +211,7 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * && (payload[offset+6] == 0x00) && (payload[offset+7] == 0x00)) { /* Either skype for business or "normal" skype with multiparty call */ - flow->protos.stun_ssl.stun.is_skype = 1; + flow->protos.stun_tls.stun.is_skype = 1; return(NDPI_IS_STUN); } break; @@ -226,7 +226,7 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * case 0x8095: case 0x0800: /* printf("====>>>> %04X\n", attribute); */ - flow->protos.stun_ssl.stun.is_skype = 1; + flow->protos.stun_tls.stun.is_skype = 1; return(NDPI_IS_STUN); break; @@ -238,7 +238,7 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * && (payload[offset+6] == 0x00) && ((payload[offset+7] == 0x02) || (payload[offset+7] == 0x03)) ) { - flow->protos.stun_ssl.stun.is_skype = 1; + flow->protos.stun_tls.stun.is_skype = 1; return(NDPI_IS_STUN); } break; @@ -266,7 +266,7 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * } } - if((flow->protos.stun_ssl.stun.num_udp_pkts > 0) && (msg_type <= 0x00FF)) { + if((flow->protos.stun_tls.stun.num_udp_pkts > 0) && (msg_type <= 0x00FF)) { *is_whatsapp = 1; return(NDPI_IS_STUN); /* This is WhatsApp Voice */ } else @@ -276,7 +276,7 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * if(can_this_be_whatsapp_voice) { struct ndpi_packet_struct *packet = &flow->packet; - flow->protos.stun_ssl.stun.num_processed_pkts++; + flow->protos.stun_tls.stun.num_processed_pkts++; #ifdef DEBUG_STUN printf("==>> NDPI_PROTOCOL_WHATSAPP_VOICE\n"); #endif @@ -287,7 +287,7 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * } else flow->guessed_host_protocol_id = (is_google_ip_address(ntohl(packet->iph->saddr)) || is_google_ip_address(ntohl(packet->iph->daddr))) ? NDPI_PROTOCOL_HANGOUT_DUO : NDPI_PROTOCOL_WHATSAPP_VOICE; - return((flow->protos.stun_ssl.stun.num_udp_pkts < MAX_NUM_STUN_PKTS) ? NDPI_IS_NOT_STUN : NDPI_IS_STUN); + return((flow->protos.stun_tls.stun.num_udp_pkts < MAX_NUM_STUN_PKTS) ? NDPI_IS_NOT_STUN : NDPI_IS_STUN); } else { /* We cannot immediately say that this is STUN as there are other protocols @@ -330,10 +330,10 @@ void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, struct n } else if(is_duo) { ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_HANGOUT_DUO, NDPI_PROTOCOL_STUN); return; - } else if(flow->protos.stun_ssl.stun.is_skype) { + } else if(flow->protos.stun_tls.stun.is_skype) { NDPI_LOG_INFO(ndpi_struct, "found Skype\n"); - if((flow->protos.stun_ssl.stun.num_processed_pkts >= 8) || (flow->protos.stun_ssl.stun.num_binding_requests >= 4)) + if((flow->protos.stun_tls.stun.num_processed_pkts >= 8) || (flow->protos.stun_tls.stun.num_binding_requests >= 4)) ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_SKYPE_CALL, NDPI_PROTOCOL_SKYPE); } else { NDPI_LOG_INFO(ndpi_struct, "found UDP stun\n"); /* Ummmmm we're in the TCP branch. This code looks bad */ @@ -358,11 +358,11 @@ void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, struct n } else if(is_duo) { ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_HANGOUT_DUO, NDPI_PROTOCOL_STUN); return; - } else if(flow->protos.stun_ssl.stun.is_skype) { + } else if(flow->protos.stun_tls.stun.is_skype) { NDPI_LOG_INFO(ndpi_struct, "Found Skype\n"); - /* flow->protos.stun_ssl.stun.num_binding_requests < 4) ? NDPI_PROTOCOL_SKYPE_CALL_IN : NDPI_PROTOCOL_SKYPE_CALL_OUT */ - if((flow->protos.stun_ssl.stun.num_processed_pkts >= 8) || (flow->protos.stun_ssl.stun.num_binding_requests >= 4)) + /* flow->protos.stun_tls.stun.num_binding_requests < 4) ? NDPI_PROTOCOL_SKYPE_CALL_IN : NDPI_PROTOCOL_SKYPE_CALL_OUT */ + if((flow->protos.stun_tls.stun.num_processed_pkts >= 8) || (flow->protos.stun_tls.stun.num_binding_requests >= 4)) ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_SKYPE_CALL, NDPI_PROTOCOL_SKYPE); } else { NDPI_LOG_INFO(ndpi_struct, "found UDP stun\n"); @@ -375,7 +375,7 @@ void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, struct n return; } - if(flow->protos.stun_ssl.stun.num_udp_pkts >= MAX_NUM_STUN_PKTS) + if(flow->protos.stun_tls.stun.num_udp_pkts >= MAX_NUM_STUN_PKTS) NDPI_EXCLUDE_PROTO(ndpi_struct, flow); if(flow->packet_counter > 0) { diff --git a/src/lib/protocols/tls.c b/src/lib/protocols/tls.c new file mode 100644 index 000000000..188d0bc9a --- /dev/null +++ b/src/lib/protocols/tls.c @@ -0,0 +1,1384 @@ +/* + * tls.c - SSL/TLS/DTLS dissector + * + * Copyright (C) 2016-19 - ntop.org + * + * This file is part of nDPI, an open source deep packet inspection + * library based on the OpenDPI and PACE technology by ipoque GmbH + * + * nDPI is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nDPI 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with nDPI. If not, see . + * + */ + +#include "ndpi_protocol_ids.h" + +#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_TLS + +#include "ndpi_api.h" + +/* #define CERTIFICATE_DEBUG 1 */ + +#define NDPI_MAX_TLS_REQUEST_SIZE 10000 + +/* Skype.c */ +extern u_int8_t is_skype_flow(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow); + +/* **************************************** */ + +typedef struct MD5Context { + uint32_t buf[4]; + uint32_t bits[2]; + unsigned char in[64]; +} MD5_CTX; + +/* **************************************** */ + +static int is_big_endian(void) { + static const int n = 1; + return ((char *) &n)[0] == 0; +} + +static void byteReverse(unsigned char *buf, unsigned longs) { + // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN + if (is_big_endian()) { + do { + uint32_t t; + + t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + * (uint32_t *) buf = t; + buf += 4; + } while (--longs); + } +} + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious +// initialization constants. +static void MD5Init(MD5_CTX *ctx) { + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) { + uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) { + uint32_t t; + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) + ctx->bits[1]++; + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + buf += t; + len -= t; + } + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + buf += 64; + len -= 64; + } + + memcpy(ctx->in, buf, len); +} + +static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) { + unsigned count; + unsigned char *p; + uint32_t *c = (uint32_t*)ctx->in; + + count = (ctx->bits[0] >> 3) & 0x3F; + + p = ctx->in + count; + *p++ = 0x80; + count = 64 - 1 - count; + if (count < 8) { + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + memset(ctx->in, 0, 56); + } else { + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + c[14] = ctx->bits[0]; + c[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset((char *) ctx, 0, sizeof(*ctx)); +} + +/* **************************************** */ + +static u_int32_t ndpi_tls_refine_master_protocol(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, u_int32_t protocol) { + struct ndpi_packet_struct *packet = &flow->packet; + + if(((flow->l4.tcp.tls_seen_client_cert == 1) && (flow->protos.stun_tls.tls.ja3_client[0] != '\0')) + || ((flow->l4.tcp.tls_seen_server_cert == 1) && (flow->protos.stun_tls.tls.ja3_server[0] != '\0')) + // || (flow->host_server_name[0] != '\0') + ) + protocol = NDPI_PROTOCOL_TLS; + else + protocol = NDPI_PROTOCOL_TLS_NO_CERT; + + if(packet->tcp != NULL) { + switch(protocol) { + + case NDPI_PROTOCOL_TLS: + case NDPI_PROTOCOL_TLS_NO_CERT: + { + /* + In case of SSL there are probably sub-protocols + such as IMAPS that can be otherwise detected + */ + 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)) + protocol = NDPI_PROTOCOL_MAIL_SMTPS; + else if((sport == 993) || (dport == 993) + || (flow->l4.tcp.mail_imap_starttls) + ) protocol = NDPI_PROTOCOL_MAIL_IMAPS; + else if((sport == 995) || (dport == 995)) protocol = NDPI_PROTOCOL_MAIL_POPS; + } + break; + } + } + + return protocol; +} + +/* **************************************** */ + +static void ndpi_int_tls_add_connection(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, u_int32_t protocol) { + if((protocol != NDPI_PROTOCOL_TLS) + && (protocol != NDPI_PROTOCOL_TLS_NO_CERT)) { + ndpi_set_detected_protocol(ndpi_struct, flow, protocol, NDPI_PROTOCOL_UNKNOWN); + } else { + protocol = ndpi_tls_refine_master_protocol(ndpi_struct, flow, protocol); + ndpi_set_detected_protocol(ndpi_struct, flow, protocol, NDPI_PROTOCOL_UNKNOWN); + } +} + +/* **************************************** */ + +/* Can't call libc functions from kernel space, define some stub instead */ + +#define ndpi_isalpha(ch) (((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z')) +#define ndpi_isdigit(ch) ((ch) >= '0' && (ch) <= '9') +#define ndpi_isspace(ch) (((ch) >= '\t' && (ch) <= '\r') || ((ch) == ' ')) +#define ndpi_isprint(ch) ((ch) >= 0x20 && (ch) <= 0x7e) +#define ndpi_ispunct(ch) (((ch) >= '!' && (ch) <= '/') || \ + ((ch) >= ':' && (ch) <= '@') || \ + ((ch) >= '[' && (ch) <= '`') || \ + ((ch) >= '{' && (ch) <= '~')) + +/* **************************************** */ + +static void stripCertificateTrailer(char *buffer, int buffer_len) { + int i, is_puny; + + // printf("->%s<-\n", buffer); + + for(i = 0; i < buffer_len; i++) { + // printf("%c [%d]\n", buffer[i], buffer[i]); + + if((buffer[i] != '.') + && (buffer[i] != '-') + && (buffer[i] != '_') + && (buffer[i] != '*') + && (!ndpi_isalpha(buffer[i])) + && (!ndpi_isdigit(buffer[i]))) { + buffer[i] = '\0'; + buffer_len = i; + break; + } + } + + /* check for punycode encoding */ + is_puny = ndpi_check_punycode_string(buffer, buffer_len); + + // not a punycode string - need more checks + if(is_puny == 0) { + + if(i > 0) i--; + + while(i > 0) { + if(!ndpi_isalpha(buffer[i])) { + buffer[i] = '\0'; + buffer_len = i; + i--; + } else + break; + } + + for(i = buffer_len; i > 0; i--) { + if(buffer[i] == '.') break; + else if(ndpi_isdigit(buffer[i])) + buffer[i] = '\0', buffer_len = i; + } + } + + /* Now all lowecase */ + for(i=0; ipacket; + struct ja3_info ja3; + int i; + u_int8_t invalid_ja3 = 0; + u_int16_t pkt_tls_version = (packet->payload[1] << 8) + packet->payload[2], ja3_str_len; + char ja3_str[JA3_STR_LEN]; + MD5_CTX ctx; + u_char md5_hash[16]; + + if(packet->udp) { + /* Check if this is DTLS or return */ + if((packet->payload[1] != 0xfe) + || ((packet->payload[2] != 0xff) && (packet->payload[2] != 0xfd))) { + NDPI_EXCLUDE_PROTO(ndpi_struct, flow); + return(0); + } + } + + flow->protos.stun_tls.tls.tls_version = pkt_tls_version; + + memset(&ja3, 0, sizeof(ja3)); + +#ifdef CERTIFICATE_DEBUG + { + u_int16_t tls_len = (packet->payload[3] << 8) + packet->payload[4]; + + printf("SSL Record [version: 0x%04X][len: %u]\n", pkt_tls_version, tls_len); + } +#endif + + /* + Nothing matched so far: let's decode the certificate with some heuristics + Patches courtesy of Denys Fedoryshchenko + */ + if(packet->payload[0] == 0x16 /* Handshake */) { + u_int16_t total_len; + u_int8_t handshake_protocol, header_len; + + if(packet->tcp) { + header_len = 5; /* SSL Header */ + handshake_protocol = packet->payload[5]; /* handshake protocol a bit misleading, it is message type according TLS specs */ + total_len = (packet->payload[3] << 8) + packet->payload[4]; + } else { + header_len = 13; /* DTLS header */ + handshake_protocol = packet->payload[13]; + total_len = ntohs(*((u_int16_t*)&packet->payload[11])); + } + + total_len += header_len; + + memset(buffer, 0, buffer_len); + + /* Truncate total len, search at least in incomplete packet */ + if(total_len > packet->payload_packet_len) + total_len = packet->payload_packet_len; + + /* At least "magic" 3 bytes, null for string end, otherwise no need to waste cpu cycles */ + if(total_len > 4) { + u_int16_t base_offset = packet->tcp ? 43 : 59; + +#ifdef CERTIFICATE_DEBUG + printf("SSL [len: %u][handshake_protocol: %02X]\n", packet->payload_packet_len, handshake_protocol); +#endif + + if((handshake_protocol == 0x02) + || (handshake_protocol == 0x0b) /* Server Hello and Certificate message types are interesting for us */) { + u_int num_found = 0; + u_int16_t tls_version; + int i; + + if(packet->tcp) + tls_version = ntohs(*((u_int16_t*)&packet->payload[header_len+4])); + else + tls_version = ntohs(*((u_int16_t*)&packet->payload[header_len+12])); + + ja3.tls_version = tls_version; + + if(handshake_protocol == 0x02) { + u_int16_t offset = base_offset, extension_len, j; + u_int8_t session_id_len = packet->payload[offset]; + +#ifdef CERTIFICATE_DEBUG + printf("SSL Server Hello [version: 0x%04X]\n", tls_version); +#endif + + /* + The server hello decides about the SSL version of this flow + https://networkengineering.stackexchange.com/questions/55752/why-does-wireshark-show-version-tls-1-2-here-instead-of-tls-1-3 + */ + flow->protos.stun_tls.tls.tls_version = tls_version; + + if(packet->udp) + offset += 1; + else { + if(tls_version < 0x7F15 /* TLS 1.3 lacks of session id */) + offset += session_id_len+1; + } + + ja3.num_cipher = 1, ja3.cipher[0] = ntohs(*((u_int16_t*)&packet->payload[offset])); + flow->protos.stun_tls.tls.server_unsafe_cipher = ndpi_is_safe_tls_cipher(ja3.cipher[0]); + flow->protos.stun_tls.tls.server_cipher = ja3.cipher[0]; + +#ifdef CERTIFICATE_DEBUG + printf("SSL [server][session_id_len: %u][cipher: %04X]\n", session_id_len, ja3.cipher[0]); +#endif + + offset += 2 + 1; + extension_len = ntohs(*((u_int16_t*)&packet->payload[offset])); + +#ifdef CERTIFICATE_DEBUG + printf("SSL [server][extension_len: %u]\n", extension_len); +#endif + offset += 2; + + for(i=0; i= (packet->payload_packet_len+4)) break; + + id = ntohs(*((u_int16_t*)&packet->payload[offset])); + len = ntohs(*((u_int16_t*)&packet->payload[offset+2])); + + if(ja3.num_tls_extension < MAX_NUM_JA3) + ja3.tls_extension[ja3.num_tls_extension++] = id; + +#ifdef CERTIFICATE_DEBUG + printf("SSL [server][extension_id: %u/0x%04X]\n", id, id); +#endif + + i += 4 + len, offset += 4 + len; + } + + ja3_str_len = snprintf(ja3_str, sizeof(ja3_str), "%u,", ja3.tls_version); + + for(i=0; i 0) ? "-" : "", ja3.cipher[i]); + + ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); + + /* ********** */ + + for(i=0; i 0) ? "-" : "", ja3.tls_extension[i]); + +#ifdef CERTIFICATE_DEBUG + printf("SSL [server] %s\n", ja3_str); +#endif + +#ifdef CERTIFICATE_DEBUG + printf("[JA3] Server: %s \n", ja3_str); +#endif + + MD5Init(&ctx); + MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str)); + MD5Final(md5_hash, &ctx); + + for(i=0, j=0; i<16; i++) + j += snprintf(&flow->protos.stun_tls.tls.ja3_server[j], + sizeof(flow->protos.stun_tls.tls.ja3_server)-j, "%02x", md5_hash[i]); + +#ifdef CERTIFICATE_DEBUG + printf("[JA3] Server: %s \n", flow->protos.stun_tls.tls.ja3_server); +#endif + + flow->l4.tcp.tls_seen_server_cert = 1; + } else + flow->l4.tcp.tls_seen_certificate = 1; + + /* Check after handshake protocol header (5 bytes) and message header (4 bytes) */ + for(i = 9; i < packet->payload_packet_len-3; i++) { + if(((packet->payload[i] == 0x04) && (packet->payload[i+1] == 0x03) && (packet->payload[i+2] == 0x0c)) + || ((packet->payload[i] == 0x04) && (packet->payload[i+1] == 0x03) && (packet->payload[i+2] == 0x13)) + || ((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x03))) { + u_int8_t server_len = packet->payload[i+3]; + + if(packet->payload[i] == 0x55) { + num_found++; + + if(num_found != 2) continue; + } + + if((server_len+i+3) < packet->payload_packet_len) { + char *server_name = (char*)&packet->payload[i+4]; + u_int8_t begin = 0, len, j, num_dots; + + while(begin < server_len) { + if(!ndpi_isprint(server_name[begin])) + begin++; + else + break; + } + + // len = ndpi_min(server_len-begin, buffer_len-1); + len = buffer_len-1; + strncpy(buffer, &server_name[begin], len); + buffer[len] = '\0'; + + /* We now have to check if this looks like an IP address or host name */ + for(j=0, num_dots = 0; j=1) break; + } + } + + if(num_dots >= 1) { + if(!ndpi_struct->disable_metadata_export) { + stripCertificateTrailer(buffer, buffer_len); + snprintf(flow->protos.stun_tls.tls.server_certificate, + sizeof(flow->protos.stun_tls.tls.server_certificate), "%s", buffer); + } + + return(1 /* Server Certificate */); + } + } + } + } + } else if(handshake_protocol == 0x01 /* Client Hello */) { + u_int offset; + +#ifdef CERTIFICATE_DEBUG + printf("[base_offset: %u][payload_packet_len: %u]\n", base_offset, packet->payload_packet_len); +#endif + + if(base_offset + 2 <= packet->payload_packet_len) { + u_int16_t session_id_len; + u_int16_t tls_version; + + if(packet->tcp) + tls_version = ntohs(*((u_int16_t*)&packet->payload[header_len+4])); + else + tls_version = ntohs(*((u_int16_t*)&packet->payload[header_len+12])); + + session_id_len = packet->payload[base_offset]; + + ja3.tls_version = tls_version; + + if((session_id_len+base_offset+2) <= total_len) { + u_int16_t cipher_len, cipher_offset; + + if(packet->tcp) { + 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; + } + +#ifdef CERTIFICATE_DEBUG + printf("Client SSL [client cipher_len: %u][tls_version: 0x%04X]\n", cipher_len, tls_version); +#endif + + if((cipher_offset+cipher_len) <= total_len) { + for(i=0; ipayload[cipher_offset+i]; + +#ifdef CERTIFICATE_DEBUG + printf("Client SSL [cipher suite: %u/0x%04X] [%u/%u]\n", ntohs(*id), ntohs(*id), i, cipher_len); +#endif + if((*id == 0) || (packet->payload[cipher_offset+i] != packet->payload[cipher_offset+i+1])) { + /* + Skip GREASE [https://tools.ietf.org/id/draft-ietf-tls-grease-01.html] + https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967 + */ + + if(ja3.num_cipher < MAX_NUM_JA3) + ja3.cipher[ja3.num_cipher++] = ntohs(*id); + else { + invalid_ja3 = 1; +#ifdef CERTIFICATE_DEBUG + printf("Client SSL Invalid cipher %u\n", ja3.num_cipher); +#endif + } + } + + i += 2; + } + } else { + invalid_ja3 = 1; +#ifdef CERTIFICATE_DEBUG + printf("Client SSL Invalid len %u vs %u\n", (cipher_offset+cipher_len), total_len); +#endif + } + + offset = base_offset + session_id_len + cipher_len + 2; + + flow->l4.tcp.tls_seen_client_cert = 1; + + if(offset < total_len) { + u_int16_t compression_len; + u_int16_t extensions_len; + + offset += packet->tcp ? 1 : 2; + compression_len = packet->payload[offset]; + offset++; + +#ifdef CERTIFICATE_DEBUG + printf("Client SSL [compression_len: %u]\n", compression_len); +#endif + + // offset += compression_len + 3; + offset += compression_len; + + if(offset < total_len) { + extensions_len = ntohs(*((u_int16_t*)&packet->payload[offset])); + offset += 2; + +#ifdef CERTIFICATE_DEBUG + printf("Client SSL [extensions_len: %u]\n", extensions_len); +#endif + + if((extensions_len+offset) <= total_len) { + /* Move to the first extension + Type is u_int to avoid possible overflow on extension_len addition */ + u_int extension_offset = 0; + u_int32_t j; + + while(extension_offset < extensions_len) { + u_int16_t extension_id, extension_len, extn_off = offset+extension_offset; + + extension_id = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset])); + extension_offset += 2; + + extension_len = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset])); + extension_offset += 2; + +#ifdef CERTIFICATE_DEBUG + printf("Client SSL [extension_id: %u][extension_len: %u]\n", extension_id, extension_len); +#endif + + if((extension_id == 0) || (packet->payload[extn_off] != packet->payload[extn_off+1])) { + /* Skip GREASE */ + + if(ja3.num_tls_extension < MAX_NUM_JA3) + ja3.tls_extension[ja3.num_tls_extension++] = extension_id; + else { + invalid_ja3 = 1; +#ifdef CERTIFICATE_DEBUG + printf("Client SSL Invalid extensions %u\n", ja3.num_tls_extension); +#endif + } + } + + if(extension_id == 0 /* server name */) { + u_int16_t len; + + len = (packet->payload[offset+extension_offset+3] << 8) + packet->payload[offset+extension_offset+4]; + len = (u_int)ndpi_min(len, buffer_len-1); + strncpy(buffer, (char*)&packet->payload[offset+extension_offset+5], len); + buffer[len] = '\0'; + + stripCertificateTrailer(buffer, buffer_len); + + if(!ndpi_struct->disable_metadata_export) { + snprintf(flow->protos.stun_tls.tls.client_certificate, + sizeof(flow->protos.stun_tls.tls.client_certificate), "%s", buffer); + } + } else if(extension_id == 10 /* supported groups */) { + u_int16_t s_offset = offset+extension_offset + 2; + +#ifdef CERTIFICATE_DEBUG + printf("Client SSL [EllipticCurveGroups: len=%u]\n", extension_len); +#endif + + if((s_offset+extension_len-2) <= total_len) { + for(i=0; ipayload[s_offset+i])); + +#ifdef CERTIFICATE_DEBUG + printf("Client SSL [EllipticCurve: %u/0x%04X]\n", s_group, s_group); +#endif + if((s_group == 0) || (packet->payload[s_offset+i] != packet->payload[s_offset+i+1])) { + /* Skip GREASE */ + if(ja3.num_elliptic_curve < MAX_NUM_JA3) + ja3.elliptic_curve[ja3.num_elliptic_curve++] = s_group; + else { + invalid_ja3 = 1; +#ifdef CERTIFICATE_DEBUG + printf("Client SSL Invalid num elliptic %u\n", ja3.num_elliptic_curve); +#endif + } + } + + i += 2; + } + } else { + invalid_ja3 = 1; +#ifdef CERTIFICATE_DEBUG + printf("Client SSL Invalid len %u vs %u\n", (s_offset+extension_len-1), total_len); +#endif + } + } else if(extension_id == 11 /* ec_point_formats groups */) { + u_int16_t s_offset = offset+extension_offset + 1; + +#ifdef CERTIFICATE_DEBUG + printf("Client SSL [EllipticCurveFormat: len=%u]\n", extension_len); +#endif + if((s_offset+extension_len) < total_len) { + for(i=0; ipayload[s_offset+i]; + +#ifdef CERTIFICATE_DEBUG + printf("Client SSL [EllipticCurveFormat: %u]\n", s_group); +#endif + + if(ja3.num_elliptic_curve_point_format < MAX_NUM_JA3) + ja3.elliptic_curve_point_format[ja3.num_elliptic_curve_point_format++] = s_group; + else { + invalid_ja3 = 1; +#ifdef CERTIFICATE_DEBUG + printf("Client SSL Invalid num elliptic %u\n", ja3.num_elliptic_curve_point_format); +#endif + } + } + } else { + invalid_ja3 = 1; +#ifdef CERTIFICATE_DEBUG + printf("Client SSL Invalid len %u vs %u\n", s_offset+extension_len, total_len); +#endif + } + } + + extension_offset += extension_len; + +#ifdef CERTIFICATE_DEBUG + printf("Client SSL [extension_offset/len: %u/%u]\n", extension_offset, extension_len); +#endif + } /* while */ + + if(!invalid_ja3) { + ja3_str_len = snprintf(ja3_str, sizeof(ja3_str), "%u,", ja3.tls_version); + + for(i=0; i 0) ? "-" : "", ja3.cipher[i]); + } + + ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); + + /* ********** */ + + for(i=0; i 0) ? "-" : "", ja3.tls_extension[i]); + + ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); + + /* ********** */ + + for(i=0; i 0) ? "-" : "", ja3.elliptic_curve[i]); + + ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); + + for(i=0; i 0) ? "-" : "", ja3.elliptic_curve_point_format[i]); + +#ifdef CERTIFICATE_DEBUG + printf("[JA3] Client: %s \n", ja3_str); +#endif + + MD5Init(&ctx); + MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str)); + MD5Final(md5_hash, &ctx); + + for(i=0, j=0; i<16; i++) + j += snprintf(&flow->protos.stun_tls.tls.ja3_client[j], + sizeof(flow->protos.stun_tls.tls.ja3_client)-j, "%02x", md5_hash[i]); + +#ifdef CERTIFICATE_DEBUG + printf("[JA3] Client: %s \n", flow->protos.stun_tls.tls.ja3_client); +#endif + } + + return(2 /* Client Certificate */); + } + } + } + } + } + } + } + } + + return(0); /* Not found */ +} + +/* **************************************** */ + +void getSSLorganization(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, + char *buffer, int buffer_len) { + struct ndpi_packet_struct *packet = &flow->packet; + + if(packet->payload[0] != 0x16 /* Handshake */) + return; + + u_int16_t total_len = (packet->payload[3] << 8) + packet->payload[4] + 5 /* SSL Header */; + u_int8_t handshake_protocol = packet->payload[5]; /* handshake protocol a bit misleading, it is message type according TLS specs */ + + if(handshake_protocol != 0x02 && handshake_protocol != 0xb /* Server Hello and Certificate message types are interesting for us */) + return; + + /* Truncate total len, search at least in incomplete packet */ + if(total_len > packet->payload_packet_len) + total_len = packet->payload_packet_len; + + memset(buffer, 0, buffer_len); + + /* Check after handshake protocol header (5 bytes) and message header (4 bytes) */ + u_int num_found = 0; + u_int i, j; + for(i = 9; i < packet->payload_packet_len-4; i++) { + /* Organization OID: 2.5.4.10 */ + if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x0a)) { + u_int8_t server_len = packet->payload[i+4]; + + num_found++; + /* what we want is subject certificate, so we bypass the issuer certificate */ + if(num_found != 2) continue; + + // packet is truncated... further inspection is not needed + if(i+4+server_len >= packet->payload_packet_len) { + break; + } + + char *server_org = (char*)&packet->payload[i+5]; + + u_int len = (u_int)ndpi_min(server_len, buffer_len-1); + strncpy(buffer, server_org, len); + buffer[len] = '\0'; + + // check if organization string are all printable + u_int8_t is_printable = 1; + for (j = 0; j < len; j++) { + if(!ndpi_isprint(buffer[j])) { + is_printable = 0; + break; + } + } + + if(is_printable == 1) { + snprintf(flow->protos.stun_tls.tls.server_organization, + sizeof(flow->protos.stun_tls.tls.server_organization), "%s", buffer); +#ifdef CERTIFICATE_DEBUG + printf("Certificate organization: %s\n", flow->protos.stun_tls.tls.server_organization); +#endif + } + } + } +} + +/* **************************************** */ + +int sslTryAndRetrieveServerCertificate(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) { + struct ndpi_packet_struct *packet = &flow->packet; + + /* consider only specific SSL packets (handshake) */ + if((packet->payload_packet_len > 9) && (packet->payload[0] == 0x16)) { + char certificate[64]; + int rc; + + certificate[0] = '\0'; + rc = getTLScertificate(ndpi_struct, flow, certificate, sizeof(certificate)); + packet->tls_certificate_num_checks++; + + if(rc > 0) { + char organization[64]; + + // try fetch server organization once server certificate is found + organization[0] = '\0'; + getSSLorganization(ndpi_struct, flow, organization, sizeof(organization)); + + packet->tls_certificate_detected++; + if((flow->l4.tcp.tls_seen_server_cert == 1) && (flow->protos.stun_tls.tls.server_certificate[0] != '\0')) + /* 0 means we're done processing extra packets (since we found what we wanted) */ + return 0; + } + + /* Client hello, Server Hello, and certificate packets probably all checked in this case */ + if((packet->tls_certificate_num_checks >= 3) + && (flow->l4.tcp.seen_syn) + && (flow->l4.tcp.seen_syn_ack) + && (flow->l4.tcp.seen_ack) /* We have seen the 3-way handshake */) + { + /* We're done processing extra packets since we've probably checked all possible cert packets */ + return 0; + } + } + + /* 1 means keep looking for more packets */ + return 1; +} + +/* **************************************** */ + +void sslInitExtraPacketProcessing(int caseNum, struct ndpi_flow_struct *flow) { + flow->check_extra_packets = 1; + /* 0 is the case for waiting for the server certificate */ + if(caseNum == 0) { + /* At most 7 packets should almost always be enough to find the server certificate if it's there */ + flow->max_extra_packets_to_check = 7; + flow->extra_packets_func = sslTryAndRetrieveServerCertificate; + } +} + +/* **************************************** */ + +int tlsDetectProtocolFromCertificate(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) { + struct ndpi_packet_struct *packet = &flow->packet; + + if((packet->payload_packet_len > 9) + && (packet->payload[0] == 0x16 /* consider only specific SSL packets (handshake) */)) { + if((packet->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN) + || (packet->detected_protocol_stack[0] == NDPI_PROTOCOL_TLS)) { + char certificate[64]; + int rc; + + certificate[0] = '\0'; + rc = getTLScertificate(ndpi_struct, flow, certificate, sizeof(certificate)); + packet->tls_certificate_num_checks++; + + if(rc > 0) { + packet->tls_certificate_detected++; +#ifdef CERTIFICATE_DEBUG + NDPI_LOG_DBG2(ndpi_struct, "***** [SSL] %s\n", certificate); +#endif + ndpi_protocol_match_result ret_match; + u_int32_t subproto = ndpi_match_host_subprotocol(ndpi_struct, flow, certificate, + strlen(certificate), + &ret_match, + NDPI_PROTOCOL_TLS); + + if(subproto != NDPI_PROTOCOL_UNKNOWN) { + /* If we've detected the subprotocol from client certificate but haven't had a chance + * to see the server certificate yet, set up extra packet processing to wait + * a few more packets. */ + if(((flow->l4.tcp.tls_seen_client_cert == 1) && (flow->protos.stun_tls.tls.client_certificate[0] != '\0')) + && ((flow->l4.tcp.tls_seen_server_cert != 1) && (flow->protos.stun_tls.tls.server_certificate[0] == '\0'))) { + sslInitExtraPacketProcessing(0, flow); + } + + ndpi_set_detected_protocol(ndpi_struct, flow, subproto, + ndpi_tls_refine_master_protocol(ndpi_struct, flow, NDPI_PROTOCOL_TLS)); + return(rc); /* Fix courtesy of Gianluca Costa */ + } + + if(ndpi_is_tls_tor(ndpi_struct, flow, certificate) != 0) + return(rc); + } + + if(((packet->tls_certificate_num_checks >= 3) + && flow->l4.tcp.seen_syn + && flow->l4.tcp.seen_syn_ack + && flow->l4.tcp.seen_ack /* We have seen the 3-way handshake */) + || ((flow->l4.tcp.tls_seen_certificate == 1) + && (flow->l4.tcp.tls_seen_server_cert == 1) + && (flow->protos.stun_tls.tls.server_certificate[0] != '\0')) + /* || ((flow->l4.tcp.tls_seen_client_cert == 1) && (flow->protos.stun_tls.tls.client_certificate[0] != '\0')) */ + ) { + ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_TLS); + } + } + } + return(0); +} + +/* **************************************** */ + +static void tls_mark_and_payload_search_for_other_protocols(struct ndpi_detection_module_struct + *ndpi_struct, struct ndpi_flow_struct *flow) { + struct ndpi_packet_struct *packet = &flow->packet; + u_int32_t a; + u_int32_t end; + + if(NDPI_COMPARE_PROTOCOL_TO_BITMASK(ndpi_struct->detection_bitmask, NDPI_PROTOCOL_UNENCRYPTED_JABBER) != 0) + goto check_for_tls_payload; + + if(NDPI_COMPARE_PROTOCOL_TO_BITMASK(ndpi_struct->detection_bitmask, NDPI_PROTOCOL_OSCAR) != 0) + goto check_for_tls_payload; + else + goto no_check_for_tls_payload; + + check_for_tls_payload: + end = packet->payload_packet_len - 20; + for (a = 5; a < end; a++) { + + if(packet->payload[a] == 't') { + if(memcmp(&packet->payload[a], "talk.google.com", 15) == 0) { + if(NDPI_COMPARE_PROTOCOL_TO_BITMASK + (ndpi_struct->detection_bitmask, NDPI_PROTOCOL_UNENCRYPTED_JABBER) != 0) { + NDPI_LOG_INFO(ndpi_struct, "found ssl jabber unencrypted\n"); + ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNENCRYPTED_JABBER); + return; + } + } + } + + if(packet->payload[a] == 'A' || packet->payload[a] == 'k' || packet->payload[a] == 'c' + || packet->payload[a] == 'h') { + if(((a + 19) < packet->payload_packet_len && memcmp(&packet->payload[a], "America Online Inc.", 19) == 0) + // || (end - c > 3 memcmp (&packet->payload[c],"AOL", 3) == 0 ) + // || (end - c > 7 && memcmp (&packet->payload[c], "AOL LLC", 7) == 0) + || ((a + 15) < packet->payload_packet_len && memcmp(&packet->payload[a], "kdc.uas.aol.com", 15) == 0) + || ((a + 14) < packet->payload_packet_len && memcmp(&packet->payload[a], "corehc@aol.net", 14) == 0) + || ((a + 41) < packet->payload_packet_len + && memcmp(&packet->payload[a], "http://crl.aol.com/AOLMSPKI/aolServerCert", 41) == 0) + || ((a + 28) < packet->payload_packet_len + && memcmp(&packet->payload[a], "http://ocsp.web.aol.com/ocsp", 28) == 0) + || ((a + 32) < packet->payload_packet_len + && memcmp(&packet->payload[a], "http://pki-info.aol.com/AOLMSPKI", 32) == 0)) { + NDPI_LOG_INFO(ndpi_struct, "found OSCAR SERVER SSL DETECTED\n"); + + if(flow->dst != NULL && packet->payload_packet_len > 75) { + memcpy(flow->dst->oscar_tls_session_id, &packet->payload[44], 32); + flow->dst->oscar_tls_session_id[32] = '\0'; + flow->dst->oscar_last_safe_access_time = packet->tick_timestamp; + } + + ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_OSCAR); + return; + } + } + + if(packet->payload[a] == 'm' || packet->payload[a] == 's') { + if((a + 21) < packet->payload_packet_len && + (memcmp(&packet->payload[a], "my.screenname.aol.com", 21) == 0 + || memcmp(&packet->payload[a], "sns-static.aolcdn.com", 21) == 0)) { + NDPI_LOG_DBG(ndpi_struct, "found OSCAR SERVER SSL DETECTED\n"); + ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_OSCAR); + return; + } + } + } + + no_check_for_tls_payload: + if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN) { + NDPI_LOG_DBG(ndpi_struct, "found ssl connection\n"); + tlsDetectProtocolFromCertificate(ndpi_struct, flow); + + if(!packet->tls_certificate_detected + && (!(flow->l4.tcp.tls_seen_client_cert && flow->l4.tcp.tls_seen_server_cert))) { + /* SSL without certificate (Skype, Ultrasurf?) */ + NDPI_LOG_INFO(ndpi_struct, "found ssl NO_CERT\n"); + ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_TLS_NO_CERT); + } else if(packet->tls_certificate_num_checks >= 3) { + NDPI_LOG_INFO(ndpi_struct, "found ssl\n"); + ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_TLS); + } + } +} + +/* **************************************** */ + +static u_int8_t ndpi_search_tlsv3_direction1(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) { + struct ndpi_packet_struct *packet = &flow->packet; + + if((packet->payload_packet_len >= 5) + && ((packet->payload[0] == 0x16) || packet->payload[0] == 0x17) + && (packet->payload[1] == 0x03) + && ((packet->payload[2] == 0x00) || (packet->payload[2] == 0x01) || + (packet->payload[2] == 0x02) || (packet->payload[2] == 0x03))) { + u_int32_t temp; + NDPI_LOG_DBG2(ndpi_struct, "search sslv3\n"); + // SSLv3 Record + if(packet->payload_packet_len >= 1300) { + return 1; + } + temp = ntohs(get_u_int16_t(packet->payload, 3)) + 5; + NDPI_LOG_DBG2(ndpi_struct, "temp = %u\n", temp); + if(packet->payload_packet_len == temp + || (temp < packet->payload_packet_len && packet->payload_packet_len > 500)) { + return 1; + } + + if(packet->payload_packet_len < temp && temp < 5000 && packet->payload_packet_len > 9) { + /* the server hello may be split into small packets */ + u_int32_t cert_start; + + NDPI_LOG_DBG2(ndpi_struct, + "maybe SSLv3 server hello split into smaller packets\n"); + + /* lets hope at least the server hello and the start of the certificate block are in the first packet */ + cert_start = ntohs(get_u_int16_t(packet->payload, 7)) + 5 + 4; + NDPI_LOG_DBG2(ndpi_struct, "suspected start of certificate: %u\n", + cert_start); + + if(cert_start < packet->payload_packet_len && packet->payload[cert_start] == 0x0b) { + NDPI_LOG_DBG2(ndpi_struct, + "found 0x0b at suspected start of certificate block\n"); + return 2; + } + } + + if((packet->payload_packet_len > temp) && (packet->payload_packet_len > 100)) { + /* the server hello may be split into small packets and the certificate has its own SSL Record + * so temp contains only the length for the first ServerHello block */ + u_int32_t cert_start; + + NDPI_LOG_DBG2(ndpi_struct, + "maybe SSLv3 server hello split into smaller packets but with seperate record for the certificate\n"); + + /* lets hope at least the server hello record and the start of the certificate record are in the first packet */ + cert_start = ntohs(get_u_int16_t(packet->payload, 7)) + 5 + 5 + 4; + NDPI_LOG_DBG2(ndpi_struct, "suspected start of certificate: %u\n", + cert_start); + + if(cert_start < packet->payload_packet_len && packet->payload[cert_start] == 0x0b) { + NDPI_LOG_DBG2(ndpi_struct, + "found 0x0b at suspected start of certificate block\n"); + return 2; + } + } + + + if(packet->payload_packet_len >= temp + 5 && (packet->payload[temp] == 0x14 || packet->payload[temp] == 0x16) + && packet->payload[temp + 1] == 0x03) { + u_int32_t temp2 = ntohs(get_u_int16_t(packet->payload, temp + 3)) + 5; + if(temp + temp2 > NDPI_MAX_TLS_REQUEST_SIZE) { + return 1; + } + temp += temp2; + NDPI_LOG_DBG2(ndpi_struct, "temp = %u\n", temp); + if(packet->payload_packet_len == temp) { + return 1; + } + if(packet->payload_packet_len >= temp + 5 && + packet->payload[temp] == 0x16 && packet->payload[temp + 1] == 0x03) { + temp2 = ntohs(get_u_int16_t(packet->payload, temp + 3)) + 5; + if(temp + temp2 > NDPI_MAX_TLS_REQUEST_SIZE) { + return 1; + } + temp += temp2; + NDPI_LOG_DBG2(ndpi_struct, "temp = %u\n", temp); + if(packet->payload_packet_len == temp) { + return 1; + } + if(packet->payload_packet_len >= temp + 5 && + packet->payload[temp] == 0x16 && packet->payload[temp + 1] == 0x03) { + temp2 = ntohs(get_u_int16_t(packet->payload, temp + 3)) + 5; + if(temp + temp2 > NDPI_MAX_TLS_REQUEST_SIZE) { + return 1; + } + temp += temp2; + NDPI_LOG_DBG2(ndpi_struct, "temp = %u\n", temp); + if(temp == packet->payload_packet_len) { + return 1; + } + } + } + } + } + + return 0; +} + +/* **************************************** */ + +void ndpi_search_tls_tcp_udp(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) { + struct ndpi_packet_struct *packet = &flow->packet; + u_int8_t ret; + + if(packet->udp != NULL) { + /* DTLS dissector */ + int rc = sslTryAndRetrieveServerCertificate(ndpi_struct, flow); + + if(rc) flow->guessed_protocol_id = NDPI_PROTOCOL_TLS; + + if(flow->l4.tcp.tls_seen_server_cert) + ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_TLS); + return; + } + + if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_TLS) { + if(flow->l4.tcp.tls_stage == 3 && packet->payload_packet_len > 20 && flow->packet_counter < 5) { + /* this should only happen, when we detected SSL with a packet that had parts of the certificate in subsequent packets + * so go on checking for certificate patterns for a couple more packets + */ + NDPI_LOG_DBG2(ndpi_struct, + "ssl flow but check another packet for patterns\n"); + tls_mark_and_payload_search_for_other_protocols(ndpi_struct, flow); + + if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_TLS) { + /* still ssl so check another packet */ + return; + } else { + /* protocol has changed so we are done */ + return; + } + } + + return; + } + + NDPI_LOG_DBG(ndpi_struct, "search ssl\n"); + + /* Check if this is whatsapp first (this proto runs over port 443) */ + if((packet->payload_packet_len > 5) + && ((packet->payload[0] == 'W') + && (packet->payload[1] == 'A') + && (packet->payload[4] == 0) + && (packet->payload[2] <= 9) + && (packet->payload[3] <= 9))) { + ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_WHATSAPP, NDPI_PROTOCOL_UNKNOWN); + return; + } else if((packet->payload_packet_len == 4) + && (packet->payload[0] == 'W') + && (packet->payload[1] == 'A')) { + ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_WHATSAPP, NDPI_PROTOCOL_UNKNOWN); + return; + } else { + /* No whatsapp, let's try SSL */ + if(tlsDetectProtocolFromCertificate(ndpi_struct, flow) > 0) + return; + } + + if(packet->payload_packet_len > 40 && flow->l4.tcp.tls_stage == 0) { + NDPI_LOG_DBG2(ndpi_struct, "first ssl packet\n"); + // SSLv2 Record + if(packet->payload[2] == 0x01 && packet->payload[3] == 0x03 + && (packet->payload[4] == 0x00 || packet->payload[4] == 0x01 || packet->payload[4] == 0x02) + && (packet->payload_packet_len - packet->payload[1] == 2)) { + NDPI_LOG_DBG2(ndpi_struct, "sslv2 len match\n"); + flow->l4.tcp.tls_stage = 1 + packet->packet_direction; + return; + } + + if(packet->payload[0] == 0x16 && packet->payload[1] == 0x03 + && (packet->payload[2] == 0x00 || packet->payload[2] == 0x01 || packet->payload[2] == 0x02) + && (packet->payload_packet_len - ntohs(get_u_int16_t(packet->payload, 3)) == 5)) { + // SSLv3 Record + NDPI_LOG_DBG2(ndpi_struct, "sslv3 len match\n"); + flow->l4.tcp.tls_stage = 1 + packet->packet_direction; + return; + } + + // Application Data pkt + if(packet->payload[0] == 0x17 && packet->payload[1] == 0x03 + && (packet->payload[2] == 0x00 || packet->payload[2] == 0x01 || + packet->payload[2] == 0x02 || packet->payload[2] == 0x03)) { + if(packet->payload_packet_len - ntohs(get_u_int16_t(packet->payload, 3)) == 5) { + NDPI_LOG_DBG2(ndpi_struct, "TLS len match\n"); + flow->l4.tcp.tls_stage = 1 + packet->packet_direction; + return; + } + } + } + + if(packet->payload_packet_len > 40 && + flow->l4.tcp.tls_stage == 1 + packet->packet_direction + && flow->packet_direction_counter[packet->packet_direction] < 5) { + return; + } + + if(packet->payload_packet_len > 40 && flow->l4.tcp.tls_stage == 2 - packet->packet_direction) { + NDPI_LOG_DBG2(ndpi_struct, "second ssl packet\n"); + // SSLv2 Record + if(packet->payload[2] == 0x01 && packet->payload[3] == 0x03 + && (packet->payload[4] == 0x00 || packet->payload[4] == 0x01 || packet->payload[4] == 0x02) + && (packet->payload_packet_len - 2) >= packet->payload[1]) { + NDPI_LOG_DBG2(ndpi_struct, "sslv2 server len match\n"); + tls_mark_and_payload_search_for_other_protocols(ndpi_struct, flow); + return; + } + + ret = ndpi_search_tlsv3_direction1(ndpi_struct, flow); + if(ret == 1) { + NDPI_LOG_DBG2(ndpi_struct, "sslv3 server len match\n"); + tls_mark_and_payload_search_for_other_protocols(ndpi_struct, flow); + return; + } else if(ret == 2) { + NDPI_LOG_DBG2(ndpi_struct, + "sslv3 server len match with split packet -> check some more packets for SSL patterns\n"); + tls_mark_and_payload_search_for_other_protocols(ndpi_struct, flow); + if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_TLS) { + flow->l4.tcp.tls_stage = 3; + } + return; + } + + if(packet->payload_packet_len > 40 && flow->packet_direction_counter[packet->packet_direction] < 5) { + NDPI_LOG_DBG2(ndpi_struct, "need next packet\n"); + return; + } + } + + NDPI_EXCLUDE_PROTO(ndpi_struct, flow); + + return; +} + +/* **************************************** */ + +void init_tls_dissector(struct ndpi_detection_module_struct *ndpi_struct, + u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask) { + ndpi_set_bitmask_protocol_detection("TLS", ndpi_struct, detection_bitmask, *id, + NDPI_PROTOCOL_TLS, + ndpi_search_tls_tcp_udp, + NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD, + SAVE_DETECTION_BITMASK_AS_UNKNOWN, + ADD_TO_DETECTION_BITMASK); + + *id += 1; +} diff --git a/src/lib/protocols/tor.c b/src/lib/protocols/tor.c index 1a5d4097e..fb8d1bd2c 100644 --- a/src/lib/protocols/tor.c +++ b/src/lib/protocols/tor.c @@ -18,7 +18,7 @@ static void ndpi_int_tor_add_connection(struct ndpi_detection_module_struct } -int ndpi_is_ssl_tor(struct ndpi_detection_module_struct *ndpi_struct, +int ndpi_is_tls_tor(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow, char *certificate) { int prev_num = 0, numbers_found = 0, num_found = 0, i, len, num_impossible = 0; char dummy[48], *dot, *name; diff --git a/src/lib/protocols/yahoo.c b/src/lib/protocols/yahoo.c index 3be953939..972466dc8 100644 --- a/src/lib/protocols/yahoo.c +++ b/src/lib/protocols/yahoo.c @@ -367,7 +367,7 @@ void ndpi_search_yahoo(struct ndpi_detection_module_struct *ndpi_struct, struct if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN || packet->detected_protocol_stack[0] == NDPI_PROTOCOL_HTTP - || packet->detected_protocol_stack[0] == NDPI_PROTOCOL_SSL) { + || packet->detected_protocol_stack[0] == NDPI_PROTOCOL_TLS) { /* search over TCP */ ndpi_search_yahoo_tcp(ndpi_struct, flow); } -- cgit v1.2.3