From: Russ Combs Date: Tue, 22 Apr 2014 14:42:57 +0000 (-0400) Subject: modularization that should have been committed previously X-Git-Tag: 3.0.0-233~1554^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d2c30c1d7f192b26bf12e81ceb7756ee34b1c64c;p=thirdparty%2Fsnort3.git modularization that should have been committed previously --- diff --git a/src/network_inspectors/stream5/icmp_config.h b/src/network_inspectors/stream5/icmp_config.h new file mode 100644 index 000000000..a3a35ba11 --- /dev/null +++ b/src/network_inspectors/stream5/icmp_config.h @@ -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 index 000000000..7ad14ad8e --- /dev/null +++ b/src/network_inspectors/stream5/ip_config.h @@ -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 index 000000000..6e57f2bc1 --- /dev/null +++ b/src/network_inspectors/stream5/tcp_config.h @@ -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 index 000000000..4f3a083bc --- /dev/null +++ b/src/network_inspectors/stream5/udp_config.h @@ -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 index 000000000..0737b4a20 --- /dev/null +++ b/src/service_inspectors/ftp_telnet/ftp.cc @@ -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 +#include +#include +#include + +#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;inumChoices;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 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 index 000000000..ee84278b3 --- /dev/null +++ b/src/service_inspectors/ftp_telnet/ftp_module.cc @@ -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 + +#include "ftp_module.h" +#include +#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 index 000000000..a07d305f5 --- /dev/null +++ b/src/service_inspectors/ftp_telnet/ftp_module.h @@ -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 + +#ifndef FTP_MODULE_H +#define FTP_MODULE_H + +#include +#include +#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 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 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 index 000000000..5e07c6d6d --- /dev/null +++ b/src/service_inspectors/ftp_telnet/ftp_parse.cc @@ -0,0 +1,854 @@ +/* + * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved. + * Copyright (C) 2004-2013 Sourcefire, Inc. + * Steven A. Sturges + * Daniel J. Roelker + * Marc A. Norton + * + * 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 +#include +#include +#include +#include +#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;inumChoices;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 index 000000000..f4a60a565 --- /dev/null +++ b/src/service_inspectors/ftp_telnet/ftp_parse.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved. + * Copyright (C) 2004-2013 Sourcefire, Inc. + * Steven A. Sturges + * Daniel J. Roelker + * Marc A. Norton + * + * 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 index 000000000..9053e5d33 --- /dev/null +++ b/src/service_inspectors/ftp_telnet/ftp_print.cc @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved. + * Copyright (C) 2004-2013 Sourcefire, Inc. + * Steven A. Sturges + * Daniel J. Roelker + * Marc A. Norton + * + * 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 +#include +#include +#include +#include +#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;inumChoices;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 index 000000000..2b552ec0c --- /dev/null +++ b/src/service_inspectors/ftp_telnet/ftp_print.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved. + * Copyright (C) 2004-2013 Sourcefire, Inc. + * Steven A. Sturges + * Daniel J. Roelker + * Marc A. Norton + * + * 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 index 000000000..693bced3d --- /dev/null +++ b/src/service_inspectors/ftp_telnet/telnet.cc @@ -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 +#include +#include +#include + +#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 +}; + diff --git a/src/service_inspectors/ftp_telnet/telnet.h b/src/service_inspectors/ftp_telnet/telnet.h new file mode 100644 index 000000000..d7299fe6c --- /dev/null +++ b/src/service_inspectors/ftp_telnet/telnet.h @@ -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 index 000000000..7378612a6 --- /dev/null +++ b/src/service_inspectors/ftp_telnet/telnet_module.cc @@ -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 + +#include "telnet_module.h" +#include +#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 index 000000000..c73d72c8f --- /dev/null +++ b/src/service_inspectors/ftp_telnet/telnet_module.h @@ -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 + +#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 +