diff options
Diffstat (limited to 'src/lib/protocols/tls.c')
-rw-r--r-- | src/lib/protocols/tls.c | 584 |
1 files changed, 346 insertions, 238 deletions
diff --git a/src/lib/protocols/tls.c b/src/lib/protocols/tls.c index 88c6fddde..e8444a731 100644 --- a/src/lib/protocols/tls.c +++ b/src/lib/protocols/tls.c @@ -29,7 +29,7 @@ #include "ndpi_encryption.h" #include "ndpi_private.h" -//#define JA4R_DECIMAL 1 +//#define JA4R_DECIMAL 1 static void ndpi_search_tls_wrapper(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow); @@ -107,6 +107,19 @@ static void ndpi_int_tls_add_connection(struct ndpi_detection_module_struct *ndp /* **************************************** */ +static bool str_contains_digit(char *str) { + u_int i = 0; + + for(i=0; (str[i] != '.') && (str[i] != '\0'); i++) { + if(isdigit(str[i])) + return(true); + } + + return(false); +} + +/* **************************************** */ + static u_int32_t ndpi_tls_refine_master_protocol(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { struct ndpi_packet_struct *packet = &ndpi_struct->packet; @@ -152,6 +165,50 @@ static u_int32_t __get_master(struct ndpi_detection_module_struct *ndpi_struct, /* **************************************** */ +/* TODO: rename */ +static int keep_extra_dissection_tcp(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) +{ + /* Common path: found handshake on both directions */ + if(flow->tls_quic.certificate_processed == 1 && flow->protos.tls_quic.client_hello_processed) + return 0; + /* Application Data on both directions: handshake already ended (did we miss it?) */ + if(flow->l4.tcp.tls.app_data_seen[0] == 1 && flow->l4.tcp.tls.app_data_seen[1] == 1) + return 0; + /* Handshake on one direction and Application Data on the other */ + if((flow->protos.tls_quic.client_hello_processed && flow->l4.tcp.tls.app_data_seen[!flow->protos.tls_quic.ch_direction] == 1) || + (flow->protos.tls_quic.server_hello_processed && flow->l4.tcp.tls.app_data_seen[flow->protos.tls_quic.ch_direction] == 1)) + return 0; + + /* Are we interested only in the (sub)-classification? */ + + if(/* Subclassification */ + flow->detected_protocol_stack[1] != NDPI_PROTOCOL_UNKNOWN && + /* No metadata from SH or certificate */ + !ndpi_struct->cfg.tls_alpn_negotiated_enabled && + !ndpi_struct->cfg.tls_cipher_enabled && + !ndpi_struct->cfg.tls_sha1_fingerprint_enabled && + !ndpi_struct->cfg.tls_cert_server_names_enabled && + !ndpi_struct->cfg.tls_cert_validity_enabled && + !ndpi_struct->cfg.tls_cert_issuer_enabled && + !ndpi_struct->cfg.tls_cert_subject_enabled && + !ndpi_struct->cfg.tls_broswer_enabled && + !ndpi_struct->cfg.tls_ja3s_fingerprint_enabled && + /* No flow risks from SH or certificate: we should have disabled all + metadata needed for flow risks, so we should not need to explicitly + check them */ + /* Ookla aggressiveness has no impact here because it is evaluated only + without sub-classification */ + /* TLS heuristics */ + (ndpi_struct->cfg.tls_heuristics == 0 || is_flow_addr_informative(flow))) + return 0; + + return 1; +} + + +/* **************************************** */ + /* Heuristic to detect proxied/obfuscated TLS flows, based on https://www.usenix.org/conference/usenixsecurity24/presentation/xue-fingerprinting. Main differences between the paper and our implementation: @@ -381,19 +438,6 @@ static int tls_obfuscated_heur_search(struct ndpi_detection_module_struct* ndpi_ if(check_set(ndpi_struct, set)) { /* Heuristic match */ - /* Export the matching set as metadata */ - flow->tls_quic.obfuscated_heur_matching_set = ndpi_calloc(1, sizeof(struct ndpi_tls_obfuscated_heuristic_matching_set)); - if(flow->tls_quic.obfuscated_heur_matching_set) { - flow->tls_quic.obfuscated_heur_matching_set->bytes[0] = set->bytes[0]; - flow->tls_quic.obfuscated_heur_matching_set->bytes[1] = set->bytes[1]; - flow->tls_quic.obfuscated_heur_matching_set->bytes[2] = set->bytes[2]; - flow->tls_quic.obfuscated_heur_matching_set->bytes[3] = set->bytes[3]; - flow->tls_quic.obfuscated_heur_matching_set->pkts[0] = set->pkts[0]; - flow->tls_quic.obfuscated_heur_matching_set->pkts[1] = set->pkts[1]; - flow->tls_quic.obfuscated_heur_matching_set->pkts[2] = set->pkts[2]; - flow->tls_quic.obfuscated_heur_matching_set->pkts[3] = set->pkts[3]; - } - return 2; /* Found */ } else { /* Close this set and open a new one... */ @@ -448,8 +492,8 @@ static int tls_obfuscated_heur_search_again(struct ndpi_detection_module_struct* ndpi_protocol ret = { { __get_master(ndpi_struct, flow), NDPI_PROTOCOL_UNKNOWN }, NDPI_PROTOCOL_UNKNOWN /* unused */, NDPI_PROTOCOL_CATEGORY_UNSPECIFIED, NULL}; flow->category = ndpi_get_proto_category(ndpi_struct, ret); } - NDPI_EXCLUDE_PROTO(ndpi_struct, flow); /* Not necessary in extra-dissection data path, - but we need it with the plain heuristic */ + NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); /* Not necessary in extra-dissection data path, + but we need it with the plain heuristic */ return 0; /* Stop */ } @@ -677,7 +721,7 @@ static void checkTLSSubprotocol(struct ndpi_detection_module_struct *ndpi_struct ndpi_set_detected_protocol(ndpi_struct, flow, cached_proto, __get_master(ndpi_struct, flow), NDPI_CONFIDENCE_DPI_CACHE); flow->category = ndpi_get_proto_category(ndpi_struct, ret); ndpi_check_subprotocol_risk(ndpi_struct, flow, cached_proto); - ndpi_unset_risk(flow, NDPI_NUMERIC_IP_HOST); + ndpi_unset_risk(ndpi_struct, flow, NDPI_NUMERIC_IP_HOST); } } } @@ -767,13 +811,17 @@ void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct printf("[TLS] %s() IssuerDN [%s]\n", __FUNCTION__, rdnSeqBuf); #endif - if(rdn_len && (flow->protos.tls_quic.issuerDN == NULL)) { + if(rdn_len && (flow->protos.tls_quic.issuerDN == NULL) && + ndpi_struct->cfg.tls_cert_issuer_enabled) { flow->protos.tls_quic.issuerDN = ndpi_strdup(rdnSeqBuf); if(ndpi_normalize_printable_string(rdnSeqBuf, rdn_len) == 0) { - char str[64]; - - snprintf(str, sizeof(str), "Invalid issuerDN %s", flow->protos.tls_quic.issuerDN); - ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, str); + if(is_flowrisk_info_enabled(ndpi_struct, NDPI_INVALID_CHARACTERS)) { + char str[64]; + snprintf(str, sizeof(str), "Invalid issuerDN %s", flow->protos.tls_quic.issuerDN); + ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, str); + } else { + ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, NULL); + } } } @@ -781,7 +829,8 @@ void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct } if(i + 3 < certificate_len && - (offset+packet->payload[i+3]) < packet->payload_packet_len) { + (offset+packet->payload[i+3]) < packet->payload_packet_len && + ndpi_struct->cfg.tls_cert_validity_enabled) { char utcDate[32]; u_int8_t len = packet->payload[i+3]; @@ -846,41 +895,53 @@ void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct if(flow->protos.tls_quic.notBefore > TLS_LIMIT_DATE) if((flow->protos.tls_quic.notAfter-flow->protos.tls_quic.notBefore) > TLS_THRESHOLD) { - char str[64]; + if(is_flowrisk_info_enabled(ndpi_struct, NDPI_TLS_CERT_VALIDITY_TOO_LONG)) { + char str[64]; - snprintf(str, sizeof(str), "TLS Cert lasts %u days", - (flow->protos.tls_quic.notAfter-flow->protos.tls_quic.notBefore) / 86400); + snprintf(str, sizeof(str), "TLS Cert lasts %u days", + (flow->protos.tls_quic.notAfter-flow->protos.tls_quic.notBefore) / 86400); - ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERT_VALIDITY_TOO_LONG, str); /* Certificate validity longer than 13 months */ + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERT_VALIDITY_TOO_LONG, str); /* Certificate validity longer than 13 months */ + } else { + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERT_VALIDITY_TOO_LONG, NULL); + } } if((time_sec < flow->protos.tls_quic.notBefore) || (time_sec > flow->protos.tls_quic.notAfter)) { - char str[96], b[32], e[32]; - struct tm result; - time_t theTime; + if(is_flowrisk_info_enabled(ndpi_struct, NDPI_TLS_CERTIFICATE_EXPIRED)) { + char str[96], b[32], e[32]; + struct tm result; + time_t theTime; - theTime = flow->protos.tls_quic.notBefore; - strftime(b, sizeof(b), "%d/%b/%Y %H:%M:%S", ndpi_gmtime_r(&theTime, &result)); + theTime = flow->protos.tls_quic.notBefore; + strftime(b, sizeof(b), "%d/%b/%Y %H:%M:%S", ndpi_gmtime_r(&theTime, &result)); - theTime = flow->protos.tls_quic.notAfter; - strftime(e, sizeof(e), "%d/%b/%Y %H:%M:%S", ndpi_gmtime_r(&theTime, &result)); + theTime = flow->protos.tls_quic.notAfter; + strftime(e, sizeof(e), "%d/%b/%Y %H:%M:%S", ndpi_gmtime_r(&theTime, &result)); - snprintf(str, sizeof(str), "%s - %s", b, e); - ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERTIFICATE_EXPIRED, str); /* Certificate expired */ + snprintf(str, sizeof(str), "%s - %s", b, e); + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERTIFICATE_EXPIRED, str); /* Certificate expired */ + } else { + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERTIFICATE_EXPIRED, NULL); + } } else if((time_sec > flow->protos.tls_quic.notBefore) && (time_sec > (flow->protos.tls_quic.notAfter - (ndpi_struct->cfg.tls_certificate_expire_in_x_days * 86400)))) { - char str[96], b[32], e[32]; - struct tm result; - time_t theTime; + if(is_flowrisk_info_enabled(ndpi_struct, NDPI_TLS_CERTIFICATE_ABOUT_TO_EXPIRE)) { + char str[96], b[32], e[32]; + struct tm result; + time_t theTime; - theTime = flow->protos.tls_quic.notBefore; - strftime(b, sizeof(b), "%d/%b/%Y %H:%M:%S", ndpi_gmtime_r(&theTime, &result)); + theTime = flow->protos.tls_quic.notBefore; + strftime(b, sizeof(b), "%d/%b/%Y %H:%M:%S", ndpi_gmtime_r(&theTime, &result)); - theTime = flow->protos.tls_quic.notAfter; - strftime(e, sizeof(e), "%d/%b/%Y %H:%M:%S", ndpi_gmtime_r(&theTime, &result)); + theTime = flow->protos.tls_quic.notAfter; + strftime(e, sizeof(e), "%d/%b/%Y %H:%M:%S", ndpi_gmtime_r(&theTime, &result)); - snprintf(str, sizeof(str), "%s - %s", b, e); - ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERTIFICATE_ABOUT_TO_EXPIRE, str); /* Certificate almost expired */ + snprintf(str, sizeof(str), "%s - %s", b, e); + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERTIFICATE_ABOUT_TO_EXPIRE, str); /* Certificate almost expired */ + } else { + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERTIFICATE_ABOUT_TO_EXPIRE, NULL); + } } } } @@ -992,29 +1053,32 @@ void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct } } - if(flow->protos.tls_quic.server_names == NULL) - flow->protos.tls_quic.server_names = ndpi_strdup(dNSName), - flow->protos.tls_quic.server_names_len = strlen(dNSName); - else if((u_int16_t)(flow->protos.tls_quic.server_names_len + dNSName_len + 1) > flow->protos.tls_quic.server_names_len) { - u_int16_t newstr_len = flow->protos.tls_quic.server_names_len + dNSName_len + 1; - char *newstr = (char*)ndpi_realloc(flow->protos.tls_quic.server_names, - flow->protos.tls_quic.server_names_len+1, newstr_len+1); - - if(newstr) { - flow->protos.tls_quic.server_names = newstr; - flow->protos.tls_quic.server_names[flow->protos.tls_quic.server_names_len] = ','; - strncpy(&flow->protos.tls_quic.server_names[flow->protos.tls_quic.server_names_len+1], - dNSName, dNSName_len+1); - flow->protos.tls_quic.server_names[newstr_len] = '\0'; - flow->protos.tls_quic.server_names_len = newstr_len; - } + if(ndpi_struct->cfg.tls_cert_server_names_enabled) { + if(flow->protos.tls_quic.server_names == NULL) { + flow->protos.tls_quic.server_names = ndpi_strdup(dNSName); + flow->protos.tls_quic.server_names_len = strlen(dNSName); + } else if((u_int16_t)(flow->protos.tls_quic.server_names_len + dNSName_len + 1) > flow->protos.tls_quic.server_names_len) { + u_int16_t newstr_len = flow->protos.tls_quic.server_names_len + dNSName_len + 1; + char *newstr = (char*)ndpi_realloc(flow->protos.tls_quic.server_names, + flow->protos.tls_quic.server_names_len+1, newstr_len+1); + + if(newstr) { + flow->protos.tls_quic.server_names = newstr; + flow->protos.tls_quic.server_names[flow->protos.tls_quic.server_names_len] = ','; + strncpy(&flow->protos.tls_quic.server_names[flow->protos.tls_quic.server_names_len+1], + dNSName, dNSName_len+1); + flow->protos.tls_quic.server_names[newstr_len] = '\0'; + flow->protos.tls_quic.server_names_len = newstr_len; + } + } } if(ndpi_struct->cfg.tls_subclassification_enabled && - !flow->protos.tls_quic.subprotocol_detected) { + !flow->protos.tls_quic.subprotocol_detected && + !flow->tls_quic.from_rdp) { /* No (other) sub-classification; we will have TLS.RDP anyway */ if(ndpi_match_hostname_protocol(ndpi_struct, flow, __get_master(ndpi_struct, flow), dNSName, dNSName_len)) { flow->protos.tls_quic.subprotocol_detected = 1; - ndpi_unset_risk(flow, NDPI_NUMERIC_IP_HOST); + ndpi_unset_risk(ndpi_struct, flow, NDPI_NUMERIC_IP_HOST); } } @@ -1035,10 +1099,14 @@ void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct } /* while */ if(!matched_name) { - char str[128]; + if(is_flowrisk_info_enabled(ndpi_struct, NDPI_TLS_CERTIFICATE_MISMATCH)) { + char str[128]; - snprintf(str, sizeof(str), "%s vs %s", flow->host_server_name, flow->protos.tls_quic.server_names); - ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERTIFICATE_MISMATCH, str); /* Certificate mismatch */ + snprintf(str, sizeof(str), "%s vs %s", flow->host_server_name, flow->protos.tls_quic.server_names); + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERTIFICATE_MISMATCH, str); /* Certificate mismatch */ + } else { + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERTIFICATE_MISMATCH, NULL); /* Certificate mismatch */ + } } } } @@ -1047,7 +1115,8 @@ void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct } /* for */ if(rdn_len && (flow->protos.tls_quic.subjectDN == NULL)) { - flow->protos.tls_quic.subjectDN = ndpi_strdup(rdnSeqBuf); + if(ndpi_struct->cfg.tls_cert_subject_enabled) + flow->protos.tls_quic.subjectDN = ndpi_strdup(rdnSeqBuf); if(ndpi_struct->cfg.tls_subclassification_enabled && flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN) { @@ -1064,7 +1133,7 @@ void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct ndpi_set_detected_protocol(ndpi_struct, flow, proto_id, __get_master(ndpi_struct, flow), NDPI_CONFIDENCE_DPI); flow->category = ndpi_get_proto_category(ndpi_struct, ret); ndpi_check_subprotocol_risk(ndpi_struct, flow, proto_id); - ndpi_unset_risk(flow, NDPI_NUMERIC_IP_HOST); + ndpi_unset_risk(ndpi_struct, flow, NDPI_NUMERIC_IP_HOST); if(ndpi_struct->tls_cert_cache) { u_int64_t key = make_tls_cert_key(packet, 0 /* from the server */); @@ -1307,11 +1376,14 @@ static void ndpi_looks_like_tls(struct ndpi_detection_module_struct *ndpi_struct /* **************************************** */ int ndpi_search_tls_tcp(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow) { + struct ndpi_flow_struct *flow) { struct ndpi_packet_struct *packet = &ndpi_struct->packet; u_int8_t something_went_wrong = 0; message_t *message; + if(packet->tcp == NULL) + return 0; /* Error -> stop (this doesn't seem to be TCP) */ + #ifdef DEBUG_TLS_MEMORY printf("[TLS Mem] ndpi_search_tls_tcp() Processing new packet [payload_packet_len: %u][Dir: %u]\n", packet->payload_packet_len, packet->packet_direction); @@ -1506,17 +1578,15 @@ int ndpi_search_tls_tcp(struct ndpi_detection_module_struct *ndpi_struct, #endif } +#ifdef DEBUG_TLS_MEMORY + printf("[TLS] Eval if keep going [%p]\n", flow->extra_packets_func); +#endif + if(something_went_wrong || ((ndpi_struct->num_tls_blocks_to_follow > 0) && (flow->l4.tcp.tls.num_tls_blocks == ndpi_struct->num_tls_blocks_to_follow)) || ((ndpi_struct->num_tls_blocks_to_follow == 0) - && (/* Common path: found handshake on both directions */ - (flow->tls_quic.certificate_processed == 1 && flow->protos.tls_quic.client_hello_processed) || - /* No handshake at all but Application Data on both directions */ - (flow->l4.tcp.tls.app_data_seen[0] == 1 && flow->l4.tcp.tls.app_data_seen[1] == 1) || - /* Handshake on one direction and Application Data on the other */ - (flow->protos.tls_quic.client_hello_processed && flow->l4.tcp.tls.app_data_seen[!flow->protos.tls_quic.ch_direction] == 1) || - (flow->protos.tls_quic.server_hello_processed && flow->l4.tcp.tls.app_data_seen[flow->protos.tls_quic.ch_direction] == 1))) + && (!keep_extra_dissection_tcp(ndpi_struct, flow))) ) { #ifdef DEBUG_TLS_BLOCKS printf("*** [TLS Block] No more blocks\n"); @@ -1536,7 +1606,7 @@ int ndpi_search_tls_tcp(struct ndpi_detection_module_struct *ndpi_struct, suited than NDPI_CONFIDENCE_DPI_CACHE */ ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OOKLA, NDPI_PROTOCOL_TLS, NDPI_CONFIDENCE_DPI_AGGRESSIVE); /* TLS over port 8080 usually triggers that risk; clear it */ - ndpi_unset_risk(flow, NDPI_KNOWN_PROTOCOL_ON_NON_STANDARD_PORT); + ndpi_unset_risk(ndpi_struct, flow, NDPI_KNOWN_PROTOCOL_ON_NON_STANDARD_PORT); flow->extra_packets_func = NULL; return(0); /* That's all */ /* Loook for TLS-in-TLS */ @@ -1590,7 +1660,8 @@ int is_dtls(const u_int8_t *buf, u_int32_t buf_len, u_int32_t *block_len) { /* **************************************** */ -static int ndpi_search_tls_udp(struct ndpi_detection_module_struct *ndpi_struct, +/* NOTE: this function supports both TCP and UDP */ +static int ndpi_search_dtls(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { struct ndpi_packet_struct *packet = &ndpi_struct->packet; u_int32_t handshake_len, handshake_frag_off, handshake_frag_len; @@ -1759,7 +1830,7 @@ static void tlsInitExtraPacketProcessing(struct ndpi_detection_module_struct *nd /* At most 12 packets should almost always be enough to find the server certificate if it's there. Exception: DTLS traffic with fragments, retransmissions and STUN packets */ flow->max_extra_packets_to_check = ((packet->udp != NULL) ? 20 : 12) + (ndpi_struct->num_tls_blocks_to_follow*4); - flow->extra_packets_func = (packet->udp != NULL) ? ndpi_search_tls_udp : ndpi_search_tls_tcp; + flow->extra_packets_func = (packet->udp != NULL) ? ndpi_search_dtls : ndpi_search_tls_tcp; } /* **************************************** */ @@ -1849,21 +1920,25 @@ static void tlsCheckUncommonALPN(struct ndpi_detection_module_struct *ndpi_struc alpn_len = comma_or_nul - alpn_start; if(!is_a_common_alpn(ndpi_struct, alpn_start, alpn_len)) { - char str[64]; - size_t str_len; + if(is_flowrisk_info_enabled(ndpi_struct, NDPI_TLS_UNCOMMON_ALPN)) { + char str[64]; + size_t str_len; #ifdef DEBUG_TLS - printf("TLS uncommon ALPN found: %.*s\n", (int)alpn_len, alpn_start); + printf("TLS uncommon ALPN found: %.*s\n", (int)alpn_len, alpn_start); #endif - str[0] = '\0'; - str_len = ndpi_min(alpn_len, sizeof(str)); - if(str_len > 0) { - strncpy(str, alpn_start, str_len); - str[str_len - 1] = '\0'; - } + str[0] = '\0'; + str_len = ndpi_min(alpn_len, sizeof(str)); + if(str_len > 0) { + strncpy(str, alpn_start, str_len); + str[str_len - 1] = '\0'; + } - ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_UNCOMMON_ALPN, str); + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_UNCOMMON_ALPN, str); + } else { + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_UNCOMMON_ALPN, NULL); + } break; } @@ -1887,12 +1962,12 @@ static void ndpi_int_tls_add_connection(struct ndpi_detection_module_struct *ndp NDPI_PROTOCOL_RDP, NDPI_PROTOCOL_TLS, NDPI_CONFIDENCE_DPI); return; } - + if((flow->detected_protocol_stack[0] != NDPI_PROTOCOL_UNKNOWN) || (flow->detected_protocol_stack[1] != NDPI_PROTOCOL_UNKNOWN)) { if(!flow->extra_packets_func) tlsInitExtraPacketProcessing(ndpi_struct, flow); - + return; } @@ -1923,7 +1998,7 @@ static void checkExtensions(struct ndpi_detection_module_struct *ndpi_struct, /* see: https://www.wireshark.org/docs/wsar_html/packet-tls-utils_8h_source.html */ static u_int16_t const allowed_non_iana_extensions[] = { - 65486 /* ESNI */, 13172 /* NPN - Next Proto Neg */, 17513 /* ALPS */, + /* 65486 ESNI is suspicious nowadays */ 13172 /* NPN - Next Proto Neg */, 30032 /* Channel ID */, 65445 /* QUIC transport params */, /* GREASE extensions */ 2570, 6682, 10794, 14906, 19018, 23130, 27242, @@ -1933,14 +2008,15 @@ static void checkExtensions(struct ndpi_detection_module_struct *ndpi_struct, 1035, 10794, 16696, 23130, 31354, 35466, 51914, /* Ciphers */ 102, 129, 52243, 52244, 57363, 65279, 65413, - /* ECH */ - 65037 + /* ALPS */ + 17513, 17613 }; size_t const allowed_non_iana_extensions_size = sizeof(allowed_non_iana_extensions) / sizeof(allowed_non_iana_extensions[0]); /* see: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml */ - if(extension_id > 59 && extension_id != 65281) + /* 65281 renegotiation_info, 65037 ECH */ + if(extension_id > 59 && extension_id != 65281 && extension_id != 65037) { u_int8_t extension_found = 0; size_t i; @@ -1953,15 +2029,20 @@ static void checkExtensions(struct ndpi_detection_module_struct *ndpi_struct, } if(extension_found == 0) { - char str[64]; - - snprintf(str, sizeof(str), "Extn id %u", extension_id); #ifdef DEBUG_TLS - printf("[TLS] suspicious extension id: %u\n", extension_id); + printf("[TLS] suspicious extension id: %u\n", extension_id); #endif + + if(is_flowrisk_info_enabled(ndpi_struct, NDPI_TLS_SUSPICIOUS_EXTENSION)) { + char str[64]; + + snprintf(str, sizeof(str), "Extn id %u", extension_id); ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_SUSPICIOUS_EXTENSION, str); - return; - } + } else { + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_SUSPICIOUS_EXTENSION, NULL); + } + return; + } } /* Check for DTLS-only extensions. */ @@ -1969,14 +2050,18 @@ static void checkExtensions(struct ndpi_detection_module_struct *ndpi_struct, { if(extension_id == 53 || extension_id == 54) { - char str[64]; - - snprintf(str, sizeof(str), "Extn id %u", extension_id); - #ifdef DEBUG_TLS - printf("[TLS] suspicious DTLS-only extension id: %u\n", extension_id); + printf("[TLS] suspicious DTLS-only extension id: %u\n", extension_id); #endif - ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_SUSPICIOUS_EXTENSION, str); + + if(is_flowrisk_info_enabled(ndpi_struct, NDPI_TLS_SUSPICIOUS_EXTENSION)) { + char str[64]; + + snprintf(str, sizeof(str), "Extn id %u", extension_id); + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_SUSPICIOUS_EXTENSION, str); + } else { + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_SUSPICIOUS_EXTENSION, NULL); + } return; } } @@ -2297,15 +2382,22 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, return(0); /* Not found */ ja.server.num_ciphers = 1, ja.server.cipher[0] = ntohs(*((u_int16_t*)&packet->payload[offset])); - if((flow->protos.tls_quic.server_unsafe_cipher = ndpi_is_safe_ssl_cipher(ja.server.cipher[0])) != NDPI_CIPHER_SAFE) { - char str[64]; - char unknown_cipher[8]; - snprintf(str, sizeof(str), "Cipher %s", ndpi_cipher2str(ja.server.cipher[0], unknown_cipher)); - ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_WEAK_CIPHER, str); - } + if(ndpi_struct->cfg.tls_cipher_enabled) { + if((flow->protos.tls_quic.server_unsafe_cipher = ndpi_is_safe_ssl_cipher(ja.server.cipher[0])) != NDPI_CIPHER_SAFE) { + if(is_flowrisk_info_enabled(ndpi_struct, NDPI_TLS_WEAK_CIPHER)) { + char str[64]; + char unknown_cipher[8]; + + snprintf(str, sizeof(str), "Cipher %s", ndpi_cipher2str(ja.server.cipher[0], unknown_cipher)); + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_WEAK_CIPHER, str); + } else { + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_WEAK_CIPHER, NULL); + } + } - flow->protos.tls_quic.server_cipher = ja.server.cipher[0]; + flow->protos.tls_quic.server_cipher = ja.server.cipher[0]; + } #ifdef DEBUG_TLS printf("TLS [server][session_id_len: %u][cipher: %04X]\n", session_id_len, ja.server.cipher[0]); @@ -2409,7 +2501,8 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, if(ndpi_normalize_printable_string(alpn_str, alpn_str_len) == 0) ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, alpn_str); - if(flow->protos.tls_quic.negotiated_alpn == NULL) + if(flow->protos.tls_quic.negotiated_alpn == NULL && + ndpi_struct->cfg.tls_alpn_negotiated_enabled) flow->protos.tls_quic.negotiated_alpn = ndpi_strdup(alpn_str); /* Check ALPN only if not already checked (client-side) */ @@ -2524,13 +2617,17 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, flow->protos.tls_quic.ssl_version = ja.client.tls_handshake_version = tls_version; if(flow->protos.tls_quic.ssl_version < 0x0303) /* < TLSv1.2 */ { - char str[32], buf[32]; - u_int8_t unknown_tls_version; - - snprintf(str, sizeof(str), "%s", ndpi_ssl_version2str(buf, sizeof(buf), - flow->protos.tls_quic.ssl_version, - &unknown_tls_version)); - ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_OBSOLETE_VERSION, str); + if(is_flowrisk_info_enabled(ndpi_struct, NDPI_TLS_OBSOLETE_VERSION)) { + char str[32], buf[32]; + u_int8_t unknown_tls_version; + + snprintf(str, sizeof(str), "%s", ndpi_ssl_version2str(buf, sizeof(buf), + flow->protos.tls_quic.ssl_version, + &unknown_tls_version)); + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_OBSOLETE_VERSION, str); + } else { + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_OBSOLETE_VERSION, NULL); + } } if((session_id_len+base_offset+3) > packet->payload_packet_len) @@ -2626,27 +2723,29 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, i += 2; } /* for */ - /* NOTE: - we do not check for duplicates as with signatures because - this is time consuming and we want to avoid overhead whem possible - */ - if(this_is_not_safari) - flow->protos.tls_quic.browser_heuristics.is_safari_tls = 0; - else if((safari_ciphers == 12) || (this_is_not_safari && looks_like_safari_on_big_sur)) - flow->protos.tls_quic.browser_heuristics.is_safari_tls = 1; + if(ndpi_struct->cfg.tls_broswer_enabled) { + /* NOTE: + we do not check for duplicates as with signatures because + this is time consuming and we want to avoid overhead whem possible + */ + if(this_is_not_safari) + flow->protos.tls_quic.browser_heuristics.is_safari_tls = 0; + else if((safari_ciphers == 12) || (this_is_not_safari && looks_like_safari_on_big_sur)) + flow->protos.tls_quic.browser_heuristics.is_safari_tls = 1; - if(chrome_ciphers == 13) - flow->protos.tls_quic.browser_heuristics.is_chrome_tls = 1; + if(chrome_ciphers == 13) + flow->protos.tls_quic.browser_heuristics.is_chrome_tls = 1; - /* Note that both Safari and Chrome can overlap */ + /* Note that both Safari and Chrome can overlap */ #ifdef DEBUG_HEURISTIC - printf("[CIPHERS] [is_chrome_tls: %u (%u)][is_safari_tls: %u (%u)][this_is_not_safari: %u]\n", - flow->protos.tls_quic.browser_heuristics.is_chrome_tls, - chrome_ciphers, - flow->protos.tls_quic.browser_heuristics.is_safari_tls, - safari_ciphers, - this_is_not_safari); + printf("[CIPHERS] [is_chrome_tls: %u (%u)][is_safari_tls: %u (%u)][this_is_not_safari: %u]\n", + flow->protos.tls_quic.browser_heuristics.is_chrome_tls, + chrome_ciphers, + flow->protos.tls_quic.browser_heuristics.is_safari_tls, + safari_ciphers, + this_is_not_safari); #endif + } } else { invalid_ja = 1; #ifdef DEBUG_TLS @@ -2747,10 +2846,14 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, if(!is_quic) { if(ndpi_struct->cfg.tls_subclassification_enabled && + flow->protos.tls_quic.subprotocol_detected == 0 && + !flow->tls_quic.from_rdp && /* No (other) sub-classification; we will have TLS.RDP anyway */ ndpi_match_hostname_protocol(ndpi_struct, flow, __get_master(ndpi_struct, flow), sni, sni_len)) flow->protos.tls_quic.subprotocol_detected = 1; } else { if(ndpi_struct->cfg.quic_subclassification_enabled && + flow->protos.tls_quic.subprotocol_detected == 0 && + !flow->tls_quic.from_rdp && /* No (other) sub-classification; we will have TLS.RDP anyway */ ndpi_match_hostname_protocol(ndpi_struct, flow, NDPI_PROTOCOL_QUIC, sni, sni_len)) flow->protos.tls_quic.subprotocol_detected = 1; } @@ -2760,7 +2863,12 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, ndpi_set_risk(ndpi_struct, flow, NDPI_NUMERIC_IP_HOST, sni); } - if(ndpi_check_dga_name(ndpi_struct, flow, sni, 1, 0)) { + if(ndpi_str_endswith(sni, "signal.org")) { + /* printf("[SIGNAL] SNI: [%s]\n", sni); */ + signal_add_to_cache(ndpi_struct, flow); + } + + if(ndpi_check_dga_name(ndpi_struct, flow, sni, 1, 0, 0)) { #ifdef DEBUG_TLS printf("[TLS] SNI: (DGA) [%s]\n", sni); #endif @@ -2768,7 +2876,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, if((sni_len >= 4) /* Check if it ends in .com or .net */ && ((strcmp(&sni[sni_len-4], ".com") == 0) || (strcmp(&sni[sni_len-4], ".net") == 0)) - && (strncmp(sni, "www.", 4) == 0)) /* Not starting with www.... */ + && (strncmp(sni, "www.", 4) == 0)) /* Starting with www.... */ ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_TOR, __get_master(ndpi_struct, flow), NDPI_CONFIDENCE_DPI); } else { #ifdef DEBUG_TLS @@ -2847,8 +2955,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, } } else if(extension_id == 13 /* signature algorithms */ && offset+extension_offset+1 < total_len) { - int s_offset = offset+extension_offset, safari_signature_algorithms = 0, - chrome_signature_algorithms = 0, duplicate_found = 0, last_signature = 0, id; + int s_offset = offset+extension_offset, safari_signature_algorithms = 0, id; u_int16_t tot_signature_algorithms_len = ntohs(*((u_int16_t*)&packet->payload[s_offset])); #ifdef DEBUG_TLS @@ -2880,95 +2987,99 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, if(rc < 0) break; } - for(i=0; i<tot_signature_algorithms_len && s_offset + (int)i + 2 < packet->payload_packet_len; i+=2) { - u_int16_t signature_algo = (u_int16_t)ntohs(*((u_int16_t*)&packet->payload[s_offset+i])); + if(ndpi_struct->cfg.tls_broswer_enabled) { + int chrome_signature_algorithms = 0, duplicate_found = 0, last_signature = 0; - if(last_signature == signature_algo) { - /* Consecutive duplication */ - duplicate_found = 1; - continue; - } else { - /* Check for other duplications */ - u_int all_ok = 1; + for(i=0; i<tot_signature_algorithms_len && s_offset + (int)i + 2 < packet->payload_packet_len; i+=2) { + u_int16_t signature_algo = (u_int16_t)ntohs(*((u_int16_t*)&packet->payload[s_offset+i])); - for(j=0; j<tot_signature_algorithms_len; j+=2) { - if(j != i && s_offset + (int)j + 2 < packet->payload_packet_len) { - u_int16_t j_signature_algo = (u_int16_t)ntohs(*((u_int16_t*)&packet->payload[s_offset+j])); + if(last_signature == signature_algo) { + /* Consecutive duplication */ + duplicate_found = 1; + continue; + } else { + /* Check for other duplications */ + u_int all_ok = 1; - if((signature_algo == j_signature_algo) - && (i < j) /* Don't skip both of them */) { + for(j=0; j<tot_signature_algorithms_len; j+=2) { + if(j != i && s_offset + (int)j + 2 < packet->payload_packet_len) { + u_int16_t j_signature_algo = (u_int16_t)ntohs(*((u_int16_t*)&packet->payload[s_offset+j])); + + if((signature_algo == j_signature_algo) + && (i < j) /* Don't skip both of them */) { #ifdef DEBUG_HEURISTIC - printf("[SIGNATURE] [TLS Signature Algorithm] Skipping duplicate 0x%04X\n", signature_algo); + printf("[SIGNATURE] [TLS Signature Algorithm] Skipping duplicate 0x%04X\n", signature_algo); #endif - duplicate_found = 1, all_ok = 0; - break; - } - } - } + duplicate_found = 1, all_ok = 0; + break; + } + } + } - if(!all_ok) - continue; - } + if(!all_ok) + continue; + } - last_signature = signature_algo; + last_signature = signature_algo; #ifdef DEBUG_HEURISTIC - printf("[SIGNATURE] [TLS Signature Algorithm] 0x%04X\n", signature_algo); -#endif - switch(signature_algo) { - case ECDSA_SECP521R1_SHA512: - flow->protos.tls_quic.browser_heuristics.is_firefox_tls = 1; - break; - - case ECDSA_SECP256R1_SHA256: - case ECDSA_SECP384R1_SHA384: - case RSA_PKCS1_SHA256: - case RSA_PKCS1_SHA384: - case RSA_PKCS1_SHA512: - case RSA_PSS_RSAE_SHA256: - case RSA_PSS_RSAE_SHA384: - case RSA_PSS_RSAE_SHA512: - chrome_signature_algorithms++, safari_signature_algorithms++; + printf("[SIGNATURE] [TLS Signature Algorithm] 0x%04X\n", signature_algo); +#endif + switch(signature_algo) { + case ECDSA_SECP521R1_SHA512: + flow->protos.tls_quic.browser_heuristics.is_firefox_tls = 1; + break; + + case ECDSA_SECP256R1_SHA256: + case ECDSA_SECP384R1_SHA384: + case RSA_PKCS1_SHA256: + case RSA_PKCS1_SHA384: + case RSA_PKCS1_SHA512: + case RSA_PSS_RSAE_SHA256: + case RSA_PSS_RSAE_SHA384: + case RSA_PSS_RSAE_SHA512: + chrome_signature_algorithms++, safari_signature_algorithms++; #ifdef DEBUG_HEURISTIC - printf("[SIGNATURE] [Chrome/Safari] Found 0x%04X [chrome: %u][safari: %u]\n", - signature_algo, chrome_signature_algorithms, safari_signature_algorithms); + printf("[SIGNATURE] [Chrome/Safari] Found 0x%04X [chrome: %u][safari: %u]\n", + signature_algo, chrome_signature_algorithms, safari_signature_algorithms); #endif - break; - } - } + break; + } + } #ifdef DEBUG_HEURISTIC - printf("[SIGNATURE] [safari_signature_algorithms: %u][chrome_signature_algorithms: %u]\n", - safari_signature_algorithms, chrome_signature_algorithms); + printf("[SIGNATURE] [safari_signature_algorithms: %u][chrome_signature_algorithms: %u]\n", + safari_signature_algorithms, chrome_signature_algorithms); #endif - if(flow->protos.tls_quic.browser_heuristics.is_firefox_tls) - flow->protos.tls_quic.browser_heuristics.is_safari_tls = 0, - flow->protos.tls_quic.browser_heuristics.is_chrome_tls = 0; + if(flow->protos.tls_quic.browser_heuristics.is_firefox_tls) + flow->protos.tls_quic.browser_heuristics.is_safari_tls = 0, + flow->protos.tls_quic.browser_heuristics.is_chrome_tls = 0; - if(safari_signature_algorithms != 8) - flow->protos.tls_quic.browser_heuristics.is_safari_tls = 0; + if(safari_signature_algorithms != 8) + flow->protos.tls_quic.browser_heuristics.is_safari_tls = 0; - if((chrome_signature_algorithms != 8) || duplicate_found) - flow->protos.tls_quic.browser_heuristics.is_chrome_tls = 0; + if((chrome_signature_algorithms != 8) || duplicate_found) + flow->protos.tls_quic.browser_heuristics.is_chrome_tls = 0; - /* Avoid Chrome and Safari overlaps, thing that cannot happen with Firefox */ - if(flow->protos.tls_quic.browser_heuristics.is_safari_tls) - flow->protos.tls_quic.browser_heuristics.is_chrome_tls = 0; + /* Avoid Chrome and Safari overlaps, thing that cannot happen with Firefox */ + if(flow->protos.tls_quic.browser_heuristics.is_safari_tls) + flow->protos.tls_quic.browser_heuristics.is_chrome_tls = 0; - if((flow->protos.tls_quic.browser_heuristics.is_chrome_tls == 0) - && duplicate_found) - flow->protos.tls_quic.browser_heuristics.is_safari_tls = 1; /* Safari */ + if((flow->protos.tls_quic.browser_heuristics.is_chrome_tls == 0) + && duplicate_found) + flow->protos.tls_quic.browser_heuristics.is_safari_tls = 1; /* Safari */ #ifdef DEBUG_HEURISTIC - printf("[SIGNATURE] [is_firefox_tls: %u][is_chrome_tls: %u][is_safari_tls: %u][duplicate_found: %u]\n", - flow->protos.tls_quic.browser_heuristics.is_firefox_tls, - flow->protos.tls_quic.browser_heuristics.is_chrome_tls, - flow->protos.tls_quic.browser_heuristics.is_safari_tls, - duplicate_found); + printf("[SIGNATURE] [is_firefox_tls: %u][is_chrome_tls: %u][is_safari_tls: %u][duplicate_found: %u]\n", + flow->protos.tls_quic.browser_heuristics.is_firefox_tls, + flow->protos.tls_quic.browser_heuristics.is_chrome_tls, + flow->protos.tls_quic.browser_heuristics.is_safari_tls, + duplicate_found); #endif + } if(i > 0 && i >= tot_signature_algorithms_len) { ja.client.signature_algorithms_str[i*2 - 1] = '\0'; @@ -3100,12 +3211,10 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, printf("Client TLS [SUPPORTED_VERSIONS: %s]\n", version_str); #endif - if(flow->protos.tls_quic.tls_supported_versions == NULL) + if(flow->protos.tls_quic.tls_supported_versions == NULL && + ndpi_struct->cfg.tls_versions_supported_enabled) flow->protos.tls_quic.tls_supported_versions = ndpi_strdup(version_str); } - } else if(extension_id == 65486 /* encrypted server name */) { - /* ESNI has been superseded by ECH */ - ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_SUSPICIOUS_ESNI_USAGE, NULL); } else if(extension_id == 65037 /* ECH: latest drafts */) { #ifdef DEBUG_TLS printf("Client TLS: ECH version 0x%x\n", extension_id); @@ -3169,6 +3278,22 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, #endif ndpi_set_risk(ndpi_struct, flow, NDPI_OBFUSCATED_TRAFFIC, "Abnormal Client Hello/Padding length"); } + } else if(extension_id == 22) { /* Encrypt-then-MAC */ + if(extension_len == 0) { + char *sni = flow->host_server_name; + + if(sni != NULL) { + u_int sni_len = strlen(sni); + + if((flow->protos.tls_quic.advertised_alpns == NULL) /* No ALPN */ + && (sni_len > 8) + && ((strcmp(&sni[sni_len-4], ".com") == 0) || (strcmp(&sni[sni_len-4], ".net") == 0)) + && (strncmp(sni, "www.", 4) == 0) /* Starting with www.... */ + && str_contains_digit(&sni[4])) { + ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_TOR, __get_master(ndpi_struct, flow), NDPI_CONFIDENCE_DPI); + } + } + } } extension_offset += extension_len; /* Move to the next extension */ @@ -3277,7 +3402,7 @@ static void ndpi_search_tls_wrapper(struct ndpi_detection_module_struct *ndpi_st if(flow->tls_quic.obfuscated_heur_state == NULL) { if(packet->udp != NULL || flow->stun.maybe_dtls) - rc = ndpi_search_tls_udp(ndpi_struct, flow); + rc = ndpi_search_dtls(ndpi_struct, flow); else rc = ndpi_search_tls_tcp(ndpi_struct, flow); @@ -3301,34 +3426,17 @@ static void ndpi_search_tls_wrapper(struct ndpi_detection_module_struct *ndpi_st if(flow->tls_quic.obfuscated_heur_state) { tls_obfuscated_heur_search_again(ndpi_struct, flow); } else if(rc == 0) { - if(packet->udp != NULL || flow->stun.maybe_dtls) - NDPI_EXCLUDE_PROTO_EXT(ndpi_struct, flow, NDPI_PROTOCOL_DTLS); - else - NDPI_EXCLUDE_PROTO(ndpi_struct, flow); + NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); } } /* **************************************** */ -void init_tls_dissector(struct ndpi_detection_module_struct *ndpi_struct, - u_int32_t *id) { - ndpi_set_bitmask_protocol_detection("TLS", ndpi_struct, *id, - NDPI_PROTOCOL_TLS, - ndpi_search_tls_wrapper, - NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, - SAVE_DETECTION_BITMASK_AS_UNKNOWN, - ADD_TO_DETECTION_BITMASK); - - *id += 1; - - /* *************************************************** */ - - ndpi_set_bitmask_protocol_detection("DTLS", ndpi_struct, *id, - NDPI_PROTOCOL_DTLS, - ndpi_search_tls_wrapper, - NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_UDP_WITH_PAYLOAD, - SAVE_DETECTION_BITMASK_AS_UNKNOWN, - ADD_TO_DETECTION_BITMASK); - - *id += 1; +void init_tls_dissector(struct ndpi_detection_module_struct *ndpi_struct) { + register_dissector("(D)TLS", ndpi_struct, + ndpi_search_tls_wrapper, + NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, + 2, + NDPI_PROTOCOL_TLS, + NDPI_PROTOCOL_DTLS); } |