diff options
author | Luca Deri <deri@ntop.org> | 2020-01-05 18:25:44 +0100 |
---|---|---|
committer | Luca Deri <deri@ntop.org> | 2020-01-05 18:25:44 +0100 |
commit | ef16591f3f9d2673dfacf1c715c2e10dcd1da248 (patch) | |
tree | babb2c57fec88d3942a2f13a911f569af33f9ac3 /src/lib/protocols | |
parent | 29dd45838da84bfa43da615d7624373392861751 (diff) | |
parent | 798bb6e2e113f10d9b710179553e4cef23222a61 (diff) |
Merge branch 'dev' of https://github.com/ntop/nDPI into dev
Diffstat (limited to 'src/lib/protocols')
-rw-r--r-- | src/lib/protocols/bittorrent.c | 4 | ||||
-rw-r--r-- | src/lib/protocols/dhcp.c | 52 | ||||
-rw-r--r-- | src/lib/protocols/http.c | 29 | ||||
-rw-r--r-- | src/lib/protocols/kerberos.c | 11 | ||||
-rw-r--r-- | src/lib/protocols/mdns_proto.c | 8 | ||||
-rw-r--r-- | src/lib/protocols/netbios.c | 11 | ||||
-rw-r--r-- | src/lib/protocols/oscar.c | 151 | ||||
-rw-r--r-- | src/lib/protocols/quic.c | 30 | ||||
-rw-r--r-- | src/lib/protocols/ssh.c | 47 | ||||
-rw-r--r-- | src/lib/protocols/tls.c | 1981 | ||||
-rw-r--r-- | src/lib/protocols/ubntac2.c | 8 | ||||
-rw-r--r-- | src/lib/protocols/whoisdas.c | 12 |
12 files changed, 954 insertions, 1390 deletions
diff --git a/src/lib/protocols/bittorrent.c b/src/lib/protocols/bittorrent.c index 5ed0d311a..a1ade79b2 100644 --- a/src/lib/protocols/bittorrent.c +++ b/src/lib/protocols/bittorrent.c @@ -72,9 +72,7 @@ static void ndpi_add_connection_as_bittorrent(struct ndpi_detection_module_struc } else bt_hash = (const char*)&flow->packet.payload[28]; - if(!ndpi_struct->disable_metadata_export) { - if(bt_hash) memcpy(flow->protos.bittorrent.hash, bt_hash, 20); - } + if(bt_hash) memcpy(flow->protos.bittorrent.hash, bt_hash, 20); } ndpi_int_change_protocol(ndpi_struct, flow, NDPI_PROTOCOL_BITTORRENT, NDPI_PROTOCOL_UNKNOWN); diff --git a/src/lib/protocols/dhcp.c b/src/lib/protocols/dhcp.c index 8631989e9..f40a8138c 100644 --- a/src/lib/protocols/dhcp.c +++ b/src/lib/protocols/dhcp.c @@ -100,42 +100,36 @@ void ndpi_search_dhcp_udp(struct ndpi_detection_module_struct *ndpi_struct, stru if(msg_type <= 8) foundValidMsgType = 1; } else if(id == 55 /* Parameter Request List / Fingerprint */) { - if(!ndpi_struct->disable_metadata_export) { - u_int idx, offset = 0; + u_int idx, offset = 0; + + for(idx = 0; idx < len && offset < sizeof(flow->protos.dhcp.fingerprint) - 2; idx++) { + int rc = snprintf((char*)&flow->protos.dhcp.fingerprint[offset], + sizeof(flow->protos.dhcp.fingerprint) - offset, + "%s%u", (idx > 0) ? "," : "", + (unsigned int)dhcp->options[i+2+idx] & 0xFF); - for(idx = 0; idx < len && offset < sizeof(flow->protos.dhcp.fingerprint) - 2; idx++) { - int rc = snprintf((char*)&flow->protos.dhcp.fingerprint[offset], - sizeof(flow->protos.dhcp.fingerprint) - offset, - "%s%u", (idx > 0) ? "," : "", - (unsigned int)dhcp->options[i+2+idx] & 0xFF); - - if(rc < 0) break; else offset += rc; - } - - flow->protos.dhcp.fingerprint[sizeof(flow->protos.dhcp.fingerprint) - 1] = '\0'; + if(rc < 0) break; else offset += rc; } + + flow->protos.dhcp.fingerprint[sizeof(flow->protos.dhcp.fingerprint) - 1] = '\0'; } else if(id == 60 /* Class Identifier */) { - if(!ndpi_struct->disable_metadata_export) { - char *name = (char*)&dhcp->options[i+2]; - int j = 0; - - j = ndpi_min(len, sizeof(flow->protos.dhcp.class_ident)-1); - strncpy((char*)flow->protos.dhcp.class_ident, name, j); - flow->protos.dhcp.class_ident[j] = '\0'; - } + char *name = (char*)&dhcp->options[i+2]; + int j = 0; + + j = ndpi_min(len, sizeof(flow->protos.dhcp.class_ident)-1); + strncpy((char*)flow->protos.dhcp.class_ident, name, j); + flow->protos.dhcp.class_ident[j] = '\0'; } else if(id == 12 /* Host Name */) { - if(!ndpi_struct->disable_metadata_export) { - char *name = (char*)&dhcp->options[i+2]; - int j = 0; - + char *name = (char*)&dhcp->options[i+2]; + int j = 0; + #ifdef DHCP_DEBUG - NDPI_LOG_DBG2(ndpi_struct, "[DHCP] '%.*s'\n",name,len); + NDPI_LOG_DBG2(ndpi_struct, "[DHCP] '%.*s'\n",name,len); // while(j < len) { printf( "%c", name[j]); j++; }; printf("\n"); #endif - j = ndpi_min(len, sizeof(flow->host_server_name)-1); - strncpy((char*)flow->host_server_name, name, j); - flow->host_server_name[j] = '\0'; - } + j = ndpi_min(len, sizeof(flow->host_server_name)-1); + strncpy((char*)flow->host_server_name, name, j); + flow->host_server_name[j] = '\0'; } i += len + 2; diff --git a/src/lib/protocols/http.c b/src/lib/protocols/http.c index fbe510459..14be88246 100644 --- a/src/lib/protocols/http.c +++ b/src/lib/protocols/http.c @@ -149,9 +149,8 @@ static void setHttpUserAgent(struct ndpi_detection_module_struct *ndpi_struct, * https://github.com/ua-parser/uap-core/blob/master/regexes.yaml */ //printf("==> %s\n", ua); - if(!ndpi_struct->disable_metadata_export) { - snprintf((char*)flow->protos.http.detected_os, sizeof(flow->protos.http.detected_os), "%s", ua); - } + snprintf((char*)flow->protos.http.detected_os, + sizeof(flow->protos.http.detected_os), "%s", ua); } /* ************************************************************* */ @@ -160,14 +159,12 @@ static void ndpi_http_parse_subprotocol(struct ndpi_detection_module_struct *ndp struct ndpi_flow_struct *flow) { if((flow->l4.tcp.http_stage == 0) || (flow->http.url && flow->http_detected)) { char *double_col = strchr((char*)flow->host_server_name, ':'); - ndpi_protocol_match_result ret_match; if(double_col) double_col[0] = '\0'; - ndpi_match_host_subprotocol(ndpi_struct, flow, (char *)flow->host_server_name, - strlen((const char *)flow->host_server_name), - &ret_match, - NDPI_PROTOCOL_HTTP); + ndpi_match_hostname_protocol(ndpi_struct, flow, NDPI_PROTOCOL_HTTP, + (char *)flow->host_server_name, + strlen((const char *)flow->host_server_name)); } } @@ -333,21 +330,17 @@ static void check_content_type_and_change_protocol(struct ndpi_detection_module_ packet->host_line.len, packet->host_line.ptr); /* Copy result for nDPI apps */ - if(!ndpi_struct->disable_metadata_export) { - len = ndpi_min(packet->host_line.len, sizeof(flow->host_server_name)-1); - strncpy((char*)flow->host_server_name, (char*)packet->host_line.ptr, len); - flow->host_server_name[len] = '\0'; - flow->extra_packets_func = NULL; /* We're good now */ - } + len = ndpi_min(packet->host_line.len, sizeof(flow->host_server_name)-1); + strncpy((char*)flow->host_server_name, (char*)packet->host_line.ptr, len); + flow->host_server_name[len] = '\0'; + flow->extra_packets_func = NULL; /* We're good now */ flow->server_id = flow->dst; if(packet->forwarded_line.ptr) { len = ndpi_min(packet->forwarded_line.len, sizeof(flow->protos.http.nat_ip)-1); - if(!ndpi_struct->disable_metadata_export) { - strncpy((char*)flow->protos.http.nat_ip, (char*)packet->forwarded_line.ptr, len); - flow->protos.http.nat_ip[len] = '\0'; - } + strncpy((char*)flow->protos.http.nat_ip, (char*)packet->forwarded_line.ptr, len); + flow->protos.http.nat_ip[len] = '\0'; } ndpi_http_parse_subprotocol(ndpi_struct, flow); diff --git a/src/lib/protocols/kerberos.c b/src/lib/protocols/kerberos.c index 06d258006..43f1127d1 100644 --- a/src/lib/protocols/kerberos.c +++ b/src/lib/protocols/kerberos.c @@ -28,7 +28,7 @@ #include "ndpi_api.h" -//#define KERBEROS_DEBUG 1 +/* #define KERBEROS_DEBUG 1 */ #define KERBEROS_PORT 88 @@ -190,7 +190,7 @@ void ndpi_search_kerberos(struct ndpi_detection_module_struct *ndpi_struct, u_int16_t name_offset; name_offset = body_offset + 13; - for(i=0; i<10; i++) if(packet->payload[name_offset] != 0x1b) name_offset++; /* ASN.1 */ + for(i=0; i<20; i++) if(packet->payload[name_offset] != 0x1b) name_offset++; /* ASN.1 */ #ifdef KERBEROS_DEBUG printf("name_offset=%u [%02X %02X] [byte 0 must be 0x1b]\n", name_offset, packet->payload[name_offset], packet->payload[name_offset+1]); @@ -222,8 +222,7 @@ void ndpi_search_kerberos(struct ndpi_detection_module_struct *ndpi_struct, for(i=0; i<cname_len; i++) cname_str[i] = tolower(cname_str[i]); #ifdef KERBEROS_DEBUG - printf("[AS-REQ][s/dport: %u/%u][Kerberos Cname][len: %u][%s]\n", - sport, dport, cname_len, cname_str); + printf("[AS-REQ][s/dport: %u/%u][Kerberos Cname][len: %u][%s]\n", sport, dport, cname_len, cname_str); #endif if(((strcmp(cname_str, "host") == 0) || (strcmp(cname_str, "ldap") == 0)) && (packet->payload[name_offset+1+cname_len] == 0x1b)) { @@ -242,7 +241,7 @@ void ndpi_search_kerberos(struct ndpi_detection_module_struct *ndpi_struct, } else snprintf(flow->protos.kerberos.username, sizeof(flow->protos.kerberos.username), "%s", cname_str); - for(i=0; i<10; i++) if(packet->payload[realm_offset] != 0x1b) name_offset++; /* ASN.1 */ + for(i=0; i<14; i++) if(packet->payload[realm_offset] != 0x1b) realm_offset++; /* ASN.1 */ #ifdef KERBEROS_DEBUG printf("realm_offset=%u [%02X %02X] [byte 0 must be 0x1b]\n", realm_offset, packet->payload[realm_offset], packet->payload[realm_offset+1]); #endif @@ -279,7 +278,7 @@ void ndpi_search_kerberos(struct ndpi_detection_module_struct *ndpi_struct, u_int name_offset, padding_offset = body_offset + 4; name_offset = padding_offset; - for(i=0; i<10; i++) if(packet->payload[name_offset] != 0x1b) name_offset++; /* ASN.1 */ + for(i=0; i<14; i++) if(packet->payload[name_offset] != 0x1b) name_offset++; /* ASN.1 */ #ifdef KERBEROS_DEBUG printf("name_offset=%u [%02X %02X] [byte 0 must be 0x1b]\n", name_offset, packet->payload[name_offset], packet->payload[name_offset+1]); diff --git a/src/lib/protocols/mdns_proto.c b/src/lib/protocols/mdns_proto.c index 506491be1..2b75f19ec 100644 --- a/src/lib/protocols/mdns_proto.c +++ b/src/lib/protocols/mdns_proto.c @@ -82,11 +82,9 @@ static int ndpi_int_check_mdns_payload(struct ndpi_detection_module_struct /* printf("==> [%d] %s\n", j, answer); */ - if(!ndpi_struct->disable_metadata_export) { - len = ndpi_min(sizeof(flow->protos.mdns.answer)-1, j); - strncpy(flow->protos.mdns.answer, (const char *)answer, len); - flow->protos.mdns.answer[len] = '\0'; - } + len = ndpi_min(sizeof(flow->protos.mdns.answer)-1, j); + strncpy(flow->protos.mdns.answer, (const char *)answer, len); + flow->protos.mdns.answer[len] = '\0'; NDPI_LOG_INFO(ndpi_struct, "found MDNS with answer query\n"); return 1; diff --git a/src/lib/protocols/netbios.c b/src/lib/protocols/netbios.c index e21a5639b..19cffeb82 100644 --- a/src/lib/protocols/netbios.c +++ b/src/lib/protocols/netbios.c @@ -73,13 +73,10 @@ int ndpi_netbios_name_interpret(char *in, char *out, u_int out_len) { static void ndpi_int_netbios_add_connection(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { char name[64]; - - if(!ndpi_struct->disable_metadata_export) { - u_int off = flow->packet.payload[12] == 0x20 ? 12 : 14; - - if(ndpi_netbios_name_interpret((char*)&flow->packet.payload[off], name, sizeof(name)) > 0) - snprintf((char*)flow->host_server_name, sizeof(flow->host_server_name)-1, "%s", name); - } + u_int off = flow->packet.payload[12] == 0x20 ? 12 : 14; + + if(ndpi_netbios_name_interpret((char*)&flow->packet.payload[off], name, sizeof(name)) > 0) + snprintf((char*)flow->host_server_name, sizeof(flow->host_server_name)-1, "%s", name); ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_NETBIOS, NDPI_PROTOCOL_UNKNOWN); } diff --git a/src/lib/protocols/oscar.c b/src/lib/protocols/oscar.c index 91420a5b5..a24b9441e 100644 --- a/src/lib/protocols/oscar.c +++ b/src/lib/protocols/oscar.c @@ -83,10 +83,10 @@ static void ndpi_int_oscar_add_connection(struct ndpi_detection_module_struct *n ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OSCAR, NDPI_PROTOCOL_UNKNOWN); - if (src != NULL) { + if(src != NULL) { src->oscar_last_safe_access_time = packet->tick_timestamp; } - if (dst != NULL) { + if(dst != NULL) { dst->oscar_last_safe_access_time = packet->tick_timestamp; } } @@ -107,10 +107,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct u_int16_t family; u_int16_t type; u_int16_t flag; - u_int32_t req_ID; - struct ndpi_packet_struct * packet = &flow->packet; - struct ndpi_id_struct * src = flow->src; struct ndpi_id_struct * dst = flow->dst; @@ -124,7 +121,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct * [ 4 byte of data ] * * */ - if (packet->payload_packet_len >= 6 && packet->payload[0] == 0x2a) + if(packet->payload_packet_len >= 6 && packet->payload[0] == 0x2a) { /* FLAP__FRAME_TYPE (Channel)*/ @@ -140,7 +137,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct + TLVs | [Class: FLAP__SIGNON_TAGS] TLVs + +--------------------------------------------------+ */ - if (channel == SIGNON && + if(channel == SIGNON && get_u_int16_t(packet->payload, 4) == htons(packet->payload_packet_len - 6) && get_u_int32_t(packet->payload, 6) == htonl(FLAPVERSION)) { @@ -153,28 +150,28 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct return; } /* /\* SCREEN_NAME *\/ */ - /* if (get_u_int16_t(packet->payload, 10) == htons(SCREEN_NAME)) /\* packet->payload[10] == 0x00 && packet->payload[11] == 0x01 *\/ */ + /* if(get_u_int16_t(packet->payload, 10) == htons(SCREEN_NAME)) /\* packet->payload[10] == 0x00 && packet->payload[11] == 0x01 *\/ */ /* { */ /* NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Screen Name \n"); */ /* ndpi_int_oscar_add_connection(ndpi_struct, flow); */ /* return; */ /* } */ /* /\* PASSWD *\/ */ - /* if (get_u_int16_t(packet->payload, 10) == htons(PASSWD)) /\* packet->payload[10] == 0x00 && packet->payload[11] == 0x02 *\/ */ + /* if(get_u_int16_t(packet->payload, 10) == htons(PASSWD)) /\* packet->payload[10] == 0x00 && packet->payload[11] == 0x02 *\/ */ /* { */ /* NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Password (roasted) \n"); */ /* ndpi_int_oscar_add_connection(ndpi_struct, flow); */ /* return; */ /* } */ /* CLIENT_NAME */ - if (get_u_int16_t(packet->payload, 10) == htons(CLIENT_NAME)) /* packet->payload[10] == 0x00 && packet->payload[11] == 0x03 */ + if(get_u_int16_t(packet->payload, 10) == htons(CLIENT_NAME)) /* packet->payload[10] == 0x00 && packet->payload[11] == 0x03 */ { NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Client Name \n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); return; } /* LOGIN_COOKIE */ - if (get_u_int16_t(packet->payload, 10) == htons(LOGIN_COOKIE) && + if(get_u_int16_t(packet->payload, 10) == htons(LOGIN_COOKIE) && get_u_int16_t(packet->payload, 12) == htons(0x0100)) { if(get_u_int16_t(packet->payload, packet->payload_packet_len - 5) == htons(MULTICONN_FLAGS)) /* MULTICONN_FLAGS */ @@ -191,35 +188,35 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* MAJOR_VERSION */ - if (get_u_int16_t(packet->payload, 10) == htons(MAJOR_VERSION)) + if(get_u_int16_t(packet->payload, 10) == htons(MAJOR_VERSION)) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Major_Version \n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); return; } /* MINOR_VERSION */ - if (get_u_int16_t(packet->payload, 10) == htons(MINOR_VERSION)) + if(get_u_int16_t(packet->payload, 10) == htons(MINOR_VERSION)) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Minor_Version \n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); return; } /* POINT_VERSION */ - if (get_u_int16_t(packet->payload, 10) == htons(POINT_VERSION)) + if(get_u_int16_t(packet->payload, 10) == htons(POINT_VERSION)) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Point_Version \n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); return; } /* BUILD_NUM */ - if (get_u_int16_t(packet->payload, 10) == htons(BUILD_NUM)) + if(get_u_int16_t(packet->payload, 10) == htons(BUILD_NUM)) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Build_Num \n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); return; } /* CLIENT_RECONNECT */ - if (get_u_int16_t(packet->payload, 10) == htons(CLIENT_RECONNECT)) + if(get_u_int16_t(packet->payload, 10) == htons(CLIENT_RECONNECT)) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Client_Reconnect \n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); @@ -244,24 +241,24 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct + requestId | 4 byte + +----------------------------------------------+ */ - if (channel == DATA) + if(channel == DATA) { - if (packet->payload_packet_len >= 8) + if(packet->payload_packet_len >= 8) family = get_u_int16_t(packet->payload, 6); else family = 0; - if (packet->payload_packet_len >= 10) + if(packet->payload_packet_len >= 10) type = get_u_int16_t(packet->payload, 8); else type = 0; - if (family == 0 || type == 0) + if(family == 0 || type == 0) { NDPI_EXCLUDE_PROTO(ndpi_struct, flow); return; } /* Family 0x0001 */ - if (family == htons(GE_SE_CTL)) + if(family == htons(GE_SE_CTL)) { switch (type) { @@ -297,7 +294,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0002 */ - if (family == htons(LOC_SRV)) + if(family == htons(LOC_SRV)) { switch (type) { @@ -320,7 +317,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0003 */ - if (family == htons(BUDDY_LIST)) + if(family == htons(BUDDY_LIST)) { switch (type) { @@ -340,7 +337,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0004 */ - if (family == htons(IM)) + if(family == htons(IM)) { switch (type) { @@ -361,7 +358,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0006 */ - if (family == htons(IS)) + if(family == htons(IS)) { switch (type) { @@ -372,7 +369,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0007 */ - if (family == htons(ACC_ADM)) + if(family == htons(ACC_ADM)) { switch (type) { @@ -389,7 +386,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0008 */ - if (family == htons(POPUP)) + if(family == htons(POPUP)) { switch (type) { @@ -399,7 +396,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0009 */ - if (family == htons(PMS)) + if(family == htons(PMS)) { switch (type) { @@ -418,7 +415,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x000b */ - if (family == htons(USS)) + if(family == htons(USS)) { switch (type) { @@ -430,7 +427,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x000d */ - if (family == htons(CHAT_ROOM_SETUP)) + if(family == htons(CHAT_ROOM_SETUP)) { switch (type) { @@ -447,7 +444,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x000e */ - if (family == htons(CHAT_ROOM_ACT)) + if(family == htons(CHAT_ROOM_ACT)) { switch (type) { @@ -464,7 +461,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x000f */ - if (family == htons(USER_SRCH)) + if(family == htons(USER_SRCH)) { switch (type) { @@ -477,7 +474,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0010 */ - if (family == htons(BUDDY_ICON_SERVER)) + if(family == htons(BUDDY_ICON_SERVER)) { switch (type) { @@ -492,7 +489,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0013 */ - if (family == htons(SERVER_STORED_INFO)) + if(family == htons(SERVER_STORED_INFO)) { switch (type) { @@ -521,7 +518,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0015 */ - if (family == htons(ICQ)) + if(family == htons(ICQ)) { switch (type) { @@ -532,7 +529,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0017 */ - if (family == htons(INIT_AUTH)) + if(family == htons(INIT_AUTH)) { switch (type) { @@ -549,12 +546,12 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } /* Family 0x0018 */ - if (family == htons(EMAIL)) + if(family == htons(EMAIL)) { /* TODO */ } /* Family 0x0085 */ - if (family == htons(IS_EXT)) + if(family == htons(IS_EXT)) { switch (type) { @@ -571,15 +568,15 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } /* flag */ - if (packet->payload_packet_len >= 12) + if(packet->payload_packet_len >= 12) { flag = get_u_int16_t(packet->payload, 10); - if (flag == htons(0x0000)|| flag == htons(0x8000) || flag == htons(0x0001)) + if(flag == htons(0x0000)|| flag == htons(0x8000) || flag == htons(0x0001)) { - if (packet->payload_packet_len >= 16) + if(packet->payload_packet_len >= 16) { /* request ID */ - req_ID = get_u_int32_t(packet->payload, 12); + // u_int32_t req_ID = get_u_int32_t(packet->payload, 12); /* if((req_ID <= ((u_int32_t)-1))) */ { NDPI_LOG_INFO(ndpi_struct, "found OSCAR\n"); @@ -594,7 +591,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct ERROR -> FLAP__ERROR_CHANNEL_0x03 A FLAP error - rare */ - if (channel == O_ERROR) + if(channel == O_ERROR) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Error frame \n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); @@ -604,7 +601,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct Close down the FLAP connection gracefully. SIGNOFF: FLAP__SIGNOFF_CHANNEL_0x04 */ - if (channel == SIGNOFF) + if(channel == SIGNOFF) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Signoff frame \n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); @@ -614,7 +611,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct Send a heartbeat to server to help keep connection open. KEEP_ALIVE: FLAP__KEEP_ALIVE_CHANNEL_0x05 */ - if (channel == KEEP_ALIVE) + if(channel == KEEP_ALIVE) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR - Keep Alive frame \n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); @@ -624,11 +621,11 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct /* detect http connections */ - if (packet->payload_packet_len >= 18) { - if ((packet->payload[0] == 'P') && (memcmp(packet->payload, "POST /photo/upload", 18) == 0)) { + if(packet->payload_packet_len >= 18) { + if((packet->payload[0] == 'P') && (memcmp(packet->payload, "POST /photo/upload", 18) == 0)) { NDPI_PARSE_PACKET_LINE_INFO(ndpi_struct, flow, packet); - if (packet->host_line.len >= 18 && packet->host_line.ptr != NULL) { - if (memcmp(packet->host_line.ptr, "lifestream.aol.com", 18) == 0) { + if(packet->host_line.len >= 18 && packet->host_line.ptr != NULL) { + if(memcmp(packet->host_line.ptr, "lifestream.aol.com", 18) == 0) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR over HTTP, POST method\n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); @@ -637,9 +634,9 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } } - if (packet->payload_packet_len > 40) { - if ((packet->payload[0] == 'G') && (memcmp(packet->payload, "GET /", 5) == 0)) { - if ((memcmp(&packet->payload[5], "aim/fetchEvents?aimsid=", 23) == 0) || + if(packet->payload_packet_len > 40) { + if((packet->payload[0] == 'G') && (memcmp(packet->payload, "GET /", 5) == 0)) { + if((memcmp(&packet->payload[5], "aim/fetchEvents?aimsid=", 23) == 0) || (memcmp(&packet->payload[5], "aim/startSession?", 17) == 0) || (memcmp(&packet->payload[5], "aim/gromit/aim_express", 22) == 0) || (memcmp(&packet->payload[5], "b/ss/aolwpaim", 13) == 0) || @@ -649,9 +646,9 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct return; } - if ((memcmp(&packet->payload[5], "aim", 3) == 0) || (memcmp(&packet->payload[5], "im", 2) == 0)) { + if((memcmp(&packet->payload[5], "aim", 3) == 0) || (memcmp(&packet->payload[5], "im", 2) == 0)) { NDPI_PARSE_PACKET_LINE_INFO(ndpi_struct, flow, packet); - if (packet->user_agent_line.len > 15 && packet->user_agent_line.ptr != NULL && + if(packet->user_agent_line.len > 15 && packet->user_agent_line.ptr != NULL && ((memcmp(packet->user_agent_line.ptr, "mobileAIM/", 10) == 0) || (memcmp(packet->user_agent_line.ptr, "ICQ/", 4) == 0) || (memcmp(packet->user_agent_line.ptr, "mobileICQ/", 10) == 0) || @@ -663,14 +660,14 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } NDPI_PARSE_PACKET_LINE_INFO(ndpi_struct, flow, packet); - if (packet->referer_line.ptr != NULL && packet->referer_line.len >= 22) { + if(packet->referer_line.ptr != NULL && packet->referer_line.len >= 22) { - if (memcmp(&packet->referer_line.ptr[packet->referer_line.len - NDPI_STATICSTRING_LEN("WidgetMain.swf")], + if(memcmp(&packet->referer_line.ptr[packet->referer_line.len - NDPI_STATICSTRING_LEN("WidgetMain.swf")], "WidgetMain.swf", NDPI_STATICSTRING_LEN("WidgetMain.swf")) == 0) { u_int16_t i; for (i = 0; i < (packet->referer_line.len - 22); i++) { - if (packet->referer_line.ptr[i] == 'a') { - if (memcmp(&packet->referer_line.ptr[i + 1], "im/gromit/aim_express", 21) == 0) { + if(packet->referer_line.ptr[i] == 'a') { + if(memcmp(&packet->referer_line.ptr[i + 1], "im/gromit/aim_express", 21) == 0) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR over HTTP : aim/gromit/aim_express\n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); @@ -681,13 +678,13 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } } - if (memcmp(packet->payload, "CONNECT ", 8) == 0) { - if (memcmp(packet->payload, "CONNECT login.icq.com:443 HTTP/1.", 33) == 0) { + if(memcmp(packet->payload, "CONNECT ", 8) == 0) { + if(memcmp(packet->payload, "CONNECT login.icq.com:443 HTTP/1.", 33) == 0) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR ICQ-HTTP\n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); return; } - if (memcmp(packet->payload, "CONNECT login.oscar.aol.com:5190 HTTP/1.", 40) == 0) { + if(memcmp(packet->payload, "CONNECT login.oscar.aol.com:5190 HTTP/1.", 40) == 0) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR AIM-HTTP\n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); return; @@ -696,32 +693,32 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } } - if (packet->payload_packet_len > 43 + if(packet->payload_packet_len > 43 && memcmp(packet->payload, "GET http://http.proxy.icq.com/hello HTTP/1.", 43) == 0) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR ICQ-HTTP PROXY\n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); return; } - if (packet->payload_packet_len > 46 + if(packet->payload_packet_len > 46 && memcmp(packet->payload, "GET http://aimhttp.oscar.aol.com/hello HTTP/1.", 46) == 0) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR AIM-HTTP PROXY\n"); ndpi_int_oscar_add_connection(ndpi_struct, flow); return; } - if (packet->payload_packet_len > 5 && get_u_int32_t(packet->payload, 0) == htonl(0x05010003)) { + if(packet->payload_packet_len > 5 && get_u_int32_t(packet->payload, 0) == htonl(0x05010003)) { NDPI_LOG_DBG2(ndpi_struct, "Maybe OSCAR Picturetransfer\n"); return; } - if (packet->payload_packet_len == 10 && get_u_int32_t(packet->payload, 0) == htonl(0x05000001) && + if(packet->payload_packet_len == 10 && get_u_int32_t(packet->payload, 0) == htonl(0x05000001) && get_u_int32_t(packet->payload, 4) == 0) { NDPI_LOG_DBG2(ndpi_struct, "Maybe OSCAR Picturetransfer\n"); return; } - if (packet->payload_packet_len >= 70 && + if(packet->payload_packet_len >= 70 && memcmp(&packet->payload[packet->payload_packet_len - 26], "\x67\x00\x65\x00\x74\x00\x43\x00\x61\x00\x74\x00\x61\x00\x6c\x00\x6f\x00\x67", 19) == 0) { NDPI_LOG_INFO(ndpi_struct, "found OSCAR PICTURE TRANSFER\n"); @@ -729,9 +726,9 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct return; } - if (NDPI_SRC_OR_DST_HAS_PROTOCOL(src, dst, NDPI_PROTOCOL_OSCAR) != 0) { + if(NDPI_SRC_OR_DST_HAS_PROTOCOL(src, dst, NDPI_PROTOCOL_OSCAR) != 0) { - if (flow->packet_counter == 1 + if(flow->packet_counter == 1 && ((packet->payload_packet_len == 9 && memcmp(packet->payload, "\x00\x09\x00\x00\x83\x01\xc0\x00\x00", 9) == 0) @@ -742,13 +739,13 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct } #if 0 - if (flow->oscar_video_voice && ntohs(get_u_int16_t(packet->payload, 0)) == packet->payload_packet_len + if(flow->oscar_video_voice && ntohs(get_u_int16_t(packet->payload, 0)) == packet->payload_packet_len && packet->payload[2] == 0x00 && packet->payload[3] == 0x00) { } #endif - if (packet->payload_packet_len >= 70 && ntohs(get_u_int16_t(packet->payload, 4)) == packet->payload_packet_len) { - if (memcmp(packet->payload, "OFT", 3) == 0 && + if(packet->payload_packet_len >= 70 && ntohs(get_u_int16_t(packet->payload, 4)) == packet->payload_packet_len) { + if(memcmp(packet->payload, "OFT", 3) == 0 && ((packet->payload[3] == '3' && ((memcmp(&packet->payload[4], "\x01\x00\x01\x01", 4) == 0) || (memcmp(&packet->payload[6], "\x01\x01\x00", 3) == 0))) || (packet->payload[3] == '2' && ((memcmp(&packet->payload[6], "\x01\x01", 2) @@ -760,7 +757,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct return; } - if (memcmp(packet->payload, "ODC2", 4) == 0 && memcmp(&packet->payload[6], "\x00\x01\x00\x06", 4) == 0) { + if(memcmp(packet->payload, "ODC2", 4) == 0 && memcmp(&packet->payload[6], "\x00\x01\x00\x06", 4) == 0) { //PICTURE TRANSFER PATTERN EXMAPLE:: //4f 44 43 32 00 4c 00 01 00 06 00 00 00 00 00 00 ODC2.L.......... NDPI_LOG_INFO(ndpi_struct, "found OSCAR PICTURE TRANSFER\n"); @@ -768,7 +765,7 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct return; } } - if (packet->payload_packet_len > 40 && (memcmp(&packet->payload[2], "\x04\x4a\x00", 3) == 0) + if(packet->payload_packet_len > 40 && (memcmp(&packet->payload[2], "\x04\x4a\x00", 3) == 0) && (memcmp(&packet->payload[6], "\x00\x00", 2) == 0) && packet->payload[packet->payload_packet_len - 15] == 'F' && packet->payload[packet->payload_packet_len - 12] == 'L' @@ -776,21 +773,21 @@ static void ndpi_search_oscar_tcp_connect(struct ndpi_detection_module_struct && (memcmp(&packet->payload[packet->payload_packet_len - 2], "\x00\x00", 2) == 0)) { 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) { + if(ntohs(packet->tcp->dest) == 443 || ntohs(packet->tcp->source) == 443) { flow->oscar_ssl_voice_stage = 1; } return; } } - if (flow->packet_counter < 3 && packet->payload_packet_len > 11 && (memcmp(packet->payload, "\x00\x37\x04\x4a", 4) + if(flow->packet_counter < 3 && packet->payload_packet_len > 11 && (memcmp(packet->payload, "\x00\x37\x04\x4a", 4) || memcmp(packet->payload, "\x00\x0a\x04\x4a", 4))) { return; } - if (packet->detected_protocol_stack[0] != NDPI_PROTOCOL_OSCAR) { + if(packet->detected_protocol_stack[0] != NDPI_PROTOCOL_OSCAR) { NDPI_EXCLUDE_PROTO(ndpi_struct, flow); return; } @@ -800,7 +797,7 @@ void ndpi_search_oscar(struct ndpi_detection_module_struct *ndpi_struct, struct { struct ndpi_packet_struct *packet = &flow->packet; NDPI_LOG_DBG(ndpi_struct, "search OSCAR\n"); - if (packet->tcp != NULL) { + if(packet->tcp != NULL) { ndpi_search_oscar_tcp_connect(ndpi_struct, flow); } } diff --git a/src/lib/protocols/quic.c b/src/lib/protocols/quic.c index 0d83e4739..5c565e5a1 100644 --- a/src/lib/protocols/quic.c +++ b/src/lib/protocols/quic.c @@ -129,23 +129,21 @@ void ndpi_search_quic(struct ndpi_detection_module_struct *ndpi_struct, sni_offset++; if((sni_offset+len) < udp_len) { - if(!ndpi_struct->disable_metadata_export) { - int max_len = sizeof(flow->host_server_name)-1, j = 0; - ndpi_protocol_match_result ret_match; - - if(len > max_len) len = max_len; - - while((len > 0) && (sni_offset < udp_len)) { - flow->host_server_name[j++] = packet->payload[sni_offset]; - sni_offset++, len--; - } - - ndpi_match_host_subprotocol(ndpi_struct, flow, - (char *)flow->host_server_name, - strlen((const char*)flow->host_server_name), - &ret_match, - NDPI_PROTOCOL_QUIC); + int max_len = sizeof(flow->host_server_name)-1, j = 0; + ndpi_protocol_match_result ret_match; + + if(len > max_len) len = max_len; + + while((len > 0) && (sni_offset < udp_len)) { + flow->host_server_name[j++] = packet->payload[sni_offset]; + sni_offset++, len--; } + + ndpi_match_host_subprotocol(ndpi_struct, flow, + (char *)flow->host_server_name, + strlen((const char*)flow->host_server_name), + &ret_match, + NDPI_PROTOCOL_QUIC); } break; diff --git a/src/lib/protocols/ssh.c b/src/lib/protocols/ssh.c index 2f7fe97ae..44eaf333e 100644 --- a/src/lib/protocols/ssh.c +++ b/src/lib/protocols/ssh.c @@ -251,18 +251,16 @@ static void ndpi_search_ssh_tcp(struct ndpi_detection_module_struct *ndpi_struct if(flow->l4.tcp.ssh_stage == 0) { if(packet->payload_packet_len > 7 && packet->payload_packet_len < 100 && memcmp(packet->payload, "SSH-", 4) == 0) { - if(!ndpi_struct->disable_metadata_export) { - int len = ndpi_min(sizeof(flow->protos.ssh.client_signature)-1, packet->payload_packet_len); - - strncpy(flow->protos.ssh.client_signature, (const char *)packet->payload, len); - flow->protos.ssh.client_signature[len] = '\0'; - ndpi_ssh_zap_cr(flow->protos.ssh.client_signature, len); - + int len = ndpi_min(sizeof(flow->protos.ssh.client_signature)-1, packet->payload_packet_len); + + strncpy(flow->protos.ssh.client_signature, (const char *)packet->payload, len); + flow->protos.ssh.client_signature[len] = '\0'; + ndpi_ssh_zap_cr(flow->protos.ssh.client_signature, len); + #ifdef SSH_DEBUG - printf("[SSH] [client_signature: %s]\n", flow->protos.ssh.client_signature); -#endif - } - + printf("[SSH] [client_signature: %s]\n", flow->protos.ssh.client_signature); +#endif + NDPI_LOG_DBG2(ndpi_struct, "ssh stage 0 passed\n"); flow->l4.tcp.ssh_stage = 1 + packet->packet_direction; ndpi_int_ssh_add_connection(ndpi_struct, flow); @@ -271,24 +269,19 @@ static void ndpi_search_ssh_tcp(struct ndpi_detection_module_struct *ndpi_struct } else if(flow->l4.tcp.ssh_stage == (2 - packet->packet_direction)) { if(packet->payload_packet_len > 7 && packet->payload_packet_len < 500 && memcmp(packet->payload, "SSH-", 4) == 0) { - if(!ndpi_struct->disable_metadata_export) { - int len = ndpi_min(sizeof(flow->protos.ssh.server_signature)-1, packet->payload_packet_len); - - strncpy(flow->protos.ssh.server_signature, (const char *)packet->payload, len); - flow->protos.ssh.server_signature[len] = '\0'; - ndpi_ssh_zap_cr(flow->protos.ssh.server_signature, len); - + int len = ndpi_min(sizeof(flow->protos.ssh.server_signature)-1, packet->payload_packet_len); + + strncpy(flow->protos.ssh.server_signature, (const char *)packet->payload, len); + flow->protos.ssh.server_signature[len] = '\0'; + ndpi_ssh_zap_cr(flow->protos.ssh.server_signature, len); + #ifdef SSH_DEBUG - printf("[SSH] [server_signature: %s]\n", flow->protos.ssh.server_signature); + printf("[SSH] [server_signature: %s]\n", flow->protos.ssh.server_signature); #endif - - NDPI_LOG_DBG2(ndpi_struct, "ssh stage 1 passed\n"); - flow->guessed_host_protocol_id = flow->guessed_protocol_id = NDPI_PROTOCOL_SSH; - } else { - NDPI_LOG_INFO(ndpi_struct, "found ssh\n"); - ndpi_int_ssh_add_connection(ndpi_struct, flow); - } - + + NDPI_LOG_DBG2(ndpi_struct, "ssh stage 1 passed\n"); + flow->guessed_host_protocol_id = flow->guessed_protocol_id = NDPI_PROTOCOL_SSH; + #ifdef SSH_DEBUG printf("[SSH] [completed stage: %u]\n", flow->l4.tcp.ssh_stage); #endif diff --git a/src/lib/protocols/tls.c b/src/lib/protocols/tls.c index 185056fba..737585db4 100644 --- a/src/lib/protocols/tls.c +++ b/src/lib/protocols/tls.c @@ -31,9 +31,17 @@ extern char *strptime(const char *s, const char *format, struct tm *tm); +extern int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow); + +#if 0 +#define DEBUG_TLS_MEMORY 1 +#define DEBUG_TLS 1 +#endif -/* #define DEBUG_TLS 1 */ +// #define DEBUG_CERTIFICATE_HASH + /* #define DEBUG_FINGERPRINT 1 */ /* @@ -60,8 +68,8 @@ extern u_int8_t is_skype_flow(struct ndpi_detection_module_struct *ndpi_struct, /* stun.c */ extern u_int32_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev); -extern int sslTryAndRetrieveServerCertificate(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow); +static void ndpi_int_tls_add_connection(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, u_int32_t protocol); /* **************************************** */ @@ -93,30 +101,61 @@ static u_int32_t ndpi_tls_refine_master_protocol(struct ndpi_detection_module_st } } - return protocol; + return(protocol); } /* **************************************** */ -static void sslInitExtraPacketProcessing(struct ndpi_flow_struct *flow) { - flow->check_extra_packets = 1; +void ndpi_search_tls_tcp_memory(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) { + struct ndpi_packet_struct *packet = &flow->packet; - /* 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; -} + /* TCP */ +#ifdef DEBUG_TLS_MEMORY + printf("[TLS Mem] Handling TCP/TLS flow [payload_len: %u][buffer_len: %u][direction: %u]\n", + packet->payload_packet_len, + flow->l4.tcp.tls.message.buffer_len, + packet->packet_direction); +#endif -/* **************************************** */ + if(flow->l4.tcp.tls.message.buffer == NULL) { + /* Allocate buffer */ + flow->l4.tcp.tls.message.buffer_len = 2048, flow->l4.tcp.tls.message.buffer_used = 0; + flow->l4.tcp.tls.message.buffer = (u_int8_t*)ndpi_malloc(flow->l4.tcp.tls.message.buffer_len); -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) - ; - else - protocol = ndpi_tls_refine_master_protocol(ndpi_struct, flow, protocol); + if(flow->l4.tcp.tls.message.buffer == NULL) + return; - ndpi_set_detected_protocol(ndpi_struct, flow, protocol, NDPI_PROTOCOL_TLS); - sslInitExtraPacketProcessing(flow); +#ifdef DEBUG_TLS_MEMORY + printf("[TLS Mem] Allocating %u buffer\n", flow->l4.tcp.tls.message.buffer_len); +#endif + } + + u_int avail_bytes = flow->l4.tcp.tls.message.buffer_len - flow->l4.tcp.tls.message.buffer_used; + if(avail_bytes < packet->payload_packet_len) { + u_int new_len = flow->l4.tcp.tls.message.buffer_len + packet->payload_packet_len; + void *newbuf = ndpi_realloc(flow->l4.tcp.tls.message.buffer, + flow->l4.tcp.tls.message.buffer_len, new_len); + if(!newbuf) return; + + flow->l4.tcp.tls.message.buffer = (u_int8_t*)newbuf, flow->l4.tcp.tls.message.buffer_len = new_len; + avail_bytes = flow->l4.tcp.tls.message.buffer_len - flow->l4.tcp.tls.message.buffer_used; + +#ifdef DEBUG_TLS_MEMORY + printf("[TLS Mem] Enlarging %u -> %u buffer\n", flow->l4.tcp.tls.message.buffer_len, new_len); +#endif + } + + if(avail_bytes >= packet->payload_packet_len) { + memcpy(&flow->l4.tcp.tls.message.buffer[flow->l4.tcp.tls.message.buffer_used], + packet->payload, packet->payload_packet_len); + + flow->l4.tcp.tls.message.buffer_used += packet->payload_packet_len; +#ifdef DEBUG_TLS_MEMORY + printf("[TLS Mem] Copied data to buffer [%u/%u bytes]\n", + flow->l4.tcp.tls.message.buffer_used, flow->l4.tcp.tls.message.buffer_len); +#endif + } } /* **************************************** */ @@ -134,9 +173,12 @@ static void ndpi_int_tls_add_connection(struct ndpi_detection_module_struct *ndp /* **************************************** */ -static void stripCertificateTrailer(char *buffer, int buffer_len) { - int i, is_puny; - +static void cleanupServerName(char *buffer, int buffer_len) { + u_int i; + +#if 0 + int is_puny; + // printf("->%s<-\n", buffer); for(i = 0; i < buffer_len; i++) { @@ -159,7 +201,6 @@ static void stripCertificateTrailer(char *buffer, int buffer_len) { // not a punycode string - need more checks if(is_puny == 0) { - if(i > 0) i--; while(i > 0) { @@ -177,7 +218,8 @@ static void stripCertificateTrailer(char *buffer, int buffer_len) { buffer[i] = '\0', buffer_len = i; } } - +#endif + /* Now all lowecase */ for(i=0; i<buffer_len; i++) buffer[i] = tolower(buffer[i]); @@ -185,1412 +227,971 @@ static void stripCertificateTrailer(char *buffer, int buffer_len) { /* **************************************** */ -/* https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967 */ - -#define JA3_STR_LEN 1024 -#define MAX_NUM_JA3 128 +/* See https://blog.catchpoint.com/2017/05/12/dissecting-tls-using-wireshark/ */ +static void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, + u_int16_t offset, u_int16_t certificate_len) { + struct ndpi_packet_struct *packet = &flow->packet; + u_int num_found = 0, i, j; + char buffer[64] = { '\0' }; + +#ifdef DEBUG_TLS + printf("[TLS] %s() [offset: %u][certificate_len: %u]\n", __FUNCTION__, offset, certificate_len); +#endif -struct ja3_info { - u_int16_t tls_handshake_version; - u_int16_t num_cipher, cipher[MAX_NUM_JA3]; - u_int16_t num_tls_extension, tls_extension[MAX_NUM_JA3]; - u_int16_t num_elliptic_curve, elliptic_curve[MAX_NUM_JA3]; - u_int8_t num_elliptic_curve_point_format, elliptic_curve_point_format[MAX_NUM_JA3]; -}; + /* Check after handshake protocol header (5 bytes) and message header (4 bytes) */ + for(i = offset; i < certificate_len; 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; -int getTLScertificate(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; - 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]; - ndpi_MD5_CTX ctx; - u_char md5_hash[16]; - int i; + // packet is truncated... further inspection is not needed + if(i+4+server_len >= packet->payload_packet_len) { + break; + } - 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); - } - } + char *server_org = (char*)&packet->payload[i+5]; - flow->protos.stun_ssl.ssl.ssl_version = pkt_tls_version; + u_int len = (u_int)ndpi_min(server_len, sizeof(buffer)-1); + strncpy(buffer, server_org, len); + buffer[len] = '\0'; - memset(&ja3, 0, sizeof(ja3)); + // 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 DEBUG_TLS - { - 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); - } + printf("Certificate organization: %s\n", flow->protos.stun_ssl.ssl.server_organization); #endif + } + } else if((packet->payload[i] == 0x30) && (packet->payload[i+1] == 0x1e) && (packet->payload[i+2] == 0x17)) { + /* Certificate Validity */ + u_int8_t len = packet->payload[i+3]; + u_int offset = i+4; - /* - Nothing matched so far: let's decode the certificate with some heuristics - Patches courtesy of Denys Fedoryshchenko <nuclearcat@nuclearcat.com> - */ - 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; + if((offset+len) < packet->payload_packet_len) { + char utcDate[32]; - memset(buffer, 0, buffer_len); +#ifdef DEBUG_TLS + printf("[CERTIFICATE] notBefore [len: %u][", len); + for(j=0; j<len; j++) printf("%c", packet->payload[i+4+j]); + printf("]\n"); +#endif - /* Truncate total len, search at least in incomplete packet */ - if(total_len > packet->payload_packet_len) - total_len = packet->payload_packet_len; + if(len < (sizeof(utcDate)-1)) { + struct tm utc; + utc.tm_isdst = -1; /* Not set by strptime */ - /* 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; + strncpy(utcDate, (const char*)&packet->payload[i+4], len); + utcDate[len] = '\0'; + /* 141021000000Z */ + if(strptime(utcDate, "%y%m%d%H%M%SZ", &utc) != NULL) { + flow->protos.stun_ssl.ssl.notBefore = timegm(&utc); #ifdef DEBUG_TLS - printf("SSL [len: %u][handshake_protocol: %02X]\n", packet->payload_packet_len, handshake_protocol); + printf("[CERTIFICATE] notBefore %u [%s]\n", + flow->protos.stun_ssl.ssl.notBefore, utcDate); #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, rc; - - 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])); + offset += len; - ja3.tls_handshake_version = tls_version; + if((offset+1) < packet->payload_packet_len) { + len = packet->payload[offset+1]; - if(handshake_protocol == 0x02) { - u_int16_t offset = base_offset, extension_len, j; - u_int8_t session_id_len = packet->payload[offset]; + offset += 2; + if((offset+len) < packet->payload_packet_len) { #ifdef DEBUG_TLS - printf("SSL Server Hello [version: 0x%04X]\n", tls_version); + printf("[CERTIFICATE] notAfter [len: %u][", len); + for(j=0; j<len; j++) printf("%c", packet->payload[offset+j]); + printf("]\n"); #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 = tls_version; - - if(packet->udp) - offset += 1; - else { - if(tls_version < 0x7F15 /* TLS 1.3 lacks of session id */) - offset += session_id_len+1; - } + if(len < (sizeof(utcDate)-1)) { + struct tm utc; + utc.tm_isdst = -1; /* Not set by strptime */ - 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]; + strncpy(utcDate, (const char*)&packet->payload[offset], len); + utcDate[len] = '\0'; + /* 141021000000Z */ + if(strptime(utcDate, "%y%m%d%H%M%SZ", &utc) != NULL) { + flow->protos.stun_ssl.ssl.notAfter = timegm(&utc); #ifdef DEBUG_TLS - printf("TLS [server][session_id_len: %u][cipher: %04X]\n", session_id_len, ja3.cipher[0]); + printf("[CERTIFICATE] notAfter %u [%s]\n", + flow->protos.stun_ssl.ssl.notAfter, utcDate); #endif - - offset += 2 + 1; - - if((offset + 1) < packet->payload_packet_len) /* +1 because we are goint to read 2 bytes */ - extension_len = ntohs(*((u_int16_t*)&packet->payload[offset])); - else - extension_len = 0; + } + } + } + } + } + } else if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x1d) && (packet->payload[i+2] == 0x11)) { + /* Organization OID: 2.5.29.17 (subjectAltName) */ + u_int16_t servernames_len = 0; + char servernames[2048]; #ifdef DEBUG_TLS - printf("TLS [server][extension_len: %u]\n", extension_len); + printf("******* [TLS] Found subjectAltName\n"); #endif - offset += 2; - for(i=0; i<extension_len; ) { - u_int16_t extension_id, extension_len; + i += 3 /* skip the initial patten 55 1D 11 */; + i++; /* skip the first type, 0x04 == BIT STRING, and jump to it's length */ + i += packet->payload[i] & 0x80 ? packet->payload[i] & 0x7F : 0; /* skip BIT STRING length */ + i += 2; /* skip the second type, 0x30 == SEQUENCE, and jump to it's length */ + i += packet->payload[i] & 0x80 ? packet->payload[i] & 0x7F : 0; /* skip SEQUENCE length */ + i++; + + while(i < packet->payload_packet_len) { + if(packet->payload[i] == 0x82) { + if((i < (packet->payload_packet_len - 1)) + && ((i + packet->payload[i + 1] + 2) < packet->payload_packet_len)) { + u_int8_t len = packet->payload[i + 1]; + char dNSName[256]; + int rc; + + i += 2; + + strncpy(dNSName, (const char*)&packet->payload[i], len); + dNSName[len] = '\0'; - if(offset >= (packet->payload_packet_len+4)) break; + cleanupServerName(dNSName, len); - extension_id = ntohs(*((u_int16_t*)&packet->payload[offset])); - extension_len = ntohs(*((u_int16_t*)&packet->payload[offset+2])); + rc = snprintf(&servernames[servernames_len], sizeof(servernames)-servernames_len, "%s%s", + (servernames_len == 0) ? "" : ",", dNSName); - if(ja3.num_tls_extension < MAX_NUM_JA3) - ja3.tls_extension[ja3.num_tls_extension++] = extension_id; + if(rc > 0) + servernames_len += rc; -#ifdef DEBUG_TLS - printf("TLS [server][extension_id: %u/0x%04X][len: %u]\n", - extension_id, extension_id, extension_len); +#if DEBUG_TLS + printf("[TLS] dNSName %s [%s]\n", dNSName, servernames); #endif - if(extension_id == 43 /* supported versions */) { - if(extension_len >= 2) { - u_int16_t tls_version = ntohs(*((u_int16_t*)&packet->payload[offset+4])); - -#ifdef DEBUG_TLS - printf("TLS [server] [TLS version: 0x%04X]\n", tls_version); -#endif - - flow->protos.stun_ssl.ssl.ssl_version = tls_version; + if(flow->protos.stun_ssl.ssl.server_names == NULL) + flow->protos.stun_ssl.ssl.server_names = ndpi_strdup(dNSName), + flow->protos.stun_ssl.ssl.server_names_len = strlen(dNSName); + else { + u_int16_t dNSName_len = strlen(dNSName); + u_int16_t newstr_len = flow->protos.stun_ssl.ssl.server_names_len + dNSName_len + 1; + char *newstr = (char*)ndpi_realloc(flow->protos.stun_ssl.ssl.server_names, + flow->protos.stun_ssl.ssl.server_names_len+1, newstr_len+1); + + if(newstr) { + flow->protos.stun_ssl.ssl.server_names = newstr; + flow->protos.stun_ssl.ssl.server_names[flow->protos.stun_ssl.ssl.server_names_len] = ','; + strncpy(&flow->protos.stun_ssl.ssl.server_names[flow->protos.stun_ssl.ssl.server_names_len+1], + dNSName, dNSName_len+1); + flow->protos.stun_ssl.ssl.server_names[newstr_len] = '\0'; + flow->protos.stun_ssl.ssl.server_names_len = newstr_len; } } + + if(!flow->l4.tcp.tls.subprotocol_detected) + if(ndpi_match_hostname_protocol(ndpi_struct, flow, NDPI_PROTOCOL_TLS, dNSName, len)) + flow->l4.tcp.tls.subprotocol_detected = 1; - i += 4 + extension_len, offset += 4 + extension_len; + i += len; + } else { +#if DEBUG_TLS + printf("[TLS] Leftover %u bytes", packet->payload_packet_len - i); +#endif + break; } + } else { + break; + } + } /* while */ + } + } +} - ja3_str_len = snprintf(ja3_str, sizeof(ja3_str), "%u,", ja3.tls_handshake_version); +/* **************************************** */ - for(i=0; i<ja3.num_cipher; i++) { - rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", (i > 0) ? "-" : "", ja3.cipher[i]); +/* See https://blog.catchpoint.com/2017/05/12/dissecting-tls-using-wireshark/ */ +int processCertificate(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) { + struct ndpi_packet_struct *packet = &flow->packet; + u_int32_t certificates_length, length = (packet->payload[1] << 16) + (packet->payload[2] << 8) + packet->payload[3]; + u_int16_t certificates_offset = 7; + u_int8_t num_certificates_found = 0; + +#ifdef DEBUG_TLS + printf("[TLS] %s() [payload_packet_len=%u][direction: %u][%02X %02X %02X %02X %02X %02X...]\n", + __FUNCTION__, packet->payload_packet_len, + packet->packet_direction, + packet->payload[0], packet->payload[1], packet->payload[2], + packet->payload[3], packet->payload[4], packet->payload[5]); +#endif - if(rc <= 0) break; else ja3_str_len += rc; - } - - rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); - if(rc > 0) ja3_str_len += rc; - - /* ********** */ + if(packet->payload_packet_len != (length + 4)) + return(-1); /* Invalid length */ - for(i=0; i<ja3.num_tls_extension; i++) { - int rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", (i > 0) ? "-" : "", ja3.tls_extension[i]); + certificates_length = (packet->payload[4] << 16) + (packet->payload[5] << 8) + packet->payload[6]; + + if((certificates_length+3) != length) + return(-2); /* Invalid length */ + + if((flow->l4.tcp.tls.srv_cert_fingerprint_ctx = (void*)ndpi_malloc(sizeof(SHA1_CTX))) == NULL) + return(-3); /* Not enough memory */ - if(rc <= 0) break; else ja3_str_len += rc; - } - -#ifdef DEBUG_TLS - printf("TLS [server] %s\n", ja3_str); -#endif + /* Now let's process each individual certificates */ + while(certificates_offset < certificates_length) { + u_int16_t certificate_len = (packet->payload[certificates_offset] << 16) + (packet->payload[certificates_offset+1] << 8) + packet->payload[certificates_offset+2]; + certificates_offset += 3; #ifdef DEBUG_TLS - printf("[JA3] Server: %s \n", ja3_str); + printf("[TLS] Processing %u bytes certificate [%02X %02X %02X]\n", + certificate_len, + packet->payload[certificates_offset], + packet->payload[certificates_offset+1], + packet->payload[certificates_offset+2]); #endif - ndpi_MD5Init(&ctx); - ndpi_MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str)); - ndpi_MD5Final(md5_hash, &ctx); + if(num_certificates_found++ == 0) /* Dissect only the first certificate that is the one we care */ { + /* For SHA-1 we take into account only the first certificate and not all of them */ + + SHA1Init(flow->l4.tcp.tls.srv_cert_fingerprint_ctx); + +#ifdef DEBUG_CERTIFICATE_HASH + { + int i; + + for(i=0;i<certificate_len;i++) + printf("%02X ", packet->payload[certificates_offset+i]); + + printf("\n"); + } +#endif + + SHA1Update(flow->l4.tcp.tls.srv_cert_fingerprint_ctx, + &packet->payload[certificates_offset], + certificate_len); + + SHA1Final(flow->l4.tcp.tls.sha1_certificate_fingerprint, flow->l4.tcp.tls.srv_cert_fingerprint_ctx); - for(i=0, j=0; i<16; i++) { - int rc = snprintf(&flow->protos.stun_ssl.ssl.ja3_server[j], - sizeof(flow->protos.stun_ssl.ssl.ja3_server)-j, "%02x", md5_hash[i]); - if(rc <= 0) break; else j += rc; - } - + flow->l4.tcp.tls.fingerprint_set = 1; + #ifdef DEBUG_TLS - printf("[JA3] Server: %s \n", flow->protos.stun_ssl.ssl.ja3_server); + { + int i; + + printf("[TLS] SHA-1: "); + for(i=0;i<20;i++) + printf("%s%02X", (i > 0) ? ":" : "", flow->l4.tcp.tls.sha1_certificate_fingerprint[i]); + printf("\n"); + } #endif - flow->l4.tcp.tls_seen_server_cert = 1; - } else - flow->l4.tcp.tls_seen_certificate = 1; + processCertificateElements(ndpi_struct, flow, certificates_offset, certificate_len); + } + + certificates_offset += certificate_len; + } + + flow->extra_packets_func = NULL; /* We're good now */ + return(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, off = 0; +/* **************************************** */ - if(packet->payload[i] == 0x55) { - num_found++, off++; +static int processTLSBlock(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) { + struct ndpi_packet_struct *packet = &flow->packet; + + switch(packet->payload[0] /* block type */) { + case 0x01: /* Client Hello */ + case 0x02: /* Server Hello */ + processClientServerHello(ndpi_struct, flow); + flow->l4.tcp.tls.hello_processed = 1; + ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_TLS); + break; + + case 0x0b: /* Certificate */ + processCertificate(ndpi_struct, flow); + flow->l4.tcp.tls.certificate_processed = 1; + break; - if(num_found != 2) continue; - } + default: + return(-1); + } - server_len = packet->payload[i+3+off]; - - if((server_len+i+3) < packet->payload_packet_len) { - char *server_name = (char*)&packet->payload[i+4+off]; - u_int8_t begin = 0, len, j, num_dots; - - while(begin < server_len) { - if(!ndpi_isprint(server_name[begin])) - begin++; - else - break; - } + return(0); +} - len = ndpi_min(server_len-begin, buffer_len-1); - // len = buffer_len-1; - - strncpy(buffer, &server_name[begin], len); - buffer[len] = '\0'; - - // if(len != (buffer_len-1)) printf("len=%u / buffer_len-1=%u\n", len, buffer_len-1); - - /* We now have to check if this looks like an IP address or host name */ - for(j=0, num_dots = 0; j<len; j++) { - if(!ndpi_isprint((buffer[j]))) { - num_dots = 0; /* This is not what we look for */ - break; - } else if(buffer[j] == '.') { - num_dots++; - if(num_dots >=1) break; - } - } +/* **************************************** */ - if(num_dots >= 1) { - if(!ndpi_struct->disable_metadata_export) { - ndpi_protocol_match_result ret_match; - u_int16_t subproto; - - stripCertificateTrailer(buffer, buffer_len); - snprintf(flow->protos.stun_ssl.ssl.server_certificate, - sizeof(flow->protos.stun_ssl.ssl.server_certificate), "%s", buffer); - -#ifdef DEBUG_TLS - printf("[server_certificate: %s]\n", flow->protos.stun_ssl.ssl.server_certificate); +static int ndpi_search_tls_tcp(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) { + struct ndpi_packet_struct *packet = &flow->packet; + int rc = 1; + +#ifdef DEBUG_TLS_MEMORY + printf("[TLS Mem] ndpi_search_tls_tcp() [payload_packet_len: %u]\n", + packet->payload_packet_len); #endif - - subproto = ndpi_match_host_subprotocol(ndpi_struct, flow, - flow->protos.stun_ssl.ssl.server_certificate, - strlen(flow->protos.stun_ssl.ssl.server_certificate), - &ret_match, - NDPI_PROTOCOL_TLS); - - if(subproto != NDPI_PROTOCOL_UNKNOWN) - ndpi_set_detected_protocol(ndpi_struct, flow, subproto, NDPI_PROTOCOL_TLS); - } - return(1 /* Server Certificate */); - } - } - } - } - } else if(handshake_protocol == 0x01 /* Client Hello */) { - u_int offset; + if(packet->payload_packet_len == 0) + return(1); /* Keep working */ -#ifdef DEBUG_TLS - printf("[base_offset: %u][payload_packet_len: %u]\n", base_offset, packet->payload_packet_len); + ndpi_search_tls_tcp_memory(ndpi_struct, flow); + + while(1) { + u_int16_t len, p_len; + const u_int8_t *p; + + if(flow->l4.tcp.tls.message.buffer_used < 5) + return(1); /* Keep working */ + + len = (flow->l4.tcp.tls.message.buffer[3] << 8) + flow->l4.tcp.tls.message.buffer[4] + 5; + + if(len > flow->l4.tcp.tls.message.buffer_used) { +#ifdef DEBUG_TLS_MEMORY + printf("[TLS Mem] Not enough TLS data [%u < %u][%02X %02X %02X %02X %02X]\n", + len, flow->l4.tcp.tls.message.buffer_used, + flow->l4.tcp.tls.message.buffer[0], + flow->l4.tcp.tls.message.buffer[1], + flow->l4.tcp.tls.message.buffer[2], + flow->l4.tcp.tls.message.buffer[3], + flow->l4.tcp.tls.message.buffer[4]); #endif + break; + } - if(base_offset + 2 <= packet->payload_packet_len) { - u_int16_t session_id_len; - u_int16_t tls_version; +#ifdef DEBUG_TLS_MEMORY + printf("[TLS Mem] Processing %u bytes message\n", len); +#endif - 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])); + /* Overwriting packet payload */ + p = packet->payload, p_len = packet->payload_packet_len; /* Backup */ - session_id_len = packet->payload[base_offset]; + /* Split the element in blocks */ + u_int16_t processed = 5; + + while((processed+4) < len) { + const u_int8_t *block = (const u_int8_t *)&flow->l4.tcp.tls.message.buffer[processed]; + u_int16_t block_len = (block[1] << 16) + (block[2] << 8) + block[3]; - ja3.tls_handshake_version = tls_version; + packet->payload = block, packet->payload_packet_len = block_len+4; - if((session_id_len+base_offset+2) <= total_len) { - u_int16_t cipher_len, cipher_offset; + if((processed+packet->payload_packet_len) > len) + break; + +#ifdef DEBUG_TLS_MEMORY + printf("*** [TLS Mem] Processing %u bytes block [%02X %02X %02X %02X %02X]\n", + packet->payload_packet_len, + packet->payload[0], packet->payload[1], packet->payload[2], packet->payload[3], packet->payload[4]); +#endif - 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 DEBUG_TLS - printf("Client SSL [client cipher_len: %u][tls_version: 0x%04X]\n", cipher_len, tls_version); + processTLSBlock(ndpi_struct, flow); + processed += packet->payload_packet_len; + } + + packet->payload = p, packet->payload_packet_len = p_len; /* Restore */ + flow->l4.tcp.tls.message.buffer_used -= len; + + memmove(flow->l4.tcp.tls.message.buffer, + &flow->l4.tcp.tls.message.buffer[len], + flow->l4.tcp.tls.message.buffer_used); + +#ifdef DEBUG_TLS_MEMORY + printf("[TLS Mem] Left memory buffer %u bytes\n", flow->l4.tcp.tls.message.buffer_used); #endif + } + +#ifdef DEBUG_TLS_MEMORY + printf("[TLS Mem] Returning %u\n", rc); +#endif + + return(rc); +} - if((cipher_offset+cipher_len) <= total_len) { - for(i=0; i<cipher_len;) { - u_int16_t *id = (u_int16_t*)&packet->payload[cipher_offset+i]; +/* **************************************** */ +static int ndpi_search_tls_udp(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) { + struct ndpi_packet_struct *packet = &flow->packet; + u_int8_t handshake_type; + u_int32_t handshake_len; + u_int16_t p_len; + const u_int8_t *p; + #ifdef DEBUG_TLS - printf("Client SSL [cipher suite: %u/0x%04X] [%d/%u]\n", ntohs(*id), ntohs(*id), i, cipher_len); + printf("[TLS] %s()\n", __FUNCTION__); #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; + + /* Consider only specific SSL packets (handshake) */ + if((packet->payload_packet_len < 17) + || (packet->payload[0] != 0x16) + || (packet->payload[1] != 0xfe) /* We ignore old DTLS versions */ + || ((packet->payload[2] != 0xff) && (packet->payload[2] != 0xfd)) + || ((ntohs(*((u_int16_t*)&packet->payload[11]))+13) != packet->payload_packet_len) + ) { + no_dtls: + #ifdef DEBUG_TLS - printf("Client SSL Invalid cipher %u\n", ja3.num_cipher); + printf("[TLS] No DTLS found\n"); #endif - } - } + + NDPI_EXCLUDE_PROTO(ndpi_struct, flow); + return(0); /* Giveup */ + } - i += 2; - } - } else { - invalid_ja3 = 1; -#ifdef DEBUG_TLS - printf("Client SSL Invalid len %u vs %u\n", (cipher_offset+cipher_len), total_len); -#endif - } + handshake_type = packet->payload[13]; + handshake_len = (packet->payload[14] << 16) + (packet->payload[15] << 8) + packet->payload[16]; - offset = base_offset + session_id_len + cipher_len + 2; + if((handshake_len+25) != packet->payload_packet_len) + goto no_dtls; + + /* Overwriting packet payload */ + p = packet->payload, p_len = packet->payload_packet_len; /* Backup */ + packet->payload = &packet->payload[13], packet->payload_packet_len -= 13; - flow->l4.tcp.tls_seen_client_cert = 1; + processTLSBlock(ndpi_struct, flow); + + packet->payload = p, packet->payload_packet_len = p_len; /* Restore */ + + ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_TLS); + return(1); /* Keep working */ +} - 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++; +static void tlsInitExtraPacketProcessing(struct ndpi_flow_struct *flow) { + flow->check_extra_packets = 1; -#ifdef DEBUG_TLS - printf("Client SSL [compression_len: %u]\n", compression_len); + /* At most 12 packets should almost always be enough to find the server certificate if it's there */ + flow->max_extra_packets_to_check = 12; + flow->extra_packets_func = (flow->packet.udp != NULL) ? ndpi_search_tls_udp : ndpi_search_tls_tcp; +} + +/* **************************************** */ + +static void ndpi_int_tls_add_connection(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, u_int32_t protocol) { +#if DEBUG_TLS + printf("[TLS] %s()\n", __FUNCTION__); #endif - // offset += compression_len + 3; - offset += compression_len; + if((flow->detected_protocol_stack[0] == protocol) + || (flow->detected_protocol_stack[1] == protocol)) { + if(!flow->check_extra_packets) + tlsInitExtraPacketProcessing(flow); + return; + } + + if(protocol != NDPI_PROTOCOL_TLS) + ; + else + protocol = ndpi_tls_refine_master_protocol(ndpi_struct, flow, protocol); - if(offset < total_len) { - extensions_len = ntohs(*((u_int16_t*)&packet->payload[offset])); - offset += 2; + ndpi_set_detected_protocol(ndpi_struct, flow, protocol, NDPI_PROTOCOL_TLS); + tlsInitExtraPacketProcessing(flow); +} -#ifdef DEBUG_TLS - 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; +/* https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967 */ - while(extension_offset < extensions_len) { - u_int16_t extension_id, extension_len, extn_off = offset+extension_offset; +#define JA3_STR_LEN 1024 +#define MAX_NUM_JA3 128 - extension_id = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset])); - extension_offset += 2; +struct ja3_info { + u_int16_t tls_handshake_version; + u_int16_t num_cipher, cipher[MAX_NUM_JA3]; + u_int16_t num_tls_extension, tls_extension[MAX_NUM_JA3]; + u_int16_t num_elliptic_curve, elliptic_curve[MAX_NUM_JA3]; + u_int8_t num_elliptic_curve_point_format, elliptic_curve_point_format[MAX_NUM_JA3]; +}; - extension_len = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset])); - extension_offset += 2; +/* **************************************** */ +int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) { + struct ndpi_packet_struct *packet = &flow->packet; + struct ja3_info ja3; + u_int8_t invalid_ja3 = 0; + u_int16_t tls_version, ja3_str_len; + char ja3_str[JA3_STR_LEN]; + ndpi_MD5_CTX ctx; + u_char md5_hash[16]; + int i; + u_int16_t total_len; + u_int8_t handshake_type; + char buffer[64] = { '\0' }; + #ifdef DEBUG_TLS - printf("Client SSL [extension_id: %u][extension_len: %u]\n", extension_id, extension_len); + printf("SSL %s() called\n", __FUNCTION__); #endif - if((extension_id == 0) || (packet->payload[extn_off] != packet->payload[extn_off+1])) { - /* Skip GREASE */ + memset(&ja3, 0, sizeof(ja3)); - if(ja3.num_tls_extension < MAX_NUM_JA3) - ja3.tls_extension[ja3.num_tls_extension++] = extension_id; - else { - invalid_ja3 = 1; + handshake_type = packet->payload[0]; + total_len = (packet->payload[1] << 16) + (packet->payload[2] << 8) + packet->payload[3]; + + if(total_len > packet->payload_packet_len) + return(0); /* Not found */ + + 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 ? 38 : 46; + u_int16_t version_offset = packet->tcp ? 4 : 12; + u_int16_t offset = 38, extension_len, j; + u_int8_t session_id_len = packet->tcp ? packet->payload[offset] : packet->payload[46]; + #ifdef DEBUG_TLS - printf("Client SSL Invalid extensions %u\n", ja3.num_tls_extension); + printf("SSL [len: %u][handshake_type: %02X]\n", packet->payload_packet_len, handshake_type); #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); - - if((offset+extension_offset+5+len) < packet->payload_packet_len) { - 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; + tls_version = ntohs(*((u_int16_t*)&packet->payload[version_offset])); + flow->protos.stun_ssl.ssl.ssl_version = ja3.tls_handshake_version = tls_version; + + if(handshake_type == 0x02 /* Server Hello */) { + int i, rc; + #ifdef DEBUG_TLS - printf("Client SSL [EllipticCurveGroups: len=%u]\n", extension_len); + printf("SSL Server Hello [version: 0x%04X]\n", tls_version); #endif - if((s_offset+extension_len-2) <= total_len) { - for(i=0; i<extension_len-2;) { - u_int16_t s_group = ntohs(*((u_int16_t*)&packet->payload[s_offset+i])); + /* + 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 + */ + if(packet->udp) + offset += 1; + else { + if(tls_version < 0x7F15 /* TLS 1.3 lacks of session id */) + offset += session_id_len+1; + } -#ifdef DEBUG_TLS - 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 DEBUG_TLS - printf("Client SSL Invalid num elliptic %u\n", ja3.num_elliptic_curve); -#endif - } - } + 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]; - i += 2; - } - } else { - invalid_ja3 = 1; #ifdef DEBUG_TLS - printf("Client SSL Invalid len %u vs %u\n", (s_offset+extension_len-1), total_len); + printf("TLS [server][session_id_len: %u][cipher: %04X]\n", session_id_len, ja3.cipher[0]); #endif - } - } else if(extension_id == 11 /* ec_point_formats groups */) { - u_int16_t s_offset = offset+extension_offset + 1; -#ifdef DEBUG_TLS - printf("Client SSL [EllipticCurveFormat: len=%u]\n", extension_len); -#endif - if((s_offset+extension_len) < total_len) { - for(i=0; i<extension_len-1;i++) { - u_int8_t s_group = packet->payload[s_offset+i]; + offset += 2 + 1; -#ifdef DEBUG_TLS - printf("Client SSL [EllipticCurveFormat: %u]\n", s_group); -#endif + if((offset + 1) < packet->payload_packet_len) /* +1 because we are goint to read 2 bytes */ + extension_len = ntohs(*((u_int16_t*)&packet->payload[offset])); + else + extension_len = 0; - 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 DEBUG_TLS - printf("Client SSL Invalid num elliptic %u\n", ja3.num_elliptic_curve_point_format); + printf("TLS [server][extension_len: %u]\n", extension_len); #endif - } - } - } else { - invalid_ja3 = 1; -#ifdef DEBUG_TLS - printf("Client SSL Invalid len %u vs %u\n", s_offset+extension_len, total_len); -#endif - } - } else if(extension_id == 43 /* supported versions */) { - u_int8_t version_len = packet->payload[offset+4]; - - if(version_len == (extension_len-1)) { -#ifdef DEBUG_TLS - u_int8_t j; - - for(j=0; j<version_len; j += 2) { - u_int16_t tls_version = ntohs(*((u_int16_t*)&packet->payload[offset+5+j])); - - printf("Client SSL [TLS version: 0x%04X]\n", tls_version); - } -#endif - } - } + offset += 2; - extension_offset += extension_len; + for(i=0; i<extension_len; ) { + u_int16_t extension_id, extension_len; -#ifdef DEBUG_TLS - printf("Client SSL [extension_offset/len: %u/%u]\n", extension_offset, extension_len); -#endif - } /* while */ - - if(!invalid_ja3) { - int rc; - - compute_ja3c: - ja3_str_len = snprintf(ja3_str, sizeof(ja3_str), "%u,", ja3.tls_handshake_version); - - for(i=0; i<ja3.num_cipher; i++) { - rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", - (i > 0) ? "-" : "", ja3.cipher[i]); - if(rc > 0) ja3_str_len += rc; else break; - } + if(offset >= (packet->payload_packet_len+4)) break; - rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); - if(rc > 0) ja3_str_len += rc; - - /* ********** */ + extension_id = ntohs(*((u_int16_t*)&packet->payload[offset])); + extension_len = ntohs(*((u_int16_t*)&packet->payload[offset+2])); - for(i=0; i<ja3.num_tls_extension; i++) { - rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", - (i > 0) ? "-" : "", ja3.tls_extension[i]); - if(rc > 0) ja3_str_len += rc; else break; - } - - rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); - if(rc > 0) ja3_str_len += rc; - - /* ********** */ - - for(i=0; i<ja3.num_elliptic_curve; i++) { - rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", - (i > 0) ? "-" : "", ja3.elliptic_curve[i]); - if(rc > 0) ja3_str_len += rc; else break; - } - - rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); - if(rc > 0) ja3_str_len += rc; - - for(i=0; i<ja3.num_elliptic_curve_point_format; i++) { - rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", - (i > 0) ? "-" : "", ja3.elliptic_curve_point_format[i]); - if(rc > 0) ja3_str_len += rc; else break; - } + if(ja3.num_tls_extension < MAX_NUM_JA3) + ja3.tls_extension[ja3.num_tls_extension++] = extension_id; #ifdef DEBUG_TLS - printf("[JA3] Client: %s \n", ja3_str); + printf("TLS [server][extension_id: %u/0x%04X][len: %u]\n", + extension_id, extension_id, extension_len); #endif - ndpi_MD5Init(&ctx); - ndpi_MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str)); - ndpi_MD5Final(md5_hash, &ctx); + if(extension_id == 43 /* supported versions */) { + if(extension_len >= 2) { + u_int16_t tls_version = ntohs(*((u_int16_t*)&packet->payload[offset+4])); - for(i=0, j=0; i<16; i++) { - rc = snprintf(&flow->protos.stun_ssl.ssl.ja3_client[j], - sizeof(flow->protos.stun_ssl.ssl.ja3_client)-j, "%02x", - md5_hash[i]); - if(rc > 0) j += rc; else break; - } #ifdef DEBUG_TLS - printf("[JA3] Client: %s \n", flow->protos.stun_ssl.ssl.ja3_client); + printf("TLS [server] [TLS version: 0x%04X]\n", tls_version); #endif - } - return(2 /* Client Certificate */); - } - } else if(offset == total_len) { - /* SSL does not have extensions etc */ - goto compute_ja3c; - } - } + flow->protos.stun_ssl.ssl.ssl_version = tls_version; } } + + i += 4 + extension_len, offset += 4 + extension_len; } - } - } - return(0); /* Not found */ -} + ja3_str_len = snprintf(ja3_str, sizeof(ja3_str), "%u,", ja3.tls_handshake_version); -/* **************************************** */ + for(i=0; i<ja3.num_cipher; i++) { + rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", (i > 0) ? "-" : "", ja3.cipher[i]); -/* See https://blog.catchpoint.com/2017/05/12/dissecting-tls-using-wireshark/ */ -int getSSCertificateFingerprint(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow) { - struct ndpi_packet_struct *packet = &flow->packet; - u_int8_t multiple_messages; + if(rc <= 0) break; else ja3_str_len += rc; + } - if(flow->l4.tcp.tls_srv_cert_fingerprint_processed) - return(0); /* We're good */ - -#ifdef DEBUG_TLS - printf("=>> [TLS] %s() [tls_record_offset=%d][payload_packet_len=%u][direction: %u][%02X %02X %02X...]\n", - __FUNCTION__, flow->l4.tcp.tls_record_offset, packet->payload_packet_len, - packet->packet_direction, - packet->payload[0], packet->payload[1], packet->payload[2]); -#endif - - if((packet->packet_direction == 0) /* Client -> Server */ - || (packet->payload_packet_len == 0)) - return(1); /* More packets please */ - else if(flow->l4.tcp.tls_srv_cert_fingerprint_processed) - return(0); /* We're good */ - - if(packet->payload_packet_len <= flow->l4.tcp.tls_record_offset) { - /* Avoid invalid memory accesses */ - return(1); - } + rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); + if(rc > 0) ja3_str_len += rc; - if(flow->l4.tcp.tls_fingerprint_len > 0) { - unsigned int avail = packet->payload_packet_len - flow->l4.tcp.tls_record_offset; + /* ********** */ - if(avail > flow->l4.tcp.tls_fingerprint_len) - avail = flow->l4.tcp.tls_fingerprint_len; + for(i=0; i<ja3.num_tls_extension; i++) { + int rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", (i > 0) ? "-" : "", ja3.tls_extension[i]); -#ifdef DEBUG_TLS - printf("=>> [TLS] Certificate record [%02X %02X %02X...][missing: %u][offset: %u][avail: %u] (B)\n", - packet->payload[flow->l4.tcp.tls_record_offset], - packet->payload[flow->l4.tcp.tls_record_offset+1], - packet->payload[flow->l4.tcp.tls_record_offset+2], - flow->l4.tcp.tls_fingerprint_len, flow->l4.tcp.tls_record_offset, avail - ); -#endif - -#ifdef DEBUG_CERTIFICATE_HASH - for(i=0;i<avail;i++) - printf("%02X ", packet->payload[flow->l4.tcp.tls_record_offset+i]); - printf("\n"); -#endif - - SHA1Update(flow->l4.tcp.tls_srv_cert_fingerprint_ctx, - &packet->payload[flow->l4.tcp.tls_record_offset], - avail); - - flow->l4.tcp.tls_fingerprint_len -= avail; - - if(flow->l4.tcp.tls_fingerprint_len == 0) { - SHA1Final(flow->l4.tcp.tls_sha1_certificate_fingerprint, flow->l4.tcp.tls_srv_cert_fingerprint_ctx); + if(rc <= 0) break; else ja3_str_len += rc; + } #ifdef DEBUG_TLS - { - int i; - - printf("=>> [TLS] SHA-1: "); - for(i=0;i<20;i++) - printf("%s%02X", (i > 0) ? ":" : "", flow->l4.tcp.tls_sha1_certificate_fingerprint[i]); - printf("\n"); - } + printf("TLS [server] %s\n", ja3_str); #endif - - flow->l4.tcp.tls_srv_cert_fingerprint_processed = 1; - return(0); /* We're good */ - } else { - flow->l4.tcp.tls_record_offset = 0; + #ifdef DEBUG_TLS - printf("=>> [TLS] Certificate record: still missing %u bytes\n", flow->l4.tcp.tls_fingerprint_len); + printf("[JA3] Server: %s \n", ja3_str); #endif - return(1); /* More packets please */ - } - } - if(packet->payload[flow->l4.tcp.tls_record_offset] == 0x15 /* Alert */) { - u_int len = ntohs(*(u_int16_t*)&packet->payload[flow->l4.tcp.tls_record_offset+3]) + 5 /* SSL header len */; + ndpi_MD5Init(&ctx); + ndpi_MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str)); + ndpi_MD5Final(md5_hash, &ctx); - if(len < 10 /* Sanity check */) { - if((flow->l4.tcp.tls_record_offset+len) < packet->payload_packet_len) - flow->l4.tcp.tls_record_offset += len; - } else - goto invalid_len; - } - - multiple_messages = (packet->payload[flow->l4.tcp.tls_record_offset] == 0x16 /* Handshake */) ? 0 : 1; + for(i=0, j=0; i<16; i++) { + int rc = snprintf(&flow->protos.stun_ssl.ssl.ja3_server[j], + sizeof(flow->protos.stun_ssl.ssl.ja3_server)-j, "%02x", md5_hash[i]); + if(rc <= 0) break; else j += rc; + } #ifdef DEBUG_TLS - printf("=>> [TLS] [multiple_messages: %d]\n", multiple_messages); -#endif - - if((!multiple_messages) && (packet->payload[flow->l4.tcp.tls_record_offset] != 0x16 /* Handshake */)) - return(1); - else if(((!multiple_messages) && (packet->payload[flow->l4.tcp.tls_record_offset+5] == 0xb) /* Certificate */) - || (packet->payload[flow->l4.tcp.tls_record_offset] == 0xb) /* Certificate */) { - /* TODO: Do not take into account all certificates but only the first one */ + printf("[JA3] Server: %s \n", flow->protos.stun_ssl.ssl.ja3_server); +#endif + } else if(handshake_type == 0x01 /* Client Hello */) { + 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 DEBUG_TLS - printf("=>> [TLS] Certificate found\n"); + printf("Client SSL [client cipher_len: %u][tls_version: 0x%04X]\n", cipher_len, tls_version); #endif - if(flow->l4.tcp.tls_srv_cert_fingerprint_ctx == NULL) - flow->l4.tcp.tls_srv_cert_fingerprint_ctx = (void*)ndpi_malloc(sizeof(SHA1_CTX)); - else { -#ifdef DEBUG_TLS - printf("[TLS] Internal error: double allocation\n:"); -#endif - } - - if(flow->l4.tcp.tls_srv_cert_fingerprint_ctx) { - SHA1Init(flow->l4.tcp.tls_srv_cert_fingerprint_ctx); - flow->l4.tcp.tls_srv_cert_fingerprint_found = 1; - flow->l4.tcp.tls_record_offset += (!multiple_messages) ? 13 : 8; - flow->l4.tcp.tls_fingerprint_len = ntohs(*(u_int16_t*)&packet->payload[flow->l4.tcp.tls_record_offset]); - flow->l4.tcp.tls_record_offset = flow->l4.tcp.tls_record_offset+2; + if((cipher_offset+cipher_len) <= total_len) { + for(i=0; i<cipher_len;) { + u_int16_t *id = (u_int16_t*)&packet->payload[cipher_offset+i]; + #ifdef DEBUG_TLS - printf("=>> [TLS] Certificate [total certificate len: %u][certificate initial offset: %u]\n", - flow->l4.tcp.tls_fingerprint_len, flow->l4.tcp.tls_record_offset); + printf("Client SSL [cipher suite: %u/0x%04X] [%d/%u]\n", ntohs(*id), ntohs(*id), i, cipher_len); #endif - return(getSSCertificateFingerprint(ndpi_struct, flow)); - } else - return(0); /* That's all */ - } else if(flow->l4.tcp.tls_seen_certificate) - return(0); /* That's all */ - else if(packet->payload_packet_len > flow->l4.tcp.tls_record_offset+7+1/* +1 because we are going to read 2 bytes */) { - /* This is a handshake but not a certificate record */ - u_int16_t len = ntohs(*(u_int16_t*)&packet->payload[flow->l4.tcp.tls_record_offset+7]); - + 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 DEBUG_TLS - printf("=>> [TLS] Found record %02X [len: %u]\n", - packet->payload[flow->l4.tcp.tls_record_offset+5], len); + printf("Client SSL Invalid cipher %u\n", ja3.num_cipher); #endif + } + } - if(len > 4096) { - invalid_len: - /* This looks an invalid len: we giveup */ - flow->l4.tcp.tls_record_offset = 0, flow->l4.tcp.tls_srv_cert_fingerprint_processed = 1; + i += 2; + } + } else { + invalid_ja3 = 1; #ifdef DEBUG_TLS - printf("=>> [TLS] Invalid fingerprint processing %u <-> %u\n", - ntohs(packet->tcp->source), ntohs(packet->tcp->dest)); + printf("Client SSL Invalid len %u vs %u\n", (cipher_offset+cipher_len), total_len); #endif - return(0); - } else { - flow->l4.tcp.tls_record_offset += len + 9; - - if(flow->l4.tcp.tls_record_offset < packet->payload_packet_len) - return(getSSCertificateFingerprint(ndpi_struct, flow)); - else { - flow->l4.tcp.tls_record_offset -= packet->payload_packet_len; } - } - } - - flow->extra_packets_func = NULL; /* We're good now */ - return(1); -} -/* **************************************** */ + offset = base_offset + session_id_len + cipher_len + 2; -/* See https://blog.catchpoint.com/2017/05/12/dissecting-tls-using-wireshark/ */ -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; - u_int16_t total_len; - u_int8_t handshake_protocol; - - if(packet->payload[0] != 0x16 /* Handshake */) - return; + if(offset < total_len) { + u_int16_t compression_len; + u_int16_t extensions_len; - total_len = (packet->payload[3] << 8) + packet->payload[4] + 5 /* SSL Header */; - 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; + offset += packet->tcp ? 1 : 2; + compression_len = packet->payload[offset]; + offset++; #ifdef DEBUG_TLS - printf("=>> [TLS] Certificate [total_len: %u/%u]\n", ntohs(*(u_int16_t*)&packet->payload[3]), total_len); + printf("Client SSL [compression_len: %u]\n", compression_len); #endif - - /* 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); + // offset += compression_len + 3; + offset += compression_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]; + if(offset < total_len) { + extensions_len = ntohs(*((u_int16_t*)&packet->payload[offset])); + offset += 2; - num_found++; - /* what we want is subject certificate, so we bypass the issuer certificate */ - if(num_found != 2) continue; +#ifdef DEBUG_TLS + printf("Client SSL [extensions_len: %u]\n", extensions_len); +#endif - // packet is truncated... further inspection is not needed - if(i+4+server_len >= packet->payload_packet_len) { - break; - } + 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; - char *server_org = (char*)&packet->payload[i+5]; + while(extension_offset < extensions_len) { + u_int16_t extension_id, extension_len, extn_off = offset+extension_offset; - u_int len = (u_int)ndpi_min(server_len, buffer_len-1); - strncpy(buffer, server_org, len); - buffer[len] = '\0'; + extension_id = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset])); + extension_offset += 2; - // 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; - } - } + extension_len = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset])); + extension_offset += 2; - if(is_printable == 1) { - snprintf(flow->protos.stun_ssl.ssl.server_organization, - sizeof(flow->protos.stun_ssl.ssl.server_organization), "%s", buffer); #ifdef DEBUG_TLS - printf("Certificate organization: %s\n", flow->protos.stun_ssl.ssl.server_organization); + printf("Client SSL [extension_id: %u][extension_len: %u]\n", extension_id, extension_len); #endif - } - } else if((packet->payload[i] == 0x30) && (packet->payload[i+1] == 0x1e) && (packet->payload[i+2] == 0x17)) { - u_int8_t len = packet->payload[i+3]; - u_int offset = i+4; - if((offset+len) < packet->payload_packet_len) { - char utcDate[32]; + 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 DEBUG_TLS - printf("[CERTIFICATE] notBefore [len: %u][", len); - for(j=0; j<len; j++) printf("%c", packet->payload[i+4+j]); - printf("]\n"); + printf("Client SSL Invalid extensions %u\n", ja3.num_tls_extension); #endif + } + } - if(len < (sizeof(utcDate)-1)) { - struct tm utc; - utc.tm_isdst = -1; /* Not set by strptime */ - - strncpy(utcDate, (const char*)&packet->payload[i+4], len); - utcDate[len] = '\0'; + if(extension_id == 0 /* server name */) { + u_int16_t len; - /* 141021000000Z */ - if(strptime(utcDate, "%y%m%d%H%M%SZ", &utc) != NULL) { - flow->protos.stun_ssl.ssl.notBefore = timegm(&utc); #ifdef DEBUG_TLS - printf("[CERTIFICATE] notBefore %u [%s]\n", - flow->protos.stun_ssl.ssl.notBefore, utcDate); + printf("[TLS] Extensions: found server name\n"); #endif - } - } - offset += len; + len = (packet->payload[offset+extension_offset+3] << 8) + packet->payload[offset+extension_offset+4]; + len = (u_int)ndpi_min(len, sizeof(buffer)-1); - if((offset+1) < packet->payload_packet_len) { - len = packet->payload[offset+1]; + if((offset+extension_offset+5+len) < packet->payload_packet_len) { + strncpy(buffer, (char*)&packet->payload[offset+extension_offset+5], len); + buffer[len] = '\0'; - offset += 2; + cleanupServerName(buffer, sizeof(buffer)); - if((offset+len) < packet->payload_packet_len) { + snprintf(flow->protos.stun_ssl.ssl.client_requested_server_name, + sizeof(flow->protos.stun_ssl.ssl.client_requested_server_name), + "%s", buffer); + + if(ndpi_match_hostname_protocol(ndpi_struct, flow, NDPI_PROTOCOL_TLS, buffer, strlen(buffer))) + flow->l4.tcp.tls.subprotocol_detected = 1; + } else { #ifdef DEBUG_TLS - printf("[CERTIFICATE] notAfter [len: %u][", len); - for(j=0; j<len; j++) printf("%c", packet->payload[offset+j]); - printf("]\n"); + printf("[TLS] Extensions server len too short: %u vs %u\n", + offset+extension_offset+5+len, + packet->payload_packet_len); #endif + } + } else if(extension_id == 10 /* supported groups */) { + u_int16_t s_offset = offset+extension_offset + 2; - if(len < (sizeof(utcDate)-1)) { - struct tm utc; - utc.tm_isdst = -1; /* Not set by strptime */ - - strncpy(utcDate, (const char*)&packet->payload[offset], len); - utcDate[len] = '\0'; - - /* 141021000000Z */ - if(strptime(utcDate, "%y%m%d%H%M%SZ", &utc) != NULL) { - flow->protos.stun_ssl.ssl.notAfter = timegm(&utc); #ifdef DEBUG_TLS - printf("[CERTIFICATE] notAfter %u [%s]\n", - flow->protos.stun_ssl.ssl.notAfter, utcDate); + printf("Client SSL [EllipticCurveGroups: len=%u]\n", extension_len); #endif - } - } - } - } - } - } - } -} - -/* **************************************** */ - -int sslTryAndRetrieveServerCertificate(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow) { - struct ndpi_packet_struct *packet = &flow->packet; - int rc = 1; - - if(packet->tcp) { - if(!flow->l4.tcp.tls_srv_cert_fingerprint_processed) - getSSCertificateFingerprint(ndpi_struct, flow); - } - -#if 1 - /* 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]; + if((s_offset+extension_len-2) <= total_len) { + for(i=0; i<extension_len-2;) { + u_int16_t s_group = ntohs(*((u_int16_t*)&packet->payload[s_offset+i])); - // try fetch server organization once server certificate is found - organization[0] = '\0'; - getSSLorganization(ndpi_struct, flow, organization, sizeof(organization)); - - packet->tls_certificate_detected++; -#if 0 - if((flow->l4.tcp.tls_seen_server_cert == 1) - && (flow->protos.stun_ssl.ssl.server_certificate[0] != '\0')) - /* 0 means we've done processing extra packets (since we found what we wanted) */ - return 0; +#ifdef DEBUG_TLS + printf("Client SSL [EllipticCurve: %u/0x%04X]\n", s_group, s_group); #endif - } - - if(flow->l4.tcp.tls_record_offset == 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 */ - && flow->l4.tcp.tls_srv_cert_fingerprint_processed) - /* || (flow->protos.stun_ssl.ssl.ja3_server[0] != '\0') */ - ) { - /* We're done processing extra packets since we've probably checked all possible cert packets */ - return(rc); - } - } - } + 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 DEBUG_TLS + printf("Client SSL Invalid num elliptic %u\n", ja3.num_elliptic_curve); #endif - - /* 1 means keep looking for more packets */ - if(!flow->l4.tcp.tls_srv_cert_fingerprint_processed) rc = 1; - return(rc); -} - -/* **************************************** */ - -int tlsDetectProtocolFromCertificate(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow, - u_int8_t skip_cert_processing) { - struct ndpi_packet_struct *packet = &flow->packet; - - if((!skip_cert_processing) && packet->tcp) { - if(!flow->l4.tcp.tls_srv_cert_fingerprint_processed) - getSSCertificateFingerprint(ndpi_struct, flow); - } - - 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++; + i += 2; + } + } else { + invalid_ja3 = 1; #ifdef DEBUG_TLS - NDPI_LOG_DBG2(ndpi_struct, "***** [SSL] %s\n", certificate); + printf("Client SSL Invalid len %u vs %u\n", (s_offset+extension_len-1), total_len); #endif - ndpi_protocol_match_result ret_match; - u_int16_t subproto; - - if(certificate[0] == '\0') - subproto = NDPI_PROTOCOL_UNKNOWN; - else - 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_ssl.ssl.client_certificate[0] != '\0')) - && ((flow->l4.tcp.tls_seen_server_cert != 1) && (flow->protos.stun_ssl.ssl.server_certificate[0] == '\0'))) { - sslInitExtraPacketProcessing(flow); - } - - ndpi_set_detected_protocol(ndpi_struct, flow, subproto, - ndpi_tls_refine_master_protocol(ndpi_struct, flow, NDPI_PROTOCOL_TLS)); - return(rc); - } - - if(ndpi_is_tls_tor(ndpi_struct, flow, certificate) != 0) - return(rc); - } + } + } else if(extension_id == 11 /* ec_point_formats groups */) { + u_int16_t s_offset = offset+extension_offset + 1; #ifdef DEBUG_TLS - printf("[TLS] %s() [tls_certificate_num_checks: %u][tls_srv_cert_fingerprint_processed: %u][tls_certificate_detected: %u][%u/%u]", - __FUNCTION__, packet->tls_certificate_num_checks, flow->l4.tcp.tls_srv_cert_fingerprint_processed, - packet->tls_certificate_detected, - flow->l4.tcp.tls_seen_client_cert, - flow->l4.tcp.tls_seen_server_cert - ); + printf("Client SSL [EllipticCurveFormat: len=%u]\n", extension_len); #endif + if((s_offset+extension_len) < total_len) { + for(i=0; i<extension_len-1;i++) { + u_int8_t s_group = packet->payload[s_offset+i]; - - if(((packet->tls_certificate_num_checks >= 1) -#if 0 - && (flow->l4.tcp.seen_syn /* User || to be tolerant */ - || flow->l4.tcp.seen_syn_ack - || flow->l4.tcp.seen_ack /* We have seen the 3-way handshake */) +#ifdef DEBUG_TLS + printf("Client SSL [EllipticCurveFormat: %u]\n", s_group); #endif - && (flow->l4.tcp.tls_srv_cert_fingerprint_processed - || flow->l4.tcp.tls_seen_client_cert - || flow->l4.tcp.tls_seen_server_cert - || packet->tls_certificate_detected) - ) - /* - || ((flow->l4.tcp.tls_seen_certificate == 1) - && (flow->l4.tcp.tls_seen_server_cert == 1) - && (flow->protos.stun_ssl.ssl.server_certificate[0] != '\0')) - */ - /* || ((flow->l4.tcp.tls_seen_client_cert == 1) && (flow->protos.stun_ssl.ssl.client_certificate[0] != '\0')) */ - ) { - ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_TLS); - } - } - } - - return(0); -} - -/* **************************************** */ - -static void tls_mark_and_payload_search(struct ndpi_detection_module_struct - *ndpi_struct, struct ndpi_flow_struct *flow, - u_int8_t skip_cert_processing) { - struct ndpi_packet_struct *packet = &flow->packet; - u_int32_t a; - u_int32_t end; + 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 DEBUG_TLS - printf("[TLS] %s()\n", __FUNCTION__); + printf("Client SSL Invalid num elliptic %u\n", ja3.num_elliptic_curve_point_format); #endif - - 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; - } - } - } + } + } + } else { + invalid_ja3 = 1; +#ifdef DEBUG_TLS + printf("Client SSL Invalid len %u vs %u\n", s_offset+extension_len, total_len); +#endif + } + } else if(extension_id == 43 /* supported versions */) { + u_int8_t version_len = packet->payload[offset+4]; - 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; - } + if(version_len == (extension_len-1)) { +#ifdef DEBUG_TLS + u_int8_t j; - ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_OSCAR); - return; - } - } + for(j=0; j<version_len; j += 2) { + u_int16_t tls_version = ntohs(*((u_int16_t*)&packet->payload[offset+5+j])); - 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; - } - } - } + printf("Client SSL [TLS version: 0x%04X]\n", tls_version); + } +#endif + } + } - 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, skip_cert_processing); + extension_offset += extension_len; #ifdef DEBUG_TLS - printf("[TLS] %s() [tls_seen_client_cert: %u][tls_seen_server_cert: %u]\n", __FUNCTION__, - flow->l4.tcp.tls_seen_client_cert, flow->l4.tcp.tls_seen_server_cert); + printf("Client SSL [extension_offset/len: %u/%u]\n", extension_offset, extension_len); #endif + } /* while */ - 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); - } else if((packet->tls_certificate_num_checks >= 3) - && flow->l4.tcp.tls_srv_cert_fingerprint_processed) { - NDPI_LOG_INFO(ndpi_struct, "found ssl\n"); - ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_TLS); - } - } -} + if(!invalid_ja3) { + int rc; -/* **************************************** */ + compute_ja3c: + ja3_str_len = snprintf(ja3_str, sizeof(ja3_str), "%u,", ja3.tls_handshake_version); -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; + for(i=0; i<ja3.num_cipher; i++) { + rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", + (i > 0) ? "-" : "", ja3.cipher[i]); + if(rc > 0) ja3_str_len += rc; else break; + } - 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; - } + rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); + if(rc > 0) ja3_str_len += rc; - 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"); + for(i=0; i<ja3.num_tls_extension; i++) { + rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", + (i > 0) ? "-" : "", ja3.tls_extension[i]); + if(rc > 0) ja3_str_len += rc; else break; + } - /* 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); + rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); + if(rc > 0) ja3_str_len += rc; - 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; + for(i=0; i<ja3.num_elliptic_curve; i++) { + rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", + (i > 0) ? "-" : "", ja3.elliptic_curve[i]); + if(rc > 0) ja3_str_len += rc; else break; + } - NDPI_LOG_DBG2(ndpi_struct, - "maybe SSLv3 server hello split into smaller packets but with seperate record for the certificate\n"); + rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ","); + if(rc > 0) ja3_str_len += rc; - /* 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); + for(i=0; i<ja3.num_elliptic_curve_point_format; i++) { + rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", + (i > 0) ? "-" : "", ja3.elliptic_curve_point_format[i]); + if(rc > 0) ja3_str_len += rc; else break; + } - 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; - } - } +#ifdef DEBUG_TLS + printf("[JA3] Client: %s \n", ja3_str); +#endif + ndpi_MD5Init(&ctx); + ndpi_MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str)); + ndpi_MD5Final(md5_hash, &ctx); - 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; + for(i=0, j=0; i<16; i++) { + rc = snprintf(&flow->protos.stun_ssl.ssl.ja3_client[j], + sizeof(flow->protos.stun_ssl.ssl.ja3_client)-j, "%02x", + md5_hash[i]); + if(rc > 0) j += rc; else break; + } +#ifdef DEBUG_TLS + printf("[JA3] Client: %s \n", flow->protos.stun_ssl.ssl.ja3_client); +#endif + } + + return(2 /* Client Certificate */); + } else { +#ifdef DEBUG_TLS + printf("[TLS] Client: too short [%u vs %u]\n", + (extensions_len+offset), total_len); +#endif } + } else if(offset == total_len) { + /* SSL does not have extensions etc */ + goto compute_ja3c; } + } else { +#ifdef DEBUG_TLS + printf("[JA3] Client: invalid length detected\n"); +#endif } } } - - return 0; + + return(0); /* Not found */ } /* **************************************** */ -void ndpi_search_tls_tcp_udp(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow) { +static 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, skip_cert_processing = 0; #ifdef DEBUG_TLS - printf("==>> %u [len: %u][version: %u]\n", + printf("==>> %s() %u [len: %u][version: %u]\n", + __FUNCTION__, flow->guessed_host_protocol_id, packet->payload_packet_len, flow->protos.stun_ssl.ssl.ssl_version); #endif - - if(packet->udp != NULL) { - /* DTLS dissector */ - int rc = sslTryAndRetrieveServerCertificate(ndpi_struct, flow); - - if((rc == 0) && (flow->protos.stun_ssl.ssl.ssl_version != 0)) { - flow->guessed_protocol_id = NDPI_PROTOCOL_TLS; - - if(flow->protos.stun_ssl.stun.num_udp_pkts > 0) { - if(ndpi_struct->stun_cache == NULL) - ndpi_struct->stun_cache = ndpi_lru_cache_init(1024); - - if(ndpi_struct->stun_cache) { -#ifdef DEBUG_TLS - printf("[LRU] Adding Signal cached keys\n"); -#endif - - ndpi_lru_add_to_cache(ndpi_struct->stun_cache, get_stun_lru_key(flow, 0), NDPI_PROTOCOL_SIGNAL); - ndpi_lru_add_to_cache(ndpi_struct->stun_cache, get_stun_lru_key(flow, 1), NDPI_PROTOCOL_SIGNAL); - } - - /* In Signal protocol STUN turns into DTLS... */ - ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_SIGNAL); - } else if(flow->protos.stun_ssl.ssl.ja3_server[0] != '\0') { - /* Wait the server certificate the bless this flow as TLS */ - 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(ndpi_struct, flow, skip_cert_processing); - - 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, skip_cert_processing) > 0) - return; - else - skip_cert_processing = 1; - } - - 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(ndpi_struct, flow, skip_cert_processing); - 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(ndpi_struct, flow, skip_cert_processing); - 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(ndpi_struct, flow, skip_cert_processing); - - 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; + if(packet->udp != NULL) + ndpi_search_tls_udp(ndpi_struct, flow); + else + ndpi_search_tls_tcp(ndpi_struct, flow); } /* **************************************** */ diff --git a/src/lib/protocols/ubntac2.c b/src/lib/protocols/ubntac2.c index 6fc004228..49a63ed0a 100644 --- a/src/lib/protocols/ubntac2.c +++ b/src/lib/protocols/ubntac2.c @@ -64,11 +64,9 @@ void ndpi_search_ubntac2(struct ndpi_detection_module_struct *ndpi_struct, struc version[j] = '\0'; - if(!ndpi_struct->disable_metadata_export) { - len = ndpi_min(sizeof(flow->protos.ubntac2.version)-1, j); - strncpy(flow->protos.ubntac2.version, (const char *)version, len); - flow->protos.ubntac2.version[len] = '\0'; - } + len = ndpi_min(sizeof(flow->protos.ubntac2.version)-1, j); + strncpy(flow->protos.ubntac2.version, (const char *)version, len); + flow->protos.ubntac2.version[len] = '\0'; } NDPI_LOG_INFO(ndpi_struct, "UBNT AirControl 2 request\n"); diff --git a/src/lib/protocols/whoisdas.c b/src/lib/protocols/whoisdas.c index 9a65656a8..6ccac8c77 100644 --- a/src/lib/protocols/whoisdas.c +++ b/src/lib/protocols/whoisdas.c @@ -40,15 +40,13 @@ void ndpi_search_whois_das(struct ndpi_detection_module_struct *ndpi_struct, str u_int max_len = sizeof(flow->host_server_name) - 1; u_int i, j; - if(!ndpi_struct->disable_metadata_export) { - for(i=strlen((const char *)flow->host_server_name), j=0; (i<max_len) && (j<packet->payload_packet_len); i++, j++) { - if((packet->payload[j] == '\n') || (packet->payload[j] == '\r')) break; - flow->host_server_name[i] = packet->payload[j]; - } - - flow->host_server_name[i] = '\0'; + for(i=strlen((const char *)flow->host_server_name), j=0; (i<max_len) && (j<packet->payload_packet_len); i++, j++) { + if((packet->payload[j] == '\n') || (packet->payload[j] == '\r')) break; + flow->host_server_name[i] = packet->payload[j]; } + flow->host_server_name[i] = '\0'; + flow->server_id = ((sport == 43) || (sport == 4343)) ? flow->src : flow->dst; NDPI_LOG_INFO(ndpi_struct, "[WHOIS/DAS] %s\n", flow->host_server_name); |