1 /*
2  * snort_ftptelnet.c
3  *
4  * Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
5  * Copyright (C) 2004-2013 Sourcefire, Inc.
6  * Steven A. Sturges <ssturges@sourcefire.com>
7  * Daniel J. Roelker <droelker@sourcefire.com>
8  * Marc A. Norton <mnorton@sourcefire.com>
9  * Kevin Liu <kliu@sourcefire.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License Version 2 as
13  * published by the Free Software Foundation.  You may not use, modify or
14  * distribute this program under any other version of the GNU General
15  * Public License.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
25  *
26  * Description:
27  *
28  * This file wraps the FTPTelnet functionality for Snort
29  * and starts the Normalization & Protocol checks.
30  *
31  * The file takes a Packet structure from the Snort IDS to start the
32  * FTP/Telnet Normalization & Protocol checks.  It also uses the Stream
33  * Interface Module which is also Snort-centric.  Mainly, just a wrapper
34  * to FTP/Telnet functionality, but a key part to starting the basic flow.
35  *
36  * The main bulk of this file is taken up with user configuration and
37  * parsing.  The reason this is so large is because FTPTelnet takes
38  * very detailed configuration parameters for each specified FTP client,
39  * to provide detailed control over an internal network and robust control
40  * of the external network.
41  *
42  * The main functions of note are:
43  *   - FTPTelnetSnortConf()    the configuration portion
44  *   - SnortFTPTelnet()        the actual normalization & inspection
45  *   - LogEvents()             where we log the FTPTelnet events
46  *
47  * NOTES:
48  * - 16.09.04:  Initial Development.  SAS
49  *
50  */
51 
52 #define _GNU_SOURCE
53 
54 #ifdef HAVE_CONFIG_H
55 #include "config.h"
56 #endif
57 
58 #ifdef HAVE_STRINGS_H
59 #include <strings.h>
60 #endif
61 
62 #include <stdlib.h>
63 #include <stdio.h>
64 #include <string.h>
65 #include <sys/types.h>
66 #include <errno.h>
67 #include "sf_ip.h"
68 
69 #ifndef WIN32
70 #include <sys/socket.h>
71 #include <netinet/in.h>
72 #include <arpa/inet.h>
73 #include <ctype.h>
74 #endif
75 
76 #define BUF_SIZE 1024
77 
78 #include "sf_types.h"
79 #include "snort_debug.h"
80 #include "ftpp_return_codes.h"
81 #include "ftpp_ui_config.h"
82 #include "ftpp_ui_client_lookup.h"
83 #include "ftpp_ui_server_lookup.h"
84 #include "ftp_cmd_lookup.h"
85 #include "ftp_bounce_lookup.h"
86 #include "ftpp_si.h"
87 #include "ftpp_eo_log.h"
88 #include "pp_telnet.h"
89 #include "pp_ftp.h"
90 #include "snort_ftptelnet.h"
91 #include "sfPolicy.h"
92 #include "sfPolicyUserData.h"
93 #include "stream_api.h"
94 #include "profiler.h"
95 #include "sf_snort_plugin_api.h"
96 #include "Unified2_common.h"
97 #include "ssl_include.h"
98 #include <daq_common.h>
99 #include "memory_stats.h"
100 
101 #ifdef DUMP_BUFFER
102 #include "ftptelnet_buffer_dump.h"
103 #endif
104 
105 #ifdef PERF_PROFILING
106 extern PreprocStats ftpPerfStats;
107 extern PreprocStats telnetPerfStats;
108 PreprocStats ftppDetectPerfStats;
109 int ftppDetectCalled = 0;
110 #endif
111 
112 #ifdef TARGET_BASED
113 unsigned s_ftpdata_eof_cb_id = 0;
114 unsigned s_ftpdata_flush_cb_id = 0;
115 #endif
116 
117 extern tSfPolicyUserContextId ftp_telnet_config;
118 
119 /*
120  * GLOBAL subkeyword values
121  */
122 #define ENCRYPTED_TRAFFIC "encrypted_traffic"
123 #define CHECK_ENCRYPTED   "check_encrypted"
124 #define INSPECT_TYPE      "inspection_type"
125 #define INSPECT_TYPE_STATELESS "stateless"
126 #define INSPECT_TYPE_STATEFUL  "stateful"
127 /*
128  * Protocol subkeywords.
129  */
130 #define PORTS             "ports"
131 
132 /*
133  * Telnet subkeywords.
134  */
135 #define AYT_THRESHOLD     "ayt_attack_thresh"
136 #define NORMALIZE         "normalize"
137 #define DETECT_ANOMALIES  "detect_anomalies"
138 
139 /*
140  * FTP SERVER subkeywords.
141  */
142 #define FTP_CMDS          "ftp_cmds"
143 #define PRINT_CMDS        "print_cmds"
144 #define MAX_PARAM_LEN     "def_max_param_len"
145 #define ALT_PARAM_LEN     "alt_max_param_len"
146 #define CMD_VALIDITY      "cmd_validity"
147 #define STRING_FORMAT     "chk_str_fmt"
148 #define TELNET_CMDS       "telnet_cmds"
149 #define IGNORE_TELNET_CMDS "ignore_telnet_erase_cmds"
150 #define DATA_CHAN_CMD     "data_chan_cmds"
151 #define DATA_XFER_CMD     "data_xfer_cmds"
152 #define DATA_REST_CMD     "data_rest_cmds"
153 #define FILE_PUT_CMD      "file_put_cmds"
154 #define FILE_GET_CMD      "file_get_cmds"
155 #define DATA_CHAN         "data_chan"
156 #define LOGIN_CMD         "login_cmds"
157 #define ENCR_CMD          "encr_cmds"
158 #define DIR_CMD           "dir_cmds"
159 #define IGNORE_DATA_CHAN  "ignore_data_chan"
160 
161 /*
162  * FTP CLIENT subkeywords
163  */
164 #define BOUNCE            "bounce"
165 #define ALLOW_BOUNCE      "bounce_to"
166 #define MAX_RESP_LEN      "max_resp_len"
167 
168 /*
169  * Data type keywords
170  */
171 #define START_CMD_FORMAT    "<"
172 #define END_CMD_FORMAT      ">"
173 #define F_INT               "int"
174 #define F_NUMBER            "number"
175 #define F_CHAR              "char"
176 #define F_DATE              "date"
177 #define F_LITERAL           "'"
178 #define F_STRING            "string"
179 #define F_STRING_FMT        "formated_string"
180 #define F_HOST_PORT         "host_port"
181 #define F_LONG_HOST_PORT    "long_host_port"
182 #define F_EXTD_HOST_PORT    "extd_host_port"
183 
184 /*
185  * Optional parameter delimiters
186  */
187 #define START_OPT_FMT       "["
188 #define END_OPT_FMT         "]"
189 #define START_CHOICE_FMT    "{"
190 #define END_CHOICE_FMT      "}"
191 #define OR_FMT              "|"
192 
193 
194 /*
195  * The cmd_validity keyword can be used with the format keyword to
196  * restrict data types.  The interpretation is specific to the data
197  * type.  'format' is only supported with date & char data types.
198  *
199  * A few examples:
200  *
201  * 1. Will perform validity checking of an FTP Mode command to
202  * check for one of the characters A, S, B, or C.
203  *
204  * cmd_validity MODE char ASBC
205  *
206  *
207  * 2. Will perform validity checking of an FTP MDTM command to
208  * check for an optional date argument following the format
209  * specified.  The date would uses the YYYYMMDDHHmmss+TZ format.
210  *
211  * cmd_validity MDTM [ date nnnnnnnnnnnnnn[.n[n[n]]] ] string
212  *
213  *
214  * 3. Will perform validity checking of an FTP ALLO command to
215  * check for an integer, then optionally, the letter R and another
216  * integer.
217  *
218  * cmd_validity ALLO int [ char R int ]
219  */
220 
221 /*
222  * The def_max_param_len & alt_max_param_len keywords can be used to
223  * restrict parameter length for one or more commands.  The space
224  * separated list of commands is enclosed in {}s.
225  *
226  * A few examples:
227  *
228  * 1. Restricts all command parameters to 100 characters
229  *
230  * def_max_param_len 100
231  *
232  * 2. Overrides CWD pathname to 256 characters
233  *
234  * alt_max_param_len 256 { CWD }
235  *
236  * 3. Overrides PWD & SYST to no parameters
237  *
238  * alt_max_param_len 0 { PWD SYST }
239  *
240  */
241 
242 /*
243  * Alert subkeywords
244  */
245 #define BOOL_YES     "yes"
246 #define BOOL_NO      "no"
247 
248 /*
249  **  IP Address list delimiters
250  */
251 #define START_IPADDR_LIST "{"
252 #define END_IPADDR_LIST   "}"
253 
254 /*
255  * Port list delimiters
256  */
257 #define START_PORT_LIST "{"
258 #define END_PORT_LIST   "}"
259 
260 /*
261  * Keyword for the default client/server configuration
262  */
263 #define DEFAULT "default"
264 
265 /*
266  * The default FTP server configuration for FTP command validation.
267  * Most of this comes from RFC 959, with additional commands being
268  * drawn from other RFCs/Internet Drafts that are in use.
269  *
270  * Any of the below can be overridden in snort.conf.
271  *
272  * This is here to eliminate most of it from snort.conf to
273  * avoid an ugly configuration file.  The default_max_param_len
274  * is somewhat arbitrary, but is taken from the majority of
275  * the snort FTP rules that limit parameter size to 100
276  * characters, as of 18 Sep 2004.
277  *
278  * The data_chan_cmds, data_xfer_cmds are used to track open
279  * data channel connections.
280  *
281  * The login_cmds and dir_cmds are used to track state of username
282  * and current directory.
283  *
284  * The file_put_cmds and file_get_cmds are used to track file transfers
285  * over open data channel connections.
286  */
287 /* DEFAULT_FTP_CONF_* deliberately break the default conf into
288  * chunks with lengths < 509 to keep ISO C89 compilers happy
289  */
290 static const char* DEFAULT_FTP_CONF[] = {
291     "hardcoded_config "
292         "def_max_param_len 100 "
293 
294         "ftp_cmds { USER PASS ACCT CWD CDUP SMNT QUIT REIN TYPE STRU"
295         " MODE RETR STOR STOU APPE ALLO REST RNFR RNTO ABOR"
296         " DELE RMD MKD PWD LIST NLST SITE SYST STAT HELP NOOP } "
297         "ftp_cmds { AUTH ADAT PROT PBSZ CONF ENC } "
298         "ftp_cmds { PORT PASV LPRT LPSV EPRT EPSV } "
299         "ftp_cmds { FEAT OPTS } "
300         "ftp_cmds { MDTM REST SIZE MLST MLSD } "
301 
302         "alt_max_param_len 0 { CDUP QUIT REIN PASV STOU ABOR PWD SYST NOOP } ",
303 
304     "cmd_validity MODE < char SBC > "
305         "cmd_validity STRU < char FRPO [ string ] > "
306         "cmd_validity ALLO < int [ char R int ] > "
307         "cmd_validity TYPE < { char AE [ char NTC ] | char I | char L [ number ] } > "
308         "cmd_validity PORT < host_port > "
309         "cmd_validity LPRT < long_host_port > "
310         "cmd_validity EPRT < extd_host_port > "
311         "cmd_validity EPSV < [ { '1' | '2' | 'ALL' } ] > ",
312 
313     "data_chan_cmds { PORT PASV LPRT LPSV EPRT EPSV } "
314         "data_xfer_cmds { RETR STOR STOU APPE LIST NLST } "
315         "data_rest_cmds { REST } "
316         "file_put_cmds { STOR STOU } "
317         "file_get_cmds { RETR } "
318         "login_cmds { USER PASS } "
319         "dir_cmds { CWD 250 CDUP 250 PWD 257 } "
320         "encr_cmds { AUTH } "
321 };
322 
323 #define CONF_CHUNKS (sizeof(DEFAULT_FTP_CONF)/sizeof(DEFAULT_FTP_CONF[0]))
324 
325 static uint8_t ftp_paf_id = 0;
326 
DefaultConf(size_t * pn)327 static char* DefaultConf (size_t* pn) {
328     unsigned i;
329     size_t sz = 1, ns = 0;
330     char* str = NULL;
331 
332     for ( i = 0; i < CONF_CHUNKS; i++ )
333         sz += strlen(DEFAULT_FTP_CONF[i]);
334 
335     str = _dpd.snortAlloc(1, sz, PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
336 
337     if ( !str )
338         DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
339                 *(_dpd.config_file), *(_dpd.config_line));
340 
341     for ( i = 0; i < CONF_CHUNKS; i++ )
342         ns += snprintf(str+ns, sz-ns, "%s", DEFAULT_FTP_CONF[i]);
343 
344     *pn = sz;
345     return str;
346 }
347 
348 static int printedFTPHeader = 0;
349 static int _checkServerConfig(struct _SnortConfig *, void *pData);
350 
351 char *maxToken = NULL;
352 static tSfPolicyId ftp_current_policy = 0;
353 
354 static void _addPortsToStream(struct _SnortConfig *, char *, tSfPolicyId, int);
355 static int _addFtpServerConfPortsToStream(struct _SnortConfig *, void *);
356 
357 static void _FTPTelnetAddPortsOfInterest(struct _SnortConfig *, FTPTELNET_GLOBAL_CONF *, tSfPolicyId);
358 #ifdef TARGET_BASED
359 static void _FTPTelnetAddService(struct _SnortConfig *, int16_t, tSfPolicyId);
360 #endif
361 void FTP_Set_flow_id( void *app_data, uint32_t fid );
362 void FTPData_Set_flow_id( void *app_data, uint32_t fid );
363 
mystrtok(char * s,const char * delim)364 char* mystrtok (char* s, const char* delim)
365 {
366     static char* last = NULL;
367     if ( s || last )
368         last = strtok(s, delim);
369     return last;
370 }
371 
NextToken(char * delimiters)372 char *NextToken(char *delimiters)
373 {
374     char *retTok = mystrtok(NULL, delimiters);
375     if (retTok > maxToken)
376         return NULL;
377 
378     return retTok;
379 }
380 
381 /*
382  * Function: ProcessConfOpt(FTPTELNET_CONF_OPT *ConfOpt,
383  *                          char *Option,
384  *                          char *ErrorString, int ErrStrLen)
385  *
386  * Purpose: Set the CONF_OPT on and alert fields.
387  *
388  *          We check to make sure of valid parameters and then set
389  *          the appropriate fields.
390  *
391  * Arguments: ConfOpt       => pointer to the configuration option
392  *            Option        => character pointer to the option being configured
393  *            ErrorString   => error string buffer
394  *            ErrStrLen     => the length of the error string buffer
395  *
396  * Returns: int     => an error code integer (0 = success,
397  *                     >0 = non-fatal error, <0 = fatal error)
398  *
399  */
ProcessConfOpt(FTPTELNET_CONF_OPT * ConfOpt,char * Option,char * ErrorString,int ErrStrLen)400 static int ProcessConfOpt(FTPTELNET_CONF_OPT *ConfOpt, char *Option,
401         char *ErrorString, int ErrStrLen)
402 {
403     char *pcToken;
404 
405     pcToken = NextToken(CONF_SEPARATORS);
406     if(pcToken == NULL)
407     {
408         snprintf(ErrorString, ErrStrLen,
409                 "No argument to token '%s'.", Option);
410 
411         return FTPP_FATAL_ERR;
412     }
413 
414     /*
415      * Check for the alert value
416      */
417     if(!strcmp(BOOL_YES, pcToken))
418     {
419         ConfOpt->alert = 1;
420     }
421     else if(!strcmp(BOOL_NO, pcToken))
422     {
423         ConfOpt->alert = 0;
424     }
425     else
426     {
427         snprintf(ErrorString, ErrStrLen,
428                 "Invalid argument to token '%s'.", Option);
429 
430         return FTPP_FATAL_ERR;
431     }
432 
433     ConfOpt->on = 1;
434 
435     return FTPP_SUCCESS;
436 }
437 
438 /*
439  * Function: PrintConfOpt(FTPTELNET_CONF_OPT *ConfOpt,
440  *                          char *Option)
441  *
442  * Purpose: Prints the CONF_OPT and alert fields.
443  *
444  * Arguments: ConfOpt       => pointer to the configuration option
445  *            Option        => character pointer to the option being configured
446  *
447  * Returns: int     => an error code integer (0 = success,
448  *                     >0 = non-fatal error, <0 = fatal error)
449  *
450  */
PrintConfOpt(FTPTELNET_CONF_OPT * ConfOpt,char * Option)451 static int PrintConfOpt(FTPTELNET_CONF_OPT *ConfOpt, char *Option)
452 {
453     if(!ConfOpt || !Option)
454     {
455         return FTPP_INVALID_ARG;
456     }
457 
458     if(ConfOpt->on)
459     {
460         _dpd.logMsg("      %s: YES alert: %s\n", Option,
461                 ConfOpt->alert ? "YES" : "NO");
462     }
463     else
464     {
465         _dpd.logMsg("      %s: OFF\n", Option);
466     }
467 
468     return FTPP_SUCCESS;
469 }
470 
471 /*
472  * Function: ProcessInspectType(FTPTELNET_CONF_OPT *ConfOpt,
473  *                          char *ErrorString, int ErrStrLen)
474  *
475  * Purpose: Process the type of inspection.
476  *          This sets the type of inspection for FTPTelnet to do.
477  *
478  * Arguments: GlobalConf    => pointer to the global configuration
479  *            ErrorString   => error string buffer
480  *            ErrStrLen     => the length of the error string buffer
481  *
482  * Returns: int     => an error code integer (0 = success,
483  *                     >0 = non-fatal error, <0 = fatal error)
484  *
485  */
ProcessInspectType(FTPTELNET_GLOBAL_CONF * GlobalConf,char * ErrorString,int ErrStrLen)486 static int ProcessInspectType(FTPTELNET_GLOBAL_CONF *GlobalConf,
487         char *ErrorString, int ErrStrLen)
488 {
489     char *pcToken;
490 
491     pcToken = NextToken(CONF_SEPARATORS);
492     if(pcToken == NULL)
493     {
494         snprintf(ErrorString, ErrStrLen,
495                 "No argument to token '%s'.", INSPECT_TYPE);
496 
497         return FTPP_FATAL_ERR;
498     }
499 
500     if(!strcmp(INSPECT_TYPE_STATEFUL, pcToken))
501     {
502         GlobalConf->inspection_type = FTPP_UI_CONFIG_STATEFUL;
503     }
504     else if(!strcmp(INSPECT_TYPE_STATELESS, pcToken))
505     {
506         GlobalConf->inspection_type = FTPP_UI_CONFIG_STATELESS;
507     }
508     else
509     {
510         snprintf(ErrorString, ErrStrLen,
511                 "Invalid argument to token '%s'.  Must be either "
512                 "'%s' or '%s'.", INSPECT_TYPE, INSPECT_TYPE_STATEFUL,
513                 INSPECT_TYPE_STATELESS);
514 
515         return FTPP_FATAL_ERR;
516     }
517 
518     return FTPP_SUCCESS;
519 }
520 
521 /*
522  * Function: ProcessFTPGlobalConf(FTPTELNET_GLOBAL_CONF *GlobalConf,
523  *                          char *ErrorString, int ErrStrLen)
524  *
525  * Purpose: This is where we process the global configuration for FTPTelnet.
526  *
527  *          We set the values of the global configuraiton here.  Any errors
528  *          that are encountered are specified in the error string and the
529  *          type of error is returned through the return code, i.e. fatal,
530  *          non-fatal.
531  *
532  *          The configuration options that are dealt with here are:
533  *          - inspection_type
534  *              Indicate whether to operate in stateful stateless mode
535  *          - encrypted_traffic
536  *              Detect and alert on encrypted sessions
537  *          - check_after_encrypted
538  *              Instructs the preprocessor to continue checking a data stream
539  *              after it is encrypted, looking for an eventual
540  *              non-ecrypted data.
541  *
542  * Arguments: GlobalConf    => pointer to the global configuration
543  *            ErrorString   => error string buffer
544  *            ErrStrLen     => the length of the error string buffer
545  *
546  * Returns: int     => an error code integer (0 = success,
547  *                     >0 = non-fatal error, <0 = fatal error)
548  *
549  */
ProcessFTPGlobalConf(FTPTELNET_GLOBAL_CONF * GlobalConf,char * ErrorString,int ErrStrLen)550 int ProcessFTPGlobalConf(FTPTELNET_GLOBAL_CONF *GlobalConf,
551         char *ErrorString, int ErrStrLen)
552 {
553     FTPTELNET_CONF_OPT *ConfOpt;
554     int  iRet = 0;
555     char *pcToken;
556     int  iTokens = 0;
557 
558     while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL)
559     {
560         /*
561          * Show that we at least got one token
562          */
563         iTokens = 1;
564 
565         /*
566          * Search for configuration keywords
567          */
568         if (!strcmp(pcToken, CHECK_ENCRYPTED))
569         {
570             GlobalConf->check_encrypted_data = 1;
571         }
572         else if (!strcmp(pcToken, ENCRYPTED_TRAFFIC))
573         {
574             ConfOpt = &GlobalConf->encrypted;
575             iRet = ProcessConfOpt(ConfOpt, ENCRYPTED_TRAFFIC, ErrorString, ErrStrLen);
576             if (iRet)
577             {
578                 return iRet;
579             }
580         }
581         else if(!strcmp(INSPECT_TYPE, pcToken))
582         {
583             iRet = ProcessInspectType(GlobalConf, ErrorString, ErrStrLen);
584             if (iRet)
585             {
586                 return iRet;
587             }
588         }
589         else
590         {
591             snprintf(ErrorString, ErrStrLen,
592                     "Invalid keyword '%s' for '%s' configuration.",
593                     pcToken, GLOBAL);
594 
595             return FTPP_FATAL_ERR;
596         }
597     }
598 
599     /*
600      * If there are not any tokens to the configuration, then
601      * we let the user know and log the error.  return non-fatal
602      * error.
603      */
604     if(!iTokens)
605     {
606         snprintf(ErrorString, ErrStrLen,
607                 "No tokens to '%s' configuration.", GLOBAL);
608 
609         return FTPP_NONFATAL_ERR;
610     }
611 
612     return FTPP_SUCCESS;
613 }
614 
615 /*
616  * Function: ProcessPorts(PROTO_CONF *protocol,
617  *                        char *ErrorString, int ErrStrLen)
618  *
619  * Purpose: Process the port list for the server configuration.
620  *          This configuration is a list of valid ports and is ended
621  *          by a delimiter.
622  *
623  * Arguments: protocol      => pointer to the ports configuration
624  *            ErrorString   => error string buffer
625  *            ErrStrLen     => the length of the error string buffer
626  *
627  * Returns: int     => an error code integer (0 = success,
628  *                     >0 = non-fatal error, <0 = fatal error)
629  *
630  */
ProcessPorts(PROTO_CONF * protocol,char * ErrorString,int ErrStrLen)631 static int ProcessPorts(PROTO_CONF *protocol,
632         char *ErrorString, int ErrStrLen)
633 {
634     char *pcToken;
635     char *pcEnd;
636     int  iPort;
637     int  iEndPorts = 0;
638 
639     pcToken = NextToken(CONF_SEPARATORS);
640     if(!pcToken)
641     {
642         snprintf(ErrorString, ErrStrLen,
643                 "Invalid port list format.");
644 
645         return FTPP_FATAL_ERR;
646     }
647 
648     if(strcmp(START_PORT_LIST, pcToken))
649     {
650         snprintf(ErrorString, ErrStrLen,
651                 "Must start a port list with the '%s' token.",
652                 START_PORT_LIST);
653 
654         return FTPP_FATAL_ERR;
655     }
656 
657     /* Unset the defaults */
658     for (iPort = 0;iPort<MAXPORTS;iPort++)
659         protocol->ports[iPort] = 0;
660 
661     while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL)
662     {
663         if(!strcmp(END_PORT_LIST, pcToken))
664         {
665             iEndPorts = 1;
666             break;
667         }
668 
669         iPort = strtol(pcToken, &pcEnd, 10);
670 
671         /*
672          * Validity check for port
673          */
674         if(*pcEnd)
675         {
676             snprintf(ErrorString, ErrStrLen,
677                     "Invalid port number.");
678 
679             return FTPP_FATAL_ERR;
680         }
681 
682         if(iPort < 0 || iPort > MAXPORTS-1)
683         {
684             snprintf(ErrorString, ErrStrLen,
685                     "Invalid port number.  Must be between 0 and "
686                     "65535.");
687 
688             return FTPP_FATAL_ERR;
689         }
690 
691         protocol->ports[iPort] = 1;
692 
693         if(protocol->port_count < MAXPORTS)
694             protocol->port_count++;
695     }
696 
697     if(!iEndPorts)
698     {
699         snprintf(ErrorString, ErrStrLen,
700                 "Must end '%s' configuration with '%s'.",
701                 PORTS, END_PORT_LIST);
702 
703         return FTPP_FATAL_ERR;
704     }
705 
706     return FTPP_SUCCESS;
707 }
708 
709 /*
710  * Function: ProcessTelnetAYTThreshold(TELNET_PROTO_CONF *TelnetConf,
711  *                        char *ErrorString, int ErrStrLen)
712  *
713  * Purpose: Process the 'are you there' threshold configuration
714  *          This sets the maximum number of telnet ayt commands that
715  *          we will tolerate, before alerting.
716  *
717  * Arguments: TelnetConf    => pointer to the telnet configuration
718  *            ErrorString   => error string buffer
719  *            ErrStrLen     => the length of the error string buffer
720  *
721  * Returns: int     => an error code integer (0 = success,
722  *                     >0 = non-fatal error, <0 = fatal error)
723  *
724  */
ProcessTelnetAYTThreshold(TELNET_PROTO_CONF * TelnetConf,char * ErrorString,int ErrStrLen)725 static int ProcessTelnetAYTThreshold(TELNET_PROTO_CONF *TelnetConf,
726         char *ErrorString, int ErrStrLen)
727 {
728     char *pcToken;
729     char *pcEnd = NULL;
730 
731     pcToken = NextToken(CONF_SEPARATORS);
732     if(pcToken == NULL)
733     {
734         snprintf(ErrorString, ErrStrLen,
735                 "No argument to token '%s'.", AYT_THRESHOLD);
736 
737         return FTPP_FATAL_ERR;
738     }
739 
740     TelnetConf->ayt_threshold = strtol(pcToken, &pcEnd, 10);
741 
742     /*
743      * Let's check to see if the entire string was valid.
744      * If there is an address here, then there was an
745      * invalid character in the string.
746      */
747     if(*pcEnd)
748     {
749         snprintf(ErrorString, ErrStrLen,
750                 "Invalid argument to token '%s'.  Must be a positive "
751                 "number.", AYT_THRESHOLD);
752 
753         return FTPP_FATAL_ERR;
754     }
755 
756     return FTPP_SUCCESS;
757 }
758 
759 /*
760  * Function: PrintTelnetConf(TELNET_PROTO_CONF *TelnetConf,
761  *                          char *Option)
762  *
763  * Purpose: Prints the telnet configuration
764  *
765  * Arguments: TelnetConf    => pointer to the telnet configuration
766  *
767  * Returns: int     => an error code integer (0 = success,
768  *                     >0 = non-fatal error, <0 = fatal error)
769  *
770  */
PrintTelnetConf(TELNET_PROTO_CONF * TelnetConf)771 static int PrintTelnetConf(TELNET_PROTO_CONF *TelnetConf)
772 {
773     char buf[BUF_SIZE+1];
774     int iCtr;
775 
776     if(!TelnetConf)
777     {
778         return FTPP_INVALID_ARG;
779     }
780 
781     _dpd.logMsg("    TELNET CONFIG:\n");
782     memset(buf, 0, BUF_SIZE+1);
783     snprintf(buf, BUF_SIZE, "      Ports: ");
784 
785     /*
786      * Print out all the applicable ports.
787      */
788     for(iCtr = 0; iCtr < MAXPORTS; iCtr++)
789     {
790         if(TelnetConf->proto_ports.ports[iCtr])
791         {
792             _dpd.printfappend(buf, BUF_SIZE, "%d ", iCtr);
793         }
794     }
795 
796     _dpd.logMsg("%s\n", buf);
797 
798     _dpd.logMsg("      Are You There Threshold: %d\n",
799             TelnetConf->ayt_threshold);
800     _dpd.logMsg("      Normalize: %s\n", TelnetConf->normalize ? "YES" : "NO");
801     _dpd.logMsg("      Detect Anomalies: %s\n",
802             TelnetConf->detect_anomalies ? "YES" : "NO");
803 
804     return FTPP_SUCCESS;
805 }
806 
807 /*
808  * Function: ProcessTelnetConf(FTPTELNET_GLOBAL_CONF *GlobalConf,
809  *                          char *ErrorString, int ErrStrLen)
810  *
811  * Purpose: This is where we process the telnet configuration for FTPTelnet.
812  *
813  *          We set the values of the telnet configuraiton here.  Any errors
814  *          that are encountered are specified in the error string and the
815  *          type of error is returned through the return code, i.e. fatal,
816  *          non-fatal.
817  *
818  *          The configuration options that are dealt with here are:
819  *          - ports { x }           Ports on which to do telnet checks
820  *          - normalize             Turns on normalization
821  *          - ayt_attack_thresh x   Detect consecutive are you there commands
822  *
823  * Arguments: GlobalConf    => pointer to the global configuration
824  *            ErrorString   => error string buffer
825  *            ErrStrLen     => the length of the error string buffer
826  *
827  * Returns: int     => an error code integer (0 = success,
828  *                     >0 = non-fatal error, <0 = fatal error)
829  *
830  */
ProcessTelnetConf(FTPTELNET_GLOBAL_CONF * GlobalConf,char * ErrorString,int ErrStrLen)831 int ProcessTelnetConf(FTPTELNET_GLOBAL_CONF *GlobalConf,
832         char *ErrorString, int ErrStrLen)
833 {
834     int  iRet;
835     char *pcToken;
836     int  iTokens = 0;
837 
838     if (GlobalConf->telnet_config != NULL)
839     {
840         snprintf(ErrorString, ErrStrLen,
841                 "Telnet can only be configured once.\n");
842 
843         return FTPP_FATAL_ERR;
844     }
845 
846     GlobalConf->telnet_config =
847         (TELNET_PROTO_CONF *)_dpd.snortAlloc(1, sizeof(TELNET_PROTO_CONF),
848                                          PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
849     if (GlobalConf->telnet_config == NULL)
850     {
851         DynamicPreprocessorFatalMessage("Out of memory trying to create "
852                 "telnet configuration.\n");
853     }
854 
855     /*
856      * Reset the global telnet configuration
857      */
858     if(ftpp_ui_config_reset_telnet_proto(GlobalConf->telnet_config))
859     {
860         snprintf(ErrorString, ErrStrLen,
861                 "Cannot reset the FTPTelnet global telnet configuration.");
862 
863         return FTPP_FATAL_ERR;
864     }
865 
866     while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL)
867     {
868         /*
869          * Show that we at least got one token
870          */
871         iTokens = 1;
872 
873         /*
874          * Search for configuration keywords
875          */
876         if(!strcmp(PORTS, pcToken))
877         {
878             PROTO_CONF *ports = (PROTO_CONF*)GlobalConf->telnet_config;
879             iRet = ProcessPorts(ports, ErrorString, ErrStrLen);
880             if (iRet)
881             {
882                 return iRet;
883             }
884         }
885         else if(!strcmp(AYT_THRESHOLD, pcToken))
886         {
887             iRet = ProcessTelnetAYTThreshold(GlobalConf->telnet_config,
888                     ErrorString, ErrStrLen);
889             if (iRet)
890             {
891                 return iRet;
892             }
893         }
894         else if(!strcmp(NORMALIZE, pcToken))
895         {
896             GlobalConf->telnet_config->normalize = 1;
897         }
898         else if(!strcmp(DETECT_ANOMALIES, pcToken))
899         {
900             GlobalConf->telnet_config->detect_anomalies = 1;
901         }
902         /*
903          * Start the CONF_OPT configurations.
904          */
905         else
906         {
907             snprintf(ErrorString, ErrStrLen,
908                     "Invalid keyword '%s' for '%s' configuration.",
909                     pcToken, GLOBAL);
910 
911             return FTPP_FATAL_ERR;
912         }
913     }
914 
915     /*
916      * If there are not any tokens to the configuration, then
917      * we let the user know and log the error.  return non-fatal
918      * error.
919      */
920     if(!iTokens)
921     {
922         snprintf(ErrorString, ErrStrLen,
923                 "No tokens to '%s' configuration.", TELNET);
924         return FTPP_NONFATAL_ERR;
925     }
926 
927     /* Let's print out the telnet config */
928     PrintTelnetConf(GlobalConf->telnet_config);
929 
930     return FTPP_SUCCESS;
931 }
932 
933 #if 0
934 /**obsoleted during changes for bug_31418
935 */
936 /*
937  * Function: GetIPAddr(char *addrString, unsigned uint32_t *ipAddr,
938  *                     char *ErrorString, int ErrStrLen)
939  *
940  * Purpose: This is where we convert an IP address to a numeric
941  *
942  *          Any errors that are encountered are specified in the error
943  *          string and the type of error is returned through the return
944  *          code, i.e. fatal, non-fatal.
945  *
946  * Arguments: addrString    => pointer to the address string
947  *            ipAddr        => pointer to converted address
948  *            ErrorString   => error string buffer
949  *            ErrStrLen     => the length of the error string buffer
950  *
951  * Returns: int     => an error code integer (0 = success,
952  *                     >0 = non-fatal error, <0 = fatal error)
953  *
954  */
955 static int GetIPAddr(char *addrString, sfaddr_t *ipAddr,
956         char *ErrorString, int ErrStrLen)
957 {
958     if(sfip_pton(addrString, ipAddr) != SFIP_SUCCESS)
959     {
960         snprintf(ErrorString, ErrStrLen,
961                 "Invalid FTP client IP address '%s'.", addrString);
962 
963         return FTPP_FATAL_ERR;
964     }
965 
966     return FTPP_SUCCESS;
967 }
968 #endif
969 /*
970  * Function: ProcessFTPCmdList(FTP_SERVER_PROTO_CONF *ServerConf,
971  *                             char *confOption,
972  *                             char *ErrorString, int ErrStrLen,
973  *                             int require_cmds, int require_length)
974  *
975  * Purpose: Process the FTP cmd lists for the client configuration.
976  *          This configuration is a parameter length for the list of
977  *          FTP commands and is ended by a delimiter.
978  *
979  * Arguments: ServerConf    => pointer to the FTP server configuration
980  *            confOption    => pointer to the name of the option
981  *            ErrorString   => error string buffer
982  *            ErrStrLen     => the length of the error string buffer
983  *            require_cmds  => flag to require a command list
984  *            require_length => flag to require a length specifier
985  *
986  * Returns: int     => an error code integer (0 = success,
987  *                     >0 = non-fatal error, <0 = fatal error)
988  *
989  */
ProcessFTPCmdList(FTP_SERVER_PROTO_CONF * ServerConf,char * confOption,char * ErrorString,int ErrStrLen,int require_cmds,int require_length)990 static int ProcessFTPCmdList(FTP_SERVER_PROTO_CONF *ServerConf,
991         char *confOption,
992         char *ErrorString, int ErrStrLen,
993         int require_cmds, int require_length)
994 {
995     FTP_CMD_CONF *FTPCmd = NULL;
996     char *pcToken;
997     char *pcEnd = NULL;
998     char *cmd;
999     int  iLength = 0;
1000     int  iEndCmds = 0;
1001     int  iRet;
1002 
1003     if (require_length)
1004     {
1005         pcToken = NextToken(CONF_SEPARATORS);
1006         if(!pcToken)
1007         {
1008             snprintf(ErrorString, ErrStrLen,
1009                     "Invalid cmd list format.");
1010 
1011             return FTPP_FATAL_ERR;
1012         }
1013 
1014         iLength = strtol(pcToken, &pcEnd, 10);
1015 
1016         /*
1017          * Let's check to see if the entire string was valid.
1018          * If there is an address here, then there was an
1019          * invalid character in the string.
1020          */
1021         if((*pcEnd) || (iLength < 0))
1022         {
1023             snprintf(ErrorString, ErrStrLen,
1024                     "Invalid argument to token '%s'.  "
1025                     "Length must be a positive number",
1026                     confOption);
1027 
1028             return FTPP_FATAL_ERR;
1029         }
1030     }
1031 
1032     if (require_cmds)
1033     {
1034         pcToken = NextToken(CONF_SEPARATORS);
1035         if(!pcToken)
1036         {
1037             snprintf(ErrorString, ErrStrLen,
1038                     "Invalid cmd list format.");
1039 
1040             return FTPP_FATAL_ERR;
1041         }
1042 
1043         if(strcmp(START_PORT_LIST, pcToken))
1044         {
1045             snprintf(ErrorString, ErrStrLen,
1046                     "Must start a cmd list with the '%s' token.",
1047                     START_PORT_LIST);
1048 
1049             return FTPP_FATAL_ERR;
1050         }
1051 
1052         while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL)
1053         {
1054             if(!strcmp(END_PORT_LIST, pcToken))
1055             {
1056                 iEndCmds = 1;
1057                 break;
1058             }
1059 
1060             cmd = pcToken;
1061 
1062             FTPCmd = ftp_cmd_lookup_find(ServerConf->cmd_lookup, cmd,
1063                     strlen(cmd), &iRet);
1064 
1065             if (FTPCmd == NULL)
1066             {
1067                 /* Add it to the list */
1068                 // note that struct includes 1 byte for null, so just add len
1069                 FTPCmd = (FTP_CMD_CONF *)_dpd.snortAlloc(1,
1070                                           sizeof(FTP_CMD_CONF)+strlen(cmd),
1071                                           PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1072                 if (FTPCmd == NULL)
1073                 {
1074                     DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1075                             *(_dpd.config_file), *(_dpd.config_line));
1076                 }
1077 
1078                 strcpy(FTPCmd->cmd_name, cmd);
1079 
1080                 ftp_cmd_lookup_add(ServerConf->cmd_lookup, cmd,
1081                         strlen(cmd), FTPCmd);
1082                 FTPCmd->max_param_len = ServerConf->def_max_param_len;
1083             }
1084 
1085             if (require_length)
1086             {
1087                 FTPCmd->max_param_len = iLength;
1088                 FTPCmd->max_param_len_overridden = 1;
1089             }
1090         }
1091 
1092         if(!iEndCmds)
1093         {
1094             snprintf(ErrorString, ErrStrLen,
1095                     "Must end '%s' configuration with '%s'.",
1096                     FTP_CMDS, END_PORT_LIST);
1097 
1098             return FTPP_FATAL_ERR;
1099         }
1100     }
1101 
1102     if (!strcmp(confOption, MAX_PARAM_LEN))
1103     {
1104         ServerConf->def_max_param_len = iLength;
1105         /* Reset the max length to the default for all existing commands  */
1106         FTPCmd = ftp_cmd_lookup_first(ServerConf->cmd_lookup, &iRet);
1107         while (FTPCmd)
1108         {
1109             if (!FTPCmd->max_param_len_overridden)
1110             {
1111                 FTPCmd->max_param_len = ServerConf->def_max_param_len;
1112             }
1113             FTPCmd = ftp_cmd_lookup_next(ServerConf->cmd_lookup, &iRet);
1114         }
1115     }
1116 
1117     return FTPP_SUCCESS;
1118 }
1119 
1120 /*
1121  * Function: ResetStringFormat (FTP_PARAM_FMT *Fmt)
1122  *
1123  * Purpose: Recursively sets nodes that allow strings to nodes that check
1124  *          for a string format attack within the FTP parameter validation tree
1125  *
1126  * Arguments: Fmt       => pointer to the FTP Parameter configuration
1127  *
1128  * Returns: None
1129  *
1130  */
ResetStringFormat(FTP_PARAM_FMT * Fmt)1131 void ResetStringFormat (FTP_PARAM_FMT *Fmt)
1132 {
1133     int i;
1134     if (!Fmt)
1135         return;
1136 
1137     if (Fmt->type == e_unrestricted)
1138         Fmt->type = e_strformat;
1139 
1140     ResetStringFormat(Fmt->optional_fmt);
1141     for (i=0;i<Fmt->numChoices;i++)
1142     {
1143         ResetStringFormat(Fmt->choices[i]);
1144     }
1145     ResetStringFormat(Fmt->next_param_fmt);
1146 }
1147 
1148 /*
1149  * Function: ProcessFTPDataChanCmdsList(FTP_SERVER_PROTO_CONF *ServerConf,
1150  *                             char *confOption,
1151  *                             char *ErrorString, int ErrStrLen)
1152  *
1153  * Purpose: Process the FTP cmd lists for the client configuration.
1154  *          This configuration is an indicator of data channels, data transfer,
1155  *          string format, encryption, or login commands.
1156  *
1157  * Arguments: ServerConf    => pointer to the FTP server configuration
1158  *            confOption    => pointer to the name of the option
1159  *            ErrorString   => error string buffer
1160  *            ErrStrLen     => the length of the error string buffer
1161  *
1162  * Returns: int     => an error code integer (0 = success,
1163  *                     >0 = non-fatal error, <0 = fatal error)
1164  *
1165  */
ProcessFTPDataChanCmdsList(FTP_SERVER_PROTO_CONF * ServerConf,char * confOption,char * ErrorString,int ErrStrLen)1166 static int ProcessFTPDataChanCmdsList(FTP_SERVER_PROTO_CONF *ServerConf,
1167         char *confOption,
1168         char *ErrorString, int ErrStrLen)
1169 {
1170     FTP_CMD_CONF *FTPCmd = NULL;
1171     char *pcToken;
1172     char *cmd;
1173     int  iEndCmds = 0;
1174     int  iRet;
1175 
1176     pcToken = NextToken(CONF_SEPARATORS);
1177     if(!pcToken)
1178     {
1179         snprintf(ErrorString, ErrStrLen,
1180                 "Invalid %s list format.", confOption);
1181 
1182         return FTPP_FATAL_ERR;
1183     }
1184 
1185     if(strcmp(START_PORT_LIST, pcToken))
1186     {
1187         snprintf(ErrorString, ErrStrLen,
1188                 "Must start a %s list with the '%s' token.",
1189                 confOption, START_PORT_LIST);
1190 
1191         return FTPP_FATAL_ERR;
1192     }
1193 
1194     while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL)
1195     {
1196         if(!strcmp(END_PORT_LIST, pcToken))
1197         {
1198             iEndCmds = 1;
1199             break;
1200         }
1201 
1202         cmd = pcToken;
1203 
1204         FTPCmd = ftp_cmd_lookup_find(ServerConf->cmd_lookup, cmd,
1205                 strlen(cmd), &iRet);
1206 
1207         if (FTPCmd == NULL)
1208         {
1209             /* Add it to the list */
1210             // note that struct includes 1 byte for null, so just add len
1211             FTPCmd = (FTP_CMD_CONF *)_dpd.snortAlloc(1,
1212                                          sizeof(FTP_CMD_CONF) + strlen(cmd),
1213                                          PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1214             if (FTPCmd == NULL)
1215             {
1216                 DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1217                         *(_dpd.config_file), *(_dpd.config_line));
1218             }
1219 
1220             strcpy(FTPCmd->cmd_name, cmd);
1221 
1222             FTPCmd->max_param_len = ServerConf->def_max_param_len;
1223 
1224             ftp_cmd_lookup_add(ServerConf->cmd_lookup, cmd,
1225                     strlen(cmd), FTPCmd);
1226         }
1227 
1228         if (!strcmp(confOption, DATA_CHAN_CMD))
1229             FTPCmd->data_chan_cmd = 1;
1230         else if (!strcmp(confOption, DATA_XFER_CMD))
1231             FTPCmd->data_xfer_cmd = 1;
1232         else if (!strcmp(confOption, DATA_REST_CMD))
1233             FTPCmd->data_rest_cmd = 1;
1234         else if (!strcmp(confOption, FILE_PUT_CMD))
1235         {
1236             FTPCmd->data_xfer_cmd = 1;
1237             FTPCmd->file_put_cmd = 1;
1238         }
1239         else if (!strcmp(confOption, FILE_GET_CMD))
1240         {
1241             FTPCmd->data_xfer_cmd = 1;
1242             FTPCmd->file_get_cmd = 1;
1243         }
1244         else if (!strcmp(confOption, STRING_FORMAT))
1245         {
1246             FTP_PARAM_FMT *Fmt = FTPCmd->param_format;
1247             if (Fmt)
1248             {
1249                 ResetStringFormat(Fmt);
1250             }
1251             else
1252             {
1253                 Fmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT),
1254                                           PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1255                 if (Fmt == NULL)
1256                 {
1257                     DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1258                             *(_dpd.config_file), *(_dpd.config_line));
1259                 }
1260 
1261                 Fmt->type = e_head;
1262                 FTPCmd->param_format = Fmt;
1263 
1264                 Fmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT),
1265                                           PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1266                 if (Fmt == NULL)
1267                 {
1268                     DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1269                             *(_dpd.config_file), *(_dpd.config_line));
1270                 }
1271 
1272                 Fmt->type = e_strformat;
1273                 FTPCmd->param_format->next_param_fmt = Fmt;
1274                 Fmt->prev_param_fmt = FTPCmd->param_format;
1275             }
1276             FTPCmd->check_validity = 1;
1277         }
1278         else if (!strcmp(confOption, ENCR_CMD))
1279             FTPCmd->encr_cmd = 1;
1280         else if (!strcmp(confOption, LOGIN_CMD))
1281             FTPCmd->login_cmd = 1;
1282     }
1283 
1284     if(!iEndCmds)
1285     {
1286         snprintf(ErrorString, ErrStrLen,
1287                 "Must end '%s' configuration with '%s'.",
1288                 confOption, END_PORT_LIST);
1289 
1290         return FTPP_FATAL_ERR;
1291     }
1292 
1293     return FTPP_SUCCESS;
1294 }
1295 
1296 /*
1297  * Function: ProcessFTPDirCmdsList(FTP_SERVER_PROTO_CONF *ServerConf,
1298  *                             char *confOption,
1299  *                             char *ErrorString, int ErrStrLen)
1300  *
1301  * Purpose: Process the FTP cmd lists for the client configuration.
1302  *          This configuration is an indicator of commands used to
1303  *          retrieve or update the current directory.
1304  *
1305  * Arguments: ServerConf    => pointer to the FTP server configuration
1306  *            confOption    => pointer to the name of the option
1307  *            ErrorString   => error string buffer
1308  *            ErrStrLen     => the length of the error string buffer
1309  *
1310  * Returns: int     => an error code integer (0 = success,
1311  *                     >0 = non-fatal error, <0 = fatal error)
1312  *
1313  */
ProcessFTPDirCmdsList(FTP_SERVER_PROTO_CONF * ServerConf,char * confOption,char * ErrorString,int ErrStrLen)1314 static int ProcessFTPDirCmdsList(FTP_SERVER_PROTO_CONF *ServerConf,
1315         char *confOption,
1316         char *ErrorString, int ErrStrLen)
1317 {
1318     FTP_CMD_CONF *FTPCmd = NULL;
1319     char *pcToken;
1320     char *pcEnd = NULL;
1321     char *cmd;
1322     int  iCode;
1323     int  iEndCmds = 0;
1324     int  iRet;
1325 
1326     pcToken = NextToken(CONF_SEPARATORS);
1327     if(!pcToken)
1328     {
1329         snprintf(ErrorString, ErrStrLen,
1330                 "Invalid %s list format.", confOption);
1331 
1332         return FTPP_FATAL_ERR;
1333     }
1334 
1335     if(strcmp(START_PORT_LIST, pcToken))
1336     {
1337         snprintf(ErrorString, ErrStrLen,
1338                 "Must start a %s list with the '%s' token.",
1339                 confOption, START_PORT_LIST);
1340 
1341         return FTPP_FATAL_ERR;
1342     }
1343 
1344     while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL)
1345     {
1346         if(!strcmp(END_PORT_LIST, pcToken))
1347         {
1348             iEndCmds = 1;
1349             break;
1350         }
1351 
1352         cmd = pcToken;
1353 
1354         FTPCmd = ftp_cmd_lookup_find(ServerConf->cmd_lookup, cmd,
1355                 strlen(cmd), &iRet);
1356 
1357         if (FTPCmd == NULL)
1358         {
1359             /* Add it to the list  */
1360             // note that struct includes 1 byte for null, so just add len
1361             FTPCmd = (FTP_CMD_CONF *)_dpd.snortAlloc(1,
1362                                          sizeof(FTP_CMD_CONF) + strlen(cmd),
1363                                          PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1364             if (FTPCmd == NULL)
1365             {
1366                 DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1367                         *(_dpd.config_file), *(_dpd.config_line));
1368             }
1369 
1370             strcpy(FTPCmd->cmd_name, cmd);
1371 
1372             FTPCmd->max_param_len = ServerConf->def_max_param_len;
1373 
1374             ftp_cmd_lookup_add(ServerConf->cmd_lookup, cmd,
1375                     strlen(cmd), FTPCmd);
1376         }
1377 
1378         pcToken = NextToken(CONF_SEPARATORS);
1379 
1380         if (!pcToken)
1381         {
1382             snprintf(ErrorString, ErrStrLen,
1383                     "FTP Dir Cmds must have associated response code: '%s'.",
1384                     cmd);
1385 
1386             return FTPP_FATAL_ERR;
1387         }
1388 
1389         iCode = strtol(pcToken, &pcEnd, 10);
1390 
1391         /*
1392          * Let's check to see if the entire string was valid.
1393          * If there is an address here, then there was an
1394          * invalid character in the string.
1395          */
1396         if((*pcEnd) || (iCode < 0))
1397         {
1398             snprintf(ErrorString, ErrStrLen,
1399                     "Invalid argument to token '%s'.  "
1400                     "Code must be a positive number",
1401                     confOption);
1402 
1403             return FTPP_FATAL_ERR;
1404         }
1405 
1406         FTPCmd->dir_response = iCode;
1407     }
1408 
1409     if(!iEndCmds)
1410     {
1411         snprintf(ErrorString, ErrStrLen,
1412                 "Must end '%s' configuration with '%s'.",
1413                 confOption, END_PORT_LIST);
1414 
1415         return FTPP_FATAL_ERR;
1416     }
1417 
1418     return FTPP_SUCCESS;
1419 }
1420 
ProcessFTPIgnoreDataChan(FTP_SERVER_PROTO_CONF * ServerConf,char * confOption,char * ErrorString,int ErrStrLen)1421 static int ProcessFTPIgnoreDataChan(FTP_SERVER_PROTO_CONF *ServerConf,
1422         char *confOption,
1423         char *ErrorString, int ErrStrLen)
1424 {
1425     char *pcToken;
1426 
1427     pcToken = NextToken(CONF_SEPARATORS);
1428     if (pcToken == NULL)
1429     {
1430         snprintf(ErrorString, ErrStrLen, "No argument provided to option '%s'. "
1431                 "Argument must be 'yes' or 'no'.",
1432                 confOption);
1433         return FTPP_FATAL_ERR;
1434     }
1435     if (!strcasecmp("yes", pcToken))
1436     {
1437         ServerConf->data_chan = 1;
1438     }
1439     else if (!strcasecmp("no", pcToken))
1440     {
1441         if (ServerConf->data_chan == 1)
1442         {
1443             snprintf(ErrorString, ErrStrLen, "Both 'data_chan' and "
1444                     "'ignore_data_chan' configured with conflicting options.");
1445             return FTPP_FATAL_ERR;
1446         }
1447         ServerConf->data_chan = 0;
1448     }
1449     else
1450     {
1451         snprintf(ErrorString, ErrStrLen, "Invalid argument to token '%s'. "
1452                 "Argument must be 'yes' or 'no'.", confOption);
1453         return FTPP_FATAL_ERR;
1454     }
1455 
1456     return FTPP_SUCCESS;
1457 }
1458 
1459 /*
1460  * Function: SetOptionalsNext(FTP_PARAM_FMT *ThisFmt,
1461  *                            FTP_PARAM_FMT *NextFmt,
1462  *                            FTP_PARAM_FMT **choices,
1463  *                            int numChoices)
1464  *
1465  * Purpose: Recursively updates the next value for nodes in the FTP
1466  *          Parameter validation tree.
1467  *
1468  * Arguments: ThisFmt       => pointer to an FTP parameter validation node
1469  *            NextFmt       => pointer to an FTP parameter validation node
1470  *            choices       => pointer to a list of FTP parameter
1471  *                             validation nodes
1472  *            numChoices    => the number of nodes in the list
1473  *
1474  * Returns: int     => an error code integer (0 = success,
1475  *                     >0 = non-fatal error, <0 = fatal error)
1476  *
1477  */
SetOptionalsNext(FTP_PARAM_FMT * ThisFmt,FTP_PARAM_FMT * NextFmt,FTP_PARAM_FMT ** choices,int numChoices)1478 static void SetOptionalsNext(FTP_PARAM_FMT *ThisFmt, FTP_PARAM_FMT *NextFmt,
1479         FTP_PARAM_FMT **choices, int numChoices)
1480 {
1481     if (!ThisFmt)
1482         return;
1483 
1484     if (ThisFmt->optional)
1485     {
1486         if (ThisFmt->next_param_fmt == NULL)
1487         {
1488             ThisFmt->next_param_fmt = NextFmt;
1489             if (numChoices)
1490             {
1491                 ThisFmt->numChoices = numChoices;
1492                 ThisFmt->choices = (FTP_PARAM_FMT **)_dpd.snortAlloc(numChoices,
1493                                                         sizeof(FTP_PARAM_FMT *),
1494                                                         PP_FTPTELNET,
1495                                                         PP_MEM_CATEGORY_CONFIG);
1496                 if (ThisFmt->choices == NULL)
1497                 {
1498                     DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1499                             *(_dpd.config_file), *(_dpd.config_line));
1500                 }
1501 
1502                 memcpy(ThisFmt->choices, choices, sizeof(FTP_PARAM_FMT *) * numChoices);
1503             }
1504         }
1505         else
1506         {
1507             SetOptionalsNext(ThisFmt->next_param_fmt, NextFmt,
1508                     choices, numChoices);
1509         }
1510     }
1511     else
1512     {
1513         int i;
1514         SetOptionalsNext(ThisFmt->optional_fmt, ThisFmt->next_param_fmt,
1515                 ThisFmt->choices, ThisFmt->numChoices);
1516         for (i=0;i<ThisFmt->numChoices;i++)
1517         {
1518             SetOptionalsNext(ThisFmt->choices[i], ThisFmt,
1519                     choices, numChoices);
1520         }
1521         SetOptionalsNext(ThisFmt->next_param_fmt, ThisFmt,
1522                 choices, numChoices);
1523     }
1524 }
1525 
1526 /*
1527  * Function: ProcessDateFormat(FTP_DATE_FMT *dateFmt,
1528  *                             FTP_DATE_FMT *LastNonOptFmt,
1529  *                             char **format)
1530  *
1531  * Purpose: Sets the value for nodes in the FTP Date validation tree.
1532  *
1533  * Arguments: dateFmt       => pointer to an FTP date validation node
1534  *            LastNonOptFmt => pointer to previous FTP date validation node
1535  *            format        => pointer to next part of date validation string
1536  *                             Updated on function exit.
1537  *
1538  * Returns: int     => an error code integer (0 = success,
1539  *                     >0 = non-fatal error, <0 = fatal error)
1540  *
1541  */
ProcessDateFormat(FTP_DATE_FMT * dateFmt,FTP_DATE_FMT * LastNonOptFmt,char ** format)1542 static int ProcessDateFormat(FTP_DATE_FMT *dateFmt,
1543         FTP_DATE_FMT *LastNonOptFmt,
1544         char **format)
1545 {
1546     char *curr_format;
1547     int iRet = FTPP_SUCCESS;
1548     int curr_len = 0;
1549     char *curr_ch;
1550     char *start_ch;
1551     FTP_DATE_FMT *CurrFmt = dateFmt;
1552 
1553     if (!dateFmt)
1554         return FTPP_INVALID_ARG;
1555 
1556     if (!format || !*format)
1557         return FTPP_INVALID_ARG;
1558 
1559     start_ch = curr_ch = *format;
1560 
1561     while (*curr_ch != '\0')
1562     {
1563         switch (*curr_ch)
1564         {
1565             case 'n':
1566             case 'C':
1567             case '+':
1568             case '-':
1569             case '.':
1570                 curr_len++;
1571                 curr_ch++;
1572                 break;
1573             case '[':
1574                 curr_ch++;
1575                 if (curr_len > 0)
1576                 {
1577                     FTP_DATE_FMT *OptFmt;
1578                     OptFmt = (FTP_DATE_FMT *)_dpd.snortAlloc(1,
1579                                                    sizeof(FTP_DATE_FMT),
1580                                                    PP_FTPTELNET,
1581                                                    PP_MEM_CATEGORY_CONFIG);
1582                     if (OptFmt == NULL)
1583                     {
1584                         DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1585                                 *(_dpd.config_file), *(_dpd.config_line));
1586                     }
1587 
1588                     curr_format = (char *)_dpd.snortAlloc(curr_len + 1,
1589                                                         sizeof(char),
1590                                                         PP_FTPTELNET,
1591                                                         PP_MEM_CATEGORY_CONFIG);
1592                     if (curr_format == NULL)
1593                     {
1594                         DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1595                                 *(_dpd.config_file), *(_dpd.config_line));
1596                     }
1597 
1598                     strncpy(curr_format, start_ch, curr_len);
1599                     CurrFmt->format_string = curr_format;
1600                     CurrFmt->optional = OptFmt;
1601                     OptFmt->prev = CurrFmt;
1602                     iRet = ProcessDateFormat(OptFmt, CurrFmt, &curr_ch);
1603                     if (iRet != FTPP_SUCCESS)
1604                     {
1605                         _dpd.snortFree(OptFmt, sizeof(FTP_DATE_FMT),
1606                                        PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1607                         _dpd.snortFree(curr_format,
1608                                        (curr_len + 1) * sizeof(char),
1609                                        PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1610                         return iRet;
1611                     }
1612                     curr_len = 0;
1613                 }
1614                 start_ch = curr_ch;
1615                 break;
1616             case ']':
1617                 curr_ch++;
1618                 if (curr_len > 0)
1619                 {
1620                     curr_format = (char *)_dpd.snortAlloc(curr_len + 1,
1621                                                           sizeof(char),
1622                                                           PP_FTPTELNET,
1623                                                           PP_MEM_CATEGORY_CONFIG);
1624                     if (curr_format == NULL)
1625                     {
1626                         DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1627                                 *(_dpd.config_file), *(_dpd.config_line));
1628                     }
1629 
1630                     strncpy(curr_format, start_ch, curr_len);
1631                     CurrFmt->format_string = curr_format;
1632                     curr_len = 0;
1633                 }
1634                 *format = curr_ch;
1635                 return FTPP_SUCCESS;
1636                 break;
1637             case '{':
1638                 curr_ch++;
1639                 {
1640                     FTP_DATE_FMT *NewFmt;
1641                     NewFmt = (FTP_DATE_FMT *)_dpd.snortAlloc(1,
1642                                                            sizeof(FTP_DATE_FMT),
1643                                                            PP_FTPTELNET,
1644                                                            PP_MEM_CATEGORY_CONFIG);
1645                     if (NewFmt == NULL)
1646                     {
1647                         DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1648                                 *(_dpd.config_file), *(_dpd.config_line));
1649                     }
1650 
1651                     if (curr_len > 0)
1652                     {
1653                         curr_format = (char *)_dpd.snortAlloc(curr_len + 1,
1654                                                               sizeof(char),
1655                                                               PP_FTPTELNET,
1656                                                               PP_MEM_CATEGORY_CONFIG);
1657                         if (curr_format == NULL)
1658                         {
1659                             DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1660                                     *(_dpd.config_file), *(_dpd.config_line));
1661                         }
1662 
1663                         strncpy(curr_format, start_ch, curr_len);
1664                         CurrFmt->format_string = curr_format;
1665                         curr_len = 0;
1666                     }
1667                     else
1668                     {
1669                         CurrFmt->empty = 1;
1670                     }
1671                     NewFmt->prev = LastNonOptFmt;
1672                     CurrFmt->next_a = NewFmt;
1673                     iRet = ProcessDateFormat(NewFmt, CurrFmt, &curr_ch);
1674                     if (iRet != FTPP_SUCCESS)
1675                     {
1676                         return iRet;
1677                     }
1678                     NewFmt = (FTP_DATE_FMT *)_dpd.snortAlloc(1,
1679                                                   sizeof(FTP_DATE_FMT),
1680                                                   PP_FTPTELNET,
1681                                                   PP_MEM_CATEGORY_CONFIG);
1682                     if (NewFmt == NULL)
1683                     {
1684                         DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1685                                 *(_dpd.config_file), *(_dpd.config_line));
1686                     }
1687 
1688                     NewFmt->prev = LastNonOptFmt;
1689                     CurrFmt->next_b = NewFmt;
1690                     iRet = ProcessDateFormat(NewFmt, CurrFmt, &curr_ch);
1691                     if (iRet != FTPP_SUCCESS)
1692                     {
1693                         return iRet;
1694                     }
1695 
1696                     NewFmt = (FTP_DATE_FMT *)_dpd.snortAlloc(1,
1697                                                    sizeof(FTP_DATE_FMT),
1698                                                    PP_FTPTELNET,
1699                                                    PP_MEM_CATEGORY_CONFIG);
1700                     if (NewFmt == NULL)
1701                     {
1702                         DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1703                                 *(_dpd.config_file), *(_dpd.config_line));
1704                     }
1705 
1706                     NewFmt->prev = CurrFmt;
1707                     CurrFmt->next = NewFmt;
1708                     iRet = ProcessDateFormat(NewFmt, CurrFmt, &curr_ch);
1709                     if (iRet != FTPP_SUCCESS)
1710                     {
1711                         return iRet;
1712                     }
1713                 }
1714                 break;
1715             case '}':
1716                 curr_ch++;
1717                 if (curr_len > 0)
1718                 {
1719                     curr_format = (char *)_dpd.snortAlloc(curr_len + 1,
1720                                                sizeof(char), PP_FTPTELNET,
1721                                                PP_MEM_CATEGORY_CONFIG);
1722                     if (curr_format == NULL)
1723                     {
1724                         DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1725                                 *(_dpd.config_file), *(_dpd.config_line));
1726                     }
1727 
1728                     strncpy(curr_format, start_ch, curr_len);
1729                     CurrFmt->format_string = curr_format;
1730                     curr_len = 0;
1731                     *format = curr_ch;
1732                     return FTPP_SUCCESS;
1733                 }
1734                 else
1735                 {
1736                     CurrFmt->empty = 1;
1737                     *format = curr_ch;
1738                     return FTPP_SUCCESS;
1739                 }
1740                 break;
1741             case '|':
1742                 curr_ch++;
1743                 if (curr_len > 0)
1744                 {
1745                     curr_format = (char *)_dpd.snortAlloc(curr_len + 1,
1746                                                     sizeof(char), PP_FTPTELNET,
1747                                                     PP_MEM_CATEGORY_CONFIG);
1748                     if (curr_format == NULL)
1749                     {
1750                         DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1751                                 *(_dpd.config_file), *(_dpd.config_line));
1752                     }
1753 
1754                     strncpy(curr_format, start_ch, curr_len);
1755                     CurrFmt->format_string = curr_format;
1756                     curr_len = 0;
1757                     *format = curr_ch;
1758                     return FTPP_SUCCESS;
1759                 }
1760                 else
1761                 {
1762                     CurrFmt->empty = 1;
1763                     *format = curr_ch;
1764                     return FTPP_SUCCESS;
1765                 }
1766                 break;
1767             default:
1768                 /* Uh, shouldn't get this.  */
1769                 return FTPP_INVALID_ARG;
1770                 break;
1771         }
1772     }
1773 
1774     if (curr_len > 0)
1775     {
1776         curr_format = (char *)_dpd.snortAlloc(curr_len + 1, sizeof(char),
1777                                       PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1778         if (curr_format == NULL)
1779         {
1780             DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1781                     *(_dpd.config_file), *(_dpd.config_line));
1782         }
1783 
1784         strncpy(curr_format, start_ch, curr_len);
1785         CurrFmt->format_string = curr_format;
1786         start_ch = curr_ch;
1787         curr_len = 0;
1788     }
1789 
1790     /* Should've closed all options & ORs  */
1791     *format = curr_ch;
1792     return FTPP_SUCCESS;
1793 }
1794 
1795 /*
1796  * Function: DoNextFormat(FTP_PARAM_FMT *ThisFmt, int allocated,
1797  *                 char *ErrorString, int ErrStrLen)
1798  *
1799  * Purpose: Processes the next FTP parameter validation node.
1800  *
1801  * Arguments: ThisFmt       => pointer to an FTP parameter validation node
1802  *            allocated     => indicator whether the next node is allocated
1803  *            ErrorString   => error string buffer
1804  *            ErrStrLen     => the length of the error string buffer
1805  *
1806  * Returns: int     => an error code integer (0 = success,
1807  *                     >0 = non-fatal error, <0 = fatal error)
1808  *
1809  */
DoNextFormat(FTP_PARAM_FMT * ThisFmt,int allocated,char * ErrorString,int ErrStrLen)1810 int DoNextFormat(FTP_PARAM_FMT *ThisFmt, int allocated,
1811         char *ErrorString, int ErrStrLen)
1812 {
1813     FTP_PARAM_FMT *NextFmt;
1814     int iRet = FTPP_SUCCESS;
1815     char *fmt = NextToken(CONF_SEPARATORS);
1816 
1817     if (!fmt)
1818         return FTPP_INVALID_ARG;
1819 
1820     if(!strcmp(END_CMD_FORMAT, fmt))
1821     {
1822         return FTPP_SUCCESS;
1823     }
1824 
1825     if (!strcmp(fmt, OR_FMT))
1826     {
1827         return FTPP_OR_FOUND;
1828     }
1829 
1830     if (!strcmp(fmt, END_OPT_FMT))
1831     {
1832         return FTPP_OPT_END_FOUND;
1833     }
1834 
1835     if (!strcmp(fmt, END_CHOICE_FMT))
1836     {
1837         return FTPP_CHOICE_END_FOUND;
1838     }
1839 
1840     if (!strcmp(fmt, START_OPT_FMT))
1841     {
1842         NextFmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT),
1843                                          PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1844         if (NextFmt == NULL)
1845         {
1846             DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1847                     *(_dpd.config_file), *(_dpd.config_line));
1848         }
1849 
1850         ThisFmt->optional_fmt = NextFmt;
1851         NextFmt->optional = 1;
1852         NextFmt->prev_param_fmt = ThisFmt;
1853         if (ThisFmt->optional)
1854             NextFmt->prev_optional = 1;
1855         iRet = DoNextFormat(NextFmt, 1, ErrorString, ErrStrLen);
1856         if (iRet != FTPP_OPT_END_FOUND)
1857         {
1858             return FTPP_INVALID_ARG;
1859         }
1860 
1861         return DoNextFormat(ThisFmt, 0, ErrorString, ErrStrLen);
1862     }
1863 
1864     if (!strcmp(fmt, START_CHOICE_FMT))
1865     {
1866         int numChoices = 1;
1867         do
1868         {
1869             FTP_PARAM_FMT **tmpChoices =
1870                     (FTP_PARAM_FMT **)_dpd.snortAlloc(numChoices,
1871                                                       sizeof(FTP_PARAM_FMT *),
1872                                                       PP_FTPTELNET,
1873                                                       PP_MEM_CATEGORY_CONFIG);
1874             if (tmpChoices == NULL)
1875             {
1876                 DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1877                         *(_dpd.config_file), *(_dpd.config_line));
1878             }
1879 
1880             if (ThisFmt->numChoices)
1881             {
1882                 /* explicit check that we have enough room for copy */
1883                 if (numChoices <= ThisFmt->numChoices)
1884                     DynamicPreprocessorFatalMessage("%s(%d) => Can't do memcpy - index out of range \n",
1885                             *(_dpd.config_file), *(_dpd.config_line));
1886 
1887                 memcpy(tmpChoices, ThisFmt->choices,
1888                         sizeof(FTP_PARAM_FMT*) * ThisFmt->numChoices);
1889             }
1890             NextFmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT),
1891                                           PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1892             if (NextFmt == NULL)
1893             {
1894                 DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1895                         *(_dpd.config_file), *(_dpd.config_line));
1896             }
1897 
1898             tmpChoices[numChoices-1] = NextFmt;
1899             if (ThisFmt->choices)
1900                 _dpd.snortFree(ThisFmt->choices,
1901                                (ThisFmt->numChoices * sizeof(FTP_PARAM_FMT *)),
1902                                PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1903             ThisFmt->numChoices = numChoices;
1904             ThisFmt->choices = tmpChoices;
1905             NextFmt->prev_param_fmt = ThisFmt;
1906             iRet = DoNextFormat(NextFmt, 1, ErrorString, ErrStrLen);
1907             numChoices++;
1908         }
1909         while (iRet == FTPP_OR_FOUND);
1910 
1911         if (iRet != FTPP_CHOICE_END_FOUND)
1912         {
1913             return FTPP_INVALID_ARG;
1914         }
1915 
1916         return DoNextFormat(ThisFmt, 0, ErrorString, ErrStrLen);
1917     }
1918 
1919     if (!allocated)
1920     {
1921         NextFmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT),
1922                                          PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1923         if (NextFmt == NULL)
1924         {
1925             DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1926                     *(_dpd.config_file), *(_dpd.config_line));
1927         }
1928 
1929         NextFmt->prev_param_fmt = ThisFmt;
1930         ThisFmt->next_param_fmt = NextFmt;
1931         if (ThisFmt->optional)
1932             NextFmt->prev_optional = 1;
1933     }
1934     else
1935     {
1936         NextFmt = ThisFmt;
1937     }
1938 
1939     /* If its not an end cmd, OR, START/END Opt...
1940      * it must be a parameter specification.
1941      */
1942     /* Setup the type & format specs  */
1943     if (!strcmp(fmt, F_INT))
1944     {
1945         NextFmt->type = e_int;
1946     }
1947     else if (!strcmp(fmt, F_NUMBER))
1948     {
1949         NextFmt->type = e_number;
1950     }
1951     else if (!strcmp(fmt, F_CHAR))
1952     {
1953         char *chars_allowed = NextToken(CONF_SEPARATORS);
1954         if(!chars_allowed)
1955         {
1956             snprintf(ErrorString, ErrStrLen,
1957                     "Illegal format '' for token '%s'.",
1958                      CMD_VALIDITY);
1959             return FTPP_INVALID_ARG;
1960         }
1961 
1962         NextFmt->type = e_char;
1963         NextFmt->format.chars_allowed = 0;
1964         while (*chars_allowed != 0)
1965         {
1966             int bitNum = (*chars_allowed & 0x1f);
1967             NextFmt->format.chars_allowed |= (1 << (bitNum-1));
1968             chars_allowed++;
1969         }
1970     }
1971     else if (!strcmp(fmt, F_DATE))
1972     {
1973         FTP_DATE_FMT *DateFmt;
1974         char *format = NextToken(CONF_SEPARATORS);
1975         NextFmt->type = e_date;
1976         DateFmt = (FTP_DATE_FMT *)_dpd.snortAlloc(1, sizeof(FTP_DATE_FMT),
1977                                        PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
1978         if (DateFmt == NULL)
1979         {
1980             DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
1981                     *(_dpd.config_file), *(_dpd.config_line));
1982         }
1983 
1984         NextFmt->format.date_fmt = DateFmt;
1985         iRet = ProcessDateFormat(DateFmt, NULL, &format);
1986         if (iRet)
1987         {
1988             snprintf(ErrorString, ErrStrLen,
1989                     "Illegal format %s for token '%s'.",
1990                     format, CMD_VALIDITY);
1991 
1992             return FTPP_INVALID_ARG;
1993         }
1994     }
1995     else if ( *fmt == *F_LITERAL )
1996     {
1997         char* end = strchr(++fmt, *F_LITERAL);
1998         int len = end ? end - fmt : 0;
1999 
2000         if ( len < 1 )
2001         {
2002             snprintf(
2003                     ErrorString, ErrStrLen,
2004                     "Illegal format '' for token '%s'.", CMD_VALIDITY
2005                     );
2006             return FTPP_INVALID_ARG;
2007         }
2008         NextFmt->type = e_literal;
2009         NextFmt->format.literal = (char *)_dpd.snortAlloc(1, len + 1,
2010                                         PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
2011         if ( !NextFmt->format.literal )
2012         {
2013             DynamicPreprocessorFatalMessage(
2014                     "%s(%d) => Failed to allocate memory\n",
2015                     *(_dpd.config_file), *(_dpd.config_line)
2016                     );
2017         }
2018         strncpy(NextFmt->format.literal, fmt, len);
2019         NextFmt->format.literal[len] = '\0';
2020     }
2021     else if (!strcmp(fmt, F_STRING))
2022     {
2023         NextFmt->type = e_unrestricted;
2024     }
2025     else if (!strcmp(fmt, F_HOST_PORT))
2026     {
2027         NextFmt->type = e_host_port;
2028     }
2029     else if (!strcmp(fmt, F_LONG_HOST_PORT))
2030     {
2031         NextFmt->type = e_long_host_port;
2032     }
2033     else if (!strcmp(fmt, F_EXTD_HOST_PORT))
2034     {
2035         NextFmt->type = e_extd_host_port;
2036     }
2037     else
2038     {
2039         snprintf(ErrorString, ErrStrLen,
2040                 "Illegal format type %s for token '%s'.",
2041                 fmt, CMD_VALIDITY);
2042 
2043         return FTPP_INVALID_ARG;
2044     }
2045 
2046     return DoNextFormat(NextFmt, 0, ErrorString, ErrStrLen);
2047 }
2048 
2049 /*
2050  * Function: ProcessFTPCmdValidity(FTP_SERVER_PROTO_CONF *ServerConf,
2051  *                              char *ErrorString, int ErrStrLen)
2052  *
2053  * Purpose: Process the ftp cmd validity configuration.
2054  *          This sets the FTP command parameter validation tree.
2055  *
2056  * Arguments: ServerConf    => pointer to the FTP server configuration
2057  *            confOption    => pointer to the name of the option
2058  *            ErrorString   => error string buffer
2059  *            ErrStrLen     => the length of the error string buffer
2060  *
2061  * Returns: int     => an error code integer (0 = success,
2062  *                     >0 = non-fatal error, <0 = fatal error)
2063  *
2064  */
ProcessFTPCmdValidity(FTP_SERVER_PROTO_CONF * ServerConf,char * ErrorString,int ErrStrLen)2065 static int ProcessFTPCmdValidity(FTP_SERVER_PROTO_CONF *ServerConf,
2066         char *ErrorString, int ErrStrLen)
2067 {
2068     FTP_CMD_CONF *FTPCmd = NULL;
2069     FTP_PARAM_FMT *HeadFmt = NULL;
2070     char *cmd;
2071     char *fmt;
2072     int iRet;
2073 
2074     fmt = NextToken(CONF_SEPARATORS);
2075     if(fmt == NULL)
2076     {
2077         snprintf(ErrorString, ErrStrLen,
2078                 "No argument to token '%s'.", CMD_VALIDITY);
2079 
2080         return FTPP_FATAL_ERR;
2081     }
2082 
2083     cmd = fmt;
2084 
2085     fmt = NextToken(CONF_SEPARATORS);
2086     if(!fmt)
2087     {
2088         snprintf(ErrorString, ErrStrLen,
2089                 "Invalid cmd validity format.");
2090 
2091         return FTPP_FATAL_ERR;
2092     }
2093 
2094     if(strcmp(START_CMD_FORMAT, fmt))
2095     {
2096         snprintf(ErrorString, ErrStrLen,
2097                 "Must start a cmd validity with the '%s' token.",
2098                 START_CMD_FORMAT);
2099 
2100         return FTPP_FATAL_ERR;
2101     }
2102 
2103     HeadFmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT),
2104                                          PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
2105     if (HeadFmt == NULL)
2106     {
2107         DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
2108                 *(_dpd.config_file), *(_dpd.config_line));
2109     }
2110 
2111     HeadFmt->type = e_head;
2112 
2113     iRet = DoNextFormat(HeadFmt, 0, ErrorString, ErrStrLen);
2114 
2115     /* Need to check to be sure we got a complete command  */
2116     if (iRet)
2117     {
2118         return FTPP_FATAL_ERR;
2119     }
2120 
2121     SetOptionalsNext(HeadFmt, NULL, NULL, 0);
2122 
2123     FTPCmd = ftp_cmd_lookup_find(ServerConf->cmd_lookup, cmd,
2124             strlen(cmd), &iRet);
2125     if (FTPCmd == NULL)
2126     {
2127         /* Add it to the list  */
2128         // note that struct includes 1 byte for null, so just add len
2129         FTPCmd = (FTP_CMD_CONF *)_dpd.snortAlloc(1,
2130                                          sizeof(FTP_CMD_CONF)+strlen(cmd),
2131                                          PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
2132         if (FTPCmd == NULL)
2133         {
2134             DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
2135                     *(_dpd.config_file), *(_dpd.config_line));
2136         }
2137 
2138         strcpy(FTPCmd->cmd_name, cmd);
2139 
2140         FTPCmd->max_param_len = ServerConf->def_max_param_len;
2141         ftp_cmd_lookup_add(ServerConf->cmd_lookup, cmd, strlen(cmd), FTPCmd);
2142     }
2143 
2144     FTPCmd->check_validity = 1;
2145     if (FTPCmd->param_format)
2146     {
2147         ftpp_ui_config_reset_ftp_cmd_format(FTPCmd->param_format);
2148         FTPCmd->param_format = NULL;
2149     }
2150     FTPCmd->param_format = HeadFmt;
2151 
2152     return FTPP_SUCCESS;
2153 }
2154 
2155 /*
2156  * Function: PrintFormatDate(FTP_DATE_FMT *DateFmt)
2157  *
2158  * Purpose: Recursively prints the FTP date validation tree
2159  *
2160  * Arguments: DateFmt       => pointer to the date format node
2161  *
2162  * Returns: None
2163  *
2164  */
PrintFormatDate(char * buf,FTP_DATE_FMT * DateFmt)2165 static void PrintFormatDate(char *buf, FTP_DATE_FMT *DateFmt)
2166 {
2167     FTP_DATE_FMT *OptChild;
2168 
2169     if (!DateFmt->empty)
2170         _dpd.printfappend(buf, BUF_SIZE, "%s", DateFmt->format_string);
2171 
2172     if (DateFmt->optional)
2173     {
2174         OptChild = DateFmt->optional;
2175         _dpd.printfappend(buf, BUF_SIZE, "[");
2176         PrintFormatDate(buf, OptChild);
2177         _dpd.printfappend(buf, BUF_SIZE, "]");
2178     }
2179 
2180     if (DateFmt->next_a)
2181     {
2182         if (DateFmt->next_b)
2183             _dpd.printfappend(buf, BUF_SIZE, "{");
2184         OptChild = DateFmt->next_a;
2185         PrintFormatDate(buf, OptChild);
2186         if (DateFmt->next_b)
2187         {
2188             _dpd.printfappend(buf, BUF_SIZE, "|");
2189             OptChild = DateFmt->next_b;
2190             PrintFormatDate(buf, OptChild);
2191             _dpd.printfappend(buf, BUF_SIZE, "}");
2192         }
2193     }
2194 
2195     if (DateFmt->next)
2196         PrintFormatDate(buf, DateFmt->next);
2197 }
2198 
2199 /*
2200  * Function: PrintCmdFmt(FTP_PARAM_FMT *CmdFmt)
2201  *
2202  * Purpose: Recursively prints the FTP command parameter validation tree
2203  *
2204  * Arguments: CmdFmt       => pointer to the parameter validation node
2205  *
2206  * Returns: None
2207  *
2208  */
PrintCmdFmt(char * buf,FTP_PARAM_FMT * CmdFmt)2209 static void PrintCmdFmt(char *buf, FTP_PARAM_FMT *CmdFmt)
2210 {
2211     FTP_PARAM_FMT *OptChild;
2212 
2213     switch(CmdFmt->type)
2214     {
2215         case e_int:
2216             _dpd.printfappend(buf, BUF_SIZE, " %s", F_INT);
2217             break;
2218         case e_number:
2219             _dpd.printfappend(buf, BUF_SIZE, " %s", F_NUMBER);
2220             break;
2221         case e_char:
2222             _dpd.printfappend(buf, BUF_SIZE, " %s 0x%x", F_CHAR,
2223                     CmdFmt->format.chars_allowed);
2224             break;
2225         case e_date:
2226             _dpd.printfappend(buf, BUF_SIZE, " %s", F_DATE);
2227             PrintFormatDate(buf, CmdFmt->format.date_fmt);
2228             break;
2229         case e_literal:
2230             _dpd.printfappend(buf, BUF_SIZE, " %s 0x%x", F_LITERAL,
2231                     CmdFmt->format.literal);
2232             break;
2233         case e_unrestricted:
2234             _dpd.printfappend(buf, BUF_SIZE, " %s", F_STRING);
2235             break;
2236         case e_strformat:
2237             _dpd.printfappend(buf, BUF_SIZE, " %s", F_STRING_FMT);
2238             break;
2239         case e_host_port:
2240             _dpd.printfappend(buf, BUF_SIZE, " %s", F_HOST_PORT);
2241             break;
2242         case e_long_host_port:
2243             _dpd.printfappend(buf, BUF_SIZE, " %s", F_LONG_HOST_PORT);
2244             break;
2245         case e_extd_host_port:
2246             _dpd.printfappend(buf, BUF_SIZE, " %s", F_EXTD_HOST_PORT);
2247             break;
2248         case e_head:
2249             break;
2250         default:
2251             break;
2252     }
2253 
2254     if (CmdFmt->optional_fmt)
2255     {
2256         OptChild = CmdFmt->optional_fmt;
2257         _dpd.printfappend(buf, BUF_SIZE, "[");
2258         PrintCmdFmt(buf, OptChild);
2259         _dpd.printfappend(buf, BUF_SIZE, "]");
2260     }
2261 
2262     if (CmdFmt->numChoices)
2263     {
2264         int i;
2265         _dpd.printfappend(buf, BUF_SIZE, "{");
2266         for (i=0;i<CmdFmt->numChoices;i++)
2267         {
2268             if (i)
2269                 _dpd.printfappend(buf, BUF_SIZE, "|");
2270             OptChild = CmdFmt->choices[i];
2271             PrintCmdFmt(buf, OptChild);
2272         }
2273         _dpd.printfappend(buf, BUF_SIZE, "}");
2274     }
2275 
2276     if (CmdFmt->next_param_fmt && CmdFmt->next_param_fmt->prev_optional)
2277         PrintCmdFmt(buf, CmdFmt->next_param_fmt);
2278 
2279 }
2280 
2281 /*
2282  * Function: ProcessFTPMaxRespLen(FTP_CLIENT_PROTO_CONF *ClientConf,
2283  *                                char *ErrorString, int ErrStrLen)
2284  *
2285  * Purpose: Process the max response length configuration
2286  *          This sets the max length of an FTP response that we
2287  *          will tolerate, before alerting.
2288  *
2289  * Arguments: ClientConf    => pointer to the FTP client configuration
2290  *            ErrorString   => error string buffer
2291  *            ErrStrLen     => the length of the error string buffer
2292  *
2293  * Returns: int     => an error code integer (0 = success,
2294  *                     >0 = non-fatal error, <0 = fatal error)
2295  *
2296  */
ProcessFTPMaxRespLen(FTP_CLIENT_PROTO_CONF * ClientConf,char * ErrorString,int ErrStrLen)2297 static int ProcessFTPMaxRespLen(FTP_CLIENT_PROTO_CONF *ClientConf,
2298         char *ErrorString, int ErrStrLen)
2299 {
2300     char *pcToken;
2301     char *pcEnd = NULL;
2302     long int max_resp_len;
2303 
2304     pcToken = NextToken(CONF_SEPARATORS);
2305     if(pcToken == NULL)
2306     {
2307         snprintf(ErrorString, ErrStrLen,
2308                 "No argument to token '%s'.", MAX_RESP_LEN);
2309 
2310         return FTPP_FATAL_ERR;
2311     }
2312 
2313     max_resp_len = _dpd.SnortStrtol(pcToken, &pcEnd, 10);
2314 
2315     /*
2316      * Let's check to see if the entire string was valid.
2317      * If there is an address here, then there was an
2318      * invalid character in the string.
2319      */
2320     if ((*pcEnd) || (max_resp_len < 0) || (errno == ERANGE))
2321     {
2322         snprintf(ErrorString, ErrStrLen,
2323                 "Invalid argument to token '%s'.  Must be a positive "
2324                 "number.", MAX_RESP_LEN);
2325 
2326         return FTPP_FATAL_ERR;
2327     }
2328 
2329     ClientConf->max_resp_len = (unsigned int)max_resp_len;
2330 
2331     return FTPP_SUCCESS;
2332 }
2333 
2334 /*
2335  * Function:  ParseBounceTo(char *token, FTP_BOUNCE_TO*)
2336  *
2337  * Purpose: Extract the IP address, masking bits (CIDR format), and
2338  *          port information from an FTP Bounce To configuration.
2339  *
2340  * Arguments: token         => string pointer to the FTP bounce configuration
2341  *                             required format:  IP/CIDR,port[,portHi]\0
2342  *            FTP_BOUNCE_TO => populated with parsed data
2343  *
2344  * Returns:   int           => an error code integer (0 = success,
2345  *                             >0 = non-fatal error, <0 = fatal error)
2346  *
2347  */
ParseBounceTo(char * token,FTP_BOUNCE_TO * bounce)2348 int ParseBounceTo(char* token, FTP_BOUNCE_TO* bounce)
2349 {
2350     char **toks;
2351     int num_toks;
2352     long int port_lo;
2353     char *endptr = NULL;
2354     sfcidr_t tmp_ip;
2355 
2356     toks = _dpd.tokenSplit(token, ",", 3, &num_toks, 0);
2357     if (num_toks < 2)
2358         return FTPP_INVALID_ARG;
2359 
2360     if (sfip_pton(toks[0], &tmp_ip) != SFIP_SUCCESS)
2361     {
2362         _dpd.tokenFree(&toks, num_toks);
2363         return FTPP_INVALID_ARG;
2364     }
2365 
2366     memcpy(&bounce->ip, &tmp_ip, sizeof(sfcidr_t));
2367 
2368     port_lo = _dpd.SnortStrtol(toks[1], &endptr, 10);
2369     if ((errno == ERANGE) || (*endptr != '\0') ||
2370             (port_lo < 0) || (port_lo >= MAXPORTS))
2371     {
2372         _dpd.tokenFree(&toks, num_toks);
2373         return FTPP_INVALID_ARG;
2374     }
2375 
2376     bounce->portlo = (unsigned short)port_lo;
2377 
2378     if (num_toks == 3)
2379     {
2380         long int port_hi = _dpd.SnortStrtol(toks[2], &endptr, 10);
2381 
2382         if ((errno == ERANGE) || (*endptr != '\0') ||
2383                 (port_hi < 0) || (port_hi >= MAXPORTS))
2384         {
2385             _dpd.tokenFree(&toks, num_toks);
2386             return FTPP_INVALID_ARG;
2387         }
2388 
2389         if (bounce->portlo != (unsigned short)port_hi)
2390         {
2391             bounce->porthi = (unsigned short)port_hi;
2392             if (bounce->porthi < bounce->portlo)
2393             {
2394                 unsigned short tmp = bounce->porthi;
2395                 bounce->porthi = bounce->portlo;
2396                 bounce->portlo = tmp;
2397             }
2398         }
2399     }
2400 
2401     _dpd.tokenFree(&toks, num_toks);
2402     return FTPP_SUCCESS;
2403 }
2404 
2405 /*
2406  * Function: ProcessFTPAlowBounce(FTP_CLIENT_PROTO_CONF *ClientConf,
2407  *                                char *ErrorString, int ErrStrLen)
2408  *
2409  * Purpose: Process the FTP allow bounce configuration.
2410  *          This creates an allow bounce node and adds it to the list for the
2411  *          client configuration.
2412  *
2413  * Arguments: ClientConf    => pointer to the FTP client configuration
2414  *            ErrorString   => error string buffer
2415  *            ErrStrLen     => the length of the error string buffer
2416  *
2417  * Returns: int     => an error code integer (0 = success,
2418  *                     >0 = non-fatal error, <0 = fatal error)
2419  *
2420  */
ProcessFTPAllowBounce(FTP_CLIENT_PROTO_CONF * ClientConf,char * ErrorString,int ErrStrLen)2421 static int ProcessFTPAllowBounce(FTP_CLIENT_PROTO_CONF *ClientConf,
2422         char *ErrorString, int ErrStrLen)
2423 {
2424     char *pcToken;
2425     int iOneAddr = 0;
2426     int iEndList = 0;
2427     int iRet;
2428 
2429     pcToken = NextToken(CONF_SEPARATORS);
2430     if(pcToken == NULL)
2431     {
2432         snprintf(ErrorString, ErrStrLen,
2433                 "No argument to token '%s'.", ALLOW_BOUNCE);
2434 
2435         return FTPP_FATAL_ERR;
2436     }
2437 
2438     if(strcmp(START_PORT_LIST, pcToken))
2439     {
2440         snprintf(ErrorString, ErrStrLen,
2441                 "Must start a %s list with the '%s' token.",
2442                 ALLOW_BOUNCE, START_PORT_LIST);
2443 
2444         return FTPP_FATAL_ERR;
2445     }
2446 
2447     while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL)
2448     {
2449         FTP_BOUNCE_TO *newBounce;
2450 
2451         if(!strcmp(END_PORT_LIST, pcToken))
2452         {
2453             iEndList = 1;
2454             break;
2455         }
2456 
2457         /* TODO: Maybe want to redo this with high-speed searcher for ip/port.
2458          * Would be great if we could handle both full addresses and
2459          * subnets quickly -- using CIDR format.  Need something that would
2460          * return most specific match -- ie a specific host is more specific
2461          * than subnet.
2462          */
2463         newBounce = (FTP_BOUNCE_TO *)_dpd.snortAlloc(1, sizeof(FTP_BOUNCE_TO),
2464                                          PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
2465         if (newBounce == NULL)
2466         {
2467             snprintf(ErrorString, ErrStrLen,
2468                     "Failed to allocate memory for Bounce");
2469             return FTPP_FATAL_ERR;
2470         }
2471 
2472         iRet = ParseBounceTo(pcToken, newBounce);
2473         if (iRet)
2474         {
2475             snprintf(ErrorString, ErrStrLen,
2476                     "Invalid argument to token '%s': %s", ALLOW_BOUNCE, pcToken);
2477             _dpd.snortFree(newBounce, sizeof(FTP_BOUNCE_TO),
2478                            PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
2479             return FTPP_FATAL_ERR;
2480         }
2481 
2482         iRet = ftp_bounce_lookup_add(
2483                 ClientConf->bounce_lookup, &newBounce->ip, newBounce
2484                 );
2485         if (iRet)
2486         {
2487             snprintf(ErrorString, ErrStrLen,
2488                     "Failed to add configuration for Bounce object '%s'.", ALLOW_BOUNCE);
2489             _dpd.snortFree(newBounce, sizeof(FTP_BOUNCE_TO),
2490                            PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
2491             return FTPP_FATAL_ERR;
2492         }
2493 
2494         iOneAddr = 1;
2495     }
2496 
2497     if(!iEndList)
2498     {
2499         snprintf(ErrorString, ErrStrLen,
2500                 "Must end '%s' configuration with '%s'.",
2501                 ALLOW_BOUNCE, END_PORT_LIST);
2502 
2503         return FTPP_FATAL_ERR;
2504     }
2505 
2506     if(!iOneAddr)
2507     {
2508         snprintf(ErrorString, ErrStrLen,
2509                 "Must include at least one address in '%s' configuration.",
2510                 ALLOW_BOUNCE);
2511 
2512         return FTPP_FATAL_ERR;
2513     }
2514 
2515     return FTPP_SUCCESS;
2516 }
2517 
2518 /*
2519  * Function: PrintFTPClientConf(char * client,
2520  *                              FTP_CLIENT_PROTO_CONF *ClientConf)
2521  *
2522  * Purpose: Prints the FTP client configuration
2523  *
2524  * Arguments: client        => string pointer to the client IP
2525  *            ClientConf    => pointer to the client configuration
2526  *
2527  * Returns: int     => an error code integer (0 = success,
2528  *                     >0 = non-fatal error, <0 = fatal error)
2529  *
2530  */
PrintFTPClientConf(char * client,FTP_CLIENT_PROTO_CONF * ClientConf)2531 static int PrintFTPClientConf(char * client, FTP_CLIENT_PROTO_CONF *ClientConf)
2532 {
2533     FTP_BOUNCE_TO *FTPBounce;
2534     int iErr;
2535 
2536     if(!ClientConf)
2537     {
2538         return FTPP_INVALID_ARG;
2539     }
2540 
2541     if (!printedFTPHeader)
2542     {
2543         _dpd.logMsg("    FTP CONFIG:\n");
2544         printedFTPHeader = 1;
2545     }
2546 
2547     _dpd.logMsg("      FTP Client: %s\n", client);
2548 
2549     PrintConfOpt(&ClientConf->bounce, "  Check for Bounce Attacks");
2550     PrintConfOpt(&ClientConf->telnet_cmds, "  Check for Telnet Cmds");
2551     PrintConfOpt(&ClientConf->ignore_telnet_erase_cmds, "  Ignore Telnet Cmd Operations");
2552     _dpd.logMsg("        Max Response Length: %d\n", ClientConf->max_resp_len);
2553 
2554     FTPBounce = ftp_bounce_lookup_first(ClientConf->bounce_lookup, &iErr);
2555     if (FTPBounce)
2556     {
2557         _dpd.logMsg("        Allow FTP bounces to:\n");
2558 
2559         while (FTPBounce)
2560         {
2561             char *addr_str;
2562             char bits_str[5];
2563             int bits;
2564             bits_str[0] = '\0';
2565 
2566             addr_str = sfip_to_str(&FTPBounce->ip.addr);
2567             bits = (int)FTPBounce->ip.bits;
2568             if (bits != 128)
2569             {
2570                 snprintf(bits_str, sizeof(bits_str), "/%d",
2571                          (sfaddr_family(&FTPBounce->ip.addr) == AF_INET) ? ((bits >= 96) ? (bits - 96) : -1) : bits);
2572             }
2573             if (FTPBounce->porthi)
2574             {
2575                 _dpd.logMsg("          Address: %s%s, Ports: %d-%d\n",
2576                         addr_str, bits_str[0] ? bits_str : "",
2577                         FTPBounce->portlo, FTPBounce->porthi);
2578             }
2579             else
2580             {
2581                 _dpd.logMsg("          Address: %s%s, Port: %d\n",
2582                         addr_str, bits_str[0] ? bits_str : "",
2583                         FTPBounce->portlo);
2584             }
2585 
2586             FTPBounce = ftp_bounce_lookup_next(ClientConf->bounce_lookup, &iErr);
2587         }
2588     }
2589 
2590     return FTPP_SUCCESS;
2591 }
2592 
2593 /*
2594  * Function: ProcessFTPClientOptions(FTP_CLIENT_PROTO_CONF *ClientConf,
2595  *                          char *ErrorString, int ErrStrLen)
2596  *
2597  * Purpose: This is where we process the specific ftp client configuration
2598  *          for FTPTelnet.
2599  *
2600  *          We set the values of the ftp client configuraiton here.  Any errors
2601  *          that are encountered are specified in the error string and the type
2602  *          of error is returned through the return code, i.e. fatal, non-fatal.
2603  *
2604  * Arguments: ClientConf    => pointer to the client configuration
2605  *            ErrorString   => error string buffer
2606  *            ErrStrLen     => the length of the error string buffer
2607  *
2608  * Returns: int     => an error code integer (0 = success,
2609  *                     >0 = non-fatal error, <0 = fatal error)
2610  *
2611  */
ProcessFTPClientOptions(FTP_CLIENT_PROTO_CONF * ClientConf,char * ErrorString,int ErrStrLen)2612 int ProcessFTPClientOptions(FTP_CLIENT_PROTO_CONF *ClientConf,
2613         char *ErrorString, int ErrStrLen)
2614 {
2615     FTPTELNET_CONF_OPT *ConfOpt;
2616     int  iRet;
2617     char *pcToken;
2618     int  iTokens = 0;
2619 
2620     while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL)
2621     {
2622         /*
2623          * Show that we at least got one token
2624          */
2625         iTokens = 1;
2626 
2627         /*
2628          * Search for configuration keywords
2629          */
2630         if(!strcmp(MAX_RESP_LEN, pcToken))
2631         {
2632             iRet = ProcessFTPMaxRespLen(ClientConf, ErrorString, ErrStrLen);
2633             if (iRet)
2634             {
2635                 return iRet;
2636             }
2637         }
2638         else if (!strcmp(ALLOW_BOUNCE, pcToken))
2639         {
2640             iRet = ProcessFTPAllowBounce(ClientConf, ErrorString, ErrStrLen);
2641             if (iRet)
2642             {
2643                 return iRet;
2644             }
2645         }
2646         /*
2647          * Start the CONF_OPT configurations.
2648          */
2649         else if(!strcmp(BOUNCE, pcToken))
2650         {
2651             ConfOpt = &ClientConf->bounce;
2652             iRet = ProcessConfOpt(ConfOpt, BOUNCE, ErrorString, ErrStrLen);
2653             if (iRet)
2654             {
2655                 return iRet;
2656             }
2657         }
2658         else if(!strcmp(TELNET_CMDS, pcToken))
2659         {
2660             ConfOpt = &ClientConf->telnet_cmds;
2661             iRet = ProcessConfOpt(ConfOpt, TELNET_CMDS, ErrorString, ErrStrLen);
2662             if (iRet)
2663             {
2664                 return iRet;
2665             }
2666         }
2667         else if(!strcmp(IGNORE_TELNET_CMDS, pcToken))
2668         {
2669             ConfOpt = &ClientConf->ignore_telnet_erase_cmds;
2670             iRet = ProcessConfOpt(ConfOpt, IGNORE_TELNET_CMDS, ErrorString, ErrStrLen);
2671             if (iRet)
2672             {
2673                 return iRet;
2674             }
2675         }
2676         else
2677         {
2678             snprintf(ErrorString, ErrStrLen,
2679                     "Invalid keyword '%s' for '%s' configuration.",
2680                     pcToken, GLOBAL);
2681 
2682             return FTPP_FATAL_ERR;
2683         }
2684     }
2685 
2686     /*
2687      * If there are not any tokens to the configuration, then
2688      * we let the user know and log the error.  return non-fatal
2689      * error.
2690      */
2691     if(!iTokens)
2692     {
2693         snprintf(ErrorString, ErrStrLen,
2694                 "No tokens to '%s %s' configuration.", FTP, CLIENT);
2695 
2696         return FTPP_NONFATAL_ERR;
2697     }
2698 
2699     return FTPP_SUCCESS;
2700 }
2701 
2702 /*
2703  * Function: ProcessFTPClientConf(FTPTELNET_GLOBAL_CONF *GlobalConf,
2704  *                          char *ErrorString, int ErrStrLen)
2705  *
2706  * Purpose: This is where we process the ftp client configuration for FTPTelnet.
2707  *
2708  *          We set the values of the ftp client configuraiton here.  Any errors
2709  *          that are encountered are specified in the error string and the type
2710  *          of error is returned through the return code, i.e. fatal, non-fatal.
2711  *
2712  *          The configuration options that are dealt with here are:
2713  *          ports { x }        Ports on which to do FTP checks
2714  *          telnet_cmds yes|no Detect telnet cmds on FTP command channel
2715  *          ignore_telnet_erase_cmds yes|no  Do not process telnet EAC and EAL
2716  *                             commands during normalization of FTP command
2717  *                             channel.
2718  *          max_resp_len x     Max response length
2719  *          bounce yes|no      Detect FTP bounce attacks
2720  *          bounce_to IP port|port-range Allow FTP bounces to specified IP/ports
2721  *          data_chan          Ignore data channel OR coordinate with cmd chan
2722  *
2723  * Arguments: GlobalConf    => pointer to the global configuration
2724  *            ErrorString   => error string buffer
2725  *            ErrStrLen     => the length of the error string buffer
2726  *
2727  * Returns: int     => an error code integer (0 = success,
2728  *                     >0 = non-fatal error, <0 = fatal error)
2729  *
2730  */
ProcessFTPClientConf(struct _SnortConfig * sc,FTPTELNET_GLOBAL_CONF * GlobalConf,char * ErrorString,int ErrStrLen)2731 int ProcessFTPClientConf(struct _SnortConfig *sc, FTPTELNET_GLOBAL_CONF *GlobalConf,
2732         char *ErrorString, int ErrStrLen)
2733 {
2734     int  iRet;
2735     int  retVal = 0;
2736     char *client;
2737     char client_list[STD_BUF];
2738     sfcidr_t ipAddr;
2739     char *pIpAddressList = NULL;
2740     char *pIpAddressList2 = NULL;
2741     char *brkt = NULL;
2742     char firstIpAddress = 1;
2743     FTP_CLIENT_PROTO_CONF *new_client_conf = NULL;
2744 
2745     //char *ConfigParseResumePtr = NULL;  // Use this if a default client conf is added
2746     char ip_list = 0;
2747     FTP_CLIENT_PROTO_CONF *ftp_conf = NULL;
2748 
2749     /*
2750      * If not default, create one for this IP
2751      */
2752     client = NextToken(CONF_SEPARATORS);
2753 
2754     if ( !client )
2755     {
2756         DynamicPreprocessorFatalMessage(
2757                 "%s(%d) Missing ftp_telnet ftp client address.\n",
2758                 *(_dpd.config_file), *(_dpd.config_line));
2759     }
2760     else if(strcmp(DEFAULT, client))
2761     {
2762         /*
2763          **  Convert string to IP address
2764          */
2765         /// get the first delimiter
2766         if(strcmp(START_IPADDR_LIST, client) == 0)
2767         {
2768             //list begin token matched
2769             ip_list = 1;
2770             if ((pIpAddressList = mystrtok(NULL, END_IPADDR_LIST)) == NULL)
2771             {
2772                 snprintf(ErrorString, ErrStrLen,
2773                         "Invalid IP Address list in '%s' token.", CLIENT);
2774 
2775                 retVal = FTPP_INVALID_ARG;
2776                 goto _return;
2777             }
2778         }
2779         else
2780         {
2781             //list begin didn't match so this must be an IP address
2782             pIpAddressList = client;
2783         }
2784 
2785         //ConfigParseResumePtr = pIpAddressList+strlen(pIpAddressList);
2786 
2787         pIpAddressList2 = strdup(pIpAddressList);
2788         if (!pIpAddressList2)
2789         {
2790             snprintf(ErrorString, ErrStrLen,
2791                     "Could not allocate memory for server configuration.");
2792 
2793             retVal = FTPP_INVALID_ARG;
2794             goto _return;
2795         }
2796 
2797 
2798 
2799         for (client = strtok_r(pIpAddressList2, CONF_SEPARATORS, &brkt);
2800                 client;
2801                 client = strtok_r(NULL, CONF_SEPARATORS, &brkt))
2802         {
2803 
2804             if (sfip_pton(client, &ipAddr) != SFIP_SUCCESS)
2805             {
2806                 snprintf(ErrorString, ErrStrLen,
2807                         "Invalid IP to '%s' token.", CLIENT);
2808 
2809                 retVal = FTPP_INVALID_ARG;
2810                 goto _return;
2811             }
2812 
2813             /*
2814              **  allocate the memory for the client configuration
2815              */
2816             if (firstIpAddress)
2817             {
2818                 // Write this IP into the buffer for printing
2819                 snprintf(client_list, STD_BUF, "%s", client);
2820 
2821                 new_client_conf = (FTP_CLIENT_PROTO_CONF *)_dpd.snortAlloc(1,
2822                                                   sizeof(FTP_CLIENT_PROTO_CONF),
2823                                                   PP_FTPTELNET,
2824                                                   PP_MEM_CATEGORY_CONFIG);
2825                 if (new_client_conf == NULL)
2826                 {
2827                     DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
2828                             *(_dpd.config_file), *(_dpd.config_line));
2829                 }
2830 
2831                 ftpp_ui_config_reset_ftp_client(new_client_conf, 1);
2832 
2833                 //process the first IP address as usual
2834                 firstIpAddress = 0;
2835 
2836                 ftp_conf = new_client_conf;
2837             }
2838             else
2839             {
2840                 // Write this IP into the buffer for printing
2841                 snprintf(client_list + strlen(client_list), STD_BUF - strlen(client_list) , ", %s", client);
2842 
2843                 new_client_conf = ftp_conf;
2844             }
2845 
2846 
2847             ftpp_ui_config_add_ftp_client(GlobalConf, &ipAddr, new_client_conf);
2848 
2849             //create a reference
2850             new_client_conf->referenceCount++;
2851         }
2852 
2853         if (firstIpAddress)
2854         {
2855             //no IP address was found
2856             snprintf(ErrorString, ErrStrLen,
2857                     "Invalid IP Address list in '%s' token.", CLIENT);
2858 
2859             retVal = FTPP_INVALID_ARG;
2860             goto _return;
2861         }
2862     }
2863     else
2864     {
2865         /**default configuration */
2866 
2867         if (GlobalConf->default_ftp_client != NULL)
2868         {
2869             snprintf(ErrorString, ErrStrLen,
2870                     "Cannot configure '%s' settings more than once.", CLIENT);
2871 
2872             retVal = FTPP_INVALID_ARG;
2873             goto _return;
2874         }
2875 
2876         GlobalConf->default_ftp_client =
2877             (FTP_CLIENT_PROTO_CONF *)_dpd.snortAlloc(1,
2878                                      sizeof(FTP_CLIENT_PROTO_CONF),
2879                                      PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
2880         if (GlobalConf->default_ftp_client == NULL)
2881         {
2882             DynamicPreprocessorFatalMessage("Out of memory trying to create "
2883                     "default ftp client configuration.\n");
2884         }
2885 
2886         ftpp_ui_config_reset_ftp_client(GlobalConf->default_ftp_client, 0);
2887         ftp_conf = GlobalConf->default_ftp_client;
2888 
2889         //ConfigParseResumePtr = client+strlen(client);
2890     }
2891 
2892     iRet = ProcessFTPClientOptions(ftp_conf, ErrorString, ErrStrLen);
2893     if (iRet < 0)
2894     {
2895         retVal = FTPP_INVALID_ARG;
2896         goto _return;
2897     }
2898 
2899     /*
2900      * Let's print out the FTP config
2901      */
2902     if (ip_list)
2903     {
2904         client = &client_list[0];
2905     }
2906     else if (pIpAddressList2)
2907     {
2908         client = pIpAddressList2;
2909     }
2910     PrintFTPClientConf(client, ftp_conf);
2911 
2912 _return:
2913     if (pIpAddressList2)
2914     {
2915         free(pIpAddressList2);
2916     }
2917     return retVal;
2918 }
2919 
2920 /*
2921  * Function: PrintFTPServerConf(char * server,
2922  *                              FTP_SERVER_PROTO_CONF *ServerConf)
2923  *
2924  * Purpose: Prints the FTP server configuration
2925  *
2926  * Arguments: server        => string pointer to the server IP
2927  *            ServerConf    => pointer to the server configuration
2928  *
2929  * Returns: int     => an error code integer (0 = success,
2930  *                     >0 = non-fatal error, <0 = fatal error)
2931  *
2932  */
PrintFTPServerConf(char * server,FTP_SERVER_PROTO_CONF * ServerConf)2933 static int PrintFTPServerConf(char * server, FTP_SERVER_PROTO_CONF *ServerConf)
2934 {
2935     const char* spaf = "";
2936     char buf[BUF_SIZE+1];
2937     int iCtr;
2938     int iRet;
2939     FTP_CMD_CONF *FTPCmd;
2940 
2941     if(!ServerConf)
2942     {
2943         return FTPP_INVALID_ARG;
2944     }
2945 
2946     if (!printedFTPHeader)
2947     {
2948         _dpd.logMsg("    FTP CONFIG:\n");
2949         printedFTPHeader = 1;
2950     }
2951 
2952     if ( _dpd.isPafEnabled() )
2953         spaf = " (PAF)";
2954 
2955     _dpd.logMsg("      FTP Server: %s\n", server);
2956 
2957     memset(buf, 0, BUF_SIZE+1);
2958     snprintf(buf, BUF_SIZE, "        Ports%s: ", spaf);
2959 
2960     /*
2961      * Print out all the applicable ports.
2962      */
2963     for(iCtr = 0; iCtr < MAXPORTS; iCtr++)
2964     {
2965         if(ServerConf->proto_ports.ports[iCtr])
2966         {
2967             _dpd.printfappend(buf, BUF_SIZE, "%d ", iCtr);
2968         }
2969     }
2970 
2971     _dpd.logMsg("%s\n", buf);
2972 
2973     PrintConfOpt(&ServerConf->telnet_cmds, "  Check for Telnet Cmds");
2974     PrintConfOpt(&ServerConf->ignore_telnet_erase_cmds, "  Ignore Telnet Cmd Operations");
2975     _dpd.logMsg("        Ignore open data channels: %s\n",
2976             ServerConf->data_chan ? "YES" : "NO");
2977 
2978     if (ServerConf->print_commands)
2979     {
2980         _dpd.logMsg("        FTP Commands:\n");
2981 
2982         FTPCmd = ftp_cmd_lookup_first(ServerConf->cmd_lookup, &iRet);
2983         while (FTPCmd != NULL)
2984         {
2985             memset(buf, 0, BUF_SIZE+1);
2986             snprintf(buf, BUF_SIZE, "          %s { %d ",
2987                     FTPCmd->cmd_name, FTPCmd->max_param_len);
2988 #ifdef PRINT_DEFAULT_CONFIGS
2989             if (FTPCmd->data_chan_cmd)
2990                 snprintf(buf, BUF_SIZE, "%s data_chan ");
2991             if (FTPCmd->data_xfer_cmd)
2992                 snprintf(buf, BUF_SIZE, "%s data_xfer ");
2993             if (FTPCmd->encr_cmd)
2994                 snprintf(buf, BUF_SIZE, "%s encr ");
2995 #endif
2996 
2997             if (FTPCmd->check_validity)
2998             {
2999                 FTP_PARAM_FMT *CmdFmt = FTPCmd->param_format;
3000                 while (CmdFmt != NULL)
3001                 {
3002                     PrintCmdFmt(buf, CmdFmt);
3003 
3004                     CmdFmt = CmdFmt->next_param_fmt;
3005                 }
3006             }
3007             _dpd.logMsg("%s}\n", buf);
3008             FTPCmd = ftp_cmd_lookup_next(ServerConf->cmd_lookup, &iRet);
3009         }
3010     }
3011 
3012     return FTPP_SUCCESS;
3013 }
3014 
3015 /*
3016  * Function: ProcessFTPServerOptions(FTP_SERVER_PROTO_CONF *ServerConf,
3017  *                          char *ErrorString, int ErrStrLen)
3018  *
3019  * Purpose: This is where we process the specific ftp server configuration
3020  *          for FTPTelnet.
3021  *
3022  *          We set the values of the ftp server configuraiton here.  Any errors
3023  *          that are encountered are specified in the error string and the type
3024  *          of error is returned through the return code, i.e. fatal, non-fatal.
3025  *
3026  * Arguments: ServerConf    => pointer to the server configuration
3027  *            ErrorString   => error string buffer
3028  *            ErrStrLen     => the length of the error string buffer
3029  *
3030  * Returns: int     => an error code integer (0 = success,
3031  *                     >0 = non-fatal error, <0 = fatal error)
3032  *
3033  */
ProcessFTPServerOptions(FTP_SERVER_PROTO_CONF * ServerConf,char * ErrorString,int ErrStrLen)3034 int ProcessFTPServerOptions(FTP_SERVER_PROTO_CONF *ServerConf,
3035         char *ErrorString, int ErrStrLen)
3036 {
3037     FTPTELNET_CONF_OPT *ConfOpt;
3038     int  iRet = 0;
3039     char *pcToken;
3040     int  iTokens = 0;
3041     int  data_chan_configured = 0;
3042 
3043     while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL)
3044     {
3045         /*
3046          * Show that we at least got one token
3047          */
3048         iTokens = 1;
3049 
3050         /*
3051          * Search for configuration keywords
3052          */
3053         if(!strcmp(PORTS, pcToken))
3054         {
3055             PROTO_CONF *ports = (PROTO_CONF*)&ServerConf->proto_ports;
3056             iRet = ProcessPorts(ports, ErrorString, ErrStrLen);
3057             if (iRet)
3058             {
3059                 return iRet;
3060             }
3061         }
3062         else if(!strcmp(FTP_CMDS, pcToken))
3063         {
3064             iRet = ProcessFTPCmdList(ServerConf, FTP_CMDS, ErrorString, ErrStrLen, 1, 0);
3065             if (iRet)
3066             {
3067                 return iRet;
3068             }
3069         }
3070         else if(!strcmp(MAX_PARAM_LEN, pcToken))
3071         {
3072             iRet = ProcessFTPCmdList(ServerConf, MAX_PARAM_LEN, ErrorString, ErrStrLen, 0, 1);
3073             if (iRet)
3074             {
3075                 return iRet;
3076             }
3077         }
3078         else if(!strcmp(ALT_PARAM_LEN, pcToken))
3079         {
3080             iRet = ProcessFTPCmdList(ServerConf, ALT_PARAM_LEN, ErrorString, ErrStrLen, 1, 1);
3081             if (iRet)
3082             {
3083                 return iRet;
3084             }
3085         }
3086         else if(!strcmp(CMD_VALIDITY, pcToken))
3087         {
3088             iRet = ProcessFTPCmdValidity(ServerConf, ErrorString, ErrStrLen);
3089             if (iRet)
3090             {
3091                 return iRet;
3092             }
3093         }
3094         else if(!strcmp(STRING_FORMAT, pcToken))
3095         {
3096             iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen);
3097             if (iRet)
3098             {
3099                 return iRet;
3100             }
3101         }
3102         else if (!strcmp(DATA_CHAN_CMD, pcToken))
3103         {
3104             iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen);
3105             if (iRet)
3106             {
3107                 return iRet;
3108             }
3109         }
3110         else if (!strcmp(DATA_XFER_CMD, pcToken))
3111         {
3112             iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen);
3113             if (iRet)
3114             {
3115                 return iRet;
3116             }
3117         }
3118         else if (!strcmp(DATA_REST_CMD, pcToken))
3119         {
3120            iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen);
3121            if (iRet)
3122            {
3123                return iRet;
3124            }
3125         }
3126         else if (!strcmp(FILE_PUT_CMD, pcToken))
3127         {
3128             iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen);
3129             if (iRet)
3130             {
3131                 return iRet;
3132             }
3133         }
3134         else if (!strcmp(FILE_GET_CMD, pcToken))
3135         {
3136             iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen);
3137             if (iRet)
3138             {
3139                 return iRet;
3140             }
3141         }
3142         else if (!strcmp(ENCR_CMD, pcToken))
3143         {
3144             iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen);
3145             if (iRet)
3146             {
3147                 return iRet;
3148             }
3149         }
3150         else if (!strcmp(LOGIN_CMD, pcToken))
3151         {
3152             iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen);
3153             if (iRet)
3154             {
3155                 return iRet;
3156             }
3157         }
3158         else if (!strcmp(DIR_CMD, pcToken))
3159         {
3160             iRet = ProcessFTPDirCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen);
3161             if (iRet)
3162             {
3163                 return iRet;
3164             }
3165         }
3166         else if (!strcmp(DATA_CHAN, pcToken))
3167         {
3168             if (data_chan_configured && ServerConf->data_chan == 0)
3169             {
3170                 snprintf(ErrorString, ErrStrLen, "Both 'data_chan' and "
3171                         "'ignore_data_chan' configured with conflicting options.");
3172                 return FTPP_FATAL_ERR;
3173             }
3174             else
3175             {
3176                 ServerConf->data_chan = 1;
3177                 data_chan_configured = 1;
3178             }
3179         }
3180         else if (!strcmp(PRINT_CMDS, pcToken))
3181         {
3182             ServerConf->print_commands = 1;
3183         }
3184         else if (!strcmp(IGNORE_DATA_CHAN, pcToken))
3185         {
3186             iRet = ProcessFTPIgnoreDataChan(ServerConf, pcToken, ErrorString, ErrStrLen);
3187             if (iRet)
3188             {
3189                 return iRet;
3190             }
3191             data_chan_configured = 1;
3192         }
3193 
3194         /*
3195          * Start the CONF_OPT configurations.
3196          */
3197         else if(!strcmp(TELNET_CMDS, pcToken))
3198         {
3199             ConfOpt = &ServerConf->telnet_cmds;
3200             iRet = ProcessConfOpt(ConfOpt, TELNET_CMDS, ErrorString, ErrStrLen);
3201             if (iRet)
3202             {
3203                 return iRet;
3204             }
3205         }
3206         else if(!strcmp(IGNORE_TELNET_CMDS, pcToken))
3207         {
3208             ConfOpt = &ServerConf->ignore_telnet_erase_cmds;
3209             iRet = ProcessConfOpt(ConfOpt, IGNORE_TELNET_CMDS, ErrorString, ErrStrLen);
3210             if (iRet)
3211             {
3212                 return iRet;
3213             }
3214         }
3215         else
3216         {
3217             snprintf(ErrorString, ErrStrLen,
3218                     "Invalid keyword '%s' for '%s' configuration.",
3219                     pcToken, GLOBAL);
3220 
3221             return FTPP_FATAL_ERR;
3222         }
3223     }
3224 
3225     /*
3226      * If there are not any tokens to the configuration, then
3227      * we let the user know and log the error.  return non-fatal
3228      * error.
3229      */
3230     if(!iTokens)
3231     {
3232         snprintf(ErrorString, ErrStrLen,
3233                 "No tokens to '%s %s' configuration.", FTP, SERVER);
3234 
3235         return FTPP_NONFATAL_ERR;
3236     }
3237 
3238     return FTPP_SUCCESS;
3239 }
3240 
parseFtpServerConfigStr(FTP_SERVER_PROTO_CONF * ftp_conf,char * ConfigParseResumePtr,char ip_list,char * ErrorString,int ErrStrLen)3241 int parseFtpServerConfigStr( FTP_SERVER_PROTO_CONF *ftp_conf, char *ConfigParseResumePtr,
3242                              char ip_list, char *ErrorString, int ErrStrLen )
3243 {
3244     int iRet = 0;
3245     char *saveMaxToken = maxToken;
3246     size_t default_conf_len;
3247     char *default_conf_str = DefaultConf(&default_conf_len);
3248 
3249     /* First, process the default configuration -- namely, the
3250      * list of FTP commands, and the parameter validation checks  */
3251     maxToken = default_conf_str + default_conf_len;
3252     mystrtok(default_conf_str, CONF_SEPARATORS);
3253 
3254     iRet = ProcessFTPServerOptions(ftp_conf, ErrorString, ErrStrLen);
3255 
3256     _dpd.snortFree(default_conf_str, default_conf_len,
3257                    PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
3258     maxToken = saveMaxToken;
3259 
3260     if (iRet < 0)
3261         return iRet;
3262 
3263     /* Okay, now we need to reset the mystrtok pointers so we can process
3264      * the specific server configuration.  Quick hack/trick here: reset
3265      * the end of the client string to a conf separator, then call mystrtok.
3266      * That will reset mystrtok's internal pointer to the next token after
3267      * the client name, which is what we're expecting it to be.
3268      */
3269     if (ConfigParseResumePtr < maxToken)
3270     {
3271         /* only if there is data after the server/client name */
3272         if (ip_list)
3273             *ConfigParseResumePtr-- = END_IPADDR_LIST[0];
3274         else
3275             *ConfigParseResumePtr-- = CONF_SEPARATORS[0];
3276 
3277         mystrtok(ConfigParseResumePtr, CONF_SEPARATORS);
3278         iRet = ProcessFTPServerOptions(ftp_conf, ErrorString, ErrStrLen);
3279         if (iRet < 0)
3280             return iRet;
3281     }
3282 
3283     return iRet;
3284 }
3285 
enableFtpTelnetPortStreamServices(struct _SnortConfig * sc,PROTO_CONF * pc,char * network,int direction)3286 void enableFtpTelnetPortStreamServices( struct _SnortConfig *sc, PROTO_CONF *pc, char *network, int direction )
3287 {
3288     uint32_t port;
3289 
3290     for ( port = 0; port < MAXPORTS; port++ )
3291     {
3292         if( pc->ports[ port ] )
3293         {
3294             _dpd.streamAPI->register_reassembly_port( network, port, direction );
3295             _dpd.sessionAPI->enable_preproc_for_port( sc, PP_FTPTELNET, PROTO_BIT__TCP, port );
3296         }
3297     }
3298 }
3299 
3300 /*
3301  * Function: ProcessFTPServerConf::
3302  *
3303  * Purpose: This is where we process the ftp server configuration for FTPTelnet.
3304  *
3305  *          We set the values of the ftp server configuraiton here.  Any
3306  *          errors that are encountered are specified in the error string and
3307  *          the type of error is returned through the return code, i.e. fatal,
3308  *          non-fatal.
3309  *
3310  *          The configuration options that are dealt with here are:
3311  *          ports { x }             Ports on which to do FTP checks
3312  *          ftp_cmds { CMD1 CMD2 ... }  Valid FTP commands
3313  *          def_max_param_len x     Default max param length
3314  *          alt_max_param_len x { CMD1 ... }  Override default max param len
3315  *                                  for CMD
3316  *          chk_str_fmt { CMD1 ...}  Detect string format attacks for CMD
3317  *          cmd_validity CMD < fmt > Check the parameter validity for CMD
3318  *          fmt is as follows:
3319  *              int                 Param is an int
3320  *              char _chars         Param is one of _chars
3321  *              date _datefmt       Param follows format specified where
3322  *                                   # = Number, C=Char, []=optional, |=OR,
3323  *                                   +-.=literal
3324  *              []                  Optional parameters
3325  *              string              Param is string (unrestricted)
3326  *          data_chan               Ignore data channel
3327  *
3328  * Arguments: GlobalConf    => pointer to the global configuration
3329  *            ErrorString   => error string buffer
3330  *            ErrStrLen     => the length of the error string buffer
3331  *
3332  * Returns: int     => an error code integer (0 = success,
3333  *                     >0 = non-fatal error, <0 = fatal error)
3334  *
3335  */
ProcessFTPServerConf(struct _SnortConfig * sc,FTPTELNET_GLOBAL_CONF * GlobalConf,char * ErrorString,int ErrStrLen)3336 int ProcessFTPServerConf( struct _SnortConfig *sc, FTPTELNET_GLOBAL_CONF *GlobalConf,
3337                           char *ErrorString, int ErrStrLen )
3338 {
3339     int  iRet = 0;
3340     int  retVal = 0;
3341     char *server;
3342     char server_list[STD_BUF];
3343     sfcidr_t ipAddr;
3344     char *pIpAddressList = NULL;
3345     char *pIpAddressList2 = NULL;
3346     char *brkt = NULL;
3347     char firstIpAddress = 1;
3348     FTP_SERVER_PROTO_CONF *new_server_conf = NULL;
3349     char *ConfigParseResumePtr = NULL;
3350     char ip_list = 0;
3351     FTP_SERVER_PROTO_CONF *ftp_conf = NULL;
3352 
3353     /*
3354      * If not default, create one for this IP
3355      */
3356     server = NextToken(CONF_SEPARATORS);
3357 
3358     if ( !server )
3359     {
3360         DynamicPreprocessorFatalMessage(
3361                 "%s(%d) Missing ftp_telnet ftp server address.\n",
3362                 *(_dpd.config_file), *(_dpd.config_line));
3363     }
3364     else if(strcmp(DEFAULT, server))
3365     {
3366         if(strcmp(START_IPADDR_LIST, server) == 0)
3367         {
3368             //list begin token matched
3369             ip_list = 1;
3370             if ((pIpAddressList = mystrtok(NULL, END_IPADDR_LIST)) == NULL)
3371             {
3372                 snprintf(ErrorString, ErrStrLen,
3373                         "Invalid IP Address list in '%s' token.", SERVER);
3374 
3375                 retVal = FTPP_INVALID_ARG;
3376                 goto _return;
3377             }
3378         }
3379         else
3380         {
3381             //list begin didn't match so this must be an IP address
3382             pIpAddressList = server;
3383         }
3384 
3385         ConfigParseResumePtr = pIpAddressList+strlen(pIpAddressList);
3386 
3387         pIpAddressList2 = strdup(pIpAddressList);
3388         if (!pIpAddressList2)
3389         {
3390             snprintf(ErrorString, ErrStrLen,
3391                     "Could not allocate memory for server configuration.");
3392 
3393             retVal = FTPP_INVALID_ARG;
3394             goto _return;
3395         }
3396 
3397         for (server = strtok_r(pIpAddressList2, CONF_SEPARATORS, &brkt);
3398                 server;
3399                 server = strtok_r(NULL, CONF_SEPARATORS, &brkt))
3400         {
3401             if (sfip_pton(server, &ipAddr) != SFIP_SUCCESS)
3402             {
3403                 snprintf(ErrorString, ErrStrLen,
3404                         "Invalid IP to '%s' token.", SERVER);
3405 
3406                 retVal = FTPP_INVALID_ARG;
3407                 goto _return;
3408             }
3409 
3410             if (firstIpAddress)
3411             {
3412                 /* Write this IP into the buffer for printing */
3413                 snprintf(server_list, STD_BUF, "%s", server);
3414 
3415                 new_server_conf = (FTP_SERVER_PROTO_CONF *)_dpd.snortAlloc(1,
3416                                                  sizeof(FTP_SERVER_PROTO_CONF),
3417                                          PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
3418                 if (new_server_conf == NULL)
3419                 {
3420                     DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n",
3421                             *(_dpd.config_file), *(_dpd.config_line));
3422                 }
3423 
3424                 ftpp_ui_config_reset_ftp_server(new_server_conf, 1);
3425 
3426                 new_server_conf->serverAddr = strdup(server);
3427                 if (new_server_conf->serverAddr == NULL)
3428                 {
3429                     DynamicPreprocessorFatalMessage("ProcessFTPServerConf(): Out of memory allocing serverAddr.\n");
3430                 }
3431 
3432                 ftp_conf = new_server_conf;
3433                 iRet = parseFtpServerConfigStr( ftp_conf, ConfigParseResumePtr, ip_list, ErrorString, ErrStrLen );
3434                 if (iRet)
3435                 {
3436                     retVal = iRet;
3437                     goto _return;
3438                 }
3439 
3440                 //process the first IP address as usual
3441                 firstIpAddress = 0;
3442             }
3443             else
3444             {
3445                 /* Write this IP into the buffer for printing */
3446                 snprintf(server_list + strlen(server_list), STD_BUF - strlen(server_list) , ", %s", server);
3447 
3448                 new_server_conf = ftp_conf;
3449             }
3450 
3451             ftpp_ui_config_add_ftp_server(GlobalConf, &ipAddr, new_server_conf);
3452             enableFtpTelnetPortStreamServices( sc, &ftp_conf->proto_ports, server,
3453                                                SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT );
3454 
3455             //create a reference
3456             new_server_conf->referenceCount++;
3457         }
3458 
3459         if (firstIpAddress)
3460         {
3461             //no IP address was found
3462             snprintf(ErrorString, ErrStrLen,
3463                     "Invalid IP Address list in '%s' token.", CLIENT);
3464 
3465             retVal = FTPP_INVALID_ARG;
3466             goto _return;
3467         }
3468     }
3469     else
3470     {
3471         if (GlobalConf->default_ftp_server != NULL)
3472         {
3473             snprintf(ErrorString, ErrStrLen,
3474                     "Cannot configure '%s' settings more than once.", SERVER);
3475 
3476             retVal = FTPP_INVALID_ARG;
3477             goto _return;
3478         }
3479 
3480         GlobalConf->default_ftp_server =
3481             (FTP_SERVER_PROTO_CONF *)_dpd.snortAlloc(1,
3482                                        sizeof(FTP_SERVER_PROTO_CONF),
3483                                        PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
3484         if (GlobalConf->default_ftp_server == NULL)
3485         {
3486             DynamicPreprocessorFatalMessage("Out of memory trying to create "
3487                     "default ftp server configuration.\n");
3488         }
3489 
3490         ftpp_ui_config_reset_ftp_server(GlobalConf->default_ftp_server, 0);
3491         ftp_conf = GlobalConf->default_ftp_server;
3492         ConfigParseResumePtr = server+strlen(server);
3493         GlobalConf->default_ftp_server->serverAddr = strdup("default");
3494         if (GlobalConf->default_ftp_server->serverAddr == NULL)
3495         {
3496             _dpd.snortFree(GlobalConf->default_ftp_server,
3497                            sizeof(FTP_SERVER_PROTO_CONF),
3498                            PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
3499             DynamicPreprocessorFatalMessage("Out of memory trying to create "
3500                     "default ftp server configuration.\n");
3501         }
3502         iRet = parseFtpServerConfigStr( ftp_conf, ConfigParseResumePtr, ip_list, ErrorString, ErrStrLen );
3503         if (iRet)
3504         {
3505             retVal = iRet;
3506             goto _return;
3507         }
3508 
3509         enableFtpTelnetPortStreamServices( sc, &ftp_conf->proto_ports, NULL,
3510                                            SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT );
3511     }
3512 
3513     /*
3514      * Let's print out the FTP config
3515      */
3516     if (ip_list)
3517     {
3518         server = &server_list[0];
3519     }
3520     else if (pIpAddressList2)
3521     {
3522         server = pIpAddressList2;
3523     }
3524     PrintFTPServerConf(server, ftp_conf);
3525 
3526 _return:
3527     if (pIpAddressList2)
3528     {
3529         free(pIpAddressList2);
3530     }
3531     return retVal;
3532 }
3533 
3534 /*
3535  * Function: PrintFTPGlobalConf(FTPTELNET_GLOBAL_CONF *GlobalConf)
3536  *
3537  * Purpose: Prints the FTPTelnet preprocessor global configuration
3538  *
3539  * Arguments: GlobalConf    => pointer to the global configuration
3540  *
3541  * Returns: int     => an error code integer (0 = success,
3542  *                     >0 = non-fatal error, <0 = fatal error)
3543  *
3544  */
PrintFTPGlobalConf(FTPTELNET_GLOBAL_CONF * GlobalConf)3545 int PrintFTPGlobalConf(FTPTELNET_GLOBAL_CONF *GlobalConf)
3546 {
3547     _dpd.logMsg("FTPTelnet Config:\n");
3548 
3549     _dpd.logMsg("    GLOBAL CONFIG\n");
3550     _dpd.logMsg("      Inspection Type: %s\n",
3551             GlobalConf->inspection_type == FTPP_UI_CONFIG_STATELESS ?
3552             "stateless" : "stateful");
3553     PrintConfOpt(&GlobalConf->encrypted, "Check for Encrypted Traffic");
3554     _dpd.logMsg("      Continue to check encrypted data: %s\n",
3555             GlobalConf->check_encrypted_data ? "YES" : "NO");
3556 
3557     return FTPP_SUCCESS;
3558 }
3559 
FTPTelnetCleanupFTPCMDConf(void * ftpCmd)3560 void FTPTelnetCleanupFTPCMDConf(void *ftpCmd)
3561 {
3562     FTP_CMD_CONF *FTPCmd = (FTP_CMD_CONF *)ftpCmd;
3563     /* Free the FTP_PARAM_FMT stuff... */
3564     ftpp_ui_config_reset_ftp_cmd(FTPCmd);
3565 
3566     _dpd.snortFree(FTPCmd, sizeof(FTP_CMD_CONF)+strlen(FTPCmd->cmd_name),
3567                           PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
3568 }
3569 
FTPTelnetCleanupFTPServerConf(void * serverConf)3570 void FTPTelnetCleanupFTPServerConf(void *serverConf)
3571 {
3572     FTP_SERVER_PROTO_CONF *ServerConf = (FTP_SERVER_PROTO_CONF*)serverConf;
3573     if (ServerConf == NULL)
3574         return;
3575 
3576     free(ServerConf->serverAddr);
3577     ServerConf->serverAddr = NULL;
3578 
3579     /* Iterate through each cmd_lookup for this server */
3580     ftp_cmd_lookup_cleanup(&ServerConf->cmd_lookup);
3581 }
3582 
FTPTelnetCleanupFTPBounceTo(void * ftpBounce)3583 void FTPTelnetCleanupFTPBounceTo(void *ftpBounce)
3584 {
3585     FTP_BOUNCE_TO *FTPBounce = (FTP_BOUNCE_TO *)ftpBounce;
3586     _dpd.snortFree(FTPBounce, sizeof(FTP_BOUNCE_TO),
3587                    PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
3588 }
3589 
FTPTelnetCleanupFTPClientConf(void * clientConf)3590 void FTPTelnetCleanupFTPClientConf(void *clientConf)
3591 {
3592     FTP_CLIENT_PROTO_CONF *ClientConf = (FTP_CLIENT_PROTO_CONF*)clientConf;
3593     if (ClientConf == NULL)
3594         return;
3595 
3596     /* Iterate through each bounce_lookup for this client */
3597     ftp_bounce_lookup_cleanup(&ClientConf->bounce_lookup);
3598 }
3599 
FTPTelnetFreeConfigsPolicy(tSfPolicyUserContextId config,tSfPolicyId policyId,void * pData)3600 static int FTPTelnetFreeConfigsPolicy(
3601         tSfPolicyUserContextId config,
3602         tSfPolicyId policyId,
3603         void* pData
3604         )
3605 {
3606     FTPTELNET_GLOBAL_CONF *pPolicyConfig = (FTPTELNET_GLOBAL_CONF *)pData;
3607 
3608     //do any housekeeping before freeing FTPTELNET_GLOBAL_CONF
3609     sfPolicyUserDataClear (config, policyId);
3610     FTPTelnetFreeConfig(pPolicyConfig);
3611 
3612     return 0;
3613 }
3614 
FTPTelnetFreeConfigs(tSfPolicyUserContextId GlobalConf)3615 void FTPTelnetFreeConfigs(tSfPolicyUserContextId GlobalConf)
3616 {
3617     if (GlobalConf == NULL)
3618         return;
3619 
3620     sfPolicyUserDataFreeIterate(GlobalConf, FTPTelnetFreeConfigsPolicy);
3621 
3622     sfPolicyConfigDelete(GlobalConf);
3623 }
3624 
FTPTelnetFreeConfig(FTPTELNET_GLOBAL_CONF * GlobalConf)3625 void FTPTelnetFreeConfig(FTPTELNET_GLOBAL_CONF *GlobalConf)
3626 {
3627     if (GlobalConf == NULL)
3628         return;
3629 
3630     if (GlobalConf->default_ftp_client != NULL)
3631     {
3632         FTPTelnetCleanupFTPClientConf((void *)GlobalConf->default_ftp_client);
3633         _dpd.snortFree(GlobalConf->default_ftp_client,
3634                        sizeof(FTP_CLIENT_PROTO_CONF),
3635                        PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
3636     }
3637 
3638     if (GlobalConf->default_ftp_server != NULL)
3639     {
3640         FTPTelnetCleanupFTPServerConf((void *)GlobalConf->default_ftp_server);
3641         _dpd.snortFree(GlobalConf->default_ftp_server,
3642                        sizeof(FTP_SERVER_PROTO_CONF),
3643                        PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
3644     }
3645 
3646     if (GlobalConf->telnet_config != NULL)
3647         _dpd.snortFree(GlobalConf->telnet_config, sizeof(TELNET_PROTO_CONF),
3648                           PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG);
3649 
3650     ftpp_ui_client_lookup_cleanup(&GlobalConf->client_lookup);
3651     ftpp_ui_server_lookup_cleanup(&GlobalConf->server_lookup);
3652 
3653     _dpd.snortFree(GlobalConf, sizeof(FTPTELNET_GLOBAL_CONF), PP_FTPTELNET,
3654                                                        PP_MEM_CATEGORY_CONFIG);
3655 }
3656 
3657 /*
3658  * Function: FTPTelnetCheckFTPCmdOptions(FTP_SERVER_PROTO_CONF *serverConf)
3659  *
3660  * Purpose: This checks that the FTP configuration provided has
3661  *          options for CMDs that make sense:
3662  *          -- check if max_len == 0 & there is a cmd_validity
3663  *
3664  * Arguments: serverConf    => pointer to Server Configuration
3665  *
3666  * Returns: 0               => no errors
3667  *          1               => errors
3668  *
3669  */
FTPTelnetCheckFTPCmdOptions(FTP_SERVER_PROTO_CONF * serverConf)3670 int FTPTelnetCheckFTPCmdOptions(FTP_SERVER_PROTO_CONF *serverConf)
3671 {
3672     FTP_CMD_CONF *cmdConf;
3673     int iRet =0;
3674     int config_error = 0;
3675 
3676     cmdConf = ftp_cmd_lookup_first(serverConf->cmd_lookup, &iRet);
3677     while (cmdConf && (iRet == FTPP_SUCCESS))
3678     {
3679         size_t len = strlen(cmdConf->cmd_name);
3680         if ( len > serverConf->max_cmd_len ) serverConf->max_cmd_len = len;
3681 
3682         if (cmdConf->check_validity && (cmdConf->max_param_len == 0))
3683         {
3684             _dpd.errMsg("FTPConfigCheck() configuration for server '%s', "
3685                     "command '%s' has max length of 0 and parameters to validate\n",
3686                     serverConf->serverAddr, cmdConf->cmd_name);
3687             config_error = 1;
3688         }
3689         cmdConf = ftp_cmd_lookup_next(serverConf->cmd_lookup, &iRet);
3690     }
3691 
3692     return config_error;
3693 }
3694 
3695 /*
3696  * Function: FTPTelnetCheckFTPServerConfigs(void)
3697  *
3698  * Purpose: This checks that the FTP server configurations are reasonable
3699  *
3700  * Arguments: None
3701  *
3702  * Returns: -1 on error
3703  *
3704  */
FTPTelnetCheckFTPServerConfigs(struct _SnortConfig * sc,FTPTELNET_GLOBAL_CONF * config)3705 int FTPTelnetCheckFTPServerConfigs(struct _SnortConfig *sc, FTPTELNET_GLOBAL_CONF *config)
3706 {
3707     FTP_SERVER_PROTO_CONF *serverConf;
3708     int iRet = 0;
3709     int rval;
3710 
3711     if (config == NULL)
3712         return 0;
3713 
3714     if ((rval = ftpp_ui_server_iterate(sc, config->server_lookup, _checkServerConfig, &iRet)))
3715         return rval;
3716 
3717     serverConf = config->default_ftp_server;
3718     if (FTPTelnetCheckFTPCmdOptions(serverConf))
3719     {
3720         _dpd.errMsg("FTPConfigCheck(): invalid configuration for FTP commands\n");
3721         return -1;
3722     }
3723     return 0;
3724 }
3725 
_checkServerConfig(struct _SnortConfig * sc,void * pData)3726 static int _checkServerConfig(struct _SnortConfig *sc, void *pData)
3727 {
3728     FTP_SERVER_PROTO_CONF *serverConf = (FTP_SERVER_PROTO_CONF *)pData;
3729 
3730     if (FTPTelnetCheckFTPCmdOptions(serverConf))
3731     {
3732         _dpd.errMsg("FTPConfigCheck(): invalid configuration for FTP commands\n");
3733         return -1;
3734     }
3735     return 0;
3736 }
3737 
3738 /*
3739  * Function: FTPConfigCheck(void)
3740  *
3741  * Purpose: This checks that the FTP configuration provided includes
3742  *          the default configurations for Server & Client.
3743  *
3744  * Arguments: None
3745  *
3746  * Returns: None
3747  *
3748  */
FTPTelnetCheckConfigs(struct _SnortConfig * sc,void * pData,tSfPolicyId policyId)3749 int FTPTelnetCheckConfigs(struct _SnortConfig *sc, void* pData, tSfPolicyId policyId)
3750 {
3751     int rval;
3752     FTPTELNET_GLOBAL_CONF *pPolicyConfig = (FTPTELNET_GLOBAL_CONF *)pData;
3753 
3754     if ( pPolicyConfig == NULL )
3755         return 0;
3756 
3757     if ((pPolicyConfig->default_ftp_server == NULL) ||
3758             (pPolicyConfig->default_ftp_client == NULL))
3759     {
3760         _dpd.errMsg("FTP/Telnet configuration requires "
3761                 "default client and default server configurations.\n");
3762         return -1;
3763     }
3764     if ( pPolicyConfig->telnet_config == NULL )
3765     {
3766         ProcessTelnetConf(pPolicyConfig,"",0);
3767     }
3768 
3769     if ((pPolicyConfig->telnet_config->ayt_threshold > 0) &&
3770             !pPolicyConfig->telnet_config->normalize)
3771     {
3772         _dpd.errMsg("WARNING: Telnet Configuration Check: using an "
3773                 "AreYouThere threshold requires telnet normalization to be "
3774                 "turned on.\n");
3775     }
3776     if ((pPolicyConfig->encrypted.alert != 0) &&
3777             !pPolicyConfig->telnet_config->normalize)
3778     {
3779         _dpd.errMsg("WARNING: Telnet Configuration Check: checking for "
3780                 "encrypted traffic requires telnet normalization to be turned "
3781                 "on.\n");
3782     }
3783     /* So we don't have to check it every time we use it */
3784     if ((!_dpd.streamAPI) || (_dpd.streamAPI->version < STREAM_API_VERSION5))
3785     {
3786         _dpd.errMsg("FTPConfigCheck() Streaming & reassembly must be "
3787                 "enabled\n");
3788         return -1;
3789     }
3790 
3791     _dpd.setParserPolicy(sc, policyId);
3792 
3793     /* Add FTPTelnet into the preprocessor list */
3794 #ifdef TARGET_BASED
3795     if ( _dpd.fileAPI->get_max_file_depth(sc, true) >= 0 )
3796     {
3797         _dpd.addPreproc(sc, FTPDataTelnetChecks, PRIORITY_APPLICATION, PP_FTPTELNET, PROTO_BIT__TCP);
3798         s_ftpdata_eof_cb_id = _dpd.streamAPI->register_event_handler(SnortFTPData_EOF);
3799         s_ftpdata_flush_cb_id = _dpd.streamAPI->register_ftp_flush_cb(SnortFTPData_Flush);
3800     }
3801     else
3802 #endif
3803     {
3804         _dpd.addPreproc(sc, FTPTelnetChecks, PRIORITY_APPLICATION, PP_FTPTELNET, PROTO_BIT__TCP);
3805     }
3806 
3807     if ((rval = FTPTelnetCheckFTPServerConfigs(sc, pPolicyConfig)))
3808         return rval;
3809 
3810     _FTPTelnetAddPortsOfInterest(sc, pPolicyConfig, policyId);
3811 #ifdef TARGET_BASED
3812     _FTPTelnetAddService(sc, ftp_app_id, policyId);
3813 #endif
3814 
3815     return 0;
3816 
3817 }
3818 
FTPConfigCheckPolicy(struct _SnortConfig * sc,tSfPolicyUserContextId config,tSfPolicyId policyId,void * pData)3819 static int FTPConfigCheckPolicy(
3820         struct _SnortConfig *sc,
3821         tSfPolicyUserContextId config,
3822         tSfPolicyId policyId,
3823         void* pData
3824         )
3825 {
3826     return FTPTelnetCheckConfigs(sc, pData, policyId);
3827 }
3828 
FTPConfigCheck(struct _SnortConfig * sc)3829 int FTPConfigCheck(struct _SnortConfig *sc)
3830 {
3831     int rval;
3832 
3833     if (ftp_telnet_config == NULL)
3834         return 0;
3835 
3836     if ((rval = sfPolicyUserDataIterate (sc, ftp_telnet_config, FTPConfigCheckPolicy)))
3837         return rval;
3838 
3839     return 0;
3840 }
3841 
3842 /*
3843  * Function: LogFTPPEvents(FTPP_GEN_EVENTS *GenEvents,
3844  *                         int iGenerator)
3845  *
3846  * Purpose: This is the routine that logs FTP/Telnet Preprocessor (FTPP)
3847  *          alerts through Snort.
3848  *
3849  *          Every Session gets looked at for any logged events, and if
3850  *          there are events to be logged then we select the one with the
3851  *          highest priority.
3852  *
3853  *          We use a generic event structure that we set for each different
3854  *          event structure.  This way we can use the same code for event
3855  *          logging regardless of what type of event strucure we are dealing
3856  *          with.
3857  *
3858  *          The important things to know about this function is how to work
3859  *          with the event queue.  The number of unique events is contained
3860  *          in the stack_count variable.  So we loop through all the unique
3861  *          events and find which one has the highest priority.  During this
3862  *          loop, we also re-initialize the individual event counts for the
3863  *          next iteration, saving us time in a separate initialization phase.
3864  *
3865  *          After we've iterated through all the events and found the one
3866  *          with the highest priority, we then log that event through snort.
3867  *
3868  *          We've mapped the FTPTelnet and the Snort alert IDs together, so
3869  *          we can access them directly instead of having a more complex
3870  *          mapping function.
3871  *
3872  * Arguments: GenEvents     => pointer a list of events
3873  *            iGenerator    => Generator ID (Telnet or FTP)
3874  *
3875  * Returns: int     => an error code integer (0 = success,
3876  *                     >0 = non-fatal error, <0 = fatal error)
3877  *
3878  */
LogFTPPEvents(FTPP_GEN_EVENTS * GenEvents,int iGenerator)3879 static inline int LogFTPPEvents(FTPP_GEN_EVENTS *GenEvents,
3880         int iGenerator)
3881 {
3882     FTPP_EVENT      *OrigEvent;
3883     FTPP_EVENT      *HiEvent = NULL;
3884     int           iStackCnt;
3885     int           iEvent;
3886     int           iCtr;
3887 
3888     /*
3889      * Now starts the generic event processing
3890      */
3891     iStackCnt = GenEvents->stack_count;
3892 
3893     /*
3894      * IMPORTANT::
3895      * We have to check the stack count of the event queue before we process
3896      * an log.
3897      */
3898     if(iStackCnt == 0)
3899     {
3900         return FTPP_SUCCESS;
3901     }
3902 
3903     /*
3904      * Cycle through the events and select the event with the highest
3905      * priority.
3906      */
3907     for(iCtr = 0; iCtr < iStackCnt; iCtr++)
3908     {
3909         iEvent = GenEvents->stack[iCtr];
3910         OrigEvent = &(GenEvents->events[iEvent]);
3911 
3912         /*
3913          * Set the event to start off the comparison
3914          */
3915         if(!HiEvent)
3916         {
3917             HiEvent = OrigEvent;
3918         }
3919 
3920         /*
3921          * This is our "comparison function".  Log the event with the highest
3922          * priority.
3923          */
3924         if(OrigEvent->event_info->priority < HiEvent->event_info->priority)
3925         {
3926             HiEvent = OrigEvent;
3927         }
3928 
3929         /*
3930          * IMPORTANT:
3931          *   This is how we reset the events in the event queue.
3932          *   If you miss this step, you can be really screwed.
3933          */
3934         OrigEvent->count = 0;
3935     }
3936 
3937     if (!HiEvent)
3938         return FTPP_SUCCESS;
3939 
3940     /*
3941      * We use the iEvent+1 because the event IDs between snort and
3942      * FTPTelnet are mapped off-by-one.  They're mapped off-by one
3943      * because in the internal FTPTelnet queue, events are mapped
3944      * starting at 0.  For some reason, it appears that the first
3945      * event can't be zero, so we use the internal value and add
3946      * one for snort.
3947      */
3948     iEvent = HiEvent->event_info->alert_id + 1;
3949 
3950     /* GenID, SID, Rev, Classification, Pri, Msg, RuleInfo  */
3951     _dpd.alertAdd(iGenerator,
3952             HiEvent->event_info->alert_sid, 1, /* Revision 1  */
3953             HiEvent->event_info->classification,
3954             HiEvent->event_info->priority,
3955             HiEvent->event_info->alert_str, NULL); /* No Rule info  */
3956 
3957     /*
3958      * Reset the event queue stack counter, in the case of pipelined
3959      * requests.
3960      */
3961     GenEvents->stack_count = 0;
3962 
3963     return FTPP_SUCCESS;
3964 }
3965 
3966 /*
3967  * Function: LogFTPEvents(FTP_SESSION *FtpSession)
3968  *
3969  * Purpose: This is the routine that logs FTP alerts through Snort.
3970  *          It maps the event into a generic event and calls
3971  *          LOGFTPPEvents().
3972  *
3973  * Arguments: FtpSession    => pointer the session structure
3974  *
3975  * Returns: int     => an error code integer (0 = success,
3976  *                     >0 = non-fatal error, <0 = fatal error)
3977  *
3978  */
LogFTPEvents(FTP_SESSION * FtpSession)3979 static inline int LogFTPEvents(FTP_SESSION *FtpSession)
3980 {
3981     FTPP_GEN_EVENTS GenEvents;
3982     int             iGenerator;
3983     int             iRet;
3984 
3985     GenEvents.stack =       FtpSession->event_list.stack;
3986     GenEvents.stack_count = FtpSession->event_list.stack_count;
3987     GenEvents.events =      FtpSession->event_list.events;
3988     iGenerator = GENERATOR_SPP_FTPP_FTP;
3989 
3990     iRet = LogFTPPEvents(&GenEvents, iGenerator);
3991 
3992     /* Reset the count... */
3993     FtpSession->event_list.stack_count = 0;
3994 
3995     return iRet;
3996 }
3997 
3998 /*
3999  * Function: LogTelnetEvents(TELNET_SESSION *TelnetSession)
4000  *
4001  * Purpose: This is the routine that logs Telnet alerts through Snort.
4002  *          It maps the event into a generic event and calls
4003  *          LOGFTPPEvents().
4004  *
4005  * Arguments: TelnetSession    => pointer the session structure
4006  *
4007  * Returns: int     => an error code integer (0 = success,
4008  *                     >0 = non-fatal error, <0 = fatal error)
4009  *
4010  */
LogTelnetEvents(TELNET_SESSION * TelnetSession)4011 static inline int LogTelnetEvents(TELNET_SESSION *TelnetSession)
4012 {
4013     FTPP_GEN_EVENTS GenEvents;
4014     int             iGenerator;
4015     int             iRet;
4016     GenEvents.stack =       TelnetSession->event_list.stack;
4017     GenEvents.stack_count = TelnetSession->event_list.stack_count;
4018     GenEvents.events =      TelnetSession->event_list.events;
4019     iGenerator = GENERATOR_SPP_FTPP_TELNET;
4020 
4021     iRet = LogFTPPEvents(&GenEvents, iGenerator);
4022 
4023     /* Reset the count... */
4024     TelnetSession->event_list.stack_count = 0;
4025 
4026     return iRet;
4027 }
4028 
4029 /*
4030  * Function: SetSiInput(FTPP_SI_INPUT *SiInput, Packet *p)
4031  *
4032  * Purpose: This is the routine sets the source and destination IP
4033  *          address and port pairs so as to determine the direction
4034  *          of the FTP or telnet connection.
4035  *
4036  * Arguments: SiInput       => pointer the session input structure
4037  *            p             => pointer to the packet structure
4038  *
4039  * Returns: int     => an error code integer (0 = success,
4040  *                     >0 = non-fatal error, <0 = fatal error)
4041  *
4042  */
SetSiInput(FTPP_SI_INPUT * SiInput,SFSnortPacket * p)4043 static inline int SetSiInput(FTPP_SI_INPUT *SiInput, SFSnortPacket *p)
4044 {
4045     IP_COPY_VALUE(SiInput->sip, GET_SRC_IP(p));
4046     IP_COPY_VALUE(SiInput->dip, GET_DST_IP(p));
4047     SiInput->sport = p->src_port;
4048     SiInput->dport = p->dst_port;
4049 
4050     /*
4051      * We now set the packet direction
4052      */
4053     if(p->stream_session &&
4054             _dpd.sessionAPI->get_session_flags(p->stream_session) & SSNFLAG_MIDSTREAM)
4055     {
4056         SiInput->pdir = FTPP_SI_NO_MODE;
4057     }
4058     else if(p->flags & FLAG_FROM_SERVER)
4059     {
4060         SiInput->pdir = FTPP_SI_SERVER_MODE;
4061     }
4062     else if(p->flags & FLAG_FROM_CLIENT)
4063     {
4064         SiInput->pdir = FTPP_SI_CLIENT_MODE;
4065     }
4066     else
4067     {
4068         SiInput->pdir = FTPP_SI_NO_MODE;
4069     }
4070 
4071     return FTPP_SUCCESS;
4072 
4073 }
4074 
4075 /*
4076  * Function: do_detection(Packet *p)
4077  *
4078  * Purpose: This is the routine that directly performs the rules checking
4079  *          for each of the FTP & telnet preprocessing modules.
4080  *
4081  * Arguments: p             => pointer to the packet structure
4082  *
4083  * Returns: None
4084  *
4085  */
do_detection(SFSnortPacket * p)4086 void do_detection(SFSnortPacket *p)
4087 {
4088     //extern int     do_detect;
4089     //extern OptTreeNode *otn_tmp;
4090     PROFILE_VARS;
4091 
4092     /*
4093      * If we get here we either had a client or server request/response.
4094      * We do the detection here, because we're starting a new paradigm
4095      * about protocol decoders.
4096      *
4097      * Protocol decoders are now their own detection engine, since we are
4098      * going to be moving protocol field detection from the generic
4099      * detection engine into the protocol module.  This idea scales much
4100      * better than having all these Packet struct field checks in the
4101      * main detection engine for each protocol field.
4102      */
4103     PREPROC_PROFILE_START(ftppDetectPerfStats);
4104     _dpd.detect(p);
4105 
4106     _dpd.disableAllDetect(p);
4107     PREPROC_PROFILE_END(ftppDetectPerfStats);
4108 #ifdef PERF_PROFILING
4109     ftppDetectCalled = 1;
4110 #endif
4111     //otn_tmp = NULL;
4112 
4113     /*
4114      * We set the global detection flag here so that if request pipelines
4115      * fail, we don't do any detection.
4116      */
4117     //do_detect = 0;
4118 }
4119 
4120 /*
4121  * Function: SnortTelnet(FTPTELNET_GLOBAL_CONF *GlobalConf,
4122  *                       Packet *p,
4123  *                       int iInspectMode)
4124  *
4125  * Purpose: This is the routine that handles the protocol layer checks
4126  *          for telnet.
4127  *
4128  * Arguments: GlobalConf    => pointer the global configuration
4129  *            p             => pointer to the packet structure
4130  *            iInspectMode  => indicator whether this is a client or server
4131  *                             packet.
4132  *
4133  * Returns: int     => an error code integer (0 = success,
4134  *                     >0 = non-fatal error, <0 = fatal error)
4135  *
4136  */
SnortTelnet(FTPTELNET_GLOBAL_CONF * GlobalConf,TELNET_SESSION * TelnetSession,SFSnortPacket * p,int iInspectMode)4137 int SnortTelnet(FTPTELNET_GLOBAL_CONF *GlobalConf, TELNET_SESSION *TelnetSession,
4138         SFSnortPacket *p, int iInspectMode)
4139 {
4140     int iRet;
4141     PROFILE_VARS;
4142 
4143 #ifdef DUMP_BUFFER
4144     dumpBuffer(TELNET_DUMP,(const char *)p->payload,p->payload_size);
4145 #endif
4146 
4147     if (!TelnetSession)
4148     {
4149         if (GlobalConf->inspection_type == FTPP_UI_CONFIG_STATEFUL)
4150         {
4151             return FTPP_NONFATAL_ERR;
4152         }
4153         else
4154         {
4155             return FTPP_INVALID_SESSION;
4156         }
4157     }
4158 
4159     if (TelnetSession->encr_state && !GlobalConf->check_encrypted_data)
4160     {
4161         return FTPP_SUCCESS;
4162     }
4163 
4164     PREPROC_PROFILE_START(telnetPerfStats);
4165 
4166     if (!GlobalConf->telnet_config->normalize)
4167     {
4168         do_detection(p);
4169     }
4170     else
4171     {
4172         iRet = normalize_telnet(GlobalConf, TelnetSession, p,
4173                 iInspectMode, FTPP_APPLY_TNC_ERASE_CMDS);
4174         if ((iRet == FTPP_SUCCESS) || (iRet == FTPP_NORMALIZED))
4175         {
4176             do_detection(p);
4177         }
4178         LogTelnetEvents(TelnetSession);
4179     }
4180     PREPROC_PROFILE_END(telnetPerfStats);
4181 #ifdef PERF_PROFILING
4182     if (ftppDetectCalled)
4183     {
4184         telnetPerfStats.ticks -= ftppDetectPerfStats.ticks;
4185         /* And Reset ticks to 0 */
4186         ftppDetectPerfStats.ticks = 0;
4187         ftppDetectCalled = 0;
4188     }
4189 #endif
4190 
4191     return FTPP_SUCCESS;
4192 }
4193 
4194 /*
4195  * Function: SnortFTP(FTPTELNET_GLOBAL_CONF *GlobalConf,
4196  *                       Packet *p,
4197  *                       int iInspectMode)
4198  *
4199  * Purpose: This is the routine that handles the protocol layer checks
4200  *          for FTP.
4201  *
4202  * Arguments: GlobalConf    => pointer the global configuration
4203  *            p             => pointer to the packet structure
4204  *            iInspectMode  => indicator whether this is a client or server
4205  *                             packet.
4206  *
4207  * Returns: int     => an error code integer (0 = success,
4208  *                     >0 = non-fatal error, <0 = fatal error)
4209  *
4210  */
SnortFTP(FTPTELNET_GLOBAL_CONF * GlobalConf,FTP_SESSION * FTPSession,SFSnortPacket * p,int iInspectMode)4211 int SnortFTP(FTPTELNET_GLOBAL_CONF *GlobalConf, FTP_SESSION *FTPSession,
4212         SFSnortPacket *p, int iInspectMode)
4213 {
4214     int iRet;
4215     ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback();
4216     PROFILE_VARS;
4217 
4218     if (!FTPSession ||
4219             FTPSession->server_conf == NULL ||
4220             FTPSession->client_conf == NULL)
4221     {
4222         return FTPP_INVALID_SESSION;
4223     }
4224 
4225     if(((FTPSession->encr_state == AUTH_TLS_ENCRYPTED) ||
4226                 (FTPSession->encr_state == AUTH_SSL_ENCRYPTED) ||
4227                 (FTPSession->encr_state == AUTH_UNKNOWN_ENCRYPTED)) )
4228     {
4229         if ((iInspectMode == FTPP_SI_CLIENT_MODE) && FTPSession->encr_state_chello )
4230         {
4231             if (IsTlsClientHello(p->payload, p->payload + p->payload_size))
4232             {
4233                 FTPSession->encr_state_chello = false;
4234                 if(ssl_cb)
4235                     ssl_cb->session_initialize(p, FTPSession, FTP_Set_flow_id);
4236             }
4237 
4238         }
4239 
4240         if( _dpd.streamAPI->is_session_decrypted(p->stream_session))
4241             FTPSession->encr_state = 0;
4242         else if (!GlobalConf->check_encrypted_data)
4243             return FTPP_SUCCESS;
4244     }
4245 
4246     PREPROC_PROFILE_START(ftpPerfStats);
4247 
4248     if (iInspectMode == FTPP_SI_SERVER_MODE)
4249     {
4250         DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
4251                     "Server packet: %.*s\n", p->payload_size, p->payload));
4252 
4253         // FIXTHIS breaks target-based non-standard ports
4254         //if ( !_dpd.isPafEnabled() )
4255         /* Force flush of client side of stream  */
4256         _dpd.streamAPI->response_flush_stream(p);
4257     }
4258     else
4259     {
4260         if ( !_dpd.readyForProcess(p) )
4261         {
4262             DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
4263                         "Client packet will be reassembled\n"));
4264             PREPROC_PROFILE_END(ftpPerfStats);
4265             return FTPP_SUCCESS;
4266         }
4267         else
4268         {
4269             DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
4270                         "Client packet: rebuilt %s: %.*s\n",
4271                         (p->flags & FLAG_REBUILT_STREAM) ? "yes" : "no",
4272                         p->payload_size, p->payload));
4273         }
4274     }
4275 
4276     iRet = initialize_ftp(FTPSession, p, iInspectMode);
4277     if (iRet)
4278     {
4279         LogFTPEvents(FTPSession);
4280 
4281         PREPROC_PROFILE_END(ftpPerfStats);
4282         return iRet;
4283     }
4284 
4285     iRet = check_ftp(FTPSession, p, iInspectMode);
4286     if (iRet == FTPP_SUCCESS)
4287     {
4288         /* Ideally, Detect(), called from do_detection, will look at
4289          * the cmd & param buffers, or the rsp & msg buffers.  Current
4290          * architecture does not support this...
4291          * So, we call do_detection() here.  Otherwise, we'd call it
4292          * from inside check_ftp -- each time we process a pipelined
4293          * FTP command.
4294          */
4295         do_detection(p);
4296     }
4297 
4298     LogFTPEvents(FTPSession);
4299 
4300     PREPROC_PROFILE_END(ftpPerfStats);
4301 #ifdef PERF_PROFILING
4302     if (ftppDetectCalled)
4303     {
4304         ftpPerfStats.ticks -= ftppDetectPerfStats.ticks;
4305         /* And Reset ticks to 0 */
4306         ftppDetectPerfStats.ticks = 0;
4307         ftppDetectCalled = 0;
4308     }
4309 #endif
4310 
4311     return iRet;
4312 }
4313 
4314 /*
4315  * Funtcion: SnortFTPTelnet
4316  *
4317  * Purpose: This function calls the FTPTelnet function that handles
4318  *          the protocol layer checks for an FTP or Telnet session,
4319  *          after determining which, if either, protocol applies.
4320  *
4321  * Arguments: GlobalConf    => pointer the global configuration
4322  *            p             => pointer to the packet structure
4323  *
4324  * Returns: int     => an error code integer (0 = success,
4325  *                     >0 = non-fatal error, <0 = fatal error)
4326  *
4327  */
SnortFTPTelnet(SFSnortPacket * p)4328 int SnortFTPTelnet(SFSnortPacket *p)
4329 {
4330     FTPP_SI_INPUT SiInput;
4331     int iInspectMode = FTPP_SI_NO_MODE;
4332     FTP_TELNET_SESSION *ft_ssn = NULL;
4333     tSfPolicyId policy_id = _dpd.getNapRuntimePolicy();
4334     FTPTELNET_GLOBAL_CONF *GlobalConf = NULL;
4335 
4336 #ifdef DUMP_BUFFER
4337     dumpBufferInit();
4338 #endif
4339 
4340     sfPolicyUserPolicySet (ftp_telnet_config, policy_id);
4341     GlobalConf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGetCurrent(ftp_telnet_config);
4342 
4343     /*
4344      * Set up the FTPP_SI_INPUT pointer.  This is what the session_inspection()
4345      * routines use to determine client and server traffic.  Plus, this makes
4346      * the FTPTelnet library very independent from snort.
4347      */
4348     SetSiInput(&SiInput, p);
4349 
4350     if (p->stream_session)
4351     {
4352         ft_ssn = (FTP_TELNET_SESSION *)
4353             _dpd.sessionAPI->get_application_data(p->stream_session, PP_FTPTELNET);
4354 
4355         if (ft_ssn != NULL)
4356         {
4357             SiInput.pproto = ft_ssn->proto;
4358 
4359             if (ft_ssn->proto == FTPP_SI_PROTO_TELNET)
4360             {
4361                 TELNET_SESSION *telnet_ssn = (TELNET_SESSION *)ft_ssn;
4362 
4363                 GlobalConf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(telnet_ssn->global_conf, telnet_ssn->policy_id);
4364 
4365                 if (SiInput.pdir != FTPP_SI_NO_MODE)
4366                 {
4367                     iInspectMode = SiInput.pdir;
4368                 }
4369                 else
4370                 {
4371                     if ((telnet_ssn->telnet_conf != NULL) &&
4372                             (telnet_ssn->telnet_conf->proto_ports.ports[SiInput.sport]))
4373                     {
4374                         iInspectMode = FTPP_SI_SERVER_MODE;
4375                     }
4376                     else if ((telnet_ssn->telnet_conf != NULL) &&
4377                             (telnet_ssn->telnet_conf->proto_ports.ports[SiInput.dport]))
4378                     {
4379                         iInspectMode = FTPP_SI_CLIENT_MODE;
4380                     }
4381                 }
4382             }
4383             else if (ft_ssn->proto == FTPP_SI_PROTO_FTP)
4384             {
4385                 FTP_SESSION *ftp_ssn = (FTP_SESSION *)ft_ssn;
4386 
4387                 GlobalConf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(ftp_ssn->global_conf, ftp_ssn->policy_id);
4388 
4389                 if (SiInput.pdir != FTPP_SI_NO_MODE)
4390                 {
4391                     iInspectMode = SiInput.pdir;
4392                 }
4393                 else
4394                 {
4395                     if ((ftp_ssn->server_conf != NULL) &&
4396                             ftp_ssn->server_conf->proto_ports.ports[SiInput.sport])
4397                     {
4398                         iInspectMode = FTPP_SI_SERVER_MODE;
4399                     }
4400                     else if ((ftp_ssn->server_conf != NULL) &&
4401                             ftp_ssn->server_conf->proto_ports.ports[SiInput.dport])
4402                     {
4403                         iInspectMode = FTPP_SI_CLIENT_MODE;
4404                     }
4405                     else
4406                     {
4407                         iInspectMode = FTPGetPacketDir(p);
4408                     }
4409                 }
4410             }
4411             else
4412             {
4413                 /* XXX - Not FTP or Telnet */
4414                 _dpd.sessionAPI->set_application_data(p->stream_session, PP_FTPTELNET, NULL, NULL);
4415                 return 0;
4416             }
4417         }
4418     }
4419 
4420     if (GlobalConf == NULL)
4421         return 0;
4422 
4423     /*
4424      * FTPTelnet PACKET FLOW::
4425      *
4426      * Determine Proto Module::
4427      *   The Session Inspection Module retrieves the appropriate
4428      *   configuration for sessions, and takes care of the stateless
4429      *   vs. stateful processing in order to do this.  Once this module
4430      *   does it's magic, we're ready for the primetime.  This means
4431      *   determining whether this is an FTP or a Telnet session.
4432      *
4433      * Proto Specific Module::
4434      *   This is where we normalize the data.  The Protocol specific module
4435      *   handles what type of normalization to do (telnet, ftp) and does
4436      *   protocol related checks.
4437      *
4438      */
4439     if (ft_ssn == NULL)
4440     {
4441         int iRet = ftpp_si_determine_proto(p, GlobalConf, &ft_ssn, &SiInput, &iInspectMode);
4442         if (iRet)
4443             return iRet;
4444     }
4445 
4446     if (ft_ssn != NULL)
4447     {
4448         switch (SiInput.pproto)
4449         {
4450             case FTPP_SI_PROTO_TELNET:
4451                 return SnortTelnet(GlobalConf, (TELNET_SESSION *)ft_ssn, p, iInspectMode);
4452                 break;
4453             case FTPP_SI_PROTO_FTP:
4454                 return SnortFTP(GlobalConf, (FTP_SESSION *)ft_ssn, p, iInspectMode);
4455                 break;
4456         }
4457     }
4458 
4459     /* Uh, shouldn't get here  */
4460     return FTPP_INVALID_PROTO;
4461 }
4462 
4463 #ifdef TARGET_BASED
FTPDataProcess(SFSnortPacket * p,FTP_DATA_SESSION * data_ssn,uint8_t * file_data,uint16_t data_length)4464 static void FTPDataProcess(SFSnortPacket *p, FTP_DATA_SESSION *data_ssn,
4465         uint8_t *file_data, uint16_t data_length)
4466 {
4467     int status;
4468 
4469     _dpd.setFileDataPtr((uint8_t *)p->payload, (uint16_t)p->payload_size);
4470 
4471     if(data_ssn->flags & FTPDATA_FLG_FLUSH)
4472     {
4473         _dpd.fileAPI->set_file_partial(p,data_ssn->position,data_ssn->direction,true);
4474     }
4475     if(data_ssn->flags & FTPDATA_FLG_STOP)
4476     {
4477         _dpd.fileAPI->set_file_partial(p,data_ssn->position,data_ssn->direction,false);
4478     }
4479     status = _dpd.fileAPI->file_process(p, (uint8_t *)file_data,
4480             data_length, data_ssn->position, data_ssn->direction, false, (data_ssn->flags & FTPDATA_FLG_FLUSH) ? true : false);
4481 
4482     FTP_SESSION *ft_ssn = (FTP_SESSION *) _dpd.sessionAPI->get_application_data_from_key(data_ssn->ftp_key, PP_FTPTELNET);
4483     if(ft_ssn && (ft_ssn->flags & FTP_FLG_MALWARE_ENABLED) && _dpd.active_PacketWasDropped())
4484     {
4485         _dpd.fileAPI->file_resume_block_add_file(p, data_ssn->path_hash, 0, 0, 0, NULL, ft_ssn->control_clientPort,
4486                 ft_ssn->control_serverPort, true , data_ssn->direction);
4487     }
4488 
4489     /* Filename needs to be set AFTER the first call to file_process( ) */
4490     if (data_ssn->filename && !(data_ssn->flags & FTPDATA_FLG_FILENAME_SET))
4491     {
4492         _dpd.fileAPI->set_file_name(p->stream_session,
4493                 (uint8_t *)data_ssn->filename, data_ssn->file_xfer_info, false);
4494         data_ssn->flags |= FTPDATA_FLG_FILENAME_SET;
4495     }
4496 
4497     /* Ignore the rest of this transfer if file processing is complete
4498      * and preprocessor was configured to ignore ftp-data sessions. */
4499     if (!status && data_ssn->data_chan)
4500     {
4501         _dpd.sessionAPI->set_ignore_direction(
4502                 p->stream_session, SSN_DIR_BOTH);
4503     }
4504 }
4505 
SnortFTPData_EOF(SFSnortPacket * p)4506 void SnortFTPData_EOF(SFSnortPacket *p)
4507 {
4508     FTP_SESSION *ftp_ssn;
4509     FTP_DATA_SESSION *data_ssn = (FTP_DATA_SESSION *)
4510                 _dpd.sessionAPI->get_application_data(p->stream_session, PP_FTPTELNET);
4511 
4512     if (!PROTO_IS_FTP_DATA(data_ssn) || !FTPDataDirection(p, data_ssn))
4513         return;
4514 
4515     ftp_ssn = (FTP_SESSION *) _dpd.sessionAPI->get_application_data_from_key(data_ssn->ftp_key, PP_FTPTELNET);
4516     initFilePosition(&data_ssn->position, _dpd.fileAPI->get_file_processed_size(p->stream_session));
4517     finalFilePosition(&data_ssn->position);
4518 
4519     _dpd.streamAPI->request_flush_stream(p);
4520     if (ftp_ssn && (data_ssn->flags & FTPDATA_FLG_REST) && (ftp_ssn->rest_cmd_offset > 0))
4521     {
4522         File_Verdict verdict;
4523         verdict = _dpd.fileAPI->file_resume_block_check(p, data_ssn->path_hash);
4524         data_ssn->flags &= ~FTPDATA_FLG_REST;
4525         ftp_ssn->rest_cmd_offset = 0;
4526         if((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT))
4527         {
4528             _dpd.fileAPI->file_resume_block_add_file(p, data_ssn->path_hash, 0, 0, 0, NULL, ftp_ssn->control_clientPort,
4529                     ftp_ssn->control_serverPort, true, data_ssn->direction);
4530         }
4531         return;
4532     }
4533 
4534     if (!(data_ssn->flags & FTPDATA_FLG_STOP))
4535     {
4536         data_ssn->flags |= FTPDATA_FLG_STOP;
4537         FTPDataProcess(p, (FTP_DATA_SESSION *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_FTPTELNET),
4538                 (uint8_t *)p->payload,
4539                 (uint16_t)p->payload_size);
4540     }
4541 }
4542 
SnortFTPData_Flush(SFSnortPacket * p)4543 void SnortFTPData_Flush(SFSnortPacket *p)
4544 {
4545     FTP_DATA_SESSION *data_ssn = (FTP_DATA_SESSION *)
4546                 _dpd.sessionAPI->get_application_data(p->stream_session, PP_FTPTELNET);
4547 
4548     if (!PROTO_IS_FTP_DATA(data_ssn) || !FTPDataDirection(p, data_ssn))
4549         return;
4550 
4551     initFilePosition(&data_ssn->position, _dpd.fileAPI->get_file_processed_size(p->stream_session));
4552     data_ssn->flags |= FTPDATA_FLG_FLUSH;
4553 
4554     _dpd.streamAPI->request_flush_stream(p);
4555 
4556     data_ssn->flags &= ~FTPDATA_FLG_FLUSH;
4557     return;
4558 }
4559 
SnortFTPData(SFSnortPacket * p)4560 int SnortFTPData(SFSnortPacket *p)
4561 {
4562     FTP_SESSION *ftp_ssn;
4563     FTP_DATA_SESSION *data_ssn;
4564 
4565     if (!p->stream_session)
4566         return -1;
4567 
4568     data_ssn = (FTP_DATA_SESSION *)
4569         _dpd.sessionAPI->get_application_data(p->stream_session, PP_FTPTELNET);
4570 
4571     if (!PROTO_IS_FTP_DATA(data_ssn))
4572         return -2;
4573 
4574     if (data_ssn->flags & FTPDATA_FLG_STOP)
4575         return 0;
4576 
4577     /* FTP-Data Session is in limbo, we need to lookup the control session
4578      * to figure out what to do. */
4579      ftp_ssn = (FTP_SESSION *) _dpd.sessionAPI->get_application_data_from_key(data_ssn->ftp_key, PP_FTPTELNET);
4580      if(!ftp_ssn)
4581          return -3;
4582 
4583 #ifdef DAQ_PKT_FLAG_SSL_DETECTED
4584     //  Set up the flow context when an SSL client hello is detected and
4585     //  ignore packets until the flow is decrypted.
4586     if(p->pkt_header->flags & DAQ_PKT_FLAG_SSL_DETECTED)
4587     {
4588         ssl_callback_interface_t *ssl_cb;
4589         ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback();
4590         if(ssl_cb)
4591         {
4592             ftp_ssn->data_chan_state |= DATA_CHAN_CLIENT_HELLO_SEEN;
4593             ssl_cb->session_initialize(p, data_ssn, FTPData_Set_flow_id);
4594         }
4595         return 0;  //  Ignore SSL client hello.
4596     }
4597     else if(ftp_ssn->data_chan_state & DATA_CHAN_CLIENT_HELLO_SEEN)
4598     {
4599         if( _dpd.streamAPI->is_session_decrypted(p->stream_session))
4600         {
4601             //  Done handling SSL handshake.
4602             ftp_ssn->data_chan_state &= ~DATA_CHAN_CLIENT_HELLO_SEEN;
4603         }
4604         else
4605         {
4606             return 0;   //  Ignore packet.
4607         }
4608     }
4609 #endif
4610     if ((data_ssn->flags & FTPDATA_FLG_REST) && (ftp_ssn->rest_cmd_offset > 0))
4611     {
4612         File_Verdict verdict;
4613         verdict = _dpd.fileAPI->file_resume_block_check(p, data_ssn->path_hash);
4614         data_ssn->flags &= ~FTPDATA_FLG_REST;
4615         ftp_ssn->rest_cmd_offset = 0;
4616         if((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT))
4617         {
4618             data_ssn->flags |= FTPDATA_FLG_STOP;
4619             _dpd.fileAPI->file_resume_block_add_file(p, data_ssn->path_hash, 0, 0, 0, NULL, ftp_ssn->control_clientPort,
4620                                         ftp_ssn->control_serverPort, true, data_ssn->direction);
4621         }
4622         return 0 ;
4623     }
4624     //  bail if we have not rebuilt the stream yet.
4625     if (!_dpd.readyForProcess(p))
4626         return 0;
4627 
4628     if (data_ssn->file_xfer_info == FTPP_FILE_UNKNOWN)
4629     {
4630         if (!PROTO_IS_FTP(ftp_ssn))
4631         {
4632             DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
4633                         "FTP-DATA Invalid FTP_SESSION retrieved durring lookup\n"););
4634 
4635             if (data_ssn->data_chan)
4636                 _dpd.sessionAPI->set_ignore_direction(p->stream_session, SSN_DIR_BOTH);
4637 
4638             return -2;
4639         }
4640 
4641         switch (ftp_ssn->file_xfer_info)
4642         {
4643             case FTPP_FILE_UNKNOWN:
4644                 /* Keep waiting */
4645                 break;
4646 
4647             case FTPP_FILE_IGNORE:
4648                 /* This wasn't a file transfer; ignore it */
4649                 if (data_ssn->data_chan)
4650                     _dpd.sessionAPI->set_ignore_direction(p->stream_session, SSN_DIR_BOTH);
4651                 return 0;
4652 
4653             default:
4654                 /* A file transfer was detected. */
4655                 data_ssn->direction = ftp_ssn->data_xfer_dir;
4656                 data_ssn->file_xfer_info = ftp_ssn->file_xfer_info;
4657                 ftp_ssn->file_xfer_info  = 0;
4658                 data_ssn->filename  = ftp_ssn->filename;
4659                 ftp_ssn->filename   = NULL;
4660                 break;
4661         }
4662     }
4663 
4664     if (!FTPDataDirection(p, data_ssn))
4665         return 0;
4666 
4667     if (isFileEnd(data_ssn->position))
4668     {
4669         data_ssn->flags |= FTPDATA_FLG_STOP;
4670     }
4671     else
4672     {
4673         initFilePosition(&data_ssn->position,
4674                 _dpd.fileAPI->get_file_processed_size(p->stream_session));
4675         if (p->tcp_header && (p->tcp_header->flags & TCPHEADER_FIN))
4676             finalFilePosition(&data_ssn->position);
4677     }
4678 
4679     FTPDataProcess(p, data_ssn, (uint8_t *)p->payload, (uint16_t)p->payload_size);
4680     return 0;
4681 }
4682 #endif /* TARGET_BASED */
4683 
FTPPBounceInit(struct _SnortConfig * sc,char * name,char * parameters,void ** dataPtr)4684 int FTPPBounceInit(struct _SnortConfig *sc, char *name, char *parameters, void **dataPtr)
4685 {
4686     char **toks;
4687     int num_toks;
4688 
4689     toks = _dpd.tokenSplit(parameters, ",", 12, &num_toks, 0);
4690 
4691     if(num_toks > 0)
4692     {
4693         DynamicPreprocessorFatalMessage("ERROR: Bad arguments to '%s' option: '%s'\n",
4694                 name, parameters);
4695     }
4696 
4697     _dpd.tokenFree(&toks, num_toks);
4698 
4699     *dataPtr = NULL;
4700 
4701     return 1;
4702 }
4703 
4704 
4705 /****************************************************************************
4706  *
4707  * Function: FTPPBounce(void *pkt, uint8_t **cursor, void **dataPtr)
4708  *
4709  * Purpose: Use this function to perform the particular detection routine
4710  *          that this rule keyword is supposed to encompass.
4711  *
4712  * Arguments: p => pointer to the decoded packet
4713  *            cursor => pointer to the current location in the buffer
4714  *            dataPtr => pointer to rule specific data (not used for this option)
4715  *
4716  * Returns: If the detection test fails, this function *must* return a zero!
4717  *          On success, it returns 1;
4718  *
4719  ****************************************************************************/
FTPPBounceEval(void * pkt,const uint8_t ** cursor,void * dataPtr)4720 int FTPPBounceEval(void *pkt, const uint8_t **cursor, void *dataPtr)
4721 {
4722     uint32_t ip = 0;
4723     SFSnortPacket *p = (SFSnortPacket *)pkt;
4724     int octet=0;
4725     const char *start_ptr, *end_ptr;
4726     const char *this_param = *(const char **)cursor;
4727 
4728     int dsize;
4729 
4730     if ( !p->ip4_header )
4731         return 0;
4732 
4733     if(_dpd.Is_DetectFlag(SF_FLAG_ALT_DETECT))
4734     {
4735         dsize = _dpd.altDetect->len;
4736         start_ptr = (const char*) _dpd.altDetect->data;
4737         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
4738                     "Using Alternative Detect buffer!\n"););
4739     }
4740     else if(_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE))
4741     {
4742         dsize = _dpd.altBuffer->len;
4743         start_ptr = (const char *) _dpd.altBuffer->data;
4744         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
4745                     "Using Alternative Decode buffer!\n"););
4746 
4747     }
4748     else
4749     {
4750         start_ptr = (const char *)p->payload;
4751         dsize = p->payload_size;
4752     }
4753 
4754     DEBUG_WRAP(
4755             DebugMessage(DEBUG_PATTERN_MATCH,"[*] ftpbounce firing...\n");
4756             DebugMessage(DEBUG_PATTERN_MATCH,"payload starts at %p\n", start_ptr);
4757             );  /* END DEBUG_WRAP */
4758 
4759     /* save off whatever our ending pointer is */
4760     end_ptr = start_ptr + dsize;
4761 
4762     while ((this_param < end_ptr) && isspace((int)*this_param)) this_param++;
4763 
4764     do
4765     {
4766         int value = 0;
4767 
4768         do
4769         {
4770             if (!isdigit((int)*this_param))
4771             {
4772                 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
4773                             "[*] ftpbounce non digit char failed..\n"););
4774                 return RULE_NOMATCH;
4775             }
4776 
4777             value = value * 10 + (*this_param - '0');
4778             this_param++;
4779 
4780         } while ((this_param < end_ptr) &&
4781                 (*this_param != ',') &&
4782                 (!(isspace((int)*this_param))));
4783 
4784         if (value > 0xFF)
4785         {
4786             DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
4787                         "[*] ftpbounce value > 256 ..\n"););
4788             return RULE_NOMATCH;
4789         }
4790 
4791         if (octet  < 4)
4792         {
4793             ip = (ip << 8) + value;
4794         }
4795 
4796         if ((this_param < end_ptr) && !isspace((int)*this_param))
4797             this_param++;
4798 
4799         octet++;
4800 
4801     } while ((this_param < end_ptr) && !isspace((int)*this_param) && (octet < 4));
4802 
4803     if (octet < 4)
4804     {
4805         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
4806                     "[*] ftpbounce insufficient data ..\n"););
4807         return RULE_NOMATCH;
4808     }
4809 
4810     if (ip != ntohl(p->ip4_header->source.s_addr))
4811     {
4812         /* Bounce attempt -- IPs not equal */
4813         return RULE_MATCH;
4814     }
4815     else
4816     {
4817         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
4818                     "PORT command not being used in bounce\n"););
4819         return RULE_NOMATCH;
4820     }
4821 
4822     /* Never reached */
4823     return RULE_NOMATCH;
4824 }
4825 
4826 /* Add ports configured for ftptelnet preprocessor to stream5 port filtering so that
4827  * if any_any rules are being ignored then the packet still reaches ftptelnet.
4828  *
4829  * For ports in global_server configuration, server_lookup and server_lookupIpv6,
4830  * add the port to stream5 port filter list.
4831  */
_FTPTelnetAddPortsOfInterest(struct _SnortConfig * sc,FTPTELNET_GLOBAL_CONF * config,tSfPolicyId policy_id)4832 static void _FTPTelnetAddPortsOfInterest(struct _SnortConfig *sc, FTPTELNET_GLOBAL_CONF *config, tSfPolicyId policy_id)
4833 {
4834     int i;
4835 
4836     if (config == NULL)
4837         return;
4838 
4839     /* For the server callback */
4840     ftp_current_policy = policy_id;
4841 
4842     _addPortsToStream(sc, config->telnet_config->proto_ports.ports, policy_id, 0);
4843     _addPortsToStream(sc, config->default_ftp_server->proto_ports.ports, policy_id, 1);
4844     ftpp_ui_server_iterate(sc, config->server_lookup,
4845             _addFtpServerConfPortsToStream, &i);
4846 }
4847 
_addFtpServerConfPortsToStream(struct _SnortConfig * sc,void * pData)4848 static int _addFtpServerConfPortsToStream(struct _SnortConfig *sc, void *pData)
4849 {
4850     FTP_SERVER_PROTO_CONF *pConf = (FTP_SERVER_PROTO_CONF *)pData;
4851     _addPortsToStream(sc, pConf->proto_ports.ports, ftp_current_policy, 1);
4852     return 0;
4853 }
4854 
4855 // flush at last line feed in payload
4856 // preproc will deal with any pipelined commands
ftp_paf(void * ssn,void ** pv,const uint8_t * data,uint32_t len,uint64_t * flags,uint32_t * fp,uint32_t * fp_eoh)4857 static PAF_Status ftp_paf (
4858         void* ssn, void** pv, const uint8_t* data, uint32_t len,
4859         uint64_t *flags, uint32_t* fp, uint32_t* fp_eoh)
4860 {
4861 #ifdef HAVE_MEMRCHR
4862     uint8_t* lf =  memrchr(data, '\n', len);
4863 #else
4864     uint32_t n = len;
4865     uint8_t* lf = NULL, * tmp = (uint8_t*) data;
4866 
4867     while ( (tmp = memchr(tmp, '\n', n)) )
4868     {
4869         lf = tmp++;
4870         n = len - (tmp - data);
4871     }
4872 #endif
4873 
4874     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
4875                 "%s[%d] '%*.*s'\n", __FUNCTION__, len, len, len, data));
4876 
4877     if ( !lf )
4878         return PAF_SEARCH;
4879 
4880     *fp = lf - data + 1;
4881     return PAF_FLUSH;
4882 }
4883 
4884 #ifdef TARGET_BASED
_FTPTelnetAddService(struct _SnortConfig * sc,int16_t app,tSfPolicyId policy)4885 static void _FTPTelnetAddService (struct _SnortConfig *sc, int16_t app, tSfPolicyId policy)
4886 {
4887     if ( _dpd.isPafEnabled() )
4888     {
4889        ftp_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, true, ftp_paf, true);
4890        ftp_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, false, ftp_paf, true);
4891     }
4892 }
4893 #endif
4894 
_addPortsToStream(struct _SnortConfig * sc,char * ports,tSfPolicyId policy_id,int ftp)4895 static void _addPortsToStream(struct _SnortConfig *sc, char *ports, tSfPolicyId policy_id, int ftp)
4896 {
4897     unsigned int i;
4898 
4899     for (i = 0; i < MAXPORTS; i++)
4900     {
4901         if (ports[i])
4902         {
4903             //Add port the port
4904             _dpd.streamAPI->set_port_filter_status(sc, IPPROTO_TCP, (uint16_t)i,
4905                     PORT_MONITOR_SESSION, policy_id, 1);
4906 
4907             if ( ftp && _dpd.isPafEnabled() )
4908             {
4909                 ftp_paf_id = _dpd.streamAPI->register_paf_port(sc, policy_id, (uint16_t)i, true, ftp_paf, false);
4910                 ftp_paf_id = _dpd.streamAPI->register_paf_port(sc, policy_id, (uint16_t)i, false, ftp_paf, false);
4911             }
4912         }
4913     }
4914 }
4915 
FtpTelnetInitGlobalConfig(FTPTELNET_GLOBAL_CONF * config,char * ErrorString,int iErrStrLen)4916 int FtpTelnetInitGlobalConfig(FTPTELNET_GLOBAL_CONF *config,
4917         char *ErrorString, int iErrStrLen)
4918 {
4919     int iRet;
4920 
4921     if (config == NULL)
4922     {
4923         snprintf(ErrorString, iErrStrLen, "Global configuration is NULL.");
4924         return FTPP_FATAL_ERR;
4925     }
4926 
4927     iRet = ftpp_ui_config_init_global_conf(config);
4928     if (iRet)
4929     {
4930         snprintf(ErrorString, iErrStrLen,
4931                 "Error initializing Global Configuration.");
4932 
4933         return FTPP_FATAL_ERR;
4934     }
4935 
4936     return 0;
4937 }
4938 
4939 
FTP_Set_flow_id(void * app_data,uint32_t fid)4940 void FTP_Set_flow_id( void *app_data, uint32_t fid )
4941 {
4942     FTP_SESSION *ssn = (FTP_SESSION *)app_data;
4943     if( ssn )
4944         ssn->flow_id = fid;
4945 }
4946 
FTPData_Set_flow_id(void * app_data,uint32_t fid)4947 void FTPData_Set_flow_id( void *app_data, uint32_t fid )
4948 {
4949 #ifdef TARGET_BASED
4950     FTP_DATA_SESSION *ssn = (FTP_DATA_SESSION *)app_data;
4951     if( ssn )
4952         ssn->flow_id = fid;
4953 #endif
4954 }
4955