/src/ndpi/src/lib/protocols/telnet.c
Line | Count | Source |
1 | | /* |
2 | | * telnet.c |
3 | | * |
4 | | * Copyright (C) 2011-25 - ntop.org |
5 | | * Copyright (C) 2009-11 - ipoque GmbH |
6 | | * |
7 | | * This file is part of nDPI, an open source deep packet inspection |
8 | | * library based on the OpenDPI and PACE technology by ipoque GmbH |
9 | | * |
10 | | * nDPI is free software: you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation, either version 3 of the License, or |
13 | | * (at your option) any later version. |
14 | | * |
15 | | * nDPI is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public License |
21 | | * along with nDPI. If not, see <http://www.gnu.org/licenses/>. |
22 | | * |
23 | | */ |
24 | | |
25 | | |
26 | | #include "ndpi_protocol_ids.h" |
27 | | |
28 | | #define NDPI_CURRENT_PROTO NDPI_PROTOCOL_TELNET |
29 | | |
30 | | #include "ndpi_api.h" |
31 | | #include "ndpi_private.h" |
32 | | |
33 | | /* #define TELNET_DEBUG 1 */ |
34 | | |
35 | | /* ************************************************************************ */ |
36 | | |
37 | | static int search_telnet_again(struct ndpi_detection_module_struct *ndpi_struct, |
38 | 0 | struct ndpi_flow_struct *flow) { |
39 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
40 | 0 | int i; |
41 | |
|
42 | | #ifdef TELNET_DEBUG |
43 | | printf("==> %s() [%.*s][direction: %u]\n", __FUNCTION__, packet->payload_packet_len, |
44 | | packet->payload, packet->packet_direction); |
45 | | #endif |
46 | | |
47 | 0 | if((packet->payload == NULL) |
48 | 0 | || (packet->payload_packet_len == 0) |
49 | 0 | || (packet->payload[0] == 0xFF)) |
50 | 0 | return(1); |
51 | | |
52 | 0 | if(flow->protos.telnet.username_detected) { |
53 | 0 | if((!flow->protos.telnet.password_found) |
54 | 0 | && (packet->payload_packet_len > 9)) { |
55 | | |
56 | 0 | if(strncasecmp((char*)packet->payload, "password:", 9) == 0) { |
57 | 0 | flow->protos.telnet.password_found = 1; |
58 | 0 | } |
59 | |
|
60 | 0 | return(1); |
61 | 0 | } |
62 | | |
63 | 0 | if(packet->payload[0] == '\r' || packet->payload[0] == '\n') { |
64 | 0 | if(!flow->protos.telnet.password_found) |
65 | 0 | return(1); |
66 | | |
67 | 0 | flow->protos.telnet.password_detected = 1; |
68 | 0 | ndpi_set_risk(ndpi_struct, flow, NDPI_CLEAR_TEXT_CREDENTIALS, "Found password"); |
69 | 0 | flow->protos.telnet.password[flow->protos.telnet.character_id] = '\0'; |
70 | 0 | return(0); |
71 | 0 | } |
72 | | |
73 | 0 | if(packet->packet_direction == 0) /* client -> server */ { |
74 | 0 | for(i=0; i<packet->payload_packet_len; i++) { |
75 | 0 | if(flow->protos.telnet.character_id < (sizeof(flow->protos.telnet.password)-1)) |
76 | 0 | flow->protos.telnet.password[flow->protos.telnet.character_id++] = packet->payload[i]; |
77 | 0 | } |
78 | 0 | } |
79 | |
|
80 | 0 | return(1); |
81 | 0 | } |
82 | | |
83 | 0 | if((!flow->protos.telnet.username_found) |
84 | 0 | && (packet->payload_packet_len > 6)) { |
85 | |
|
86 | 0 | if(strncasecmp((char*)packet->payload, "login:", 6) == 0) { |
87 | 0 | flow->protos.telnet.username_found = 1; |
88 | 0 | } |
89 | |
|
90 | 0 | return(1); |
91 | 0 | } |
92 | | |
93 | 0 | if(packet->payload[0] == '\r' || packet->payload[0] == '\n') { |
94 | 0 | char buf[64]; |
95 | | |
96 | 0 | flow->protos.telnet.username_detected = 1; |
97 | 0 | flow->protos.telnet.username[flow->protos.telnet.character_id] = '\0'; |
98 | 0 | flow->protos.telnet.character_id = 0; |
99 | |
|
100 | 0 | snprintf(buf, sizeof(buf), "Found Telnet username (%s)", |
101 | 0 | flow->protos.telnet.username); |
102 | 0 | ndpi_set_risk(ndpi_struct, flow, NDPI_CLEAR_TEXT_CREDENTIALS, buf); |
103 | |
|
104 | 0 | return(1); |
105 | 0 | } |
106 | | |
107 | 0 | for(i=0; i<packet->payload_packet_len; i++) { |
108 | 0 | if(packet->packet_direction == 0) /* client -> server */ { |
109 | 0 | if(flow->protos.telnet.character_id < (sizeof(flow->protos.telnet.username)-1)) |
110 | 0 | { |
111 | 0 | if (i>=packet->payload_packet_len-2 && |
112 | 0 | (packet->payload[i] == '\r' || packet->payload[i] == '\n')) |
113 | 0 | { |
114 | 0 | continue; |
115 | 0 | } |
116 | 0 | else if (ndpi_isprint(packet->payload[i]) == 0) |
117 | 0 | { |
118 | 0 | flow->protos.telnet.username[flow->protos.telnet.character_id++] = '?'; |
119 | 0 | } else { |
120 | 0 | flow->protos.telnet.username[flow->protos.telnet.character_id++] = packet->payload[i]; |
121 | 0 | } |
122 | 0 | } |
123 | 0 | } |
124 | 0 | } |
125 | | |
126 | | /* Possibly more processing */ |
127 | 0 | return(1); |
128 | 0 | } |
129 | | |
130 | | /* ************************************************************************ */ |
131 | | |
132 | | static void ndpi_int_telnet_add_connection(struct ndpi_detection_module_struct |
133 | 0 | *ndpi_struct, struct ndpi_flow_struct *flow) { |
134 | 0 | flow->max_extra_packets_to_check = 64; |
135 | 0 | flow->extra_packets_func = search_telnet_again; |
136 | |
|
137 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_TELNET, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
138 | 0 | } |
139 | | |
140 | | /* ************************************************************************ */ |
141 | | |
142 | | #if !defined(WIN32) |
143 | | static inline |
144 | | #elif defined(MINGW_GCC) |
145 | | __mingw_forceinline static |
146 | | #else |
147 | | __forceinline static |
148 | | #endif |
149 | 197 | u_int8_t search_iac(struct ndpi_detection_module_struct *ndpi_struct) { |
150 | 197 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
151 | | |
152 | 197 | u_int16_t a; |
153 | | |
154 | | #ifdef TELNET_DEBUG |
155 | | printf("==> %s()\n", __FUNCTION__); |
156 | | #endif |
157 | | |
158 | 197 | if(packet->payload_packet_len < 3) |
159 | 0 | return(0); |
160 | | |
161 | 197 | if(!((packet->payload[0] == 0xff) |
162 | 5 | && (packet->payload[1] > 0xf9) |
163 | 3 | && (packet->payload[1] != 0xff) |
164 | 1 | && (packet->payload[2] < 0x28))) |
165 | 196 | return(0); |
166 | | |
167 | 1 | a = 3; |
168 | | |
169 | 3 | while (a < packet->payload_packet_len - 2) { |
170 | | // commands start with a 0xff byte followed by a command byte >= 0xf0 and < 0xff |
171 | | // command bytes 0xfb to 0xfe are followed by an option byte <= 0x28 |
172 | 3 | if(!(packet->payload[a] != 0xff || |
173 | 1 | (packet->payload[a] == 0xff && (packet->payload[a + 1] >= 0xf0) && (packet->payload[a + 1] <= 0xfa)) || |
174 | 1 | (packet->payload[a] == 0xff && (packet->payload[a + 1] >= 0xfb) && (packet->payload[a + 1] != 0xff) |
175 | 0 | && (packet->payload[a + 2] <= 0x28)))) |
176 | 1 | return(0); |
177 | | |
178 | 2 | a += 3; |
179 | 2 | } |
180 | | |
181 | 0 | return 1; |
182 | 1 | } |
183 | | |
184 | | /* ************************************************************************ */ |
185 | | |
186 | | /* this detection also works asymmetrically */ |
187 | | static void ndpi_search_telnet_tcp(struct ndpi_detection_module_struct *ndpi_struct, |
188 | 197 | struct ndpi_flow_struct *flow) { |
189 | 197 | NDPI_LOG_DBG(ndpi_struct, "search telnet\n"); |
190 | | |
191 | 197 | if(search_iac(ndpi_struct) == 1) { |
192 | 0 | NDPI_LOG_INFO(ndpi_struct, "found telnet\n"); |
193 | 0 | ndpi_int_telnet_add_connection(ndpi_struct, flow); |
194 | 0 | return; |
195 | 0 | } |
196 | | |
197 | 197 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
198 | | |
199 | 197 | return; |
200 | 197 | } |
201 | | |
202 | | |
203 | | void init_telnet_dissector(struct ndpi_detection_module_struct *ndpi_struct) |
204 | 838 | { |
205 | 838 | register_dissector("Telnet", ndpi_struct, |
206 | 838 | ndpi_search_telnet_tcp, |
207 | 838 | NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, |
208 | 838 | 1, NDPI_PROTOCOL_TELNET); |
209 | 838 | } |