diff options
-rw-r--r-- | example/reader_util.c | 39 | ||||
-rw-r--r-- | src/include/ndpi_typedefs.h | 5 | ||||
-rw-r--r-- | src/lib/ndpi_main.c | 3 | ||||
-rw-r--r-- | src/lib/protocols/kerberos.c | 278 |
4 files changed, 252 insertions, 73 deletions
diff --git a/example/reader_util.c b/example/reader_util.c index 2352369ee..cf3c82475 100644 --- a/example/reader_util.c +++ b/example/reader_util.c @@ -981,11 +981,14 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl else if(is_ndpi_proto(flow, NDPI_PROTOCOL_KERBEROS)) { if((flow->ndpi_flow->protos.kerberos.hostname[0] != '\0') || (flow->ndpi_flow->protos.kerberos.username[0] != '\0')) { - snprintf(flow->info, sizeof(flow->info), "%s%s (%s)", + snprintf(flow->info, sizeof(flow->info), "%s%s%s%s", + flow->ndpi_flow->protos.kerberos.domain /* = realm */, + flow->ndpi_flow->protos.kerberos.domain[0] != '\0' ? "\\" : "", flow->ndpi_flow->protos.kerberos.hostname, - flow->ndpi_flow->protos.kerberos.username, + flow->ndpi_flow->protos.kerberos.username); + } else if(flow->ndpi_flow->protos.kerberos.domain[0] != '\0') + snprintf(flow->info, sizeof(flow->info), "%s", flow->ndpi_flow->protos.kerberos.domain); - } } /* HTTP */ else if((flow->detected_protocol.master_protocol == NDPI_PROTOCOL_HTTP) @@ -1171,21 +1174,21 @@ static struct ndpi_proto packet_processing(struct ndpi_workflow * workflow, if((flow->entropy.src2dst_pkt_count+flow->entropy.dst2src_pkt_count) <= max_num_packets_per_flow) { if(flow->bidirectional) flow->entropy.score = ndpi_classify(flow->entropy.src2dst_pkt_len, flow->entropy.src2dst_pkt_time, - flow->entropy.dst2src_pkt_len, flow->entropy.dst2src_pkt_time, - flow->entropy.src2dst_start, flow->entropy.dst2src_start, - max_num_packets_per_flow, flow->src_port, flow->dst_port, - flow->src2dst_packets, flow->dst2src_packets, - flow->entropy.src2dst_opackets, flow->entropy.dst2src_opackets, - flow->entropy.src2dst_l4_bytes, flow->entropy.dst2src_l4_bytes, 1, - flow->entropy.src2dst_byte_count, flow->entropy.dst2src_byte_count); - else - flow->entropy.score = ndpi_classify(flow->entropy.src2dst_pkt_len, flow->entropy.src2dst_pkt_time, - NULL, NULL, flow->entropy.src2dst_start, flow->entropy.src2dst_start, - max_num_packets_per_flow, flow->src_port, flow->dst_port, - flow->src2dst_packets, 0, - flow->entropy.src2dst_opackets, 0, - flow->entropy.src2dst_l4_bytes, 0, 1, - flow->entropy.src2dst_byte_count, NULL); + flow->entropy.dst2src_pkt_len, flow->entropy.dst2src_pkt_time, + flow->entropy.src2dst_start, flow->entropy.dst2src_start, + max_num_packets_per_flow, flow->src_port, flow->dst_port, + flow->src2dst_packets, flow->dst2src_packets, + flow->entropy.src2dst_opackets, flow->entropy.dst2src_opackets, + flow->entropy.src2dst_l4_bytes, flow->entropy.dst2src_l4_bytes, 1, + flow->entropy.src2dst_byte_count, flow->entropy.dst2src_byte_count); + else + flow->entropy.score = ndpi_classify(flow->entropy.src2dst_pkt_len, flow->entropy.src2dst_pkt_time, + NULL, NULL, flow->entropy.src2dst_start, flow->entropy.src2dst_start, + max_num_packets_per_flow, flow->src_port, flow->dst_port, + flow->src2dst_packets, 0, + flow->entropy.src2dst_opackets, 0, + flow->entropy.src2dst_l4_bytes, 0, 1, + flow->entropy.src2dst_byte_count, NULL); } } diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index 82a450462..b8c3b40bb 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -1203,8 +1203,9 @@ struct ndpi_flow_struct { } ntp; struct { - - char hostname[24], domain[24], username[24]; + char *pktbuf; + u_int16_t pktbuf_maxlen, pktbuf_currlen; + char hostname[48], domain[48], username[48]; } kerberos; struct { diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c index 40bf9ae20..a2a7e593d 100644 --- a/src/lib/ndpi_main.c +++ b/src/lib/ndpi_main.c @@ -6374,7 +6374,8 @@ void ndpi_free_flow(struct ndpi_flow_struct *flow) { if(flow->http.url) ndpi_free(flow->http.url); if(flow->http.content_type) ndpi_free(flow->http.content_type); if(flow->http.user_agent) ndpi_free(flow->http.user_agent); - + if(flow->protos.kerberos.pktbuf) ndpi_free(flow->protos.kerberos.pktbuf); + if(flow->l4_proto == IPPROTO_TCP) { if(flow->l4.tcp.tls_srv_cert_fingerprint_ctx) ndpi_free(flow->l4.tcp.tls_srv_cert_fingerprint_ctx); diff --git a/src/lib/protocols/kerberos.c b/src/lib/protocols/kerberos.c index 533995604..ae33d525a 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 static void ndpi_int_kerberos_add_connection(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { @@ -41,62 +41,195 @@ static void ndpi_int_kerberos_add_connection(struct ndpi_detection_module_struct void ndpi_search_kerberos(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { struct ndpi_packet_struct *packet = &flow->packet; +#ifdef KERBEROS_DEBUG + u_int16_t sport = packet->tcp ? ntohs(packet->tcp->source) : ntohs(packet->udp->source); + u_int16_t dport = packet->tcp ? ntohs(packet->tcp->dest) : ntohs(packet->udp->dest); +#endif NDPI_LOG_DBG(ndpi_struct, "search KERBEROS\n"); +#ifdef KERBEROS_DEBUG + printf("\n[Kerberos] Process packet [len: %u]\n", packet->payload_packet_len); +#endif + + if(flow->protos.kerberos.pktbuf != NULL) { + u_int missing = flow->protos.kerberos.pktbuf_maxlen - flow->protos.kerberos.pktbuf_currlen; + + if(packet->payload_packet_len <= missing) { + memcpy(&flow->protos.kerberos.pktbuf[flow->protos.kerberos.pktbuf_currlen], packet->payload, packet->payload_packet_len); + flow->protos.kerberos.pktbuf_currlen += packet->payload_packet_len; + + if(flow->protos.kerberos.pktbuf_currlen == flow->protos.kerberos.pktbuf_maxlen) { + packet->payload = (u_int8_t *)flow->protos.kerberos.pktbuf; + packet->payload_packet_len = flow->protos.kerberos.pktbuf_currlen; +#ifdef KERBEROS_DEBUG + printf("[Kerberos] Packet is now full: processing\n"); +#endif + } else { +#ifdef KERBEROS_DEBUG + printf("[Kerberos] Missing %u bytes: skipping\n", + flow->protos.kerberos.pktbuf_maxlen - flow->protos.kerberos.pktbuf_currlen); +#endif + + return; + } + } + } + /* I have observed 0a,0c,0d,0e at packet->payload[19/21], maybe there are other possibilities */ if(packet->payload_packet_len >= 4) { - u_int32_t kerberos_len = ntohl(get_u_int32_t(packet->payload, 0)); - u_int32_t expected_len = packet->payload_packet_len - 4; + u_int32_t kerberos_len, expected_len; + u_int16_t base_offset = 0; + + if(packet->tcp) { + kerberos_len = ntohl(get_u_int32_t(packet->payload, 0)), + expected_len = packet->payload_packet_len - 4; + base_offset = 4; + } else + base_offset = 0, kerberos_len = expected_len = packet->payload_packet_len; - if(kerberos_len < 1514) { +#ifdef KERBEROS_DEBUG + printf("[Kerberos] [Kerberos len: %u][expected_len: %u]\n", kerberos_len, expected_len); +#endif + + if(kerberos_len < 12000) { /* Kerberos packets might be too long for a TCP packet so it could be split across two packets. Instead of rebuilding the stream we use a heuristic approach */ - if(kerberos_len >= expected_len) { + if(kerberos_len > expected_len) { + if(packet->tcp) { + flow->protos.kerberos.pktbuf = (char*)ndpi_malloc(kerberos_len+4); + if(flow->protos.kerberos.pktbuf != NULL) { + flow->protos.kerberos.pktbuf_maxlen = kerberos_len+4; + memcpy(flow->protos.kerberos.pktbuf, packet->payload, packet->payload_packet_len); + flow->protos.kerberos.pktbuf_currlen = packet->payload_packet_len; + } + } + + return; + } else if(kerberos_len == expected_len) { if(packet->payload_packet_len > 128) { - u_int16_t koffset; + u_int16_t koffset, i; - if(packet->payload[14] == 0x05) /* PVNO */ - koffset = 19; - else - koffset = 21; + for(i=8; i<16; i++) + if((packet->payload[base_offset+i] == 0x03) + && (packet->payload[base_offset+i+1] == 0x02) + && (packet->payload[base_offset+i+2] == 0x01) + && (packet->payload[base_offset+i+3] != 0x05) + ) + break; + + koffset = base_offset + i + 3; - if((packet->payload[koffset] == 0x0a || packet->payload[koffset] == 0x0c || packet->payload[koffset] == 0x0d || packet->payload[koffset] == 0x0e)) { #ifdef KERBEROS_DEBUG - printf("[Kerberos] Packet found\n"); + printf("[Kerberos] [msg-type: 0x%02X/%u][koffset: %u]\n", + packet->payload[koffset], packet->payload[koffset], koffset); +#endif + + if(((packet->payload[koffset] == 0x0A) + || (packet->payload[koffset] == 0x0C) + || (packet->payload[koffset] == 0x0D) + || (packet->payload[koffset] == 0x0E))) { + u_int16_t koffsetp, body_offset, pad_len; + u_int8_t msg_type = packet->payload[koffset]; + +#ifdef KERBEROS_DEBUG + printf("[Kerberos] Packet found 0x%02X/%u\n", msg_type, msg_type); +#endif + if(msg_type != 0x0d) /* TGS-REP */ { + /* Process only on requests */ + if(packet->payload[koffset+1] == 0xA3) { + if(packet->payload[koffset+3] == 0x30) + pad_len = packet->payload[koffset+4]; + else { + /* Long pad */ + pad_len = packet->payload[koffset+2]; + for(i=3; i<10; i++) if(packet->payload[koffset+i] == pad_len) break; + + pad_len = (packet->payload[koffset+i+1] << 8) + packet->payload[koffset+i+2]; + koffset += i-2; + } + } else + pad_len = 0; + +#ifdef KERBEROS_DEBUG + printf("pad_len=0x%02X/%u\n", pad_len, pad_len); +#endif + + if(pad_len > 0) { + koffsetp = koffset + 2; + for(i=0; i<4; i++) if(packet->payload[koffsetp] != 0x30) koffsetp++; /* ASN.1 */ +#ifdef KERBEROS_DEBUG + printf("koffsetp=%u [%02X %02X] [byte 0 must be 0x30]\n", koffsetp, packet->payload[koffsetp], packet->payload[koffsetp+1]); +#endif + } else + koffsetp = koffset; + + body_offset = koffsetp + 1 + pad_len; + + for(i=0; i<10; i++) if(packet->payload[body_offset] != 0x05) body_offset++; /* ASN.1 */ +#ifdef KERBEROS_DEBUG + printf("body_offset=%u [%02X %02X] [byte 0 must be 0x05]\n", body_offset, packet->payload[body_offset], packet->payload[body_offset+1]); +#endif + } + + if(msg_type == 0x0A) /* AS-REQ */ { +#ifdef KERBEROS_DEBUG + printf("[Kerberos] Processing AS-REQ\n"); #endif - if(packet->payload[koffset] == 0x0a) /* AS-REQ */ { - u_int16_t koffsetp, pad_data_len, body_offset; - - koffsetp = koffset + 4; - pad_data_len = packet->payload[koffsetp]; - body_offset = pad_data_len + koffsetp; if(body_offset < packet->payload_packet_len) { - u_int name_offset = body_offset + 30; + 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 */ + +#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]); +#endif if(name_offset < packet->payload_packet_len) { - u_int cname_len = packet->payload[name_offset]; + u_int cname_len; + + name_offset += 1; + if(packet->payload[name_offset+1] < ' ') /* Isn't printable ? */ + name_offset++; + + if(packet->payload[name_offset+1] == 0x1b) + name_offset += 2; + + cname_len = packet->payload[name_offset]; if((cname_len+name_offset) < packet->payload_packet_len) { - u_int realm_len, realm_offset = cname_len + name_offset + 4, i; - char cname_str[24]; + u_int realm_len, realm_offset, i; + char cname_str[48]; + u_int8_t num_cname = 0; - if(cname_len > sizeof(cname_str)-1) - cname_len = sizeof(cname_str)-1; + while(++num_cname <= 2) { + if(cname_len > sizeof(cname_str)-1) + cname_len = sizeof(cname_str)-1; - strncpy(cname_str, (char*)&packet->payload[name_offset+1], cname_len); - cname_str[cname_len] = '\0'; - for(i=0; i<cname_len; i++) cname_str[i] = tolower(cname_str[i]); + strncpy(cname_str, (char*)&packet->payload[name_offset+1], cname_len); + cname_str[cname_len] = '\0'; + for(i=0; i<cname_len; i++) cname_str[i] = tolower(cname_str[i]); #ifdef KERBEROS_DEBUG - printf("[Kerberos Cname][len: %u][%s]\n", 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)) { + name_offset += cname_len + 2; + cname_len = packet->payload[name_offset]; + } else + break; + } + + realm_offset = cname_len + name_offset + 3; + /* if cname does not end with a $ then it's a username */ if(cname_len && cname_str[cname_len-1] == '$') { cname_str[cname_len-1] = '\0'; @@ -104,62 +237,97 @@ 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 */ +#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 + realm_offset += 1; + //if(num_cname == 2) realm_offset++; realm_len = packet->payload[realm_offset]; if((realm_offset+realm_len) < packet->payload_packet_len) { - char realm_str[24]; + char realm_str[48]; if(realm_len > sizeof(realm_str)-1) realm_len = sizeof(realm_str); - strncpy(realm_str, (char*)&packet->payload[realm_offset+1], realm_len); + realm_offset += 1; + + strncpy(realm_str, (char*)&packet->payload[realm_offset], realm_len); realm_str[realm_len] = '\0'; for(i=0; i<realm_len; i++) realm_str[i] = tolower(realm_str[i]); #ifdef KERBEROS_DEBUG - printf("[Kerberos Realm][len: %u][%s]\n", realm_len, realm_str); + printf("[AS-REQ][Kerberos Realm][len: %u][%s]\n", realm_len, realm_str); #endif snprintf(flow->protos.kerberos.domain, sizeof(flow->protos.kerberos.domain), "%s", realm_str); } } } - } - } else if(packet->payload[koffset] == 0x0c) /* TGS-REQ */ { - u_int16_t koffsetp, pad_data_len, body_offset; - - koffsetp = koffset + 3; - pad_data_len = ntohs(*((u_int16_t*)&packet->payload[koffsetp])); - body_offset = pad_data_len + koffsetp + 4; + } + } else if(msg_type == 0x0c) /* TGS-REQ */ { +#ifdef KERBEROS_DEBUG + printf("[Kerberos] Processing TGS-REQ\n"); +#endif if(body_offset < packet->payload_packet_len) { - u_int name_offset = body_offset + 14; + 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 */ + +#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]); +#endif if(name_offset < packet->payload_packet_len) { - u_int realm_len = packet->payload[name_offset]; + u_int realm_len; + + name_offset++; + realm_len = packet->payload[name_offset]; if((realm_len+name_offset) < packet->payload_packet_len) { u_int i; - char realm_str[24]; + char realm_str[48]; if(realm_len > sizeof(realm_str)-1) realm_len = sizeof(realm_str)-1; - strncpy(realm_str, (char*)&packet->payload[name_offset+1], realm_len); + name_offset += 1; + + strncpy(realm_str, (char*)&packet->payload[name_offset], realm_len); realm_str[realm_len] = '\0'; for(i=0; i<realm_len; i++) realm_str[i] = tolower(realm_str[i]); #ifdef KERBEROS_DEBUG - printf("[Kerberos Realm][len: %u][%s]\n", realm_len, realm_str); + printf("[TGS-REQ][s/dport: %u/%u][Kerberos Realm][len: %u][%s]\n", sport, dport, realm_len, realm_str); #endif snprintf(flow->protos.kerberos.domain, sizeof(flow->protos.kerberos.domain), "%s", realm_str); + + /* If necessary we can decode sname */ + + if(flow->protos.kerberos.pktbuf) ndpi_free(flow->protos.kerberos.pktbuf); + flow->protos.kerberos.pktbuf = NULL; } } } + if(packet->udp) + ndpi_int_kerberos_add_connection(ndpi_struct, flow); + /* We set the protocol in the response */ + if(flow->protos.kerberos.pktbuf != NULL) { + free(flow->protos.kerberos.pktbuf); + flow->protos.kerberos.pktbuf = NULL; + } + return; - } else if(packet->payload[koffset] == 0x0d) /* TGS-RES */ { + } else if(msg_type == 0x0d) /* TGS-REP */ { u_int16_t koffsetp, pad_data_len, cname_offset; + +#ifdef KERBEROS_DEBUG + printf("[Kerberos] Processing TGS-REP\n"); +#endif koffsetp = koffset + 4; pad_data_len = packet->payload[koffsetp]; @@ -170,20 +338,21 @@ void ndpi_search_kerberos(struct ndpi_detection_module_struct *ndpi_struct, u_int8_t cname_len = packet->payload[cname_offset]; if((cname_offset+cname_offset) < packet->payload_packet_len) { - char cname_str[24]; + char cname_str[48]; u_int i; - + if(cname_len > sizeof(cname_str)-1) cname_len = sizeof(cname_str)-1; - + strncpy(cname_str, (char*)&packet->payload[cname_offset+1], cname_len); cname_str[cname_len] = '\0'; for(i=0; i<cname_len; i++) cname_str[i] = tolower(cname_str[i]); - + #ifdef KERBEROS_DEBUG - printf("[Kerberos Cname][len: %u][%s]\n", cname_len, cname_str); + printf("[TGS-REP][s/dport: %u/%u][Kerberos Cname][len: %u][%s]\n", + sport, dport, cname_len, cname_str); #endif - + if(cname_len && cname_str[cname_len-1] == '$') { cname_str[cname_len-1] = '\0'; snprintf(flow->protos.kerberos.hostname, sizeof(flow->protos.kerberos.hostname), "%s", cname_str); @@ -194,7 +363,7 @@ void ndpi_search_kerberos(struct ndpi_detection_module_struct *ndpi_struct, } } } - + return; } @@ -208,6 +377,11 @@ void ndpi_search_kerberos(struct ndpi_detection_module_struct *ndpi_struct, } } } else { +#ifdef KERBEROS_DEBUG + printf("[Kerberos][s/dport: %u/%u] Skipping packet: too long [kerberos_len: %u]\n", + sport, dport, kerberos_len); +#endif + if(flow->protos.kerberos.domain[0] != '\0') return; } @@ -222,7 +396,7 @@ void init_kerberos_dissector(struct ndpi_detection_module_struct *ndpi_struct, ndpi_set_bitmask_protocol_detection("Kerberos", ndpi_struct, detection_bitmask, *id, NDPI_PROTOCOL_KERBEROS, ndpi_search_kerberos, - NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, + NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, SAVE_DETECTION_BITMASK_AS_UNKNOWN, ADD_TO_DETECTION_BITMASK); |