]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
modularization that should have been committed previously
authorRuss Combs <rucombs@cisco.com>
Tue, 22 Apr 2014 14:42:57 +0000 (10:42 -0400)
committerRuss Combs <rucombs@cisco.com>
Tue, 22 Apr 2014 14:42:57 +0000 (10:42 -0400)
15 files changed:
src/network_inspectors/stream5/icmp_config.h [new file with mode: 0644]
src/network_inspectors/stream5/ip_config.h [new file with mode: 0644]
src/network_inspectors/stream5/tcp_config.h [new file with mode: 0644]
src/network_inspectors/stream5/udp_config.h [new file with mode: 0644]
src/service_inspectors/ftp_telnet/ftp.cc [new file with mode: 0644]
src/service_inspectors/ftp_telnet/ftp_module.cc [new file with mode: 0644]
src/service_inspectors/ftp_telnet/ftp_module.h [new file with mode: 0644]
src/service_inspectors/ftp_telnet/ftp_parse.cc [new file with mode: 0644]
src/service_inspectors/ftp_telnet/ftp_parse.h [new file with mode: 0644]
src/service_inspectors/ftp_telnet/ftp_print.cc [new file with mode: 0644]
src/service_inspectors/ftp_telnet/ftp_print.h [new file with mode: 0644]
src/service_inspectors/ftp_telnet/telnet.cc [new file with mode: 0644]
src/service_inspectors/ftp_telnet/telnet.h [new file with mode: 0644]
src/service_inspectors/ftp_telnet/telnet_module.cc [new file with mode: 0644]
src/service_inspectors/ftp_telnet/telnet_module.h [new file with mode: 0644]

diff --git a/src/network_inspectors/stream5/icmp_config.h b/src/network_inspectors/stream5/icmp_config.h
new file mode 100644 (file)
index 0000000..a3a35ba
--- /dev/null
@@ -0,0 +1,38 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ * Copyright (C) 2005-2013 Sourcefire, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ ****************************************************************************/
+
+#ifndef ICMP_CONFIG_H
+#define ICMP_CONFIG_H
+
+typedef struct _Stream5IcmpPolicy
+{
+    uint32_t   session_timeout;
+
+} Stream5IcmpPolicy;
+
+struct Stream5IcmpConfig
+{
+    Stream5IcmpPolicy default_policy;
+};
+
+#endif
+
diff --git a/src/network_inspectors/stream5/ip_config.h b/src/network_inspectors/stream5/ip_config.h
new file mode 100644 (file)
index 0000000..7ad14ad
--- /dev/null
@@ -0,0 +1,37 @@
+/****************************************************************************
+ *
+ *  Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ *  Copyright (C) 2005-2013 Sourcefire, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License Version 2 as
+ *  published by the Free Software Foundation.  You may not use, modify or
+ *  distribute this program under any other version of the GNU General
+ *  Public License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ *****************************************************************************/
+
+#ifndef IP_CONFIG_H
+#define IP_CONFIG_H
+
+struct Stream5IpPolicy
+{
+    uint32_t   session_timeout;
+};
+
+struct Stream5IpConfig
+{
+    Stream5IpPolicy default_policy;
+};
+
+#endif
+
diff --git a/src/network_inspectors/stream5/tcp_config.h b/src/network_inspectors/stream5/tcp_config.h
new file mode 100644 (file)
index 0000000..6e57f2b
--- /dev/null
@@ -0,0 +1,110 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ * Copyright (C) 2005-2013 Sourcefire, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ ****************************************************************************/
+
+#ifndef TCP_CONFIG_H
+#define TCP_CONFIG_H
+
+#include "decode.h"
+#include "target_based/sftarget_protocol_reference.h"
+
+typedef struct _FlushMgr
+{
+    uint32_t   flush_pt;
+    uint16_t   last_count;
+    uint16_t   last_size;
+    uint8_t    flush_policy;
+    uint8_t    flush_type;
+    uint8_t    auto_disable;
+    //uint8_t    spare;
+
+} FlushMgr;
+
+typedef struct _FlushConfig
+{
+    FlushMgr client;
+    FlushMgr server;
+    //SF_LIST *dynamic_policy;
+    uint8_t configured;
+} FlushConfig;
+
+#ifndef DYNAMIC_RANDOM_FLUSH_POINTS
+typedef struct _FlushPointList
+{
+    uint8_t    current;
+    uint8_t    initialized;
+
+    uint32_t   flush_range;
+    uint32_t   flush_base;  /* Set as value - range/2 */
+    /* flush_pt is split evently on either side of flush_value, within
+     * the flush_range.  flush_pt can be from:
+     * (flush_value - flush_range/2) to (flush_value + flush_range/2)
+     *
+     * For example:
+     * flush_value = 192
+     * flush_range = 128
+     * flush_pt will vary from 128 to 256
+     */
+    uint32_t *flush_points;
+
+} FlushPointList;
+#endif
+
+typedef struct _Stream5TcpPolicy
+{
+    uint16_t   policy;
+    uint16_t   reassembly_policy;
+    uint16_t   flags;
+    uint16_t   flush_factor;
+
+    uint32_t   session_timeout;
+    uint32_t   max_window;
+    uint32_t   overlap_limit;
+    uint32_t   hs_timeout;
+
+    uint32_t   max_queued_bytes;
+    uint32_t   max_queued_segs;
+
+    uint32_t   max_consec_small_segs;
+    uint32_t   max_consec_small_seg_size;
+
+    FlushConfig flush_config[MAX_PORTS];
+    FlushConfig flush_config_protocol[MAX_PROTOCOL_ORDINAL];
+#ifndef DYNAMIC_RANDOM_FLUSH_POINTS
+    FlushPointList flush_point_list;
+#endif
+
+    char       small_seg_ignore[MAX_PORTS/8];
+
+    struct _Stream5TcpConfig* config;
+} Stream5TcpPolicy;
+
+struct Stream5TcpConfig
+{
+    Stream5TcpPolicy* policy;
+    void* paf_config;
+
+    uint16_t session_on_syn;
+    uint16_t port_filter[MAX_PORTS + 1];
+};
+
+#endif
+
diff --git a/src/network_inspectors/stream5/udp_config.h b/src/network_inspectors/stream5/udp_config.h
new file mode 100644 (file)
index 0000000..4f3a083
--- /dev/null
@@ -0,0 +1,42 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ * Copyright (C) 2005-2013 Sourcefire, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ ****************************************************************************/
+
+#ifndef UDP_CONFIG_H
+#define UDP_CONFIG_H
+
+#include "decode.h"
+
+typedef struct _Stream5UdpPolicy
+{
+    uint32_t   session_timeout;
+    uint16_t   flags;
+
+} Stream5UdpPolicy;
+
+struct Stream5UdpConfig
+{
+    Stream5UdpPolicy* policy;
+    uint16_t port_filter[MAX_PORTS + 1];
+};
+
+#endif
+
diff --git a/src/service_inspectors/ftp_telnet/ftp.cc b/src/service_inspectors/ftp_telnet/ftp.cc
new file mode 100644 (file)
index 0000000..0737b4a
--- /dev/null
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ * Copyright (C) 2004-2013 Sourcefire, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "snort_types.h"
+#include "snort_debug.h"
+
+#include "ftp_module.h"
+#include "ftpp_si.h"
+#include "ftpp_ui_config.h"
+#include "ftpp_return_codes.h"
+#include "ftp_cmd_lookup.h"
+#include "ft_main.h"
+#include "ftp_parse.h"
+#include "ftp_print.h"
+#include "pp_ftp.h"
+#include "profiler.h"
+#include "telnet.h"
+
+#include "stream5/stream_api.h"
+#include "file_api/file_api.h"
+#include "parser.h"
+#include "framework/inspector.h"
+#include "framework/plug_data.h"
+#include "framework/share.h"
+#include "detection/detection_util.h"
+
+int16_t ftp_app_id = SFTARGET_UNKNOWN_PROTOCOL;
+int16_t ftp_data_app_id = SFTARGET_UNKNOWN_PROTOCOL;
+
+static const char* client_key = "ftp_client";
+static const char* server_key = "ftp_server";
+static const char* data_key = "ftp_data";
+
+#ifdef PERF_PROFILING
+static THREAD_LOCAL PreprocStats ftpPerfStats;
+static THREAD_LOCAL PreprocStats ftpdataPerfStats;
+
+static PreprocStats* ftp_get_profile(const char* key)
+{
+    if ( !strcmp(key, server_key) )
+        return &ftpPerfStats;
+
+    if ( !strcmp(key, data_key) )
+        return &ftpdataPerfStats;
+
+    return nullptr;
+}
+#endif
+
+static THREAD_LOCAL SimpleStats ftstats;
+static SimpleStats gftstats;
+
+static FTP_CLIENT_PROTO_CONF* bind_client = nullptr;
+static FTP_SERVER_PROTO_CONF* bind_server = nullptr;
+
+FTP_CLIENT_PROTO_CONF* get_default_ftp_client()
+{ return bind_client; }
+
+FTP_SERVER_PROTO_CONF* get_default_ftp_server()
+{ return bind_server; }
+
+//-------------------------------------------------------------------------
+// implementation stuff
+//-------------------------------------------------------------------------
+
+static void FTPDataProcess(Packet *p, FTP_DATA_SESSION *data_ssn)
+{
+    int status;
+
+    setFileDataPtr((uint8_t *)p->data, (uint16_t)p->dsize);
+
+    status = file_api->file_process(p, (uint8_t *)p->data,
+        (uint16_t)p->dsize, data_ssn->position, data_ssn->direction, false);
+
+    /* Filename needs to be set AFTER the first call to file_process( ) */
+    if (data_ssn->filename && !(data_ssn->packet_flags & FTPDATA_FLG_FILENAME_SET))
+    {
+        file_api->set_file_name(p->flow,
+          (uint8_t *)data_ssn->filename, data_ssn->file_xfer_info);
+        data_ssn->packet_flags |= FTPDATA_FLG_FILENAME_SET;
+    }
+
+    /* Ignore the rest of this transfer if file processing is complete
+     * and preprocessor was configured to ignore ftp-data sessions. */
+    if (!status && data_ssn->data_chan)
+    {
+        stream.set_ignore_direction(p->flow, SSN_DIR_BOTH);
+    }
+}
+
+static int SnortFTPData(Packet *p)
+{
+    FTP_DATA_SESSION *data_ssn;
+
+    if (!p->flow)
+        return -1;
+
+    data_ssn = (FTP_DATA_SESSION *)
+        p->flow->get_application_data(FtpFlowData::flow_id);
+
+    if (!PROTO_IS_FTP_DATA(data_ssn))
+        return -2;
+
+    /* Do this now before splitting the work for rebuilt and raw packets. */
+    if ((p->packet_flags & PKT_PDU_TAIL) || (p->tcph->th_flags & TH_FIN))
+        SetFTPDataEOFDirection(p, data_ssn);
+
+    /*
+     * Raw Packet Processing
+     */
+    if (!(p->packet_flags & PKT_REBUILT_STREAM))
+    {
+        if (!(data_ssn->packet_flags & FTPDATA_FLG_REASSEMBLY_SET))
+        {
+            /* Enable Reassembly */
+            stream.set_reassembly(
+                p->flow,
+                STREAM_FLPOLICY_FOOTPRINT, SSN_DIR_BOTH,
+                STREAM_FLPOLICY_SET_ABSOLUTE);
+
+            data_ssn->packet_flags |= FTPDATA_FLG_REASSEMBLY_SET;
+        }
+
+        if (data_ssn->file_xfer_info == FTPP_FILE_UNKNOWN)
+            return 0;
+
+        if (!FTPDataDirection(p, data_ssn) && FTPDataEOF(data_ssn))
+        {
+            /* flush any remaining data from transmitter. */
+            stream.response_flush_stream(p);
+
+            /* If position is not set to END then no data has been flushed */
+            if ((data_ssn->position != SNORT_FILE_END) ||
+                (data_ssn->position != SNORT_FILE_FULL))
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
+                  "FTP-DATA Processing Raw Packet\n"););
+
+                finalFilePosition(&data_ssn->position);
+                FTPDataProcess(p, data_ssn);
+            }
+        }
+
+        return 0;
+    }
+
+    if (data_ssn->file_xfer_info == FTPP_FILE_UNKNOWN)
+    {
+        /* FTP-Data session is in limbo, we need to lookup the control session
+         * to figure out what to do. */
+
+        FtpFlowData* fd = (FtpFlowData*)stream.get_application_data_from_key(
+            &data_ssn->ftp_key, FtpFlowData::flow_id);
+
+        FTP_SESSION *ftp_ssn = fd ? &fd->session : NULL;
+
+        if (!PROTO_IS_FTP(ftp_ssn))
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
+              "FTP-DATA Invalid FTP_SESSION retrieved durring lookup\n"););
+
+            if (data_ssn->data_chan)
+                stream.set_ignore_direction(p->flow, SSN_DIR_BOTH);
+
+            return -2;
+        }
+
+        switch (ftp_ssn->file_xfer_info)
+        {
+            case FTPP_FILE_UNKNOWN:
+                /* Keep waiting */
+                break;
+
+            case FTPP_FILE_IGNORE:
+                /* This wasn't a file transfer; ignore it */
+                if (data_ssn->data_chan)
+                    stream.set_ignore_direction(p->flow, SSN_DIR_BOTH);
+                return 0;
+
+            default:
+                /* A file transfer was detected. */
+                data_ssn->direction = ftp_ssn->data_xfer_dir;
+                data_ssn->file_xfer_info = ftp_ssn->file_xfer_info;
+                ftp_ssn->file_xfer_info  = 0;
+                data_ssn->filename  = ftp_ssn->filename;
+                ftp_ssn->filename   = NULL;
+                break;
+        }
+    }
+
+    if (!FTPDataDirection(p, data_ssn))
+        return 0;
+
+    if (FTPDataEOFDirection(p, data_ssn))
+        finalFilePosition(&data_ssn->position);
+    else
+        initFilePosition(&data_ssn->position,
+          file_api->get_file_processed_size(p->flow));
+
+    FTPDataProcess(p, data_ssn);
+    return 0;
+}
+
+static inline int InspectClientPacket (Packet* p)
+{
+    if ( ScPafEnabled() )
+        return PacketHasPAFPayload(p);
+
+    return !(p->packet_flags & PKT_STREAM_INSERT);
+}
+
+static int SnortFTP(
+    FTP_SESSION *FTPsession, Packet *p, int iInspectMode)
+{
+    int iRet;
+    PROFILE_VARS;
+
+    if (!FTPsession ||
+         FTPsession->server_conf == NULL ||
+         FTPsession->client_conf == NULL)
+    {
+        return FTPP_INVALID_SESSION;
+    }
+
+    if (!FTPsession->server_conf->check_encrypted_data &&
+        ((FTPsession->encr_state == AUTH_TLS_ENCRYPTED) ||
+         (FTPsession->encr_state == AUTH_SSL_ENCRYPTED) ||
+         (FTPsession->encr_state == AUTH_UNKNOWN_ENCRYPTED)) )
+    {
+        return FTPP_SUCCESS;
+    }
+
+    PREPROC_PROFILE_START(ftpPerfStats);
+
+    if (iInspectMode == FTPP_SI_SERVER_MODE)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
+            "Server packet: %.*s\n", p->dsize, p->data));
+
+        // FIXTHIS breaks target-based non-standard ports
+        //if ( !ScPafEnabled() )
+            /* Force flush of client side of stream  */
+        stream.response_flush_stream(p);
+    }
+    else
+    {
+        if ( !InspectClientPacket(p) )
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
+                "Client packet will be reassembled\n"));
+            PREPROC_PROFILE_END(ftpPerfStats);
+            return FTPP_SUCCESS;
+        }
+        else
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET,
+                "Client packet: rebuilt %s: %.*s\n",
+                (p->packet_flags & PKT_REBUILT_STREAM) ? "yes" : "no",
+                p->dsize, p->data));
+        }
+    }
+
+    iRet = initialize_ftp(FTPsession, p, iInspectMode);
+    if (iRet)
+    {
+        PREPROC_PROFILE_END(ftpPerfStats);
+        return iRet;
+    }
+
+    iRet = check_ftp(FTPsession, p, iInspectMode);
+    if (iRet == FTPP_SUCCESS)
+    {
+        /* Ideally, Detect(), called from do_detection, will look at
+         * the cmd & param buffers, or the rsp & msg buffers.  Current
+         * architecture does not support this...
+         * So, we call do_detection() here.  Otherwise, we'd call it
+         * from inside check_ftp -- each time we process a pipelined
+         * FTP command.
+         */
+        do_detection(p);
+    }
+
+    PREPROC_PROFILE_END(ftpPerfStats);
+#ifdef PERF_PROFILING
+    ft_update_perf(ftpPerfStats);
+#endif
+
+    return iRet;
+}
+
+static int snort_ftp(Packet *p)
+{
+    FTPP_SI_INPUT SiInput;
+    int iInspectMode = FTPP_SI_NO_MODE;
+    FTP_TELNET_SESSION *ft_ssn = NULL;
+
+    /*
+     * Set up the FTPP_SI_INPUT pointer.  This is what the session_inspection()
+     * routines use to determine client and server traffic.  Plus, this makes
+     * the FTPTelnet library very independent from snort.
+     */
+    SetSiInput(&SiInput, p);
+
+    if (p->flow)
+    {
+        ft_ssn = (FTP_TELNET_SESSION*)
+            p->flow->get_application_data(FtpFlowData::flow_id);
+
+        if (ft_ssn != NULL)
+        {
+            SiInput.pproto = ft_ssn->proto;
+
+            if (ft_ssn->proto == FTPP_SI_PROTO_FTP)
+            {
+                FTP_SESSION *ftp_ssn = (FTP_SESSION *)ft_ssn;
+
+                if (SiInput.pdir != FTPP_SI_NO_MODE)
+                {
+                    iInspectMode = SiInput.pdir;
+                }
+                else
+                {
+                    if ((ftp_ssn->server_conf != NULL) &&
+                        ftp_ssn->server_conf->ports[SiInput.sport])
+                    {
+                        iInspectMode = FTPP_SI_SERVER_MODE;
+                    }
+                    else if ((ftp_ssn->server_conf != NULL) &&
+                        ftp_ssn->server_conf->ports[SiInput.dport])
+                    {
+                        iInspectMode = FTPP_SI_CLIENT_MODE;
+                    }
+                    else
+                    {
+                        iInspectMode = FTPGetPacketDir(p);
+                    }
+                }
+            }
+            else
+            {
+                /* XXX - Not FTP or Telnet */
+                p->flow->free_application_data(FtpFlowData::flow_id);
+                return 0;
+            }
+        }
+    }
+
+    if (ft_ssn == NULL)
+    {
+        SiInput.pproto = FTPP_SI_PROTO_UNKNOWN;
+        iInspectMode = FTPP_SI_NO_MODE;
+
+        FTPsessionInspection(p, (FTP_SESSION**)&ft_ssn, &SiInput, &iInspectMode);
+
+        if ( SiInput.pproto != FTPP_SI_PROTO_FTP )
+            return FTPP_INVALID_PROTO;
+    }
+
+    if (ft_ssn != NULL)
+    {
+        switch (SiInput.pproto)
+        {
+            case FTPP_SI_PROTO_FTP:
+                return SnortFTP((FTP_SESSION *)ft_ssn, p, iInspectMode);
+                break;
+        }
+    }
+
+    /* Uh, shouldn't get here  */
+    return FTPP_INVALID_PROTO;
+}
+
+/*
+ * Function: ResetStringFormat (FTP_PARAM_FMT *Fmt)
+ *
+ * Purpose: Recursively sets nodes that allow strings to nodes that check
+ *          for a string format attack within the FTP parameter validation tree
+ *
+ * Arguments: Fmt       => pointer to the FTP Parameter configuration
+ *
+ * Returns: None
+ *
+ */
+static void ResetStringFormat (FTP_PARAM_FMT *Fmt)
+{
+    int i;
+    if (!Fmt)
+        return;
+
+    if (Fmt->type == e_unrestricted)
+        Fmt->type = e_strformat;
+
+    ResetStringFormat(Fmt->optional_fmt);
+    for (i=0;i<Fmt->numChoices;i++)
+    {
+        ResetStringFormat(Fmt->choices[i]);
+    }
+    ResetStringFormat(Fmt->next_param_fmt);
+}
+
+static int ProcessFTPDataChanCmdsList(
+    FTP_SERVER_PROTO_CONF *ServerConf, const FtpCmd* fc)
+{
+    const char* cmd = fc->name.c_str();
+    int iRet;
+
+    FTP_CMD_CONF* FTPCmd = 
+        ftp_cmd_lookup_find(ServerConf->cmd_lookup, cmd, strlen(cmd), &iRet);
+
+        if (FTPCmd == NULL)
+        {
+            /* Add it to the list */
+            // note that struct includes 1 byte for null, so just add len
+            FTPCmd = (FTP_CMD_CONF *)calloc(1, sizeof(FTP_CMD_CONF)+strlen(cmd));
+            if (FTPCmd == NULL)
+            {
+                ParseError("Failed to allocate memory");
+            }
+
+            strcpy(FTPCmd->cmd_name, cmd);
+
+            // FIXIT make sure pulled from server conf when used if not
+            // overridden
+            //FTPCmd->max_param_len = ServerConf->def_max_param_len;
+
+            ftp_cmd_lookup_add(ServerConf->cmd_lookup, cmd,
+                               strlen(cmd), FTPCmd);
+        }
+        if ( fc->flags & CMD_DIR )
+            FTPCmd->dir_response = fc->number;
+
+        if ( fc->flags & CMD_LEN )
+        {
+            FTPCmd->max_param_len = fc->number;
+            FTPCmd->max_param_len_overridden = 1;
+        }
+        if ( fc->flags & CMD_DATA )
+            FTPCmd->data_chan_cmd = 1;
+
+        if ( fc->flags & CMD_XFER )
+            FTPCmd->data_xfer_cmd = 1;
+
+        if ( fc->flags & CMD_PUT )
+            FTPCmd->file_put_cmd = 1;
+
+        if ( fc->flags & CMD_GET )
+            FTPCmd->data_xfer_cmd = 1;
+
+        if ( fc->flags & CMD_CHECK )
+        {
+            FTP_PARAM_FMT *Fmt = FTPCmd->param_format;
+            if (Fmt)
+            {
+                ResetStringFormat(Fmt);
+            }
+            else
+            {
+                Fmt = (FTP_PARAM_FMT *)calloc(1, sizeof(FTP_PARAM_FMT));
+                if (Fmt == NULL)
+                {
+                    ParseError("Failed to allocate memory");
+                }
+
+                Fmt->type = e_head;
+                FTPCmd->param_format = Fmt;
+
+                Fmt = (FTP_PARAM_FMT *)calloc(1, sizeof(FTP_PARAM_FMT));
+                if (Fmt == NULL)
+                {
+                    ParseError("Failed to allocate memory");
+                }
+
+                Fmt->type = e_strformat;
+                FTPCmd->param_format->next_param_fmt = Fmt;
+                Fmt->prev_param_fmt = FTPCmd->param_format;
+            }
+            FTPCmd->check_validity = 1;
+        }
+        if ( fc->flags & CMD_VALID )
+        {
+            char err[1024];
+            ProcessFTPCmdValidity(
+                ServerConf, cmd, fc->format.c_str(), err, sizeof(err));
+        }
+        if ( fc->flags & CMD_ENCR )
+            FTPCmd->encr_cmd = 1;
+
+        if ( fc->flags & CMD_LOGIN )
+            FTPCmd->login_cmd = 1;
+
+    return 0;
+}
+
+//-------------------------------------------------------------------------
+// class stuff
+//-------------------------------------------------------------------------
+
+typedef PlugDataType<FTP_CLIENT_PROTO_CONF> ClientData;
+
+class FtpServer : public Inspector {
+public:
+    FtpServer(FTP_SERVER_PROTO_CONF*);
+    ~FtpServer();
+
+    void configure(SnortConfig*, const char*, char *args);
+    int verify(SnortConfig*);
+    void show(SnortConfig*);
+    void eval(Packet*);
+    void eval_alt(Packet*);
+
+private:
+    FTP_SERVER_PROTO_CONF* ftp_server;
+    ClientData* ftp_client;  // FIXIT delete this when bindings implemented
+};
+
+FtpServer::FtpServer(FTP_SERVER_PROTO_CONF* server)
+{
+    ftp_server = server;
+    ftp_client = nullptr;
+}
+
+FtpServer::~FtpServer ()
+{
+    CleanupFTPServerConf(ftp_server);
+    free(ftp_server);
+
+    if ( ftp_client )
+        // FIXIT make sure CleanupFTPClientConf() is called
+        Share::release(ftp_client);
+}
+
+void FtpServer::configure (
+    SnortConfig* sc, const char*, char*)
+{
+    ftp_client = (ClientData*)Share::acquire(client_key);
+
+    stream.set_service_filter_status(
+        sc, ftp_app_id, PORT_MONITOR_SESSION);
+
+    bind_server = ftp_server;
+    bind_client = ftp_client->data;
+}
+
+int FtpServer::verify(SnortConfig* sc)
+{
+    return FTPCheckConfigs(sc, ftp_server);
+}
+
+void FtpServer::show(SnortConfig*)
+{
+    PrintFTPClientConf(ftp_client->data);
+    PrintFTPServerConf(ftp_server);
+}
+
+void FtpServer::eval_alt(Packet* p)
+{
+    // precondition - what we registered for
+    assert(IsTCP(p) && p->data && p->dsize);
+
+    ++ftstats.total_packets;
+    snort_ftp(p);
+}
+
+void FtpServer::eval(Packet* p)
+{
+    // precondition - what we registered for
+    assert(IsTCP(p));
+
+    if ( file_api->get_max_file_depth() >= 0 )
+    {
+        if ( stream.get_application_protocol_id(p->flow)
+            == ftp_data_app_id )
+        {
+            PROFILE_VARS;
+            PREPROC_PROFILE_START(ftpdataPerfStats);
+            ++ftstats.total_packets;
+            SnortFTPData(p);
+            PREPROC_PROFILE_END(ftpdataPerfStats);
+            return;
+        }
+    }
+    if ( !p->dsize || (p->data == NULL) )
+        return;
+
+    ++ftstats.total_packets;
+    snort_ftp(p);
+}
+
+//-------------------------------------------------------------------------
+// api stuff
+//
+// fc_ = ftp_client
+// fs_ = ftp_server
+//
+// FIXIT fc is a data module but may need to
+// be an inspector with separate bindings.
+//-------------------------------------------------------------------------
+
+static Module* fc_mod_ctor()
+{ return new FtpClientModule; }
+
+// this can be used for both modules
+static void mod_dtor(Module* m)
+{ delete m; }
+
+static PlugData* fc_ctor(Module* m)
+{
+    FtpClientModule* mod = (FtpClientModule*)m;
+    FTP_CLIENT_PROTO_CONF* gc = mod->get_data();
+    unsigned i = 0;
+
+    while ( const BounceTo* bt = mod->get_bounce(i++) )
+    {
+        printf("%s\n", bt->address.c_str());
+        ProcessFTPAllowBounce(
+            gc, (uint8_t*)bt->address.c_str(), bt->address.size(), bt->low, bt->high);
+    }
+    return new ClientData(gc);
+}
+
+static void fc_dtor(PlugData* p)
+{ delete p; }
+
+static const DataApi fc_api =
+{
+    {
+        PT_DATA,
+        client_key,
+        MODAPI_PLUGIN_V0,
+        0,
+        fc_mod_ctor,
+        mod_dtor
+    },
+    fc_ctor,
+    fc_dtor
+};
+
+//-------------------------------------------------------------------------
+
+static Module* fs_mod_ctor()
+{ return new FtpServerModule; }
+
+static void fs_init()
+{
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile(
+        server_key, &ftpPerfStats, 0, &totalPerfStats, ftp_get_profile);
+    RegisterPreprocessorProfile(
+        data_key, &ftpdataPerfStats, 0, &totalPerfStats, ftp_get_profile);
+#endif
+
+    ftp_app_id = AddProtocolReference("ftp");
+    ftp_data_app_id = AddProtocolReference("ftp-data");
+
+    FtpFlowData::init();
+    FtpDataFlowData::init();
+}
+
+static Inspector* fs_ctor(Module* mod)
+{
+    FtpServerModule* fsm = (FtpServerModule*)mod;
+    FTP_SERVER_PROTO_CONF* conf = fsm->get_data();
+    unsigned i = 0;
+
+    while ( const FtpCmd* cmd = fsm->get_cmd(i++) )
+        ProcessFTPDataChanCmdsList(conf, cmd);
+
+    return new FtpServer(conf);
+}
+
+static void fs_dtor(Inspector* p)
+{
+    delete p;
+}
+
+static void fs_sum(void*)
+{
+    sum_stats(&gftstats, &ftstats);
+}
+
+static void fs_stats(void*)
+{
+    show_stats(&gftstats, server_key);
+}
+
+static void fs_reset(void*)
+{
+    memset(&gftstats, 0, sizeof(gftstats));
+}
+
+static const InspectApi fs_api =
+{
+    {
+        PT_INSPECTOR,
+        server_key,
+        INSAPI_PLUGIN_V0,
+        0,
+        fs_mod_ctor,
+        mod_dtor
+    },
+    PRIORITY_SESSION, // or PRIORITY_APPLICATION
+    PROTO_BIT__TCP,
+    fs_init,
+    nullptr, // term
+    fs_ctor,
+    fs_dtor,
+    nullptr, // stop
+    nullptr, // purge
+    fs_sum,
+    fs_stats,
+    fs_reset
+};
+
+#ifdef BUILDING_SO
+SO_PUBLIC const BaseApi* snort_plugins[] =
+{
+    &tn_api,
+    &fc_api.base,
+    &fs_api.base,
+    nullptr
+};
+#else
+const BaseApi* sin_telnet = &tn_api.base;
+const BaseApi* sin_ftp_client = &fc_api.base;
+const BaseApi* sin_ftp_server = &fs_api.base;
+#endif
+
diff --git a/src/service_inspectors/ftp_telnet/ftp_module.cc b/src/service_inspectors/ftp_telnet/ftp_module.cc
new file mode 100644 (file)
index 0000000..ee84278
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License Version 2 as
+ ** published by the Free Software Foundation.  You may not use, modify or
+ ** distribute this program under any other version of the GNU General
+ ** Public License.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+// ftp_module.cc author Russ Combs <rucombs@cisco.com>
+
+#include "ftp_module.h"
+#include <sstream>
+#include "snort_config.h"
+
+using namespace std;
+
+#define FTP_CLIENT "ftp_client"
+#define FTP_SERVER "ftp_server"
+
+//-------------------------------------------------------------------------
+// client stuff
+//-------------------------------------------------------------------------
+
+static const Parameter client_bounce_params[] =
+{
+    { "address", Parameter::PT_ADDR, nullptr, nullptr,
+      "allowed ip address in CIDR format" },
+
+    { "port", Parameter::PT_PORT, "1:", nullptr,
+      "allowed port" },
+
+    { "last_port", Parameter::PT_PORT, "0:", nullptr,
+      "optional allowed range from port to last_port inclusive" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+static const Parameter ftp_client_params[] =
+{
+    { "bounce", Parameter::PT_BOOL, nullptr, "false",
+      "check for bounces" },
+
+    { "bounce_to", Parameter::PT_TABLE, nullptr, client_bounce_params,
+      "allow bounces to CIDRs / ports" },
+
+    { "ignore_telnet_erase_cmds", Parameter::PT_BOOL, nullptr, "false",
+      "ignore erase character and erase line commands when normalizing" },
+
+    { "max_resp_len", Parameter::PT_INT, "-1:", "-1",
+      "maximum ftp response accepted by client" },
+
+    { "telnet_cmds", Parameter::PT_BOOL, nullptr, "false",
+      "detect telnet escape sequences on ftp control channel" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+FtpClientModule::FtpClientModule() :
+    Module(FTP_CLIENT, ftp_client_params)
+{
+    conf = nullptr;
+}
+
+FtpClientModule::~FtpClientModule()
+{
+    if ( conf )
+        delete conf;
+
+    for ( auto p : bounce_to )
+        delete p;
+}
+
+bool FtpClientModule::set(const char*, Value& v, SnortConfig*)
+{
+    if ( v.is("address") )
+        address = v.get_string();
+
+    else if ( v.is("bounce") )
+        conf->bounce = v.get_bool();
+
+    else if ( v.is("ignore_telnet_erase_cmds") )
+        conf->ignore_telnet_erase_cmds = v.get_bool();
+
+    else if ( v.is("last_port") )
+        last_port = v.get_long();
+
+    else if ( v.is("max_resp_len") )
+        conf->max_resp_len = v.get_long();
+
+    else if ( v.is("port") )
+        port = v.get_long();
+
+    else if ( v.is("telnet_cmds") )
+        conf->telnet_cmds = v.get_bool();
+
+    else
+        return false;
+
+    return true;
+}
+
+BounceTo::BounceTo(string& a, Port l, Port h)
+{
+    address = a;
+    low = l;
+    high = h;
+}
+
+const BounceTo* FtpClientModule::get_bounce(unsigned idx)
+{
+    if ( idx < bounce_to.size() )
+        return bounce_to[idx];
+    else
+        return nullptr;
+}
+
+FTP_CLIENT_PROTO_CONF* FtpClientModule::get_data()
+{   
+    FTP_CLIENT_PROTO_CONF* tmp = conf;
+    conf = nullptr;
+    return tmp;
+}
+
+bool FtpClientModule::begin(const char*, int, SnortConfig*)
+{
+    if ( !conf )
+        conf = new FTP_CLIENT_PROTO_CONF;
+
+    address.clear();
+    port = last_port = 0;
+    return true;
+}
+
+bool FtpClientModule::end(const char* fqn, int, SnortConfig*)
+{
+    if ( strcmp(fqn, "ftp_client.bounce_to") )
+        return true;
+
+    if ( last_port && (port > last_port) )
+        return false;
+
+    bounce_to.push_back(new BounceTo(address, port, last_port));
+    return true;
+}
+
+//-------------------------------------------------------------------------
+// server stuff
+//-------------------------------------------------------------------------
+
+FtpCmd::FtpCmd(std::string& key, uint32_t flg, unsigned num)
+{
+    name = key;
+    flags = flg;
+    number = num;
+}
+
+FtpCmd::FtpCmd(std::string& key, std::string& fmt)
+{
+    name = key;
+    format = fmt;
+    flags = CMD_VALID;
+    number = 0;
+}
+
+//-------------------------------------------------------------------------
+
+#define FTP_TELNET_CMD_STR                       \
+        "(ftp) TELNET CMD on FTP Command Channel"
+#define FTP_INVALID_CMD_STR                      \
+        "(ftp) Invalid FTP Command"
+#define FTP_PARAMETER_LENGTH_OVERFLOW_STR        \
+        "(ftp) FTP command parameters were too long"
+#define FTP_MALFORMED_PARAMETER_STR              \
+        "(ftp) FTP command parameters were malformed"
+#define FTP_PARAMETER_STR_FORMAT_STR             \
+        "(ftp) FTP command parameters contained potential string format"
+#define FTP_RESPONSE_LENGTH_OVERFLOW_STR         \
+        "(ftp) FTP response message was too long"
+#define FTP_ENCRYPTED_STR                        \
+        "(ftp) FTP traffic encrypted"
+#define FTP_BOUNCE_STR                           \
+        "(ftp) FTP bounce attempt"
+#define FTP_EVASIVE_TELNET_CMD_STR               \
+        "(ftp) Evasive (incomplete) TELNET CMD on FTP Command Channel"
+
+// FIXIT convert to Lua and use as module default settings
+#if 0
+static const char* DEFAULT_FTP_CONF[] =
+{
+    "hardcoded_config "
+    "def_max_param_len 100 "
+
+    // FIXIT should not have to list commands more than once
+    // eg must appear in at least one *_cmds parameter
+    "ftp_cmds { USER PASS ACCT CWD CDUP SMNT QUIT REIN TYPE STRU"
+              " MODE RETR STOR STOU APPE ALLO REST RNFR RNTO ABOR"
+              " DELE RMD MKD PWD LIST NLST SITE SYST STAT HELP NOOP } "
+    "ftp_cmds { AUTH ADAT PROT PBSZ CONF ENC } "
+    "ftp_cmds { PORT PASV LPRT LPSV EPRT EPSV } "
+    "ftp_cmds { FEAT OPTS } "
+    "ftp_cmds { MDTM REST SIZE MLST MLSD } "
+
+    "alt_max_param_len 0 { CDUP QUIT REIN PASV STOU ABOR PWD SYST NOOP } ",
+
+    "cmd_validity MODE < char SBC > "
+    "cmd_validity STRU < char FRPO [ string ] > "
+    "cmd_validity ALLO < int [ char R int ] > "
+    "cmd_validity TYPE < { char AE [ char NTC ] | char I | char L [ number ] } > "
+    "cmd_validity PORT < host_port > "
+    "cmd_validity LPRT < long_host_port > "
+    "cmd_validity EPRT < extd_host_port > "
+    "cmd_validity EPSV < [ { '1' | '2' | 'ALL' } ] > ",
+
+    "data_chan_cmds { PORT PASV LPRT LPSV EPRT EPSV } "
+    "data_xfer_cmds { RETR STOR STOU APPE LIST NLST } "
+    "file_put_cmds { STOR STOU } "
+    "file_get_cmds { RETR } "
+    "login_cmds { USER PASS } "
+    "dir_cmds { CWD 250 CDUP 250 PWD 257 } "
+    "encr_cmds { AUTH } "
+};
+#endif
+
+//-------------------------------------------------------------------------
+
+static const Parameter ftp_server_alt_max_params[] =
+{
+    { "commands", Parameter::PT_STRING, nullptr, nullptr,
+      "list of commands" },
+
+    { "length", Parameter::PT_INT, "0:", "0",
+      "specify non-default maximum for command" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+static const Parameter ftp_server_validity_params[] =
+{
+    { "command", Parameter::PT_STRING, nullptr, nullptr,
+      "command string" },
+
+    { "format", Parameter::PT_STRING, nullptr, nullptr,
+      "format specification" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+static const Parameter ftp_directory_params[] =
+{
+    { "dir_cmd", Parameter::PT_STRING, nullptr, nullptr,
+      "directory command" },
+
+    { "rsp_code", Parameter::PT_INT, "200:", "0",
+      "expected successful response code for command" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+static const Parameter ftp_server_params[] =
+{
+    { "alt_max_param", Parameter::PT_LIST, nullptr, ftp_server_alt_max_params,
+      "specify non-default maximum command lengths" },
+
+    { "chk_str_fmt", Parameter::PT_STRING, nullptr, nullptr,
+      "check the formatting of the given commands" },
+
+    { "data_chan_cmds", Parameter::PT_STRING, nullptr, nullptr,
+      "check the formatting of the given commands" },
+
+    { "data_xfer_cmds", Parameter::PT_STRING, nullptr, nullptr,
+      "check the formatting of the given commands" },
+
+    { "directory_cmds", Parameter::PT_LIST, nullptr, ftp_directory_params,
+      "specify command-response pairs" },
+
+    { "file_put_cmds", Parameter::PT_STRING, nullptr, nullptr,
+      "check the formatting of the given commands" },
+
+    { "file_get_cmds", Parameter::PT_STRING, nullptr, nullptr,
+      "check the formatting of the given commands" },
+
+    { "encr_cmds", Parameter::PT_STRING, nullptr, nullptr,
+      "check the formatting of the given commands" },
+
+    { "login_cmds", Parameter::PT_STRING, nullptr, nullptr,
+      "check the formatting of the given commands" },
+
+    { "check_encrypted", Parameter::PT_BOOL, nullptr, "false",
+      "check for end of encryption" },
+
+    { "cmd_validity", Parameter::PT_LIST, nullptr, ftp_server_validity_params,
+      "specify command formats" },
+
+    { "def_max_param_len", Parameter::PT_INT, "1:", "100",
+      "default maximum length of commands handled by server; 0 is unlimited" },
+
+    { "encrypted_traffic", Parameter::PT_BOOL, nullptr, "false",
+      "check for encrypted telnet and ftp" },
+
+    { "ftp_cmds", Parameter::PT_STRING, nullptr, nullptr,
+      "specify additional commands supported by server beyond RFC 959" },
+
+    { "ignore_data_chan", Parameter::PT_BOOL, nullptr, "false",
+      "do not inspect ftp data channels" },
+
+    { "ignore_telnet_erase_cmds", Parameter::PT_BOOL, nullptr, "false",
+      "ignore erase character and erase line commands when normalizing" },
+
+    { "ports", Parameter::PT_BIT_LIST, "65535", "21",
+      "specify known ftp ports" },
+
+    { "print_cmds", Parameter::PT_BOOL, nullptr, "false",
+      "print command configurations on start up" },
+
+    { "telnet_cmds", Parameter::PT_BOOL, nullptr, "false",
+      "detect telnet escape sequences of ftp control channel" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+//-------------------------------------------------------------------------
+
+static const RuleMap ftp_server_rules[] =
+{
+    { FTP_TELNET_CMD, FTP_TELNET_CMD_STR },
+    { FTP_INVALID_CMD, FTP_INVALID_CMD_STR },
+    { FTP_PARAMETER_LENGTH_OVERFLOW, FTP_PARAMETER_LENGTH_OVERFLOW_STR },
+    { FTP_MALFORMED_PARAMETER, FTP_MALFORMED_PARAMETER_STR },
+    { FTP_PARAMETER_STR_FORMAT, FTP_PARAMETER_STR_FORMAT_STR },
+    { FTP_RESPONSE_LENGTH_OVERFLOW, FTP_RESPONSE_LENGTH_OVERFLOW_STR },
+    { FTP_ENCRYPTED, FTP_ENCRYPTED_STR },
+    { FTP_BOUNCE, FTP_BOUNCE_STR },
+    { FTP_EVASIVE_TELNET_CMD, FTP_EVASIVE_TELNET_CMD_STR },
+
+    { 0, nullptr }
+};
+
+//-------------------------------------------------------------------------
+
+FtpServerModule::FtpServerModule() :
+    Module(FTP_SERVER, ftp_server_params, ftp_server_rules)
+{
+    conf = nullptr;
+}
+
+FtpServerModule::~FtpServerModule()
+{
+    if ( conf )
+        delete conf;
+
+    for ( auto p : cmds )
+        delete p;
+}
+
+void FtpServerModule::add_commands(
+    Value& v, uint32_t flags, unsigned num)
+{
+    string tok;
+    v.set_first_token();
+        
+    while ( v.get_next_token(tok) )
+        cmds.push_back(new FtpCmd(tok, flags, num));
+}
+
+const FtpCmd* FtpServerModule::get_cmd(unsigned idx)
+{
+    if ( idx < cmds.size() )
+        return cmds[idx];
+    else
+        return nullptr;
+}
+
+//-------------------------------------------------------------------------
+
+bool FtpServerModule::set(const char*, Value& v, SnortConfig*)
+{
+    if ( v.is("check_encrypted") )
+        conf->detect_encrypted = v.get_bool();
+
+    else if ( v.is("chk_str_fmt") )
+        add_commands(v, CMD_CHECK);
+
+    else if ( v.is("command") )
+        names = v.get_string();
+
+    else if ( v.is("data_chan_cmds") )
+        add_commands(v, CMD_DATA);
+
+    else if ( v.is("data_xfer_cmds") )
+        add_commands(v, CMD_XFER);
+
+    else if ( v.is("def_max_param_len") )
+        conf->def_max_param_len = v.get_long();
+
+    else if ( v.is("dir_cmd") )
+        names = v.get_string();
+
+    else if ( v.is("encr_cmds") )
+        add_commands(v, CMD_ENCR);
+
+    else if ( v.is("encrypted_traffic") )
+        conf->check_encrypted_data = v.get_bool();
+
+    else if ( v.is("file_get_cmds") )
+        add_commands(v, CMD_XFER|CMD_GET);
+
+    else if ( v.is("file_put_cmds") )
+        add_commands(v, CMD_XFER|CMD_PUT);
+
+    else if ( v.is("format") )
+        format = v.get_string();
+
+    else if ( v.is("ftp_cmds") )
+        add_commands(v, CMD_ALLOW);
+
+    else if ( v.is("ignore_data_chan") )
+        conf->data_chan = v.get_bool();
+
+    else if ( v.is("ignore_telnet_erase_cmds") )
+        conf->ignore_telnet_erase_cmds = v.get_bool();
+
+    else if ( v.is("length") )
+        number = v.get_long();
+
+    else if ( v.is("login_cmds") )
+        add_commands(v, CMD_LOGIN);
+
+    else if ( v.is("ports") )
+        v.get_bits(conf->ports);
+
+    else if ( v.is("print_cmds") )
+        conf->print_commands = v.get_bool();
+
+    else if ( v.is("rsp_code") )
+        number = v.get_long();
+
+    else if ( v.is("telnet_cmds") )
+        conf->telnet_cmds = v.get_bool();
+
+    return true;
+}
+
+//-------------------------------------------------------------------------
+
+FTP_SERVER_PROTO_CONF* FtpServerModule::get_data()
+{   
+    FTP_SERVER_PROTO_CONF* tmp = conf;
+    conf = nullptr;
+    return tmp;
+}
+
+bool FtpServerModule::begin(const char* fqn, int, SnortConfig*)
+{
+    if ( strcmp(fqn, FTP_SERVER) )
+    {
+        names.clear();
+        format.clear();
+        number = 0;
+        return true;
+    }
+
+    for ( auto p : cmds )
+        delete p;
+
+    cmds.clear();
+
+    if ( !conf )
+        conf = new FTP_SERVER_PROTO_CONF;
+
+    return true;
+}
+
+bool FtpServerModule::end(const char* fqn, int, SnortConfig*)
+{
+    if ( !strcmp(fqn, "ftp_server.cmd_validity") )
+        cmds.push_back(new FtpCmd(names, format));
+
+    else if ( !strcmp(fqn, "ftp_server.alt_max_param") )
+    {
+        Value v(names.c_str());
+        add_commands(v, CMD_LEN, number);
+    }
+    else if ( !strcmp(fqn, "ftp_server.dir_cmds") )
+    {
+        Value v(names.c_str());
+        add_commands(v, CMD_DIR, number);
+    }
+    else if ( strcmp(fqn, FTP_SERVER) )
+        return false;
+
+    return true;
+}
+
diff --git a/src/service_inspectors/ftp_telnet/ftp_module.h b/src/service_inspectors/ftp_telnet/ftp_module.h
new file mode 100644 (file)
index 0000000..a07d305
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License Version 2 as
+ ** published by the Free Software Foundation.  You may not use, modify or
+ ** distribute this program under any other version of the GNU General
+ ** Public License.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+// ftp_module.h author Russ Combs <rucombs@cisco.com>
+
+#ifndef FTP_MODULE_H
+#define FTP_MODULE_H
+
+#include <string>
+#include <vector>
+#include "ftpp_ui_config.h"
+#include "framework/module.h"
+
+#define GID_FTP 125
+
+#define FTP_TELNET_CMD                   1
+#define FTP_INVALID_CMD                  2
+#define FTP_PARAMETER_LENGTH_OVERFLOW    3
+#define FTP_MALFORMED_PARAMETER          4
+#define FTP_PARAMETER_STR_FORMAT         5
+#define FTP_RESPONSE_LENGTH_OVERFLOW     6
+#define FTP_ENCRYPTED                    7
+#define FTP_BOUNCE                       8
+#define FTP_EVASIVE_TELNET_CMD           9
+
+class SnortConfig;
+
+//-------------------------------------------------------------------------
+
+struct BounceTo
+{
+    std::string address;
+    Port low;
+    Port high;
+
+    BounceTo(std::string& address, Port lo, Port hi);
+};
+
+class FtpClientModule : public Module
+{
+public:
+    FtpClientModule();
+    ~FtpClientModule();
+
+    bool set(const char*, Value&, SnortConfig*);
+    bool begin(const char*, int, SnortConfig*);
+    bool end(const char*, int, SnortConfig*);
+
+    FTP_CLIENT_PROTO_CONF* get_data();
+    const BounceTo* get_bounce(unsigned idx);
+
+private:
+    FTP_CLIENT_PROTO_CONF* conf;
+    std::vector<BounceTo*> bounce_to;
+
+    std::string address;
+    Port port, last_port;
+};
+
+//-------------------------------------------------------------------------
+
+#define CMD_LEN    0x0000
+#define CMD_ALLOW  0x0001
+#define CMD_CHECK  0x0002
+#define CMD_DATA   0x0004
+#define CMD_XFER   0x0008
+#define CMD_PUT    0x0010
+#define CMD_GET    0x0020
+#define CMD_LOGIN  0x0040
+#define CMD_ENCR   0x0080
+#define CMD_DIR    0x0100
+#define CMD_VALID  0x0200
+
+struct FtpCmd
+{
+    std::string name;
+    std::string format;
+
+    uint32_t flags;
+    unsigned number;
+
+    FtpCmd(std::string&, uint32_t, unsigned);
+    FtpCmd(std::string&, std::string&);
+};
+
+class FtpServerModule : public Module
+{
+public:
+    FtpServerModule();
+    ~FtpServerModule();
+
+    bool set(const char*, Value&, SnortConfig*);
+    bool begin(const char*, int, SnortConfig*);
+    bool end(const char*, int, SnortConfig*);
+
+    unsigned get_gid() const
+    { return GID_FTP; };
+
+    FTP_SERVER_PROTO_CONF* get_data();
+    const FtpCmd* get_cmd(unsigned idx);
+
+private:
+    void add_commands(Value&, uint32_t flags, unsigned num = 0);
+
+private:
+    FTP_SERVER_PROTO_CONF* conf;
+    std::vector<FtpCmd*> cmds;
+    std::string names;
+    std::string format;
+    unsigned number;
+};
+
+#endif
+
diff --git a/src/service_inspectors/ftp_telnet/ftp_parse.cc b/src/service_inspectors/ftp_telnet/ftp_parse.cc
new file mode 100644 (file)
index 0000000..5e07c6d
--- /dev/null
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ * Copyright (C) 2004-2013 Sourcefire, Inc.
+ * Steven A. Sturges <ssturges@sourcefire.com>
+ * Daniel J. Roelker <droelker@sourcefire.com>
+ * Marc A. Norton <mnorton@sourcefire.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "ftp_parse.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#include "sf_ip.h"
+
+#include "snort_types.h"
+#include "snort_debug.h"
+#include "ftpp_return_codes.h"
+#include "ftpp_ui_config.h"
+#include "ftp_cmd_lookup.h"
+#include "ftp_bounce_lookup.h"
+#include "ftpp_si.h"
+#include "pp_telnet.h"
+#include "pp_ftp.h"
+#include "stream5/stream_api.h"
+#include "profiler.h"
+#include "detection_util.h"
+#include "parser.h"
+#include "mstring.h"
+#include "sfsnprintfappend.h"
+
+#define CONF_SEPARATORS " \t\n\r"
+
+#define ALLOW_BOUNCE      "bounce_to"
+#define CMD_VALIDITY      "cmd_validity"
+
+/*
+ * Optional parameter delimiters
+ */
+#define START_OPT_FMT       "["
+#define END_OPT_FMT         "]"
+#define START_CHOICE_FMT    "{"
+#define END_CHOICE_FMT      "}"
+#define OR_FMT              "|"
+
+/*
+ * The cmd_validity keyword can be used with the format keyword to
+ * restrict data types.  The interpretation is specific to the data
+ * type.  'format' is only supported with date & char data types.
+ *
+ * A few examples:
+ *
+ * 1. Will perform validity checking of an FTP Mode command to
+ * check for one of the characters A, S, B, or C.
+ *
+ * cmd_validity MODE char ASBC
+ *
+ *
+ * 2. Will perform validity checking of an FTP MDTM command to
+ * check for an optional date argument following the format
+ * specified.  The date would uses the YYYYMMDDHHmmss+TZ format.
+ *
+ * cmd_validity MDTM [ date nnnnnnnnnnnnnn[.n[n[n]]] ] string
+ *
+ *
+ * 3. Will perform validity checking of an FTP ALLO command to
+ * check for an integer, then optionally, the letter R and another
+ * integer.
+ *
+ * cmd_validity ALLO int [ char R int ]
+ */
+
+/*
+ * The def_max_param_len & alt_max_param_len keywords can be used to
+ * restrict parameter length for one or more commands.  The space
+ * separated list of commands is enclosed in {}s.
+ *
+ * A few examples:
+ *
+ * 1. Restricts all command parameters to 100 characters
+ *
+ * def_max_param_len 100
+ *
+ * 2. Overrides CWD pathname to 256 characters
+ *
+ * alt_max_param_len 256 { CWD }
+ *
+ * 3. Overrides PWD & SYST to no parameters
+ *
+ * alt_max_param_len 0 { PWD SYST }
+ *
+ */
+
+THREAD_LOCAL char *maxToken = NULL;
+
+static char* mystrtok (char* s, const char* delim)
+{
+    static THREAD_LOCAL char* last = NULL;
+    if ( s || last )
+        last = strtok(s, delim);
+    return last;
+}
+
+static char *NextToken(const char *delimiters)
+{
+    char *retTok = mystrtok(NULL, delimiters);
+    if (retTok > maxToken)
+        return NULL;
+
+    return retTok;
+}
+
+/*
+ * Function: SetOptionalsNext(FTP_PARAM_FMT *ThisFmt,
+ *                            FTP_PARAM_FMT *NextFmt,
+ *                            FTP_PARAM_FMT **choices,
+ *                            int numChoices)
+ *
+ * Purpose: Recursively updates the next value for nodes in the FTP
+ *          Parameter validation tree.
+ *
+ * Arguments: ThisFmt       => pointer to an FTP parameter validation node
+ *            NextFmt       => pointer to an FTP parameter validation node
+ *            choices       => pointer to a list of FTP parameter
+ *                             validation nodes
+ *            numChoices    => the number of nodes in the list
+ *
+ * Returns: int     => an error code integer (0 = success,
+ *                     >0 = non-fatal error, <0 = fatal error)
+ *
+ */
+static void SetOptionalsNext(FTP_PARAM_FMT *ThisFmt, FTP_PARAM_FMT *NextFmt,
+                             FTP_PARAM_FMT **choices, int numChoices)
+{
+    if (!ThisFmt)
+        return;
+
+    if (ThisFmt->optional)
+    {
+        if (ThisFmt->next_param_fmt == NULL)
+        {
+            ThisFmt->next_param_fmt = NextFmt;
+            if (numChoices)
+            {
+                ThisFmt->numChoices = numChoices;
+                ThisFmt->choices = (FTP_PARAM_FMT **)calloc(numChoices, sizeof(FTP_PARAM_FMT *));
+                if (ThisFmt->choices == NULL)
+                {
+                    ParseError("Failed to allocate memory");
+                }
+
+                memcpy(ThisFmt->choices, choices, sizeof(FTP_PARAM_FMT *) * numChoices);
+            }
+        }
+        else
+        {
+            SetOptionalsNext(ThisFmt->next_param_fmt, NextFmt,
+                choices, numChoices);
+        }
+    }
+    else
+    {
+        int i;
+        SetOptionalsNext(ThisFmt->optional_fmt, ThisFmt->next_param_fmt,
+            ThisFmt->choices, ThisFmt->numChoices);
+        for (i=0;i<ThisFmt->numChoices;i++)
+        {
+            SetOptionalsNext(ThisFmt->choices[i], ThisFmt,
+                choices, numChoices);
+        }
+        SetOptionalsNext(ThisFmt->next_param_fmt, ThisFmt,
+            choices, numChoices);
+    }
+}
+
+/*
+ * Function: ProcessDateFormat(FTP_DATE_FMT *dateFmt,
+ *                             FTP_DATE_FMT *LastNonOptFmt,
+ *                             char **format)
+ *
+ * Purpose: Sets the value for nodes in the FTP Date validation tree.
+ *
+ * Arguments: dateFmt       => pointer to an FTP date validation node
+ *            LastNonOptFmt => pointer to previous FTP date validation node
+ *            format        => pointer to next part of date validation string
+ *                             Updated on function exit.
+ *
+ * Returns: int     => an error code integer (0 = success,
+ *                     >0 = non-fatal error, <0 = fatal error)
+ *
+ */
+static int ProcessDateFormat(FTP_DATE_FMT *dateFmt,
+                             FTP_DATE_FMT *LastNonOptFmt,
+                             char **format)
+{
+    char *curr_format;
+    int iRet = FTPP_SUCCESS;
+    int curr_len = 0;
+    char *curr_ch;
+    char *start_ch;
+    FTP_DATE_FMT *CurrFmt = dateFmt;
+
+    if (!dateFmt)
+        return FTPP_INVALID_ARG;
+
+    if (!format || !*format)
+        return FTPP_INVALID_ARG;
+
+    start_ch = curr_ch = *format;
+
+    while (*curr_ch != '\0')
+    {
+        switch (*curr_ch)
+        {
+        case 'n':
+        case 'C':
+        case '+':
+        case '-':
+        case '.':
+            curr_len++;
+            curr_ch++;
+            break;
+        case '[':
+            curr_ch++;
+            if (curr_len > 0)
+            {
+                FTP_DATE_FMT *OptFmt;
+                OptFmt = (FTP_DATE_FMT *)calloc(1, sizeof(FTP_DATE_FMT));
+                if (OptFmt == NULL)
+                {
+                    ParseError("Failed to allocate memory");
+                }
+
+                curr_format = (char *)calloc(curr_len + 1, sizeof(char));
+                if (curr_format == NULL)
+                {
+                    ParseError("Failed to allocate memory");
+                }
+
+                strncpy(curr_format, start_ch, curr_len);
+                CurrFmt->format_string = curr_format;
+                curr_len = 0;
+                CurrFmt->optional = OptFmt;
+                OptFmt->prev = CurrFmt;
+                iRet = ProcessDateFormat(OptFmt, CurrFmt, &curr_ch);
+                if (iRet != FTPP_SUCCESS)
+                {
+                    free(OptFmt);
+                    free(curr_format);
+                    return iRet;
+                }
+            }
+            start_ch = curr_ch;
+            break;
+        case ']':
+            curr_ch++;
+            if (curr_len > 0)
+            {
+                curr_format = (char *)calloc(curr_len + 1, sizeof(char));
+                if (curr_format == NULL)
+                {
+                    ParseError("Failed to allocate memory");
+                }
+
+                strncpy(curr_format, start_ch, curr_len);
+                CurrFmt->format_string = curr_format;
+                curr_len = 0;
+            }
+            *format = curr_ch;
+            return FTPP_SUCCESS;
+            break;
+        case '{':
+            curr_ch++;
+            {
+                FTP_DATE_FMT *NewFmt;
+                NewFmt = (FTP_DATE_FMT *)calloc(1, sizeof(FTP_DATE_FMT));
+                if (NewFmt == NULL)
+                {
+                    ParseError("Failed to allocate memory");
+                }
+
+                if (curr_len > 0)
+                {
+                    curr_format = (char *)calloc(curr_len + 1, sizeof(char));
+                    if (curr_format == NULL)
+                    {
+                        ParseError("Failed to allocate memory");
+                    }
+
+                    strncpy(curr_format, start_ch, curr_len);
+                    CurrFmt->format_string = curr_format;
+                    curr_len = 0;
+                }
+                else
+                {
+                    CurrFmt->empty = 1;
+                }
+                NewFmt->prev = LastNonOptFmt;
+                CurrFmt->next_a = NewFmt;
+                iRet = ProcessDateFormat(NewFmt, CurrFmt, &curr_ch);
+                if (iRet != FTPP_SUCCESS)
+                {
+                    return iRet;
+                }
+                NewFmt = (FTP_DATE_FMT *)calloc(1, sizeof(FTP_DATE_FMT));
+                if (NewFmt == NULL)
+                {
+                    ParseError("Failed to allocate memory");
+                }
+
+                NewFmt->prev = LastNonOptFmt;
+                CurrFmt->next_b = NewFmt;
+                iRet = ProcessDateFormat(NewFmt, CurrFmt, &curr_ch);
+                if (iRet != FTPP_SUCCESS)
+                {
+                    return iRet;
+                }
+
+                NewFmt = (FTP_DATE_FMT *)calloc(1, sizeof(FTP_DATE_FMT));
+                if (NewFmt == NULL)
+                {
+                    ParseError("Failed to allocate memory");
+                }
+
+                NewFmt->prev = CurrFmt;
+                CurrFmt->next = NewFmt;
+                iRet = ProcessDateFormat(NewFmt, CurrFmt, &curr_ch);
+                if (iRet != FTPP_SUCCESS)
+                {
+                    return iRet;
+                }
+            }
+            break;
+        case '}':
+            curr_ch++;
+            if (curr_len > 0)
+            {
+                curr_format = (char *)calloc(curr_len + 1, sizeof(char));
+                if (curr_format == NULL)
+                {
+                    ParseError("Failed to allocate memory");
+                }
+
+                strncpy(curr_format, start_ch, curr_len);
+                CurrFmt->format_string = curr_format;
+                curr_len = 0;
+                *format = curr_ch;
+                return FTPP_SUCCESS;
+            }
+            else
+            {
+                CurrFmt->empty = 1;
+                *format = curr_ch;
+                return FTPP_SUCCESS;
+            }
+            break;
+        case '|':
+            curr_ch++;
+            if (curr_len > 0)
+            {
+                curr_format = (char *)calloc(curr_len + 1, sizeof(char));
+                if (curr_format == NULL)
+                {
+                    ParseError("Failed to allocate memory");
+                }
+
+                strncpy(curr_format, start_ch, curr_len);
+                CurrFmt->format_string = curr_format;
+                curr_len = 0;
+                *format = curr_ch;
+                return FTPP_SUCCESS;
+            }
+            else
+            {
+                CurrFmt->empty = 1;
+                *format = curr_ch;
+                return FTPP_SUCCESS;
+            }
+            break;
+        default:
+            /* Uh, shouldn't get this.  */
+            return FTPP_INVALID_ARG;
+            break;
+        }
+    }
+
+    if (curr_len > 0)
+    {
+        curr_format = (char *)calloc(curr_len + 1, sizeof(char));
+        if (curr_format == NULL)
+        {
+            ParseError("Failed to allocate memory");
+        }
+
+        strncpy(curr_format, start_ch, curr_len);
+        CurrFmt->format_string = curr_format;
+        start_ch = curr_ch;
+        curr_len = 0;
+    }
+
+    /* Should've closed all options & ORs  */
+    *format = curr_ch;
+    return FTPP_SUCCESS;
+}
+
+/*
+ * Function: DoNextFormat(FTP_PARAM_FMT *ThisFmt, int allocated,
+ *                 char *ErrorString, int ErrStrLen)
+ *
+ * Purpose: Processes the next FTP parameter validation node.
+ *
+ * Arguments: ThisFmt       => pointer to an FTP parameter validation node
+ *            allocated     => indicator whether the next node is allocated
+ *            ErrorString   => error string buffer
+ *            ErrStrLen     => the length of the error string buffer
+ *
+ * Returns: int     => an error code integer (0 = success,
+ *                     >0 = non-fatal error, <0 = fatal error)
+ *
+ */
+int DoNextFormat(FTP_PARAM_FMT *ThisFmt, int allocated,
+                 char *ErrorString, int ErrStrLen)
+{
+    FTP_PARAM_FMT *NextFmt;
+    int iRet = FTPP_SUCCESS;
+    char *fmt = NextToken(CONF_SEPARATORS);
+
+    if (!fmt)
+        return FTPP_INVALID_ARG;
+
+    if(!strcmp(END_CMD_FORMAT, fmt))
+    {
+        return FTPP_SUCCESS;
+    }
+
+    if (!strcmp(fmt, OR_FMT))
+    {
+        return FTPP_OR_FOUND;
+    }
+
+    if (!strcmp(fmt, END_OPT_FMT))
+    {
+        return FTPP_OPT_END_FOUND;
+    }
+
+    if (!strcmp(fmt, END_CHOICE_FMT))
+    {
+        return FTPP_CHOICE_END_FOUND;
+    }
+
+    if (!strcmp(fmt, START_OPT_FMT))
+    {
+        NextFmt = (FTP_PARAM_FMT *)calloc(1, sizeof(FTP_PARAM_FMT));
+        if (NextFmt == NULL)
+        {
+            ParseError("Failed to allocate memory");
+        }
+
+        ThisFmt->optional_fmt = NextFmt;
+        NextFmt->optional = 1;
+        NextFmt->prev_param_fmt = ThisFmt;
+        if (ThisFmt->optional)
+            NextFmt->prev_optional = 1;
+        iRet = DoNextFormat(NextFmt, 1, ErrorString, ErrStrLen);
+        if (iRet != FTPP_OPT_END_FOUND)
+        {
+            return FTPP_INVALID_ARG;
+        }
+
+        return DoNextFormat(ThisFmt, 0, ErrorString, ErrStrLen);
+    }
+
+    if (!strcmp(fmt, START_CHOICE_FMT))
+    {
+        int numChoices = 1;
+        do
+        {
+            FTP_PARAM_FMT **tmpChoices = (FTP_PARAM_FMT **)calloc(numChoices, sizeof(FTP_PARAM_FMT *));
+            if (tmpChoices == NULL)
+            {
+                ParseError("Failed to allocate memory");
+            }
+
+            if (ThisFmt->numChoices)
+            {
+                /* explicit check that we have enough room for copy */
+                if (numChoices <= ThisFmt->numChoices)
+                    ParseError("Can't do memcpy - index out of range ");
+
+                memcpy(tmpChoices, ThisFmt->choices,
+                    sizeof(FTP_PARAM_FMT*) * ThisFmt->numChoices);
+            }
+            NextFmt = (FTP_PARAM_FMT *)calloc(1, sizeof(FTP_PARAM_FMT));
+            if (NextFmt == NULL)
+            {
+                ParseError("Failed to allocate memory");
+            }
+
+            ThisFmt->numChoices = numChoices;
+            tmpChoices[numChoices-1] = NextFmt;
+            if (ThisFmt->choices)
+                free(ThisFmt->choices);
+            ThisFmt->choices = tmpChoices;
+            NextFmt->prev_param_fmt = ThisFmt;
+            iRet = DoNextFormat(NextFmt, 1, ErrorString, ErrStrLen);
+            numChoices++;
+        }
+        while (iRet == FTPP_OR_FOUND);
+
+        if (iRet != FTPP_CHOICE_END_FOUND)
+        {
+            return FTPP_INVALID_ARG;
+        }
+
+        return DoNextFormat(ThisFmt, 0, ErrorString, ErrStrLen);
+    }
+
+    if (!allocated)
+    {
+        NextFmt = (FTP_PARAM_FMT *)calloc(1, sizeof(FTP_PARAM_FMT));
+        if (NextFmt == NULL)
+        {
+            ParseError("Failed to allocate memory");
+        }
+
+        NextFmt->prev_param_fmt = ThisFmt;
+        ThisFmt->next_param_fmt = NextFmt;
+        if (ThisFmt->optional)
+            NextFmt->prev_optional = 1;
+    }
+    else
+    {
+        NextFmt = ThisFmt;
+    }
+
+    /* If its not an end cmd, OR, START/END Opt...
+     * it must be a parameter specification.
+     */
+    /* Setup the type & format specs  */
+    if (!strcmp(fmt, F_INT))
+    {
+        NextFmt->type = e_int;
+    }
+    else if (!strcmp(fmt, F_NUMBER))
+    {
+        NextFmt->type = e_number;
+    }
+    else if (!strcmp(fmt, F_CHAR))
+    {
+        char *chars_allowed = NextToken(CONF_SEPARATORS);
+        NextFmt->type = e_char;
+        NextFmt->format.chars_allowed = 0;
+        while (*chars_allowed != 0)
+        {
+            int bitNum = (*chars_allowed & 0x1f);
+            NextFmt->format.chars_allowed |= (1 << (bitNum-1));
+            chars_allowed++;
+        }
+    }
+    else if (!strcmp(fmt, F_DATE))
+    {
+        FTP_DATE_FMT *DateFmt;
+        char *format = NextToken(CONF_SEPARATORS);
+        NextFmt->type = e_date;
+        DateFmt = (FTP_DATE_FMT *)calloc(1, sizeof(FTP_DATE_FMT));
+        if (DateFmt == NULL)
+        {
+            ParseError("Failed to allocate memory");
+        }
+
+        NextFmt->format.date_fmt = DateFmt;
+        iRet = ProcessDateFormat(DateFmt, NULL, &format);
+        if (iRet)
+        {
+            snprintf(ErrorString, ErrStrLen,
+                    "Illegal format %s for token '%s'.",
+                    format, CMD_VALIDITY);
+
+            return FTPP_INVALID_ARG;
+        }
+    }
+    else if ( *fmt == *F_LITERAL )
+    {
+        char* end = strchr(++fmt, *F_LITERAL);
+        int len = end ? end - fmt : 0;
+
+        if ( len < 1 )
+        {
+            snprintf(
+                 ErrorString, ErrStrLen,
+                 "Illegal format '' for token '%s'.", CMD_VALIDITY
+            );
+            return FTPP_INVALID_ARG;
+        }
+        NextFmt->type = e_literal;
+        NextFmt->format.literal = (char *)calloc(1, len+1);
+        if ( !NextFmt->format.literal )
+        {
+            ParseError("Failed to allocate memory");
+        }
+        strncpy(NextFmt->format.literal, fmt, len);
+        NextFmt->format.literal[len] = '\0';
+    }
+    else if (!strcmp(fmt, F_STRING))
+    {
+        NextFmt->type = e_unrestricted;
+    }
+    else if (!strcmp(fmt, F_HOST_PORT))
+    {
+        NextFmt->type = e_host_port;
+    }
+    else if (!strcmp(fmt, F_LONG_HOST_PORT))
+    {
+        NextFmt->type = e_long_host_port;
+    }
+    else if (!strcmp(fmt, F_EXTD_HOST_PORT))
+    {
+        NextFmt->type = e_extd_host_port;
+    }
+    else
+    {
+        snprintf(ErrorString, ErrStrLen,
+                "Illegal format type %s for token '%s'.",
+                fmt, CMD_VALIDITY);
+
+        return FTPP_INVALID_ARG;
+    }
+
+    return DoNextFormat(NextFmt, 0, ErrorString, ErrStrLen);
+}
+
+/*
+ * Function: ProcessFTPCmdValidity(FTP_SERVER_PROTO_CONF *ServerConf,
+ *                              char *ErrorString, int ErrStrLen)
+ *
+ * Purpose: Process the ftp cmd validity configuration.
+ *          This sets the FTP command parameter validation tree.
+ *
+ * Arguments: ServerConf    => pointer to the FTP server configuration
+ *            confOption    => pointer to the name of the option
+ *            ErrorString   => error string buffer
+ *            ErrStrLen     => the length of the error string buffer
+ *
+ * Returns: int     => an error code integer (0 = success,
+ *                     >0 = non-fatal error, <0 = fatal error)
+ *
+ */
+int ProcessFTPCmdValidity(
+    FTP_SERVER_PROTO_CONF *ServerConf, 
+    const char* cmd, const char* fmt,
+    char *ErrorString, int ErrStrLen)
+{
+    FTP_CMD_CONF *FTPCmd = NULL;
+    FTP_PARAM_FMT *HeadFmt = NULL;
+
+    char buf[1024];
+    strncpy(buf, fmt, sizeof(buf));
+    buf[sizeof(buf)-1] = '\0';
+
+    int iRet;
+    fmt = mystrtok(buf, CONF_SEPARATORS);
+
+    if(!fmt)
+    {
+        snprintf(ErrorString, ErrStrLen,
+                "Invalid cmd validity format.");
+
+        return FTPP_FATAL_ERR;
+    }
+
+    if(strcmp(START_CMD_FORMAT, fmt))
+    {
+        snprintf(ErrorString, ErrStrLen,
+                "Must start a cmd validity with the '%s' token.",
+                START_CMD_FORMAT);
+
+        return FTPP_FATAL_ERR;
+    }
+
+    HeadFmt = (FTP_PARAM_FMT *)calloc(1, sizeof(FTP_PARAM_FMT));
+    if (HeadFmt == NULL)
+    {
+        ParseError("Failed to allocate memory");
+    }
+
+    HeadFmt->type = e_head;
+
+    iRet = DoNextFormat(HeadFmt, 0, ErrorString, ErrStrLen);
+
+    /* Need to check to be sure we got a complete command  */
+    if (iRet)
+    {
+        return FTPP_FATAL_ERR;
+    }
+
+    SetOptionalsNext(HeadFmt, NULL, NULL, 0);
+
+    FTPCmd = ftp_cmd_lookup_find(ServerConf->cmd_lookup, cmd,
+                                 strlen(cmd), &iRet);
+    if (FTPCmd == NULL)
+    {
+        /* Add it to the list  */
+        // note that struct includes 1 byte for null, so just add len
+        FTPCmd = (FTP_CMD_CONF *)calloc(1, sizeof(FTP_CMD_CONF)+strlen(cmd));
+        if (FTPCmd == NULL)
+        {
+            ParseError("Failed to allocate memory");
+        }
+
+        strcpy(FTPCmd->cmd_name, cmd);
+
+        FTPCmd->max_param_len = ServerConf->def_max_param_len;
+        ftp_cmd_lookup_add(ServerConf->cmd_lookup, cmd, strlen(cmd), FTPCmd);
+    }
+
+    FTPCmd->check_validity = 1;
+    if (FTPCmd->param_format)
+    {
+        ftpp_ui_config_reset_ftp_cmd_format(FTPCmd->param_format);
+        FTPCmd->param_format = NULL;
+    }
+    FTPCmd->param_format = HeadFmt;
+
+    return FTPP_SUCCESS;
+}
+
+/*
+ * Function:  ParseBounceTo(char *token, FTP_BOUNCE_TO*)
+ *
+ * Purpose: Extract the IP address, masking bits (CIDR format), and
+ *          port information from an FTP Bounce To configuration.
+ *
+ * Arguments: token         => string pointer to the FTP bounce configuration
+ *                             required format:  IP/CIDR,port[,portHi]\0
+ *            FTP_BOUNCE_TO => populated with parsed data
+ *
+ * Returns:   int           => an error code integer (0 = success,
+ *                             >0 = non-fatal error, <0 = fatal error)
+ *
+ */
+int ParseBounceTo(char* token, FTP_BOUNCE_TO* bounce)
+{
+    char **toks;
+    int num_toks;
+    long int port_lo;
+    char *endptr = NULL;
+    sfip_t tmp_ip;
+
+    toks = mSplit(token, ",", 3, &num_toks, 0);
+    if (num_toks < 2)
+        return FTPP_INVALID_ARG;
+
+    if (sfip_pton(toks[0], &tmp_ip) != SFIP_SUCCESS)
+    {
+        mSplitFree(&toks, num_toks);
+        return FTPP_INVALID_ARG;
+    }
+
+    memcpy(&bounce->ip, &tmp_ip, sizeof(sfip_t));
+
+    port_lo = SnortStrtol(toks[1], &endptr, 10);
+    if ((errno == ERANGE) || (*endptr != '\0') ||
+        (port_lo < 0) || (port_lo >= MAXPORTS))
+    {
+        mSplitFree(&toks, num_toks);
+        return FTPP_INVALID_ARG;
+    }
+
+    bounce->portlo = (unsigned short)port_lo;
+
+    if (num_toks == 3)
+    {
+        long int port_hi = SnortStrtol(toks[2], &endptr, 10);
+
+        if ((errno == ERANGE) || (*endptr != '\0') ||
+            (port_hi < 0) || (port_hi >= MAXPORTS))
+        {
+            mSplitFree(&toks, num_toks);
+            return FTPP_INVALID_ARG;
+        }
+
+        if (bounce->portlo != (unsigned short)port_hi)
+        {
+            bounce->porthi = (unsigned short)port_hi;
+            if (bounce->porthi < bounce->portlo)
+            {
+                unsigned short tmp = bounce->porthi;
+                bounce->porthi = bounce->portlo;
+                bounce->portlo = tmp;
+            }
+        }
+    }
+
+    mSplitFree(&toks, num_toks);
+    return FTPP_SUCCESS;
+}
+
+/* TODO: Maybe want to redo this with high-speed searcher for ip/port.
+ * Would be great if we could handle both full addresses and
+ * subnets quickly -- using CIDR format.  Need something that would
+ * return most specific match -- ie a specific host is more specific
+ * than subnet.
+ */
+int ProcessFTPAllowBounce(
+    FTP_CLIENT_PROTO_CONF* ClientConf,
+    const uint8_t* addr, unsigned len,
+    Port low, Port high)
+{
+        FTP_BOUNCE_TO* newBounce = 
+            (FTP_BOUNCE_TO*)calloc(1, sizeof(FTP_BOUNCE_TO));
+
+        if (newBounce == NULL)
+        {
+            ParseError("Failed to allocate memory for Bounce");
+            return FTPP_FATAL_ERR;
+        }
+        sfip_set_raw(&newBounce->ip, addr, len == 4 ? AF_INET : AF_INET6);
+        newBounce->portlo = low;
+        newBounce->porthi = high;
+        int iRet = ftp_bounce_lookup_add(
+            ClientConf->bounce_lookup, IP_ARG(newBounce->ip), newBounce);
+
+        if (iRet)
+        {
+            ParseError("Failed to add configuration for Bounce object '%s'.", ALLOW_BOUNCE);
+            free(newBounce);
+            return FTPP_FATAL_ERR;
+        }
+
+    return FTPP_SUCCESS;
+}
+
diff --git a/src/service_inspectors/ftp_telnet/ftp_parse.h b/src/service_inspectors/ftp_telnet/ftp_parse.h
new file mode 100644 (file)
index 0000000..f4a60a5
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ * Copyright (C) 2004-2013 Sourcefire, Inc.
+ * Steven A. Sturges <ssturges@sourcefire.com>
+ * Daniel J. Roelker <droelker@sourcefire.com>
+ * Marc A. Norton <mnorton@sourcefire.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef FTP_PARSE_H
+#define FTP_PARSE_H
+
+#include "ftpp_ui_config.h"
+#include "decode.h"
+#include "framework/bits.h"
+
+/*
+ * These are the definitions of the parser section delimiting
+ * keywords to configure FtpTelnet.  When one of these keywords
+ * are seen, we begin a new section.
+ */
+#define TELNET        "telnet"
+#define FTP           "ftp"
+#define CLIENT        "client"
+#define SERVER        "server"
+
+/*
+ * Data type keywords
+ */
+#define START_CMD_FORMAT    "<"
+#define END_CMD_FORMAT      ">"
+#define F_INT               "int"
+#define F_NUMBER            "number"
+#define F_CHAR              "char"
+#define F_DATE              "date"
+#define F_LITERAL           "'"
+#define F_STRING            "string"
+#define F_STRING_FMT        "formated_string"
+#define F_HOST_PORT         "host_port"
+#define F_LONG_HOST_PORT    "long_host_port"
+#define F_EXTD_HOST_PORT    "extd_host_port"
+
+int ProcessFTPCmdValidity(
+    FTP_SERVER_PROTO_CONF *ServerConf,
+    const char* cmd, const char* fmt,
+    char *ErrorString, int ErrStrLen);
+
+int ProcessFTPAllowBounce(
+    FTP_CLIENT_PROTO_CONF* ClientConf, const uint8_t* addr, unsigned len, 
+    Port low, Port high);
+
+#endif
+
diff --git a/src/service_inspectors/ftp_telnet/ftp_print.cc b/src/service_inspectors/ftp_telnet/ftp_print.cc
new file mode 100644 (file)
index 0000000..9053e5d
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ * Copyright (C) 2004-2013 Sourcefire, Inc.
+ * Steven A. Sturges <ssturges@sourcefire.com>
+ * Daniel J. Roelker <droelker@sourcefire.com>
+ * Marc A. Norton <mnorton@sourcefire.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "ftp_print.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#include "sf_ip.h"
+
+#include "snort_types.h"
+#include "snort_debug.h"
+#include "ftpp_return_codes.h"
+#include "ftpp_ui_config.h"
+#include "ftp_cmd_lookup.h"
+#include "ftp_bounce_lookup.h"
+#include "ftpp_si.h"
+#include "pp_telnet.h"
+#include "pp_ftp.h"
+#include "ft_main.h"
+#include "ftp_parse.h"
+#include "stream5/stream_api.h"
+#include "profiler.h"
+#include "detection_util.h"
+#include "parser.h"
+#include "mstring.h"
+#include "sfsnprintfappend.h"
+
+int PrintConfOpt(bool on, const char* Option)
+{
+    LogMessage("    %s: %s\n", Option, on ? "ON" : "OFF");
+    return FTPP_SUCCESS;
+}
+
+/*
+ * Function: PrintFormatDate(FTP_DATE_FMT *DateFmt)
+ *
+ * Purpose: Recursively prints the FTP date validation tree
+ *
+ * Arguments: DateFmt       => pointer to the date format node
+ *
+ * Returns: None
+ *
+ */
+static void PrintFormatDate(char *buf, FTP_DATE_FMT *DateFmt)
+{
+    FTP_DATE_FMT *OptChild;
+
+    if (!DateFmt->empty)
+        sfsnprintfappend(buf, BUF_SIZE, "%s", DateFmt->format_string);
+
+    if (DateFmt->optional)
+    {
+        OptChild = DateFmt->optional;
+        sfsnprintfappend(buf, BUF_SIZE, "[");
+        PrintFormatDate(buf, OptChild);
+        sfsnprintfappend(buf, BUF_SIZE, "]");
+    }
+
+    if (DateFmt->next_a)
+    {
+        if (DateFmt->next_b)
+            sfsnprintfappend(buf, BUF_SIZE, "{");
+        OptChild = DateFmt->next_a;
+        PrintFormatDate(buf, OptChild);
+        if (DateFmt->next_b)
+        {
+            sfsnprintfappend(buf, BUF_SIZE, "|");
+            OptChild = DateFmt->next_b;
+            PrintFormatDate(buf, OptChild);
+            sfsnprintfappend(buf, BUF_SIZE, "}");
+        }
+    }
+
+    if (DateFmt->next)
+        PrintFormatDate(buf, DateFmt->next);
+}
+
+/*
+ * Function: PrintCmdFmt(FTP_PARAM_FMT *CmdFmt)
+ *
+ * Purpose: Recursively prints the FTP command parameter validation tree
+ *
+ * Arguments: CmdFmt       => pointer to the parameter validation node
+ *
+ * Returns: None
+ *
+ */
+static void PrintCmdFmt(char *buf, FTP_PARAM_FMT *CmdFmt)
+{
+    FTP_PARAM_FMT *OptChild;
+
+    switch(CmdFmt->type)
+    {
+    case e_int:
+        sfsnprintfappend(buf, BUF_SIZE, " %s", F_INT);
+        break;
+    case e_number:
+        sfsnprintfappend(buf, BUF_SIZE, " %s", F_NUMBER);
+        break;
+    case e_char:
+        sfsnprintfappend(buf, BUF_SIZE, " %s 0x%x", F_CHAR,
+            CmdFmt->format.chars_allowed);
+        break;
+    case e_date:
+        sfsnprintfappend(buf, BUF_SIZE, " %s", F_DATE);
+        PrintFormatDate(buf, CmdFmt->format.date_fmt);
+        break;
+    case e_literal:
+        sfsnprintfappend(buf, BUF_SIZE, " %s 0x%x", F_LITERAL,
+                CmdFmt->format.literal);
+        break;
+    case e_unrestricted:
+        sfsnprintfappend(buf, BUF_SIZE, " %s", F_STRING);
+        break;
+    case e_strformat:
+        sfsnprintfappend(buf, BUF_SIZE, " %s", F_STRING_FMT);
+        break;
+    case e_host_port:
+        sfsnprintfappend(buf, BUF_SIZE, " %s", F_HOST_PORT);
+        break;
+    case e_long_host_port:
+        sfsnprintfappend(buf, BUF_SIZE, " %s", F_LONG_HOST_PORT);
+        break;
+    case e_extd_host_port:
+        sfsnprintfappend(buf, BUF_SIZE, " %s", F_EXTD_HOST_PORT);
+        break;
+    case e_head:
+        break;
+    default:
+        break;
+    }
+
+    if (CmdFmt->optional_fmt)
+    {
+        OptChild = CmdFmt->optional_fmt;
+        sfsnprintfappend(buf, BUF_SIZE, "[");
+        PrintCmdFmt(buf, OptChild);
+        sfsnprintfappend(buf, BUF_SIZE, "]");
+    }
+
+    if (CmdFmt->numChoices)
+    {
+        int i;
+        sfsnprintfappend(buf, BUF_SIZE, "{");
+        for (i=0;i<CmdFmt->numChoices;i++)
+        {
+            if (i)
+                sfsnprintfappend(buf, BUF_SIZE, "|");
+            OptChild = CmdFmt->choices[i];
+            PrintCmdFmt(buf, OptChild);
+        }
+        sfsnprintfappend(buf, BUF_SIZE, "}");
+    }
+
+    if (CmdFmt->next_param_fmt && CmdFmt->next_param_fmt->prev_optional)
+        PrintCmdFmt(buf, CmdFmt->next_param_fmt);
+
+}
+
+int PrintFTPClientConf(FTP_CLIENT_PROTO_CONF *ClientConf)
+{
+    FTP_BOUNCE_TO *FTPBounce;
+    int iErr;
+
+    LogMessage("ftp_client:\n");
+
+    PrintConfOpt(ClientConf->bounce, "Check for Bounce Attacks");
+    PrintConfOpt(ClientConf->telnet_cmds, "Check for Telnet Cmds");
+    PrintConfOpt(ClientConf->ignore_telnet_erase_cmds, "Ignore Telnet Cmd Operations");
+    LogMessage("    Max Response Length: %d\n", ClientConf->max_resp_len);
+
+    FTPBounce = ftp_bounce_lookup_first(ClientConf->bounce_lookup, &iErr);
+    if (FTPBounce)
+    {
+        LogMessage("    Allow FTP bounces to:\n");
+
+        while (FTPBounce)
+        {
+            char *addr_str;
+            char bits_str[5];
+            uint8_t bits;
+            bits_str[0] = '\0';
+
+            addr_str = sfip_to_str(&FTPBounce->ip);
+            bits = (uint8_t)FTPBounce->ip.bits;
+            if (((FTPBounce->ip.family == AF_INET) && (bits != 32)) ||
+                ((FTPBounce->ip.family == AF_INET6) && (bits != 128)))
+            {
+                snprintf(bits_str, sizeof(bits_str), "/%u", bits);
+            }
+            if (FTPBounce->porthi)
+            {
+                LogMessage("        Address: %s%s, Ports: %d-%d\n",
+                            addr_str, bits_str[0] ? bits_str : "",
+                            FTPBounce->portlo, FTPBounce->porthi);
+            }
+            else
+            {
+                LogMessage("        Address: %s%s, Port: %d\n",
+                            addr_str, bits_str[0] ? bits_str : "",
+                            FTPBounce->portlo);
+            }
+
+            FTPBounce = ftp_bounce_lookup_next(ClientConf->bounce_lookup, &iErr);
+        }
+    }
+
+    return FTPP_SUCCESS;
+}
+
+int PrintFTPServerConf(FTP_SERVER_PROTO_CONF *ServerConf)
+{
+    const char* spaf = "";
+    char buf[BUF_SIZE+1];
+    int iCtr;
+    int iRet;
+    FTP_CMD_CONF *FTPCmd;
+
+    if(!ServerConf)
+    {
+        return FTPP_INVALID_ARG;
+    }
+
+    LogMessage("ftp_server:\n");
+
+    if ( ScPafEnabled() )
+        spaf = " (PAF)";
+
+    memset(buf, 0, BUF_SIZE+1);
+    snprintf(buf, BUF_SIZE, "    Ports%s: ", spaf);
+
+    /*
+     * Print out all the applicable ports.
+     */
+    for(iCtr = 0; iCtr < MAXPORTS; iCtr++)
+    {
+        if(ServerConf->ports[iCtr])
+        {
+            sfsnprintfappend(buf, BUF_SIZE, "%d ", iCtr);
+        }
+    }
+
+    LogMessage("%s\n", buf);
+
+    PrintConfOpt(ServerConf->telnet_cmds, "Check for Telnet Cmds");
+    PrintConfOpt(ServerConf->ignore_telnet_erase_cmds, "Ignore Telnet Cmd Operations");
+    LogMessage("    Identify open data channels: %s\n",
+        ServerConf->data_chan ? "YES" : "NO");
+    PrintConfOpt(ServerConf->detect_encrypted, "Check for Encrypted Traffic");
+    LogMessage("    Continue to check encrypted data: %s\n",
+        ServerConf->check_encrypted_data ? "YES" : "NO");
+
+    if (ServerConf->print_commands)
+    {
+        LogMessage("    FTP Commands:\n");
+
+        FTPCmd = ftp_cmd_lookup_first(ServerConf->cmd_lookup, &iRet);
+        while (FTPCmd != NULL)
+        {
+            memset(buf, 0, BUF_SIZE+1);
+            snprintf(buf, BUF_SIZE, "        %s { %d ",
+                FTPCmd->cmd_name, FTPCmd->max_param_len);
+#ifdef PRINT_DEFAULT_CONFIGS
+            if (FTPCmd->data_chan_cmd)
+                snprintf(buf, BUF_SIZE, "%s data_chan ");
+            if (FTPCmd->data_xfer_cmd)
+                snprintf(buf, BUF_SIZE, "%s data_xfer ");
+            if (FTPCmd->encr_cmd)
+                snprintf(buf, BUF_SIZE, "%s encr ");
+#endif
+
+            if (FTPCmd->check_validity)
+            {
+                FTP_PARAM_FMT *CmdFmt = FTPCmd->param_format;
+                while (CmdFmt != NULL)
+                {
+                    PrintCmdFmt(buf, CmdFmt);
+
+                    CmdFmt = CmdFmt->next_param_fmt;
+                }
+            }
+            LogMessage("%s}\n", buf);
+            FTPCmd = ftp_cmd_lookup_next(ServerConf->cmd_lookup, &iRet);
+        }
+    }
+
+    return FTPP_SUCCESS;
+}
+
diff --git a/src/service_inspectors/ftp_telnet/ftp_print.h b/src/service_inspectors/ftp_telnet/ftp_print.h
new file mode 100644 (file)
index 0000000..2b552ec
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ * Copyright (C) 2004-2013 Sourcefire, Inc.
+ * Steven A. Sturges <ssturges@sourcefire.com>
+ * Daniel J. Roelker <droelker@sourcefire.com>
+ * Marc A. Norton <mnorton@sourcefire.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef FTP_PRINT_H
+#define FTP_PRINT_H
+
+#include "ftpp_ui_config.h"
+
+int PrintConfOpt(bool on, const char* Option);
+int PrintFTPClientConf(FTP_CLIENT_PROTO_CONF*);
+int PrintFTPServerConf(FTP_SERVER_PROTO_CONF*);
+
+#endif
+
diff --git a/src/service_inspectors/ftp_telnet/telnet.cc b/src/service_inspectors/ftp_telnet/telnet.cc
new file mode 100644 (file)
index 0000000..693bced
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ * Copyright (C) 2004-2013 Sourcefire, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "snort_types.h"
+#include "snort_debug.h"
+
+#include "telnet.h"
+#include "pp_telnet.h"
+#include "ftpp_si.h"
+#include "ftpp_ui_config.h"
+#include "ftpp_return_codes.h"
+#include "ft_main.h"
+#include "ftp_print.h"
+#include "telnet_module.h"
+#include "profiler.h"
+#include "stream5/stream_api.h"
+#include "file_api/file_api.h"
+#include "parser.h"
+#include "framework/inspector.h"
+#include "utils/sfsnprintfappend.h"
+
+int16_t telnet_app_id = SFTARGET_UNKNOWN_PROTOCOL;
+
+static const char* tn_name = "telnet";
+
+#ifdef PERF_PROFILING
+static THREAD_LOCAL PreprocStats telnetPerfStats;
+
+static PreprocStats* tn_get_profile(const char* key)
+{
+    if ( !strcmp(key, tn_name) )
+        return &telnetPerfStats;
+
+    return nullptr;
+}
+#endif
+
+static THREAD_LOCAL SimpleStats tnstats;
+static SimpleStats gtnstats;
+
+//-------------------------------------------------------------------------
+// implementation
+//-------------------------------------------------------------------------
+
+static int TelnetCheckConfigs(SnortConfig* sc, void* pData)
+{
+    TELNET_PROTO_CONF* telnet_config = (TELNET_PROTO_CONF*)pData;
+
+    if ((telnet_config->ayt_threshold > 0) &&
+            !telnet_config->normalize)
+    {
+         ErrorMessage("WARNING: Telnet Configuration Check: using an "
+                 "AreYouThere threshold requires telnet normalization to be "
+                 "turned on.\n");
+    }
+    if ( telnet_config->detect_encrypted &&
+            !telnet_config->normalize)
+    {
+        ErrorMessage("WARNING: Telnet Configuration Check: checking for "
+                "encrypted traffic requires telnet normalization to be turned "
+                "on.\n");
+    }
+
+    _addPortsToStream5(sc, telnet_config->ports, 0);
+
+    return 0;
+}
+
+static int SnortTelnet(TELNET_PROTO_CONF* telnet_config, TELNET_SESSION *Telnetsession,
+                Packet *p, int iInspectMode)
+{
+    int iRet;
+    PROFILE_VARS;
+
+    if (!Telnetsession)
+    {
+        return FTPP_NONFATAL_ERR;
+    }
+
+    if (Telnetsession->encr_state && !Telnetsession->telnet_conf->check_encrypted_data)
+    {
+        return FTPP_SUCCESS;
+    }
+
+    PREPROC_PROFILE_START(telnetPerfStats);
+
+    if (!telnet_config->normalize)
+    {
+        do_detection(p);
+    }
+    else
+    {
+        iRet = normalize_telnet(
+            Telnetsession, p, iInspectMode, FTPP_APPLY_TNC_ERASE_CMDS);
+
+        if ((iRet == FTPP_SUCCESS) || (iRet == FTPP_NORMALIZED))
+        {
+            do_detection(p);
+        }
+    }
+    PREPROC_PROFILE_END(telnetPerfStats);
+#ifdef PERF_PROFILING
+    ft_update_perf(telnetPerfStats);
+#endif
+
+    return FTPP_SUCCESS;
+}
+
+static int snort_telnet(TELNET_PROTO_CONF* GlobalConf, Packet *p)
+{
+    FTPP_SI_INPUT SiInput;
+    int iInspectMode = FTPP_SI_NO_MODE;
+    FTP_TELNET_SESSION *ft_ssn = NULL;
+
+    /*
+     * Set up the FTPP_SI_INPUT pointer.  This is what the session_inspection()
+     * routines use to determine client and server traffic.  Plus, this makes
+     * the FTPTelnet library very independent from snort.
+     */
+    SetSiInput(&SiInput, p);
+
+    if (p->flow)
+    {
+        ft_ssn = (FTP_TELNET_SESSION *)
+            p->flow->get_application_data(FtpFlowData::flow_id);
+
+        if (ft_ssn != NULL)
+        {
+            SiInput.pproto = ft_ssn->proto;
+
+            if (ft_ssn->proto == FTPP_SI_PROTO_TELNET)
+            {
+                TELNET_SESSION *telnet_ssn = (TELNET_SESSION *)ft_ssn;
+
+                if (SiInput.pdir != FTPP_SI_NO_MODE)
+                {
+                    iInspectMode = SiInput.pdir;
+                }
+                else
+                {
+                    if ((telnet_ssn->telnet_conf != NULL) &&
+                        (telnet_ssn->telnet_conf->ports[SiInput.sport]))
+                    {
+                        iInspectMode = FTPP_SI_SERVER_MODE;
+                    }
+                    else if ((telnet_ssn->telnet_conf != NULL) &&
+                             (telnet_ssn->telnet_conf->ports[SiInput.dport]))
+                    {
+                        iInspectMode = FTPP_SI_CLIENT_MODE;
+                    }
+                }
+            }
+            else
+            {
+                p->flow->free_application_data(FtpFlowData::flow_id);
+                return 0;
+            }
+        }
+    }
+
+    if (GlobalConf == NULL)
+        return 0;
+
+    if (ft_ssn == NULL)
+    {
+        SiInput.pproto = FTPP_SI_PROTO_UNKNOWN;
+        iInspectMode = FTPP_SI_NO_MODE;
+
+        TelnetsessionInspection(p, GlobalConf, (TELNET_SESSION**)&ft_ssn, &SiInput, &iInspectMode);
+
+        if ( SiInput.pproto != FTPP_SI_PROTO_TELNET )
+            return FTPP_INVALID_PROTO;
+    }
+
+    if (ft_ssn != NULL)
+    {
+        switch (SiInput.pproto)
+        {
+            case FTPP_SI_PROTO_TELNET:
+                return SnortTelnet(GlobalConf, (TELNET_SESSION *)ft_ssn, p, iInspectMode);
+                break;
+        }
+    }
+
+    /* Uh, shouldn't get here  */
+    return FTPP_INVALID_PROTO;
+}
+
+/*
+ * Function: PrintTelnetConf(TELNET_PROTO_CONF *TelnetConf,
+ *                          char *Option)
+ *
+ * Purpose: Prints the telnet configuration
+ *
+ * Arguments: TelnetConf    => pointer to the telnet configuration
+ *
+ * Returns: int     => an error code integer (0 = success,
+ *                     >0 = non-fatal error, <0 = fatal error)
+ *
+ */
+static int PrintTelnetConf(TELNET_PROTO_CONF *TelnetConf)
+{
+    char buf[BUF_SIZE+1];
+    int iCtr;
+
+    if(!TelnetConf)
+    {
+        return FTPP_INVALID_ARG;
+    }
+
+    LogMessage("    TELNET CONFIG:\n");
+    memset(buf, 0, BUF_SIZE+1);
+    snprintf(buf, BUF_SIZE, "      Ports: ");
+
+    /*
+     * Print out all the applicable ports.
+     */
+    for(iCtr = 0; iCtr < MAXPORTS; iCtr++)
+    {
+        if(TelnetConf->ports[iCtr])
+        {
+            sfsnprintfappend(buf, BUF_SIZE, "%d ", iCtr);
+        }
+    }
+
+    LogMessage("%s\n", buf);
+    LogMessage("      Are You There Threshold: %d\n",
+        TelnetConf->ayt_threshold);
+    LogMessage("      Normalize: %s\n", TelnetConf->normalize ? "YES" : "NO");
+    LogMessage("      Detect Anomalies: %s\n",
+            TelnetConf->detect_anomalies ? "YES" : "NO");
+    PrintConfOpt(TelnetConf->detect_encrypted, "Check for Encrypted Traffic");
+    LogMessage("      Continue to check encrypted data: %s\n",
+        TelnetConf->check_encrypted_data ? "YES" : "NO");
+
+    return FTPP_SUCCESS;
+}
+
+//-------------------------------------------------------------------------
+// class stuff
+//-------------------------------------------------------------------------
+
+class Telnet : public Inspector {
+public:
+    Telnet(TELNET_PROTO_CONF*);
+    ~Telnet();
+
+    void configure(SnortConfig*, const char*, char*);
+    int verify(SnortConfig*);
+    void show(SnortConfig*);
+    void eval(Packet*);
+
+private:
+    TELNET_PROTO_CONF* config;
+};
+
+Telnet::Telnet(TELNET_PROTO_CONF* pc)
+{
+    config = pc;
+}
+
+Telnet::~Telnet()
+{
+    if ( config )
+        delete config;
+}
+
+void Telnet::configure(SnortConfig* sc, const char*, char*)
+{
+    stream.set_service_filter_status(
+        sc, telnet_app_id, PORT_MONITOR_SESSION);
+}
+
+int Telnet::verify(SnortConfig* sc)
+{
+    return TelnetCheckConfigs(sc, config);
+}
+
+void Telnet::show(SnortConfig*)
+{
+    PrintTelnetConf(config);
+}
+
+void Telnet::eval(Packet* p)
+{
+    // precondition - what we registered for
+    assert(IsTCP(p) && p->dsize && p->data);
+
+    ++tnstats.total_packets;
+    snort_telnet(config, p);
+}
+
+//-------------------------------------------------------------------------
+// api stuff
+//-------------------------------------------------------------------------
+
+static void tn_init()
+{
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile(
+        tn_name, &telnetPerfStats, 0, &totalPerfStats, tn_get_profile);
+#endif
+
+    telnet_app_id = AddProtocolReference(tn_name);
+    TelnetFlowData::init();
+}
+
+static Inspector* tn_ctor(Module* m)
+{
+    TelnetModule* mod = (TelnetModule*)m;
+    return new Telnet(mod->get_data());
+}
+
+static void tn_dtor(Inspector* p)
+{
+    delete p;
+}
+
+static void tn_sum(void*)
+{
+    sum_stats(&gtnstats, &tnstats);
+}
+
+static void tn_stats(void*)
+{
+    show_stats(&gtnstats, tn_name);
+}
+
+static void tn_reset(void*)
+{
+    memset(&gtnstats, 0, sizeof(gtnstats));
+}
+
+// exported in ftp.cc
+const InspectApi tn_api =
+{
+    {
+        PT_INSPECTOR,
+        tn_name,
+        INSAPI_PLUGIN_V0,
+        0,
+        nullptr,
+        nullptr
+    },
+    PRIORITY_APPLICATION,
+    PROTO_BIT__TCP,
+    tn_init,
+    nullptr, // term
+    tn_ctor,
+    tn_dtor,
+    nullptr, // stop
+    nullptr, // purge
+    tn_sum,
+    tn_stats,
+    tn_reset
+};
+
diff --git a/src/service_inspectors/ftp_telnet/telnet.h b/src/service_inspectors/ftp_telnet/telnet.h
new file mode 100644 (file)
index 0000000..d7299fe
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef TELNET_H
+#define TELNET_H
+
+#include "framework/inspector.h"
+
+extern int16_t telnet_app_id;
+extern const InspectApi tn_api;
+
+#endif
+
diff --git a/src/service_inspectors/ftp_telnet/telnet_module.cc b/src/service_inspectors/ftp_telnet/telnet_module.cc
new file mode 100644 (file)
index 0000000..7378612
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License Version 2 as
+ ** published by the Free Software Foundation.  You may not use, modify or
+ ** distribute this program under any other version of the GNU General
+ ** Public License.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+// telnet_module.cc author Russ Combs <rucombs@cisco.com>
+
+#include "telnet_module.h"
+#include <sstream>
+#include "snort_config.h"
+
+using namespace std;
+
+//-------------------------------------------------------------------------
+// telnet module
+//-------------------------------------------------------------------------
+
+#define TELNET_AYT_OVERFLOW_STR                  \
+        "(telnet) Consecutive Telnet AYT commands beyond threshold"
+#define TELNET_ENCRYPTED_STR                     \
+        "(telnet) Telnet traffic encrypted"
+#define TELNET_SB_NO_SE_STR                      \
+        "(telnet) Telnet Subnegotiation Begin Command without Subnegotiation End"
+
+static const Parameter telnet_params[] =
+{
+    { "ayt_attack_thresh", Parameter::PT_INT, "-1:", "-1",
+      "alert on this number of consecutive telnet AYT commands" },
+
+    { "check_encrypted", Parameter::PT_BOOL, nullptr, "false",
+      "check for end of encryption" },
+
+    { "detect_anomalies", Parameter::PT_BOOL, nullptr, "false",
+      "help" },
+
+    { "encrypted_traffic", Parameter::PT_BOOL, nullptr, "false",
+      "check for encrypted telnet and ftp" },
+
+    { "normalize", Parameter::PT_BOOL, nullptr, "false",
+      "eliminate escape sequences" },
+
+    { "ports", Parameter::PT_BIT_LIST, "65535", "23",
+      "specify known telnet ports" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+static const RuleMap telnet_rules[] =
+{
+    { TELNET_AYT_OVERFLOW, TELNET_AYT_OVERFLOW_STR },
+    { TELNET_ENCRYPTED, TELNET_ENCRYPTED_STR },
+    { TELNET_SB_NO_SE, TELNET_SB_NO_SE_STR },
+
+    { 0, nullptr }
+};
+
+TelnetModule::TelnetModule() :
+    Module("telnet", telnet_params, telnet_rules)
+{
+    conf = nullptr;
+}
+
+TelnetModule::~TelnetModule()
+{
+    if ( conf )
+        delete conf;
+}
+
+bool TelnetModule::set(const char*, Value& v, SnortConfig*)
+{
+    if ( v.is("ayt_attack_thresh") )
+        conf->ayt_threshold = v.get_long();
+
+    else if ( v.is("check_encrypted") )
+        conf->detect_encrypted = v.get_bool();
+
+    else if ( v.is("detect_anomalies") )
+        conf->detect_anomalies = v.get_bool();
+
+    else if ( v.is("encrypted_traffic") )
+        conf->check_encrypted_data = v.get_bool();
+
+    else if ( v.is("normalize") )
+        conf->normalize = v.get_bool();
+
+    else if ( v.is("ports") )
+        v.get_bits(conf->ports);
+
+    return true;
+}
+
+TELNET_PROTO_CONF* TelnetModule::get_data()
+{   
+    TELNET_PROTO_CONF* tmp = conf;
+    conf = nullptr;
+    return tmp;
+}
+
+bool TelnetModule::begin(const char*, int, SnortConfig*)
+{
+    conf = new TELNET_PROTO_CONF;
+    return true;
+}
+
+bool TelnetModule::end(const char*, int, SnortConfig*)
+{
+    return true;
+}
+
diff --git a/src/service_inspectors/ftp_telnet/telnet_module.h b/src/service_inspectors/ftp_telnet/telnet_module.h
new file mode 100644 (file)
index 0000000..c73d72c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License Version 2 as
+ ** published by the Free Software Foundation.  You may not use, modify or
+ ** distribute this program under any other version of the GNU General
+ ** Public License.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+// telnet_module.h author Russ Combs <rucombs@cisco.com>
+
+#ifndef TELNET_MODULE_H
+#define TELNET_MODULE_H
+
+#include "ftpp_ui_config.h"
+#include "framework/module.h"
+
+#define GID_TELNET 126
+
+#define TELNET_AYT_OVERFLOW   1
+#define TELNET_ENCRYPTED      2
+#define TELNET_SB_NO_SE       3
+
+class SnortConfig;
+
+class TelnetModule : public Module
+{
+public:
+    TelnetModule();
+    ~TelnetModule();
+
+    bool set(const char*, Value&, SnortConfig*);
+    bool begin(const char*, int, SnortConfig*);
+    bool end(const char*, int, SnortConfig*);
+
+    unsigned get_gid() const
+    { return GID_TELNET; };
+
+    TELNET_PROTO_CONF* get_data();
+
+private:
+    TELNET_PROTO_CONF* conf;
+};
+
+#endif
+