1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 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  */
getIP959(const char ** ip_start,const char * last_char,const char * term_char,SfIp * ipRet,uint16_t * portRet)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  */
getIP1639(const char ** ip_start,const char * last_char,const char *,SfIp * ipRet,uint16_t * portRet)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  */
CopyField(char * buf,const char * tok,int max,const char * end,char delim)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 
getIP2428(const char ** ip_start,const char * last_char,const char *,SfIp * ipRet,uint16_t * portRet,FTP_PARAM_TYPE ftyp)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 
getFTPip(FTP_PARAM_TYPE ftyp,const char ** ip_start,const char * last_char,const char * term_char,SfIp * ipRet,uint16_t * portRet)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  */
validate_date_format(FTP_DATE_FMT * ThisFmt,const char ** this_param)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  */
validate_param(Packet * p,const char * param,const char * end,FTP_PARAM_FMT * ThisFmt,const char ** this_fmt_next_param,FTP_SESSION * session)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  */
check_ftp_param_validity(Packet * p,const char * params_begin,const char * params_end,FTP_PARAM_FMT * param_format,const char ** this_fmt_next_param,FTP_SESSION * session)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  */
initialize_ftp(FTP_SESSION * session,Packet * p,int iMode)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?
do_stateful_checks(FTP_SESSION * session,Packet * p,FTP_CLIENT_REQ * req,int rsp_code)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->clientIP, session->clientPort,
1204                                 &session->serverIP, session->serverPort,
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->clientIP, session->clientPort,
1220                                 &session->serverIP, session->serverPort,
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(OPPORTUNISTIC_TLS_EVENT, 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 
check_ftp(FTP_SESSION * ftpssn,Packet * p,int iMode)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             (*read_ptr != SP) &&
1398             (*read_ptr != CR) &&
1399             (*read_ptr != LF) &&    /* Check for LF when there wasn't a CR,
1400                                      * protocol violation, but accepted by
1401                                      * some servers. */
1402             (*read_ptr != DASH))
1403         {
1404             /* If the first char is a digit this is a response
1405              * in server mode. */
1406             if (iMode == FTPP_SI_SERVER_MODE)
1407             {
1408                 if (isdigit(*read_ptr))
1409                 {
1410                     if (state != FTP_RESPONSE_INV)
1411                     {
1412                         state = FTP_RESPONSE;
1413                     }
1414                 }
1415                 else if (!isascii(*read_ptr))
1416                 {
1417                     /* Non-ascii char here?  Bad response */
1418                     state = FTP_RESPONSE_INV;
1419                 }
1420             }
1421             /* Or, if this is not a char, this is garbage in client mode */
1422             else if (!isalpha(*read_ptr) && (iMode == FTPP_SI_CLIENT_MODE))
1423             {
1424                 state = FTP_CMD_INV;
1425             }
1426 
1427             read_ptr++;
1428         }
1429         req->cmd_end = (const char*)read_ptr;
1430         req->cmd_size = req->cmd_end - req->cmd_begin;
1431 
1432         if (iMode == FTPP_SI_CLIENT_MODE)
1433         {
1434             if ( (req->cmd_size > ftpssn->server_conf->max_cmd_len)
1435                 || (req->cmd_size < MIN_CMD)
1436                 || (state == FTP_CMD_INV) )
1437             {
1438                 /* Uh, something is very wrong...
1439                  * nonalpha char seen or cmd is bad length.
1440                  * See if this might be encrypted, ie, non-alpha bytes. */
1441                 const unsigned char* ptr = (const unsigned char*)req->cmd_begin;
1442                 while (ptr < (const unsigned char*)req->cmd_end)
1443                 {
1444                     if (!isalpha((int)(*ptr)))
1445                     {
1446                         if (!isascii((int)(*ptr)) || !isprint((int)(*ptr)))
1447                         {
1448                             encrypted = 1;
1449                         }
1450                         break;
1451                     }
1452                     ptr++;
1453                 }
1454             }
1455 
1456             if (encrypted)
1457             {
1458                 /* If the session wasn't already marked as encrypted...
1459                  * Don't want to double-alert if we've already
1460                  * determined the session is encrypted and we're
1461                  * checking encrypted sessions.
1462                  */
1463                 if (ftpssn->encr_state == 0)
1464                 {
1465                     ftpssn->encr_state = AUTH_UNKNOWN_ENCRYPTED;
1466                     DetectionEngine::queue_event(GID_FTP, FTP_ENCRYPTED);
1467 
1468                     if (!ftpssn->server_conf->check_encrypted_data)
1469                     {
1470                         /* Mark this session & packet as one to ignore */
1471                         Stream::stop_inspection(p->flow, p, SSN_DIR_BOTH, -1, 0);
1472                     }
1473                 }
1474                 break;
1475             }
1476             else
1477             {
1478                 /*
1479                  * Check the list of valid FTP commands as
1480                  * supplied in ftpssn.
1481                  */
1482                 if ( req->cmd_size > ftpssn->server_conf->max_cmd_len )
1483                 {
1484                     /* Alert, cmd not found */
1485                     DetectionEngine::queue_event(GID_FTP, FTP_INVALID_CMD);
1486                     state = FTP_CMD_INV;
1487                 }
1488                 else
1489                 {
1490                     CmdConf = ftp_cmd_lookup_find(ftpssn->server_conf->cmd_lookup,
1491                         req->cmd_begin,
1492                         req->cmd_size,
1493                         &iRet);
1494                     if ((iRet == FTPP_NOT_FOUND) || (CmdConf == nullptr))
1495                     {
1496                         /* Alert, cmd not found */
1497                         DetectionEngine::queue_event(GID_FTP, FTP_INVALID_CMD);
1498                         state = FTP_CMD_INV;
1499                     }
1500                     else
1501                     {
1502                         /* In case we were encrypted, but aren't now */
1503                         ftpssn->encr_state = 0;
1504                     }
1505                 }
1506             }
1507         }
1508         else if (iMode == FTPP_SI_SERVER_MODE)
1509         {
1510             if (state == FTP_CMD_INV)
1511                 state = FTP_RESPONSE_INV;
1512 
1513             if ( (req->cmd_size != 3) || (state == FTP_RESPONSE_INV) )
1514             {
1515                 /* Uh, something is very wrong...
1516                  * nondigit char seen or resp code is not 3 chars.
1517                  * See if this might be encrypted, ie, non-alpha bytes. */
1518                 const char* ptr = req->cmd_begin;
1519                 while (ptr < req->cmd_end)
1520                 {
1521                     if (!isdigit((int)(*ptr)))
1522                     {
1523                         if (!isascii((int)(*ptr)) || !isprint((int)(*ptr)))
1524                         {
1525                             encrypted = 1;
1526                         }
1527                         break;
1528                     }
1529                     ptr++;
1530                 }
1531             }
1532 
1533             if (encrypted)
1534             {
1535                 /* If the session wasn't already marked as encrypted...
1536                  * Don't want to double-alert if we've already
1537                  * determined the session is encrypted and we're
1538                  * checking encrypted sessions.
1539                  */
1540                 if (ftpssn->encr_state == 0)
1541                 {
1542                     ftpssn->encr_state = AUTH_UNKNOWN_ENCRYPTED;
1543                     DetectionEngine::queue_event(GID_FTP, FTP_ENCRYPTED);
1544 
1545                     if (!ftpssn->server_conf->check_encrypted_data)
1546                     {
1547                         /* Mark this session & packet as one to ignore */
1548                         Stream::stop_inspection(p->flow, p, SSN_DIR_BOTH, -1, 0);
1549                     }
1550                 }
1551                 break;
1552             }
1553             else
1554             {
1555                 /* In case we were encrypted, but aren't now */
1556                 if ((ftpssn->encr_state == AUTH_TLS_ENCRYPTED) ||
1557                     (ftpssn->encr_state == AUTH_SSL_ENCRYPTED) ||
1558                     (ftpssn->encr_state == AUTH_UNKNOWN_ENCRYPTED))
1559                 {
1560                     ftpssn->encr_state = 0;
1561                 }
1562 
1563                 /* Otherwise, might have an encryption command pending */
1564             }
1565 
1566             if (read_ptr < end)
1567             {
1568                 if (*read_ptr != DASH)
1569                 {
1570                     const unsigned char* resp_begin = (const unsigned char*)req->cmd_begin;
1571                     const unsigned char* resp_end = (const unsigned char*)req->cmd_end;
1572                     if (resp_end - resp_begin >= 3)
1573                     {
1574                         if (isdigit(*(resp_begin)) &&
1575                             isdigit(*(resp_begin+1)) &&
1576                             isdigit(*(resp_begin+2)) )
1577                         {
1578                             rsp_code = ( (*(resp_begin) - '0') * 100 +
1579                                 (*(resp_begin+1) - '0') * 10 +
1580                                 (*(resp_begin+2) - '0') );
1581                             if (rsp_code == ftpssn->server.response.state)
1582                             {
1583                                 /* End of continued response */
1584                                 state = FTP_RESPONSE_ENDCONT;
1585                                 ftpssn->server.response.state = 0;
1586                             }
1587                             else
1588                             {
1589                                 /* Single line response */
1590                                 state = FTP_RESPONSE;
1591                             }
1592                         }
1593                     }
1594 
1595                     if (ftpssn->server.response.state != 0)
1596                     {
1597                         req->cmd_begin = nullptr;
1598                         req->cmd_end = nullptr;
1599                         if (*read_ptr != SP)
1600                             read_ptr--;
1601                         state = FTP_RESPONSE_CONT;
1602                     }
1603                 }
1604                 else if ((state == FTP_RESPONSE) && (*read_ptr == DASH))
1605                 {
1606                     const unsigned char* resp_begin = (const unsigned char*)req->cmd_begin;
1607                     if (isdigit(*(resp_begin)) &&
1608                         isdigit(*(resp_begin+1)) &&
1609                         isdigit(*(resp_begin+2)) )
1610                     {
1611                         int resp_code = ( (*(resp_begin) - '0') * 100 +
1612                             (*(resp_begin+1) - '0') * 10 +
1613                             (*(resp_begin+2) - '0') );
1614                         if (resp_code == ftpssn->server.response.state)
1615                         {
1616                             /* Continuation of previous response */
1617                             state = FTP_RESPONSE_CONT;
1618                         }
1619                         else
1620                         {
1621                             /* Start of response, state stays as -2 */
1622                             state = FTP_RESPONSE_2BCONT;
1623                             ftpssn->server.response.state = resp_code;
1624                             rsp_code = resp_code;
1625                         }
1626                     }
1627                     else
1628                     {
1629                         ftpssn->server.response.state = FTP_RESPONSE_INV;
1630                     }
1631                 }
1632             }
1633         }
1634 
1635         if (read_ptr < end)
1636         {
1637             if (*read_ptr == SP)
1638             {
1639                 space = 1;
1640             }
1641 
1642             read_ptr++; /* Move past the space, dash, or CR */
1643         }
1644 
1645         /* If there is anything left... */
1646 
1647         if (read_ptr < end)
1648         {
1649             /* Look for an LF --> implies no parameters/message */
1650             if (*read_ptr == LF)
1651             {
1652                 read_ptr++;
1653                 req->param_begin = nullptr;
1654                 req->param_end = nullptr;
1655                 req->param_size = 0;
1656             }
1657             else if (space || ftpssn->server.response.state != 0)
1658             {
1659                 /* Now grab the command parameters/response message
1660                  * read_ptr < end already checked */
1661                 req->param_begin = (const char*)read_ptr;
1662                 if ((read_ptr = (const unsigned char*)memchr(read_ptr, CR, end - read_ptr)) == nullptr)
1663                     read_ptr = end;
1664                 req->param_end = (const char*)read_ptr;
1665                 req->param_size = req->param_end - req->param_begin;
1666                 read_ptr++;
1667 
1668                 if (read_ptr < end)
1669                 {
1670                     /* Cool, got the end of the parameters, move past
1671                      * the LF, so we can process the next one in
1672                      * the pipeline.
1673                      */
1674                     if (*read_ptr == LF)
1675                         read_ptr++;
1676                 }
1677             }
1678         }
1679         else
1680         {
1681             /* Nothing left --> no parameters/message.  Not even an LF */
1682             req->param_begin = nullptr;
1683             req->param_end = nullptr;
1684             req->param_size = 0;
1685         }
1686 
1687         /* Set the pointer for the next request/response
1688          * in the pipeline. */
1689         if (read_ptr < end)
1690             req->pipeline_req = (const char*)read_ptr;
1691         else
1692             req->pipeline_req = nullptr;
1693 
1694         switch (state)
1695         {
1696         case FTP_CMD_INV:
1697             iRet = FTPP_ALERT;
1698             break;
1699         case FTP_RESPONSE: /* Response */
1700             if ((ftpssn->client_conf->max_resp_len > 0) &&
1701                 (req->param_size > ftpssn->client_conf->max_resp_len))
1702             {
1703                 /* Alert on response message overflow */
1704                 DetectionEngine::queue_event(GID_FTP, FTP_RESPONSE_LENGTH_OVERFLOW);
1705                 iRet = FTPP_ALERT;
1706             }
1707 
1708             {
1709                 int newRet = do_stateful_checks(ftpssn, p, req, rsp_code);
1710                 if (newRet != FTPP_SUCCESS)
1711                     iRet = newRet;
1712             }
1713             break;
1714         case FTP_RESPONSE_CONT: /* Response continued */
1715             if ((ftpssn->client_conf->max_resp_len > 0) &&
1716                 (req->param_size > ftpssn->client_conf->max_resp_len))
1717             {
1718                 /* Alert on response message overflow */
1719                 DetectionEngine::queue_event(GID_FTP, FTP_RESPONSE_LENGTH_OVERFLOW);
1720                 iRet = FTPP_ALERT;
1721             }
1722             break;
1723         case FTP_RESPONSE_ENDCONT: /* Continued response end */
1724             if ((ftpssn->client_conf->max_resp_len > 0) &&
1725                 (req->param_size > ftpssn->client_conf->max_resp_len))
1726             {
1727                 /* Alert on response message overflow */
1728                 DetectionEngine::queue_event(GID_FTP, FTP_RESPONSE_LENGTH_OVERFLOW);
1729                 iRet = FTPP_ALERT;
1730             }
1731             break;
1732         default:
1733             if (CmdConf)
1734             {
1735                 unsigned max = CmdConf->max_param_len;
1736                 if ( !max )
1737                     max = ftpssn->server_conf->def_max_param_len;
1738 
1739                 if ( req->param_size > max )
1740                 {
1741                     /* Alert on param length overrun */
1742                     DetectionEngine::queue_event(GID_FTP, FTP_PARAMETER_LENGTH_OVERFLOW);
1743                     iRet = FTPP_ALERT;
1744                 }
1745 
1746                 if (CmdConf->data_chan_cmd)
1747                 {
1748                     ftpssn->data_chan_state |= DATA_CHAN_PASV_CMD_ISSUED;
1749                     ftpssn->data_chan_index = ftp_cmd_pipe_index;
1750                     if (ftpssn->data_chan_state & DATA_CHAN_PORT_CMD_ISSUED)
1751                     {
1752                         /*
1753                          * If there was a PORT command previously in
1754                          * a series of pipelined requests, this
1755                          * cancels it.
1756                          */
1757                         ftpssn->data_chan_state &= ~DATA_CHAN_PORT_CMD_ISSUED;
1758                     }
1759                 }
1760                 else if ((ftpssn->flags & FTP_FLG_MALWARE) && CmdConf->data_rest_cmd)
1761                 {
1762                     if ((req->param_begin != nullptr) && (req->param_size > 0))
1763                     {
1764                         char *return_ptr = nullptr;
1765                         errno = 0;
1766                         unsigned long offset = strtoul(req->param_begin, &return_ptr, 10);
1767                         if ((errno == ERANGE || errno == EINVAL) || (offset > 0))
1768                         {
1769                             ftpssn->data_chan_state |= DATA_CHAN_REST_CMD_ISSUED;
1770                             ftpssn->data_xfer_index = ftp_cmd_pipe_index;
1771                         }
1772                     }
1773                 }
1774                 else if (CmdConf->data_xfer_cmd)
1775                 {
1776                     /* If we are not ignoring the data channel OR file processing is enabled */
1777                     if (!ftpssn->server_conf->data_chan || (FileService::get_max_file_depth() > -1))
1778                     {
1779                         /* The following  check cleans up filename  for failed data
1780                          * transfers.  If  the  transfer had  been  successful  the
1781                          * filename  pointer  would have  been  handed  off to  the
1782                          * FTP_DATA_SESSION for tracking. */
1783                         if (ftpssn->filename)
1784                         {
1785                             snort_free(ftpssn->filename);
1786                             ftpssn->filename = nullptr;
1787                             ftpssn->path_hash = 0;
1788                             ftpssn->file_xfer_info = FTPP_FILE_IGNORE;
1789                         }
1790 
1791                         // Get the file name and set direction of the get/put request.
1792                         // Request could have been sent without parameters, i.e. filename,
1793                         // so make sure something is there.
1794                         if (((req->param_begin != nullptr) && (req->param_size > 0))
1795                             && (CmdConf->file_get_cmd || CmdConf->file_put_cmd))
1796                         {
1797                             ftpssn->filename = (char*)snort_alloc(req->param_size+1);
1798                             memcpy(ftpssn->filename, req->param_begin, req->param_size);
1799                             ftpssn->filename[req->param_size] = '\0';
1800                             ftpssn->file_xfer_info = req->param_size;
1801                             char *file_name = strrchr(ftpssn->filename, '/');
1802                             if(!file_name)
1803                                 file_name = ftpssn->filename;
1804                             ftpssn->path_hash = str_to_hash((uint8_t *)file_name, strlen(file_name));
1805 
1806                             // 0 for Download, 1 for Upload
1807                             ftpssn->data_xfer_dir = CmdConf->file_get_cmd ? false : true;
1808                         }
1809                         else
1810                         {
1811                             ftpssn->file_xfer_info = FTPP_FILE_IGNORE;
1812                         }
1813                     }
1814                     ftpssn->data_chan_state |= DATA_CHAN_XFER_CMD_ISSUED;
1815                     ftpssn->data_xfer_index = ftp_cmd_pipe_index;
1816                 }
1817                 else if (CmdConf->encr_cmd)
1818                 {
1819                     if (req->param_begin && (req->param_size > 0) &&
1820                         ((req->param_begin[0] == 'T') || (req->param_begin[0] == 't')))
1821                     {
1822                         ftpssn->encr_state = AUTH_TLS_CMD_ISSUED;
1823                     }
1824                     else if (req->param_begin && (req->param_size > 0) &&
1825                         ((req->param_begin[0] == 'S') || (req->param_begin[0] == 's')))
1826                     {
1827                         ftpssn->encr_state = AUTH_SSL_CMD_ISSUED;
1828                     }
1829                     else
1830                     {
1831                         ftpssn->encr_state = AUTH_UNKNOWN_CMD_ISSUED;
1832                     }
1833                 }
1834                 else if (CmdConf->prot_cmd)
1835                 {
1836                     if (req->param_begin && (req->param_size > 0) &&
1837                         ((req->param_begin[0] == 'P') || (req->param_begin[0] == 'p')))
1838                     {
1839                         ftpssn->flags &= ~FTP_PROTP_CMD_ACCEPT;
1840                         ftpssn->flags |= FTP_PROTP_CMD_ISSUED;
1841                     }
1842                 }
1843                 else if (CmdConf->login_cmd and !p->flow->flags.data_decrypted and
1844                     !(ftpssn->flags & FTP_FLG_SEARCH_ABANDONED))
1845                 {
1846                     ftpssn->flags |= FTP_FLG_SEARCH_ABANDONED;
1847                     DataBus::publish(SSL_SEARCH_ABANDONED, p);
1848                     ++ftstats.ssl_search_abandoned;
1849                 }
1850 
1851                 if (CmdConf->check_validity)
1852                 {
1853                     const char* next_param = nullptr;
1854                     iRet = check_ftp_param_validity(p, req->param_begin,
1855                         req->param_end, CmdConf->param_format, &next_param,
1856                         ftpssn);
1857                     /* If negative, haven't already alerted on violation */
1858                     if (iRet < 0)
1859                     {
1860                         /* Set Alert on malformatted parameter */
1861                         DetectionEngine::queue_event(GID_FTP, FTP_MALFORMED_PARAMETER);
1862                         iRet = FTPP_ALERT;
1863                         break;
1864                     }
1865                     else if (iRet > 0)
1866                     {
1867                         /* Already alerted -- ie, string format attack. */
1868                         break;
1869                     }
1870                 }
1871             }
1872             break;
1873         }
1874 
1875         if (iMode == FTPP_SI_CLIENT_MODE)
1876             ftp_cmd_pipe_index++;
1877         else if ((rsp_code != 226) && (rsp_code != 426))
1878         {
1879             /*
1880              * In terms of counting responses, ignore
1881              * 226 response saying transfer complete
1882              * 426 response saying transfer aborted
1883              * The 226 may or may not be sent by the server.
1884              * Both are 2nd response to a transfer command.
1885              */
1886             ftp_cmd_pipe_index++;
1887         }
1888     }
1889 
1890     if (iMode == FTPP_SI_CLIENT_MODE)
1891     {
1892         ftp_cmd_pipe_index = 0;
1893     }
1894 
1895     if (encrypted)
1896         return FTPP_ALERT;
1897 
1898     return iRet;
1899 }
1900 
1901