/*
* tls.c - SSL/TLS/DTLS dissector
*
* Copyright (C) 2016-20 - ntop.org
*
* This file is part of nDPI, an open source deep packet inspection
* library based on the OpenDPI and PACE technology by ipoque GmbH
*
* nDPI is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nDPI is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with nDPI. If not, see .
*
*/
#include "ndpi_protocol_ids.h"
#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_TLS
#include "ndpi_api.h"
#include "ndpi_md5.h"
#include "ndpi_sha1.h"
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);
// #define DEBUG_TLS_MEMORY 1
// #define DEBUG_TLS 1
// #define DEBUG_CERTIFICATE_HASH
/* #define DEBUG_FINGERPRINT 1 */
/* #define DEBUG_ENCRYPTED_SNI 1 */
/*
NOTE
How to view the certificate fingerprint
1. Using wireshark save the certificate on certificate.bin file as explained
in https://security.stackexchange.com/questions/123851/how-can-i-extract-the-certificate-from-this-pcap-file
2. openssl x509 -inform der -in certificate.bin -text > certificate.der
3. openssl x509 -noout -fingerprint -sha1 -inform pem -in certificate.der
SHA1 Fingerprint=15:9A:76....
$ shasum -a 1 www.grc.com.bin
159a76.....
*/
#define NDPI_MAX_TLS_REQUEST_SIZE 10000
/* skype.c */
extern u_int8_t is_skype_flow(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow);
/* stun.c */
extern u_int32_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev);
static void ndpi_int_tls_add_connection(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow, u_int32_t protocol);
/* **************************************** */
static u_int32_t ndpi_tls_refine_master_protocol(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow, u_int32_t protocol) {
struct ndpi_packet_struct *packet = &flow->packet;
// protocol = NDPI_PROTOCOL_TLS;
if(packet->tcp != NULL) {
switch(protocol) {
case NDPI_PROTOCOL_TLS:
{
/*
In case of SSL there are probably sub-protocols
such as IMAPS that can be otherwise detected
*/
u_int16_t sport = ntohs(packet->tcp->source);
u_int16_t dport = ntohs(packet->tcp->dest);
if((sport == 465) || (dport == 465) || (sport == 587) || (dport == 587))
protocol = NDPI_PROTOCOL_MAIL_SMTPS;
else if((sport == 993) || (dport == 993)
|| (flow->l4.tcp.mail_imap_starttls)
) protocol = NDPI_PROTOCOL_MAIL_IMAPS;
else if((sport == 995) || (dport == 995)) protocol = NDPI_PROTOCOL_MAIL_POPS;
}
break;
}
}
return(protocol);
}
/* **************************************** */
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;
/* 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);
if(flow->l4.tcp.tls.message.buffer == NULL)
return;
#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
}
}
/* **************************************** */
/* Can't call libc functions from kernel space, define some stub instead */
#define ndpi_isalpha(ch) (((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
#define ndpi_isdigit(ch) ((ch) >= '0' && (ch) <= '9')
#define ndpi_isspace(ch) (((ch) >= '\t' && (ch) <= '\r') || ((ch) == ' '))
#define ndpi_isprint(ch) ((ch) >= 0x20 && (ch) <= 0x7e)
#define ndpi_ispunct(ch) (((ch) >= '!' && (ch) <= '/') || \
((ch) >= ':' && (ch) <= '@') || \
((ch) >= '[' && (ch) <= '`') || \
((ch) >= '{' && (ch) <= '~'))
/* **************************************** */
static void cleanupServerName(char *buffer, int buffer_len) {
u_int i;
/* Now all lowecase */
for(i=0; ipayload[offset+4], is_printable = 1;
char *str;
u_int len, j;
if (*rdnSeqBuf_offset >= rdnSeqBuf_len) {
#ifdef DEBUG_TLS
printf("[TLS] %s() [buffer capacity reached][%u]\n",
__FUNCTION__, rdnSeqBuf_len);
#endif
return -1;
}
// packet is truncated... further inspection is not needed
if((offset+4+str_len) >= packet->payload_packet_len)
return(-1);
str = (char*)&packet->payload[offset+5];
len = (u_int)ndpi_min(str_len, buffer_len-1);
strncpy(buffer, str, len);
buffer[len] = '\0';
// check string is printable
for(j = 0; j < len; j++) {
if(!ndpi_isprint(buffer[j])) {
is_printable = 0;
break;
}
}
if(is_printable) {
int rc = snprintf(&rdnSeqBuf[*rdnSeqBuf_offset],
rdnSeqBuf_len-(*rdnSeqBuf_offset),
"%s%s=%s", (*rdnSeqBuf_offset > 0) ? ", " : "",
label, buffer);
if(rc > 0)
(*rdnSeqBuf_offset) += rc;
}
return(is_printable);
}
/* **************************************** */
/* 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 p_offset, u_int16_t certificate_len) {
struct ndpi_packet_struct *packet = &flow->packet;
u_int num_found = 0, i;
char buffer[64] = { '\0' }, rdnSeqBuf[2048] = { '\0' };
u_int rdn_len = 0;
#ifdef DEBUG_TLS
printf("[TLS] %s() [offset: %u][certificate_len: %u]\n", __FUNCTION__, p_offset, certificate_len);
#endif
/* Check after handshake protocol header (5 bytes) and message header (4 bytes) */
for(i = p_offset; i < certificate_len; i++) {
/*
See https://www.ibm.com/support/knowledgecenter/SSFKSJ_7.5.0/com.ibm.mq.sec.doc/q009860_.htm
for X.509 certificate labels
*/
if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x03)) {
/* Common Name */
int rc = extractRDNSequence(packet, i, buffer, sizeof(buffer), rdnSeqBuf, &rdn_len, sizeof(rdnSeqBuf), "CN");
if(rc == -1) break;
#ifdef DEBUG_TLS
printf("[TLS] %s() [%s][%s: %s]\n", __FUNCTION__, (num_found == 0) ? "Subject" : "Issuer", "Common Name", buffer);
#endif
} else if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x06)) {
/* Country */
int rc = extractRDNSequence(packet, i, buffer, sizeof(buffer), rdnSeqBuf, &rdn_len, sizeof(rdnSeqBuf), "C");
if(rc == -1) break;
#ifdef DEBUG_TLS
printf("[TLS] %s() [%s][%s: %s]\n", __FUNCTION__, (num_found == 0) ? "Subject" : "Issuer", "Country", buffer);
#endif
} else if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x07)) {
/* Locality */
int rc = extractRDNSequence(packet, i, buffer, sizeof(buffer), rdnSeqBuf, &rdn_len, sizeof(rdnSeqBuf), "L");
if(rc == -1) break;
#ifdef DEBUG_TLS
printf("[TLS] %s() [%s][%s: %s]\n", __FUNCTION__, (num_found == 0) ? "Subject" : "Issuer", "Locality", buffer);
#endif
} else if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x08)) {
/* State or Province */
int rc = extractRDNSequence(packet, i, buffer, sizeof(buffer), rdnSeqBuf, &rdn_len, sizeof(rdnSeqBuf), "ST");
if(rc == -1) break;
#ifdef DEBUG_TLS
printf("[TLS] %s() [%s][%s: %s]\n", __FUNCTION__, (num_found == 0) ? "Subject" : "Issuer", "State or Province", buffer);
#endif
} else if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x0a)) {
/* Organization Name */
int rc = extractRDNSequence(packet, i, buffer, sizeof(buffer), rdnSeqBuf, &rdn_len, sizeof(rdnSeqBuf), "O");
if(rc == -1) break;
#ifdef DEBUG_TLS
printf("[TLS] %s() [%s][%s: %s]\n", __FUNCTION__, (num_found == 0) ? "Subject" : "Issuer", "Organization Name", buffer);
#endif
} else if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x0b)) {
/* Organization Unit */
int rc = extractRDNSequence(packet, i, buffer, sizeof(buffer), rdnSeqBuf, &rdn_len, sizeof(rdnSeqBuf), "OU");
if(rc == -1) break;
#ifdef DEBUG_TLS
printf("[TLS] %s() [%s][%s: %s]\n", __FUNCTION__, (num_found == 0) ? "Subject" : "Issuer", "Organization Unit", buffer);
#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;
if(num_found == 0) {
num_found++;
#ifdef DEBUG_TLS
printf("[TLS] %s() IssuerDN [%s]\n", __FUNCTION__, rdnSeqBuf);
#endif
if(rdn_len) flow->protos.stun_ssl.ssl.issuerDN = ndpi_strdup(rdnSeqBuf);
rdn_len = 0; /* Reset buffer */
}
if((offset+len) < packet->payload_packet_len) {
char utcDate[32];
#ifdef DEBUG_TLS
u_int j;
printf("[CERTIFICATE] notBefore [len: %u][", len);
for(j=0; jpayload[i+4+j]);
printf("]\n");
#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';
/* 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);
#endif
}
}
offset += len;
if((offset+1) < packet->payload_packet_len) {
len = packet->payload[offset+1];
offset += 2;
if((offset+len) < packet->payload_packet_len) {
u_int32_t time_sec = flow->packet.current_time_ms / 1000;
#ifdef DEBUG_TLS
u_int j;
printf("[CERTIFICATE] notAfter [len: %u][", len);
for(j=0; jpayload[offset+j]);
printf("]\n");
#endif
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);
#endif
}
}
if((time_sec < flow->protos.stun_ssl.ssl.notBefore)
|| (time_sec > flow->protos.stun_ssl.ssl.notAfter))
NDPI_SET_BIT(flow->risk, NDPI_TLS_CERTIFICATE_EXPIRED); /* Certificate expired */
}
}
}
} else if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x1d) && (packet->payload[i+2] == 0x11)) {
/* Organization OID: 2.5.29.17 (subjectAltName) */
u_int8_t matched_name = 0;
#ifdef DEBUG_TLS
printf("******* [TLS] Found subjectAltName\n");
#endif
i += 3 /* skip the initial patten 55 1D 11 */;
i++; /* skip the first type, 0x04 == BIT STRING, and jump to it's length */
if(i < packet->payload_packet_len) {
i += (packet->payload[i] & 0x80) ? (packet->payload[i] & 0x7F) : 0; /* skip BIT STRING length */
if(i < packet->payload_packet_len) {
i += 2; /* skip the second type, 0x30 == SEQUENCE, and jump to it's length */
if(i < packet->payload_packet_len) {
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];
i += 2;
/* The check "len > sizeof(dNSName) - 1" will be always false. If we add it,
the compiler is smart enough to detect it and throws a warning */
if(len == 0 /* Looks something went wrong */)
break;
strncpy(dNSName, (const char*)&packet->payload[i], len);
dNSName[len] = '\0';
cleanupServerName(dNSName, len);
#if DEBUG_TLS
printf("[TLS] dNSName %s [%s]\n", dNSName, flow->protos.stun_ssl.ssl.client_requested_server_name);
#endif
if(matched_name == 0) {
if((dNSName[0] == '*') && strstr(flow->protos.stun_ssl.ssl.client_requested_server_name, &dNSName[1]))
matched_name = 1;
else if(strcmp(flow->protos.stun_ssl.ssl.client_requested_server_name, dNSName) == 0)
matched_name = 1;
}
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 += len;
} else {
#if DEBUG_TLS
printf("[TLS] Leftover %u bytes", packet->payload_packet_len - i);
#endif
break;
}
} else {
break;
}
} /* while */
if(!matched_name)
NDPI_SET_BIT(flow->risk, NDPI_TLS_CERTIFICATE_MISMATCH); /* Certificate mismatch */
}
}
}
}
}
if(rdn_len) flow->protos.stun_ssl.ssl.subjectDN = ndpi_strdup(rdnSeqBuf);
if(flow->protos.stun_ssl.ssl.subjectDN && flow->protos.stun_ssl.ssl.issuerDN
&& (!strcmp(flow->protos.stun_ssl.ssl.subjectDN, flow->protos.stun_ssl.ssl.issuerDN)))
NDPI_SET_BIT(flow->risk, NDPI_TLS_SELFSIGNED_CERTIFICATE);
#if DEBUG_TLS
printf("[TLS] %s() SubjectDN [%s]\n", __FUNCTION__, rdnSeqBuf);
#endif
}
/* **************************************** */
/* 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((packet->payload_packet_len != (length + 4)) || (packet->payload[1] != 0x0)) {
NDPI_SET_BIT(flow->risk, NDPI_MALFORMED_PACKET);
return(-1); /* Invalid length */
}
certificates_length = (packet->payload[4] << 16) + (packet->payload[5] << 8) + packet->payload[6];
if((packet->payload[4] != 0x0) || ((certificates_length+3) != length)) {
NDPI_SET_BIT(flow->risk, NDPI_MALFORMED_PACKET);
return(-2); /* Invalid length */
}
if(!flow->l4.tcp.tls.srv_cert_fingerprint_ctx) {
if((flow->l4.tcp.tls.srv_cert_fingerprint_ctx = (void*)ndpi_malloc(sizeof(SHA1_CTX))) == NULL)
return(-3); /* Not enough memory */
}
/* Now let's process each individual certificates */
while(certificates_offset < certificates_length) {
u_int32_t certificate_len = (packet->payload[certificates_offset] << 16) + (packet->payload[certificates_offset+1] << 8) + packet->payload[certificates_offset+2];
/* Invalid lenght */
if((certificate_len == 0)
|| (packet->payload[certificates_offset] != 0x0)
|| ((certificates_offset+certificate_len) > (4+certificates_length))) {
#ifdef DEBUG_TLS
printf("[TLS] Invalid length [certificate_len: %u][certificates_offset: %u][%u vs %u]\n",
certificate_len, certificates_offset,
(certificates_offset+certificate_len),
certificates_length);
#endif
break;
}
certificates_offset += 3;
#ifdef DEBUG_TLS
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
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;ipayload[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);
flow->l4.tcp.tls.fingerprint_set = 1;
#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");
}
#endif
processCertificateElements(ndpi_struct, flow, certificates_offset, certificate_len);
}
certificates_offset += certificate_len;
}
flow->extra_packets_func = NULL; /* We're good now */
return(1);
}
/* **************************************** */
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 */
/* Important: populate the tls union fields only after
* ndpi_int_tls_add_connection has been called */
if(flow->l4.tcp.tls.hello_processed) {
processCertificate(ndpi_struct, flow);
flow->l4.tcp.tls.certificate_processed = 1;
}
break;
default:
return(-1);
}
return(0);
}
/* **************************************** */
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;
u_int8_t something_went_wrong = 0;
#ifdef DEBUG_TLS_MEMORY
printf("[TLS Mem] ndpi_search_tls_tcp() [payload_packet_len: %u]\n",
packet->payload_packet_len);
#endif
if(packet->payload_packet_len == 0)
return(1); /* Keep working */
ndpi_search_tls_tcp_memory(ndpi_struct, flow);
while(!something_went_wrong) {
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(len == 0) {
something_went_wrong = 1;
break;
}
#ifdef DEBUG_TLS_MEMORY
printf("[TLS Mem] Processing %u bytes message\n", len);
#endif
/* Overwriting packet payload */
p = packet->payload, p_len = packet->payload_packet_len; /* Backup */
/* 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_int32_t block_len = (block[1] << 16) + (block[2] << 8) + block[3];
if((block_len == 0) || (block_len > len) || ((block[1] != 0x0))) {
something_went_wrong = 1;
break;
}
packet->payload = block, packet->payload_packet_len = ndpi_min(block_len+4, flow->l4.tcp.tls.message.buffer_used);
if((processed+packet->payload_packet_len) > len) {
something_went_wrong = 1;
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
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;
if(flow->l4.tcp.tls.message.buffer_used > 0)
memmove(flow->l4.tcp.tls.message.buffer,
&flow->l4.tcp.tls.message.buffer[len],
flow->l4.tcp.tls.message.buffer_used);
else
break;
#ifdef DEBUG_TLS_MEMORY
printf("[TLS Mem] Left memory buffer %u bytes\n", flow->l4.tcp.tls.message.buffer_used);
#endif
}
if(something_went_wrong) {
flow->check_extra_packets = 0, flow->extra_packets_func = NULL;
return(0); /* That's all */
} else
return(1);
}
/* **************************************** */
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("[TLS] %s()\n", __FUNCTION__);
#endif
/* 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("[TLS] No DTLS found\n");
#endif
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return(0); /* Giveup */
}
// handshake_type = packet->payload[13];
handshake_len = (packet->payload[14] << 16) + (packet->payload[15] << 8) + packet->payload[16];
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;
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 */
}
/* **************************************** */
static void tlsInitExtraPacketProcessing(struct ndpi_flow_struct *flow) {
flow->check_extra_packets = 1;
/* 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
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);
ndpi_set_detected_protocol(ndpi_struct, flow, protocol, NDPI_PROTOCOL_TLS);
tlsInitExtraPacketProcessing(flow);
}
/* **************************************** */
/* https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967 */
#define JA3_STR_LEN 1024
#define MAX_NUM_JA3 512
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_int16_t num_elliptic_curve_point_format, elliptic_curve_point_format[MAX_NUM_JA3];
};
/* **************************************** */
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("SSL %s() called\n", __FUNCTION__);
#endif
memset(&ja3, 0, sizeof(ja3));
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) || (packet->payload[1] != 0x0))
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 = packet->tcp ? 38 : 46, extension_len, j;
u_int8_t session_id_len = 0;
if (base_offset < total_len)
session_id_len = packet->payload[base_offset];
#ifdef DEBUG_TLS
printf("SSL [len: %u][handshake_type: %02X]\n", packet->payload_packet_len, handshake_type);
#endif
tls_version = ntohs(*((u_int16_t*)&packet->payload[version_offset]));
flow->protos.stun_ssl.ssl.ssl_version = ja3.tls_handshake_version = tls_version;
if(flow->protos.stun_ssl.ssl.ssl_version < 0x0302) /* TLSv1.1 */
NDPI_SET_BIT(flow->risk, NDPI_TLS_OBSOLETE_VERSION);
if(handshake_type == 0x02 /* Server Hello */) {
int i, rc;
#ifdef DEBUG_TLS
printf("SSL Server Hello [version: 0x%04X]\n", tls_version);
#endif
/*
The server hello decides about the SSL version of this flow
https://networkengineering.stackexchange.com/questions/55752/why-does-wireshark-show-version-tls-1-2-here-instead-of-tls-1-3
*/
if(packet->udp)
offset += 1;
else {
if(tls_version < 0x7F15 /* TLS 1.3 lacks of session id */)
offset += session_id_len+1;
}
if((offset+3) > packet->payload_packet_len)
return(0); /* Not found */
ja3.num_cipher = 1, ja3.cipher[0] = ntohs(*((u_int16_t*)&packet->payload[offset]));
if((flow->protos.stun_ssl.ssl.server_unsafe_cipher = ndpi_is_safe_ssl_cipher(ja3.cipher[0])) == 1)
NDPI_SET_BIT(flow->risk, NDPI_TLS_WEAK_CIPHER);
flow->protos.stun_ssl.ssl.server_cipher = ja3.cipher[0];
#ifdef DEBUG_TLS
printf("TLS [server][session_id_len: %u][cipher: %04X]\n", session_id_len, ja3.cipher[0]);
#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;
#ifdef DEBUG_TLS
printf("TLS [server][extension_len: %u]\n", extension_len);
#endif
offset += 2;
for(i=0; i= (packet->payload_packet_len+4)) break;
extension_id = ntohs(*((u_int16_t*)&packet->payload[offset]));
extension_len = ntohs(*((u_int16_t*)&packet->payload[offset+2]));
if(ja3.num_tls_extension < MAX_NUM_JA3)
ja3.tls_extension[ja3.num_tls_extension++] = extension_id;
#ifdef DEBUG_TLS
printf("TLS [server][extension_id: %u/0x%04X][len: %u]\n",
extension_id, extension_id, extension_len);
#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;
}
}
i += 4 + extension_len, offset += 4 + extension_len;
}
ja3_str_len = snprintf(ja3_str, sizeof(ja3_str), "%u,", ja3.tls_handshake_version);
for(i=0; i 0) ? "-" : "", ja3.cipher[i]);
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 < JA3_STR_LEN) ja3_str_len += rc;
/* ********** */
for(i=0; i 0) ? "-" : "", ja3.tls_extension[i]);
if(rc <= 0) break; else ja3_str_len += rc;
}
#ifdef DEBUG_TLS
printf("TLS [server] %s\n", ja3_str);
#endif
#ifdef DEBUG_TLS
printf("[JA3] Server: %s \n", ja3_str);
#endif
ndpi_MD5Init(&ctx);
ndpi_MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str));
ndpi_MD5Final(md5_hash, &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;
}
#ifdef DEBUG_TLS
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((session_id_len+base_offset+3) > packet->payload_packet_len)
return(0); /* Not found */
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);
#endif
if((cipher_offset+cipher_len) <= total_len) {
for(i=0; ipayload[cipher_offset+i];
#ifdef DEBUG_TLS
printf("Client SSL [cipher suite: %u/0x%04X] [%d/%u]\n", ntohs(*id), ntohs(*id), i, cipher_len);
#endif
if((*id == 0) || (packet->payload[cipher_offset+i] != packet->payload[cipher_offset+i+1])) {
/*
Skip GREASE [https://tools.ietf.org/id/draft-ietf-tls-grease-01.html]
https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967
*/
if(ja3.num_cipher < MAX_NUM_JA3)
ja3.cipher[ja3.num_cipher++] = ntohs(*id);
else {
invalid_ja3 = 1;
#ifdef DEBUG_TLS
printf("Client SSL Invalid cipher %u\n", ja3.num_cipher);
#endif
}
}
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
}
offset = base_offset + session_id_len + cipher_len + 2;
if(offset < total_len) {
u_int16_t compression_len;
u_int16_t extensions_len;
offset += packet->tcp ? 1 : 2;
compression_len = packet->payload[offset];
offset++;
#ifdef DEBUG_TLS
printf("Client SSL [compression_len: %u]\n", compression_len);
#endif
// offset += compression_len + 3;
offset += compression_len;
if(offset < total_len) {
extensions_len = ntohs(*((u_int16_t*)&packet->payload[offset]));
offset += 2;
#ifdef 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;
while(extension_offset < extensions_len) {
u_int16_t extension_id, extension_len, extn_off = offset+extension_offset;
extension_id = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset]));
extension_offset += 2;
extension_len = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset]));
extension_offset += 2;
#ifdef DEBUG_TLS
printf("Client SSL [extension_id: %u][extension_len: %u]\n", extension_id, extension_len);
#endif
if((extension_id == 0) || (packet->payload[extn_off] != packet->payload[extn_off+1])) {
/* Skip GREASE */
if(ja3.num_tls_extension < MAX_NUM_JA3)
ja3.tls_extension[ja3.num_tls_extension++] = extension_id;
else {
invalid_ja3 = 1;
#ifdef DEBUG_TLS
printf("Client SSL Invalid extensions %u\n", ja3.num_tls_extension);
#endif
}
}
if(extension_id == 0 /* server name */) {
u_int16_t len;
#ifdef DEBUG_TLS
printf("[TLS] Extensions: found server name\n");
#endif
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+extension_offset+5+len) <= packet->payload_packet_len) {
strncpy(buffer, (char*)&packet->payload[offset+extension_offset+5], len);
buffer[len] = '\0';
cleanupServerName(buffer, sizeof(buffer));
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;
ndpi_check_dga_name(ndpi_struct, flow, flow->protos.stun_ssl.ssl.client_requested_server_name);
} else {
#ifdef DEBUG_TLS
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;
#ifdef DEBUG_TLS
printf("Client SSL [EllipticCurveGroups: len=%u]\n", extension_len);
#endif
if((s_offset+extension_len-2) <= total_len) {
for(i=0; ipayload[s_offset+i]));
#ifdef 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
}
}
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);
#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-1) <= total_len) {
for(i=0; ipayload[s_offset+i];
#ifdef DEBUG_TLS
printf("Client SSL [EllipticCurveFormat: %u]\n", s_group);
#endif
if(ja3.num_elliptic_curve_point_format < MAX_NUM_JA3)
ja3.elliptic_curve_point_format[ja3.num_elliptic_curve_point_format++] = s_group;
else {
invalid_ja3 = 1;
#ifdef DEBUG_TLS
printf("Client SSL Invalid num elliptic %u\n", ja3.num_elliptic_curve_point_format);
#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 == 16 /* application_layer_protocol_negotiation */) {
u_int16_t s_offset = offset+extension_offset;
u_int16_t tot_alpn_len = ntohs(*((u_int16_t*)&packet->payload[s_offset]));
char alpn_str[256];
u_int8_t alpn_str_len = 0;
#ifdef DEBUG_TLS
printf("Client SSL [ALPN: block_len=%u/len=%u]\n", extension_len, tot_alpn_len);
#endif
s_offset += 2;
tot_alpn_len += s_offset;
while(s_offset < tot_alpn_len && s_offset < total_len) {
u_int8_t alpn_i, alpn_len = packet->payload[s_offset++];
if((s_offset + alpn_len) <= tot_alpn_len) {
#ifdef DEBUG_TLS
printf("Client SSL [ALPN: %u]\n", alpn_len);
#endif
if((alpn_str_len+alpn_len+1) < (sizeof(alpn_str)-1)) {
if(alpn_str_len > 0) {
alpn_str[alpn_str_len] = ',';
alpn_str_len++;
}
for(alpn_i=0; alpn_ipayload[s_offset+alpn_i];
s_offset += alpn_len, alpn_str_len += alpn_len;;
} else
break;
} else
break;
} /* while */
alpn_str[alpn_str_len] = '\0';
#ifdef DEBUG_TLS
printf("Client SSL [ALPN: %s][len: %u]\n", alpn_str, alpn_str_len);
#endif
if(flow->protos.stun_ssl.ssl.alpn == NULL)
flow->protos.stun_ssl.ssl.alpn = ndpi_strdup(alpn_str);
} else if(extension_id == 43 /* supported versions */) {
u_int16_t s_offset = offset+extension_offset;
u_int8_t version_len = packet->payload[s_offset];
char version_str[256];
u_int8_t version_str_len = 0;
version_str[0] = 0;
#ifdef DEBUG_TLS
printf("Client SSL [TLS version len: %u]\n", version_len);
#endif
if(version_len == (extension_len-1)) {
u_int8_t j;
s_offset++;
// careful not to overflow and loop forever with u_int8_t
for(j=0; j+1payload[s_offset+j]));
u_int8_t unknown_tls_version;
#ifdef DEBUG_TLS
printf("Client SSL [TLS version: %s/0x%04X]\n",
ndpi_ssl_version2str(tls_version, &unknown_tls_version), tls_version);
#endif
if((version_str_len+8) < sizeof(version_str)) {
int rc = snprintf(&version_str[version_str_len],
sizeof(version_str) - version_str_len, "%s%s",
(version_str_len > 0) ? "," : "",
ndpi_ssl_version2str(tls_version, &unknown_tls_version));
if(rc <= 0)
break;
else
version_str_len += rc;
}
}
if(flow->protos.stun_ssl.ssl.tls_supported_versions == NULL)
flow->protos.stun_ssl.ssl.tls_supported_versions = ndpi_strdup(version_str);
}
} else if(extension_id == 65486 /* encrypted server name */) {
/*
- https://tools.ietf.org/html/draft-ietf-tls-esni-06
- https://blog.cloudflare.com/encrypted-sni/
*/
u_int16_t e_offset = offset+extension_offset;
u_int16_t initial_offset = e_offset;
u_int16_t e_sni_len, cipher_suite = ntohs(*((u_int16_t*)&packet->payload[e_offset]));
flow->protos.stun_ssl.ssl.encrypted_sni.cipher_suite = cipher_suite;
e_offset += 2; /* Cipher suite len */
/* Key Share Entry */
e_offset += 2; /* Group */
e_offset += ntohs(*((u_int16_t*)&packet->payload[e_offset])) + 2; /* Lenght */
if((e_offset+4) < packet->payload_packet_len) {
/* Record Digest */
e_offset += ntohs(*((u_int16_t*)&packet->payload[e_offset])) + 2; /* Lenght */
if((e_offset+4) < packet->payload_packet_len) {
e_sni_len = ntohs(*((u_int16_t*)&packet->payload[e_offset]));
e_offset += 2;
if((e_offset+e_sni_len-extension_len-initial_offset) >= 0) {
#ifdef DEBUG_ENCRYPTED_SNI
printf("Client SSL [Encrypted Server Name len: %u]\n", e_sni_len);
#endif
if(flow->protos.stun_ssl.ssl.encrypted_sni.esni == NULL) {
flow->protos.stun_ssl.ssl.encrypted_sni.esni = (char*)ndpi_malloc(e_sni_len*2+1);
if(flow->protos.stun_ssl.ssl.encrypted_sni.esni) {
u_int16_t i, off;
for(i=e_offset, off=0; i<(e_offset+e_sni_len); i++) {
int rc = sprintf(&flow->protos.stun_ssl.ssl.encrypted_sni.esni[off], "%02X", packet->payload[i] & 0XFF);
if(rc <= 0) {
flow->protos.stun_ssl.ssl.encrypted_sni.esni[off] = '\0';
break;
} else
off += rc;
}
}
}
}
}
}
}
extension_offset += extension_len; /* Move to the next extension */
#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 0) ? "-" : "", ja3.cipher[i]);
if(rc > 0 && ja3_str_len + rc < JA3_STR_LEN) 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 < JA3_STR_LEN) ja3_str_len += rc;
/* ********** */
for(i=0; i 0) ? "-" : "", ja3.tls_extension[i]);
if(rc > 0 && ja3_str_len + rc < JA3_STR_LEN) 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 < JA3_STR_LEN) ja3_str_len += rc;
/* ********** */
for(i=0; i 0) ? "-" : "", ja3.elliptic_curve[i]);
if(rc > 0 && ja3_str_len + rc < JA3_STR_LEN) 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 < JA3_STR_LEN) ja3_str_len += rc;
for(i=0; i 0) ? "-" : "", ja3.elliptic_curve_point_format[i]);
if(rc > 0 && ja3_str_len + rc < JA3_STR_LEN) ja3_str_len += rc; else break;
}
#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);
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
}
/* Before returning to the caller we need to make a final check */
if((flow->protos.stun_ssl.ssl.ssl_version >= 0x0303) /* >= TLSv1.2 */
&& (flow->protos.stun_ssl.ssl.alpn == NULL) /* No ALPN */) {
NDPI_SET_BIT(flow->risk, NDPI_TLS_NOT_CARRYING_HTTPS);
}
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); /* Not found */
}
/* **************************************** */
static void ndpi_search_tls_wrapper(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow) {
struct ndpi_packet_struct *packet = &flow->packet;
#ifdef DEBUG_TLS
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)
ndpi_search_tls_udp(ndpi_struct, flow);
else
ndpi_search_tls_tcp(ndpi_struct, flow);
}
/* **************************************** */
void init_tls_dissector(struct ndpi_detection_module_struct *ndpi_struct,
u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask) {
ndpi_set_bitmask_protocol_detection("TLS", ndpi_struct, detection_bitmask, *id,
NDPI_PROTOCOL_TLS,
ndpi_search_tls_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("TLS", ndpi_struct, detection_bitmask, *id,
NDPI_PROTOCOL_TLS,
ndpi_search_tls_wrapper,
NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_UDP_WITH_PAYLOAD,
SAVE_DETECTION_BITMASK_AS_UNKNOWN,
ADD_TO_DETECTION_BITMASK);
*id += 1;
}