From: Russ Combs Date: Thu, 11 Jun 2015 19:27:40 +0000 (-0400) Subject: Squashed commit of the following: X-Git-Tag: 3.0.0-233~951 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e65e7d7eb62c1b89f767d4435e202022cebb4a24;p=thirdparty%2Fsnort3.git Squashed commit of the following: commit f0852f3da95431b5706963c66463a05b529dbdaa Author: Bhagyashree Bantwal Date: Mon Jun 8 10:30:38 2015 -0400 port ssl preprocessor from snort --- diff --git a/configure.ac b/configure.ac index 5ea639392..93320a31e 100644 --- a/configure.ac +++ b/configure.ac @@ -979,6 +979,7 @@ src/service_inspectors/pop/Makefile \ src/service_inspectors/rpc_decode/Makefile \ src/service_inspectors/smtp/Makefile \ src/service_inspectors/ssh/Makefile \ +src/service_inspectors/ssl/Makefile \ src/service_inspectors/wizard/Makefile \ src/protocols/Makefile \ src/search_engines/Makefile \ diff --git a/lua/snort.lua b/lua/snort.lua index ef790e0c6..4a26a2e61 100644 --- a/lua/snort.lua +++ b/lua/snort.lua @@ -75,6 +75,7 @@ pop = { } port_scan = { } rpc_decode = { } ssh = { } +ssl = { } telnet = { } -- use http_inspect or new_http_inspect (incomplete) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7a869e09a..ce85c4769 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,6 +63,7 @@ if (STATIC_INSPECTORS) rpc_decode smtp ssh + ssl wizard ) endif() diff --git a/src/Makefile.am b/src/Makefile.am index f2ac16883..8c0dd9ed3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,7 @@ service_inspectors/pop/libpop.a \ service_inspectors/rpc_decode/librpc_decode.a \ service_inspectors/smtp/libsmtp.a \ service_inspectors/ssh/libssh.a \ +service_inspectors/ssl/libssl.a \ service_inspectors/wizard/libwizard.a endif diff --git a/src/service_inspectors/CMakeLists.txt b/src/service_inspectors/CMakeLists.txt index 786708d7f..176de5c73 100644 --- a/src/service_inspectors/CMakeLists.txt +++ b/src/service_inspectors/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(pop) add_subdirectory(rpc_decode) add_subdirectory(smtp) add_subdirectory(ssh) +add_subdirectory(ssl) add_subdirectory(wizard) if (STATIC_INSPECTORS) diff --git a/src/service_inspectors/Makefile.am b/src/service_inspectors/Makefile.am index 2ade44f5d..acaad9bc2 100644 --- a/src/service_inspectors/Makefile.am +++ b/src/service_inspectors/Makefile.am @@ -27,6 +27,7 @@ pop \ rpc_decode \ smtp \ ssh \ +ssl \ wizard AM_CXXFLAGS = @AM_CXXFLAGS@ diff --git a/src/service_inspectors/service_inspectors.cc b/src/service_inspectors/service_inspectors.cc index d2ab393f5..f0642376c 100644 --- a/src/service_inspectors/service_inspectors.cc +++ b/src/service_inspectors/service_inspectors.cc @@ -28,6 +28,8 @@ extern const BaseApi* sin_http_global; extern const BaseApi* sin_http_inspect; #ifdef STATIC_INSPECTORS +extern const BaseApi* ips_ssl_state; +extern const BaseApi* ips_ssl_version; extern const BaseApi* sin_bo; extern const BaseApi* sin_dns; extern const BaseApi* sin_ftp_client; @@ -39,6 +41,7 @@ extern const BaseApi* sin_pop; extern const BaseApi* sin_rpc_decode; extern const BaseApi* sin_smtp; extern const BaseApi* sin_ssh; +extern const BaseApi* sin_ssl; extern const BaseApi* sin_telnet; extern const BaseApi* sin_wizard; #endif @@ -49,6 +52,8 @@ const BaseApi* service_inspectors[] = sin_http_inspect, #ifdef STATIC_INSPECTORS + ips_ssl_state, + ips_ssl_version, sin_bo, sin_dns, sin_ftp_client, @@ -60,6 +65,7 @@ const BaseApi* service_inspectors[] = sin_rpc_decode, sin_smtp, sin_ssh, + sin_ssl, sin_telnet, sin_wizard, #endif diff --git a/src/service_inspectors/ssh/ssh.cc b/src/service_inspectors/ssh/ssh.cc index 44dd924c4..6bc9848c6 100644 --- a/src/service_inspectors/ssh/ssh.cc +++ b/src/service_inspectors/ssh/ssh.cc @@ -63,9 +63,6 @@ static unsigned int ProcessSSHProtocolVersionExchange(SSH_PROTO_CONF*, SSHData*, static unsigned int ProcessSSHKeyExchange(SSHData*, Packet*, uint8_t, unsigned int); static unsigned int ProcessSSHKeyInitExchange(SSHData*, Packet*, uint8_t, unsigned int); -/* Ultimately calls SnortEventqAdd - Arguments are: gid, sid, rev, classification, priority, message, rule_info */ - unsigned SshFlowData::flow_id = 0; SSHData* SetNewSSHData(Packet* p) diff --git a/src/service_inspectors/ssl/CMakeLists.txt b/src/service_inspectors/ssl/CMakeLists.txt new file mode 100644 index 000000000..c93406d24 --- /dev/null +++ b/src/service_inspectors/ssl/CMakeLists.txt @@ -0,0 +1,18 @@ + +set( FILE_LIST + ssl_inspector.cc + ssl_inspector.h + ssl_config.h + ssl_module.cc + ssl_module.h + ips_ssl_state.cc + ips_ssl_version.cc +) + +if (STATIC_INSPECTORS) + add_library( ssl STATIC ${FILE_LIST}) + +else (STATIC_INSPECTORS) + add_shared_library(ssl inspectors ${FILE_LIST}) + +endif (STATIC_INSPECTORS) diff --git a/src/service_inspectors/ssl/Makefile.am b/src/service_inspectors/ssl/Makefile.am new file mode 100644 index 000000000..9832890cf --- /dev/null +++ b/src/service_inspectors/ssl/Makefile.am @@ -0,0 +1,24 @@ +AUTOMAKE_OPTIONS=foreign no-dependencies + +file_list = \ +ssl_config.h \ +ssl_inspector.cc \ +ssl_inspector.h \ +ssl_module.cc \ +ssl_module.h \ +ips_ssl_state.cc \ +ips_ssl_version.cc + +if STATIC_INSPECTORS +noinst_LIBRARIES = libssl.a +libssl_a_SOURCES = $(file_list) +else +shlibdir = $(pkglibdir)/inspectors +shlib_LTLIBRARIES = libssl.la +libssl_la_CXXFLAGS = $(AM_CXXFLAGS) -DBUILDING_SO +libssl_la_LDFLAGS = -export-dynamic -shared +libssl_la_SOURCES = $(file_list) +endif + +AM_CXXFLAGS = @AM_CXXFLAGS@ + diff --git a/src/service_inspectors/ssl/ips_ssl_state.cc b/src/service_inspectors/ssl/ips_ssl_state.cc new file mode 100644 index 000000000..fd3abe18c --- /dev/null +++ b/src/service_inspectors/ssl/ips_ssl_state.cc @@ -0,0 +1,295 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 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. +//-------------------------------------------------------------------------- +// ips_ssl_state.cc author Bhagyashree Bantwal + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ssl_inspector.h" +#include "protocols/ssl.h" +#include "framework/ips_option.h" +#include "framework/module.h" +#include "framework/parameter.h" +#include "detection/detect.h" +#include "detection/detection_defines.h" +#include "hash/sfhashfcn.h" +#include "time/profiler.h" + +//------------------------------------------------------------------------- +// ssl_state +//------------------------------------------------------------------------- + +#define s_name "ssl_state" +#define s_help \ + "detection option for ssl state" + +static THREAD_LOCAL ProfileStats sslStateRuleOptionPerfStats; + +struct SslStateRuleOptionData +{ + int flags; + int mask; +}; + +class SslStateOption : public IpsOption +{ +public: + SslStateOption(const SslStateRuleOptionData& c) : + IpsOption(s_name) + { ssod = c; } + + uint32_t hash() const override; + bool operator==(const IpsOption&) const override; + + int eval(Cursor&, Packet*) override; + +private: + SslStateRuleOptionData ssod; +}; + +//------------------------------------------------------------------------- +// ssl_state option +//------------------------------------------------------------------------- + +uint32_t SslStateOption::hash() const +{ + uint32_t a,b,c; + + a = ssod.flags; + b = ssod.mask; + c = 0; + + mix_str(a,b,c,get_name()); + final (a,b,c); + + return c; +} + +bool SslStateOption::operator==(const IpsOption& ips) const +{ + if ( strcmp(get_name(), ips.get_name()) ) + return false; + + const SslStateOption& rhs = (SslStateOption&)ips; + + if ( (ssod.flags == rhs.ssod.flags) && + (ssod.mask == rhs.ssod.mask) ) + return true; + + return false; +} + +int SslStateOption::eval(Cursor&, Packet* pkt) +{ + SSLData* sd; + + PROFILE_VARS; + MODULE_PROFILE_START(sslStateRuleOptionPerfStats); + + if ( !(pkt->packet_flags & PKT_REBUILT_STREAM) && !pkt->is_full_pdu() ) + { + MODULE_PROFILE_END(sslStateRuleOptionPerfStats); + return DETECTION_OPTION_NO_MATCH; + } + + if (!pkt->flow) + { + MODULE_PROFILE_END(sslStateRuleOptionPerfStats); + return DETECTION_OPTION_NO_MATCH; + } + + sd = get_ssl_session_data(pkt->flow); + + if (!sd) + { + MODULE_PROFILE_END(sslStateRuleOptionPerfStats); + return DETECTION_OPTION_NO_MATCH; + } + + if ((ssod.flags & sd->ssn_flags) ^ ssod.mask) + { + MODULE_PROFILE_END(sslStateRuleOptionPerfStats); + return DETECTION_OPTION_MATCH; + } + + MODULE_PROFILE_END(sslStateRuleOptionPerfStats); + + return DETECTION_OPTION_NO_MATCH; +} + +//------------------------------------------------------------------------- +// ssl_state module +//------------------------------------------------------------------------- + +static const Parameter s_params[] = +{ + { "client_hello", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for client hello" }, + + { "server_hello", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for server hello" }, + + { "client_keyx", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for client keyx" }, + + { "server_keyx", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for server keyx" }, + + { "unknown", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for unknown record" }, + + { "!client_hello", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for records that are not client hello" }, + + { "!server_hello", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for records that are not server hello" }, + + { "!client_keyx", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for records that are not client keyx" }, + + { "!server_keyx", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for records that are not server keyx" }, + + { "!unknown", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for records that are not unknown" }, + + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +class SslStateModule : public Module +{ +public: + SslStateModule() : Module(s_name, s_help, s_params) { } + + bool begin(const char*, int, SnortConfig*) override; + bool set(const char*, Value&, SnortConfig*) override; + + ProfileStats* get_profile() const override + { return &sslStateRuleOptionPerfStats; } + + SslStateRuleOptionData ssod; +}; + +bool SslStateModule::begin(const char*, int, SnortConfig*) +{ + ssod.flags = 0; + ssod.mask = 0; + return true; +} + +bool SslStateModule::set(const char*, Value& v, SnortConfig*) +{ + if ( v.is("client_hello") ) + ssod.flags |= SSL_CUR_CLIENT_HELLO_FLAG; + + else if ( v.is("server_hello") ) + ssod.flags |= SSL_CUR_SERVER_HELLO_FLAG; + + else if ( v.is("client_keyx") ) + ssod.flags |= SSL_CUR_CLIENT_KEYX_FLAG; + + else if ( v.is("server_keyx") ) + ssod.flags |= SSL_CUR_SERVER_KEYX_FLAG; + + else if ( v.is("unknown") ) + ssod.flags |= SSL_UNKNOWN_FLAG; + + else if ( v.is("!client_hello") ) + { + ssod.flags |= SSL_CUR_CLIENT_HELLO_FLAG; + ssod.mask |= SSL_CUR_CLIENT_HELLO_FLAG; + } + else if ( v.is("!server_hello") ) + { + ssod.flags |= SSL_CUR_SERVER_HELLO_FLAG; + ssod.mask |= SSL_CUR_SERVER_HELLO_FLAG; + } + else if ( v.is("!client_keyx") ) + { + ssod.flags |= SSL_CUR_CLIENT_KEYX_FLAG; + ssod.mask |= SSL_CUR_CLIENT_KEYX_FLAG; + } + else if ( v.is("!server_keyx") ) + { + ssod.flags |= SSL_CUR_SERVER_KEYX_FLAG; + ssod.mask |= SSL_CUR_SERVER_KEYX_FLAG; + } + else if ( v.is("!unknown") ) + { + ssod.flags |= SSL_UNKNOWN_FLAG; + ssod.mask |= SSL_UNKNOWN_FLAG; + } + else + return false; + + return true; +} + +//------------------------------------------------------------------------- +// ssl_state api methods +//------------------------------------------------------------------------- + +static Module* ssl_state_mod_ctor() +{ + return new SslStateModule; +} + +static void mod_dtor(Module* m) +{ + delete m; +} + +static IpsOption* ssl_state_ctor(Module* p, OptTreeNode*) +{ + SslStateModule* m = (SslStateModule*)p; + return new SslStateOption(m->ssod); +} + +static void opt_dtor(IpsOption* p) +{ + delete p; +} + +static const IpsApi ssl_state_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 0, + API_RESERVED, + API_OPTIONS, + s_name, + s_help, + ssl_state_mod_ctor, + mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + ssl_state_ctor, + opt_dtor, + nullptr +}; + +const BaseApi* ips_ssl_state = &ssl_state_api.base; + diff --git a/src/service_inspectors/ssl/ips_ssl_version.cc b/src/service_inspectors/ssl/ips_ssl_version.cc new file mode 100644 index 000000000..879a2ff7b --- /dev/null +++ b/src/service_inspectors/ssl/ips_ssl_version.cc @@ -0,0 +1,296 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 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. +//-------------------------------------------------------------------------- +// ips_ssl_version.cc author Bhagyashree Bantwal + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ssl_inspector.h" +#include "protocols/ssl.h" +#include "framework/ips_option.h" +#include "framework/module.h" +#include "framework/parameter.h" +#include "detection/detect.h" +#include "detection/detection_defines.h" +#include "hash/sfhashfcn.h" +#include "time/profiler.h" + +//------------------------------------------------------------------------- +// ssl_version +//------------------------------------------------------------------------- + +#define s_name "ssl_version" +#define s_help \ + "detection option for ssl version" + +static THREAD_LOCAL ProfileStats sslVersionRuleOptionPerfStats; + +struct SslVersionRuleOptionData +{ + int flags; + int mask; +}; + +class SslVersionOption : public IpsOption +{ +public: + SslVersionOption(const SslVersionRuleOptionData& c) : + IpsOption(s_name) + { svod = c; } + + uint32_t hash() const override; + bool operator==(const IpsOption&) const override; + + int eval(Cursor&, Packet*) override; + +private: + SslVersionRuleOptionData svod; +}; + +//------------------------------------------------------------------------- +// ssl_version option +//------------------------------------------------------------------------- + +uint32_t SslVersionOption::hash() const +{ + uint32_t a,b,c; + + a = svod.flags; + b = svod.mask; + c = 0; + + mix_str(a,b,c,get_name()); + final (a,b,c); + + return c; +} + +bool SslVersionOption::operator==(const IpsOption& ips) const +{ + if ( strcmp(get_name(), ips.get_name()) ) + return false; + + const SslVersionOption& rhs = (SslVersionOption&)ips; + + if ( (svod.flags == rhs.svod.flags) && + (svod.mask == rhs.svod.mask) ) + return true; + + return false; +} + +int SslVersionOption::eval(Cursor&, Packet* pkt) +{ + SSLData* sd; + + PROFILE_VARS; + MODULE_PROFILE_START(sslVersionRuleOptionPerfStats); + + if ( !(pkt->packet_flags & PKT_REBUILT_STREAM) && !pkt->is_full_pdu() ) + { + MODULE_PROFILE_END(sslVersionRuleOptionPerfStats); + return DETECTION_OPTION_NO_MATCH; + } + + if (!pkt->flow) + { + MODULE_PROFILE_END(sslVersionRuleOptionPerfStats); + return DETECTION_OPTION_NO_MATCH; + } + + sd = get_ssl_session_data(pkt->flow); + + if (!sd) + { + MODULE_PROFILE_END(sslVersionRuleOptionPerfStats); + return DETECTION_OPTION_NO_MATCH; + } + + if ((svod.flags & sd->ssn_flags) ^ svod.mask) + { + MODULE_PROFILE_END(sslVersionRuleOptionPerfStats); + return DETECTION_OPTION_MATCH; + } + + MODULE_PROFILE_END(sslVersionRuleOptionPerfStats); + + return DETECTION_OPTION_NO_MATCH; +} + +//------------------------------------------------------------------------- +// ssl_version module +//------------------------------------------------------------------------- + +static const Parameter s_params[] = +{ + { "sslv2", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for sslv2" }, + + { "sslv3", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for sslv3" }, + + { "tls1.0", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for tls1.0" }, + + { "tls1.1", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for tls1.1" }, + + { "tls1.2", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for tls1.2" }, + + { "!sslv2", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for records that are not sslv2" }, + + { "!sslv3", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for records that are not sslv3" }, + + { "!tls1.0", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for records that are not tls1.0" }, + + { "!tls1.1", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for records that are not tls1.1" }, + + { "!tls1.2", Parameter::PT_IMPLIED, nullptr, nullptr, + "check for records that are not tls1.2" }, + + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +class SslVersionModule : public Module +{ +public: + SslVersionModule() : Module(s_name, s_help, s_params) { } + + bool begin(const char*, int, SnortConfig*) override; + bool set(const char*, Value&, SnortConfig*) override; + + ProfileStats* get_profile() const override + { return &sslVersionRuleOptionPerfStats; } + + SslVersionRuleOptionData svod; +}; + +bool SslVersionModule::begin(const char*, int, SnortConfig*) +{ + svod.flags = 0; + svod.mask = 0; + return true; +} + +bool SslVersionModule::set(const char*, Value& v, SnortConfig*) +{ + + if ( v.is("sslv2") ) + svod.flags |= SSL_VER_SSLV2_FLAG; + + else if ( v.is("sslv3") ) + svod.flags |= SSL_VER_SSLV3_FLAG; + + else if ( v.is("tls1.0") ) + svod.flags |= SSL_VER_TLS10_FLAG; + + else if ( v.is("tls1.1") ) + svod.flags |= SSL_VER_TLS11_FLAG; + + else if ( v.is("tls1.2") ) + svod.flags |= SSL_VER_TLS12_FLAG; + + else if ( v.is("!sslv2") ) + { + svod.flags |= SSL_VER_SSLV2_FLAG; + svod.mask |= SSL_VER_SSLV2_FLAG; + } + else if ( v.is("!sslv3") ) + { + svod.flags |= SSL_VER_SSLV3_FLAG; + svod.mask |= SSL_VER_SSLV3_FLAG; + } + else if ( v.is("!tls1.0") ) + { + svod.flags |= SSL_VER_TLS10_FLAG; + svod.mask |= SSL_VER_TLS10_FLAG; + } + else if ( v.is("!tls1.1") ) + { + svod.flags |= SSL_VER_TLS11_FLAG; + svod.mask |= SSL_VER_TLS11_FLAG; + } + else if ( v.is("!tls1.2") ) + { + svod.flags |= SSL_VER_TLS12_FLAG; + svod.mask |= SSL_VER_TLS12_FLAG; + } + else + return false; + + return true; +} + +//------------------------------------------------------------------------- +// ssl_version api methods +//------------------------------------------------------------------------- + +static Module* ssl_version_mod_ctor() +{ + return new SslVersionModule; +} + +static void mod_dtor(Module* m) +{ + delete m; +} + +static IpsOption* ssl_version_ctor(Module* p, OptTreeNode*) +{ + SslVersionModule* m = (SslVersionModule*)p; + return new SslVersionOption(m->svod); +} + +static void opt_dtor(IpsOption* p) +{ + delete p; +} + +static const IpsApi ssl_version_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 0, + API_RESERVED, + API_OPTIONS, + s_name, + s_help, + ssl_version_mod_ctor, + mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + ssl_version_ctor, + opt_dtor, + nullptr +}; + +const BaseApi* ips_ssl_version = &ssl_version_api.base; + diff --git a/src/service_inspectors/ssl/ssl_config.h b/src/service_inspectors/ssl/ssl_config.h new file mode 100644 index 000000000..5cb28139d --- /dev/null +++ b/src/service_inspectors/ssl/ssl_config.h @@ -0,0 +1,36 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 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 SSL_CONFIG_H +#define SSL_CONFIG_H + +#define SSLPP_TRUSTSERVER_FLAG 0x0002 + +/* + * Global SSL preprocessor configuration. + * + */ +struct SSL_PROTO_CONF +{ + uint16_t flags; + int max_heartbeat_len; +}; + +#endif + diff --git a/src/service_inspectors/ssl/ssl_inspector.cc b/src/service_inspectors/ssl/ssl_inspector.cc new file mode 100644 index 000000000..77a9f1af2 --- /dev/null +++ b/src/service_inspectors/ssl/ssl_inspector.cc @@ -0,0 +1,517 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 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. +//-------------------------------------------------------------------------- +// + +/* + * SSL inspector + * + */ + +#include "ssl_inspector.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "snort_types.h" +#include "snort_debug.h" + +#include "ssl.h" +#include "ssl_module.h" +#include "profiler.h" +#include "stream/stream_api.h" +#include "parser.h" +#include "framework/inspector.h" +#include "utils/sfsnprintfappend.h" +#include "target_based/sftarget_protocol_reference.h" +#include "detect.h" + +THREAD_LOCAL ProfileStats sslPerfStats; +THREAD_LOCAL SimpleStats sslstats; +THREAD_LOCAL SSL_counters_t counts; + +/* + * Function prototype(s) + */ +static void snort_ssl(SSL_PROTO_CONF* GlobalConf, Packet* p); + +unsigned SslFlowData::flow_id = 0; + +SSLData* SetNewSSLData(Packet* p) +{ + SslFlowData* fd = new SslFlowData; + p->flow->set_application_data(fd); + return &fd->session; +} + +SSLData* get_ssl_session_data(Flow* flow) +{ + SslFlowData* fd = (SslFlowData*)flow->get_application_data( + SslFlowData::flow_id); + + return fd ? &fd->session : NULL; +} + +void SSL_InitGlobals(void) +{ + memset(&counts, 0, sizeof(counts)); +} + +static void PrintSslConf(SSL_PROTO_CONF* config) +{ + if (config == NULL) + return; + LogMessage("SSL config:\n"); + if ( config->flags & SSLPP_TRUSTSERVER_FLAG ) + { + LogMessage(" Server side data is trusted\n"); + } + + LogMessage("\n"); +} + +static void SSL_UpdateCounts(const uint32_t new_flags) +{ + if (new_flags & SSL_CHANGE_CIPHER_FLAG) + counts.cipher_change++; + + if (new_flags & SSL_ALERT_FLAG) + counts.alerts++; + + if (new_flags & SSL_CLIENT_HELLO_FLAG) + counts.hs_chello++; + + if (new_flags & SSL_SERVER_HELLO_FLAG) + counts.hs_shello++; + + if (new_flags & SSL_CERTIFICATE_FLAG) + counts.hs_cert++; + + if (new_flags & SSL_SERVER_KEYX_FLAG) + counts.hs_skey++; + + if (new_flags & SSL_CLIENT_KEYX_FLAG) + counts.hs_ckey++; + + if (new_flags & SSL_SFINISHED_FLAG) + counts.hs_finished++; + + if (new_flags & SSL_HS_SDONE_FLAG) + counts.hs_sdone++; + + if (new_flags & SSL_SAPP_FLAG) + counts.sapp++; + + if (new_flags & SSL_CAPP_FLAG) + counts.capp++; +} + +static inline bool SSLPP_is_encrypted(SSL_PROTO_CONF* config, uint32_t ssl_flags, Packet* packet) +{ + if (config->flags & SSLPP_TRUSTSERVER_FLAG) + { + if (ssl_flags & SSL_SAPP_FLAG) + return true; + } + + if (SSL_IS_CLEAN(ssl_flags)) + { + if (((ssl_flags & SSLPP_ENCRYPTED_FLAGS) == SSLPP_ENCRYPTED_FLAGS) || + ((ssl_flags & SSLPP_ENCRYPTED_FLAGS2) == SSLPP_ENCRYPTED_FLAGS2)) + { + counts.completed_hs++; + return true; + } + /* Check if we're either midstream or if packets were missed after the + * * connection was established */ + else if ((stream.get_session_flags (packet->flow) & SSNFLAG_MIDSTREAM) || + (stream.missed_packets(packet->flow, SSN_DIR_BOTH))) + { + if ((ssl_flags & (SSL_CAPP_FLAG | SSL_SAPP_FLAG)) == (SSL_CAPP_FLAG | SSL_SAPP_FLAG)) + { + return true; + } + } + } + + return false; +} + +static inline uint32_t SSLPP_process_alert(SSL_PROTO_CONF* config, uint32_t ssn_flags, uint32_t + new_flags, Packet* packet) +{ + DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Process Alert\n"); ); + + ssn_flags |= new_flags; + + /* Check if we've seen a handshake, that this isn't it, + * * that the cipher flags is not set, and that we are disabling detection */ + if (SSL_IS_HANDSHAKE(ssn_flags) && + !SSL_IS_HANDSHAKE(new_flags) && + !(new_flags & SSL_CHANGE_CIPHER_FLAG) && + !(new_flags & SSL_HEARTBEAT_SEEN)) + { + DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Disabling detect\n"); ); + DisableDetect(packet); + } + + /* Need to negate the application flags from the opposing side. */ + + if (packet->packet_flags & PKT_FROM_CLIENT) + return ssn_flags & ~SSL_SAPP_FLAG; + + else if (packet->packet_flags & PKT_FROM_SERVER) + return ssn_flags & ~SSL_CAPP_FLAG; + + return ssn_flags; +} + +static inline uint32_t SSLPP_process_hs(uint32_t ssl_flags, uint32_t new_flags) +{ + DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Process Handshake\n"); ); + + if (!SSL_BAD_HS(new_flags)) + { + ssl_flags |= new_flags & (SSL_CLIENT_HELLO_FLAG | + SSL_SERVER_HELLO_FLAG | + SSL_CLIENT_KEYX_FLAG | + SSL_SFINISHED_FLAG); + } + else + { + counts.bad_handshakes++; + } + + return ssl_flags; +} + +static inline uint32_t SSLPP_process_app(SSL_PROTO_CONF* config, uint32_t ssn_flags, uint32_t + new_flags, Packet* packet) +{ + DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Process Application\n"); ); + + if (SSLPP_is_encrypted(config, ssn_flags | new_flags, packet) ) + { + ssn_flags |= SSL_ENCRYPTED_FLAG; + + // Heartbleed check is disabled. Stop inspection on this session. + if (!config->max_heartbeat_len) + { + DEBUG_WRAP(DebugMessage(DEBUG_SSL, "STOPPING INSPECTION (process_app)\n"); ); + stream.stop_inspection(packet->flow, + packet, SSN_DIR_BOTH, -1, 0); + counts.stopped++; + } + else if (!(new_flags & SSL_HEARTBEAT_SEEN)) + { + DisableDetect(packet); + } + } + + return ssn_flags | new_flags; +} + +static inline void SSLPP_process_other(SSL_PROTO_CONF* config, SSLData* sd, uint32_t new_flags, + Packet* packet) +{ + /* Encrypted SSLv2 will appear unrecognizable. Check if the handshake was + * * seen and stop inspecting if so. */ + /* Check for an existing handshake from both sides */ + if ((sd->ssn_flags & SSL_VER_SSLV2_FLAG) && + SSL_IS_CHELLO(sd->ssn_flags) && SSL_IS_SHELLO(sd->ssn_flags) && + !(new_flags & SSL_CHANGE_CIPHER_FLAG) && + !(new_flags & SSL_HEARTBEAT_SEEN)) + { + sd->ssn_flags |= SSL_ENCRYPTED_FLAG | new_flags; + + if (!config->max_heartbeat_len) + { + DEBUG_WRAP(DebugMessage(DEBUG_SSL, "STOPPING INSPECTION (process_other)\n"); ); + stream.stop_inspection(packet->flow, + packet, SSN_DIR_BOTH, -1, 0); + } + else if (!(new_flags & SSL_HEARTBEAT_SEEN)) + { + DisableDetect(packet); + } + } + else + { + counts.unrecognized++; + + /* Special handling for SSLv2 */ + if (new_flags & SSL_VER_SSLV2_FLAG) + sd->ssn_flags |= new_flags; + + if (new_flags & SSL_UNKNOWN_FLAG) + sd->ssn_flags |= new_flags; + } +} + +/* Main runtime entry point for SSL preprocessor. + * Analyzes SSL packets for anomalies/exploits. + * + * PARAMETERS: + * + * p: Pointer to current packet to process. + * contextp: Pointer to context block, not used. + * + * RETURNS: Nothing. + */ +static void snort_ssl(SSL_PROTO_CONF* config, Packet* p) +{ + SSLData* sd = NULL; + uint8_t dir; + uint8_t index; + uint32_t new_flags; + uint8_t heartbleed_type = 0; + PROFILE_VARS; + + MODULE_PROFILE_START(sslPerfStats); + + /* Attempt to get a previously allocated SSL block. */ + sd = get_ssl_session_data(p->flow); + + if (sd == NULL) + { + /* Check the stream session. If it does not currently + * have our SSL data-block attached, create one. + */ + sd = SetNewSSLData(p); + + if ( !sd ) + { + /* Could not get/create the session data for this packet. */ + MODULE_PROFILE_END(sslPerfStats); + return; + } + } + SSL_CLEAR_TEMPORARY_FLAGS(sd->ssn_flags); + + dir = (p->packet_flags & PKT_FROM_SERVER) ? 1 : 0; + index = (p->packet_flags & PKT_REBUILT_STREAM) ? 2 : 0; + new_flags = SSL_decode(p->data, (int)p->dsize, p->packet_flags, sd->ssn_flags, + &heartbleed_type, &(sd->partial_rec_len[dir+index]), config->max_heartbeat_len); + + if (heartbleed_type & SSL_HEARTBLEED_REQUEST) + { + SnortEventqAdd(GID_SSL, SSL_ALERT_HB_REQUEST); + } + else if (heartbleed_type & SSL_HEARTBLEED_RESPONSE) + { + SnortEventqAdd(GID_SSL, SSL_ALERT_HB_RESPONSE); + } + else if (heartbleed_type & SSL_HEARTBLEED_UNKNOWN) + { + if (!dir) + { + SnortEventqAdd(GID_SSL, SSL_ALERT_HB_REQUEST); + } + else + { + SnortEventqAdd(GID_SSL, SSL_ALERT_HB_RESPONSE); + } + } + if (sd->ssn_flags & SSL_ENCRYPTED_FLAG ) + { + counts.decoded++; + + SSL_UpdateCounts(new_flags); + + if (!(new_flags & SSL_HEARTBEAT_SEEN)) + { + DisableDetect(p); + } + + sd->ssn_flags |= new_flags; + + MODULE_PROFILE_END(sslPerfStats); + return; + } + +// If the client used an SSLv2 ClientHello with an SSLv3/TLS version and +// the server replied with an SSLv3/TLS ServerHello, remove the backward +// compatibility flag and the SSLv2 flag since this session will continue +// as SSLv3/TLS. + + if ((sd->ssn_flags & SSL_V3_BACK_COMPAT_V2) && SSL_V3_SERVER_HELLO(new_flags)) + sd->ssn_flags &= ~(SSL_VER_SSLV2_FLAG|SSL_V3_BACK_COMPAT_V2); + + if ( (SSL_IS_CHELLO(new_flags) && SSL_IS_CHELLO(sd->ssn_flags) && SSL_IS_SHELLO(sd->ssn_flags) ) + || (SSL_IS_CHELLO(new_flags) && SSL_IS_SHELLO(sd->ssn_flags) )) + { + SnortEventqAdd(GID_SSL, SSL_INVALID_CLIENT_HELLO); + } + else if (!(config->flags & SSLPP_TRUSTSERVER_FLAG)) + { + if ( (SSL_IS_SHELLO(new_flags) && !SSL_IS_CHELLO(sd->ssn_flags) )) + { + if (!(stream.missed_packets(p->flow, SSN_DIR_FROM_CLIENT))) + SnortEventqAdd(GID_SSL, SSL_INVALID_SERVER_HELLO); + } + } + + counts.decoded++; + + SSL_UpdateCounts(new_flags); + + /* Note, there can be multiple record types in each SSL packet. + * * Processing them in this order is intentional. If there is an + * * Alert, we don't care about the other records */ + + if (SSL_IS_ALERT(new_flags)) + { + sd->ssn_flags = SSLPP_process_alert(config, sd->ssn_flags, new_flags, p); + } + else if (SSL_IS_HANDSHAKE(new_flags)) + { + sd->ssn_flags = SSLPP_process_hs(sd->ssn_flags, new_flags); + } + else if (SSL_IS_APP(new_flags)) + { + sd->ssn_flags = SSLPP_process_app(config, sd->ssn_flags, new_flags, p); + } + else + { + /* Different record type that we don't care about. + * * Either it's a 'change cipher spec' or we failed to recognize the + * * record type. Do not update session data */ + SSLPP_process_other(config, sd, new_flags, p); + + /* Application data is updated inside of SSLPP_process_other */ + + MODULE_PROFILE_END(sslPerfStats); + return; + } + + sd->ssn_flags |= new_flags; + + MODULE_PROFILE_END(sslPerfStats); +} + +//------------------------------------------------------------------------- +// class stuff +//------------------------------------------------------------------------- + +class Ssl : public Inspector +{ +public: + Ssl(SSL_PROTO_CONF*); + ~Ssl(); + + void show(SnortConfig*) override; + void eval(Packet*) override; + +private: + SSL_PROTO_CONF* config; +}; + +Ssl::Ssl(SSL_PROTO_CONF* pc) +{ + config = pc; +} + +Ssl::~Ssl() +{ + if ( config ) + delete config; +} + +void Ssl::show(SnortConfig*) +{ + PrintSslConf(config); +} + +void Ssl::eval(Packet* p) +{ + // precondition - what we registered for + assert(p->is_tcp() && p->dsize && p->data); + + ++sslstats.total_packets; + snort_ssl(config, p); +} + +//------------------------------------------------------------------------- +// api stuff +//------------------------------------------------------------------------- + +static Module* mod_ctor() +{ return new SslModule; } + +static void mod_dtor(Module* m) +{ delete m; } + +static void ssl_init() +{ + SslFlowData::init(); +} + +static Inspector* ssl_ctor(Module* m) +{ + SslModule* mod = (SslModule*)m; + return new Ssl(mod->get_data()); +} + +static void ssl_dtor(Inspector* p) +{ + delete p; +} + +const InspectApi ssl_api = +{ + { + PT_INSPECTOR, + sizeof(InspectApi), + INSAPI_VERSION, + 0, + API_RESERVED, + API_OPTIONS, + SSL_NAME, + SSL_HELP, + mod_ctor, + mod_dtor + }, + IT_SERVICE, + (uint16_t)PktType::TCP, + nullptr, // buffers + "ssl", + ssl_init, + nullptr, // pterm + nullptr, // tinit + nullptr, // tterm + ssl_ctor, + ssl_dtor, + nullptr, // ssn + nullptr // reset +}; + +#ifdef BUILDING_SO +SO_PUBLIC const BaseApi* snort_plugins[] = +{ + &ssl_api.base, + nullptr +}; +#else +const BaseApi* sin_ssl = &ssl_api.base; +#endif + diff --git a/src/service_inspectors/ssl/ssl_inspector.h b/src/service_inspectors/ssl/ssl_inspector.h new file mode 100644 index 000000000..2d46ff621 --- /dev/null +++ b/src/service_inspectors/ssl/ssl_inspector.h @@ -0,0 +1,87 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 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. +//-------------------------------------------------------------------------- +// + +/* + * ssl.h: Definitions, structs, function prototype(s) for + * the SSL service inspectors. + */ + +#ifndef SSL_INSPECTOR_H +#define SSL_INSPECTOR_H + +#include "protocols/packet.h" +#include "stream/stream_api.h" +#include "profiler.h" +#include "ssl_config.h" + +#define SSLPP_ENCRYPTED_FLAGS \ + (SSL_HS_SDONE_FLAG | SSL_CLIENT_KEYX_FLAG | \ + SSL_CAPP_FLAG | SSL_SAPP_FLAG) +#define SSLPP_ENCRYPTED_FLAGS2 \ + (SSL_HS_SDONE_FLAG | SSL_CHANGE_CIPHER_FLAG | \ + SSL_CAPP_FLAG | SSL_SAPP_FLAG) + +struct SSLData +{ + uint32_t ssn_flags; + uint16_t partial_rec_len[4]; +}; + +struct SSL_counters_t +{ + uint64_t stopped; + uint64_t disabled; + uint64_t decoded; + uint64_t alerts; + uint64_t cipher_change; + uint64_t unrecognized; + uint64_t completed_hs; + uint64_t bad_handshakes; + uint64_t hs_chello; + uint64_t hs_shello; + uint64_t hs_cert; + uint64_t hs_skey; + uint64_t hs_ckey; + uint64_t hs_finished; + uint64_t hs_sdone; + uint64_t capp; + uint64_t sapp; +}; + +class SslFlowData : public FlowData +{ +public: + SslFlowData() : FlowData(flow_id) + { memset(&session, 0, sizeof(session)); } + + ~SslFlowData() { } + + static void init() + { flow_id = FlowData::get_flow_id(); } + +public: + static unsigned flow_id; + SSLData session; +}; + +SSLData* get_ssl_session_data(Flow* flow); +void SSL_InitGlobals(void); + +#endif /* SSL_INSPECTOR_H */ + diff --git a/src/service_inspectors/ssl/ssl_module.cc b/src/service_inspectors/ssl/ssl_module.cc new file mode 100644 index 000000000..becbbe736 --- /dev/null +++ b/src/service_inspectors/ssl/ssl_module.cc @@ -0,0 +1,121 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 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. +//-------------------------------------------------------------------------- + +// ssl_module.cc author Bhagyashree Bantwal + +#include "ssl_module.h" + +#include +#include + +#include "parser.h" +#include "ssl_inspector.h" + +using namespace std; + +#define SSL_INVALID_CLIENT_HELLO_STR "Invalid Client HELLO after Server HELLO Detected" +#define SSL_INVALID_SERVER_HELLO_STR "Invalid Server HELLO without Client HELLO Detected" +#define SSL_HEARTBLEED_REQUEST_STR "Heartbeat Read Overrun Attempt Detected" +#define SSL_HEARTBLEED_RESPONSE_STR "Large Heartbeat Response Detected" + +static const Parameter s_params[] = +{ + { "trust_servers", Parameter::PT_BOOL, nullptr, "false", + "disables requirement that application (encrypted) data must be observed on both sides" }, + + { "max_heartbeat_length", Parameter::PT_INT, "0:65535", "0", + "maximum length of heartbeat record allowed" }, + + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +static const RuleMap ssl_rules[] = +{ + { SSL_INVALID_CLIENT_HELLO, SSL_INVALID_CLIENT_HELLO_STR }, + { SSL_INVALID_SERVER_HELLO, SSL_INVALID_SERVER_HELLO_STR }, + { SSL_ALERT_HB_REQUEST, SSL_HEARTBLEED_REQUEST_STR }, + { SSL_ALERT_HB_RESPONSE, SSL_HEARTBLEED_RESPONSE_STR }, + + { 0, nullptr } +}; + +//------------------------------------------------------------------------- +// ssl module +//------------------------------------------------------------------------- + +SslModule::SslModule() : Module(SSL_NAME, SSL_HELP, s_params) +{ + conf = nullptr; +} + +SslModule::~SslModule() +{ + if ( conf ) + delete conf; +} + +const RuleMap* SslModule::get_rules() const +{ return ssl_rules; } + +const PegInfo* SslModule::get_pegs() const +{ return simple_pegs; } + +PegCount* SslModule::get_counts() const +{ return (PegCount*)&sslstats; } + +ProfileStats* SslModule::get_profile() const +{ return &sslPerfStats; } + +bool SslModule::set(const char*, Value& v, SnortConfig*) +{ + if ( v.is("trust_servers") ) + { + if (v.get_bool()) + conf->flags |= SSLPP_TRUSTSERVER_FLAG; + } + else if ( v.is("max_heartbeat_length") ) + conf->max_heartbeat_len = v.get_long(); + + else + return false; + + return true; +} + +SSL_PROTO_CONF* SslModule::get_data() +{ + SSL_PROTO_CONF* tmp = conf; + conf = nullptr; + return tmp; +} + +bool SslModule::begin(const char*, int, SnortConfig*) +{ + conf = new SSL_PROTO_CONF; + conf->max_heartbeat_len = 0; + conf->flags = 0; + return true; +} + +bool SslModule::end(const char*, int, SnortConfig*) +{ + SSL_InitGlobals(); + + return true; +} + diff --git a/src/service_inspectors/ssl/ssl_module.h b/src/service_inspectors/ssl/ssl_module.h new file mode 100644 index 000000000..01f96f29b --- /dev/null +++ b/src/service_inspectors/ssl/ssl_module.h @@ -0,0 +1,69 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 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. +//-------------------------------------------------------------------------- + +// ssl_module.h author Bhagyashree Bantwal + +#ifndef SSL_MODULE_H +#define SSL_MODULE_H + +#include "framework/module.h" +#include "framework/bits.h" +#include "main/thread.h" +#include "ssl_config.h" + +#define GID_SSL 137 + +#define SSL_INVALID_CLIENT_HELLO 1 +#define SSL_INVALID_SERVER_HELLO 2 +#define SSL_ALERT_HB_REQUEST 3 +#define SSL_ALERT_HB_RESPONSE 4 + +#define SSL_NAME "ssl" +#define SSL_HELP "ssl inspection" + +struct SnortConfig; + +extern THREAD_LOCAL SimpleStats sslstats; +extern THREAD_LOCAL ProfileStats sslPerfStats; + +class SslModule : public Module +{ +public: + SslModule(); + ~SslModule(); + + bool set(const char*, Value&, SnortConfig*) override; + bool begin(const char*, int, SnortConfig*) override; + bool end(const char*, int, SnortConfig*) override; + + unsigned get_gid() const override + { return GID_SSL; } + + const RuleMap* get_rules() const override; + const PegInfo* get_pegs() const override; + PegCount* get_counts() const override; + ProfileStats* get_profile() const override; + + SSL_PROTO_CONF* get_data(); + +private: + SSL_PROTO_CONF* conf; +}; + +#endif + diff --git a/tools/snort2lua/preprocessor_states/CMakeLists.txt b/tools/snort2lua/preprocessor_states/CMakeLists.txt index fb7e1615b..3d76e291e 100644 --- a/tools/snort2lua/preprocessor_states/CMakeLists.txt +++ b/tools/snort2lua/preprocessor_states/CMakeLists.txt @@ -13,6 +13,7 @@ add_library(preprocessor_states pps_perfmonitor.cc pps_rpc_decode.cc pps_ssh.cc + pps_ssl.cc pps_dns.cc pps_pop.cc pps_imap.cc diff --git a/tools/snort2lua/preprocessor_states/Makefile.am b/tools/snort2lua/preprocessor_states/Makefile.am index 0d190f55f..c75a3a47c 100644 --- a/tools/snort2lua/preprocessor_states/Makefile.am +++ b/tools/snort2lua/preprocessor_states/Makefile.am @@ -17,6 +17,7 @@ pps_normalizers.cc \ pps_perfmonitor.cc \ pps_rpc_decode.cc \ pps_ssh.cc \ +pps_ssl.cc \ pps_dns.cc \ pps_pop.cc \ pps_imap.cc \ diff --git a/tools/snort2lua/preprocessor_states/pps_ssl.cc b/tools/snort2lua/preprocessor_states/pps_ssl.cc new file mode 100644 index 000000000..0bb028b9e --- /dev/null +++ b/tools/snort2lua/preprocessor_states/pps_ssl.cc @@ -0,0 +1,162 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 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. +//-------------------------------------------------------------------------- +// pps_ssl.cc author Bhagya Bantwal + +#include +#include + +#include "conversion_state.h" +#include "helpers/s2l_util.h" +#include "helpers/util_binder.h" + +namespace preprocessors +{ +namespace +{ +class Ssl : public ConversionState +{ +public: + Ssl(Converter& c) : ConversionState(c) { } + virtual ~Ssl() { } + virtual bool convert(std::istringstream& data_stream); +}; +} // namespace + +bool Ssl::convert(std::istringstream& data_stream) +{ + std::string keyword; + bool retval = true; + bool ports_set = false; + Binder bind(table_api); + + bind.set_when_proto("tcp"); + bind.set_use_type("ssl"); + + table_api.open_table("ssl"); + + // parse the file configuration + while (util::get_string(data_stream, keyword, ",")) + { + bool tmpval = true; + std::istringstream arg_stream(keyword); + + // should be gauranteed to happen. Checking for error just cause + if (!(arg_stream >> keyword)) + tmpval = false; + + else if (!keyword.compare("noinspect_encrypted")) + table_api.add_deleted_comment("noinspect_encrypted"); + + else if (!keyword.compare("trustservers")) + tmpval = table_api.add_option("trust_servers", true); + + else if (!keyword.compare("max_heartbeat_length")) + { + tmpval = parse_int_option("max_heartbeat_length", arg_stream, false); + } + else if (!keyword.compare("ports")) + { + std::string tmp = ""; + table_api.add_diff_option_comment("ports", "bindings"); + + if (arg_stream >> keyword) + { + if (!keyword.compare("{")) + { + while (arg_stream >> keyword && keyword.compare("}")) + { + ports_set = true; + bind.add_when_port(keyword); + } + } + else + { + data_api.failed_conversion(arg_stream, "ports "); + retval = false; + } + } + } + else + { + tmpval = false; + } + + if (!tmpval) + { + data_api.failed_conversion(arg_stream, keyword); + retval = false; + } + } + + if (!ports_set) + { + bind.add_when_port("443"); + bind.add_when_port("465"); + bind.add_when_port("563"); + bind.add_when_port("639"); + bind.add_when_port("989"); + bind.add_when_port("992"); + bind.add_when_port("993"); + bind.add_when_port("994"); + bind.add_when_port("995"); + bind.add_when_port("7801"); + bind.add_when_port("7802"); + bind.add_when_port("7900"); + bind.add_when_port("7901"); + bind.add_when_port("7902"); + bind.add_when_port("7903"); + bind.add_when_port("7904"); + bind.add_when_port("7905"); + bind.add_when_port("7906"); + bind.add_when_port("7907"); + bind.add_when_port("7908"); + bind.add_when_port("7909"); + bind.add_when_port("7910"); + bind.add_when_port("7911"); + bind.add_when_port("7912"); + bind.add_when_port("7913"); + bind.add_when_port("7914"); + bind.add_when_port("7915"); + bind.add_when_port("7916"); + bind.add_when_port("7917"); + bind.add_when_port("7918"); + bind.add_when_port("7919"); + bind.add_when_port("7920"); + } + + return retval; +} + +/************************** + ******* A P I *********** + **************************/ + +static ConversionState* ctor(Converter& c) +{ + return new Ssl(c); +} + +static const ConvertMap preprocessor_ssl = +{ + "ssl", + ctor, +}; + +const ConvertMap* ssl_map = &preprocessor_ssl; +} + diff --git a/tools/snort2lua/preprocessor_states/preprocessor_api.cc b/tools/snort2lua/preprocessor_states/preprocessor_api.cc index 5001ab469..b84d30072 100644 --- a/tools/snort2lua/preprocessor_states/preprocessor_api.cc +++ b/tools/snort2lua/preprocessor_states/preprocessor_api.cc @@ -39,6 +39,7 @@ extern const ConvertMap* normalizer_tcp_map; extern const ConvertMap* perfmonitor_map; extern const ConvertMap* rpc_decode_map; extern const ConvertMap* ssh_map; +extern const ConvertMap* ssl_map; extern const ConvertMap* dns_map; extern const ConvertMap* pop_map; extern const ConvertMap* imap_map; @@ -69,6 +70,7 @@ const std::vector preprocessor_api = perfmonitor_map, rpc_decode_map, ssh_map, + ssl_map, dns_map, pop_map, imap_map, diff --git a/tools/snort2lua/rule_states/rule_api.cc b/tools/snort2lua/rule_states/rule_api.cc index af862f2c2..3a7f82a8a 100644 --- a/tools/snort2lua/rule_states/rule_api.cc +++ b/tools/snort2lua/rule_states/rule_api.cc @@ -71,6 +71,8 @@ extern const ConvertMap* session_map; extern const ConvertMap* sid_map; extern const ConvertMap* stream_reassemble_map; extern const ConvertMap* stream_size_map; +extern const ConvertMap* ssl_state_map; +extern const ConvertMap* ssl_version_map; extern const ConvertMap* tag_map; extern const ConvertMap* threshold_map; extern const ConvertMap* ttl_map; @@ -128,6 +130,8 @@ const std::vector rule_options_api = sid_map, stream_reassemble_map, stream_size_map, + ssl_state_map, + ssl_version_map, tag_map, threshold_map, ttl_map, diff --git a/tools/snort2lua/rule_states/rule_unchanged.cc b/tools/snort2lua/rule_states/rule_unchanged.cc index 31589c76f..16ab080ec 100644 --- a/tools/snort2lua/rule_states/rule_unchanged.cc +++ b/tools/snort2lua/rule_states/rule_unchanged.cc @@ -547,5 +547,32 @@ static const ConvertMap rule_priority = }; const ConvertMap* priority_map = &rule_priority; + +/************************************ + ********* SSL STATE ************** + ************************************/ + +static const std::string ssl_state = "ssl_state"; +static const ConvertMap rule_ssl_state = +{ + ssl_state, + unchanged_rule_ctor<& ssl_state>, +}; + +const ConvertMap* ssl_state_map = &rule_ssl_state; + +/************************************ + ********* SSL VERSION ************** + ************************************/ + +static const std::string ssl_version = "ssl_version"; +static const ConvertMap rule_ssl_version = +{ + ssl_version, + unchanged_rule_ctor<& ssl_version>, +}; + +const ConvertMap* ssl_version_map = &rule_ssl_version; + } // namespace rule