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