--- /dev/null
+/****************************************************************************
+ *
+ * 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
+
--- /dev/null
+/****************************************************************************
+ *
+ * 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
+
--- /dev/null
+/****************************************************************************
+ *
+ * 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
+
--- /dev/null
+/****************************************************************************
+ *
+ * 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
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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(>nstats, &tnstats);
+}
+
+static void tn_stats(void*)
+{
+ show_stats(>nstats, tn_name);
+}
+
+static void tn_reset(void*)
+{
+ memset(>nstats, 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
+};
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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
+