/src/ndpi/src/lib/protocols/ssh.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * ssh.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 | | #include "ndpi_protocol_ids.h" |
26 | | |
27 | | #define NDPI_CURRENT_PROTO NDPI_PROTOCOL_SSH |
28 | | |
29 | | #include "ndpi_api.h" |
30 | | #include "ndpi_private.h" |
31 | | #include "ndpi_md5.h" |
32 | | |
33 | | #include <string.h> |
34 | | |
35 | | /* |
36 | | HASSH - https://github.com/salesforce/hassh |
37 | | |
38 | | https://github.com/salesforce/hassh/blob/master/python/hassh.py |
39 | | |
40 | | [server] |
41 | | skex = packet.ssh.kex_algorithms |
42 | | seastc = packet.ssh.encryption_algorithms_server_to_client |
43 | | smastc = packet.ssh.mac_algorithms_server_to_client |
44 | | scastc = packet.ssh.compression_algorithms_server_to_client |
45 | | hasshs_str = ';'.join([skex, seastc, smastc, scastc]) |
46 | | |
47 | | [client] |
48 | | ckex = packet.ssh.kex_algorithms |
49 | | ceacts = packet.ssh.encryption_algorithms_client_to_server |
50 | | cmacts = packet.ssh.mac_algorithms_client_to_server |
51 | | ccacts = packet.ssh.compression_algorithms_client_to_server |
52 | | hassh_str = ';'.join([ckex, ceacts, cmacts, ccacts]) |
53 | | |
54 | | NOTE |
55 | | THe ECDSA key fingerprint is SHA256 -> ssh.kex.h_sig (wireshark) |
56 | | is in the Message Code: Diffie-Hellman Key Exchange Reply (31) |
57 | | that usually is packet 14 |
58 | | */ |
59 | | |
60 | | // #define SSH_DEBUG 1 |
61 | | |
62 | | static void ndpi_search_ssh_tcp(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow); |
63 | | |
64 | | typedef struct { |
65 | | const char *signature; |
66 | | u_int16_t major, minor, patch; |
67 | | } ssh_pattern; |
68 | | |
69 | | /* ************************************************************************ */ |
70 | | |
71 | | static void ssh_analyze_signature_version(struct ndpi_detection_module_struct *ndpi_struct, |
72 | | struct ndpi_flow_struct *flow, |
73 | | char *str_to_check, |
74 | 0 | u_int8_t is_client_signature) { |
75 | 0 | u_int i; |
76 | 0 | u_int8_t obsolete_ssh_version = 0; |
77 | 0 | const ssh_pattern ssh_servers_strings[] = |
78 | 0 | { |
79 | 0 | { (const char*)"SSH-%*f-OpenSSH_%d.%d.%d", 7, 0, 0 }, /* OpenSSH */ |
80 | 0 | { (const char*)"SSH-%*f-APACHE-SSHD-%d.%d.%d", 2, 5, 1 }, /* Apache MINA SSHD */ |
81 | 0 | { (const char*)"SSH-%*f-FileZilla_%d.%d.%d", 3, 40, 0 }, /* FileZilla SSH*/ |
82 | 0 | { (const char*)"SSH-%*f-paramiko_%d.%d.%d", 2, 4, 0 }, /* Paramiko SSH */ |
83 | 0 | { (const char*)"SSH-%*f-dropbear_%d.%d", 2020, 0, 0 }, /* Dropbear SSH */ |
84 | 0 | { NULL, 0, 0, 0 } |
85 | 0 | }; |
86 | |
|
87 | 0 | for(i = 0; ssh_servers_strings[i].signature != NULL; i++) { |
88 | 0 | int matches; |
89 | 0 | int major = 0; |
90 | 0 | int minor = 0; |
91 | 0 | int patch = 0; |
92 | 0 | matches = sscanf(str_to_check, ssh_servers_strings[i].signature, &major, &minor, &patch); |
93 | |
|
94 | 0 | if(matches == 3 || matches == 2) { |
95 | | /* checking if is an old version */ |
96 | 0 | if(major < ssh_servers_strings[i].major) |
97 | 0 | obsolete_ssh_version = 1; |
98 | 0 | else if(major == ssh_servers_strings[i].major) { |
99 | 0 | if(minor < ssh_servers_strings[i].minor) |
100 | 0 | obsolete_ssh_version = 1; |
101 | 0 | else if(minor == ssh_servers_strings[i].minor) |
102 | 0 | if(patch < ssh_servers_strings[i].patch) |
103 | 0 | obsolete_ssh_version = 1; |
104 | 0 | } |
105 | |
|
106 | | #ifdef SSH_DEBUG |
107 | | printf("[SSH] [SSH Version: %d.%d.%d]\n", major, minor, patch); |
108 | | #endif |
109 | | |
110 | 0 | break; |
111 | 0 | } |
112 | 0 | } |
113 | | |
114 | 0 | if(obsolete_ssh_version) |
115 | 0 | ndpi_set_risk(ndpi_struct, flow, |
116 | 0 | (is_client_signature ? NDPI_SSH_OBSOLETE_CLIENT_VERSION_OR_CIPHER : NDPI_SSH_OBSOLETE_SERVER_VERSION_OR_CIPHER), |
117 | 0 | NULL); |
118 | 0 | } |
119 | | |
120 | | /* ************************************************************************ */ |
121 | | |
122 | | static void ssh_analyse_cipher(struct ndpi_detection_module_struct *ndpi_struct, |
123 | | struct ndpi_flow_struct *flow, |
124 | | char *ciphers, u_int cipher_len, |
125 | 0 | u_int8_t is_client_signature) { |
126 | |
|
127 | 0 | char *rem; |
128 | 0 | char *cipher; |
129 | 0 | u_int found_obsolete_cipher = 0; |
130 | 0 | char *cipher_copy; |
131 | | /* |
132 | | List of obsolete ciphers can be found at |
133 | | https://www.linuxminion.com/deprecated-ssh-cryptographic-settings/ |
134 | | */ |
135 | 0 | const char *obsolete_ciphers[] = { |
136 | 0 | "arcfour256", |
137 | 0 | "arcfour128", |
138 | 0 | "3des-cbc", |
139 | 0 | "blowfish-cbc", |
140 | 0 | "cast128-cbc", |
141 | 0 | "arcfour", |
142 | 0 | NULL, |
143 | 0 | }; |
144 | |
|
145 | 0 | if((cipher_copy = (char*)ndpi_malloc(cipher_len+1)) == NULL) { |
146 | | #ifdef SSH_DEBUG |
147 | | printf("[SSH] Nout enough memory\n"); |
148 | | #endif |
149 | 0 | return; |
150 | 0 | } |
151 | | |
152 | 0 | strncpy(cipher_copy, ciphers, cipher_len); |
153 | 0 | cipher_copy[cipher_len] = '\0'; |
154 | |
|
155 | 0 | cipher = strtok_r(cipher_copy, ",", &rem); |
156 | |
|
157 | 0 | while(cipher && !found_obsolete_cipher) { |
158 | 0 | u_int i; |
159 | | |
160 | 0 | for(i = 0; obsolete_ciphers[i]; i++) { |
161 | 0 | if(strcmp(cipher, obsolete_ciphers[i]) == 0) { |
162 | 0 | found_obsolete_cipher = i; |
163 | | #ifdef SSH_DEBUG |
164 | | printf("[SSH] [SSH obsolete %s cipher][%s]\n", |
165 | | is_client_signature ? "client" : "server", |
166 | | obsolete_ciphers[i]); |
167 | | #endif |
168 | 0 | break; |
169 | 0 | } |
170 | 0 | } |
171 | |
|
172 | 0 | cipher = strtok_r(NULL, ",", &rem); |
173 | 0 | } |
174 | |
|
175 | 0 | if(found_obsolete_cipher) { |
176 | 0 | char str[64]; |
177 | |
|
178 | 0 | snprintf(str, sizeof(str), "Found cipher %s", obsolete_ciphers[found_obsolete_cipher]); |
179 | 0 | ndpi_set_risk(ndpi_struct, flow, |
180 | 0 | (is_client_signature ? NDPI_SSH_OBSOLETE_CLIENT_VERSION_OR_CIPHER : NDPI_SSH_OBSOLETE_SERVER_VERSION_OR_CIPHER), |
181 | 0 | str); |
182 | 0 | } |
183 | |
|
184 | 0 | ndpi_free(cipher_copy); |
185 | 0 | } |
186 | | |
187 | | /* ************************************************************************ */ |
188 | | |
189 | 0 | static int search_ssh_again(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { |
190 | 0 | ndpi_search_ssh_tcp(ndpi_struct, flow); |
191 | |
|
192 | 0 | if((flow->protos.ssh.hassh_client[0] != '\0') |
193 | 0 | && (flow->protos.ssh.hassh_server[0] != '\0')) { |
194 | | /* stop extra processing */ |
195 | 0 | flow->extra_packets_func = NULL; /* We're good now */ |
196 | 0 | return(0); |
197 | 0 | } |
198 | | |
199 | | /* Possibly more processing */ |
200 | 0 | return(1); |
201 | 0 | } |
202 | | |
203 | | /* ************************************************************************ */ |
204 | | |
205 | | static void ndpi_int_ssh_add_connection(struct ndpi_detection_module_struct |
206 | 0 | *ndpi_struct, struct ndpi_flow_struct *flow) { |
207 | 0 | if(flow->extra_packets_func != NULL) |
208 | 0 | return; |
209 | | |
210 | 0 | flow->max_extra_packets_to_check = 12; |
211 | 0 | flow->extra_packets_func = search_ssh_again; |
212 | | |
213 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_SSH, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
214 | 0 | } |
215 | | |
216 | | /* ************************************************************************ */ |
217 | | |
218 | | static u_int16_t concat_hash_string(struct ndpi_detection_module_struct *ndpi_struct, |
219 | | struct ndpi_flow_struct *flow, |
220 | | struct ndpi_packet_struct *packet, |
221 | 0 | char *buf, u_int8_t client_hash) { |
222 | 0 | u_int32_t offset = 22, len, buf_out_len = 0, max_payload_len = packet->payload_packet_len-sizeof(u_int32_t); |
223 | 0 | const u_int32_t len_max = 65565; |
224 | | |
225 | 0 | if(offset >= max_payload_len) |
226 | 0 | goto invalid_payload; |
227 | | |
228 | 0 | len = ntohl(*(u_int32_t*)&packet->payload[offset]); |
229 | 0 | offset += 4; |
230 | | |
231 | | /* -1 for ';' */ |
232 | 0 | if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1)) |
233 | 0 | goto invalid_payload; |
234 | | |
235 | | /* ssh.kex_algorithms [C/S] */ |
236 | 0 | strncpy(buf, (const char *)&packet->payload[offset], buf_out_len = len); |
237 | 0 | buf[buf_out_len++] = ';'; |
238 | 0 | offset += len; |
239 | |
|
240 | 0 | if(offset >= max_payload_len) |
241 | 0 | goto invalid_payload; |
242 | | |
243 | | /* ssh.server_host_key_algorithms [None] */ |
244 | 0 | len = ntohl(*(u_int32_t*)&packet->payload[offset]); |
245 | |
|
246 | 0 | if(len > len_max) |
247 | 0 | goto invalid_payload; |
248 | 0 | offset += 4 + len; |
249 | |
|
250 | 0 | if(offset >= max_payload_len) |
251 | 0 | goto invalid_payload; |
252 | | |
253 | | /* ssh.encryption_algorithms_client_to_server [C] */ |
254 | 0 | len = ntohl(*(u_int32_t*)&packet->payload[offset]); |
255 | |
|
256 | 0 | offset += 4; |
257 | 0 | if(client_hash) { |
258 | 0 | if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1)) |
259 | 0 | goto invalid_payload; |
260 | | |
261 | 0 | strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len); |
262 | 0 | ssh_analyse_cipher(ndpi_struct, flow, (char*)&packet->payload[offset], len, 1 /* client */); |
263 | 0 | buf_out_len += len; |
264 | 0 | buf[buf_out_len++] = ';'; |
265 | 0 | } |
266 | | |
267 | 0 | if(len > len_max) |
268 | 0 | goto invalid_payload; |
269 | 0 | offset += len; |
270 | |
|
271 | 0 | if(offset >= max_payload_len) |
272 | 0 | goto invalid_payload; |
273 | | |
274 | | /* ssh.encryption_algorithms_server_to_client [S] */ |
275 | 0 | len = ntohl(*(u_int32_t*)&packet->payload[offset]); |
276 | |
|
277 | 0 | offset += 4; |
278 | 0 | if(!client_hash) { |
279 | 0 | if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1)) |
280 | 0 | goto invalid_payload; |
281 | | |
282 | 0 | strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len); |
283 | 0 | ssh_analyse_cipher(ndpi_struct, flow, (char*)&packet->payload[offset], len, 0 /* server */); |
284 | 0 | buf_out_len += len; |
285 | 0 | buf[buf_out_len++] = ';'; |
286 | 0 | } |
287 | | |
288 | 0 | if(len > len_max) |
289 | 0 | goto invalid_payload; |
290 | 0 | offset += len; |
291 | |
|
292 | 0 | if(offset >= max_payload_len) |
293 | 0 | goto invalid_payload; |
294 | | /* ssh.mac_algorithms_client_to_server [C] */ |
295 | 0 | len = ntohl(*(u_int32_t*)&packet->payload[offset]); |
296 | |
|
297 | 0 | offset += 4; |
298 | 0 | if(client_hash) { |
299 | 0 | if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1)) |
300 | 0 | goto invalid_payload; |
301 | | |
302 | 0 | strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len); |
303 | 0 | buf_out_len += len; |
304 | 0 | buf[buf_out_len++] = ';'; |
305 | 0 | } |
306 | | |
307 | 0 | if(len > len_max) |
308 | 0 | goto invalid_payload; |
309 | 0 | offset += len; |
310 | |
|
311 | 0 | if(offset >= max_payload_len) |
312 | 0 | goto invalid_payload; |
313 | | /* ssh.mac_algorithms_server_to_client [S] */ |
314 | 0 | len = ntohl(*(u_int32_t*)&packet->payload[offset]); |
315 | |
|
316 | 0 | offset += 4; |
317 | 0 | if(!client_hash) { |
318 | 0 | if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1)) |
319 | 0 | goto invalid_payload; |
320 | | |
321 | 0 | strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len); |
322 | 0 | buf_out_len += len; |
323 | 0 | buf[buf_out_len++] = ';'; |
324 | 0 | } |
325 | | |
326 | 0 | if(len > len_max) |
327 | 0 | goto invalid_payload; |
328 | 0 | offset += len; |
329 | | |
330 | | /* ssh.compression_algorithms_client_to_server [C] */ |
331 | 0 | if(offset >= max_payload_len) |
332 | 0 | goto invalid_payload; |
333 | | |
334 | 0 | len = ntohl(*(u_int32_t*)&packet->payload[offset]); |
335 | |
|
336 | 0 | offset += 4; |
337 | 0 | if(client_hash) { |
338 | 0 | if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1)) |
339 | 0 | goto invalid_payload; |
340 | | |
341 | 0 | strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len); |
342 | 0 | buf_out_len += len; |
343 | 0 | } |
344 | | |
345 | 0 | if(len > len_max) |
346 | 0 | goto invalid_payload; |
347 | 0 | offset += len; |
348 | |
|
349 | 0 | if(offset >= max_payload_len) |
350 | 0 | goto invalid_payload; |
351 | | /* ssh.compression_algorithms_server_to_client [S] */ |
352 | 0 | len = ntohl(*(u_int32_t*)&packet->payload[offset]); |
353 | |
|
354 | 0 | offset += 4; |
355 | 0 | if(!client_hash) { |
356 | 0 | if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1)) |
357 | 0 | goto invalid_payload; |
358 | | |
359 | 0 | strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len); |
360 | 0 | buf_out_len += len; |
361 | 0 | } |
362 | | |
363 | 0 | if(len > len_max) |
364 | 0 | goto invalid_payload; |
365 | | |
366 | | /* ssh.languages_client_to_server [None] */ |
367 | | |
368 | | /* ssh.languages_server_to_client [None] */ |
369 | | |
370 | | #ifdef SSH_DEBUG |
371 | | printf("[SSH] %s\n", buf); |
372 | | #endif |
373 | | |
374 | 0 | return(buf_out_len); |
375 | | |
376 | 0 | invalid_payload: |
377 | | #ifdef SSH_DEBUG |
378 | | printf("[SSH] Invalid packet payload\n"); |
379 | | #endif |
380 | |
|
381 | 0 | return(0); |
382 | 0 | } |
383 | | |
384 | | /* ************************************************************************ */ |
385 | | |
386 | 0 | static void ndpi_ssh_zap_cr(char *str, int len) { |
387 | 0 | len--; |
388 | |
|
389 | 0 | while(len > 0) { |
390 | 0 | if((str[len] == '\n') || (str[len] == '\r')) { |
391 | 0 | str[len] = '\0'; |
392 | 0 | len--; |
393 | 0 | } else |
394 | 0 | break; |
395 | 0 | } |
396 | 0 | } |
397 | | |
398 | | /* ************************************************************************ */ |
399 | | |
400 | 1.99k | static void ndpi_search_ssh_tcp(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { |
401 | 1.99k | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
402 | | |
403 | | #ifdef SSH_DEBUG |
404 | | printf("[SSH] %s()\n", __FUNCTION__); |
405 | | #endif |
406 | | |
407 | 1.99k | if(flow->l4.tcp.ssh_stage <= 1) { |
408 | 1.99k | if(packet->payload_packet_len > 7 && memcmp(packet->payload, "SSH-", 4) == 0) { |
409 | 0 | if(current_pkt_from_client_to_server(ndpi_struct, flow)) { |
410 | 0 | int len = ndpi_min(sizeof(flow->protos.ssh.client_signature)-1, packet->payload_packet_len); |
411 | | |
412 | 0 | strncpy(flow->protos.ssh.client_signature, (const char *)packet->payload, len); |
413 | 0 | flow->protos.ssh.client_signature[len] = '\0'; |
414 | 0 | ndpi_ssh_zap_cr(flow->protos.ssh.client_signature, len); |
415 | |
|
416 | 0 | ssh_analyze_signature_version(ndpi_struct, flow, flow->protos.ssh.client_signature, 1); |
417 | | |
418 | | #ifdef SSH_DEBUG |
419 | | printf("[SSH] [client_signature: %s]\n", flow->protos.ssh.client_signature); |
420 | | #endif |
421 | | |
422 | 0 | ndpi_int_ssh_add_connection(ndpi_struct, flow); |
423 | 0 | } else { |
424 | 0 | int len = ndpi_min(sizeof(flow->protos.ssh.server_signature)-1, packet->payload_packet_len); |
425 | | |
426 | 0 | strncpy(flow->protos.ssh.server_signature, (const char *)packet->payload, len); |
427 | 0 | flow->protos.ssh.server_signature[len] = '\0'; |
428 | 0 | ndpi_ssh_zap_cr(flow->protos.ssh.server_signature, len); |
429 | |
|
430 | 0 | ssh_analyze_signature_version(ndpi_struct, flow, flow->protos.ssh.server_signature, 0); |
431 | | |
432 | | #ifdef SSH_DEBUG |
433 | | printf("[SSH] [server_signature: %s]\n", flow->protos.ssh.server_signature); |
434 | | #endif |
435 | | |
436 | 0 | NDPI_LOG_DBG2(ndpi_struct, "ssh stage 1 passed\n"); |
437 | 0 | flow->fast_callback_protocol_id = NDPI_PROTOCOL_SSH; |
438 | | |
439 | | #ifdef SSH_DEBUG |
440 | | printf("[SSH] [completed stage: %u]\n", flow->l4.tcp.ssh_stage); |
441 | | #endif |
442 | 0 | } |
443 | |
|
444 | 0 | flow->l4.tcp.ssh_stage++; |
445 | 0 | return; |
446 | 0 | } |
447 | 1.99k | } else if(packet->payload_packet_len > 5) { |
448 | 0 | u_int8_t msgcode = *(packet->payload + 5); |
449 | 0 | ndpi_MD5_CTX ctx; |
450 | | |
451 | 0 | if(msgcode == 20 /* key exchange init */) { |
452 | 0 | char *hassh_buf = ndpi_calloc(packet->payload_packet_len, sizeof(char)); |
453 | 0 | u_int i, len; |
454 | |
|
455 | | #ifdef SSH_DEBUG |
456 | | printf("[SSH] [stage: %u][msg: %u][direction: %u][key exchange init]\n", flow->l4.tcp.ssh_stage, msgcode, packet->packet_direction); |
457 | | #endif |
458 | |
|
459 | 0 | if(hassh_buf) { |
460 | 0 | if(packet->packet_direction == 0 /* client */) { |
461 | 0 | u_char fingerprint_client[16]; |
462 | |
|
463 | 0 | len = concat_hash_string(ndpi_struct, flow, packet, hassh_buf, 1 /* client */); |
464 | |
|
465 | 0 | ndpi_MD5Init(&ctx); |
466 | 0 | ndpi_MD5Update(&ctx, (const unsigned char *)hassh_buf, len); |
467 | 0 | ndpi_MD5Final(fingerprint_client, &ctx); |
468 | |
|
469 | | #ifdef SSH_DEBUG |
470 | | { |
471 | | printf("[SSH] [client][%s][", hassh_buf); |
472 | | for(i=0; i<16; i++) printf("%02X", fingerprint_client[i]); |
473 | | printf("]\n"); |
474 | | } |
475 | | #endif |
476 | 0 | for(i=0; i<16; i++) |
477 | 0 | snprintf(&flow->protos.ssh.hassh_client[i*2], |
478 | 0 | sizeof(flow->protos.ssh.hassh_client) - (i*2), |
479 | 0 | "%02X", fingerprint_client[i] & 0xFF); |
480 | | |
481 | 0 | flow->protos.ssh.hassh_client[32] = '\0'; |
482 | 0 | } else { |
483 | 0 | u_char fingerprint_server[16]; |
484 | |
|
485 | 0 | len = concat_hash_string(ndpi_struct, flow, packet, hassh_buf, 0 /* server */); |
486 | |
|
487 | 0 | ndpi_MD5Init(&ctx); |
488 | 0 | ndpi_MD5Update(&ctx, (const unsigned char *)hassh_buf, len); |
489 | 0 | ndpi_MD5Final(fingerprint_server, &ctx); |
490 | |
|
491 | | #ifdef SSH_DEBUG |
492 | | { |
493 | | printf("[SSH] [server][%s][", hassh_buf); |
494 | | for(i=0; i<16; i++) printf("%02X", fingerprint_server[i]); |
495 | | printf("]\n"); |
496 | | } |
497 | | #endif |
498 | |
|
499 | 0 | for(i=0; i<16; i++) |
500 | 0 | snprintf(&flow->protos.ssh.hassh_server[i*2], |
501 | 0 | sizeof(flow->protos.ssh.hassh_server) - (i*2), |
502 | 0 | "%02X", fingerprint_server[i] & 0xFF); |
503 | 0 | flow->protos.ssh.hassh_server[32] = '\0'; |
504 | 0 | } |
505 | |
|
506 | 0 | ndpi_free(hassh_buf); |
507 | 0 | } |
508 | |
|
509 | 0 | ndpi_int_ssh_add_connection(ndpi_struct, flow); |
510 | 0 | } |
511 | |
|
512 | 0 | if((flow->protos.ssh.hassh_client[0] != '\0') && (flow->protos.ssh.hassh_server[0] != '\0')) { |
513 | | #ifdef SSH_DEBUG |
514 | | printf("[SSH] Dissection completed\n"); |
515 | | #endif |
516 | 0 | flow->extra_packets_func = NULL; /* We're good now */ |
517 | 0 | } |
518 | |
|
519 | 0 | return; |
520 | 0 | } |
521 | | |
522 | | #ifdef SSH_DEBUG |
523 | | printf("[SSH] Excluding SSH"); |
524 | | #endif |
525 | | |
526 | 1.99k | NDPI_LOG_DBG(ndpi_struct, "excluding ssh at stage %d\n", flow->l4.tcp.ssh_stage); |
527 | 1.99k | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
528 | 1.99k | } |
529 | | |
530 | | /* ************************************************************************ */ |
531 | | |
532 | | void init_ssh_dissector(struct ndpi_detection_module_struct *ndpi_struct) |
533 | 7.49k | { |
534 | 7.49k | register_dissector("SSH", ndpi_struct, |
535 | 7.49k | ndpi_search_ssh_tcp, |
536 | 7.49k | NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, |
537 | 7.49k | 1, NDPI_PROTOCOL_SSH); |
538 | 7.49k | } |