1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2023 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2004-2013 Sourcefire, Inc.
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.
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.
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 //--------------------------------------------------------------------------
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
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"
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"
47 using namespace snort
;
49 #ifndef MAXHOSTNAMELEN /* Why doesn't Windows define this? */
50 #define MAXHOSTNAMELEN 256
54 * Used to keep track of pipelined commands and the last one
57 static THREAD_LOCAL
int ftp_cmd_pipe_index
= 0;
60 * Function: getIP959(char **ip_start,
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.
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
78 * Returns: int => return code indicating error or success
81 const char** ip_start
, const char* last_char
, const char* term_char
,
82 SfIp
* ipRet
, uint16_t* portRet
88 const char* this_param
= *ip_start
;
94 if (!isdigit((int)(*this_param
)))
96 return FTPP_NON_DIGIT
;
98 value
= value
* 10 + (*this_param
- '0');
101 while ((this_param
< last_char
) &&
102 (*this_param
!= ',') &&
103 (strchr(term_char
, *this_param
) == nullptr));
106 return FTPP_INVALID_ARG
;
110 ip
= (ip
<< 8) + value
;
114 port
= (port
<< 8) + value
;
117 if (strchr(term_char
, *this_param
) == nullptr)
121 while ((this_param
< last_char
) && (strchr(term_char
, *this_param
) == nullptr));
125 return FTPP_MALFORMED_IP_PORT
;
129 ipRet
->set(&ip
, AF_INET
);
131 *ip_start
= this_param
;
137 * getIP1639() parses the LPRT command parameters which have this
138 * format (ftyp == e_long_host_port):
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
144 * (The above examples correspond to the EPRT examples below.)
146 * af (address family) is the IP version. h# and p# are in network
147 * byte order (high byte first).
149 * This function is called for the LPSV response as well, which
152 * 228 <human readable text> (af,hal,h1,h2,h3,h4...,pal,p1,p2...)
154 static int getIP1639(
155 const char** ip_start
, const char* last_char
, const char*,
156 SfIp
* ipRet
, uint16_t* portRet
159 char bytes
[21]; /* max of 1+5+3 and 1+17+3 */
160 const char* tok
= *ip_start
;
164 /* first we just try to get a sequence of csv bytes */
165 while ( nBytes
< sizeof(bytes
) && tok
< last_char
)
168 unsigned long val
= strtoul(tok
, &endPtr
, 10);
171 val
> 255 || endPtr
== tok
||
172 ( *endPtr
&& *endPtr
!= ',' && endPtr
!= last_char
)
175 return FTPP_INVALID_ARG
;
177 bytes
[nBytes
++] = (uint8_t)val
;
178 tok
= (endPtr
< last_char
) ? endPtr
+ 1 : endPtr
;
182 /* now we check that the we have a valid sequence of
183 bytes and convert the address and port accordingly */
187 if ( nBytes
!= 9 || bytes
[1] != 4 || bytes
[6] != 2 )
188 return FTPP_INVALID_ARG
;
190 uint32_t ip4_addr
= 0;
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
);
199 *portRet
= (bytes
[7] << 8) | bytes
[8];
203 if ( nBytes
!= 21 || bytes
[1] != 16 || bytes
[18] != 2 )
204 return FTPP_INVALID_ARG
;
206 ipRet
->set(bytes
+2, AF_INET6
);
207 *portRet
= (bytes
[19] << 8) | bytes
[20];
210 return FTPP_INVALID_ARG
;
216 * getIP2428() parses the EPRT command parameters which have this
217 * format (ftyp == e_extd_host_port):
219 * EPRT |<family>|address|<tcp-port>|
220 * EPRT |1|132.235.1.2|6275|
221 * EPRT |2|1080::8:800:200C:417A|5282|
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]).
226 * This function is called for the EPSV response as well, which
227 * has this format (ftyp == e_int):
229 * 229 <human readable text> (|||<tcp-port>|)
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.
236 * It is assumed that *ip_start points to the first delimiter in
241 * this copy is unfortunate but inet_pton() doesn't
242 * like the delim and the src buf is const so ...
244 static void CopyField(
245 char* buf
, const char* tok
, int max
, const char* end
, char delim
248 int len
= end
- tok
+ 1;
253 strncpy(buf
, tok
, max
);
258 strncpy(buf
, tok
, len
);
261 s
= strchr(buf
, delim
);
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
274 const char* tok
= *ip_start
;
276 int field
= 1, fieldMask
= 0;
277 int family
= AF_UNSPEC
, port
= 0;
283 /* check first delimiter */
284 if ( delim
< 33 || delim
> 126 )
285 return FTPP_INVALID_ARG
;
287 while ( tok
&& tok
< last_char
&& field
< 4 )
289 int check
= (*++tok
!= delim
) ? field
: 0;
296 case 1: /* check family */
300 else if ( family
== 2 )
303 return FTPP_INVALID_ARG
;
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
;
315 case 3: /* check port */
317 if ( port
< 0 || port
> MAX_PORTS
-1 )
318 return FTPP_MALFORMED_IP_PORT
;
323 /* advance to next field */
324 tok
= strchr(tok
, delim
);
336 *ip_start
= last_char
;
339 if ( ftyp
== e_int
&& fieldMask
== 4 )
340 /* TBD: do we need to check for bounce if addr present? */
343 if ( ftyp
== e_extd_host_port
&& fieldMask
== 7 )
346 return FTPP_INVALID_ARG
;
350 FTP_PARAM_TYPE ftyp
, const char** ip_start
, const char* last_char
,
351 const char* term_char
, SfIp
* ipRet
, uint16_t* portRet
354 if ( ftyp
== e_host_port
)
356 return getIP959(ip_start
, last_char
, term_char
, ipRet
, portRet
);
358 if ( ftyp
== e_long_host_port
)
360 return getIP1639(ip_start
, last_char
, term_char
, ipRet
, portRet
);
362 return getIP2428(ip_start
, last_char
, term_char
, ipRet
, portRet
, ftyp
);
366 * Function: validate_date_format(
367 * FTP_DATE_FMT *ThisFmt,
370 * Purpose: Recursively determines whether a date matches the
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.
377 * Returns: int => return code indicating error or success
380 static int validate_date_format(FTP_DATE_FMT
* ThisFmt
, const char** this_param
)
382 int valid_string
= 0;
383 int checked_something_else
= 0;
384 int checked_next
= 0;
385 int iRet
= FTPP_ALERT
;
388 return FTPP_INVALID_ARG
;
390 if (!this_param
|| !(*this_param
))
391 return FTPP_INVALID_ARG
;
393 curr_ch
= *this_param
;
396 char* format_char
= ThisFmt
->format_string
;
400 switch (*format_char
)
403 if (!isdigit((int)(*curr_ch
)))
405 /* Return for non-digit */
406 return FTPP_INVALID_DATE
;
412 if (!isalpha((int)(*curr_ch
)))
414 /* Return for non-char */
415 return FTPP_INVALID_DATE
;
421 if (*curr_ch
!= *format_char
)
423 /* Return for non-matching char */
424 return FTPP_INVALID_DATE
;
432 while ((*format_char
!= '\0') && !isspace((int)(*curr_ch
)));
434 if ((*format_char
!= '\0') && isspace((int)(*curr_ch
)))
436 /* Didn't have enough chars to complete this format */
437 return FTPP_INVALID_DATE
;
441 if ((ThisFmt
->optional
) && !isspace((int)(*curr_ch
)))
443 const char* tmp_ch
= curr_ch
;
444 iRet
= validate_date_format(ThisFmt
->optional
, &tmp_ch
);
445 if (iRet
== FTPP_SUCCESS
)
448 if ((ThisFmt
->next_a
) && !isspace((int)(*curr_ch
)))
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
)
457 else if (ThisFmt
->next_b
)
459 iRet
= validate_date_format(ThisFmt
->next_b
, &tmp_ch
);
460 if (iRet
== FTPP_SUCCESS
)
465 iRet
= validate_date_format(ThisFmt
->next
, &tmp_ch
);
466 if (iRet
== FTPP_SUCCESS
)
472 if (iRet
== FTPP_SUCCESS
)
474 *this_param
= curr_ch
;
478 if ((!checked_next
) && (ThisFmt
->next
))
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
)
490 if ((isspace((int)(*curr_ch
))) && ((!ThisFmt
->next
) || checked_next
))
492 *this_param
= curr_ch
;
499 if (checked_something_else
)
501 if (iRet
== FTPP_SUCCESS
)
511 *this_param
= curr_ch
;
516 return FTPP_INVALID_DATE
;
520 * Function: validate_param(
524 * FTP_PARAM_FMT *param_format,
525 * const char** this_fmt_next_param,
526 * FTP_SESSION *session)
528 * Purpose: Validates the current parameter against the format
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
538 * Returns: int => return code indicating error or success
541 static int validate_param(Packet
* p
,
544 FTP_PARAM_FMT
* ThisFmt
,
545 const char** this_fmt_next_param
,
546 FTP_SESSION
* session
)
549 const char* this_param
= param
;
554 switch (ThisFmt
->type
)
557 /* shouldn't get here, but just in case
558 this hack is because we do get here! */
562 /* strings/filenames only occur as the last param,
563 * so move to the end of the param buffer. */
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)
570 #define MAX_PERCENT_SIGNS 2
575 if (*this_param
== '%')
578 if (numPercents
>= MAX_PERCENT_SIGNS
)
585 while ((this_param
< end
) &&
586 (*this_param
!= '\n'));
588 if (numPercents
>= MAX_PERCENT_SIGNS
)
590 /* Alert on string format attack in parameter */
591 DetectionEngine::queue_event(GID_FTP
, FTP_PARAMETER_STR_FORMAT
);
597 /* check that this_param is all digits up to next space */
601 if (!isdigit((int)(*this_param
)))
603 /* Alert on non-digit */
604 return FTPP_INVALID_PARAM
;
608 while ((this_param
< end
) && (*this_param
!= ' ') );
612 /* check that this_param is all digits up to next space
613 * and value is between 1 & 255 */
618 if (!isdigit((int)(*this_param
)))
620 /* Alert on non-digit */
621 return FTPP_INVALID_PARAM
;
623 iValue
= iValue
* 10 + (*this_param
- '0');
626 while ((this_param
< end
) && (*this_param
!= ' ') );
628 if ((iValue
> 255) || (iValue
== 0))
629 return FTPP_INVALID_PARAM
;
633 /* check that this_param is one of chars specified */
635 int bitNum
= (*this_param
& 0x1f);
636 if (!isalpha((int)(*this_param
)))
638 /* Alert on non-char */
639 return FTPP_INVALID_PARAM
;
643 if (!(ThisFmt
->format
.chars_allowed
& (1 << (bitNum
-1))) )
645 /* Alert on unexpected char */
646 return FTPP_INVALID_PARAM
;
649 this_param
++; /* should be a space */
653 /* check that this_param conforms to date specified */
655 const char* tmp_ch
= this_param
;
656 iRet
= validate_date_format(ThisFmt
->format
.date_fmt
, &tmp_ch
);
657 if (iRet
!= FTPP_SUCCESS
)
659 /* Alert invalid date */
660 return FTPP_INVALID_PARAM
;
662 if (!isspace((int)(*tmp_ch
)))
664 /* Alert invalid date -- didn't make it to end of parameter.
666 return FTPP_INVALID_PARAM
;
672 /* check that this_param matches the literal specified */
674 const char* s
= ThisFmt
->format
.literal
;
675 size_t n
= strlen(s
);
677 if ( strncmp(this_param
, s
, n
) )
679 /* Alert on non-char */
680 return FTPP_INVALID_PARAM
;
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>| */
694 ThisFmt
->type
, &this_param
, end
, " \n", &ipAddr
, &port
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
;
709 if ( ThisFmt
->type
== e_extd_host_port
&& !ipAddr
.is_set() )
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();
715 if ( session
->client_conf
->bounce
)
717 if (!ipAddr
.equals(*p
->ptrs
.ip_api
.get_src()))
721 FTP_BOUNCE_TO
* BounceTo
= ftp_bounce_lookup_find(
722 session
->client_conf
->bounce_lookup
, &ipAddr
, &iRet
);
725 if (BounceTo
->portlo
)
727 if (BounceTo
->porthi
)
729 if ((port
>= BounceTo
->portlo
) &&
730 (port
<= BounceTo
->porthi
))
735 if (port
== BounceTo
->portlo
)
741 /* Alert on invalid IP address for PORT */
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
748 return FTPP_PORT_ATTACK
;
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
)
759 * If there was a PORT command previously in
760 * a series of pipelined requests, this
763 session
->data_chan_state
&= ~DATA_CHAN_PASV_CMD_ISSUED
;
766 session
->serverIP
.clear();
767 session
->serverPort
= 0;
772 *this_fmt_next_param
= this_param
;
778 * Function: check_ftp_param_validity(
780 * char *params_begin,
782 * FTP_PARAM_FMT *param_format,
783 * const char** this_fmt_next_param,
784 * FTP_SESSION *session)
786 * Purpose: Recursively determines whether each of the parameters for
787 * an FTP command are valid.
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
797 * Returns: int => return code indicating error or success
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
)
807 int iRet
= FTPP_ALERT
;
808 FTP_PARAM_FMT
* ThisFmt
= param_format
;
809 FTP_PARAM_FMT
* NextFmt
;
810 const char* this_param
= params_begin
;
813 return FTPP_INVALID_ARG
;
815 if (!params_begin
&& !ThisFmt
->next_param_fmt
&& ThisFmt
->optional_fmt
)
816 return FTPP_SUCCESS
; /* no param is allowed in this case */
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 */
822 return FTPP_INVALID_ARG
;
824 if ((!ThisFmt
->next_param_fmt
) && (params_begin
>= params_end
))
827 *this_fmt_next_param
= params_begin
;
828 if (ThisFmt
->optional_fmt
)
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
)
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
)
844 this_param
= next_fmt_next_param
+1;
849 if ((iRet
!= FTPP_SUCCESS
) && (ThisFmt
->choices
))
851 /* Check against choices -- one of many */
852 for (int i
= 0; i
< ThisFmt
->numChoices
; 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
)
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
)
868 this_param
= next_fmt_next_param
+1;
874 else if ((iRet
!= FTPP_SUCCESS
) && (ThisFmt
->next_param_fmt
))
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
)
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
)
890 this_param
= next_fmt_next_param
+1;
894 else if ((iRet
!= FTPP_SUCCESS
) && (!ThisFmt
->next_param_fmt
) &&
899 if (iRet
== FTPP_SUCCESS
)
901 *this_fmt_next_param
= this_param
;
907 * Function: initialize_ftp(FTP_SESSION *session, Packet *p, int iMode)
909 * Purpose: Initializes the state machine for checking an FTP packet.
910 * Does normalization checks.
912 * Arguments: session => Pointer to session info
913 * p => pointer to the current packet struct
914 * iMode => Mode indicating server or client checks
916 * Returns: int => return code indicating error or success
919 int initialize_ftp(FTP_SESSION
* session
, Packet
* p
, int iMode
)
921 const unsigned char* read_ptr
= p
->data
;
924 if ( session
->encr_state
== NO_STATE
)
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
;
933 DataBuffer
& buf
= DetectionEngine::get_alt_buffer(p
);
935 iRet
= normalize_telnet(nullptr, p
, buf
, iMode
, ignoreTelnetErase
, true);
937 if (iRet
!= FTPP_SUCCESS
&& iRet
!= FTPP_NORMALIZED
)
939 if (iRet
== FTPP_ALERT
)
940 DetectionEngine::queue_event(GID_FTP
, FTP_EVASIVE_TELNET_CMD
);
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
))
952 DetectionEngine::queue_event(GID_FTP
, FTP_TELNET_CMD
);
953 return FTPP_ALERT
; /* Nothing else to do since we alerted */
960 if (iMode
== FTPP_SI_CLIENT_MODE
)
961 req
= &session
->client
.request
;
962 else if (iMode
== FTPP_SI_SERVER_MODE
)
964 FTP_SERVER_RSP
* rsp
= &session
->server
.response
;
965 req
= (FTP_CLIENT_REQ
*)rsp
;
968 return FTPP_INVALID_ARG
;
970 /* Set the beginning of the pipeline to the start of the
971 * (normalized) buffer */
972 req
->pipeline_req
= (const char*)read_ptr
;
978 * Function: do_stateful_checks(FTP_SESSION *session, Packet *p,
979 * FTP_CLIENT_REQ *req, int rsp_code)
981 * Purpose: Handle stateful checks and state updates for FTP response
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
991 * Returns: int => return code indicating error or success
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
)
999 int iRet
= FTPP_SUCCESS
;
1001 //if (session->server_conf->data_chan)
1003 if (rsp_code
== 226)
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. */
1009 else if (session
->flags
& FTP_PROTP_CMD_ISSUED
)
1011 if (rsp_code
== 200)
1013 session
->flags
&= ~FTP_PROTP_CMD_ISSUED
;
1014 session
->flags
|= FTP_PROTP_CMD_ACCEPT
;
1017 else if (session
->data_chan_state
& DATA_CHAN_PASV_CMD_ISSUED
)
1019 if (ftp_cmd_pipe_index
== session
->data_chan_index
)
1021 if (session
->data_xfer_index
== -1)
1022 ftp_cmd_pipe_index
= 0;
1023 session
->data_chan_index
= -1;
1025 if ( rsp_code
>= 227 && rsp_code
<= 229 )
1029 const char* ip_begin
= req
->param_begin
;
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
1039 if (req
->param_size
!= 0)
1041 while ((ip_begin
< req
->param_end
) &&
1048 if (ip_begin
< req
->param_end
)
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
);
1062 ftyp
, &ip_begin
, req
->param_end
, ")", &ipAddr
, &port
1064 if (iRet
== FTPP_SUCCESS
)
1066 if (!ipAddr
.is_set())
1067 session
->serverIP
= *p
->ptrs
.ip_api
.get_src();
1070 session
->serverIP
= ipAddr
;
1072 session
->serverPort
= port
;
1073 session
->clientIP
= *p
->ptrs
.ip_api
.get_dst();
1074 session
->clientPort
= 0;
1076 if ((FileService::get_max_file_depth() > 0) ||
1077 !(session
->server_conf
->data_chan
))
1079 FtpDataFlowData
* fd
= new FtpDataFlowData(p
);
1080 FTP_DATA_SESSION
* ftpdata
= &fd
->session
;
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
;
1089 if (p
->flow
->flags
.data_decrypted
and
1090 (session
->flags
& FTP_PROTP_CMD_ACCEPT
))
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
);
1103 session
->datassn
= nullptr;
1106 if (!session
->serverIP
.equals(*p
->ptrs
.ip_api
.get_src()))
1108 FtpDataFlowData
* fd1
= new FtpDataFlowData(p
);
1109 FTP_DATA_SESSION
* ftpdata1
= &fd1
->session
;
1111 ftpdata1
->mode
= FTPP_XFER_PASSIVE
;
1112 ftpdata1
->data_chan
= session
->server_conf
->data_chan
;
1114 if (p
->flow
->flags
.data_decrypted
and
1115 (session
->flags
& FTP_PROTP_CMD_ACCEPT
))
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
);
1127 session
->datassn
= nullptr;
1131 else if (session
->server_conf
->data_chan
)
1133 /* Call into Streams to mark data channel as something
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
)));
1145 iRet
= FTPP_MALFORMED_FTP_RESPONSE
;
1150 session
->data_chan_index
= -1;
1151 session
->data_chan_state
&= ~DATA_CHAN_PASV_CMD_ISSUED
;
1155 else if (session
->data_chan_state
& DATA_CHAN_PORT_CMD_ISSUED
)
1157 if (ftp_cmd_pipe_index
== session
->data_chan_index
)
1159 if (session
->data_xfer_index
== -1)
1160 ftp_cmd_pipe_index
= 0;
1161 session
->data_chan_index
= -1;
1162 if (rsp_code
== 200)
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())
1169 /* This means we're not in passive mode. */
1170 /* Server is listening/sending from its own IP,
1172 /* Client IP, Port specified via PORT command */
1173 session
->serverIP
= *p
->ptrs
.ip_api
.get_src();
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.
1181 session->serverPort = ntohs(p->ptrs.tcph->th_sport) -1;
1183 if ((FileService::get_max_file_depth() > 0) ||
1184 !(session
->server_conf
->data_chan
))
1186 FtpDataFlowData
* fd
= new FtpDataFlowData(p
);
1187 FTP_DATA_SESSION
* ftpdata
= &fd
->session
;
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
;
1196 if (p
->flow
->flags
.data_decrypted
and
1197 (session
->flags
& FTP_PROTP_CMD_ACCEPT
))
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);
1210 session
->datassn
= nullptr;
1213 else if (session
->server_conf
->data_chan
)
1215 /* Call into Streams to mark data channel as something
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
)));
1225 else if (ftp_cmd_pipe_index
== session
->data_chan_index
)
1227 session
->data_chan_index
= -1;
1228 session
->data_chan_state
&= ~DATA_CHAN_PORT_CMD_ISSUED
;
1232 else if (session
->data_chan_state
& DATA_CHAN_REST_CMD_ISSUED
)
1234 if (ftp_cmd_pipe_index
== session
->data_xfer_index
)
1236 if (session
->data_chan_index
== 0)
1237 ftp_cmd_pipe_index
= 1;
1238 session
->data_xfer_index
= 0;
1239 if (rsp_code
== 350)
1241 FTP_DATA_SESSION
*ftpdata
= (FTP_DATA_SESSION
*)session
->datassn
;
1243 if ((session
->flags
& FTP_FLG_MALWARE
) && ftpdata
)
1245 ftpdata
->packet_flags
|= FTPDATA_FLG_REST
;
1246 session
->datassn
= nullptr;
1249 session
->data_chan_index
= 0;
1250 session
->data_chan_state
&= ~DATA_CHAN_REST_CMD_ISSUED
;
1253 else if (session
->data_chan_state
& DATA_CHAN_XFER_CMD_ISSUED
)
1255 if (ftp_cmd_pipe_index
== session
->data_xfer_index
)
1257 if (session
->data_chan_index
== -1)
1258 ftp_cmd_pipe_index
= 0;
1260 session
->data_xfer_index
= -1;
1262 if ((rsp_code
== 150) || (rsp_code
== 125))
1263 session
->data_chan_state
= DATA_CHAN_XFER_STARTED
;
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;
1272 session
->data_chan_state
= NO_STATE
;
1275 } /* if (session->server_conf->data_chan) */
1277 if ((session
->encr_state
== AUTH_TLS_CMD_ISSUED
or session
->encr_state
== AUTH_SSL_CMD_ISSUED
)
1278 and rsp_code
== 234)
1280 OpportunisticTlsEvent
event(p
, p
->flow
->service
);
1281 DataBus::publish(intrinsic_pub_id
, IntrinsicEventIds::OPPORTUNISTIC_TLS
, event
, p
->flow
);
1283 if (session
->flags
& FTP_FLG_SEARCH_ABANDONED
)
1284 ++ftstats
.ssl_search_abandoned_too_soon
;
1287 if (session
->server_conf
->detect_encrypted
)
1289 switch (session
->encr_state
)
1291 case AUTH_TLS_CMD_ISSUED
:
1292 if (rsp_code
== 234)
1294 /* Could check that response msg includes "TLS" */
1295 session
->encr_state
= AUTH_TLS_ENCRYPTED
;
1296 DetectionEngine::queue_event(GID_FTP
, FTP_ENCRYPTED
);
1299 case AUTH_SSL_CMD_ISSUED
:
1300 if (rsp_code
== 234)
1302 /* Could check that response msg includes "SSL" */
1303 session
->encr_state
= AUTH_SSL_ENCRYPTED
;
1304 DetectionEngine::queue_event(GID_FTP
, FTP_ENCRYPTED
);
1307 case AUTH_UNKNOWN_CMD_ISSUED
:
1308 if (rsp_code
== 234)
1310 session
->encr_state
= AUTH_UNKNOWN_ENCRYPTED
;
1311 DetectionEngine::queue_event(GID_FTP
, FTP_ENCRYPTED
);
1321 * Function: check_ftp(FTP_SESSION *session, Packet *p, int iMode)
1323 * Purpose: Handle some trivial validation checks of an FTP packet. Namely,
1324 * check argument length and some protocol enforcement.
1326 * Wishful: This results in exposing the FTP command (and looking
1327 * at the results) to the rules layer.
1329 * Arguments: session => Pointer to session info
1330 * p => pointer to the current packet struct
1331 * iMode => Mode indicating server or client checks
1333 * Returns: int => return code indicating error or success
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
1350 int check_ftp(FTP_SESSION
* ftpssn
, Packet
* p
, int iMode
)
1352 int iRet
= FTPP_SUCCESS
;
1356 FTP_CLIENT_REQ
* req
;
1357 FTP_CMD_CONF
* CmdConf
= nullptr;
1359 const unsigned char* end
= p
->data
+ p
->dsize
;
1361 const DataBuffer
& buf
= DetectionEngine::get_alt_buffer(p
);
1363 end
= buf
.data
+ buf
.len
;
1365 if (iMode
== FTPP_SI_CLIENT_MODE
)
1367 req
= &ftpssn
->client
.request
;
1368 ftp_cmd_pipe_index
= 0;
1370 else if (iMode
== FTPP_SI_SERVER_MODE
)
1372 FTP_SERVER_RSP
* rsp
= &ftpssn
->server
.response
;
1373 req
= (FTP_CLIENT_REQ
*)rsp
;
1376 return FTPP_INVALID_ARG
;
1378 while (req
->pipeline_req
)
1380 long state
= FTP_CMD_OK
;
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
;
1385 /* but first we ignore leading white space */
1386 while ( (read_ptr
< end
) &&
1387 (iMode
== FTPP_SI_CLIENT_MODE
) && isspace(*read_ptr
) )
1390 // ignore extra \r\n emitted by some clients
1391 if ( read_ptr
== end
)
1394 req
->cmd_begin
= (const char*)read_ptr
;
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
1401 (*read_ptr
!= DASH
))
1403 /* If the first char is a digit this is a response
1404 * in server mode. */
1405 if (iMode
== FTPP_SI_SERVER_MODE
)
1407 if (isdigit(*read_ptr
))
1409 if (state
!= FTP_RESPONSE_INV
)
1411 state
= FTP_RESPONSE
;
1414 else if (!isascii(*read_ptr
))
1416 /* Non-ascii char here? Bad response */
1417 state
= FTP_RESPONSE_INV
;
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
))
1423 state
= FTP_CMD_INV
;
1428 req
->cmd_end
= (const char*)read_ptr
;
1429 req
->cmd_size
= req
->cmd_end
- req
->cmd_begin
;
1431 if (iMode
== FTPP_SI_CLIENT_MODE
)
1433 if ( (req
->cmd_size
> ftpssn
->server_conf
->max_cmd_len
)
1434 || (req
->cmd_size
< MIN_CMD
)
1435 || (state
== FTP_CMD_INV
) )
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
)
1443 if (!isalpha((int)(*ptr
)))
1445 if (!isascii((int)(*ptr
)) || (!isprint((int)(*ptr
)) && (!isspace((int)(*ptr
)))))
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.
1462 if (ftpssn
->encr_state
== 0)
1464 ftpssn
->encr_state
= AUTH_UNKNOWN_ENCRYPTED
;
1465 DetectionEngine::queue_event(GID_FTP
, FTP_ENCRYPTED
);
1467 if (!ftpssn
->server_conf
->check_encrypted_data
)
1469 /* Mark this session & packet as one to ignore */
1470 Stream::stop_inspection(p
->flow
, p
, SSN_DIR_BOTH
, -1, 0);
1478 * Check the list of valid FTP commands as
1479 * supplied in ftpssn.
1481 if ( req
->cmd_size
> ftpssn
->server_conf
->max_cmd_len
)
1483 /* Alert, cmd not found */
1484 DetectionEngine::queue_event(GID_FTP
, FTP_INVALID_CMD
);
1485 state
= FTP_CMD_INV
;
1489 CmdConf
= ftp_cmd_lookup_find(ftpssn
->server_conf
->cmd_lookup
,
1493 if ((iRet
== FTPP_NOT_FOUND
) || (CmdConf
== nullptr))
1495 /* Alert, cmd not found */
1496 DetectionEngine::queue_event(GID_FTP
, FTP_INVALID_CMD
);
1497 state
= FTP_CMD_INV
;
1501 /* In case we were encrypted, but aren't now */
1502 ftpssn
->encr_state
= 0;
1507 else if (iMode
== FTPP_SI_SERVER_MODE
)
1509 if (state
== FTP_CMD_INV
)
1510 state
= FTP_RESPONSE_INV
;
1512 if ( (req
->cmd_size
!= 3) || (state
== FTP_RESPONSE_INV
) )
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
)
1520 if (!isdigit((int)(*ptr
)))
1522 if (!isascii((int)(*ptr
)) || (!isprint((int)(*ptr
)) && (!isspace((int)(*ptr
)))))
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.
1539 if (ftpssn
->encr_state
== 0)
1541 ftpssn
->encr_state
= AUTH_UNKNOWN_ENCRYPTED
;
1542 DetectionEngine::queue_event(GID_FTP
, FTP_ENCRYPTED
);
1544 if (!ftpssn
->server_conf
->check_encrypted_data
)
1546 /* Mark this session & packet as one to ignore */
1547 Stream::stop_inspection(p
->flow
, p
, SSN_DIR_BOTH
, -1, 0);
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
))
1559 ftpssn
->encr_state
= 0;
1562 /* Otherwise, might have an encryption command pending */
1567 if (*read_ptr
!= DASH
)
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)
1573 if (isdigit(*(resp_begin
)) &&
1574 isdigit(*(resp_begin
+1)) &&
1575 isdigit(*(resp_begin
+2)) )
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
)
1582 /* End of continued response */
1583 state
= FTP_RESPONSE_ENDCONT
;
1584 ftpssn
->server
.response
.state
= 0;
1588 /* Single line response */
1589 state
= FTP_RESPONSE
;
1594 if (ftpssn
->server
.response
.state
!= 0)
1596 req
->cmd_begin
= nullptr;
1597 req
->cmd_end
= nullptr;
1598 if (*read_ptr
!= SP
&& read_ptr
!= p
->data
)
1600 state
= FTP_RESPONSE_CONT
;
1603 else if ((state
== FTP_RESPONSE
) && (*read_ptr
== DASH
))
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)) )
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
)
1615 /* Continuation of previous response */
1616 state
= FTP_RESPONSE_CONT
;
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
;
1628 ftpssn
->server
.response
.state
= FTP_RESPONSE_INV
;
1636 if (isspace(*read_ptr
))
1641 read_ptr
++; /* Move past the space, dash, or CR */
1644 /* If there is anything left... */
1648 /* Look for an LF --> implies no parameters/message */
1649 if (*read_ptr
== LF
)
1652 req
->param_begin
= nullptr;
1653 req
->param_end
= nullptr;
1654 req
->param_size
= 0;
1656 else if (space
|| ftpssn
->server
.response
.state
!= 0)
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)
1663 req
->param_end
= (const char*)read_ptr
;
1664 req
->param_size
= req
->param_end
- req
->param_begin
;
1669 /* Cool, got the end of the parameters, move past
1670 * the LF, so we can process the next one in
1673 if (*read_ptr
== LF
)
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;
1686 /* Set the pointer for the next request/response
1687 * in the pipeline. */
1689 req
->pipeline_req
= (const char*)read_ptr
;
1691 req
->pipeline_req
= nullptr;
1698 case FTP_RESPONSE
: /* Response */
1699 if ((ftpssn
->client_conf
->max_resp_len
> 0) &&
1700 (req
->param_size
> ftpssn
->client_conf
->max_resp_len
))
1702 /* Alert on response message overflow */
1703 DetectionEngine::queue_event(GID_FTP
, FTP_RESPONSE_LENGTH_OVERFLOW
);
1708 int newRet
= do_stateful_checks(ftpssn
, p
, req
, rsp_code
);
1709 if (newRet
!= FTPP_SUCCESS
)
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
))
1717 /* Alert on response message overflow */
1718 DetectionEngine::queue_event(GID_FTP
, FTP_RESPONSE_LENGTH_OVERFLOW
);
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
))
1726 /* Alert on response message overflow */
1727 DetectionEngine::queue_event(GID_FTP
, FTP_RESPONSE_LENGTH_OVERFLOW
);
1734 unsigned max
= CmdConf
->max_param_len
;
1736 max
= ftpssn
->server_conf
->def_max_param_len
;
1738 if ( req
->param_size
> max
)
1740 /* Alert on param length overrun */
1741 DetectionEngine::queue_event(GID_FTP
, FTP_PARAMETER_LENGTH_OVERFLOW
);
1745 if (CmdConf
->data_chan_cmd
)
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
)
1752 * If there was a PORT command previously in
1753 * a series of pipelined requests, this
1756 ftpssn
->data_chan_state
&= ~DATA_CHAN_PORT_CMD_ISSUED
;
1759 else if ((ftpssn
->flags
& FTP_FLG_MALWARE
) && CmdConf
->data_rest_cmd
)
1761 if ((req
->param_begin
!= nullptr) && (req
->param_size
> 0))
1763 char *return_ptr
= nullptr;
1765 unsigned long offset
= strtoul(req
->param_begin
, &return_ptr
, 10);
1766 if ((errno
== ERANGE
|| errno
== EINVAL
) || (offset
> 0))
1768 ftpssn
->data_chan_state
|= DATA_CHAN_REST_CMD_ISSUED
;
1769 ftpssn
->data_xfer_index
= ftp_cmd_pipe_index
;
1773 else if (CmdConf
->data_xfer_cmd
)
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))
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
)
1784 snort_free(ftpssn
->filename
);
1785 ftpssn
->filename
= nullptr;
1786 ftpssn
->path_hash
= 0;
1787 ftpssn
->file_xfer_info
= FTPP_FILE_IGNORE
;
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
))
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
, '/');
1802 file_name
= ftpssn
->filename
;
1803 ftpssn
->path_hash
= str_to_hash((uint8_t *)file_name
, strlen(file_name
));
1805 // 0 for Download, 1 for Upload
1806 ftpssn
->data_xfer_dir
= CmdConf
->file_get_cmd
? false : true;
1810 ftpssn
->file_xfer_info
= FTPP_FILE_IGNORE
;
1813 ftpssn
->data_chan_state
|= DATA_CHAN_XFER_CMD_ISSUED
;
1814 ftpssn
->data_xfer_index
= ftp_cmd_pipe_index
;
1816 else if (CmdConf
->encr_cmd
)
1818 if (req
->param_begin
&& (req
->param_size
> 0) &&
1819 ((req
->param_begin
[0] == 'T') || (req
->param_begin
[0] == 't')))
1821 ftpssn
->encr_state
= AUTH_TLS_CMD_ISSUED
;
1823 else if (req
->param_begin
&& (req
->param_size
> 0) &&
1824 ((req
->param_begin
[0] == 'S') || (req
->param_begin
[0] == 's')))
1826 ftpssn
->encr_state
= AUTH_SSL_CMD_ISSUED
;
1830 ftpssn
->encr_state
= AUTH_UNKNOWN_CMD_ISSUED
;
1833 else if (CmdConf
->prot_cmd
)
1835 if (req
->param_begin
&& (req
->param_size
> 0) &&
1836 ((req
->param_begin
[0] == 'P') || (req
->param_begin
[0] == 'p')))
1838 ftpssn
->flags
&= ~FTP_PROTP_CMD_ACCEPT
;
1839 ftpssn
->flags
|= FTP_PROTP_CMD_ISSUED
;
1842 else if (CmdConf
->login_cmd
and !p
->flow
->flags
.data_decrypted
and
1843 !(ftpssn
->flags
& FTP_FLG_SEARCH_ABANDONED
))
1845 ftpssn
->flags
|= FTP_FLG_SEARCH_ABANDONED
;
1846 DataBus::publish(intrinsic_pub_id
, IntrinsicEventIds::SSL_SEARCH_ABANDONED
, p
);
1847 ++ftstats
.ssl_search_abandoned
;
1850 if (CmdConf
->check_validity
)
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
,
1856 /* If negative, haven't already alerted on violation */
1859 /* Set Alert on malformatted parameter */
1860 DetectionEngine::queue_event(GID_FTP
, FTP_MALFORMED_PARAMETER
);
1866 /* Already alerted -- ie, string format attack. */
1874 if (iMode
== FTPP_SI_CLIENT_MODE
)
1875 ftp_cmd_pipe_index
++;
1876 else if ((rsp_code
!= 226) && (rsp_code
!= 426))
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.
1885 ftp_cmd_pipe_index
++;
1889 if (iMode
== FTPP_SI_CLIENT_MODE
)
1891 ftp_cmd_pipe_index
= 0;