"Fossies" - the Fresh Open Source Software Archive 
Member "snort-2.9.17/src/dynamic-preprocessors/ftptelnet/pp_ftp.c" (16 Oct 2020, 72344 Bytes) of package /linux/misc/snort-2.9.17.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "pp_ftp.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
2.9.16.1_vs_2.9.17.
1 /* $Id$ */
2 /*
3 ** Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
4 ** Copyright (C) 2004-2013 Sourcefire, Inc.
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License Version 2 as
8 ** published by the Free Software Foundation. You may not use, modify or
9 ** distribute this program under any other version of the GNU General
10 ** Public License.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software
19 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 /* pp_ftp.c
23 *
24 * Purpose: FTP sessions contain commands and responses. Certain
25 * commands are vectors of attack. This module checks
26 * those FTP client commands and their parameter values, as
27 * well as the server responses per the configuration.
28 *
29 * Arguments: None
30 *
31 * Effect: Alerts may be raised
32 *
33 * Comments:
34 *
35 */
36
37 /* your preprocessor header file goes here */
38
39 #define _GNU_SOURCE
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #ifdef HAVE_STRINGS_H
46 #include <strings.h>
47 #endif
48
49 #include <string.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <sys/types.h>
53 #ifndef WIN32
54 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
57 #include <ctype.h>
58 #else
59 #include <windows.h>
60 #endif
61
62 #include <errno.h>
63 #include "ftpp_eo_log.h"
64 #include "pp_ftp.h"
65 #include "pp_telnet.h"
66 #include "ftpp_return_codes.h"
67 #include "ftp_cmd_lookup.h"
68 #include "ftp_bounce_lookup.h"
69 #include "spp_ftptelnet.h"
70 //#include "decode.h"
71 #include "snort_debug.h"
72 #include "stream_api.h"
73 //#include "plugbase.h"
74
75 #ifndef MAXHOSTNAMELEN /* Why doesn't Windows define this? */
76 #define MAXHOSTNAMELEN 256
77 #endif
78
79 #include "ipv6_port.h"
80
81 #ifdef TARGET_BASED
82 extern int16_t ftp_data_app_id;
83 extern unsigned s_ftpdata_eof_cb_id;
84 #endif
85 #ifdef DUMP_BUFFER
86 #include "ftptelnet_buffer_dump.h"
87 #endif
88
89 #define DEFAULT_MEM_ALLOC 512
90
91 /*
92 * Function: getIP959(char **ip_start,
93 * char *last_char,
94 * char term_char,
95 * uint32_t *ipRet,
96 * uint16_t *portRet)
97 *
98 * Purpose: Returns a 32bit IP address and port from an RFC 959 FTP-style
99 * string -- ie, a,b,c,d,p1,p2. Stops checking when term_char
100 * is seen. Used to get address and port information from FTP
101 * PORT command and server response to PASV command.
102 *
103 * Arguments ip_start => Pointer to pointer to the start of string.
104 * Updated to end of IP address if successful.
105 * last_char => End of string
106 * term_char => Character delimiting the end of the address.
107 * ipRet => Return pointer to 32bit address on success
108 * portRet => Return pointer to 16bit port on success
109 *
110 * Returns: int => return code indicating error or success
111 */
112 static int getIP959(
113 const char **ip_start, const char *last_char, char *term_char,
114 sfaddr_t *ipRet, uint16_t *portRet
115 )
116 {
117 uint32_t ip=0;
118 uint16_t port=0;
119 int octet=0;
120 const char *this_param = *ip_start;
121 do
122 {
123 int value = 0;
124 do
125 {
126 if (!isdigit((int)(*this_param)))
127 {
128 return FTPP_NON_DIGIT;
129 }
130 value = value * 10 + (*this_param - '0');
131 this_param++;
132 } while ((this_param < last_char) &&
133 (*this_param != ',') &&
134 (strchr(term_char, *this_param) == NULL));
135 if (value > 0xFF)
136 {
137 return FTPP_INVALID_ARG;
138 }
139 if (octet < 4)
140 {
141 ip = (ip << 8) + value;
142 }
143 else
144 {
145 port = (port << 8) + value;
146 }
147
148 if (strchr(term_char, *this_param) == NULL)
149 this_param++;
150 octet++;
151 } while ((this_param < last_char) && (strchr(term_char, *this_param) == NULL));
152
153 if (octet != 6)
154 {
155 return FTPP_MALFORMED_IP_PORT;
156 }
157
158 ip = htonl(ip);
159 sfip_set_raw(ipRet, &ip, AF_INET);
160 *portRet = port;
161 *ip_start = this_param;
162
163 return FTPP_SUCCESS;
164 }
165
166 /*
167 * getIP1639() parses the LPRT command parameters which have this
168 * format (ftyp == e_long_host_port):
169 *
170 * LPRT af,hal,h1,h2,h3,h4...,pal,p1,p2...
171 * LPRT 4,4,132,235,1,2,2,24,131
172 * LPRT 6,16,16,128,0,...,0,8,128,0,32,12,65,123,2,20,162
173 *
174 * (The above examples correspond to the EPRT examples below.)
175 *
176 * af (address family) is the IP version. h# and p# are in network
177 * byte order (high byte first).
178 *
179 * This function is called for the LPSV response as well, which
180 * has this format:
181 *
182 * 228 <human readable text> (af,hal,h1,h2,h3,h4...,pal,p1,p2...)
183 */
184 static int getIP1639 (
185 const char **ip_start, const char *last_char, char *term_char,
186 sfaddr_t* ipRet, uint16_t *portRet
187 )
188 {
189 char bytes[21]; /* max of 1+5+3 and 1+17+3 */
190 const char* tok = *ip_start;
191 unsigned nBytes = 0;
192 bytes[0] = 0;
193
194 /* first we just try to get a sequence of csv bytes */
195 while ( nBytes < sizeof(bytes) && tok < last_char )
196 {
197 char* endPtr = (char*)tok;
198 unsigned long val = strtoul(tok, &endPtr, 10);
199
200 if (
201 val > 255 || endPtr == tok ||
202 ( *endPtr && *endPtr != ',' && endPtr != last_char )
203 ) {
204 return FTPP_INVALID_ARG;
205 }
206 bytes[nBytes++] = (uint8_t)val;
207 tok = (endPtr < last_char) ? endPtr + 1 : endPtr;
208 }
209 *ip_start = tok;
210
211 /* now we check that the we have a valid sequence of */
212 /* bytes and convert the address and port accordingly */
213 switch ( bytes[0] )
214 {
215 case 4:
216 if ( nBytes != 9 || bytes[1] != 4 || bytes[6] != 2 )
217 return FTPP_INVALID_ARG;
218 {
219 uint32_t ip4_addr = 0;
220 int n;
221 for ( n = 0; n < 4; n++ )
222 ip4_addr = (ip4_addr << 8) | bytes[n+2];
223 /* don't call sfip_set_raw() on raw bytes
224 to avoid possible word alignment issues */
225 ip4_addr = htonl(ip4_addr);
226 sfip_set_raw(ipRet, (void*)&ip4_addr, AF_INET);
227 }
228 *portRet = (bytes[7] << 8) | bytes[8];
229 break;
230
231 case 6:
232 if ( nBytes != 21 || bytes[1] != 16 || bytes[18] != 2 )
233 return FTPP_INVALID_ARG;
234
235 sfip_set_raw(ipRet, bytes+2, AF_INET6);
236 *portRet = (bytes[19] << 8) | bytes[20];
237 break;
238 default:
239 return FTPP_INVALID_ARG;
240 }
241 return FTPP_SUCCESS;
242 }
243
244 /*
245 * getIP2428() parses the EPRT command parameters which have this
246 * format (ftyp == e_extd_host_port):
247 *
248 * EPRT |<family>|address|<tcp-port>|
249 * EPRT |1|132.235.1.2|6275|
250 * EPRT |2|1080::8:800:200C:417A|5282|
251 *
252 * Note that the address family is 1|2 (as in RFC 2428), not 4|6
253 * (as in IP version), nor 2|10 (as in AF_INET[6]).
254 *
255 * This function is called for the EPSV response as well, which
256 * has this format (ftyp == e_int):
257 *
258 * 229 <human readable text> (|||<tcp-port>|)
259 *
260 * The delimiter may be other than '|' if required to represent
261 * the protocol address, but must be between 33-126 inclusive.
262 * Other delimiters aren't required for IPv{4,6} but we allow
263 * them for flexibility.
264 *
265 * It is assumed that *ip_start points to the first delimiter in
266 * both cases.
267 */
268
269 /*
270 * this copy is unfortunate but inet_pton() doesn't
271 * like the delim and the src buf is const so ...
272 */
273 void CopyField (
274 char* buf, const char* tok, int max, const char* end, char delim
275 )
276 {
277 int len = end - tok + 1;
278 char* s;
279
280 if ( len >= max )
281 {
282 strncpy(buf, tok, max);
283 buf[max-1] = '\0';
284 }
285 else
286 {
287 strncpy(buf, tok, len);
288 buf[len] = '\0';
289 }
290 s = strchr(buf, delim);
291
292 if ( s ) *s = '\0';
293 else *buf = '\0';
294 }
295
296 static int getIP2428 (
297 const char **ip_start, const char *last_char, char *term_char,
298 sfaddr_t* ipRet, uint16_t *portRet, FTP_PARAM_TYPE ftyp
299 )
300 {
301 const char* tok = *ip_start;
302 char delim = *tok;
303 int field = 1, fieldMask = 0;
304 int family = AF_UNSPEC, port = 0;
305 char buf[64];
306
307 IP_CLEAR((*ipRet));
308 *portRet = 0;
309
310 /* check first delimiter */
311 if ( delim < 33 || delim > 126 )
312 return FTPP_INVALID_ARG;
313
314 while ( tok && tok < last_char && field < 4 ) {
315 int check = (*++tok != delim) ? field : 0;
316
317 switch ( check ) {
318 case 0: /* empty */
319 break;
320
321 case 1: /* check family */
322 family = atoi(tok);
323 if ( family == 1 ) family = AF_INET;
324 else if ( family == 2 ) family = AF_INET6;
325 else return FTPP_INVALID_ARG;
326 fieldMask |= 1;
327 break;
328
329 case 2: /* check address */
330 CopyField(buf, tok, sizeof(buf), last_char, delim);
331 if ( sfaddr_pton(buf, ipRet) != SFIP_SUCCESS || ipRet->family != family )
332 return FTPP_INVALID_ARG;
333
334 fieldMask |= 2;
335 break;
336
337 case 3: /* check port */
338 port = atoi(tok);
339 if ( port < 0 || port > MAXPORTS-1 )
340 return FTPP_MALFORMED_IP_PORT;
341 *portRet = port;
342 fieldMask |= 4;
343 break;
344 }
345 /* advance to next field */
346 tok = strchr(tok, delim);
347 field++;
348 }
349
350 if (tok)
351 {
352 if ( *tok == delim ) tok++;
353 *ip_start = tok;
354 }
355 else
356 {
357 *ip_start = last_char;
358 }
359
360 if ( ftyp == e_int && fieldMask == 4 )
361 /* TBD: do we need to check for bounce if addr present? */
362 return FTPP_SUCCESS;
363
364 if ( ftyp == e_extd_host_port && fieldMask == 7 )
365 return FTPP_SUCCESS;
366
367 return FTPP_INVALID_ARG;
368 }
369
370 static int getFTPip(
371 FTP_PARAM_TYPE ftyp, const char **ip_start, const char *last_char,
372 char *term_char, sfaddr_t *ipRet, uint16_t *portRet
373 )
374 {
375 if ( ftyp == e_host_port )
376 {
377 return getIP959(ip_start, last_char, term_char, ipRet, portRet);
378 }
379 if ( ftyp == e_long_host_port )
380 {
381 return getIP1639(ip_start, last_char, term_char, ipRet, portRet);
382 }
383 return getIP2428(ip_start, last_char, term_char, ipRet, portRet, ftyp);
384 }
385
386
387 /*
388 * Function: validate_date_format(
389 * FTP_DATE_FMT *ThisFmt,
390 * char **this_param)
391 *
392 * Purpose: Recursively determines whether a date matches the
393 * a valid format.
394 *
395 * Arguments: ThisFmt => Pointer to the current format
396 * this_param => Pointer to start of the portion to validate.
397 * Updated to end of valid section if valid.
398 *
399 * Returns: int => return code indicating error or success
400 *
401 */
402 static int validate_date_format(FTP_DATE_FMT *ThisFmt, const char **this_param)
403 {
404 int valid_string = 0;
405 int checked_something_else = 0;
406 int checked_next = 0;
407 int iRet = FTPP_ALERT;
408 const char *curr_ch;
409 if (!ThisFmt)
410 return FTPP_INVALID_ARG;
411
412 if (!this_param || !(*this_param))
413 return FTPP_INVALID_ARG;
414
415 curr_ch = *this_param;
416 if (!ThisFmt->empty)
417 {
418 char *format_char = ThisFmt->format_string;
419
420 do
421 {
422 switch (*format_char)
423 {
424 case 'n':
425 if (!isdigit((int)(*curr_ch)))
426 {
427 /* Return for non-digit */
428 return FTPP_INVALID_DATE;
429 }
430 curr_ch++;
431 format_char++;
432 break;
433 case 'C':
434 if (!isalpha((int)(*curr_ch)))
435 {
436 /* Return for non-char */
437 return FTPP_INVALID_DATE;
438 }
439 curr_ch++;
440 format_char++;
441 break;
442 default:
443 if (*curr_ch != *format_char)
444 {
445 /* Return for non-matching char */
446 return FTPP_INVALID_DATE;
447 }
448 curr_ch++;
449 format_char++;
450 break;
451 }
452 valid_string = 1;
453 }
454 while ((*format_char != '\0') && !isspace((int)(*curr_ch)));
455
456 if ((*format_char != '\0') && isspace((int)(*curr_ch)))
457 {
458 /* Didn't have enough chars to complete this format */
459 return FTPP_INVALID_DATE;
460 }
461 }
462
463 if ((ThisFmt->optional) && !isspace((int)(*curr_ch)))
464 {
465 const char *tmp_ch = curr_ch;
466 iRet = validate_date_format(ThisFmt->optional, &tmp_ch);
467 if (iRet == FTPP_SUCCESS)
468 curr_ch = tmp_ch;
469 }
470 if ((ThisFmt->next_a) && !isspace((int)(*curr_ch)))
471 {
472 const char *tmp_ch = curr_ch;
473 checked_something_else = 1;
474 iRet = validate_date_format(ThisFmt->next_a, &tmp_ch);
475 if (iRet == FTPP_SUCCESS)
476 {
477 curr_ch = tmp_ch;
478 }
479 else if (ThisFmt->next_b)
480 {
481 iRet = validate_date_format(ThisFmt->next_b, &tmp_ch);
482 if (iRet == FTPP_SUCCESS)
483 curr_ch = tmp_ch;
484 }
485 if (ThisFmt->next)
486 {
487 iRet = validate_date_format(ThisFmt->next, &tmp_ch);
488 if (iRet == FTPP_SUCCESS)
489 {
490 curr_ch = tmp_ch;
491 checked_next = 1;
492 }
493 }
494 if (iRet == FTPP_SUCCESS)
495 {
496 *this_param = curr_ch;
497 return iRet;
498 }
499 }
500 if ((!checked_next) && (ThisFmt->next))
501 {
502 const char *tmp_ch = curr_ch;
503 checked_something_else = 1;
504 iRet = validate_date_format(ThisFmt->next, &tmp_ch);
505 if (iRet == FTPP_SUCCESS)
506 {
507 curr_ch = tmp_ch;
508 checked_next = 1;
509 }
510 }
511
512 if ((isspace((int)(*curr_ch))) && ((!ThisFmt->next) || checked_next))
513 {
514 *this_param = curr_ch;
515 return FTPP_SUCCESS;
516 }
517
518 if (valid_string)
519 {
520 int all_okay = 0;
521 if (checked_something_else)
522 {
523 if (iRet == FTPP_SUCCESS)
524 all_okay = 1;
525 }
526 else
527 {
528 all_okay = 1;
529 }
530
531 if (all_okay)
532 {
533 *this_param = curr_ch;
534 return FTPP_SUCCESS;
535 }
536 }
537
538 return FTPP_INVALID_DATE;
539 }
540
541 /*
542 * Function: validate_param(
543 * Packet *p
544 * char *param
545 * char *end
546 * FTP_PARAM_FMT *param_format,
547 * FTP_SESSION *Session)
548 *
549 * Purpose: Validates the current parameter against the format
550 * specified.
551 *
552 * Arguments: p => Pointer to the current packet
553 * params_begin => Pointer to beginning of parameters
554 * params_end => End of params buffer
555 * param_format => Parameter format specifier for this command
556 * Session => Pointer to the session info
557 *
558 * Returns: int => return code indicating error or success
559 *
560 */
561 static int validate_param(SFSnortPacket *p,
562 const char *param,
563 const char *end,
564 FTP_PARAM_FMT *ThisFmt,
565 FTP_SESSION *Session)
566 {
567 int iRet;
568 const char *this_param = param;
569
570 if (param > end)
571 return FTPP_ALERT;
572
573 switch (ThisFmt->type)
574 {
575 case e_head:
576 /* shouldn't get here, but just in case */
577 /* this hack is because we do get here! */
578 this_param--;
579 break;
580 case e_unrestricted:
581 /* strings/filenames only occur as the last param,
582 * so move to the end of the param buffer. */
583 this_param = end;
584 break;
585 case e_strformat:
586 /* Check for 2 % signs within the parameter for an FTP command
587 * 2 % signs is the magic number per existing rules (24 Sep 2004)
588 */
589 #define MAX_PERCENT_SIGNS 2
590 {
591 int numPercents = 0;
592 do
593 {
594 if (*this_param == '%')
595 {
596 numPercents++;
597 if (numPercents >= MAX_PERCENT_SIGNS)
598 {
599 break;
600 }
601 }
602 this_param++;
603 }
604 while ((this_param < end) &&
605 (*this_param != '\n'));
606
607 if (numPercents >= MAX_PERCENT_SIGNS)
608 {
609 /* Alert on string format attack in parameter */
610 ftp_eo_event_log(Session, FTP_EO_PARAMETER_STR_FORMAT,
611 NULL, NULL);
612 return FTPP_ALERTED;
613 }
614 }
615 break;
616 case e_int:
617 /* check that this_param is all digits up to next space */
618 {
619 do
620 {
621 if (!isdigit((int)(*this_param)))
622 {
623 /* Alert on non-digit */
624 return FTPP_INVALID_PARAM;
625 }
626 this_param++;
627 }
628 while ((this_param < end) && (*this_param != ' ') );
629 }
630 break;
631 case e_number:
632 /* check that this_param is all digits up to next space
633 * and value is between 1 & 255 */
634 {
635 int iValue = 0;
636 do
637 {
638 if (!isdigit((int)(*this_param)))
639 {
640 /* Alert on non-digit */
641 return FTPP_INVALID_PARAM;
642 }
643 iValue = iValue * 10 + (*this_param - '0');
644 this_param++;
645 }
646 while ((this_param < end) && (*this_param != ' ') );
647
648 if ((iValue > 255) || (iValue == 0))
649 return FTPP_INVALID_PARAM;
650 }
651 break;
652 case e_char:
653 /* check that this_param is one of chars specified */
654 {
655 int bitNum = (*this_param & 0x1f);
656 if (!isalpha((int)(*this_param)))
657 {
658 /* Alert on non-char */
659 return FTPP_INVALID_PARAM;
660 }
661 else
662 {
663 if (!(ThisFmt->format.chars_allowed & (1 << (bitNum-1))) )
664 {
665 /* Alert on unexpected char */
666 return FTPP_INVALID_PARAM;
667 }
668 }
669 this_param++; /* should be a space */
670 }
671 break;
672 case e_date:
673 /* check that this_param conforms to date specified */
674 {
675 const char *tmp_ch = this_param;
676 iRet = validate_date_format(ThisFmt->format.date_fmt, &tmp_ch);
677 if (iRet != FTPP_SUCCESS)
678 {
679 /* Alert invalid date */
680 return FTPP_INVALID_PARAM;
681 }
682 if (!isspace((int)(*tmp_ch)))
683 {
684 /* Alert invalid date -- didn't make it to end of parameter.
685 Overflow attempt? */
686 return FTPP_INVALID_PARAM;
687 }
688 this_param = tmp_ch;
689 }
690 break;
691 case e_literal:
692 /* check that this_param matches the literal specified */
693 {
694 const char* s = ThisFmt->format.literal;
695 size_t n = strlen(s);
696
697 if ( strncmp(this_param, s, n) )
698 {
699 /* Alert on non-char */
700 return FTPP_INVALID_PARAM;
701 }
702 this_param += n;
703 }
704 break;
705 /* check that this_param is: */
706 case e_host_port: /* PORT: h1,h2,h3,h4,p1,p2 */
707 case e_long_host_port: /* LPRT: af,hal,h1,h2,h3,h4...,pal,p1,p2... */
708 case e_extd_host_port: /* EPRT: |<af>|<addr>|<port>| */
709 {
710 sfaddr_t ipAddr;
711 uint16_t port=0;
712
713 int ret = getFTPip(
714 ThisFmt->type, &this_param, end, " \n", &ipAddr, &port
715 );
716 switch (ret)
717 {
718 case FTPP_NON_DIGIT:
719 /* Alert on non-digit */
720 return FTPP_INVALID_PARAM;
721 break;
722 case FTPP_INVALID_ARG:
723 /* Alert on number > 255 */
724 return FTPP_INVALID_PARAM;
725 break;
726 case FTPP_MALFORMED_IP_PORT:
727 /* Alert on malformed host-port */
728 return FTPP_INVALID_PARAM;
729 break;
730 }
731
732 if ( ThisFmt->type == e_extd_host_port && !sfaddr_is_set(&ipAddr) )
733 {
734 // actually, we expect no addr in 229 responses, which is
735 // understood to be server address, so we set that here
736 ipAddr = *GET_SRC_IP(p);
737 }
738 if ((Session->client_conf->bounce.on) &&
739 (Session->client_conf->bounce.alert))
740 {
741 if (!IP_EQUALITY(&ipAddr, GET_SRC_IP(p)))
742 {
743 int alert = 1;
744
745 FTP_BOUNCE_TO *BounceTo = ftp_bounce_lookup_find(
746 Session->client_conf->bounce_lookup, &ipAddr, &iRet);
747 if (BounceTo)
748 {
749 if (BounceTo->portlo)
750 {
751 if (BounceTo->porthi)
752 {
753 if ((port >= BounceTo->portlo) &&
754 (port <= BounceTo->porthi))
755 alert = 0;
756 }
757 else
758 {
759 if (port == BounceTo->portlo)
760 alert = 0;
761 }
762 }
763 }
764
765 /* Alert on invalid IP address for PORT */
766 if (alert)
767 {
768 ftp_eo_event_log(Session, FTP_EO_BOUNCE, NULL, NULL);
769 /* Return here -- because we will likely want to
770 * inspect the data traffic over a bounced data
771 * connection */
772 return FTPP_PORT_ATTACK;
773 }
774 }
775 }
776
777 Session->clientIP = ipAddr;
778 Session->clientPort = port;
779 Session->data_chan_state |= DATA_CHAN_PORT_CMD_ISSUED;
780 if (Session->data_chan_state & DATA_CHAN_PASV_CMD_ISSUED)
781 {
782 /*
783 * If there was a PORT command previously in
784 * a series of pipelined requests, this
785 * cancels it.
786 */
787 Session->data_chan_state &= ~DATA_CHAN_PASV_CMD_ISSUED;
788 }
789
790 IP_CLEAR(Session->serverIP);
791 Session->serverPort = 0;
792 }
793 break;
794 }
795
796 ThisFmt->next_param = this_param;
797
798 return FTPP_SUCCESS;
799 }
800
801 /*
802 * Function: check_ftp_param_validity(
803 * Packet *p,
804 * char *params_begin,
805 * char *params_end,
806 * FTP_PARAM_FMT *param_format,
807 * FTP_SESSION *Session)
808 *
809 * Purpose: Recursively determines whether each of the parameters for
810 * an FTP command are valid.
811 *
812 * Arguments: p => Pointer to the current packet
813 * params_begin => Pointer to beginning of parameters
814 * params_end => End of params buffer
815 * param_format => Parameter format specifier for this command
816 * Session => Pointer to the session info
817 *
818 * Returns: int => return code indicating error or success
819 *
820 */
821 static int check_ftp_param_validity(SFSnortPacket *p,
822 const char *params_begin,
823 const char *params_end,
824 FTP_PARAM_FMT *param_format,
825 FTP_SESSION *Session)
826 {
827 int iRet = FTPP_ALERT;
828 FTP_PARAM_FMT *ThisFmt = param_format;
829 FTP_PARAM_FMT *NextFmt;
830 const char *this_param = params_begin;
831
832 if (!param_format)
833 return FTPP_INVALID_ARG;
834
835 if (!params_begin && !ThisFmt->next_param_fmt && ThisFmt->optional_fmt)
836 return FTPP_SUCCESS; /* no param is allowed in this case */
837
838 if (!params_begin && (ThisFmt->next_param_fmt && ThisFmt->next_param_fmt->type == e_strformat))
839 return FTPP_SUCCESS; /* string format check of non existent param */
840
841 if (!params_begin)
842 return FTPP_INVALID_ARG;
843
844 if ((!ThisFmt->next_param_fmt) && (params_begin >= params_end))
845 return FTPP_SUCCESS;
846
847 ThisFmt->next_param = params_begin;
848
849 if (ThisFmt->optional_fmt)
850 {
851 /* Check against optional */
852 iRet = validate_param(p, this_param, params_end,
853 ThisFmt->optional_fmt, Session);
854 if (iRet == FTPP_SUCCESS)
855 {
856 const char *next_param;
857 NextFmt = ThisFmt->optional_fmt;
858 next_param = NextFmt->next_param+1;
859 iRet = check_ftp_param_validity(p, next_param, params_end,
860 NextFmt, Session);
861 if (iRet == FTPP_SUCCESS)
862 {
863 this_param = NextFmt->next_param+1;
864 }
865 }
866 }
867
868 if ((iRet != FTPP_SUCCESS) && (ThisFmt->choices))
869 {
870 /* Check against choices -- one of many */
871 int i;
872 int valid = 0;
873 for (i=0;i<ThisFmt->numChoices && !valid;i++)
874 {
875 /* Try choice [i] */
876 iRet = validate_param(p, this_param, params_end,
877 ThisFmt->choices[i], Session);
878 if (iRet == FTPP_SUCCESS)
879 {
880 const char *next_param;
881 NextFmt = ThisFmt->choices[i];
882 next_param = NextFmt->next_param+1;
883 iRet = check_ftp_param_validity(p, next_param, params_end,
884 NextFmt, Session);
885 if (iRet == FTPP_SUCCESS)
886 {
887 this_param = NextFmt->next_param+1;
888 valid = 1;
889 break;
890 }
891 }
892 }
893 }
894 else if ((iRet != FTPP_SUCCESS) && (ThisFmt->next_param_fmt))
895 {
896 /* Check against next param */
897 iRet = validate_param(p, this_param, params_end,
898 ThisFmt->next_param_fmt, Session);
899 if (iRet == FTPP_SUCCESS)
900 {
901 const char *next_param;
902 NextFmt = ThisFmt->next_param_fmt;
903 next_param = NextFmt->next_param+1;
904 iRet = check_ftp_param_validity(p, next_param, params_end,
905 NextFmt, Session);
906 if (iRet == FTPP_SUCCESS)
907 {
908 this_param = NextFmt->next_param+1;
909 }
910 }
911 }
912 else if ((iRet != FTPP_SUCCESS) && (!ThisFmt->next_param_fmt) &&
913 this_param)
914 {
915 iRet = FTPP_SUCCESS;
916 }
917 if (iRet == FTPP_SUCCESS)
918 {
919 ThisFmt->next_param = this_param;
920 }
921 return iRet;
922 }
923
924 /*
925 * Function: initialize_ftp(FTP_SESSION *Session, Packet *p, int iMode)
926 *
927 * Purpose: Initializes the state machine for checking an FTP packet.
928 * Does normalization checks.
929 *
930 * Arguments: Session => Pointer to session info
931 * p => pointer to the current packet struct
932 * iMode => Mode indicating server or client checks
933 *
934 * Returns: int => return code indicating error or success
935 *
936 */
937 int initialize_ftp(FTP_SESSION *Session, SFSnortPacket *p, int iMode)
938 {
939 int iRet;
940 const unsigned char *read_ptr = p->payload;
941 FTP_CLIENT_REQ *req;
942 char ignoreTelnetErase = FTPP_APPLY_TNC_ERASE_CMDS;
943 FTPTELNET_GLOBAL_CONF *global_conf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(Session->global_conf, Session->policy_id);
944
945 /* Normalize this packet ala telnet */
946 if (((iMode == FTPP_SI_CLIENT_MODE) &&
947 (Session->client_conf->ignore_telnet_erase_cmds.on == 1)) ||
948 ((iMode == FTPP_SI_SERVER_MODE) &&
949 (Session->server_conf->ignore_telnet_erase_cmds.on == 1)) )
950 ignoreTelnetErase = FTPP_IGNORE_TNC_ERASE_CMDS;
951
952 iRet = normalize_telnet(global_conf, NULL, p, iMode, ignoreTelnetErase);
953
954 if (iRet != FTPP_SUCCESS && iRet != FTPP_NORMALIZED)
955 {
956 if (iRet == FTPP_ALERT)
957 {
958 if (global_conf->telnet_config->detect_anomalies)
959 {
960 ftp_eo_event_log(Session, FTP_EO_EVASIVE_TELNET_CMD, NULL, NULL);
961 } }
962 return iRet;
963 }
964
965 if (_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE))
966 {
967 /* Normalized data will always be in decode buffer */
968 if ( ((Session->client_conf->telnet_cmds.alert) &&
969 (iMode == FTPP_SI_CLIENT_MODE)) ||
970 ((Session->server_conf->telnet_cmds.alert) &&
971 (iMode == FTPP_SI_SERVER_MODE)) )
972 {
973 /* alert -- FTP channel with telnet commands */
974 ftp_eo_event_log(Session, FTP_EO_TELNET_CMD, NULL, NULL);
975 return FTPP_ALERT; /* Nothing else to do since we alerted */
976 }
977
978 read_ptr = _dpd.altBuffer->data;
979 }
980
981 if (iMode == FTPP_SI_CLIENT_MODE)
982 {
983 req = &Session->client.request;
984
985 #ifdef DUMP_BUFFER
986 dumpBuffer(FTP_REQUEST_CMD_LINE,req->cmd_line,req->cmd_line_size);
987 dumpBuffer(FTP_REQUEST_CMD,req->cmd_begin,req->cmd_size);
988 dumpBuffer(FTP_REQUEST_PARAM,req->param_begin,req->param_size);
989 #endif
990
991 }
992 else if (iMode == FTPP_SI_SERVER_MODE)
993 {
994 FTP_SERVER_RSP *rsp = &Session->server.response;
995 req = (FTP_CLIENT_REQ *)rsp;
996
997 #ifdef DUMP_BUFFER
998 dumpBuffer(FTP_RESPONSE_LINE,rsp->rsp_line,rsp->rsp_line_size);
999 dumpBuffer(FTP_RESPONSE_DUMP,rsp->rsp_begin,rsp->rsp_size);
1000 dumpBuffer(FTP_RESPONSE_MSG,rsp->msg_begin,rsp->msg_size);
1001 #endif
1002
1003 }
1004 else
1005 return FTPP_INVALID_ARG;
1006
1007 /* Set the beginning of the pipeline to the start of the
1008 * (normalized) buffer */
1009 req->pipeline_req = (const char *)read_ptr;
1010
1011 return FTPP_SUCCESS;
1012 }
1013
1014 static inline void prepareForEncryption(FTP_SESSION *Session, FTPTELNET_GLOBAL_CONF *global_conf, int new_encr_state)
1015 {
1016 Session->encr_state = new_encr_state;
1017 Session->encr_state_chello = true;
1018 if (global_conf->encrypted.alert)
1019 {
1020 /* Alert on encrypted channel */
1021 ftp_eo_event_log(Session, FTP_EO_ENCRYPTED,
1022 NULL, NULL);
1023 }
1024 }
1025
1026 /*
1027 * Function: do_stateful_checks(FTP_SESSION *Session, Packet *p,
1028 * FTP_CLIENT_REQ *req, int rsp_code)
1029 *
1030 * Purpose: Handle stateful checks and state updates for FTP response
1031 * packets.
1032 *
1033 * Arguments: Session => Pointer to session info
1034 * p => Pointer to the current packet struct
1035 * req => Pointer to current response from packet
1036 * (this function may be called multiple
1037 * times for pipelined requests).
1038 * rsp_code => Integer response value for server response
1039 *
1040 * Returns: int => return code indicating error or success
1041 *
1042 */
1043 static int do_stateful_checks(FTP_SESSION *Session, SFSnortPacket *p,
1044 FTP_CLIENT_REQ *req, int rsp_code)
1045 {
1046 int iRet = FTPP_SUCCESS;
1047 FTPTELNET_GLOBAL_CONF *global_conf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(Session->global_conf, Session->policy_id);
1048
1049 //if (Session->server_conf->data_chan)
1050 {
1051 if (rsp_code == 226)
1052 {
1053 /* Just ignore this code -- end of transfer...
1054 * If we saw all the other dat for this channel
1055 * Session->data_chan_state should be NO_STATE. */
1056 }
1057 else if (Session->data_chan_state & DATA_CHAN_PASV_CMD_ISSUED)
1058 {
1059 if (Session->ftp_cmd_pipe_index == Session->data_chan_index)
1060 {
1061 if (Session->data_xfer_index == 0)
1062 Session->ftp_cmd_pipe_index = 1;
1063 Session->data_chan_index = 0;
1064
1065 if ( rsp_code >= 227 && rsp_code <= 229 )
1066 {
1067 sfaddr_t ipAddr;
1068 uint16_t port=0;
1069 const char *ip_begin = req->param_begin;
1070 IP_CLEAR(ipAddr);
1071 Session->data_chan_state &= ~DATA_CHAN_PASV_CMD_ISSUED;
1072 Session->data_chan_state |= DATA_CHAN_PASV_CMD_ACCEPT;
1073 Session->data_chan_index = 0;
1074 /* Interpret response message to identify the
1075 * Server IP/Port. Server response is inside
1076 * a pair of ()s. Find the left (, and use same
1077 * means to find IP/Port as is done for the PORT
1078 * command. */
1079 if (req->param_size != 0)
1080 {
1081 while ((ip_begin < req->param_end) &&
1082 (*ip_begin != '('))
1083 {
1084 ip_begin++;
1085 }
1086 }
1087
1088 if (ip_begin < req->param_end)
1089 {
1090 FTP_PARAM_TYPE ftyp =
1091 /* e_int is used in lieu of adding a new value to the
1092 * enum because this case doesn't correspond to a
1093 * validation config option; it could effectively be
1094 * replaced with an additional bool arg to getFTPip() that
1095 * differentiated between commands and responses, but
1096 * this distinction is only required for EPSV rsps. */
1097 (rsp_code == 229) ? e_int :
1098 (rsp_code == 228 ? e_long_host_port : e_host_port);
1099
1100 ip_begin++;
1101 iRet = getFTPip(
1102 ftyp, &ip_begin, req->param_end, ")", &ipAddr, &port
1103 );
1104 if (iRet == FTPP_SUCCESS)
1105 {
1106 if (!sfaddr_is_set(&ipAddr))
1107 IP_COPY_VALUE(Session->serverIP, GET_SRC_IP(p));
1108 else
1109 {
1110 Session->serverIP = ipAddr;
1111 }
1112 Session->serverPort = port;
1113 IP_COPY_VALUE(Session->clientIP, GET_DST_IP(p));
1114 Session->clientPort = 0;
1115 #ifdef TARGET_BASED
1116 if ((_dpd.fileAPI->get_max_file_depth(_dpd.getCurrentSnortConfig(), false) > 0) || !(Session->server_conf->data_chan))
1117 {
1118 FTP_DATA_SESSION *ftpdata = FTPDataSessionNew(p);
1119
1120 if (ftpdata)
1121 {
1122 int result;
1123 /* This is a passive data transfer */
1124 ftpdata->mode = FTPP_XFER_PASSIVE;
1125 ftpdata->data_chan = Session->server_conf->data_chan;
1126 /* Store the FTP_SESSION in FTP_DATA_SESSION, needed when FTP_DATA_SESSION is freed.
1127 * This is needed to handle pruning of FTP_DATA_SESSION */
1128 ftpdata->ftpssn = Session;
1129 /*If a FTP_DATA_SESSION already exists, the FTP_SESSION reference in that should be removed*/
1130 if(Session->datassn)
1131 {
1132 FTP_DATA_SESSION * ssn = Session->datassn;
1133 ssn->ftpssn = NULL;
1134 }
1135 Session->datassn = ftpdata;
1136
1137 /* Call into Streams to mark data channel as ftp-data */
1138 result = _dpd.streamAPI->set_application_protocol_id_expected_preassign_callback(
1139 p, IP_ARG(Session->clientIP), Session->clientPort,
1140 IP_ARG(Session->serverIP), Session->serverPort, GET_IPH_PROTO(p),
1141 ftp_data_app_id, PP_FTPTELNET, (void *)ftpdata, &FTPDataSessionFree,
1142 s_ftpdata_eof_cb_id, SE_EOF, &p->expectedSession);
1143
1144 if (result < 0)
1145 FTPDataSessionFree(ftpdata);
1146 }
1147 }
1148 else if (Session->server_conf->data_chan)
1149 #else
1150 if (Session->server_conf->data_chan)
1151 #endif
1152 {
1153 /* Call into Streams to mark data channel as something
1154 * to ignore. */
1155 _dpd.sessionAPI->ignore_session(p, IP_ARG(Session->clientIP), Session->clientPort,
1156 IP_ARG(Session->serverIP), Session->serverPort, GET_IPH_PROTO(p),
1157 PP_FTPTELNET, SSN_DIR_BOTH, 0 /* Not permanent */, &p->expectedSession );
1158 }
1159 }
1160 }
1161 else
1162 {
1163 iRet = FTPP_MALFORMED_FTP_RESPONSE;
1164 }
1165 }
1166 else
1167 {
1168 Session->data_chan_index = 0;
1169 Session->data_chan_state &= ~DATA_CHAN_PASV_CMD_ISSUED;
1170 }
1171 }
1172 }
1173 else if (Session->data_chan_state & DATA_CHAN_PORT_CMD_ISSUED)
1174 {
1175 if (Session->ftp_cmd_pipe_index == Session->data_chan_index)
1176 {
1177 if (Session->data_xfer_index == 0)
1178 Session->ftp_cmd_pipe_index = 1;
1179 Session->data_chan_index = 0;
1180 if (rsp_code == 200)
1181 {
1182 Session->data_chan_state &= ~DATA_CHAN_PORT_CMD_ISSUED;
1183 Session->data_chan_state |= DATA_CHAN_PORT_CMD_ACCEPT;
1184 Session->data_chan_index = 0;
1185 if (sfaddr_is_set(&Session->clientIP))
1186 {
1187 /* This means we're not in passive mode. */
1188 /* Server is listening/sending from its own IP,
1189 * FTP Port -1 */
1190 /* Client IP, Port specified via PORT command */
1191 IP_COPY_VALUE(Session->serverIP, GET_SRC_IP(p));
1192
1193 /* Can't necessarily guarantee this, especially
1194 * in the case of a proxy'd connection where the
1195 * data channel might not be on port 20 (or server
1196 * port-1). Comment it out for now.
1197 */
1198 /*
1199 Session->serverPort = ntohs(p->tcph->th_sport) -1;
1200 */
1201 #ifdef TARGET_BASED
1202 if ((_dpd.fileAPI->get_max_file_depth(_dpd.getCurrentSnortConfig(), false) > 0) || !(Session->server_conf->data_chan))
1203 {
1204 FTP_DATA_SESSION *ftpdata = FTPDataSessionNew(p);
1205
1206 if (ftpdata)
1207 {
1208 int result;
1209 /* This is a active data transfer */
1210 ftpdata->mode = FTPP_XFER_ACTIVE;
1211 ftpdata->data_chan = Session->server_conf->data_chan;
1212 /* Store the FTP_SESSION in FTP_DATA_SESSION, needed when FTP_DATA_SESSION is freed.
1213 * This is needed to handle pruning of FTP_DATA_SESSION */
1214 ftpdata->ftpssn = Session;
1215 /*If a FTP_DATA_SESSION already exists, the FTP_SESSION reference in that should be removed*/
1216 if(Session->datassn)
1217 {
1218 FTP_DATA_SESSION * ssn = Session->datassn;
1219 ssn->ftpssn = NULL;
1220 }
1221 Session->datassn = ftpdata;
1222
1223 /* Call into Streams to mark data channel as ftp-data */
1224 result = _dpd.streamAPI->set_application_protocol_id_expected_preassign_callback(
1225 p, IP_ARG(Session->serverIP), Session->serverPort,
1226 IP_ARG(Session->clientIP), Session->clientPort, GET_IPH_PROTO(p),
1227 ftp_data_app_id, PP_FTPTELNET, (void *)ftpdata, &FTPDataSessionFree,
1228 s_ftpdata_eof_cb_id, SE_EOF, &p->expectedSession);
1229
1230 if (result < 0)
1231 FTPDataSessionFree(ftpdata);
1232 }
1233 }
1234 else if (Session->server_conf->data_chan)
1235 #else
1236 if (Session->server_conf->data_chan)
1237 #endif
1238 {
1239 /* Call into Streams to mark data channel as something
1240 * to ignore. */
1241 _dpd.sessionAPI->ignore_session(p, IP_ARG(Session->serverIP), Session->serverPort,
1242 IP_ARG(Session->clientIP), Session->clientPort, GET_IPH_PROTO(p),
1243 PP_FTPTELNET, SSN_DIR_BOTH, 0 /* Not permanent */, &p->expectedSession );
1244 }
1245 }
1246 }
1247 else if (Session->ftp_cmd_pipe_index == Session->data_chan_index)
1248 {
1249 Session->data_chan_index = 0;
1250 Session->data_chan_state &= ~DATA_CHAN_PORT_CMD_ISSUED;
1251 }
1252 }
1253 }
1254 else if (Session->data_chan_state & DATA_CHAN_REST_CMD_ISSUED)
1255 {
1256 if (Session->ftp_cmd_pipe_index == Session->data_xfer_index)
1257 {
1258 if (Session->data_chan_index == 0)
1259 Session->ftp_cmd_pipe_index = 1;
1260 Session->data_xfer_index = 0;
1261 if (rsp_code == 350)
1262 {
1263 #ifdef TARGET_BASED
1264 FTP_DATA_SESSION *ftpdata = Session->datassn;
1265 if(ftpdata)
1266 ftpdata->flags |= FTPDATA_FLG_REST;
1267 #endif
1268 }
1269 else
1270 Session->rest_cmd_offset= 0;
1271 Session->data_chan_index = 0;
1272 Session->data_chan_state &= ~DATA_CHAN_REST_CMD_ISSUED;
1273 }
1274 }
1275 else if (Session->data_chan_state & DATA_CHAN_XFER_CMD_ISSUED)
1276 {
1277 if (Session->ftp_cmd_pipe_index == Session->data_xfer_index)
1278 {
1279 if (Session->data_chan_index == 0)
1280 Session->ftp_cmd_pipe_index = 1;
1281 Session->data_xfer_index = 0;
1282 if ((rsp_code == 150) || (rsp_code == 125))
1283 {
1284 Session->data_chan_state = DATA_CHAN_XFER_STARTED;
1285 }
1286 /* Clear the session info for next transfer -->
1287 * reset host/port */
1288 IP_CLEAR(Session->serverIP);
1289 IP_CLEAR(Session->clientIP);
1290 Session->serverPort = Session->clientPort = 0;
1291 Session->data_chan_state = NO_STATE;
1292 }
1293 }
1294 } /* if (Session->server_conf->data_chan) */
1295
1296 if (global_conf->encrypted.on && rsp_code == 234)
1297 {
1298 /* any of these states is now anticipatory of the client hello */
1299 switch(Session->encr_state)
1300 {
1301 case AUTH_TLS_CMD_ISSUED:
1302 prepareForEncryption(Session, global_conf, AUTH_TLS_ENCRYPTED);
1303 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1304 "FTP stream is now TLS encrypted\n"););
1305 break;
1306 case AUTH_SSL_CMD_ISSUED:
1307 prepareForEncryption(Session, global_conf, AUTH_SSL_ENCRYPTED);
1308 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1309 "FTP stream is now SSL encrypted\n"););
1310 break;
1311 case AUTH_UNKNOWN_CMD_ISSUED:
1312 default: // encr_state got confused, but we do have a 234
1313 prepareForEncryption(Session, global_conf, AUTH_UNKNOWN_ENCRYPTED);
1314 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1315 "FTP stream is now encrypted\n"););
1316 break;
1317 case AUTH_TLS_ENCRYPTED:
1318 case AUTH_SSL_ENCRYPTED:
1319 case AUTH_UNKNOWN_ENCRYPTED:
1320 // already been through here
1321 break;
1322 }
1323 } /* if (global_conf->encrypted.on && rsp_code == 234) */
1324
1325 return iRet;
1326 }
1327
1328 /*
1329 * Function: check_ftp(FTP_SESSION *Session, Packet *p, int iMode)
1330 *
1331 * Purpose: Handle some trivial validation checks of an FTP packet. Namely,
1332 * check argument length and some protocol enforcement.
1333 *
1334 * Wishful: This results in exposing the FTP command (and looking
1335 * at the results) to the rules layer.
1336 *
1337 * Arguments: Session => Pointer to session info
1338 * p => pointer to the current packet struct
1339 * iMode => Mode indicating server or client checks
1340 *
1341 * Returns: int => return code indicating error or success
1342 *
1343 */
1344 #define NUL 0x00
1345 #define CR 0x0d
1346 #define LF 0x0a
1347 #define SP 0x20
1348 #define DASH 0x2D
1349
1350 #define FTP_CMD_OK 0
1351 #define FTP_CMD_INV 1
1352 #define FTP_RESPONSE_INV 1
1353 #define FTP_RESPONSE 2
1354 #define FTP_RESPONSE_2BCONT 2
1355 #define FTP_RESPONSE_CONT 3
1356 #define FTP_RESPONSE_ENDCONT 4
1357 int check_ftp(FTP_SESSION *ftpssn, SFSnortPacket *p, int iMode)
1358 {
1359 int iRet = FTPP_SUCCESS;
1360 int encrypted = 0;
1361 int space = 0;
1362 long state = FTP_CMD_OK;
1363 int rsp_code = 0;
1364 FTPTELNET_GLOBAL_CONF *global_conf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(ftpssn->global_conf, ftpssn->policy_id);
1365 FTP_CLIENT_REQ *req;
1366 FTP_CMD_CONF *CmdConf = NULL;
1367 #ifdef TARGET_BASED
1368 FTP_DATA_SESSION *datassn;
1369 #endif
1370
1371 const unsigned char *read_ptr;
1372 const unsigned char *end = p->payload + p->payload_size;
1373
1374 if (_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE))
1375 end = _dpd.altBuffer->data + _dpd.altBuffer->len;
1376
1377 if (iMode == FTPP_SI_CLIENT_MODE)
1378 {
1379 req = &ftpssn->client.request;
1380 ftpssn->ftp_cmd_pipe_index = 1;
1381 }
1382 else if (iMode == FTPP_SI_SERVER_MODE)
1383 {
1384 FTP_SERVER_RSP *rsp = &ftpssn->server.response;
1385 req = (FTP_CLIENT_REQ *)rsp;
1386 }
1387 else
1388 return FTPP_INVALID_ARG;
1389
1390 while (req->pipeline_req)
1391 {
1392 state = FTP_CMD_OK;
1393
1394 /* Starts at the beginning of the buffer/line,
1395 * so next up is a command */
1396 read_ptr = (const unsigned char *)req->pipeline_req;
1397
1398 /* but first we ignore leading white space */
1399 while ( (read_ptr < end) &&
1400 (iMode == FTPP_SI_CLIENT_MODE) && isspace(*read_ptr) )
1401 read_ptr++;
1402
1403 // ignore extra \r\n emitted by some clients
1404 if ( read_ptr == end )
1405 break;
1406
1407 req->cmd_begin = (const char *)read_ptr;
1408
1409 while ((read_ptr < end) &&
1410 (*read_ptr != SP) &&
1411 (*read_ptr != CR) &&
1412 (*read_ptr != LF) && /* Check for LF when there wasn't a CR,
1413 * protocol violation, but accepted by
1414 * some servers. */
1415 (*read_ptr != DASH))
1416 {
1417 /* If the first char is a digit this is a response
1418 * in server mode. */
1419 if (iMode == FTPP_SI_SERVER_MODE)
1420 {
1421 if (isdigit(*read_ptr))
1422 {
1423 if (state != FTP_RESPONSE_INV)
1424 {
1425 state = FTP_RESPONSE;
1426 }
1427 }
1428 else if (!isascii(*read_ptr))
1429 {
1430 /* Non-ascii char here? Bad response */
1431 state = FTP_RESPONSE_INV;
1432 }
1433 }
1434 /* Or, if this is not a char, this is garbage in client mode */
1435 else if (!isalpha(*read_ptr) && (iMode == FTPP_SI_CLIENT_MODE))
1436 {
1437 state = FTP_CMD_INV;
1438 }
1439
1440 read_ptr++;
1441 }
1442 req->cmd_end = (const char *)read_ptr;
1443 req->cmd_size = req->cmd_end - req->cmd_begin;
1444
1445 if (iMode == FTPP_SI_CLIENT_MODE)
1446 {
1447 if ( (req->cmd_size > ftpssn->server_conf->max_cmd_len)
1448 || (req->cmd_size < MIN_CMD)
1449 || (state == FTP_CMD_INV) )
1450 {
1451 /* Uh, something is very wrong...
1452 * nonalpha char seen or cmd is bad length.
1453 * See if this might be encrypted, ie, non-alpha bytes. */
1454 const unsigned char *ptr = (const unsigned char *)req->cmd_begin;
1455 while (ptr < (const unsigned char *)req->cmd_end)
1456 {
1457 if (!isalpha((int)(*ptr)))
1458 {
1459 if (!isascii((int)(*ptr)) || !isprint((int)(*ptr)))
1460 {
1461 encrypted = 1;
1462 }
1463 break;
1464 }
1465 ptr++;
1466 }
1467 }
1468
1469 if (encrypted)
1470 {
1471 /* If the session wasn't already marked as encrypted...
1472 * Don't want to double-alert if we've already
1473 * determined the session is encrypted and we're
1474 * checking encrypted sessions.
1475 */
1476 if (ftpssn->encr_state == 0)
1477 {
1478 ftpssn->encr_state = AUTH_UNKNOWN_ENCRYPTED;
1479 if (global_conf->encrypted.alert)
1480 {
1481 /* Alert on encrypted channel */
1482 ftp_eo_event_log(ftpssn, FTP_EO_ENCRYPTED,
1483 NULL, NULL);
1484 }
1485 if (!global_conf->check_encrypted_data)
1486 {
1487 /* Mark this session & packet as one to ignore */
1488 _dpd.sessionAPI->stop_inspection(p->stream_session, p,
1489 SSN_DIR_BOTH, -1, 0);
1490 }
1491 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1492 "FTP client stream is now encrypted\n"););
1493 }
1494 break;
1495 }
1496 else
1497 {
1498 /*
1499 * Check the list of valid FTP commands as
1500 * supplied in ftpssn.
1501 */
1502 if ( req->cmd_size > ftpssn->server_conf->max_cmd_len )
1503 {
1504 /* Alert, cmd not found */
1505 ftp_eo_event_log(ftpssn, FTP_EO_INVALID_CMD, NULL, NULL);
1506 state = FTP_CMD_INV;
1507 }
1508 else
1509 {
1510 CmdConf = ftp_cmd_lookup_find(ftpssn->server_conf->cmd_lookup,
1511 req->cmd_begin,
1512 req->cmd_size,
1513 &iRet);
1514 if ((iRet == FTPP_NOT_FOUND) || (CmdConf == NULL))
1515 {
1516 /* Alert, cmd not found */
1517 ftp_eo_event_log(ftpssn, FTP_EO_INVALID_CMD, NULL, NULL);
1518 state = FTP_CMD_INV;
1519 }
1520 else
1521 {
1522 /* In case we were encrypted, but aren't now */
1523 ftpssn->encr_state = 0;
1524 }
1525 }
1526 }
1527 }
1528 else if (iMode == FTPP_SI_SERVER_MODE)
1529 {
1530 if (state == FTP_CMD_INV)
1531 state = FTP_RESPONSE_INV;
1532
1533 if ( (req->cmd_size != 3) || (state == FTP_RESPONSE_INV) )
1534 {
1535 /* Uh, something is very wrong...
1536 * nondigit char seen or resp code is not 3 chars.
1537 * See if this might be encrypted, ie, non-alpha bytes. */
1538 const char *ptr = req->cmd_begin;
1539 while (ptr < req->cmd_end)
1540 {
1541 if (!isdigit((int)(*ptr)))
1542 {
1543 if (!isascii((int)(*ptr)) || !isprint((int)(*ptr)))
1544 {
1545 encrypted = 1;
1546 }
1547 break;
1548 }
1549 ptr++;
1550 }
1551 }
1552
1553 if (encrypted)
1554 {
1555 /* If the session wasn't already marked as encrypted...
1556 * Don't want to double-alert if we've already
1557 * determined the session is encrypted and we're
1558 * checking encrypted sessions.
1559 */
1560 if (ftpssn->encr_state == 0)
1561 {
1562 ftpssn->encr_state = AUTH_UNKNOWN_ENCRYPTED;
1563 if (global_conf->encrypted.alert)
1564 {
1565 /* Alert on encrypted channel */
1566 ftp_eo_event_log(ftpssn, FTP_EO_ENCRYPTED,
1567 NULL, NULL);
1568 }
1569 if (!global_conf->check_encrypted_data)
1570 {
1571 /* Mark this session & packet as one to ignore */
1572 _dpd.sessionAPI->stop_inspection(p->stream_session, p,
1573 SSN_DIR_BOTH, -1, 0);
1574 }
1575 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1576 "FTP server stream is now encrypted\n"););
1577 }
1578 break;
1579 }
1580 else
1581 {
1582 /* In case we were encrypted, but aren't now */
1583 if ((ftpssn->encr_state == AUTH_TLS_ENCRYPTED) ||
1584 (ftpssn->encr_state == AUTH_SSL_ENCRYPTED) ||
1585 (ftpssn->encr_state == AUTH_UNKNOWN_ENCRYPTED))
1586 {
1587 ftpssn->encr_state = 0;
1588 }
1589
1590 /* Otherwise, might have an encryption command pending */
1591 }
1592
1593 if (read_ptr < end)
1594 {
1595 if (*read_ptr != DASH)
1596 {
1597 const unsigned char *resp_begin = (const unsigned char *)req->cmd_begin;
1598 const unsigned char *resp_end = (const unsigned char *)req->cmd_end;
1599 if (resp_end - resp_begin >= 3)
1600 {
1601 if (isdigit(*(resp_begin)) &&
1602 isdigit(*(resp_begin+1)) &&
1603 isdigit(*(resp_begin+2)) )
1604 {
1605 rsp_code = ( (*(resp_begin) - '0') * 100 +
1606 (*(resp_begin+1) - '0') * 10 +
1607 (*(resp_begin+2) - '0') );
1608 if (rsp_code == ftpssn->server.response.state)
1609 {
1610 /* End of continued response */
1611 state = FTP_RESPONSE_ENDCONT;
1612 ftpssn->server.response.state = 0;
1613 }
1614 else
1615 {
1616 /* Single line response */
1617 state = FTP_RESPONSE;
1618 }
1619 }
1620 }
1621
1622 if (ftpssn->server.response.state != 0)
1623 {
1624 req->cmd_begin = NULL;
1625 req->cmd_end = NULL;
1626 if (*read_ptr != SP)
1627 read_ptr--;
1628 state = FTP_RESPONSE_CONT;
1629 }
1630 }
1631 else if ((state == FTP_RESPONSE) && (*read_ptr == DASH))
1632 {
1633 const unsigned char *resp_begin = (const unsigned char *)req->cmd_begin;
1634 if (isdigit(*(resp_begin)) &&
1635 isdigit(*(resp_begin+1)) &&
1636 isdigit(*(resp_begin+2)) )
1637 {
1638 int resp_code = ( (*(resp_begin) - '0') * 100 +
1639 (*(resp_begin+1) - '0') * 10 +
1640 (*(resp_begin+2) - '0') );
1641 if (resp_code == ftpssn->server.response.state)
1642 {
1643 /* Continuation of previous response */
1644 state = FTP_RESPONSE_CONT;
1645 }
1646 else
1647 {
1648 /* Start of response, state stays as -2 */
1649 state = FTP_RESPONSE_2BCONT;
1650 ftpssn->server.response.state = resp_code;
1651 rsp_code = resp_code;
1652 }
1653 }
1654 else
1655 {
1656 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1657 "invalid FTP response code."););
1658 ftpssn->server.response.state = FTP_RESPONSE_INV;
1659 }
1660 }
1661 }
1662 }
1663
1664 if (read_ptr < end)
1665 {
1666 if (*read_ptr == SP)
1667 {
1668 space = 1;
1669 }
1670
1671 read_ptr++; /* Move past the space, dash, or CR */
1672 }
1673
1674 /* If there is anything left... */
1675
1676 if (read_ptr < end)
1677 {
1678 /* Look for an LF --> implies no parameters/message */
1679 if (*read_ptr == LF)
1680 {
1681 read_ptr++;
1682 req->param_begin = NULL;
1683 req->param_end = NULL;
1684 }
1685 else if (!space && ftpssn->server.response.state == 0)
1686 {
1687 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1688 "Missing LF from end of FTP command\n"););
1689 }
1690 else
1691 {
1692 /* Now grab the command parameters/response message
1693 * read_ptr < end already checked */
1694 req->param_begin = (const char *)read_ptr;
1695 if ((read_ptr = memchr(read_ptr, CR, end - read_ptr)) == NULL)
1696 read_ptr = end;
1697 req->param_end = (const char *)read_ptr;
1698 read_ptr++;
1699
1700 if (read_ptr < end)
1701 {
1702 /* Cool, got the end of the parameters, move past
1703 * the LF, so we can process the next one in
1704 * the pipeline.
1705 */
1706 if (*read_ptr == LF)
1707 {
1708 read_ptr++;
1709 }
1710 else
1711 {
1712 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1713 "Missing LF from end of FTP command with params\n"););
1714 }
1715 }
1716 }
1717 }
1718 else
1719 {
1720 /* Nothing left --> no parameters/message. Not even an LF */
1721 req->param_begin = NULL;
1722 req->param_end = NULL;
1723 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1724 "Missing LF from end of FTP command sans params\n"););
1725 }
1726
1727 /* Set the pointer for the next request/response
1728 * in the pipeline. */
1729 if (read_ptr < end)
1730 req->pipeline_req = (const char *)read_ptr;
1731 else
1732 req->pipeline_req = NULL;
1733
1734 req->param_size = req->param_end - req->param_begin;
1735 switch (state)
1736 {
1737 case FTP_CMD_INV:
1738 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1739 "Illegal FTP command found: %.*s\n",
1740 req->cmd_size, req->cmd_begin));
1741 iRet = FTPP_ALERT;
1742 break;
1743 case FTP_RESPONSE: /* Response */
1744 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1745 "FTP response: code: %.*s : M len %d : M %.*s\n",
1746 req->cmd_size, req->cmd_begin, req->param_size,
1747 req->param_size, req->param_begin));
1748 if ((ftpssn->client_conf->max_resp_len > 0) &&
1749 (req->param_size > ftpssn->client_conf->max_resp_len))
1750 {
1751 /* Alert on response message overflow */
1752 ftp_eo_event_log(ftpssn, FTP_EO_RESPONSE_LENGTH_OVERFLOW,
1753 NULL, NULL);
1754 iRet = FTPP_ALERT;
1755 }
1756 #ifdef TARGET_BASED
1757 if (!(ftpssn->flags & FTP_FLG_MALWARE_ENABLED) &&
1758 _dpd.fileAPI->file_config_malware_check(p->stream_session, ftp_data_app_id))
1759 {
1760 ftpssn->flags |= FTP_FLG_MALWARE_ENABLED;
1761 }
1762 #endif
1763 if (global_conf->inspection_type ==
1764 FTPP_UI_CONFIG_STATEFUL)
1765 {
1766 int newRet = do_stateful_checks(ftpssn, p, req, rsp_code);
1767 if (newRet != FTPP_SUCCESS)
1768 iRet = newRet;
1769 }
1770 break;
1771 case FTP_RESPONSE_CONT: /* Response continued */
1772 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1773 "FTP response: continuation of code: %d : M len %d : M %.*s\n",
1774 ftpssn->server.response.state, req->param_size,
1775 req->param_size, req->param_begin));
1776 if ((ftpssn->client_conf->max_resp_len > 0) &&
1777 (req->param_size > ftpssn->client_conf->max_resp_len))
1778 {
1779 /* Alert on response message overflow */
1780 ftp_eo_event_log(ftpssn, FTP_EO_RESPONSE_LENGTH_OVERFLOW,
1781 NULL, NULL);
1782 iRet = FTPP_ALERT;
1783 }
1784 break;
1785 case FTP_RESPONSE_ENDCONT: /* Continued response end */
1786 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
1787 "FTP response: final continue of code: %.*s : M len %d : "
1788 "M %.*s\n", req->cmd_size, req->cmd_begin,
1789 req->param_size, req->param_size, req->param_begin));
1790 if ((ftpssn->client_conf->max_resp_len > 0) &&
1791 (req->param_size > ftpssn->client_conf->max_resp_len))
1792 {
1793 /* Alert on response message overflow */
1794 ftp_eo_event_log(ftpssn, FTP_EO_RESPONSE_LENGTH_OVERFLOW,
1795 NULL, NULL);
1796 iRet = FTPP_ALERT;
1797 }
1798 break;
1799 default:
1800 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP command: CMD: %.*s : "
1801 "P len %d : P %.*s\n", req->cmd_size, req->cmd_begin,
1802 req->param_size, req->param_size, req->param_begin));
1803
1804 if (CmdConf)
1805 {
1806 if ((req->param_size > CmdConf->max_param_len))
1807 {
1808 /* Alert on param length overrun */
1809 ftp_eo_event_log(ftpssn, FTP_EO_PARAMETER_LENGTH_OVERFLOW,
1810 NULL, NULL);
1811 DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP command: %.*s"
1812 "parameter length overrun %d > %d \n",
1813 req->cmd_size, req->cmd_begin, req->param_size,
1814 CmdConf->max_param_len));
1815 iRet = FTPP_ALERT;
1816 }
1817
1818 if (CmdConf->data_chan_cmd)
1819 {
1820 ftpssn->data_chan_state |= DATA_CHAN_PASV_CMD_ISSUED;
1821 ftpssn->data_chan_index = ftpssn->ftp_cmd_pipe_index;
1822 if (ftpssn->data_chan_state & DATA_CHAN_PORT_CMD_ISSUED)
1823 {
1824 /*
1825 * If there was a PORT command previously in
1826 * a series of pipelined requests, this
1827 * cancels it.
1828 */
1829 ftpssn->data_chan_state &= ~DATA_CHAN_PORT_CMD_ISSUED;
1830 }
1831 }
1832 else if (CmdConf->data_rest_cmd)
1833 {
1834 if ((req->param_begin != NULL) && (req->param_size > 0))
1835 {
1836 char *return_ptr = 0;
1837 unsigned long offset = 0;
1838
1839 errno = 0;
1840 offset = strtoul(req->param_begin, &return_ptr, 10);
1841
1842 if ((errno == ERANGE || errno == EINVAL) || (offset > 0))
1843 {
1844 ftpssn->data_chan_state |= DATA_CHAN_REST_CMD_ISSUED;
1845 ftpssn->data_xfer_index = ftpssn->ftp_cmd_pipe_index;
1846 ftpssn->rest_cmd_offset = offset;
1847 }
1848 }
1849 }
1850 else if (CmdConf->data_xfer_cmd)
1851 {
1852 #ifdef TARGET_BASED
1853 /* If we are not ignoring the data channel OR file processing is enabled */
1854 if (!ftpssn->server_conf->data_chan || (_dpd.fileAPI->get_max_file_depth(_dpd.getCurrentSnortConfig(), false) > -1))
1855 {
1856 /* The following check cleans up filename for failed data
1857 * transfers. If the transfer had been successful the
1858 * filename pointer would have been handed off to the
1859 * FTP_DATA_SESSION for tracking. */
1860 if (ftpssn->filename)
1861 {
1862 free(ftpssn->filename);
1863 ftpssn->filename = NULL;
1864 ftpssn->file_xfer_info = FTPP_FILE_IGNORE;
1865 }
1866
1867 // Get the file name and set direction of the get/put request.
1868 // Request could have been sent without parameters, i.e. filename,
1869 // so make sure something is there.
1870 if (((req->param_begin != NULL) && (req->param_size > 0))
1871 && (CmdConf->file_get_cmd || CmdConf->file_put_cmd))
1872 {
1873 ftpssn->filename = (char *)malloc(req->param_size+1);
1874 ftp_telnet_stats.heap_memory += req->param_size+1;
1875 if (ftpssn->filename)
1876 {
1877 memcpy(ftpssn->filename, req->param_begin, req->param_size);
1878 ftpssn->filename[req->param_size] = '\0';
1879 ftpssn->file_xfer_info = req->param_size;
1880 if (ftpssn->flags & FTP_FLG_MALWARE_ENABLED)
1881 {
1882 IP_COPY_VALUE(ftpssn->control_clientIP, GET_SRC_IP(p));
1883 IP_COPY_VALUE(ftpssn->control_serverIP, GET_DST_IP(p));
1884 ftpssn->control_serverPort = ntohs(p->tcp_header->destination_port);
1885 ftpssn->control_clientPort = ntohs(p->tcp_header->source_port);
1886 #ifdef TARGET_BASED
1887 datassn = (FTP_DATA_SESSION *)ftpssn->datassn;
1888 if(datassn)
1889 {
1890 char *file_name = strrchr(ftpssn->filename, '/');
1891 if(!file_name)
1892 file_name = ftpssn->filename;
1893 datassn->path_hash = _dpd.fileAPI->str_to_hash((uint8_t *)file_name, strlen(file_name));
1894 }
1895 #endif
1896 }
1897 }
1898 else
1899 {
1900 _dpd.errMsg("check_ftp: "
1901 "Memory allocation failed for filename in ftpssn\n");
1902 }
1903 // 0 for Download, 1 for Upload
1904 ftpssn->data_xfer_dir = CmdConf->file_get_cmd ? false : true;
1905 FTP_DATA_SESSION *ftpdata = ftpssn->datassn;
1906 if(ftpdata)
1907 ftpdata->direction = ftpssn->data_xfer_dir;
1908 }
1909 else
1910 {
1911 ftpssn->file_xfer_info = FTPP_FILE_IGNORE;
1912 }
1913 }
1914 #endif
1915 ftpssn->data_chan_state |= DATA_CHAN_XFER_CMD_ISSUED;
1916 ftpssn->data_xfer_index = ftpssn->ftp_cmd_pipe_index;
1917 }
1918 else if (CmdConf->encr_cmd)
1919 {
1920 if (req->param_begin && (req->param_size > 0) &&
1921 ((req->param_begin[0] == 'T') || (req->param_begin[0] == 't')))
1922 {
1923 ftpssn->encr_state = AUTH_TLS_CMD_ISSUED;
1924 }
1925 else if (req->param_begin && (req->param_size > 0) &&
1926 ((req->param_begin[0] == 'S') || (req->param_begin[0] == 's')))
1927 {
1928 ftpssn->encr_state = AUTH_SSL_CMD_ISSUED;
1929 }
1930 else
1931 {
1932 ftpssn->encr_state = AUTH_UNKNOWN_CMD_ISSUED;
1933 }
1934 }
1935 if (CmdConf->check_validity)
1936 {
1937 iRet = check_ftp_param_validity(p, req->param_begin,
1938 req->param_end, CmdConf->param_format,
1939 ftpssn);
1940 /* If negative, haven't already alerted on violation */
1941 if (iRet < 0)
1942 {
1943 /* Set Alert on malformatted parameter */
1944 ftp_eo_event_log(ftpssn, FTP_EO_MALFORMED_PARAMETER,
1945 NULL, NULL);
1946 iRet = FTPP_ALERT;
1947 break;
1948 }
1949 else if (iRet > 0)
1950 {
1951 /* Already alerted -- ie, string format attack. */
1952 break;
1953 }
1954 }
1955 }
1956 break;
1957 }
1958
1959 if (iMode == FTPP_SI_CLIENT_MODE)
1960 ftpssn->ftp_cmd_pipe_index++;
1961 else if ((rsp_code != 226) && (rsp_code != 426))
1962 {
1963 /*
1964 * In terms of counting responses, ignore
1965 * 226 response saying transfer complete
1966 * 426 response saying transfer aborted
1967 * The 226 may or may not be sent by the server.
1968 * Both are 2nd response to a transfer command.
1969 */
1970 ftpssn->ftp_cmd_pipe_index++;
1971 }
1972 }
1973
1974 if (iMode == FTPP_SI_CLIENT_MODE)
1975 {
1976 ftpssn->ftp_cmd_pipe_index = 1;
1977 }
1978
1979 if (encrypted)
1980 return FTPP_ALERT;
1981
1982 return iRet;
1983 }