#include "packet_tracer/packet_tracer.h"
#include "parser/parse_rule.h"
#include "profiler/profiler.h"
+#include "pub_sub/opportunistic_tls_event.h"
#include "stream/stream.h"
#include "utils/util.h"
void FtpDataFlowData::handle_expected(Packet* p)
{
if (!p->flow->service)
+ {
p->flow->set_service(p, fd_svc_name);
+
+ FtpDataFlowData* fd =
+ (FtpDataFlowData*)p->flow->get_flow_data(FtpDataFlowData::inspector_id);
+ if (fd and fd->in_tls)
+ {
+ OpportunisticTlsEvent evt(p, fd_svc_name);
+ DataBus::publish(OPPORTUNISTIC_TLS_EVENT, evt, p->flow);
+ }
+ }
}
void FtpDataFlowData::handle_eof(Packet* p)
{ CountType::SUM, "total_bytes", "total number of bytes processed" },
{ CountType::NOW, "concurrent_sessions", "total concurrent FTP sessions" },
{ CountType::MAX, "max_concurrent_sessions", "maximum concurrent FTP sessions" },
+ { CountType::SUM, "start_tls", "total STARTTLS events generated" },
+ { CountType::SUM, "ssl_search_abandoned", "total SSL search abandoned" },
+ { CountType::SUM, "ssl_srch_abandoned_early", "total SSL search abandoned too soon" },
{ CountType::END, nullptr, nullptr }
};
if ( fc->flags & CMD_ENCR )
FTPCmd->encr_cmd = true;
+ if ( fc->flags & CMD_PROT )
+ FTPCmd->prot_cmd = true;
+
if ( fc->flags & CMD_LOGIN )
FTPCmd->login_cmd = true;
if ( !idx && !strcmp(fqn, "ftp_server") )
{
+ cmds.emplace_back(new FtpCmd("PROT", CMD_PROT, 0));
for( auto cmd : cmds)
{
if ( FTPP_SUCCESS != ProcessFTPDataChanCmdsList(conf, cmd) )
#define CMD_DIR 0x0100
#define CMD_VALID 0x0200
#define CMD_REST 0x0400
+#define CMD_PROT 0x0800
struct FtpCmd
{
#define PROTO_IS_FTP_DATA(ssn) FTPP_SI_IS_PROTO(ssn, FTPP_SI_PROTO_FTP_DATA)
#define PROTO_IS_TELNET(ssn) FTPP_SI_IS_PROTO(ssn, FTPP_SI_PROTO_TELNET)
-#define FTP_FLG_MALWARE (1<<0)
+#define FTP_FLG_MALWARE 0x01
+#define FTP_FLG_SEARCH_ABANDONED 0x02
+#define FTP_PROTP_CMD_ISSUED 0x04
+#define FTP_PROTP_CMD_ACCEPT 0x08
typedef struct s_FTP_TELNET_SESSION
{
static unsigned inspector_id;
FTP_DATA_SESSION session;
bool eof_handled = false;
+ bool in_tls = false;
};
#define FTPDATA_FLG_REASSEMBLY_SET (1<<0)
PegCount total_bytes;
PegCount concurrent_sessions;
PegCount max_concurrent_sessions;
+ PegCount starttls;
+ PegCount ssl_search_abandoned;
+ PegCount ssl_search_abandoned_too_soon;
};
struct TelnetStats
bool file_get_cmd;
bool encr_cmd;
bool login_cmd;
+ bool prot_cmd;
int dir_response;
FTP_PARAM_FMT* param_format;
#include "hash/hash_key_operations.h"
#include "file_api/file_service.h"
#include "protocols/packet.h"
+#include "pub_sub/opportunistic_tls_event.h"
#include "stream/stream.h"
#include "utils/util.h"
* If we saw all the other dat for this channel
* session->data_chan_state should be NO_STATE. */
}
+ else if (session->flags & FTP_PROTP_CMD_ISSUED)
+ {
+ if (rsp_code == 200)
+ {
+ session->flags &= ~FTP_PROTP_CMD_ISSUED;
+ session->flags |= FTP_PROTP_CMD_ACCEPT;
+ }
+
+ }
else if (session->data_chan_state & DATA_CHAN_PASV_CMD_ISSUED)
{
if (ftp_cmd_pipe_index == session->data_chan_index)
if (session->flags & FTP_FLG_MALWARE)
session->datassn = ftpdata;
+ if (p->flow->flags.data_decrypted and
+ (session->flags & FTP_PROTP_CMD_ACCEPT))
+ fd->in_tls = true;
+
/* Call into Streams to mark data channel as ftp-data */
result = Stream::set_snort_protocol_id_expected(
p, PktType::TCP, IpProtocol::TCP,
if (session->flags & FTP_FLG_MALWARE)
session->datassn = ftpdata;
+ if (p->flow->flags.data_decrypted and
+ (session->flags & FTP_PROTP_CMD_ACCEPT))
+ fd->in_tls = true;
+
/* Call into Streams to mark data channel as ftp-data */
result = Stream::set_snort_protocol_id_expected(
p, PktType::TCP, IpProtocol::TCP,
}
} /* if (session->server_conf->data_chan) */
+ if ((session->encr_state == AUTH_TLS_CMD_ISSUED or session->encr_state == AUTH_SSL_CMD_ISSUED)
+ and rsp_code == 234)
+ {
+ OpportunisticTlsEvent event(p, p->flow->service);
+ DataBus::publish(OPPORTUNISTIC_TLS_EVENT, event, p->flow);
+ ++ftstats.starttls;
+ if (session->flags & FTP_FLG_SEARCH_ABANDONED)
+ ++ftstats.ssl_search_abandoned_too_soon;
+ }
+
if (session->server_conf->detect_encrypted)
{
switch (session->encr_state)
ftpssn->encr_state = AUTH_UNKNOWN_CMD_ISSUED;
}
}
+ else if (CmdConf->prot_cmd)
+ {
+ if (req->param_begin && (req->param_size > 0) &&
+ ((req->param_begin[0] == 'P') || (req->param_begin[0] == 'p')))
+ {
+ ftpssn->flags &= ~FTP_PROTP_CMD_ACCEPT;
+ ftpssn->flags |= FTP_PROTP_CMD_ISSUED;
+ }
+ }
+ else if (CmdConf->login_cmd and !p->flow->flags.data_decrypted and
+ !(ftpssn->flags & FTP_FLG_SEARCH_ABANDONED))
+ {
+ ftpssn->flags |= FTP_FLG_SEARCH_ABANDONED;
+ DataBus::publish(SSL_SEARCH_ABANDONED, p);
+ ++ftstats.ssl_search_abandoned;
+ }
+
if (CmdConf->check_validity)
{
const char* next_param = nullptr;
{ CountType::SUM, "sessions", "total smtp sessions" },
{ CountType::NOW, "concurrent_sessions", "total concurrent smtp sessions" },
{ CountType::MAX, "max_concurrent_sessions", "maximum concurrent smtp sessions" },
+ { CountType::SUM, "start_tls", "total STARTTLS events generated" },
+ { CountType::SUM, "ssl_search_abandoned", "total SSL search abandoned" },
+ { CountType::SUM, "ssl_srch_abandoned_early", "total SSL search abandoned too soon" },
{ CountType::SUM, "b64_attachments", "total base64 attachments decoded" },
{ CountType::SUM, "b64_decoded_bytes", "total base64 decoded bytes" },
{ CountType::SUM, "qp_attachments", "total quoted-printable attachments decoded" },
{
OpportunisticTlsEvent event(p, p->flow->service);
DataBus::publish(OPPORTUNISTIC_TLS_EVENT, event, p->flow);
+ ++smtpstats.starttls;
+ if (smtp_ssn->state_flags & SMTP_FLAG_ABANDON_EVT)
+ ++smtpstats.ssl_search_abandoned_too_soon;
}
break;
{
smtp_ssn->state_flags |= SMTP_FLAG_ABANDON_EVT;
DataBus::publish(SSL_SEARCH_ABANDONED, p);
+ ++smtpstats.ssl_search_abandoned;
}
break;
PegCount sessions;
PegCount concurrent_sessions;
PegCount max_concurrent_sessions;
+ PegCount starttls;
+ PegCount ssl_search_abandoned;
+ PegCount ssl_search_abandoned_too_soon;
snort::MimeStats mime_stats;
};
SslFlowData::SslFlowData() : FlowData(inspector_id)
{
memset(&session, 0, sizeof(session));
+ finalize_info = {};
sslstats.concurrent_sessions++;
if(sslstats.max_concurrent_sessions < sslstats.concurrent_sessions)
sslstats.max_concurrent_sessions = sslstats.concurrent_sessions;
void handle(DataEvent&, Flow* flow) override
{
+ SslFlowData* fd = new SslFlowData;
+ fd->finalize_info.orig_flag = flow->flags.trigger_finalize_event;
+ fd->finalize_info.switch_in = true;
+ flow->set_flow_data(fd);
flow->flags.trigger_finalize_event = true;
}
};
{
FinalizePacketEvent* fp_event = (FinalizePacketEvent*)&e;
const Packet* pkt = fp_event->get_packet();
-
- pkt->flow->flags.trigger_finalize_event = false;
- pkt->flow->set_proxied();
- pkt->flow->set_service(const_cast<Packet*>(pkt), s_name);
+ SslFlowData* fd = (SslFlowData*)pkt->flow->get_flow_data(SslFlowData::inspector_id);
+ if (fd and fd->finalize_info.switch_in)
+ {
+ pkt->flow->flags.trigger_finalize_event = fd->finalize_info.orig_flag;
+ fd->finalize_info.switch_in = false;
+ pkt->flow->set_proxied();
+ pkt->flow->set_service(const_cast<Packet*>(pkt), s_name);
+ }
}
};
public:
static unsigned inspector_id;
SSLData session;
+ struct {
+ bool orig_flag : 1;
+ bool switch_in : 1;
+ } finalize_info;
};
//Function: API to get the ssl flow data from the packet flow.
SSLData* get_ssl_session_data(snort::Flow* flow);