"Fossies" - the Fresh Open Source Software Archive

Member "snort-2.9.17/src/dynamic-preprocessors/ftptelnet/snort_ftptelnet.c" (16 Oct 2020, 148501 Bytes) of package /linux/misc/snort-2.9.17.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "snort_ftptelnet.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.9.16.1_vs_2.9.17.

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