diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/include/ndpi_typedefs.h | 12 | ||||
-rw-r--r-- | src/lib/protocols/openvpn.c | 137 |
2 files changed, 95 insertions, 54 deletions
diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index c5275c3d8..2806826f3 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -753,7 +753,7 @@ typedef struct ndpi_proto { #define NDPI_PROTOCOL_NULL { NDPI_PROTOCOL_UNKNOWN , NDPI_PROTOCOL_UNKNOWN } struct ndpi_detection_module_struct { - + NDPI_PROTOCOL_BITMASK detection_bitmask; NDPI_PROTOCOL_BITMASK generic_http_packet_bitmask; @@ -806,7 +806,7 @@ struct ndpi_detection_module_struct { content_automa, /* Used for HTTP subprotocol_detection */ subprotocol_automa, /* Used for HTTP subprotocol_detection */ bigrams_automa, impossible_bigrams_automa; /* TOR */ - + /* IP-based protocol detection */ void *protocols_ptree; @@ -850,7 +850,7 @@ struct ndpi_detection_module_struct { ndpi_proto_defaults_t proto_defaults[NDPI_MAX_SUPPORTED_PROTOCOLS+NDPI_MAX_NUM_CUSTOM_PROTOCOLS]; - u_int8_t http_dont_dissect_response:1, dns_dissect_response:1, + u_int8_t http_dont_dissect_response:1, dns_dissect_response:1, direction_detect_disable:1; /* disable internal detection of packet direction */ }; @@ -915,7 +915,7 @@ struct ndpi_flow_struct { u_int8_t num_queries, num_answers, reply_code; u_int16_t query_type, query_class, rsp_type; } dns; - + struct { u_int8_t request_code; u_int8_t version; @@ -1006,6 +1006,10 @@ struct ndpi_flow_struct { #ifdef NDPI_PROTOCOL_STARCRAFT u_int32_t starcraft_udp_stage : 3; // 0-7 #endif +#ifdef NDPI_PROTOCOL_OPENVPN + u_int8_t ovpn_session_id[8]; + u_int8_t ovpn_counter; +#endif /* internal structures to save functions calls */ struct ndpi_packet_struct packet; diff --git a/src/lib/protocols/openvpn.c b/src/lib/protocols/openvpn.c index 9005dc3ff..9f9449df7 100644 --- a/src/lib/protocols/openvpn.c +++ b/src/lib/protocols/openvpn.c @@ -1,68 +1,105 @@ /* - * h323.c + * openvpn.c * - * Copyright (C) 2013 Remy Mudingay <mudingay@ill.fr> + * Copyright (C) 2011-16 - ntop.org + * + * OpenVPN TCP / UDP Detection - 128/160 hmac + * + * Detection based upon these openvpn protocol properties: + * - opcode + * - packet ID + * - session ID + * + * Two (good) packets are needed to perform detection. + * - First packet from client: save session ID + * - Second packet from server: report saved session ID + * + * TODO + * - Support PSK only mode (instead of TLS) + * - Support PSK + TLS mode (PSK used for early authentication) + * - TLS certificate extraction * */ #include "ndpi_api.h" - #ifdef NDPI_PROTOCOL_OPENVPN +#define P_CONTROL_HARD_RESET_CLIENT_V1 (0x01 << 3) +#define P_CONTROL_HARD_RESET_CLIENT_V2 (0x07 << 3) +#define P_CONTROL_HARD_RESET_SERVER_V1 (0x02 << 3) +#define P_CONTROL_HARD_RESET_SERVER_V2 (0x08 << 3) +#define P_OPCODE_MASK 0xF8 +#define P_SHA1_HMAC_SIZE 20 +#define P_HMAC_128 16 // (RSA-)MD5, (RSA-)MD4, ..others +#define P_HMAC_160 20 // (RSA-|DSA-)SHA(1), ..others, SHA1 is openvpn default +#define P_HARD_RESET_PACKET_ID_OFFSET(hmac_size) (9 + hmac_size) +#define P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size) (P_HARD_RESET_PACKET_ID_OFFSET(hmac_size) + 8) + +static inline u_int32_t get_packet_id(const u_int8_t * payload, u_int8_t hms) { + return be32toh(*(u_int32_t*)(payload + P_HARD_RESET_PACKET_ID_OFFSET(hms))); +} + +static inline int8_t check_pkid_and_detect_hmac_size(const u_int8_t * payload) { + // try to guess + if (get_packet_id(payload, P_HMAC_160) == 1) + return P_HMAC_160; + if (get_packet_id(payload, P_HMAC_128) == 1) + return P_HMAC_128; + return -1; +} + void ndpi_search_openvpn(struct ndpi_detection_module_struct* ndpi_struct, struct ndpi_flow_struct* flow) { struct ndpi_packet_struct* packet = &flow->packet; - u_int16_t dport = 0, sport = 0; - - if (packet->udp != NULL) { - - sport = ntohs(packet->udp->source), dport = ntohs(packet->udp->dest); - - if ((packet->payload_packet_len >= 25) && (sport == 443 || dport == 443) && - (packet->payload[0] == 0x17 && packet->payload[1] == 0x01 && - packet->payload[2] == 0x00 && packet->payload[3] == 0x00)) { - NDPI_LOG(NDPI_PROTOCOL_OPENVPN, ndpi_struct, NDPI_LOG_DEBUG, - "found openvpn udp 443.\n"); - ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN); - return; - } - - if ( ( (packet->payload_packet_len > 40) || - (packet->payload_packet_len <= 14) ) && // hard-reset - (sport == 1194 || dport == 1194) && - (packet->payload[0] == 0x30 || packet->payload[0] == 0x31 || - packet->payload[0] == 0x32 || packet->payload[0] == 0x33 || - packet->payload[0] == 0x34 || packet->payload[0] == 0x35 || - packet->payload[0] == 0x36 || packet->payload[0] == 0x37 || - packet->payload[0] == 0x38 || packet->payload[0] == 0x39)) { - NDPI_LOG(NDPI_PROTOCOL_OPENVPN, ndpi_struct, NDPI_LOG_DEBUG, - "found openvpn broadcast udp STD.\n"); - ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN); - return; - } - - } - - if (packet->tcp != NULL) { - - sport = ntohs(packet->tcp->source), dport = ntohs(packet->tcp->dest); - - if ((packet->payload_packet_len >= 40) && - (sport == 1194 || dport == 1194) && - ((packet->payload[0] == 0x00) && (packet->payload[1] == 0x2a) && - (packet->payload[2] == 0x38))) { - NDPI_LOG(NDPI_PROTOCOL_OPENVPN, ndpi_struct, NDPI_LOG_DEBUG, - "found openvpn broadcast udp STD.\n"); - ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN); - return; - } + const u_int8_t * ovpn_payload = packet->payload; + const u_int8_t * session_remote; + u_int8_t opcode; + u_int8_t alen; + int8_t hmac_size; + + if (packet->payload_packet_len >= 40) { + // skip openvpn TCP transport packet size + if (packet->tcp != NULL) + ovpn_payload += 2; + + opcode = ovpn_payload[0] & P_OPCODE_MASK; + + if (flow->ovpn_counter == 0 && (opcode == P_CONTROL_HARD_RESET_CLIENT_V1 || + opcode == P_CONTROL_HARD_RESET_CLIENT_V2)) { + + if (check_pkid_and_detect_hmac_size(ovpn_payload) > 0) { + memcpy(flow->ovpn_session_id, ovpn_payload+1, 8); + + NDPI_LOG(NDPI_PROTOCOL_OPENVPN, ndpi_struct, NDPI_LOG_DEBUG, + "session key: %02x%02x%02x%02x%02x%02x%02x%02x\n", + flow->ovpn_session_id[0], flow->ovpn_session_id[1], flow->ovpn_session_id[2], flow->ovpn_session_id[3], + flow->ovpn_session_id[4], flow->ovpn_session_id[5], flow->ovpn_session_id[6], flow->ovpn_session_id[7]); + } + } else if (flow->ovpn_counter == 1 && (opcode == P_CONTROL_HARD_RESET_SERVER_V1 || + opcode == P_CONTROL_HARD_RESET_SERVER_V2)) { + + hmac_size = check_pkid_and_detect_hmac_size(ovpn_payload); + + if (hmac_size > 0) { + alen = ovpn_payload[P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size)]; + session_remote = ovpn_payload + P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size) + 1 + alen * 4; + + if (memcmp(flow->ovpn_session_id, session_remote, 8) == 0) + ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN); + else + NDPI_LOG(NDPI_PROTOCOL_OPENVPN, ndpi_struct, NDPI_LOG_DEBUG, + "key mismatch: %02x%02x%02x%02x%02x%02x%02x%02x\n", + session_remote[0], session_remote[1], session_remote[2], session_remote[3], + session_remote[4], session_remote[5], session_remote[6], session_remote[7]); + } + } else if (flow->ovpn_counter >= 2) + NDPI_ADD_PROTOCOL_TO_BITMASK(flow->excluded_protocol_bitmask, NDPI_PROTOCOL_OPENVPN); + + flow->ovpn_counter++; } - - NDPI_ADD_PROTOCOL_TO_BITMASK(flow->excluded_protocol_bitmask, NDPI_PROTOCOL_OPENVPN); } - void init_openvpn_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask) { ndpi_set_bitmask_protocol_detection("OpenVPN", ndpi_struct, detection_bitmask, *id, |