/* * openvpn.c * * Copyright (C) 2011-22 - ntop.org * * * 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 #include #include "app_l7_protocol.h" /* * 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 * */ #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_ACK_V1 (0x05 << 3) #define P_CONTROL_V1 (0x04 << 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_HMAC_NONE 0 #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) #define P_HARD_RESET_CLIENT_MAX_COUNT 5 static inline uint32_t get_packet_id(const uint8_t * payload, uint8_t hms) { return(ntohl(*(uint32_t*)(payload + P_HARD_RESET_PACKET_ID_OFFSET(hms)))); } static inline int8_t check_pkid_and_detect_hmac_size(const uint8_t * payload) { // try to guess if((int)get_packet_id(payload, P_HMAC_160) != 0) return P_HMAC_160; if((int)get_packet_id(payload, P_HMAC_128) != 0 ) return P_HMAC_128; if((int)get_packet_id(payload, P_HMAC_NONE) != 0) return P_HMAC_NONE; return(-1); } int app_identify_guess_openvpn(const unsigned char *payload, int payload_len, int c2s_pkt_cnt, int s2c_pkt_cnt, struct ovpn_ctx *ovpn, int l4_is_tcp, int curdir_is_c2s) { const uint8_t * session_remote; uint8_t opcode; uint8_t alen; int8_t hmac_size; int8_t failed = 0; if (c2s_pkt_cnt > 5) { if (ovpn->contain_local_session_id_pkt_num == P_HARD_RESET_CLIENT_MAX_COUNT - 1 && ovpn->valid_opcode_cnt == P_HARD_RESET_CLIENT_MAX_COUNT) return 1; return -1; } if (payload_len >= 40) { // skip openvpn TCP transport packet size if (l4_is_tcp) payload += 2, payload_len -= 2; opcode = payload[0] & P_OPCODE_MASK; if (curdir_is_c2s == 1 && (opcode == P_CONTROL_HARD_RESET_CLIENT_V1 || opcode == P_CONTROL_HARD_RESET_CLIENT_V2 || opcode == P_ACK_V1 || opcode == P_CONTROL_V1)) { ovpn->valid_opcode_cnt++; } // for UDP, trying to identify by opcode and first packet length if (l4_is_tcp == 0) { if ((c2s_pkt_cnt == 1 || s2c_pkt_cnt == 1) && (((payload_len == 112) && ((opcode == 168) || (opcode == 192))) || ((payload_len == 80) && ((opcode == 184) || (opcode == 88) || (opcode == 160) || (opcode == 168) || (opcode == 200))))) { return 1; } } if(s2c_pkt_cnt >= 1 && curdir_is_c2s == 0)return 0; if (curdir_is_c2s == 1 && c2s_pkt_cnt <= P_HARD_RESET_CLIENT_MAX_COUNT && (opcode == P_CONTROL_HARD_RESET_CLIENT_V1 || opcode == P_CONTROL_HARD_RESET_CLIENT_V2 || opcode == P_ACK_V1 || opcode == P_CONTROL_V1)) { if (check_pkid_and_detect_hmac_size(payload) >= 0) { if (c2s_pkt_cnt > 1) { if (memcmp(&ovpn->local_session_id, payload + 1, 8) == 0) { ovpn->contain_local_session_id_pkt_num++; } } memcpy(&ovpn->local_session_id, payload + 1, 8); } } else if (c2s_pkt_cnt >= 1 && c2s_pkt_cnt <= P_HARD_RESET_CLIENT_MAX_COUNT && (opcode == P_CONTROL_HARD_RESET_SERVER_V1 || opcode == P_CONTROL_HARD_RESET_SERVER_V2)) { hmac_size = check_pkid_and_detect_hmac_size(payload); if (hmac_size >= 0) { u_int16_t offset = P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size); alen = payload[offset]; if (alen > 0) { offset += 1 + alen * 4; if ((offset + 8) <= payload_len) { session_remote = &payload[offset]; if (memcmp(&ovpn->local_session_id, session_remote, 8) == 0) { return 1; } else { failed = 1; } } else failed = 1; } else failed = 1; } else failed = 1; } else failed = 1; if (failed) return -1; } return 0; }