Merge remote branch 'origin/topic/seth/ssl-binpac'

* origin/topic/seth/ssl-binpac:
  Fixed bug due to vectors now initially indexed on 0.
  Finished core support for new SSL analyzer.
  SSL analyzer changes with accompanying BiF.
  A table_s_of_s type to get around bifcl type limitation.
  Regenerated the Mozilla CA bundle without the untrusted server authentication certs.
  Complete rewrite to SSL analyzer.

Conflicts:
	src/AnalyzerTags.h
	src/CMakeLists.txt

Notes:

    - Haven't looked at the script-level, postponed to
      policy-scripts-new.

    - I renamed X509Extension to X509_extension for consistency.
This commit is contained in:
Robin Sommer 2011-06-07 10:12:25 -07:00
commit 4bdb94955d
31 changed files with 1242 additions and 8148 deletions

View file

@ -11,9 +11,12 @@ global no_handler: event(name: string, val: any);
# Type declarations # Type declarations
type string_array: table[count] of string; type string_array: table[count] of string;
type string_set: set[string]; type string_set: set[string];
type count_set: set[count];
type index_vec: vector of count; type index_vec: vector of count;
type string_vec: vector of string; type string_vec: vector of string;
type table_string_of_string: table[string] of string;
type transport_proto: enum { unknown_transport, tcp, udp, icmp }; type transport_proto: enum { unknown_transport, tcp, udp, icmp };
type conn_id: record { type conn_id: record {
@ -930,16 +933,20 @@ global dns_max_queries = 5;
# has bigger cipherspecs, we won't do a comparisons of cipherspecs. # has bigger cipherspecs, we won't do a comparisons of cipherspecs.
const ssl_max_cipherspec_size = 68 &redef; const ssl_max_cipherspec_size = 68 &redef;
# SSL and X.509 types. type X509_extensions: table[count] of string;
type cipher_suites_list: set[count];
type SSL_sessionID: table[count] of count;
type X509_extension: table[count] of string;
type X509: record { type X509: record {
issuer: string; version: count;
serial: string;
subject: string; subject: string;
orig_addr: addr; issuer: string;
not_valid_before: time;
not_valid_after: time;
}; };
# This is indexed with the CA's name and yields a DER (binary) encoded certificate.
const root_ca_certs: table[string] of string = {} &redef;
type http_stats_rec: record { type http_stats_rec: record {
num_requests: count; num_requests: count;
num_replies: count; num_replies: count;

131
policy/ssl-mozilla-CAs.bro Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,6 @@
#include "Portmap.h" #include "Portmap.h"
#include "POP3.h" #include "POP3.h"
#include "SSH.h" #include "SSH.h"
#include "SSLProxy.h"
#include "SSL-binpac.h" #include "SSL-binpac.h"
#include "Syslog-binpac.h" #include "Syslog-binpac.h"
#include "ConnSizeAnalyzer.h" #include "ConnSizeAnalyzer.h"
@ -118,8 +117,6 @@ const Analyzer::Config Analyzer::analyzer_configs[] = {
SMTP_Analyzer::Available, 0, false }, SMTP_Analyzer::Available, 0, false },
{ AnalyzerTag::SSH, "SSH", SSH_Analyzer::InstantiateAnalyzer, { AnalyzerTag::SSH, "SSH", SSH_Analyzer::InstantiateAnalyzer,
SSH_Analyzer::Available, 0, false }, SSH_Analyzer::Available, 0, false },
{ AnalyzerTag::SSL, "SSL", SSLProxy_Analyzer::InstantiateAnalyzer,
SSLProxy_Analyzer::Available, 0, false },
{ AnalyzerTag::Telnet, "TELNET", Telnet_Analyzer::InstantiateAnalyzer, { AnalyzerTag::Telnet, "TELNET", Telnet_Analyzer::InstantiateAnalyzer,
Telnet_Analyzer::Available, 0, false }, Telnet_Analyzer::Available, 0, false },
@ -138,7 +135,7 @@ const Analyzer::Config Analyzer::analyzer_configs[] = {
{ AnalyzerTag::RPC_UDP_BINPAC, "RPC_UDP_BINPAC", { AnalyzerTag::RPC_UDP_BINPAC, "RPC_UDP_BINPAC",
RPC_UDP_Analyzer_binpac::InstantiateAnalyzer, RPC_UDP_Analyzer_binpac::InstantiateAnalyzer,
RPC_UDP_Analyzer_binpac::Available, 0, false }, RPC_UDP_Analyzer_binpac::Available, 0, false },
{ AnalyzerTag::SSL_BINPAC, "SSL_BINPAC", { AnalyzerTag::SSL, "SSL",
SSL_Analyzer_binpac::InstantiateAnalyzer, SSL_Analyzer_binpac::InstantiateAnalyzer,
SSL_Analyzer_binpac::Available, 0, false }, SSL_Analyzer_binpac::Available, 0, false },
{ AnalyzerTag::SYSLOG_BINPAC, "SYSLOG_BINPAC", { AnalyzerTag::SYSLOG_BINPAC, "SYSLOG_BINPAC",
@ -176,7 +173,6 @@ const Analyzer::Config Analyzer::analyzer_configs[] = {
{ AnalyzerTag::Contents_SMB, "CONTENTS_SMB", 0, 0, 0, false }, { AnalyzerTag::Contents_SMB, "CONTENTS_SMB", 0, 0, 0, false },
{ AnalyzerTag::Contents_RPC, "CONTENTS_RPC", 0, 0, 0, false }, { AnalyzerTag::Contents_RPC, "CONTENTS_RPC", 0, 0, 0, false },
{ AnalyzerTag::Contents_NFS, "CONTENTS_NFS", 0, 0, 0, false }, { AnalyzerTag::Contents_NFS, "CONTENTS_NFS", 0, 0, 0, false },
{ AnalyzerTag::Contents_SSL, "CONTENTS_SSL", 0, 0, 0, false },
}; };
AnalyzerTimer::~AnalyzerTimer() AnalyzerTimer::~AnalyzerTimer()

View file

@ -31,12 +31,11 @@ namespace AnalyzerTag {
DCE_RPC, DNS, Finger, FTP, Gnutella, HTTP, Ident, IRC, DCE_RPC, DNS, Finger, FTP, Gnutella, HTTP, Ident, IRC,
Login, NCP, NetbiosSSN, NFS, NTP, POP3, Portmapper, Rlogin, Login, NCP, NetbiosSSN, NFS, NTP, POP3, Portmapper, Rlogin,
RPC, Rsh, SMB, SMTP, SSH, RPC, Rsh, SMB, SMTP, SSH,
SSL,
Telnet, Telnet,
// Application-layer analyzers, binpac-generated. // Application-layer analyzers, binpac-generated.
DHCP_BINPAC, DNS_TCP_BINPAC, DNS_UDP_BINPAC, DHCP_BINPAC, DNS_TCP_BINPAC, DNS_UDP_BINPAC,
HTTP_BINPAC, RPC_UDP_BINPAC, SSL_BINPAC, SYSLOG_BINPAC, HTTP_BINPAC, RPC_UDP_BINPAC, SSL, SYSLOG_BINPAC,
// Other // Other
File, Backdoor, InterConn, SteppingStone, TCPStats, File, Backdoor, InterConn, SteppingStone, TCPStats,
@ -47,7 +46,6 @@ namespace AnalyzerTag {
Contents, ContentLine, NVT, Zip, Contents_DNS, Contents_NCP, Contents, ContentLine, NVT, Zip, Contents_DNS, Contents_NCP,
Contents_NetbiosSSN, Contents_Rlogin, Contents_Rsh, Contents_NetbiosSSN, Contents_Rlogin, Contents_Rsh,
Contents_DCE_RPC, Contents_SMB, Contents_RPC, Contents_NFS, Contents_DCE_RPC, Contents_SMB, Contents_RPC, Contents_NFS,
Contents_SSL,
// End-marker. // End-marker.
LastAnalyzer LastAnalyzer
}; };

View file

@ -199,8 +199,6 @@ binpac_target(smb.pac
smb-protocol.pac smb-pipe.pac smb-mailslot.pac) smb-protocol.pac smb-pipe.pac smb-mailslot.pac)
binpac_target(ssl.pac binpac_target(ssl.pac
ssl-defs.pac ssl-protocol.pac ssl-analyzer.pac) ssl-defs.pac ssl-protocol.pac ssl-analyzer.pac)
binpac_target(ssl-record-layer.pac
ssl-defs.pac ssl.pac)
binpac_target(syslog.pac binpac_target(syslog.pac
syslog-protocol.pac syslog-analyzer.pac) syslog-protocol.pac syslog-analyzer.pac)
@ -243,16 +241,6 @@ set(dns_SRCS nb_dns.c)
set_source_files_properties(nb_dns.c PROPERTIES COMPILE_FLAGS set_source_files_properties(nb_dns.c PROPERTIES COMPILE_FLAGS
-fno-strict-aliasing) -fno-strict-aliasing)
set(openssl_SRCS
X509.cc
SSLCiphers.cc
SSLInterpreter.cc
SSLProxy.cc
SSLv2.cc
SSLv3.cc
SSLv3Automaton.cc
)
if (USE_NMALLOC) if (USE_NMALLOC)
set(malloc_SRCS malloc.c) set(malloc_SRCS malloc.c)
endif () endif ()

View file

@ -18,6 +18,7 @@ RecordType* pcap_packet;
RecordType* signature_state; RecordType* signature_state;
EnumType* transport_proto; EnumType* transport_proto;
TableType* string_set; TableType* string_set;
TableType* count_set;
RecordType* net_stats; RecordType* net_stats;
@ -201,8 +202,6 @@ StringVal* ssl_private_key;
StringVal* ssl_passphrase; StringVal* ssl_passphrase;
StringVal* x509_crl_file; StringVal* x509_crl_file;
TableType* x509_extension;
TableType* SSL_sessionID;
Val* profiling_file; Val* profiling_file;
double profiling_interval; double profiling_interval;
@ -373,10 +372,7 @@ void init_net_var()
x509_trusted_cert_path = opt_internal_string("X509_trusted_cert_path"); x509_trusted_cert_path = opt_internal_string("X509_trusted_cert_path");
ssl_store_cert_path = opt_internal_string("ssl_store_cert_path"); ssl_store_cert_path = opt_internal_string("ssl_store_cert_path");
x509_type = internal_type("X509")->AsRecordType(); x509_type = internal_type("X509")->AsRecordType();
cipher_suites_list = internal_type("cipher_suites_list")->AsTableType();
x509_crl_file = opt_internal_string("X509_crl_file"); x509_crl_file = opt_internal_string("X509_crl_file");
x509_extension = internal_type("X509_extension")->AsTableType();
SSL_sessionID = internal_type("SSL_sessionID")->AsTableType();
non_analyzed_lifetime = opt_internal_double("non_analyzed_lifetime"); non_analyzed_lifetime = opt_internal_double("non_analyzed_lifetime");
tcp_inactivity_timeout = opt_internal_double("tcp_inactivity_timeout"); tcp_inactivity_timeout = opt_internal_double("tcp_inactivity_timeout");

View file

@ -21,6 +21,7 @@ extern RecordType* SYN_packet;
extern RecordType* pcap_packet; extern RecordType* pcap_packet;
extern EnumType* transport_proto; extern EnumType* transport_proto;
extern TableType* string_set; extern TableType* string_set;
extern TableType* count_set;
extern RecordType* net_stats; extern RecordType* net_stats;
@ -61,11 +62,8 @@ extern int ssl_store_key_material;
extern int ssl_max_cipherspec_size; extern int ssl_max_cipherspec_size;
extern StringVal* ssl_store_cert_path; extern StringVal* ssl_store_cert_path;
extern StringVal* x509_trusted_cert_path; extern StringVal* x509_trusted_cert_path;
extern TableType* cipher_suites_list;
extern RecordType* x509_type; extern RecordType* x509_type;
extern StringVal* x509_crl_file; extern StringVal* x509_crl_file;
extern TableType* x509_extension;
extern TableType* SSL_sessionID;
extern double non_analyzed_lifetime; extern double non_analyzed_lifetime;
extern double tcp_inactivity_timeout; extern double tcp_inactivity_timeout;

View file

@ -1,5 +1,3 @@
// $Id:$
#include "SSL-binpac.h" #include "SSL-binpac.h"
#include "TCP_Reassembler.h" #include "TCP_Reassembler.h"
#include "util.h" #include "util.h"
@ -8,13 +6,10 @@
bool SSL_Analyzer_binpac::warnings_generated = false; bool SSL_Analyzer_binpac::warnings_generated = false;
SSL_Analyzer_binpac::SSL_Analyzer_binpac(Connection* c) SSL_Analyzer_binpac::SSL_Analyzer_binpac(Connection* c)
: TCP_ApplicationAnalyzer(AnalyzerTag::SSL_BINPAC, c) : TCP_ApplicationAnalyzer(AnalyzerTag::SSL, c)
{ {
ssl = new binpac::SSL::SSLAnalyzer; interp = new binpac::SSL::SSLAnalyzer;
ssl->set_bro_analyzer(this); interp->set_bro_analyzer(this);
records = new binpac::SSLRecordLayer::SSLRecordLayerAnalyzer;
records->set_ssl_analyzer(ssl);
if ( ! warnings_generated ) if ( ! warnings_generated )
generate_warnings(); generate_warnings();
@ -22,23 +17,21 @@ SSL_Analyzer_binpac::SSL_Analyzer_binpac(Connection* c)
SSL_Analyzer_binpac::~SSL_Analyzer_binpac() SSL_Analyzer_binpac::~SSL_Analyzer_binpac()
{ {
delete records; delete interp;
delete ssl;
} }
void SSL_Analyzer_binpac::Done() void SSL_Analyzer_binpac::Done()
{ {
TCP_ApplicationAnalyzer::Done(); TCP_ApplicationAnalyzer::Done();
records->FlowEOF(true); interp->FlowEOF(true);
records->FlowEOF(false); interp->FlowEOF(false);
} }
void SSL_Analyzer_binpac::EndpointEOF(TCP_Reassembler* endp) void SSL_Analyzer_binpac::EndpointEOF(TCP_Reassembler* endp)
{ {
TCP_ApplicationAnalyzer::EndpointEOF(endp); TCP_ApplicationAnalyzer::EndpointEOF(endp);
records->FlowEOF(endp->IsOrig()); interp->FlowEOF(endp->IsOrig());
ssl->FlowEOF(endp->IsOrig());
} }
void SSL_Analyzer_binpac::DeliverStream(int len, const u_char* data, bool orig) void SSL_Analyzer_binpac::DeliverStream(int len, const u_char* data, bool orig)
@ -50,13 +43,13 @@ void SSL_Analyzer_binpac::DeliverStream(int len, const u_char* data, bool orig)
if ( TCP()->IsPartial() ) if ( TCP()->IsPartial() )
return; return;
records->NewData(orig, data, data + len); interp->NewData(orig, data, data + len);
} }
void SSL_Analyzer_binpac::Undelivered(int seq, int len, bool orig) void SSL_Analyzer_binpac::Undelivered(int seq, int len, bool orig)
{ {
TCP_ApplicationAnalyzer::Undelivered(seq, len, orig); TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
records->NewGap(orig, len); interp->NewGap(orig, len);
} }
void SSL_Analyzer_binpac::warn_(const char* msg) void SSL_Analyzer_binpac::warn_(const char* msg)

View file

@ -1,12 +1,9 @@
// $Id:$
#ifndef ssl_binpac_h #ifndef ssl_binpac_h
#define ssl_binpac_h #define ssl_binpac_h
#include "TCP.h" #include "TCP.h"
#include "ssl_pac.h" #include "ssl_pac.h"
#include "ssl-record-layer_pac.h"
class SSL_Analyzer_binpac : public TCP_ApplicationAnalyzer { class SSL_Analyzer_binpac : public TCP_ApplicationAnalyzer {
public: public:
@ -23,11 +20,9 @@ public:
static bool Available() static bool Available()
{ {
return FLAGS_use_binpac && return ( ssl_client_hello || ssl_server_hello ||
(ssl_certificate_seen || ssl_certificate || ssl_established || ssl_extension || ssl_alert ||
ssl_conn_attempt || ssl_conn_server_reply || x509_certificate || x509_extension || x509_error );
ssl_conn_established || ssl_conn_reused ||
ssl_conn_alert);
} }
static bool warnings_generated; static bool warnings_generated;
@ -35,8 +30,7 @@ public:
static void generate_warnings(); static void generate_warnings();
protected: protected:
binpac::SSLRecordLayer::SSLRecordLayerAnalyzer* records; binpac::SSL::SSLAnalyzer* interp;
binpac::SSL::SSLAnalyzer* ssl;
}; };
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -1,367 +0,0 @@
// $Id: SSLCiphers.h 1678 2005-11-08 19:16:37Z vern $
#ifndef SSL_CIPHERS_H
#define SSL_CIPHERS_H
#include "Dict.h"
// --- definitions for sslv3x cipher handling ---------------------------------
/*!
* In SSLv2, a cipher spec consists of three bytes.
*/
enum SSLv2_CipherSpec {
// --- standard SSLv2 ciphers
SSL_CK_RC4_128_WITH_MD5 = 0x010080,
SSL_CK_RC4_128_EXPORT40_WITH_MD5 = 0x020080,
SSL_CK_RC2_128_CBC_WITH_MD5 = 0x030080,
SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 0x040080,
SSL_CK_IDEA_128_CBC_WITH_MD5 = 0x050080,
SSL_CK_DES_64_CBC_WITH_MD5 = 0x060040,
SSL_CK_DES_192_EDE3_CBC_WITH_MD5 = 0x0700C0,
SSL_CK_RC4_64_WITH_MD5 = 0x080080
};
/*!
* In SSLv3x, a cipher spec consists of two bytes.
*/
enum SSL3_1_CipherSpec {
// --- standard SSLv3x ciphers
TLS_NULL_WITH_NULL_NULL = 0x0000,
TLS_RSA_WITH_NULL_MD5 = 0x0001,
TLS_RSA_WITH_NULL_SHA = 0x0002,
TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003,
TLS_RSA_WITH_RC4_128_MD5 = 0x0004,
TLS_RSA_WITH_RC4_128_SHA = 0x0005,
TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006,
TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007,
TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008,
TLS_RSA_WITH_DES_CBC_SHA = 0x0009,
TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A,
TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B,
TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C,
TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D,
TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E,
TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F,
TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010,
TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011,
TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012,
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013,
TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014,
TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015,
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016,
TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017,
TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018,
TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019,
TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A,
TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B,
// --- special SSLv3 ciphers
SSL_FORTEZZA_KEA_WITH_NULL_SHA = 0x001C,
SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA = 0x001D,
//SSL_FORTEZZA_KEA_WITH_RC4_128_SHA = 0x001E,
// -- RFC 2712 (ciphers not fully described in SSLCiphers.cc)
TLS_KRB5_WITH_DES_CBC_SHA = 0x001E,
TLS_KRB5_WITH_3DES_EDE_CBC_SHA = 0x001F,
TLS_KRB5_WITH_RC4_128_SHA = 0x0020,
TLS_KRB5_WITH_IDEA_CBC_SHA = 0x0021,
TLS_KRB5_WITH_DES_CBC_MD5 = 0x0022,
TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = 0x0023,
TLS_KRB5_WITH_RC4_128_MD5 = 0x0024,
TLS_KRB5_WITH_IDEA_CBC_MD5 = 0x0025,
TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = 0x0026,
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = 0x0027,
TLS_KRB5_EXPORT_WITH_RC4_40_SHA = 0x0028,
TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = 0x0029,
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = 0x002A,
TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = 0x002B,
// --- new AES ciphers
TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F,
TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030,
TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032,
TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034,
TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035,
TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036,
TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038,
TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039,
TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A,
TLS_RSA_WITH_NULL_SHA256 = 0x003B,
TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C,
TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D,
TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E,
TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040,
// -- RFC 4132
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041,
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042,
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043,
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044,
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045,
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046,
// -- Non-RFC. Widely deployed implementation (ciphers not fully described in SSLCiphers.cc)
TLS_RSA_EXPORT1024_WITH_RC4_56_MD5 = 0x0060,
TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5 = 0x0061,
TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA = 0x0062,
TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA = 0x0063,
TLS_RSA_EXPORT1024_WITH_RC4_56_SHA = 0x0064,
TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA = 0x0065,
TLS_DHE_DSS_WITH_RC4_128_SHA = 0x0066,
// -- RFC 5246 (ciphers not fully described in SSLCiphers.cc)
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067,
TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068,
TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A,
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B,
TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C,
TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D,
// -- RFC 5932
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084,
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085,
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086,
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087,
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088,
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089,
// -- RFC 4279 (ciphers not fully described in SSLCiphers.cc)
TLS_PSK_WITH_RC4_128_SHA = 0x008A,
TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B,
TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C,
TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D,
TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E,
TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F,
TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090,
TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091,
TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092,
TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093,
TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094,
TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095,
// -- RFC 4162
TLS_RSA_WITH_SEED_CBC_SHA = 0x0096,
TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097,
TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098,
TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099,
TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A,
TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009B,
// -- RFC 5288 (ciphers not fully described in SSLCiphers.cc)
TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C,
TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D,
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E,
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F,
TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0,
TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1,
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2,
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3,
TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4,
TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5,
TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6,
TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7,
// -- RFC 5487 (ciphers not fully described in SSLCiphers.cc)
TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8,
TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9,
TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA,
TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB,
TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC,
TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD,
TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE,
TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF,
TLS_PSK_WITH_NULL_SHA256 = 0x00B0,
TLS_PSK_WITH_NULL_SHA384 = 0x00B1,
TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2,
TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3,
TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4,
TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5,
TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6,
TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7,
TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8,
TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9,
// -- RFC 5932 (ciphers not fully described in SSLCiphers.cc)
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BA,
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BB,
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BC,
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BD,
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BE,
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BF,
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0,
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1,
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2,
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3,
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4,
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5,
// -- RFC 4492
TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001,
TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002,
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005,
TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006,
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007,
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B,
TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C,
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F,
TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010,
TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011,
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
TLS_ECDH_anon_WITH_NULL_SHA = 0xC015,
TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016,
TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017,
TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018,
TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019,
// -- RFC 5054 (ciphers not fully described in SSLCiphers.cc)
TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A,
TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B,
TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C,
TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D,
TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E,
TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F,
TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020,
TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021,
TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022,
// -- RFC 5289 (ciphers not fully described in SSLCiphers.cc)
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C,
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D,
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030,
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031,
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032,
// -- RFC 5489 (ciphers not fully described in SSLCiphers.cc)
TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033,
TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034,
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035,
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036,
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037,
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038,
TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039,
TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A,
TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B,
// --- special SSLv3 FIPS ciphers
SSL_RSA_FIPS_WITH_DES_CBC_SHA = 0xFEFE,
SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA = 0xFEFF,
SSL_RSA_FIPS_WITH_DES_CBC_SHA_2 = 0xFFE1,
SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA_2 = 0xFFE0,
// Tags for SSL 2 cipher kinds which are not specified for SSL 3.
SSL_RSA_WITH_RC2_CBC_MD5 = 0xFF80,
SSL_RSA_WITH_IDEA_CBC_MD5 = 0xFF81,
SSL_RSA_WITH_DES_CBC_MD5 = 0xFF82,
SSL_RSA_WITH_3DES_EDE_CBC_MD5 = 0xFF83,
TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF,
};
enum SSL_CipherType {
SSL_CIPHER_TYPE_STREAM,
SSL_CIPHER_TYPE_BLOCK,
SSL_CIPHER_TYPE_NULL
};
enum SSL_BulkCipherAlgorithm {
SSL_CIPHER_NULL,
SSL_CIPHER_RC4,
SSL_CIPHER_RC2,
SSL_CIPHER_DES,
SSL_CIPHER_3DES,
SSL_CIPHER_DES40,
SSL_CIPHER_FORTEZZA,
SSL_CIPHER_IDEA,
SSL_CIPHER_AES,
SSL_CIPHER_CAMELLIA,
SSL_CIPHER_SEED,
};
enum SSL_MACAlgorithm {
SSL_MAC_NULL,
SSL_MAC_MD5,
SSL_MAC_SHA
};
enum SSL_KeyExchangeAlgorithm {
SSL_KEY_EXCHANGE_NULL,
SSL_KEY_EXCHANGE_RSA,
SSL_KEY_EXCHANGE_RSA_EXPORT,
SSL_KEY_EXCHANGE_DH,
SSL_KEY_EXCHANGE_DH_DSS,
SSL_KEY_EXCHANGE_DH_DSS_EXPORT,
SSL_KEY_EXCHANGE_DH_RSA,
SSL_KEY_EXCHANGE_DH_RSA_EXPORT,
SSL_KEY_EXCHANGE_DHE_DSS,
SSL_KEY_EXCHANGE_DHE_DSS_EXPORT,
SSL_KEY_EXCHANGE_DHE_RSA,
SSL_KEY_EXCHANGE_DHE_RSA_EXPORT,
SSL_KEY_EXCHANGE_DH_anon,
SSL_KEY_EXCHANGE_DH_anon_EXPORT,
SSL_KEY_EXCHANGE_FORTEZZA_KEA,
// --- new 56 bit export ciphers
SSL_KEY_EXCHANGE_RSA_EXPORT1024,
SSL_KEY_EXCHANGE_DHE_DSS_EXPORT1024,
// -- Elliptic Curve key change algorithms (rfc4492)
SSL_KEY_EXCHANGE_ECDH_ECDSA,
SSL_KEY_EXCHANGE_ECDHE_ECDSA,
SSL_KEY_EXCHANGE_ECDH_RSA,
SSL_KEY_EXCHANGE_ECDHE_RSA,
SSL_KEY_EXCHANGE_ECDH_anon,
};
#if 0
struct SSL_CipherSpecImprove {
uint32 identifier;
// SSL_CipherType cipherType;
SSL_BulkCipherAlgorithm encryptionAlgorithm;
SSL_BulkCipherAlgorithm authenticationAlgorithm;
SSL_BulkCipherAlgorithm keyAlgorithm;
SSL_MACAlgorithm macAlgorithm;
int clearkeySize;
int encryptedkeySize;
uint32 flags; // IsExportable IsSSLv2 IsSSLv30 IsSSLv31
const char* fullName = "TLS_WITH_NULL_NULL";
};
#endif
struct SSL_CipherSpec {
uint32 identifier; ///< type code of the CIPHER-SPEC (2 or 3 Bytes)
SSL_CipherType cipherType;
uint32 flags;
SSL_BulkCipherAlgorithm bulkCipherAlgorithm;
SSL_MACAlgorithm macAlgorithm;
SSL_KeyExchangeAlgorithm keyExchangeAlgorithm;
int clearKeySize; ///< size in bits of plaintext part of master key
int encryptedKeySize; ///< size in bits of encrypted part of master key
int hashSize;
};
const uint32 SSL_FLAG_EXPORT = 0x0001; ///< set if exportable cipher
const uint32 SSL_FLAG_SSLv20 = 0x0002; ///< set if cipher defined for SSLv20
const uint32 SSL_FLAG_SSLv30 = 0x0004; ///< set if cipher defined for SSLv30
const uint32 SSL_FLAG_SSLv31 = 0x0008; ///< set if cipher defined for SSLv31
declare(PDict, SSL_CipherSpec);
extern PDict(SSL_CipherSpec) SSL_CipherSpecDict;
extern SSL_CipherSpec SSL_CipherSpecs[];
extern const uint SSL_CipherSpecs_Count;
#endif

View file

@ -1,48 +0,0 @@
// $Id: SSLDefines.h 80 2004-07-14 20:15:50Z jason $
// Defines the states and transitions in the ssl-protocol-machine.
#ifndef SSL_DEFINES_H
#define SSL_DEFINES_H
const int SSL3_1_NUM_STATES = 20;
enum SSL3_1_States {
SSL3_1_STATE_ERROR = 0,
SSL3_1_STATE_INIT = 1,
SSL3_1_STATE_SERVER_HELLO_REQ_SENT = 2,
SSL3_1_STATE_CLIENT_HELLO_SENT = 3,
SSL3_1_STATE_SERVER_HELLO_SENT = 4,
SSL3_1_STATE_SERVER_CERT_SENT = 5,
SSL3_1_STATE_SERVER_KEY_EXCHANGE_SENT = 6,
SSL3_1_STATE_SERVER_CERT_REQ_SENT = 7,
SSL3_1_STATE_SERVER_HELLO_DONE_SENT_A = 8,
SSL3_1_STATE_SERVER_HELLO_DONE_SENT_B = 9,
SSL3_1_STATE_CLIENT_KEY_EXCHANGE_SENT_A = 10,
SSL3_1_STATE_CLIENT_KEY_EXCHANGE_SENT_B = 11,
SSL3_1_STATE_CLIENT_CERT_SENT = 12,
SSL3_1_STATE_CLIENT_CERT_VERIFY_SENT = 13,
SSL3_1_STATE_CLIENT_FIN_SENT_A = 14,
SSL3_1_STATE_SERVER_FIN_SENT_A = 15,
SSL3_1_STATE_CLIENT_FIN_SENT_B = 16,
SSL3_1_STATE_SERVER_FIN_SENT_B = 17,
SSL3_1_STATE_HS_FIN_A = 18,
SSL3_1_STATE_HS_FIN_B = 19
};
const int SSL3_1_NUM_TRANS = 11;
enum SSL_3_1_Transitions {
SSL3_1_TRANS_SERVER_HELLO_REQ = 0,
SSL3_1_TRANS_CLIENT_HELLO = 1,
SSL3_1_TRANS_SERVER_HELLO = 2,
SSL3_1_TRANS_SERVER_CERT = 3,
SSL3_1_TRANS_SERVER_KEY_EXCHANGE = 4,
SSL3_1_TRANS_SERVER_CERT_REQ = 5,
SSL3_1_TRANS_SERVER_HELLO_DONE = 6,
SSL3_1_TRANS_CLIENT_CERT = 3,
SSL3_1_TRANS_CLIENT_KEY_EXCHANGE = 7,
SSL3_1_TRANS_CLIENT_CERT_VERIFY = 8,
SSL3_1_TRANS_CLIENT_FIN = 9,
SSL3_1_TRANS_SERVER_FIN = 10
};
#endif

View file

@ -1,553 +0,0 @@
// $Id: SSLInterpreter.cc 5988 2008-07-19 07:02:12Z vern $
#include "SSLInterpreter.h"
#include "SSLv2.h"
#include "X509.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
declare(PDict, CertStore);
PDict(CertStore) cert_states;
// --- Initalization of static variables --------------------------------------
uint32 SSL_Interpreter::analyzedCertificates = 0;
uint32 SSL_Interpreter::verifiedCertificates = 0;
uint32 SSL_Interpreter::failedCertificates = 0;
uint32 SSL_Interpreter::certificateChains = 0;
// --- SSL_Interpreter --------------------------------------------------------
/*!
* The constructor.
*
* \param proxy Pointer to the SSLProxy_Analyzer which created this instance.
*/
SSL_Interpreter::SSL_Interpreter(SSLProxy_Analyzer* proxy)
{
this->proxy = proxy;
}
/*!
* The destructor.
*/
SSL_Interpreter::~SSL_Interpreter()
{
delete orig;
delete resp;
}
/*!
* Analogous to TCP_Connection::Init(), this method calls
* BuildInterpreterEndpoints() to create the corresponding endpoints.
*/
void SSL_Interpreter::Init()
{
BuildInterpreterEndpoints();
orig->SetPeer(resp);
resp->SetPeer(orig);
}
/*!
* This method analyzes a given certificate (chain), using the OpenSSL library.
*
* \param s Pointer to the SSL_InterpreterEndpoint which received the
* cerificate (chain).
* \param data Pointer to the data block which contains the certificate (chain).
* \param length Size of the data block.
* \param type the certificate type
* \param isChain false if data is pointing to a single certificate,
* true if data is pointing to a certificate chain
* mod by scott in:
* uint32 ip_address = *(s->proxyEndpoint->Endpoint()->dst_addr);
* uint16 port = (uint16) s->proxyEndpoint->Endpoint()->conn->RespPort();
* inserting endpoint
*/
void SSL_Interpreter::analyzeCertificate(SSL_InterpreterEndpoint* s,
const u_char* data, int length, uint8 type, bool isChain)
{
// See if we should continue with this certificate.
if ( ssl_certificate_seen )
{
val_list* vl = new val_list;
vl->append(proxy->BuildConnVal());
vl->append(new Val(! s->IsOrig(), TYPE_BOOL));
proxy->ConnectionEvent(ssl_certificate_seen, vl);
}
++analyzedCertificates;
const u_char* pCert = data;
uint32 certLength = length;
uint certCount = 0;
if ( isChain )
{
++certificateChains;
// Sum of all cert sizes has to match certListLength.
int tempLength = 0;
while ( tempLength < length )
{
++certCount;
uint32 certLength =
uint32((data[tempLength + 0] << 16) |
data[tempLength + 1] << 8) |
data[tempLength + 2];
tempLength += certLength + 3;
}
if ( tempLength > length )
{
Weird( "SSLv3x: sum of size of certificates doesn't match size of certificate chain" );
return;
}
// Get the first certificate.
pCert = data + 3;
certLength = uint32((data[0] << 16) | data[1] << 8) | data[2];
}
// Create a hashsum of the current certificate.
hash_t hashsum = HashKey::HashBytes(pCert, certLength);
if ( ! proxy->TCP() )
return;
TCP_Endpoint* endp = s->IsOrig() ? proxy->TCP()->Orig() : proxy->TCP()->Resp();
// Check if we've seen a certificate from this addr/port before.
uint8 key[6];
// ### Won't work for IPv6.
uint32 ip_address = *(endp->dst_addr);
uint16 port = uint16(proxy->Conn()->RespPort());
memcpy(key, &ip_address, 4);
memcpy(&key[4], &port, 2);
HashKey h(key, sizeof(key));
CertStore* pCertState = 0;
pCertState = (CertStore*) cert_states.Lookup(&h);
if ( ! pCertState )
{ // new address
pCertState = new CertStore(ip_address, port, hashsum, certLength);
cert_states.Insert(&h, pCertState);
}
else
{
// We've seen this address/certificate before. Check if
// certificate changed.
if ( ! pCertState->isSameCert(hashsum, certLength) )
{
// This shouldn't happen; ### make a stronger error.
Weird("SSL: Certificate changed for this ip+port !!!");
// Update status.
++pCertState->changes;
pCertState->certHash = hashsum;
pCertState->certSize = certLength;
pCertState->isValid = -1;
}
else
{ // cert didn't change
if ( pCertState->isValid == 0 )
{
// This is an invalid cert, but we
// warned before.
}
// Save time - don't analyze it any further.
return;
}
}
// Certificate verification.
if ( ssl_verify_certificates != 0 )
{
++verifiedCertificates;
int invalid = 0;
switch ( type ) {
case SSLv2_CT_X509_CERTIFICATE:
if ( ! isChain )
invalid = X509_Cert::verify(s->GetProxyEndpoint(),
pCert, certLength);
else
invalid = X509_Cert::verifyChain(s->GetProxyEndpoint(),
data, length);
break;
default:
Weird("SSL: Unknown CERTIFICATE-TYPE!");
invalid = 1; // quick 'n dirty :)
break;
}
if ( invalid )
{
proxy->Weak("SSL: Certificate check FAILED!");
pCertState->isValid = 0;
++failedCertificates;
}
else
pCertState->isValid = 1;
}
// Store the certificate.
if ( ssl_store_certificates != 0 )
{
// Let's hope the address is currently in network byte order!
in_addr addr;
addr.s_addr = ip_address;
char* pDummy = inet_ntoa(addr);
char sFileName[PATH_MAX];
if ( ssl_store_cert_path &&
ssl_store_cert_path->AsString()->Len() > 0 )
{
const BroString* pString = ssl_store_cert_path->AsString();
safe_snprintf(sFileName, PATH_MAX, "%s/cert.%s-server-c%i.der",
pString->Bytes(), pDummy, pCertState->changes);
}
else
safe_snprintf(sFileName, PATH_MAX, "cert.%s-server-c%i.der",
pDummy, pCertState->changes);
FILE* certFile = fopen(sFileName, "wb");
if ( ! certFile )
{
Weird(fmt("SSL_Interpreter::analyzeCertificate(): Error opening '%s'!\n", sFileName));
return;
}
fwrite(pCert, 1, certLength, certFile);
fclose(certFile);
}
// TODO: test if cert is valid for the address we got it from.
}
/*!
* \return the originating SSL_InterpreterEndpoint
*/
SSL_InterpreterEndpoint* SSL_Interpreter::Orig() const
{
return orig;
}
/*!
* \return the responding SSL_InterpreterEndpoint
*/
SSL_InterpreterEndpoint* SSL_Interpreter::Resp() const
{
return resp;
}
/*!
* \param p Pointer to an SSL_InterpreterEndpoint to test
*
* \return true if p is the originating SSL_InterpreterEndpoint,
* false otherwise
*/
int SSL_Interpreter::Is_Orig(SSL_InterpreterEndpoint* p) const
{
return p == orig;
}
/*!
* \return the responding SSL_InterpreterEndpoint
*/
SSLProxy_Analyzer* SSL_Interpreter::Proxy() const
{
return proxy;
}
/*!
* This methods prints a string into the "weird" log file.
*
* \param name String to log into the "weird" file.
*/
void SSL_Interpreter::Weird(const char* name) const
{
proxy->Weird(name);
}
/*!
* Prints some counters.
*/
void SSL_Interpreter::printStats()
{
printf("SSL_Interpreter:\n");
printf("analyzedCertificates = %u\n", analyzedCertificates);
printf("verifiedCertificates = %u\n", verifiedCertificates);
printf("failedCertificates = %u\n", failedCertificates);
printf("certificateChains = %u\n", certificateChains);
}
/*!
* Wrapper function for the event ssl_conn_attempt.
*
* \param sslVersion the SSL version for which the event occured
*
* \see SSLProxy_Analyzer::SSL_Versions
*/
void SSL_Interpreter::fire_ssl_conn_attempt(uint16 sslVersion,
TableVal* currentCipherSuites)
{
EventHandlerPtr event = ssl_conn_attempt;
if ( event )
{
val_list* vl = new val_list;
vl->append(proxy->BuildConnVal());
vl->append(new Val(sslVersion, TYPE_INT));
vl->append(currentCipherSuites);
proxy->ConnectionEvent(event, vl);
}
}
/*!
* Wrapper function for the event ssl_conn_server_reply.
*
* \param sslVersion the SSL version for which the event occured
*
* \see SSLProxy_Analyzer::SSL_Versions
*/
void SSL_Interpreter::fire_ssl_conn_server_reply(uint16 sslVersion,
TableVal* currentCipherSuites)
{
EventHandlerPtr event = ssl_conn_server_reply;
if ( event )
{
val_list* vl = new val_list;
vl->append(proxy->BuildConnVal());
vl->append(new Val(sslVersion, TYPE_INT));
vl->append(currentCipherSuites);
proxy->ConnectionEvent(event, vl);
}
}
/*!
* Wrapper function for the event ssl_conn_established.
*
* \param sslVersion the SSL version for which the event occured
* \param cipherSuite constant indicating the used SSL cipher suite
*
* \see SSLProxy_Analyzer::SSL_Versions, SSLv2_CipherSpecs and SSL3_1_CipherSpec.
*/
void SSL_Interpreter::fire_ssl_conn_established(uint16 sslVersion,
uint32 cipherSuite)
{
EventHandlerPtr event = ssl_conn_established;
if ( event )
{
val_list* vl = new val_list;
vl->append(proxy->BuildConnVal());
vl->append(new Val(sslVersion, TYPE_INT));
vl->append(new Val(cipherSuite, TYPE_COUNT));
proxy->ConnectionEvent(event, vl);
}
}
/*!
* Wrapper function for the event ssl_conn_reused
*
* \param pData Pointer to a SSL_DataBlock which contains the SSL session ID
* of the originating ssl session.
*/
void SSL_Interpreter::fire_ssl_conn_reused(const SSL_DataBlock* pData)
{
EventHandlerPtr event = ssl_conn_reused;
if ( event )
{
val_list* vl = new val_list;
vl->append(proxy->BuildConnVal());
vl->append(MakeSessionID(pData->data, pData->len));
proxy->ConnectionEvent(event, vl);
}
}
/*!
* Wrapper function for the event ssl_conn_alert
*
* \param sslVersion the SSL version for which the event occured
* \param level constant indicating the level of severity
* \param description constant indicating the type of alert/error
*
* \see SSLProxy_Analyzer::SSL_Versions, SSL3x_AlertLevel, SSL3_1_AlertDescription
* and SSLv2_ErrorCodes.
*/
void SSL_Interpreter::fire_ssl_conn_alert(uint16 sslVersion, uint16 level,
uint16 description)
{
if ( ssl_conn_alert )
{
EventHandlerPtr event = ssl_conn_alert;
if ( event )
{
val_list* vl = new val_list;
vl->append(proxy->BuildConnVal());
vl->append(new Val(sslVersion, TYPE_INT));
vl->append(new Val(level, TYPE_COUNT));
vl->append(new Val(description, TYPE_COUNT));
proxy->ConnectionEvent(event, vl);
}
}
}
// Generate a session ID table. Returns an empty table
// if len is zero.
TableVal* SSL_Interpreter::MakeSessionID(const u_char* data, int len)
{
TableVal* sessionIDTable = new TableVal(SSL_sessionID);
if ( ! len )
return sessionIDTable;
for ( int i = 0; i < len; i += 4 )
{
uint32 temp = (data[i] << 24) | (data[i + 1] << 16) |
(data[i + 2] << 8) | data[i + 3];
Val* index = new Val(i / 4, TYPE_COUNT);
sessionIDTable->Assign(index, new Val(temp, TYPE_COUNT));
Unref(index);
}
return sessionIDTable;
}
//--- SSL_InterpreterEndpoint -------------------------------------------------
/*!
* The constructor.
*
* \param interpreter Pointer to the instance of an SSL_Interpreter to which
* this endpoint belongs to.
* \param is_orig true if this endpoint is the originator of the connection,
* false otherwise
* SC: an adjustment was made here since the endpoints are now assosciated with
* TCP_Contents base objects rather than TCP_Endpoint.
*/
SSL_InterpreterEndpoint::SSL_InterpreterEndpoint(SSL_Interpreter* arg_interpreter,
bool arg_is_orig )
{
interpreter = arg_interpreter;
is_orig = arg_is_orig;
proxyEndpoint = new Contents_SSL(interpreter->Proxy()->Conn(), is_orig);
ourProxyEndpoint = true;
}
/*!
* The destructor.
*/
SSL_InterpreterEndpoint::~SSL_InterpreterEndpoint()
{
SetProxyEndpoint(0);
}
/*!
* \return true if there's currently data pending for this endpoint,
* false otherwise
*/
bool SSL_InterpreterEndpoint::isDataPending()
{
return proxyEndpoint->isDataPending();
}
/*!
* Sets the peer of this endpoint.
*
* \param p Pointer to an interpreter endpoint which will be set as the peer
* of this endpoint.
*/
void SSL_InterpreterEndpoint::SetPeer(SSL_InterpreterEndpoint* p)
{
peer = p;
}
/*!
* Sets the proxy endpoint of this endpoint.
*
* \param p Pointer to a Contents_SSL analyzer which will be set as the proxy endpoint
* of this endpoint.
*/
void SSL_InterpreterEndpoint::SetProxyEndpoint(Contents_SSL* p)
{
if ( ourProxyEndpoint )
{
proxyEndpoint->Done();
delete proxyEndpoint;
ourProxyEndpoint = false;
}
proxyEndpoint = p;
}
/*!
* \return is_orig true if this endpoint is the originator of the connection,
* false otherwise
*/
int SSL_InterpreterEndpoint::IsOrig() const
{
return is_orig;
}
/*!
* \return the peer of this endpoint
*/
SSL_InterpreterEndpoint* SSL_InterpreterEndpoint::Peer() const
{
return peer;
}
/*!
* \return the interpreter of this endpoint
*/
SSL_Interpreter* SSL_InterpreterEndpoint::Interpreter() const
{
return interpreter;
}
// --- CertStore --------------------------------------------------------------
/*
* The constructor.
*
* \param ip ip adress where this certificate came from
* \param port port number where this certificate came from
* \param hash hahssum for this certificate
* \param size of this certificate in bytes
*/
CertStore::CertStore(uint32 ip, uint32 arg_port, hash_t hash, int size)
{
ip_addr = ip;
certHash = hash;
certSize = size;
isValid = -1;
changes = 0;
port = arg_port;
}
/*
* This method can be used to compare certificates by certain criterias.
*
* \param hash hashsum of the certificate to compare
* \param size size of the certificate to compare
*
* \return true if the criterias match, false otherwise
*/
bool CertStore::isSameCert(hash_t hash, int length)
{
return hash == certHash && length == certSize;
}

View file

@ -1,142 +0,0 @@
// $Id: SSLInterpreter.h 5988 2008-07-19 07:02:12Z vern $
#ifndef sslinterpreter_h
#define sslinterpreter_h
#include "util.h"
#include "SSLProxy.h"
// --- forward declarations ----------------------------------------------------
class SSLProxy_Analyzer;
class Contents_SSL;
class SSL_InterpreterEndpoint;
class SSL_DataBlock;
// --- SSL_Interpreter --------------------------------------------------------
/*!
* \brief This class is the abstract base-class for the different ssl
* interpreters used for the different ssl versions.
*
* Since there is currently no support in Bro for a change of the connection
* type (IMAP -> TLS, for example), we decided not to inherit from the class
* Connection. This way, we can easily switch to SSLv3x after we've seen (and
* analyzed) a SSLv2 client hello record with a version number > SSLv2.
*
* There currently two (non-abstract) interpreters: SSLv2_Interpreter and
* SSLv3_Interpreter. The first one supports SSL 2.0, the second one supports
* both SSL 3.0 and SSL 3.1/TLS 1.0.
*
* See SSLProxy_Analyzer for additional information.
*/
class SSL_Interpreter {
public:
SSL_Interpreter(SSLProxy_Analyzer* proxy);
virtual ~SSL_Interpreter();
static uint32 analyzedCertificates; ///< how often analyzeCertificate() has been called
static uint32 verifiedCertificates; ///< how many certificates have actually been verified
static uint32 failedCertificates; ///< how many certificates have failed verification
static uint32 certificateChains; ///< counter for certificate chains
// In order to initialize the correct SSL_InterpreterEndpoints,
// override it in the corresponding subclass.
virtual void BuildInterpreterEndpoints() = 0;
virtual void Init();
SSL_InterpreterEndpoint* Orig() const;
SSL_InterpreterEndpoint* Resp() const;
SSLProxy_Analyzer* Proxy() const;
int Is_Orig(SSL_InterpreterEndpoint* p) const;
virtual void analyzeCertificate(SSL_InterpreterEndpoint* s,
const u_char* data, int length,
uint8 type, bool isChain);
void Weird(const char* name) const;
static void printStats();
void fire_ssl_conn_attempt(uint16 sslVersion,
TableVal* currentCipherSuites);
void fire_ssl_conn_server_reply(uint16 sslVersion,
TableVal* currentCipherSuites);
void fire_ssl_conn_established(uint16 sslVersion, uint32 cipherSuite);
void fire_ssl_conn_reused(const SSL_DataBlock* pData);
void fire_ssl_conn_alert(uint16 sslVersion, uint16 level,
uint16 description);
protected:
TableVal* MakeSessionID(const u_char* data, int len);
SSLProxy_Analyzer* proxy;
SSL_InterpreterEndpoint* orig;
SSL_InterpreterEndpoint* resp;
};
// --- SSL_InterpreterEndpoint ------------------------------------------------
/*!
* \brief This abstract class represents the SSL_InterpreterEndpoints for the
* SSL_Interpreter.
*
* The key-method is Deliver() which receives the ssl records
* from the SSLProxy_Analyzer. So overwrite the Deliver()-method and do
* whatever analysis on the record content (and/or pass it to the corresponding
* SSL_Interpreter).
*/
class SSL_InterpreterEndpoint {
public:
SSL_InterpreterEndpoint(SSL_Interpreter* interpreter, bool is_orig);
virtual ~SSL_InterpreterEndpoint();
/**This method is called by corresponding SSLProxy_Analyzer and
* delivers the data.
* @param t time, when the segment was received by bro (?)
* @param len length of TCP-Segment
* @param data content of TCP-Segment
*/
virtual void Deliver(int len, const u_char* data) = 0;
bool isDataPending();
void SetPeer(SSL_InterpreterEndpoint* p);
int IsOrig() const;
SSL_InterpreterEndpoint* Peer() const;
SSL_Interpreter* Interpreter() const;
Contents_SSL* GetProxyEndpoint() { return proxyEndpoint; }
void SetProxyEndpoint(Contents_SSL* proxyEndpoint);
protected:
SSL_Interpreter* interpreter; ///< Pointer to the SSL_Interpreter to which this endpoint belongs to
SSL_InterpreterEndpoint* peer; ///< Pointer to the peer of this endpoint
Contents_SSL* proxyEndpoint; ///< Pointer to the corresponding Contents_SSL
bool ourProxyEndpoint; // true if we need to delete the proxyEndpoint
int is_orig; ///< true if this endpoint is the originator of the connection, false otherwise
};
// --- class CertStore --------------------------------------------------------
/*!
* \brief This class is used to store some information about a X509 certificate.
*
* To save memory, we only store some characteristic criterias about a
* certificate, that's currently it's size and a hashsum.
*
* \note This class is currently <b>experimental</b>.
*/
class CertStore {
public:
uint32 ip_addr; ///< ip address where this certificate is from
uint32 port; ///< port number where this certificate is from
int certSize; ///< size of the certificate in bytes
hash_t certHash; ///< hashsum obver the entire certificate
int isValid; ///< boolean value indicating if the certificate is valid
int changes; ///< counter for how often this certificate has changed for the above ip + port number
CertStore(uint32 ip, uint32 port, hash_t hash, int size);
bool isSameCert(hash_t hash, int length);
};
#endif

View file

@ -1,830 +0,0 @@
// $Id: SSLProxy.cc 6008 2008-07-23 00:24:22Z vern $
#include "SSLProxy.h"
#include "SSLv3.h"
#include "SSLv2.h"
// --- Initalization of static variables --------------------------------------
uint SSLProxy_Analyzer::totalPackets = 0;
uint SSLProxy_Analyzer::totalRecords = 0;
uint SSLProxy_Analyzer::nonSSLConnections = 0;
// --- SSL_DataBlock --------------------------------------------------------
/*!
* This constructor will allocate a block of data on the heap. If min_len is
* given, it will determine the minimum size of the new block. The data block
* referenced by data will be then be copied into the new block.
*
* \param data Pointer to the data which will be copied into the newly
* allocated heap block.
* \param len Length of the data block to copy.
* \param min_len The minimum size of data to allocate on the heap, can be omitted.
*/
SSL_DataBlock::SSL_DataBlock(const u_char* arg_data, int len, int min_len)
{
// For performance reasons, we allocate at least min_len.
if ( len < min_len )
{
data = new u_char[min_len];
size = min_len;
}
else
{
data = new u_char[len];
this->size = len;
}
memcpy(data, arg_data, len);
this->len = len;
next = 0;
}
/*!
* This is an experimental function which will print the contents of the
* internal data block in a human-readable fashion to a stream.
*
* \param stream The stream for printing the data block to.
*/
void SSL_DataBlock::toStream(FILE* stream) const
{
if ( len <= 0 )
return;
int idx;
for ( idx = 0; idx < len-1; ++idx )
fprintf(stream, "%02X:", data[idx]);
fprintf(stream, "%02X", data[idx]);
}
/*!
* This is an experimental function which will print the contents of the
* internal data block in a human-readable fashion to a string.
*
* \return A string which has to be freed by the caller.
*/
char* SSL_DataBlock::toString() const
{
if ( len <= 0 )
{
// Currently, we return an empty string if data block is empty.
char* pDummy = new char[1];
pDummy[0] = '\0';
return pDummy;
}
char* pString = new char[len*3];
char* pItx = pString;
int idx;
for ( idx = 0; idx < len-1; ++idx )
{
sprintf(pItx, "%02X:", data[idx]);
pItx += 3;
}
sprintf(pItx, "%02X", data[idx]);
return pString;
}
// --- SSL_RecordBuilder ------------------------------------------------------
uint SSL_RecordBuilder::maxAllocCount = 0;
uint SSL_RecordBuilder::maxFragmentCount = 0;
uint SSL_RecordBuilder::fragmentedHeaders = 0;
/*!
* The constructor takes an Contents_SSL as parameter. Whenever a SSL
* record has been reassembled, the DoDeliver() function of this
* Contents_SSL will be called.
*
* \param sslEndpoint The Contents_SSL to which this instance of
* SSL_RecordBuilder is bound.
*/
SSL_RecordBuilder::SSL_RecordBuilder(Contents_SSL* arg_sslEndpoint)
{
head = tail = 0;
currentSize = 0;
expectedSize = -1; // -1 means we don't know yet
hasPendingData = false;
fragmentCounter = 0;
neededSize = 5; // we need at least 5 bytes to determine version
sslEndpoint = arg_sslEndpoint;
}
/*!
* The destructor frees the chain of SSL_DataBlocks.
*/
SSL_RecordBuilder::~SSL_RecordBuilder()
{
// Free the data chain.
SSL_DataBlock* idx = head;
SSL_DataBlock* rm;
while ( idx )
{
rm = idx;
idx = idx->next;
delete rm;
}
}
/*!
* This function is the main entry point of the class. Call it with a segment
* of data to process.
*
* \param data pointer to a data segment that will be reassembled
* \param length length of the data segment to be reassembled
*
* \return true if succesfull, false otherwise
*/
bool SSL_RecordBuilder::addSegment(const u_char* data, int length)
{
while ( length > 0 )
{
if ( ! head )
{
// This is the first fragment of a SSLv2 record,
// so we analyze the header.
// Special case: SSL header has been fragmented.
if ( length < neededSize )
{
// We can't determine the record size yet,
// so we just add this stuff.
++fragmentedHeaders;
head = tail = new SSL_DataBlock(data, length,
MIN_ALLOC_SIZE);
currentSize += length;
expectedSize = -1; // special meaning
break;
}
// Get the expected length of this record.
if ( ! computeExpectedSize(data, length) )
return false;
if ( neededSize > expectedSize )
{
sslEndpoint->Weird("SSL_RecordBuilder::addSegment neededSize > expectedSize");
return false;
}
if ( tail != 0 )
{
sslEndpoint->Parent()->Weird("SSL_RecordBuilder::addSegment tail != 0");
return false;
}
if ( length > expectedSize )
{
// No fragmentation -> no memory-reallocation.
// We have additional data pending.
hasPendingData = true;
sslEndpoint->DoDeliver(expectedSize, data);
length -= expectedSize;
data += expectedSize;
expectedSize = -1;
}
else if ( length == expectedSize )
{
// No fragmentation -> no memory-reallocation.
// No additional data pending.
hasPendingData = false;
sslEndpoint->DoDeliver(expectedSize, data);
length -= expectedSize;
data += expectedSize;
expectedSize = -1;
break;
}
else
{
// First fragment of a record.
head = tail = new SSL_DataBlock(data, length,
MIN_ALLOC_SIZE);
currentSize += length;
break;
}
continue;
}
// ! head.
// We already have some data, so add the current
// segment special case.
if ( expectedSize < 0 )
{
// We don't know the expected size of
// this record yet.
if ( currentSize + length < neededSize )
{
// We still can't determine the expected size,
// so we just add the current fragment.
addData(data, length);
break;
}
// Now we can determine the expected size the
// header has been fragmented, so we have to
// reassemble it.
uint8 Header[neededSize];
memcpy(Header, head->data, head->len);
memcpy(Header + head->len, data, neededSize - head->len);
if ( ! computeExpectedSize(Header, neededSize) )
{
// Since neededSize <= MIN_ALLOC_SIZE,
// we free only head.
delete head;
head = tail = 0;
return false;
}
if ( neededSize > expectedSize )
{
sslEndpoint->Parent()->Weird("SSL_RecordBuilder::addSegment neededSize > expectedSize");
return false;
}
// No break, go on with this packet.
}
if ( currentSize + length == expectedSize )
{ // this is exactly the last segment of the record
hasPendingData = false;
// Create a continuous data structure and call
// DoDeliver().
u_char* pBlock = assembleBlocks(data, length);
sslEndpoint->DoDeliver(expectedSize, pBlock);
delete [] pBlock;
expectedSize = -1;
break;
}
else if ( currentSize + length < expectedSize )
{ // another (middle) segment
if ( length <= MIN_FRAGMENT_SIZE )
sslEndpoint->Parent()->Weird("SSLProxy: Excessive small TCP Segment!");
addData(data, length);
break;
}
else
{
// This is the last fragment of the current record,
// but there's more data in this segment.
int deltaSize = expectedSize - currentSize;
hasPendingData = true;
// Create a continuous data structure and call
// DoDeliver().
u_char* pBlock = assembleBlocks(data, deltaSize);
sslEndpoint->DoDeliver(expectedSize, pBlock);
delete [] pBlock;
expectedSize = -1;
// Process the rest.
length -= deltaSize;
data += deltaSize;
}
} // while
return true;
}
/*!
* This function is called internally by addSegment(), and add's a new SSL
* record fragment to the internally used list of SSL_DataBlocks. Note that
* the data will be copied!
*
* \param data pointer to the data that will be added
* \param length length of the data that will be added
*/
inline void SSL_RecordBuilder::addData(const u_char* data, int length)
{
++fragmentCounter;
// Check if there's some space left in the last datablock.
int bytesLeft = tail->size - tail->len;
if ( bytesLeft > 0 )
{
// There's some space left in the last data block.
if ( bytesLeft >= length )
{
// We can store all bytes in the last data block.
memcpy(tail->data + tail->len, data, length);
tail->len += length;
currentSize += length;
}
else
{
// We cannot store all bytes in the last data block,
// so we also need to add a new one.
memcpy(tail->data + tail->len, data, bytesLeft);
tail->len = tail->size;
currentSize += length;
data += bytesLeft;
length -= bytesLeft;
tail->next = new SSL_DataBlock(data, length, MIN_ALLOC_SIZE);
tail = tail->next;
}
}
else
{
// Last data block is full.
tail->next = new SSL_DataBlock(data, length, MIN_ALLOC_SIZE);
tail = tail->next;
currentSize += length;
}
}
/*!
* This function is called internally by addSegment(), whenever a SSL record
* has been fully received. It creates a single data block from the list of
* SSL record fragments while freeing them.
*
* \param data pointer to the last SSL record fragment
* \param length size of the last SSL record fragment
*
* \return pointer to a data block which contains the reassembled SSL record
*/
u_char* SSL_RecordBuilder::assembleBlocks(const u_char* data, int length)
{
// We don't store the last SSL record fragment in a DataBlock,
// instead we get it directly as parameter.
u_char* dataptr = new u_char[currentSize + length];
u_char* nextseg = dataptr;
SSL_DataBlock* idx = head;
SSL_DataBlock* rm;
uint allocCounter = 0;
while ( idx )
{
++allocCounter;
memcpy(nextseg, idx->data, idx->len);
nextseg += idx->len;
rm = idx;
idx = idx->next;
delete rm;
}
// The last fragment isn't stored in a datablock.
memcpy(nextseg, data, length);
// The first and last fragments aren't counted.
fragmentCounter += 2;
// Update statistics.
if ( allocCounter > maxAllocCount )
maxAllocCount = allocCounter;
if ( fragmentCounter > maxFragmentCount )
maxFragmentCount = fragmentCounter;
fragmentCounter = 0;
currentSize = 0;
head = tail = 0;
return dataptr;
}
/*!
* This method is called internally by computeExpectedSize(), when the SSL
* record format has not been determined yet. It tries to do so by using
* heuristics, since there's no definitive way to distinguish SSLv2 vs. SSLv3
* record headers.
*
* \param data pointer to a data block containing the SSL record to analyze
* \param length length of the SSL record to analyze, has to be >= neededSize!
*
* \return
* - 2 for SSLv2 record format
* - 3 for SSLv3 record format
* - -1 if an error occurred
*/
int SSL_RecordBuilder::analyzeSSLRecordFormat(const u_char* data, int length)
{
// We have to use heuristics for this one.
if ( length < neededSize )
{
sslEndpoint->Parent()->Weird("SSLProxy: analyzeSSLRecordFormat length < neededSize");
return -1;
}
bool found_ssl3x = 0;
bool found_ssl2x = 0;
// SSLv3x-check.
SSL3_1_ContentType ct = SSL3_1_ContentType(uint8(*data));
switch ( ct ) {
case SSL3_1_TYPE_CHANGE_CIPHER_SPEC:
case SSL3_1_TYPE_ALERT:
case SSL3_1_TYPE_HANDSHAKE:
case SSL3_1_TYPE_APPLICATION_DATA:
{
sslEndpoint->sslVersion = ((data[1]) << 8) | data[2];
uint16 v = sslEndpoint->sslVersion;
if ( v == uint16(SSLProxy_Analyzer::SSLv30) ||
v == uint16(SSLProxy_Analyzer::SSLv31) )
found_ssl3x = true;
break;
}
}
// SSLv2 check.
// We look for CLIENT-HELLOs, SERVER-HELLOs and ERRORs.
const u_char* pContents = data;
uint offset = 0;
uint16 size = 0;
if ( (data[0] & 0x80) > 0 )
{ // we have a two-byte record header
offset = 2;
size = (((data[0] & 0x7f) << 8) | data[1]) + 2;
}
else
{ // we have a three-byte record header
offset = 3;
size = (((data[0] & 0x3f) << 8) | data[1]) + 3;
}
pContents += offset;
switch ( SSLv2_MessageTypes(pContents[0]) ) {
case SSLv2_MT_ERROR:
if ( size == SSLv2_ERROR_RECORD_SIZE + offset)
{
found_ssl2x = true;
sslEndpoint->sslVersion =
uint16(SSLProxy_Analyzer::SSLv20);
}
break;
case SSLv2_MT_CLIENT_HELLO:
{
sslEndpoint->sslVersion =
uint16(pContents[1] << 8) | pContents[2];
uint16 v = sslEndpoint->sslVersion;
if ( v == SSLProxy_Analyzer::SSLv20 ||
v == SSLProxy_Analyzer::SSLv30 ||
v == SSLProxy_Analyzer::SSLv31 )
found_ssl2x = true;
break;
}
case SSLv2_MT_SERVER_HELLO:
{
sslEndpoint->sslVersion =
uint16(pContents[3] << 8) | pContents[4];
uint16 v = sslEndpoint->sslVersion;
if ( v == SSLProxy_Analyzer::SSLv20 ||
v == SSLProxy_Analyzer::SSLv30 ||
v == SSLProxy_Analyzer::SSLv31 )
found_ssl2x = true;
break;
}
default:
break;
}
// Consistency checks.
if ( (found_ssl3x || found_ssl2x) == false )
{
sslEndpoint->Parent()->Weird("SSLProxy: Could not determine SSL version!");
return -1;
}
if ( (found_ssl3x && found_ssl2x) == true )
{
sslEndpoint->Parent()->Weird("SSLProxy: Found ambigous SSL version!");
return -1;
}
if ( found_ssl2x )
return 2;
else
return 3;
}
/*!
* This method is called internally by addSegment() to determine the expected
* size of a SSL record.
*
* \param data pointer to the SSL record to analyze
* \param length length of the SSL record to analyze
*
* \return true if succesfull, false otherwise
*/
bool SSL_RecordBuilder::computeExpectedSize(const u_char* data, int length)
{
if ( sslEndpoint->sslRecordVersion < 0 )
{
// We don't know the ssl record format yet, so we try
// to find out.
sslEndpoint->sslRecordVersion =
analyzeSSLRecordFormat(data, length);
if ( sslEndpoint->sslRecordVersion != 2 &&
sslEndpoint->sslRecordVersion != 3 )
// We could not determine the ssl record version.
return false;
}
// Get the expected length of this record.
if ( sslEndpoint->sslRecordVersion == 2 )
{
if ( (data[0] & 0x80) > 0 )
// We have a two-byte record header.
expectedSize = (((data[0] & 0x7f) << 8) | data[1]) + 2;
else
// We have a three-byte record header.
expectedSize = (((data[0] & 0x3f) << 8) | data[1]) + 3;
}
else if ( sslEndpoint->sslRecordVersion == 3 )
expectedSize = ((data[3] << 8) | data[4]) + 5;
if ( expectedSize < neededSize )
{
// This should never happen (otherwise: UNTESTED).
sslEndpoint->Parent()->Weird( "SSLProxy: expectedSize < neededSize in RecordBuilder!" );
return false;
}
return true;
}
// --- SSL_Connection_Proxy ---------------------------------------------------
bool SSLProxy_Analyzer::bInited = false;
SSLProxy_Analyzer::SSLProxy_Analyzer(Connection* conn)
: TCP_ApplicationAnalyzer(AnalyzerTag::SSL, conn)
{
sSLv2Interpreter = new SSLv2_Interpreter(this);
sSLv3xInterpreter = new SSLv3_Interpreter(this);
sSLInterpreter = 0;
bPassThrough = false;
if ( ! bInited )
{
BuildCipherDict();
bInited = true;
}
AddSupportAnalyzer(sslpeo = new Contents_SSL(conn, true));
AddSupportAnalyzer(sslper = new Contents_SSL(conn, false));
}
SSLProxy_Analyzer::~SSLProxy_Analyzer()
{
delete sSLv2Interpreter;
delete sSLv3xInterpreter;
}
void SSLProxy_Analyzer::Init()
{
TCP_ApplicationAnalyzer::Init();
sSLv2Interpreter->Init();
sSLv3xInterpreter->Init();
sSLv2Interpreter->Orig()
->SetProxyEndpoint(sSLv3xInterpreter->Orig()->GetProxyEndpoint());
sSLv2Interpreter->Resp()
->SetProxyEndpoint(sSLv3xInterpreter->Resp()->GetProxyEndpoint());
}
void SSLProxy_Analyzer::BuildCipherDict()
{
for ( uint idx = 0; idx < SSL_CipherSpecs_Count; ++idx )
{
HashKey h(static_cast<bro_uint_t>(SSL_CipherSpecs[idx].identifier));
SSL_CipherSpecDict.Insert(&h, &SSL_CipherSpecs[idx]);
}
}
void SSLProxy_Analyzer::NewSSLRecord(Contents_SSL* endp,
int len, const u_char* data)
{
// This is to extract only SSLv2 traffic.
if ( recordSSLv2Traffic )
{
uint16 sslVersion = 0;
if ( (data[0] & 0x80) > 0 )
// We have a two-byte record header.
sslVersion = (data[3] << 8) | data[4];
else
// We have a three-byte record header.
sslVersion = (data[4] << 8) | data[5];
if ( ! endp->IsSSLv2Record() ||
sslVersion != SSLProxy_Analyzer::SSLv20 )
{
SetSkip(1);
Conn()->SetRecordPackets(0);
Conn()->SetRecordContents(0);
// FIXME: Could do memory cleanup here.
}
else
// No analysis - only recording.
SetSkip(1);
return;
}
if ( bPassThrough )
{
DoDeliver(len, data, endp->IsOrig());
return;
}
if ( ! endp->IsSSLv2Record() )
{
// It's TLS or SSLv3, so we are done ...
sSLInterpreter = sSLv3xInterpreter;
bPassThrough = true;
// Tell the other record builder we have SSLv3x.
endp->sslRecordVersion = 3;
DoDeliver(len, data, endp->IsOrig());
}
else
{ // we have a SSLv2 record ...
sSLInterpreter = sSLv2Interpreter;
// Check whether it's the first or second we've seen ...
if ( sslpeo->VersionRecognized() &&
sslper->VersionRecognized() )
{
// Second record we've seen.
// O.K. Both endpoints recognized the version.
// So this needs to be an SSLv2-Connection ...
bPassThrough = true;
DoDeliver(len, data, endp->IsOrig());
}
// First record we see.
// The next one may be SSLv2 or SSLv3x,
// we don't know yet ...
else if ( endp->sslVersion == SSLv20 )
{
// The client supports only SSLv2, so we're done.
bPassThrough = true;
endp->sslRecordVersion = 2;
endp->sslVersion = SSLv20;
DoDeliver(len, data, endp->IsOrig());
}
else
{
bPassThrough = false;
DoDeliver(len, data, endp->IsOrig());
// Transfer the state of the SSLv2-Interpreter
// to the state of the SSLv3x-Interpreter ...
if ( ((SSLv2_Interpreter*) sSLInterpreter)->ConnState() == CLIENT_HELLO_SEEN )
((SSLv3_Interpreter*) sSLv3xInterpreter)->SetState(SSL3_1_STATE_CLIENT_HELLO_SENT);
}
}
}
void SSLProxy_Analyzer::DoDeliver(int len, const u_char* data, bool orig)
{
if ( orig )
sSLInterpreter->Orig()->Deliver(len, data);
else
sSLInterpreter->Resp()->Deliver(len, data);
}
void SSLProxy_Analyzer::printStats()
{
printf("SSLProxy_Analyzer::totalPackets = %u\n", totalPackets);
printf("SSLProxy_Analyzer::totalRecords = %u\n", totalRecords);
printf("SSLProxy_Analyzer::nonSSLConnections = %u\n", nonSSLConnections);
}
void SSLProxy_Analyzer::Weak(const char* name)
{
if ( ssl_conn_weak )
Event(ssl_conn_weak, name);
}
// --- Contents_SSL ------------------------------------------------------
/*!
* mod Contents_SSL::Contents_SSL( TCP_Endpoint* arg_endpt, int stop_on_gap )
* : TCP_Contents( arg_conn, stop_on_gap, punt_on_partial )
*/
Contents_SSL::Contents_SSL(Connection* conn, bool orig)
: TCP_SupportAnalyzer(AnalyzerTag::Contents_SSL, conn, orig)
{
sslRecordBuilder = new SSL_RecordBuilder(this);
bVersionRecognized = false;
bIsSSLv2Record = false;
sslRecordVersion = -1; // -1 means we don't know yet
sslVersion = 0; // 0 means we don't know yet
}
Contents_SSL::~Contents_SSL()
{
delete sslRecordBuilder;
}
bool Contents_SSL::isDataPending()
{
return sslRecordBuilder->isDataPending();
}
void Contents_SSL::DeliverStream(int len, const u_char* data, bool orig)
{
TCP_SupportAnalyzer::DeliverStream(len, data, orig);
TCP_Analyzer* tcp = static_cast<TCP_ApplicationAnalyzer *>(Parent())->TCP();
assert(tcp);
if ( tcp->HadGap(orig) || tcp->IsPartial() )
return;
++SSLProxy_Analyzer::totalPackets;
TCP_Endpoint* endp = orig ? tcp->Orig() : tcp->Resp();
#if 0
// FIXME: What's this???
int ack = endp->AckSeq() - endp->StartSeq();
int top_seq = seq + len;
if ( top_seq <= ack )
// There is no new data in this packet.
return;
#endif
if ( len <= 0 )
return;
// No further processing if we have a partial connection.
if ( endp->state == TCP_ENDPOINT_PARTIAL ||
endp->peer->state == TCP_ENDPOINT_PARTIAL )
{
Parent()->SetSkip(1);
Conn()->SetRecordPackets(0);
return;
}
if ( ! sslRecordBuilder->addSegment(data, len) )
{
// The RecordBuilder failed to determine the SSL record version,
// so we can't analyze this connection any further.
++SSLProxy_Analyzer::nonSSLConnections;
Parent()->Weird("SSL: Skipping connection (not an SSL connection?!)!");
Parent()->SetSkip(1);
Conn()->SetRecordPackets(0);
}
}
// Called by the RecordBuilder with a complete SSL record.
void Contents_SSL::DoDeliver(int len, const u_char* data)
{
++SSLProxy_Analyzer::totalRecords;
bIsSSLv2Record = sslRecordVersion == 2;
bVersionRecognized = true;
((SSLProxy_Analyzer*) Parent())->NewSSLRecord(this, len, data);
}
bool Contents_SSL::IsSSLv2Record()
{
return bIsSSLv2Record;
}
bool Contents_SSL::VersionRecognized()
{
return bVersionRecognized;
}

View file

@ -1,287 +0,0 @@
// $Id: SSLProxy.h 5952 2008-07-13 19:45:15Z vern $
#ifndef SSLPROXY_H
#define SSLPROXY_H
#include "TCP.h"
#include "SSLInterpreter.h"
#include "binpac_bro.h"
// --- forward declarations ---------------------------------------------------
class SSL_Interpreter;
class SSL_RecordBuilder;
class Contents_SSL;
// --- class SSL_DataBlock ----------------------------------------------------
/*!
* \brief This class is used to store a block of data on the heap, which is
* allocated and copied by the constructor, and freed by the destructor.
*
* It is mainly used by the SSL_RecordBuilder to store the received data. To
* reduce heap operations (HeapOps), which can be quite expensive, it is
* possible to let the constructor allocate a minimum heap block size. The
* class members keep track of how much data has been allocated and how much of
* it has been used. Plus, there's a pointer to the next SSL_DataBlock, for
* easy creation of a single-linked list.
*/
class SSL_DataBlock {
public:
SSL_DataBlock(const u_char* data, int len, int min_len = 0);
int len; ///< The <b>used</b> size of the reserved heap block.
int size; ///< The <b>allocated</b> size of the reserved heap block.
u_char* data; ///< Pointer to the allocated heap block.
SSL_DataBlock* next; ///< Pointer to the next SSL_Datablock in the chain.
/*!
* The destructor will free the allocated data block.
*/
~SSL_DataBlock() { delete [] data; }
void toStream(FILE* stream) const;
char* toString() const;
};
// --- class SSL_RecordBuilder ------------------------------------------------
/*!
* \brief This class is used to reassemble SSL records from a stream of data.
*
* It supports both SSLv2 and SSLv3 record formats at the same time. The record
* builder has been designed to be robust, efficient and hard to attack. To add
* a segments of data, call addSegment(). Whenever a SSL record has been
* reassembled, the DoDeliver() function of the corresponding Contents_SSL
* will be called.
*
* Two forms of attack have been taken into consideration:
* -# The "fake size" attack, where the actual size of the SSL record is much
* smaller then the size given in the record header. This way, an attacker
* could force Bro to allocate a huge amount of memory and make it crash.
* -# The "small fragment" attack, where an attacker sends huge SSL records
* in very small (1 byte or so) TCP segments. This could lead to a huge
* amount of very small memory blocks allocated by Bro. After the last byte
* of an SSL record has been received, all allocated blocks have to be
* freed. Freeing something like 32K blocks of memory can be quite expensive,
* so packet drops may occur, which could prevent Bro from detecting an
* attacker.
*
* The current implementation always allocates a minimum size of data on the
* heap, which is MIN_ALLOC_SIZE. The processed SSL record fragments are stored
* in a single-linked list of type SSL_DataBlock.
*
* The following assumptions are made:
* - neededSize <= min( expectedSize )
* - neededSize <= MIN_ALLOC_SIZE, so the data needed to determine the SSL
* record version fits in one SSL_DataBlock
*/
class SSL_RecordBuilder {
public:
SSL_RecordBuilder(Contents_SSL* sslEndpoint);
~SSL_RecordBuilder();
static const uint MIN_ALLOC_SIZE = 16; ///< min. size of memory to alloc
static const int MIN_FRAGMENT_SIZE = 100; ///< min. size of a middle TCP Segment
static uint maxAllocCount; ///< max. number of allocated data blocks for an instance of a reassembler
static uint maxFragmentCount; ///< max. number of fragments for a ssl record
static uint fragmentedHeaders; ///< counter for the number of fragmented headers (header=neededSize)
bool addSegment(const u_char* data, int length);
/*!
* Calls this method to see if there's currently data in the
* record builder pending.
* \return true if there's data pending, false otherwise
*/
bool isDataPending() { return hasPendingData; };
protected:
u_char* assembleBlocks(const u_char* data, int length);
int analyzeSSLRecordFormat(const u_char* data, int length);
bool computeExpectedSize (const u_char* data, int length);
void addData(const u_char* data, int length);
SSL_DataBlock* head; ///< pointer to the first element in the linked list of SSL_DataBlocks
SSL_DataBlock* tail; ///< pointer to the last element in the linked list of SSL_DataBlocks
Contents_SSL* sslEndpoint; ///< pointer to the containing Contents_SSL
int expectedSize; ///< expected size of SSLv2 record including header
int currentSize; ///< current bytes stored in data blocks (that is, processed size of actual record)
int neededSize; ///< min. size in bytes so that the length of the current record can be determinded
bool hasPendingData; ///< true if there's data following in the current tcp segment
uint fragmentCounter; ///< counter for the number of tcp segments for the current record
};
// --- class SSLProxy_Analyzer ----------------------------------------------
/** This class represents an SSL_Connection with two SSL_ConnectionEndpoints.
* Note, that this class acts as a proxy, because there are different versions
* of the SSL protocol in use and you don't know in advance which SSL version
* really will be used. This depends on the first two messages of the SSL handshake
* process. Because Bro offers no possibility for switching connections we
* decided only to inherit this proxy from TCP_Connection.
* The different SSL versions are implemented in classed derived from
* SSL_Interpreter/SSL_InterpreterEndpoint and so, we can easily switch the flow
* of data to the appropriate SSL Interpreter.
* Currently, we support SSL Version 2.0 and 3.0/3.1(TLS)(@see SSLv2_Interpreter and @see
* SSLv3_Interpreter).
* This class holds an instance of both SSLv2- and SSLv3_Interpreter. The version
* of the SSL that is used for a connection is negotiated within the first
* two records (SSL messages): client hello and server hello.
* So after scanning this two records (which is mainly done in @see SSL_RecordBuilder and
* @see Contents_SSL) and determing the versions, it is clear which
* SSL version will be used for the succeding SSL records. From now
* on, they can be directly passed through to the appropriate SSL_Interpreter.
*
* FIXME: Now we have a dynamic analyzer framework so this could be restructured.
*/
class SSLProxy_Analyzer: public TCP_ApplicationAnalyzer {
public:
SSLProxy_Analyzer(Connection* conn);
virtual ~SSLProxy_Analyzer();
static uint totalPackets; ///< counter for total ssl packets seen
static uint totalRecords; ///< counter for total ssl records seen
static uint nonSSLConnections; ///< counter for connections where we couldn't reassemble a ssl record
static const bool recordSSLv2Traffic = false; ///< if true, only recording of SSLv2 connections is done (no analysis)
static bool bInited;
enum SSL_Versions {
SSLv20 = 0x0002,
SSLv30 = 0x0300,
SSLv31 = 0x0301 // = TLS 1.0
};
/* This method is called from the corresponding Contents_SSL to
* deliver the data to the SSL_ProxyConnection. It decides which
* SSL_Interpreter (Version 2 or Version 3x) gets the record or
* directly passes it through, if it's already clear which version
* this SSL connection uses.
* @param endp the sending endpoint
* @param len length of SSL record
* @param data the SSL record
*
* SC mod - pass a TCP_Contents rather than endpoint in terms of an actual
* Contents_SSL. There is much less overall work to do since we
* have already done the assosciation.
*/
void NewSSLRecord(Contents_SSL* endp, int len, const u_char* data);
// Initialises the SSLv2- and SSLv3_Interpreters.
virtual void Init();
// This method is used for passing messages to Bro that contain
// information about weaknesses in the choosen SSL encryption
// (short keys, unverifyable certificates, ...)
// @param name the name of the weakness.
void Weak(const char* name);
static Analyzer* InstantiateAnalyzer(Connection* conn)
{ return new SSLProxy_Analyzer(conn); }
static bool Available()
{
return (ssl_certificate_seen || ssl_certificate ||
ssl_conn_attempt || ssl_conn_server_reply ||
ssl_conn_established || ssl_conn_reused ||
ssl_conn_alert)
&& ! FLAGS_use_binpac;
}
static void printStats();
protected:
bool bPassThrough; ///< whether it is clear which SSL version the connection will use
SSL_Interpreter* sSLv2Interpreter; ///< Interpreter for SSL version 2
SSL_Interpreter* sSLv3xInterpreter; ///< Interpreter for SSL version 3.0 and 3.1
SSL_Interpreter* sSLInterpreter; ///< Pointer to the interpreter currently in use
Contents_SSL* sslpeo;
Contents_SSL* sslper;
/** Internally called from this class Deliver()-method.
* It delivers the data to the correct corresponding
* SSL_InterpreterEndpoint.
* @param endp the sending endpoint
* @param t time, when the segment was received by bro (not used)
* @param seq relative sequenze number (from Endpoint::start_seq) (not used)
* @param len length of SSL record
* @param data the SSL record
*/
void DoDeliver(int len, const u_char* data, bool orig);
// Initialises the dictionary where the SSL cipher specs are stored.
// It needs only to be called once for a whole bro. @see SSLDefines.h
void BuildCipherDict();
};
// --- class Contents_SSL ------------------------------------------------
/** This class represents an endpoint of a SSLProxy_Analyzer.
* It receives the new data (TCP segments) within the Deliver()-method, does
* some basic checks on the segment and passes it on to the SSL_RecordBuilder,
* which reassembles the segments into SSL records and determines the
* versions of the records. If the SSL_RecordBuilder was able to determine
* the versions of the records it delivers the reassembled records back tho this
* Contents_SSL by calling the DoDeliver()-method.
* The Contents_SSL then hands the record over to the corresponding
* SSLProxy_Analyzer by invoking it's NewSSLRecord()-method.
*
* SC mod: change class Contents_SSL: public TCP_EndpointContents
* to class Contents_SSL: public TCP_Contents
* this is done since the class uses the Deliver() method to take care of data.
*
*/
class Contents_SSL: public TCP_SupportAnalyzer {
public:
/* The constructor builds up and initialises the Contents_SSL.
* @param conn the corresponding Connection
* @param whether this is the originator
*/
Contents_SSL(Connection* conn, bool orig);
~Contents_SSL();
int sslRecordVersion; ///< record version of the first SSL record seen (set by SSLProxy_Analyzer and SSL_RecordBuilder)
uint16 sslVersion; ///< SSL version of the SSL record seen (set by SSL_RecordBuilder)
/** Via this method, this Contents_SSL receives the
* TCP segments.
* @param len length of TCP-Segment
* @param data content of TCP-Segment
* @param orig whether sending endpoint is originator
*/
virtual void DeliverStream(int len, const u_char* data, bool orig);
/** This method is called by the corresponding SSL_RecordBuilder
* upon delivering a new reassembled SSL record.
* @param len the length of the record
* @param data the record
*/
void DoDeliver(int len, const u_char* data);
/* @return whether we have already seen the first record of the connection of this endpoint yet
*/
bool VersionRecognized();
/* @return whether the first record was of SSL version 2.0
*/
bool IsSSLv2Record();
/* @return whether the corresponding SSL_RecordBuilder has pending data
*/
bool isDataPending(); // should be inline
SSL_RecordBuilder* sslRecordBuilder;
protected:
bool bVersionRecognized; ///< False, if we haven't seen the first record of the connection of this endpoint yet
bool bIsSSLv2Record; ///< Was the first record of SSL version 2.0
};
#endif

View file

@ -1,946 +0,0 @@
// $Id: SSLv2.cc 5988 2008-07-19 07:02:12Z vern $
#include "SSLv2.h"
#include "SSLv3.h"
// --- Initalization of static variables --------------------------------------
uint SSLv2_Interpreter::totalConnections = 0;
uint SSLv2_Interpreter::analyzedConnections = 0;
uint SSLv2_Interpreter::openedConnections = 0;
uint SSLv2_Interpreter::failedConnections = 0;
uint SSLv2_Interpreter::weirdConnections = 0;
uint SSLv2_Interpreter::totalRecords = 0;
uint SSLv2_Interpreter::clientHelloRecords = 0;
uint SSLv2_Interpreter::serverHelloRecords = 0;
uint SSLv2_Interpreter::clientMasterKeyRecords = 0;
uint SSLv2_Interpreter::errorRecords = 0;
// --- SSLv2_Interpreter -------------------------------------------------------
/*!
* The Constructor.
*
* \param proxy Pointer to the SSLProxy_Analyzer who created this instance.
*/
SSLv2_Interpreter::SSLv2_Interpreter(SSLProxy_Analyzer* proxy)
: SSL_Interpreter(proxy)
{
++totalConnections;
records = 0;
bAnalyzedCounted = false;
connState = START;
pServerCipherSpecs = 0;
pClientCipherSpecs = 0;
bClientWantsCachedSession = false;
usedCipherSpec = (SSLv2_CipherSpec) 0;
pConnectionId = 0;
pChallenge = 0;
pSessionId = 0;
pMasterClearKey = 0;
pMasterEncryptedKey = 0;
pClientReadKey = 0;
pServerReadKey = 0;
}
/*!
* The Destructor.
*/
SSLv2_Interpreter::~SSLv2_Interpreter()
{
if ( connState != CLIENT_MASTERKEY_SEEN &&
connState != CACHED_SESSION &&
connState != START && // we only complain if we saw some data
connState != ERROR_SEEN )
++failedConnections;
if ( connState != CLIENT_MASTERKEY_SEEN && connState != CACHED_SESSION )
++weirdConnections;
delete pServerCipherSpecs;
delete pClientCipherSpecs;
delete pConnectionId;
delete pChallenge;
delete pSessionId;
delete pMasterClearKey;
delete pMasterEncryptedKey;
delete pClientReadKey;
delete pServerReadKey;
}
/*!
* This method implements SSL_Interpreter::BuildInterpreterEndpoints()
*/
void SSLv2_Interpreter::BuildInterpreterEndpoints()
{
orig = new SSLv2_Endpoint(this, 1);
resp = new SSLv2_Endpoint(this, 0);
}
/*!
* This method prints some counters.
*/
void SSLv2_Interpreter::printStats()
{
printf("SSLv2:\n");
printf("totalConnections = %u\n", totalConnections);
printf("analyzedConnections = %u\n", analyzedConnections);
printf("openedConnections = %u\n", openedConnections);
printf("failedConnections = %u\n", failedConnections);
printf("weirdConnections = %u\n", weirdConnections);
printf("totalRecords = %u\n", totalRecords);
printf("clientHelloRecords = %u\n", clientHelloRecords);
printf("serverHelloRecords = %u\n", serverHelloRecords);
printf("clientMasterKeyRecords = %u\n", clientMasterKeyRecords);
printf("errorRecords = %u\n", errorRecords);
printf("SSL_RecordBuilder::maxAllocCount = %u\n", SSL_RecordBuilder::maxAllocCount);
printf("SSL_RecordBuilder::maxFragmentCount = %u\n", SSL_RecordBuilder::maxFragmentCount);
printf("SSL_RecordBuilder::fragmentedHeaders = %u\n", SSL_RecordBuilder::fragmentedHeaders);
}
/*!
* \return the current state of the ssl connection
*/
SSLv2_States SSLv2_Interpreter::ConnState()
{
return connState;
}
/*!
* This method is called by SSLv2_Endpoint::Deliver(). It is the main entry
* point of this class. The header of the given SSLV2 record is analyzed and
* its contents are then passed to the corresponding analyzer method. After
* the record has been analyzed, the ssl connection state is updated.
*
* \param s Pointer to the endpoint which sent the record
* \param length length of SSLv2 record
* \param data pointer to SSLv2 record to analyze
*/
void SSLv2_Interpreter::NewSSLRecord(SSL_InterpreterEndpoint* s,
int length, const u_char* data)
{
++records;
++totalRecords;
if ( ! bAnalyzedCounted )
{
++analyzedConnections;
bAnalyzedCounted = true;
}
// We should see a maximum of 4 cleartext records.
if ( records == 5 )
{ // so this should never happen
Weird("SSLv2: Saw more than 4 records, skipping connection...");
proxy->SetSkip(1);
return;
}
// SSLv2 record header analysis
uint32 recordLength = 0; // data length of SSLv2 record
bool isEscape = false;
uint8 padding = 0;
const u_char* contents;
if ( (data[0] & 0x80) > 0 )
{ // we have a two-byte record header
recordLength = ((data[0] & 0x7f) << 8) | data[1];
contents = data + 2;
if ( recordLength + 2 != uint32(length) )
{
// This should never happen, otherwise
// we have a bug in the SSL_RecordBuilder.
Weird("SSLv2: FATAL: recordLength doesn't match data block length!");
connState = ERROR_REQUIRED;
proxy->SetSkip(1);
return;
}
}
else
{ // We have a three-byte record header.
recordLength = ((data[0] & 0x3f) << 8) | data[1];
isEscape = (data[0] & 0x40) != 0;
padding = data[2];
contents = data + 3;
if ( recordLength + 3 != uint32(length) )
{
// This should never happen, otherwise
// we have a bug in the SSL_RecordBuilder.
Weird("SSLv2: FATAL: recordLength doesn't match data block length!");
connState = ERROR_REQUIRED;
proxy->SetSkip(1);
return;
}
if ( padding == 0 && ! isEscape )
Weird("SSLv2: 3 Byte record header, but no escape, no padding!");
}
if ( recordLength == 0 )
{
Weird("SSLv2: Record length is zero (no record data)!");
return;
}
if ( isEscape )
Weird("SSLv2: Record has escape bit set (security escape)!");
if ( padding > 0 && connState != CACHED_SESSION &&
connState != CLIENT_MASTERKEY_SEEN )
Weird("SSLv2 record with padding > 0 in cleartext!");
// MISSING:
// A final consistency check is done when a block cipher is used
// and the protocol is using encryption. The amount of data present
// in a record (RECORD-LENGTH))must be a multiple of the cipher's
// block size. If the received record is not a multiple of the
// cipher's block size then the record is considered damaged, and it
// is to be treated as if an "I/O Error" had occurred (i.e. an
// unrecoverable error is asserted and the connection is closed).
switch ( connState ) {
case START:
// Only CLIENT-HELLLOs allowed here.
if ( contents[0] != SSLv2_MT_CLIENT_HELLO )
{
Weird("SSLv2: First packet is not a CLIENT-HELLO!");
analyzeRecord(s, recordLength, contents);
connState = ERROR_REQUIRED;
}
else
connState = ClientHelloRecord(s, recordLength, contents);
break;
case CLIENT_HELLO_SEEN:
// Only SERVER-HELLOs or ERRORs allowed here.
if ( contents[0] == SSLv2_MT_SERVER_HELLO )
connState = ServerHelloRecord(s, recordLength, contents);
else if ( contents[0] == SSLv2_MT_ERROR )
connState = ErrorRecord(s, recordLength, contents);
else
{
Weird("SSLv2: State violation in CLIENT_HELLO_SEEN!");
analyzeRecord(s, recordLength, contents);
connState = ERROR_REQUIRED;
}
break;
case NEW_SESSION:
// We expect a client master key.
if ( contents[0] == SSLv2_MT_CLIENT_MASTER_KEY )
connState = ClientMasterKeyRecord(s, recordLength, contents);
else if ( contents[0] == SSLv2_MT_ERROR )
connState = ErrorRecord(s, recordLength, contents);
else
{
Weird("SSLv2: State violation in NEW_SESSION or encrypted record!");
analyzeRecord(s, recordLength, contents);
connState = ERROR_REQUIRED;
}
delete pServerCipherSpecs;
pServerCipherSpecs = 0;
break;
case CACHED_SESSION:
delete pServerCipherSpecs;
pServerCipherSpecs = 0;
// No break here.
case CLIENT_MASTERKEY_SEEN:
// If no error record, no further analysis.
if ( contents[0] == SSLv2_MT_ERROR &&
recordLength == SSLv2_ERROR_RECORD_SIZE )
connState = ErrorRecord(s, recordLength, contents);
else
{
// So we finished the cleartext handshake.
// Skip all further data.
proxy->SetSkip(1);
++openedConnections;
}
break;
case ERROR_REQUIRED:
if ( contents[0] == SSLv2_MT_ERROR )
connState = ErrorRecord(s, recordLength, contents);
else
{
// We lost tracking: this should not happen.
Weird("SSLv2: State inconsistency in ERROR_REQUIRED (lost tracking!)!");
analyzeRecord(s, recordLength, contents);
connState = ERROR_REQUIRED;
}
break;
case ERROR_SEEN:
// We don't have recoverable errors in cleartext phase,
// so we shouldn't see anymore packets.
Weird("SSLv2: Traffic after error record!");
analyzeRecord(s, recordLength, contents);
break;
default:
internal_error("SSLv2: unknown state");
break;
}
}
/*!
* This method is called whenever the connection tracking failed. It calls
* the corresponding analyzer method for the given SSLv2 record, but does not
* update the ssl connection state.
*
* \param s Pointer to the endpoint which sent the record
* \param length length of SSLv2 record
* \param data pointer to SSLv2 record to analyze
*/
void SSLv2_Interpreter::analyzeRecord(SSL_InterpreterEndpoint* s,
int length, const u_char* data)
{
switch ( data[0] ) {
case SSLv2_MT_ERROR:
ErrorRecord(s, length, data);
break;
case SSLv2_MT_CLIENT_HELLO:
ClientHelloRecord(s, length, data);
break;
case SSLv2_MT_CLIENT_MASTER_KEY:
ClientMasterKeyRecord(s, length, data);
break;
case SSLv2_MT_SERVER_HELLO:
ServerHelloRecord(s, length, data);
break;
case SSLv2_MT_CLIENT_FINISHED:
case SSLv2_MT_SERVER_VERIFY:
case SSLv2_MT_SERVER_FINISHED:
case SSLv2_MT_REQUEST_CERTIFICATE:
case SSLv2_MT_CLIENT_CERTIFICATE:
Weird("SSLv2: Encrypted record type seems to be in cleartext");
break;
default:
// Unknown record type.
Weird("SSLv2: Unknown record type or encrypted record");
break;
}
}
/*!
* This method analyses a SSLv2 CLIENT-HELLO record.
*
* \param s Pointer to the endpoint which sent the record
* \param length length of SSLv2 CLIENT-HELLO record
* \param data pointer to SSLv2 CLIENT-HELLO record to analyze
*
* \return the updated state of the current ssl connection
*/
SSLv2_States SSLv2_Interpreter::ClientHelloRecord(SSL_InterpreterEndpoint* s,
int recordLength, const u_char* recordData)
{
// This method gets the record's data (without the header).
++clientHelloRecords;
if ( s != orig )
Weird("SSLv2: CLIENT-HELLO record from server!");
// There should not be any pending data in the SSLv2 reassembler,
// because the client should wait for a server response.
if ( ((SSLv2_Endpoint*) s)->isDataPending() )
Weird("SSLv2: Pending data in SSL_RecordBuilder after CLIENT-HELLO!");
// Client hello minimum header size check.
if ( recordLength < SSLv2_CLIENT_HELLO_HEADER_SIZE )
{
Weird("SSLv2: CLIENT-HELLO is too small!");
return ERROR_REQUIRED;
}
// Extract the data of the client hello header.
SSLv2_ClientHelloHeader ch;
ch.clientVersion = uint16(recordData[1] << 8) | recordData[2];
ch.cipherSpecLength = uint16(recordData[3] << 8) | recordData[4];
ch.sessionIdLength = uint16(recordData[5] << 8) | recordData[6];
ch.challengeLength = uint16(recordData[7] << 8) | recordData[8];
if ( ch.clientVersion != SSLProxy_Analyzer::SSLv20 &&
ch.clientVersion != SSLProxy_Analyzer::SSLv30 &&
ch.clientVersion != SSLProxy_Analyzer::SSLv31 )
{
Weird("SSLv2: Unsupported SSL-Version in CLIENT-HELLO");
return ERROR_REQUIRED;
}
if ( ch.challengeLength + ch.cipherSpecLength + ch.sessionIdLength +
SSLv2_CLIENT_HELLO_HEADER_SIZE != recordLength )
{
Weird("SSLv2: Size inconsistency in CLIENT-HELLO");
return ERROR_REQUIRED;
}
// The CIPHER-SPECS-LENGTH must be > 0 and a multiple of 3.
if ( ch.cipherSpecLength == 0 || ch.cipherSpecLength % 3 != 0 )
{
Weird("SSLv2: Nonconform CIPHER-SPECS-LENGTH in CLIENT-HELLO.");
return ERROR_REQUIRED;
}
// The SESSION-ID-LENGTH must either be zero or 16.
if ( ch.sessionIdLength != 0 && ch.sessionIdLength != 16 )
Weird("SSLv2: Nonconform SESSION-ID-LENGTH in CLIENT-HELLO.");
if ( (ch.challengeLength < 16) || (ch.challengeLength > 32))
Weird("SSLv2: Nonconform CHALLENGE-LENGTH in CLIENT-HELLO.");
const u_char* ptr = recordData;
ptr += SSLv2_CLIENT_HELLO_HEADER_SIZE + ch.cipherSpecLength;
pSessionId = new SSL_DataBlock(ptr, ch.sessionIdLength);
// If decrypting, store the challenge.
if ( ssl_store_key_material && ch.challengeLength <= 32 )
pChallenge = new SSL_DataBlock(ptr, ch.challengeLength);
bClientWantsCachedSession = ch.sessionIdLength != 0;
TableVal* currentCipherSuites =
analyzeCiphers(s, ch.cipherSpecLength,
recordData + SSLv2_CLIENT_HELLO_HEADER_SIZE);
fire_ssl_conn_attempt(ch.clientVersion, currentCipherSuites);
return CLIENT_HELLO_SEEN;
}
/*!
* This method analyses a SSLv2 SERVER-HELLO record.
*
* \param s Pointer to the endpoint which sent the record
* \param length length of SSLv2 SERVER-HELLO record
* \param data pointer to SSLv2 SERVER-HELLO record to analyze
*
* \return the updated state of the current ssl connection
*/
SSLv2_States SSLv2_Interpreter::ServerHelloRecord(SSL_InterpreterEndpoint* s,
int recordLength, const u_char* recordData)
{
++serverHelloRecords;
TableVal* currentCipherSuites = NULL;
if ( s != resp )
Weird("SSLv2: SERVER-HELLO from client!");
if ( recordLength < SSLv2_SERVER_HELLO_HEADER_SIZE )
{
Weird("SSLv2: SERVER-HELLO is too small!");
return ERROR_REQUIRED;
}
// Extract the data of the client hello header.
SSLv2_ServerHelloHeader sh;
sh.sessionIdHit = recordData[1];
sh.certificateType = recordData[2];
sh.serverVersion = uint16(recordData[3] << 8) | recordData[4];
sh.certificateLength = uint16(recordData[5] << 8) | recordData[6];
sh.cipherSpecLength = uint16(recordData[7] << 8) | recordData[8];
sh.connectionIdLength = uint16(recordData[9] << 8) | recordData[10];
if ( sh.serverVersion != SSLProxy_Analyzer::SSLv20 )
{
Weird("SSLv2: Unsupported SSL-Version in SERVER-HELLO");
return ERROR_REQUIRED;
}
if ( sh.certificateLength + sh.cipherSpecLength +
sh.connectionIdLength +
SSLv2_SERVER_HELLO_HEADER_SIZE != recordLength )
{
Weird("SSLv2: Size inconsistency in SERVER-HELLO");
return ERROR_REQUIRED;
}
// The length of the CONNECTION-ID must be between 16 and 32 bytes.
if ( sh.connectionIdLength < 16 || sh.connectionIdLength > 32 )
Weird("SSLv2: Nonconform CONNECTION-ID-LENGTH in SERVER-HELLO");
// If decrypting, store the connection ID.
if ( ssl_store_key_material && sh.connectionIdLength <= 32 )
{
const u_char* ptr = recordData;
ptr += SSLv2_SERVER_HELLO_HEADER_SIZE + sh.cipherSpecLength +
sh.certificateLength;
pConnectionId = new SSL_DataBlock(ptr, sh.connectionIdLength);
}
if ( sh.sessionIdHit == 0 )
{
// Generating reusing-connection event.
EventHandlerPtr event = ssl_session_insertion;
if ( event )
{
TableVal* sessionIDTable =
MakeSessionID(
recordData +
SSLv2_SERVER_HELLO_HEADER_SIZE +
sh.certificateLength +
sh.cipherSpecLength,
sh.connectionIdLength);
val_list* vl = new val_list;
vl->append(proxy->BuildConnVal());
vl->append(sessionIDTable);
proxy->ConnectionEvent(ssl_session_insertion, vl);
}
}
SSLv2_States nextState;
if ( sh.sessionIdHit != 0 )
{ // we're using a cached session
// There should not be any pending data in the SSLv2
// reassembler, because the server should wait for a
// client response.
if ( ((SSLv2_Endpoint*) s)->isDataPending() )
{
// But turns out some SSL Implementations do this
// when using a cached session.
}
// Consistency check for SESSION-ID-HIT.
if ( ! bClientWantsCachedSession )
Weird("SSLv2: SESSION-ID hit in SERVER-HELLO, but no SESSION-ID in CLIENT-HELLO!");
// If the SESSION-ID-HIT flag is non-zero then the
// CERTIFICATE-TYPE, CERTIFICATE-LENGTH and
// CIPHER-SPECS-LENGTH fields will be zero.
if ( sh.certificateType != 0 || sh.certificateLength != 0 ||
sh.cipherSpecLength != 0 )
Weird("SSLv2: SESSION-ID-HIT, but session data in SERVER-HELLO");
// Generate reusing-connection event.
if ( pSessionId )
{
fire_ssl_conn_reused(pSessionId);
delete pSessionId;
pSessionId = 0;
}
nextState = CACHED_SESSION;
}
else
{ // we're starting a new session
// There should not be any pending data in the SSLv2
// reassembler, because the server should wait for
// a client response.
if ( ((SSLv2_Endpoint*) s)->isDataPending() )
Weird("SSLv2: Pending data in SSL_RecordBuilder after SERVER-HELLO (new session)!");
// TODO: check certificate length ???
if ( sh.certificateLength == 0 )
Weird("SSLv2: No certificate in SERVER-HELLO!");
// The CIPHER-SPECS-LENGTH must be > zero and a multiple of 3.
if ( sh.cipherSpecLength == 0 )
Weird("SSLv2: No CIPHER-SPECS in SERVER-HELLO!");
if ( sh.cipherSpecLength % 3 != 0 )
{
Weird("SSLv2: Nonconform CIPHER-SPECS-LENGTH in SERVER-HELLO");
return ERROR_REQUIRED;
}
const u_char* ptr = recordData;
ptr += sh.certificateLength + SSLv2_SERVER_HELLO_HEADER_SIZE;
currentCipherSuites = analyzeCiphers(s, sh.cipherSpecLength, ptr);
nextState = NEW_SESSION;
}
// Check if at least one cipher is supported by the client.
if ( pClientCipherSpecs && pServerCipherSpecs )
{
bool bFound = false;
for ( int i = 0; i < pClientCipherSpecs->len; i += 3 )
{
for ( int j = 0; j < pServerCipherSpecs->len; j += 3 )
{
if ( memcmp(pClientCipherSpecs + i,
pServerCipherSpecs + j, 3) == 0 )
{
bFound = true;
i = pClientCipherSpecs->len;
break;
}
}
}
if ( ! bFound )
{
Weird("SSLv2: Client's and server's CIPHER-SPECS don't match!");
nextState = ERROR_REQUIRED;
}
delete pClientCipherSpecs;
pClientCipherSpecs = 0;
}
// Certificate analysis.
if ( sh.certificateLength > 0 && ssl_analyze_certificates != 0 )
{
analyzeCertificate(s, recordData + SSLv2_SERVER_HELLO_HEADER_SIZE,
sh.certificateLength, sh.certificateType, false);
}
if ( nextState == NEW_SESSION )
// generate server-reply event
fire_ssl_conn_server_reply(sh.serverVersion, currentCipherSuites);
else if ( nextState == CACHED_SESSION )
{ // generate server-reply event
fire_ssl_conn_server_reply(sh.serverVersion, currentCipherSuites);
// Generate a connection-established event with a dummy
// cipher suite, since we can't remember session information
// (yet).
// Note: A new session identifier is sent encrypted in SSLv2!
fire_ssl_conn_established(sh.serverVersion, 0xABCD);
}
else
// Unref, since the table is not delivered to any event.
Unref(currentCipherSuites);
return nextState;
}
/*!
* This method analyses a SSLv2 CLIENT-MASTER-KEY record.
*
* \param s Pointer to the endpoint which sent the record
* \param length length of SSLv2 CLIENT-MASTER-KEY record
* \param data pointer to SSLv2 CLIENT-MASTER-KEY record to analyze
*
* \return the updated state of the current ssl connection
*/
SSLv2_States SSLv2_Interpreter::
ClientMasterKeyRecord(SSL_InterpreterEndpoint* s, int recordLength,
const u_char* recordData)
{
++clientMasterKeyRecords;
SSLv2_States nextState = CLIENT_MASTERKEY_SEEN;
if ( s != orig )
Weird("SSLv2: CLIENT-MASTER-KEY from server!");
if ( recordLength < SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE )
{
Weird("SSLv2: CLIENT-MASTER-KEY is too small!");
return ERROR_REQUIRED;
}
// Extract the data of the client master key header.
SSLv2_ClientMasterKeyHeader cmk;
cmk.cipherKind =
((recordData[1] << 16) | recordData[2] << 8) | recordData[3];
cmk.clearKeyLength = uint16(recordData[4] << 8) | recordData[5];
cmk.encryptedKeyLength = uint16(recordData[6] << 8) | recordData[7];
cmk.keyArgLength = uint16(recordData[8] << 8) | recordData[9];
if ( cmk.clearKeyLength + cmk.encryptedKeyLength + cmk.keyArgLength +
SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE != recordLength )
{
Weird("SSLv2: Size inconsistency in CLIENT-MASTER-KEY");
return ERROR_REQUIRED;
}
// Check if cipher is supported by the server.
if ( pServerCipherSpecs )
{
bool bFound = false;
for ( int i = 0; i < pServerCipherSpecs->len; i += 3 )
{
uint32 cipherSpec =
((pServerCipherSpecs->data[i] << 16) |
pServerCipherSpecs->data[i+1] << 8) |
pServerCipherSpecs->data[i+2];
if ( cmk.cipherKind == cipherSpec )
{
bFound = true;
break;
}
}
if ( ! bFound )
{
Weird("SSLv2: Client chooses unadvertised cipher in CLIENT-MASTER-KEY!");
nextState = ERROR_REQUIRED;
}
else
nextState = CLIENT_MASTERKEY_SEEN;
delete pServerCipherSpecs;
pServerCipherSpecs = 0;
}
// TODO: check if cipher has been advertised before.
SSL_CipherSpec* pCipherSpecTemp = 0;
HashKey h(static_cast<bro_uint_t>(cmk.cipherKind));
pCipherSpecTemp = (SSL_CipherSpec*) SSL_CipherSpecDict.Lookup(&h);
if ( ! pCipherSpecTemp || ! (pCipherSpecTemp->flags & SSL_FLAG_SSLv20) )
Weird("SSLv2: Unknown CIPHER-SPEC in CLIENT-MASTER-KEY!");
else
{ // check for conistency of clearKeyLength
if ( cmk.clearKeyLength * 8 != pCipherSpecTemp->clearKeySize )
{
Weird("SSLv2: Inconsistency of clearKeyLength in CLIENT-MASTER-KEY!");
// nextState = ERROR_REQUIRED;
}
// TODO: check for consistency of encryptedKeyLength.
// TODO: check for consistency of keyArgLength.
// switch ( cmk.cipherKind )
// {
// case SSL_CK_RC4_128_WITH_MD5:
// case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
// if ( cmk.keyArgLength != 0 )
// {
// Weird("SSLv2: Inconsistency of keyArgLength in CLIENT-MASTER-KEY!");
// //nextState = ERROR_REQUIRED;
// }
// break;
// case SSL_CK_DES_64_CBC_WITH_MD5:
// case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
// case SSL_CK_RC2_128_CBC_WITH_MD5:
// case SSL_CK_IDEA_128_CBC_WITH_MD5:
// case SSL_CK_DES_192_EDE3_CBC_WITH_MD5:
// if ( cmk.keyArgLength != 8 )
// {
// Weird("SSLv2: Inconsistency of keyArgLength in CLIENT-MASTER-KEY!");
// }
// break;
// }
}
// Remember the used cipher spec.
usedCipherSpec = SSLv2_CipherSpec(cmk.cipherKind);
// If decrypting, store the clear key part of the master key.
if ( ssl_store_key_material /* && cmk.clearKeyLength == 11 */ )
{
pMasterClearKey =
new SSL_DataBlock((recordData + SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE), cmk.clearKeyLength);
pMasterEncryptedKey =
new SSL_DataBlock((recordData + SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE + cmk.clearKeyLength ), cmk.encryptedKeyLength);
}
if ( nextState == CLIENT_MASTERKEY_SEEN )
fire_ssl_conn_established(SSLProxy_Analyzer::SSLv20,
cmk.cipherKind);
return nextState;
}
/*!
* This method analyses a SSLv2 ERROR record.
*
* \param s Pointer to the endpoint which sent the record
* \param length length of SSLv2 ERROR record
* \param data pointer to SSLv2 ERROR record to analyze
*
* \return the updated state of the current ssl connection
*/
SSLv2_States SSLv2_Interpreter::ErrorRecord(SSL_InterpreterEndpoint* s,
int recordLength, const u_char* recordData)
{
++errorRecords;
if ( unsigned(recordLength) != SSLv2_ERROR_RECORD_SIZE )
{
Weird("SSLv2: Size mismatch in Error Record!");
return ERROR_REQUIRED;
}
SSLv2_ErrorRecord er;
er.errorCode = (recordData[1] << 8) | recordData[2];
SSL3x_AlertLevel al = SSL3x_AlertLevel(255);
switch ( er.errorCode ) {
case SSLv2_PE_NO_CIPHER:
// The client doesn't support a cipher which the server
// supports. Only from client to server and not recoverable!
al = SSL3x_ALERT_LEVEL_FATAL;
break;
case SSLv2_PE_NO_CERTIFICATE:
if ( s == orig )
// from client to server: not recoverable
al = SSL3x_ALERT_LEVEL_FATAL;
else
// from server to client: recoverable
al = SSL3x_ALERT_LEVEL_WARNING;
break;
case SSLv2_PE_BAD_CERTIFICATE:
if ( s == orig )
// from client to server: not recoverable
al = SSL3x_ALERT_LEVEL_FATAL;
else
// from server to client: recoverable
al = SSL3x_ALERT_LEVEL_WARNING;
break;
case SSLv2_PE_UNSUPPORTED_CERTIFICATE_TYPE:
if ( s == orig )
// from client to server: not recoverable
al = SSL3x_ALERT_LEVEL_FATAL;
else
// from server to client: recoverable
al = SSL3x_ALERT_LEVEL_WARNING;
break;
default:
al = SSL3x_ALERT_LEVEL_FATAL;
break;
}
fire_ssl_conn_alert(SSLProxy_Analyzer::SSLv20, al, er.errorCode);
return ERROR_SEEN;
}
/*!
* This method analyses a set of SSLv2 cipher suites.
*
* \param s Pointer to the endpoint which sent the cipher suites
* \param length length of cipher suites
* \param data pointer to cipher suites to analyze
*
* \return a pointer to a Bro TableVal (of type cipher_suites_list) which contains
* the cipher suites list of the current analyzed record
*/
TableVal* SSLv2_Interpreter::analyzeCiphers(SSL_InterpreterEndpoint* s,
int length, const u_char* data)
{
if ( length > MAX_CIPHERSPEC_SIZE )
{
if ( s == orig )
Weird("SSLv2: Client has CipherSpecs > MAX_CIPHERSPEC_SIZE");
else
Weird("SSLv2: Server has CipherSpecs > MAX_CIPHERSPEC_SIZE");
}
else
{ // cipher specs are not too big
if ( ssl_compare_cipherspecs )
{ // store cipher specs for state analysis
if ( s == resp )
pServerCipherSpecs =
new SSL_DataBlock(data, length);
else
pClientCipherSpecs =
new SSL_DataBlock(data, length);
}
}
const u_char* pCipher = data;
bool bExtractCipherSuite = false;
TableVal* pCipherTable = 0;
// We only extract the cipher suite when the corresponding
// ssl events are defined (otherwise we do work for nothing
// and suffer a memory leak).
// FIXME: This check needs to be done only once!
if ( (s == orig && ssl_conn_attempt) ||
(s == resp && ssl_conn_server_reply) )
{
pCipherTable = new TableVal(cipher_suites_list);
bExtractCipherSuite = true;
}
for ( int i = 0; i < length; i += 3 )
{
SSL_CipherSpec* pCurrentCipherSpec;
uint32 cipherSpecID =
((pCipher[0] << 16) | pCipher[1] << 8) | pCipher[2];
// Check for unknown cipher specs.
HashKey h(static_cast<bro_uint_t>(cipherSpecID));
pCurrentCipherSpec =
(SSL_CipherSpec*) SSL_CipherSpecDict.Lookup(&h);
if ( ! pCurrentCipherSpec )
{
if ( s == orig )
Weird("SSLv2: Unknown CIPHER-SPEC in CLIENT-HELLO!");
else
Weird("SSLv2: Unknown CIPHER-SPEC in SERVER-HELLO!");
}
if ( bExtractCipherSuite )
{
Val* index = new Val(cipherSpecID, TYPE_COUNT);
pCipherTable->Assign(index, 0);
Unref(index);
}
pCipher += 3;
}
return pCipherTable;
}
// --- SSLv2_EndPoint ---------------------------------------------------------
/*!
* The constructor.
*
* \param interpreter Pointer to the SSLv2 interpreter to whom this endpoint belongs to
* \param is_orig true if this is the originating endpoint of the ssl connection,
* false otherwise
*/
SSLv2_Endpoint::SSLv2_Endpoint(SSLv2_Interpreter* interpreter, int is_orig)
: SSL_InterpreterEndpoint(interpreter, is_orig)
{
sentRecords = 0;
}
/*!
* The destructor.
*/
SSLv2_Endpoint::~SSLv2_Endpoint()
{
}
/*!
* This method is called by the SSLProxy_Analyzer with a complete reassembled
* SSLv2 record. It passes the record to SSLv2_Interpreter::NewSSLRecord().
*
* \param t <b>reserved</b> (always zero)
* \param seq <b>reserved</b> (always zero)
* \param len length of the data block containing the ssl record
* \param data pointer to the data block containing the ssl record
*/
void SSLv2_Endpoint::Deliver(int len, const u_char* data)
{
++((SSLv2_Endpoint*)peer)->sentRecords;
((SSLv2_Interpreter*)interpreter)->NewSSLRecord(this, len, data);
}

View file

@ -1,239 +0,0 @@
// $Id: SSLv2.h 3526 2006-09-12 07:32:21Z vern $
#ifndef SSLV2_H
#define SSLV2_H
#include "SSLInterpreter.h"
#include "SSLCiphers.h"
// --- constants for SSLv2 ---------------------------------------------------
/*!
* In SSLv2, each record is of a special message type. Note that the message
* type is encrypted if the record has been encrypted, so we can determine
* the message type only if we have a cleartext record.
*/
enum SSLv2_MessageTypes {
SSLv2_MT_ERROR = 0, ///< can be in cleartext or encrypted
SSLv2_MT_CLIENT_HELLO = 1, ///< always in cleartext
SSLv2_MT_CLIENT_MASTER_KEY = 2, ///< always in cleartext
SSLv2_MT_CLIENT_FINISHED = 3, ///< always encrypted
SSLv2_MT_SERVER_HELLO = 4, ///< always in cleartext
SSLv2_MT_SERVER_VERIFY = 5, ///< always encrypted
SSLv2_MT_SERVER_FINISHED = 6, ///< always encrypted
SSLv2_MT_REQUEST_CERTIFICATE = 7, ///< always encrypted
SSLv2_MT_CLIENT_CERTIFICATE = 8, ///< always encrypted
};
// Certificate Type Codes.
//
// Authentication Type Codes
// #define SSL_AT_MD5_WITH_RSA_ENCRYPTION 0x01
// Upper/Lower Bounds
// #define SSL_MAX_MASTER_KEY_LENGTH_IN_BITS 256
// #define SSL_MAX_SESSION_ID_LENGTH_IN_BYTES 16
// #define SSL_MIN_RSA_MODULUS_LENGTH_IN_BYTES 64
// #define SSL_MAX_RECORD_LENGTH_2_BYTE_HEADER 32767
// #define SSL_MAX_RECORD_LENGTH_3_BYTE_HEADER 16383
const uint8 SSLv2_CT_X509_CERTIFICATE = 0x01;
/*!
* Error codes used in the error record.
*/
enum SSLv2_ErrorCodes {
SSLv2_PE_NO_CIPHER = 0x0001,
SSLv2_PE_NO_CERTIFICATE = 0x0002,
SSLv2_PE_BAD_CERTIFICATE = 0x0004,
SSLv2_PE_UNSUPPORTED_CERTIFICATE_TYPE = 0x0006
};
// --- structs ----------------------------------------------------------------
const int SSLv2_CLIENT_HELLO_HEADER_SIZE = 9;
struct SSLv2_ClientHelloHeader {
uint8 messageType;
uint16 clientVersion;
uint16 cipherSpecLength;
uint16 sessionIdLength;
uint16 challengeLength;
};
const int SSLv2_SERVER_HELLO_HEADER_SIZE = 11;
struct SSLv2_ServerHelloHeader {
uint8 messageType;
uint8 sessionIdHit;
uint8 certificateType;
uint16 serverVersion;
uint16 certificateLength;
uint16 cipherSpecLength;
uint16 connectionIdLength;
};
const int SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE = 10;
struct SSLv2_ClientMasterKeyHeader {
uint8 messageType;
uint32 cipherKind; // caution: is an uint24
uint16 clearKeyLength;
uint16 encryptedKeyLength;
uint16 keyArgLength;
};
const unsigned int SSLv2_ERROR_RECORD_SIZE = 3;
struct SSLv2_ErrorRecord {
uint8 messageType;
uint16 errorCode;
};
const unsigned int SSLv2_CLIENT_FINISHED_HEADER_SIZE = 1;
struct SSLv2_ClientFinished {
uint8 messageType;
//char CONNECTION-ID[N-1]
};
struct SSLv2_ServerVerify {
uint8 messageType;
//char CHALLENGE-DATA[N-1]
};
struct SSLv2_ServerFinished {
uint8 messageType;
//char SESSION-ID-DATA[N-1]
};
// MISSING:
// CLIENT-CERTIFICATE
// REQUEST-CERTIFICATE
/*!
* States used by the internal SSLv2 automaton.
*/
enum SSLv2_States {
START, ///< start state, no data seen yet
CLIENT_HELLO_SEEN, ///< client hello flew by
NEW_SESSION, ///< server hello with sessionIdHit == 0 seen
CACHED_SESSION, ///< server hello with sessionIdHit != 0 seen
CLIENT_MASTERKEY_SEEN, ///< we saw a client master key record
ERROR_SEEN, ///< we saw an error record
ERROR_REQUIRED ///< one of our critical checks failed, so we think we should see an error record
};
// --- forward declarations ---------------------------------------------------
class SSLv2_Interpreter;
class SSLv2_Endpoint;
class SSLv2_Record;
class SSL_DataBlock;
class SSL_RecordBuilder;
// --- class SSLv2_Interpreter ------------------------------------------------
/*!
* \brief This class is used to analyze SSLv2 connections.
*
* Since there's currently no support for decrypting ssl connections, analysis
* stops when a connection switches to encrypted communication.
* The interpreter does several checks, both record- and connection-orientated.
*
* The record checks mainly consist of consistency checks, where the correct
* use of the SSL 2.0 specification is checked. Furthermore, the CIPHER-SPECS
* of the client and the server can be compared to detect non-intersecting sets.
*
* The connection check monitors the handshaking process for invalid transitions,
* until the end of the cleartext phase.
*
* Several events are thrown for BroScript, including client connection attempt,
* server reply, ssl connection establishment/reuse of former connection, proposed
* cipher suites and certificates seen.
*
* \see SSLv2_Endpoint
*/
class SSLv2_Interpreter : public SSL_Interpreter {
public:
SSLv2_Interpreter(SSLProxy_Analyzer* proxy);
~SSLv2_Interpreter();
void NewSSLRecord(SSL_InterpreterEndpoint* s, int length, const u_char* data);
void analyzeRecord(SSL_InterpreterEndpoint* s, int length, const u_char* data);
SSLv2_States ClientHelloRecord(SSL_InterpreterEndpoint* s,
int recordLength,
const u_char* recordData);
SSLv2_States ServerHelloRecord(SSL_InterpreterEndpoint* s,
int recordLength, const u_char* recordData);
SSLv2_States ClientMasterKeyRecord(SSL_InterpreterEndpoint* s,
int recordLength,
const u_char* recordData);
SSLv2_States ErrorRecord(SSL_InterpreterEndpoint* s,
int recordLength,
const u_char* recordData);
TableVal* analyzeCiphers(SSL_InterpreterEndpoint* s,
int length, const u_char* data);
SSLv2_States ConnState();
static void printStats();
#define MAX_CIPHERSPEC_SIZE ssl_max_cipherspec_size
// Global connection counters.
static uint totalConnections; ///< counter for total sslv2 connections
static uint analyzedConnections; ///< counter for analyzed (=not partial) connections
static uint openedConnections; ///< counter for SSLv2 connections with complete handshake
static uint failedConnections; ///< counter for SSLv2 connections with failed but correct handshake
static uint weirdConnections; ///< counter for SSLv2 connections with failed and weird handshake
// Global record counters.
static uint totalRecords; ///< counter for total SSLv2 records seen
static uint clientHelloRecords; ///< counter for SSLv2 CLIENT-HELLOs seen
static uint serverHelloRecords; ///< counter for SSLv2 SERVER-HELLOs seen
static uint clientMasterKeyRecords; ///< counter for SSLv2 CLIENT-MASTER-KEYSs seen
static uint errorRecords; ///< counter for SSLv2 ERRORs seen
// Counters for this instance.
uint32 records; ///< counter for SSLv2 records of this connection
SSLv2_States connState; ///< state of connection
bool bAnalyzedCounted; ///< flag for counting analyzedConnections
// FIXME: this should be states.
bool bClientWantsCachedSession; ///< true if the client wants a cached session, false otherwise
protected:
void BuildInterpreterEndpoints();
SSL_DataBlock* pClientCipherSpecs; ///< the CIPHER-SPECs from the client
SSL_DataBlock* pServerCipherSpecs; ///< the CIPHER-SPECs from the server
SSLv2_CipherSpec usedCipherSpec; ///< the used CIPHER-SPEC for this connection
// Currently experimental:
SSL_DataBlock* pConnectionId; // 16 <= ConnectionId <= 32
SSL_DataBlock* pChallenge; // 16 <= Challenge <= 32
SSL_DataBlock* pSessionId; // has to be 16 Bytes
SSL_DataBlock* pMasterClearKey;
SSL_DataBlock* pMasterEncryptedKey;
SSL_DataBlock* pClientReadKey;
SSL_DataBlock* pServerReadKey;
};
// --- class SSLv2_Endpoint ---------------------------------------------------
/*!
* \brief This class represents an endpoint of an SSLv2 connection.
*
* Fully reassembled SSLv2 records are passed to its Deliver() function.
* There, some counters are updated and the record is then passed to
* SSLv2_Interpreter::NewSSLRecord().
*/
class SSLv2_Endpoint: public SSL_InterpreterEndpoint {
public:
SSLv2_Endpoint(SSLv2_Interpreter* interpreter, int is_orig);
virtual ~SSLv2_Endpoint();
void Deliver(int len, const u_char* data);
uint32 sentRecords; ///< counter for sent records of this endpoint
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,590 +0,0 @@
// $Id: SSLv3.h 3526 2006-09-12 07:32:21Z vern $
#ifndef sslv3_h
#define sslv3_h
#include "SSLInterpreter.h"
#include "SSLProxy.h"
#include "SSLv3Automaton.h"
#include "SSLDefines.h"
// Offsets in SSL record layer header.
const int SSL3_1_CONTENTTYPEOFFSET = 0;
const int SSL3_1_VERSIONTYPEOFFSET = 1;
const int SSL3_1_LENGTHOFFSET = 3;
const int SSL3_1_HEADERLENGTH = 5;
// --- forward declarations ---------------------------------------------------
class SSL_Interpreter;
class SSL_InterpreterEndpoint;
class SSLProxy_Analyzer;
class SSLv3_Endpoint;
class SSLv3_Record;
class SSLv3_HandshakeRecord;
class SSLv3_AlertRecord;
class SSLv3_ApplicationRecord;
class SSLv3_ChangeCipherRecord;
class CertStore;
struct SSL_CipherSpec;
// --- enums for SSLv3.0/3.1 message handling ---------------------------------
enum SSL3_1_ContentType {
SSL3_1_TYPE_CHANGE_CIPHER_SPEC = 20,
SSL3_1_TYPE_ALERT = 21,
SSL3_1_TYPE_HANDSHAKE = 22,
SSL3_1_TYPE_APPLICATION_DATA = 23
};
enum SSL3_1_HandshakeType {
SSL3_1_HELLO_REQUEST = 0,
SSL3_1_CLIENT_HELLO = 1,
SSL3_1_SERVER_HELLO = 2,
SSL3_1_CERTIFICATE = 11,
SSL3_1_SERVER_KEY_EXCHANGE = 12,
SSL3_1_CERTIFICATE_REQUEST = 13,
SSL3_1_SERVER_HELLO_DONE = 14,
SSL3_1_CERTIFICATE_VERIFY = 15,
SSL3_1_CLIENT_KEY_EXCHANGE = 16,
SSL3_1_FINISHED = 20
};
enum SSL3_1_AlertDescription {
SSL3_1_CLOSE_NOTIFY = 0,
SSL3_1_UNEXPECTED_MESSAGE = 10,
SSL3_1_BAD_RECORD_MAC = 20,
SSL3_1_DECRYPTION_FAILED = 21,
SSL3_1_RECORD_OVERFLOW = 22,
SSL3_1_DECOMPRESSION_FAILURE = 30,
SSL3_1_HANDSHAKE_FAILURE = 40,
SSL3_0_NO_CERTIFICATE = 41,
SSL3_1_BAD_CERTIFICATE = 42,
SSL3_1_UNSUPPORTED_CERTIFICATE = 43,
SSL3_1_CERTIFICATE_REVOKED = 44,
SSL3_1_CERTIFICATE_EXPIRED = 45,
SSL3_1_CERTIFICATE_UNKNOWN = 46,
SSL3_1_ILLEGAL_PARAMETER = 47,
SSL3_1_UNKNOWN_CA = 48,
SSL3_1_ACCESS_DENIED = 49,
SSL3_1_DECODE_ERROR = 50,
SSL3_1_DECRYPT_ERROR = 51,
SSL3_1_EXPORT_RESTRICTION = 60,
SSL3_1_PROTOCOL_VERSION = 70,
SSL3_1_INSUFFICIENT_SECURITY = 71,
SSL3_1_INTERNAL_ERROR = 80,
SSL3_1_USER_CANCELED = 90,
SSL3_1_NO_RENEGOTIATION = 100
};
enum SSL3x_AlertLevel {
SSL3x_ALERT_LEVEL_WARNING = 1,
SSL3x_ALERT_LEVEL_FATAL = 2
};
// --- structs ----------------------------------------------------------------
struct SSLv3x_Random {
uint32 gmt_unix_time;
SSL_DataBlock* random_bytes; // 28-bytes
};
struct SSLv3x_ServerRSAParams {
SSL_DataBlock* rsa_modulus; // <1..2^16-1>
SSL_DataBlock* rsa_exponent; // <1..2^16-1>
};
struct SSLv3x_ServerDHParams{
SSL_DataBlock* dh_p; // <1..2^16-1>
SSL_DataBlock* dh_g; // <1..2^16-1>
SSL_DataBlock* dh_Ys; // <1..2^16-1>
};
struct SSLv3x_EncryptedPremasterSecret{
SSL_DataBlock* encryptedSecret;
};
struct SSLv3x_ClientDHPublic{
SSL_DataBlock* dh_Yc; // <1..2^16-1>
};
// ----------------------------------------------------------------------------
/**
* Class SSLv3_Interpreter is the implementation for a SSLv3.0/SSLv3.1 connection
* interpreter (derived from the abstract class SSL_Interpreter).
*
* The corresponding SSLProxy_Analyzer creates an instance of this class and
* properly initialises the corresponding SSLv3_Endpoints by calling the
* SSL_Interpreter's Init() method which then invokes the BuildInterpreterEndpoints() method
* (of the SSLv3_Interpreter) which causes the SSLv3_Endpoints to be created properly.
*
* The SSLv3_Interpreter receives the four various SSLv3_Records:
* - SSLv3_HandshakeRecord
* - SSLv3_AlertRecord
* - SSLv3_ChangeCipherRecord
* - SSLv3_ApplicationRecord
* via the DeliverSSLv3_Record() methods from the two corresponding SSLv3_Endpoints
* (which get fed by the SSLProxy_Analyzer).
*
* There is one global static SSLv3_Automaton which describes the possible transitions
* in the SSLv3.0/SSLv3.1 state machine for the handshaking phase. This automaton
* is initialised once, when Bro sees the first SSL connection. By the attribute
* currentState every instance of SSLv3_Interpreter holds the automaton state it
* is currently in and so is able
* to track the correctness of the SSL handshaking process. It weird-checks and verifies
* all arriving SSL records up to the point where handshaking is finished or an
* error occures caused by weird SSL records or not allowed transitions in the
* state machine. Information that was negotiated between the client and server
* during the handshaking phase is/may also be stored. That is:
* - used SSL version
* - negotiated cipher suite
* - session ID
* - client/server random
* - key exchange algorithms
* - cryptographic parameters
* - encrypted pre-master secret
*
* The certificates are verified using the analyzeCertificate() method of the
* SSL_Interpreter class.
* Events for Bro scripting are thrown for client connection attempt, server reply,
* ssl connection establishment/reuse of former connection, proposed cipher suites and
* seen certificates.
*
* @see SSLv3_Endpoint
*/
class SSLv3_Interpreter : public SSL_Interpreter {
public:
/** The constructor takes the SSLProxy_Analyzer as argument,
* that created this SSLv3_Interpreter.
*
* @param proxy the creating SSL_ConectionProxy
*/
SSLv3_Interpreter(SSLProxy_Analyzer* proxy);
~SSLv3_Interpreter();
/** Delivers an SSLv3_HandshakeRecord to the SSLv3_Interpreter.
* The record gets verified and it is checked whether it is allowed
* in the current phase of the handshaking process.
*
* @param rec the SSLv3_HandshakeRecord
*/
void DeliverSSLv3_Record(SSLv3_HandshakeRecord* rec);
/** Delivers an SSLv3_AlertRecord to the SSLv3_Interpreter.
* The record gets verified and weird checked.
*
* @param rec the SSLv3_AlertRecord
*/
void DeliverSSLv3_Record(SSLv3_AlertRecord* rec);
/** Delivers an SSLv3_ChangeCipherRecord to the SSLv3_Interpreter.
* The record gets verified and weird checked. If a change cipher
* record is received the next record from this endpoint needs
* to be a finished message.
*
* @param rec the SSLv3_ChangeCipherRecord
*/
void DeliverSSLv3_Record(SSLv3_ChangeCipherRecord* rec);
/** Delivers a SSLv3_ApplicationRecord to the SSLv3_Interpreter.
* It is checked, whether handshaking phase is already finished
* and sending application data is valid (the normal case is,
* that after finishing the handshaking phase, all further data
* is skipped).
*
* @param rec the SSLv3_AlertRecord
*/
void DeliverSSLv3_Record(SSLv3_ApplicationRecord* rec);
/** This method sets the currentState variable of this SSLv3_Interpreter
* and so sets the SSLv3.0/SSLv3.1 state machine to the state passed
* as parameter.It is invoked in the SSLProxy_Analyzer to enable
* this SSLv3_Interpreter to start work without having seen the first
* handshake record. This happens, when the client hello is sent in
* SSLv2 format and processed by the SSLv2_Interpreter but the further
* SSL connection taking place as a SSLv3.0/SSLv3.1 session.
*
* @param i the new state of the sslAutomaton
*/
void SetState(int i);
static void printStats();
// Total SSLv3x connections.
static uint totalConnections;
// Total SSLv3x connections with <b>complete</b> handshake.
static uint openedConnections;
static uint totalRecords; ///< counter for total SSLv3x records seen
static uint handshakeRecords; ///< counter for SSLv3x handshake records seen
static uint clientHelloRecords; ///< counter for SSLv3x client hellos seen
static uint serverHelloRecords; ///< counter for SSLv3x server hellos seen
static uint alertRecords; ///< counter for SSLv3x alert records seen
static uint changeCipherRecords; ///< counter for SSLv3x change cipher records seen
/**Flags for handling the change-cipher-messages and fin-handshake-
* messages
*/
bool change_cipher_client_seen; ///< whether a client change cipher record was seen
bool change_cipher_server_seen; ///< whether a server change cipher record was seen
bool fin_client_seen; ///< whether a client finished handshake message was seen (must immediately follow the client change cipher)
bool fin_server_seen; ///< whether a server finished handshake message was seen (must immediately follow the server change cipher)
protected:
static SSLv3_Automaton sslAutomaton; ///< represents the SSLv3.0/SSLv3.1 automaton
static bool bInited; ///< whether the automaton is already initialised (has to be only done once)
int currentState; ///< the current state of the SSL automaton in this SSLv3_Interpreter instance
// uint16 cipherSuite; ///< the cipher spec client and server agreed upon
SSL_DataBlock* pClientCipherSpecs; ///< the CIPHER-SPECs from the client
SSL_CipherSpec* pCipherSuite; ///< pointer to the cipher spec definition client and server agreed upon
uint32 cipherSuiteIdentifier; ///< only used for unknown cipher-specs
SSL_DataBlock* clientSessionID; ///< the session ID of the client hello record
SSL_DataBlock* serverSessionID; ///< the session ID for this SSL session
/**Attributes for cryptographic computations*/
SSLv3x_Random* clientRandom;
SSLv3x_Random* serverRandom;
//SSL_KeyExchangeAlgorithm keyXAlgorithm;
SSLv3x_ServerRSAParams* serverRSApars;
SSLv3x_ServerDHParams* serverDHPars;
SSLv3x_EncryptedPremasterSecret* encryptedPreSecret;
SSLv3x_ClientDHPublic* clientDHpublic;
bool helloRequestValid; ///< Whether sending a hello request is valid (normally after handshaking phase)
/** This method builds the corresponding SSLv3_Endpoints for this SSLv3_Interpreter.
* It is called in the SSL_Interpreter's Init() method.
*/
void BuildInterpreterEndpoints();
/** This method initialises the SSL state automaton; sets the states and transitions.
* It needs only to be called once for a whole bro. @see SSLDefines.h
*/
void BuildAutomaton();
/** This helper method translates the handshake types included in the SSL handshake
* records to the corresponding transition of the SSL automaton. It is invoked within
* the DeliverSSLv3_Record(SSLv3_HandshakeRecord*) method.
*
* @param type the handshake type of the handshake record
*/
int HandshakeType2Trans(int type);
/** This method is used for event generation during the handshaking phase and
* generates the connection-attempt, server-reply, connection-established, connection-reused
* events dependant on the currentState of the SSL Automaton.
* It calls the appropriate fire_* methods of the SSL_Interpreter for this.
* The method is called within the the DeliverSSLv3_Record() methods.
*
* @param rec the SSLv3_Record which is currently processed
*/
void GenerateEvents(SSLv3_Record* rec, TableVal* curCipherSuites);
/** This method analyzes the cipher suites the client and server offer
* each other during handshaking phase. It checks, whether it's a 'common'
* cipher suite and sets the pCipherSuite attribute according to the
* cipher suite client and server agreed on.
*
* @param s the SSLv3_Endpoint which sent the SSL record with the cipher suite(s) to be analyzed
* @param length length of data
* @param data pointer to where the cipher suites can be found
* @param version SSL version of the SSL record that contained the cipher suite(s)
* @return a pointer to a Bro TableVal (of type cipher_suites_list) which contains
* the cipher suites list of the current analyzed record
*/
TableVal* analyzeCiphers(const SSLv3_Endpoint* s, int length, const u_char* data, uint16 version);
};
// ----------------------------------------------------------------------------
/** Class SSLv3_Endpoint is the implementation for SSLv3.0/SSLv3.1 connection
* endpoints (derived from the abstract class SSL_InterpreterEndpoint).
*
* A SSLv3_Endpoint gets completely reassembled ssl records via the method
* Deliver(), which is invoked in this Endpoint's corresponding SSLProxy_Analyzer.
* The defragmentation and reassembling already took place in the
* SSLProxy_Analyzer's SSL_RecordBuilder.
* The Deliver()-method does some basic weird checks and then calls
* ProcessMessage() which determines the content type of the ssl record
* and, dependant on that, creates an instance of the appropriate SSLv3_Record.
* This is passed on to this endpoint's corresponding SSLv3_Interpreter via
* the DeliverSSLv3_Record()-method.
*/
class SSLv3_Endpoint : public SSL_InterpreterEndpoint {
public:
/** The constructor takes the corresponding SSL_Interpreter as argument.
* is_orig sets this endpoint as originator of the connection (1), and
* responder otherwise (0).
*
* @param interpreter the SSL_Interpreter this endpoint is bound to.
* @param is_orig whether this endpoint is the originator (1) of the
* connection or not (0).
*/
SSLv3_Endpoint(SSL_Interpreter* interpreter, int is_orig);
virtual ~SSLv3_Endpoint();
/** This method is invoked by this endpoint's corresponding
* SSLProxy_Analyzer and receives completely reassembled SSL
* records (by the data argument).
*
* @param t time is always 0 (former: when the segment was received
* by bro (?))
* @param len length of SSL record
* @param data content of SSL record
*/
void Deliver(int len, const u_char* data);
protected:
uint16 sslVersion; ///< holds the version of the just delivered SSL record
uint16 currentMessage_length; ///< the length of the just delivered SSL record
/** This method extracts the content type of the SSL record passed
* in the first parameter.
*
* @param data the SSL record
* @param len length of the record
* @return SSL3_1_ContentType of SSL record
*/
SSL3_1_ContentType ExtractContentType(const u_char* data, int len);
/** This method determines the version of the SSL record passed to it.
* It sets the field sslVersion of this endpoint an is called within
* the method ProcessMessage().
*
* @param data the SSL record
* @param len length of the record
* @return 0 if version is NOT 3.0/3.1, 1 otherwise
*/
int ExtractVersion(const u_char* data, int len);
/** This method processes a complete SSL record. It
* determines the content type of the SSL record
* (handshake, alert, change-cipher-spec, application),
* cuts away the SSL record layer header and generates
* the appropriate SSLv3_Record. Then it calls the SSLv3_Record's
* Deliver() method, which manages the delivery of the record
* to the corresponding SSLv3_Interpreter
*
* @param data the complete SSL record
* @param len data's (record's) length
*/
void ProcessMessage(const u_char* data, int len);
};
// ----------------------------------------------------------------------------
// Offsets are now relative to the end of the SSL record layer header
#define SSL3_1_CHANGE_CIPHER_TYPE_OFFSET (SSL3_1_HEADERLENGTH - 5)
#define SSL3_1_ALERT_LEVEL_OFFSET (SSL3_1_HEADERLENGTH - 5)
#define SSL3_1_ALERT_DESCRIPTION_OFFSET (SSL3_1_HEADERLENGTH - 4)
#define SSL3_1_SESSION_ID_LENGTH_OFFSET 38
#define SSL3_1_SESSION_ID_OFFSET 39
/** This class is an abstract base class for the four different
* SSLv3.0/SSLv3.1 record types (handshake, alert, change-cipher-spec,
* application).
*
* It contains a pointer to the data of the SSL record <b>without</b>
* the SSL record layer header, it's length and the version information, which
* was present in the now cut away SSL record layer header.
*
* (Note: the version field of the SSL record layer header may differ from
* the version of the record format that was used when sending the record.
* (e.g. a client may send a SSLv2 record including
* a version field containing 3.1 (for SSLv3.1) to show, that he supports
* version 3.1.))
*
* Every subclass of SSLv3_Record implements the Deliver() method, which
* manages the delivery of the record to the corresponding SSLv3_Interpreter.
* Instances of SSLv3_Record (resp. it's subclasses) are created within
* the ProcessMessage() method of a SSLv3_Endpoint.
* */
class SSLv3_Record : public BroObj{
public:
/** The constructor gets a pointer to the SSL record without the
* record layer header, it's length, the version information
* contained in the record layer header and a pointer to the
* SSLv3_Endpoint that created this instance of SSLv3_Record.
*
* @param data pointer to the SSL record without record layer header
* @param data's length
* @param version version information contained in the SSL record layer header
* @param e the SSLv3_Endpoint that created this instance
*/
SSLv3_Record(const u_char* data, int len, uint16 version,
SSLv3_Endpoint const* e);
~SSLv3_Record();
void Describe(ODesc* d) const;
int GetRecordLength() const;
const u_char* GetData() const;
uint16 GetVersion() const;
SSLv3_Endpoint const* GetEndpoint() const;
/** This abstract method is implemented by the various SSLv3_Record
* subclasses for handshake, alert, change cipher spec and application
* records. It manages the delivery of the SSLv3_Record to the
* SSLv3_Interpreter passed as argument.
* SSLv3_Records are created within the ProcessMessage() method of the
* SSLv3_Endpoint which then calls the just created SSLv3_Records
* Deliver() method with it's corresponding SSLv3_Interpreter as
* argument which then receives the (evtl. preprocessed) SSLv3_Record
* (via the method(s) DeliverSSLv3_Record()).
*
* @param conn the SSLv3_Interpreter to which this SSLv3_Record should be deliverd
*/
virtual void Deliver(SSLv3_Interpreter* conn) =0;
/** Helper function that converts from 24-bit big endian integer
* starting at offset to 32 bit integer in little endian format.
*
* @param data the ssl-record
* @param len length of data (record)
* @param offset where the 24 bit big endian starts
* @return 32 bit little endian
*/
int ExtractInt24(const u_char* data, int len, int offset);
int recordLength; ///< total length of the SSL record <b>without</b> the record layer header
const u_char* data; ///< pointer to the SSL record without the record layer header
SSLv3_Endpoint const* endp; ///< pointer to the SSLv3_Endpoint that created this instance of SSLv3_Record
uint16 sslVersion; ///< version information of the SSL record layer header
};
// ----------------------------------------------------------------------------
/* This class represents a handshake record used in SSLv3.0/SSLv3.1.
*
* Handshake records in SSLv3.0/SSLv3.1 need a special treatment,
* because it is possible that multiple handshake messages are coalesced into
* a single SSLv3.0/SSLv3.1 record.
* I think, this only can happen to
* handshake records (even RFC2246 page 16 generally talks about all
* messages of a same content type), because only handshake records
* have got an own length descriptor within and thus make de-coalescing
* possible.
* So when generating a new instance of a SSLv3_HandshakeRecord, it is checked whether there
* are more handshake records within data.
* If so, they are put apart and linked together to a chain by using
* the next-pointer.
* The Deliver() method takes this into account and delivers every single
* handshake record one by one to the SSLv3_Interpreter.
*/
class SSLv3_HandshakeRecord : public SSLv3_Record{
public:
SSLv3_HandshakeRecord(const u_char* data, int len, uint16 version,
SSLv3_Endpoint const* e);
~SSLv3_HandshakeRecord();
int GetType() const;
int GetLength() const;
/** This method delivers the SSLv3_HandshakeRecord(s) to the
* SSLv3_Interpreter passed as argument. The method follows
* the next-pointer and delivers every SSLv3_HandshakeRecord
* contained in this list to the SSLv3_Interpreter.
*
* @param conn the SSLv3_Interpreter to which this SSLv3_HandshakeRecord should be deliverd
*/
void Deliver(SSLv3_Interpreter* conn);
/* This method is invoked within the SSLv3_Interpreter and does lots of
* weird and consistency checks on a client hello SSL handshake record.
*
* @return 0 if further processing of this client hello is not
* possible due to inconsistency and 1 otherwise.
*/
int checkClientHello();
/* This method is invoked within the SSLv3_Interpreter and does lots of
* weird and consistency checks on a server hello SSL handshake record.
*
* @return 0 if further processing of this server hello is not
* possible due to inconsistency and 1 otherwise.
*/
int checkServerHello();
int type; ///< holds the handshake type of the handshake record (first byte)
int length; ///< holds the length of this handshake record (which is needed due to coalesced handshake messages)
private:
SSLv3_HandshakeRecord* next; ///< pointer to the next ssl handshake record if they are coalesced into a single record
SSLv3_HandshakeRecord* GetNext();
};
// ----------------------------------------------------------------------------
/** This class represents an alert record used in SSLv3.0/SSLv3.1.
*
* description holds the SSL alert description and level the alert level.
*/
class SSLv3_AlertRecord : public SSLv3_Record {
public:
SSLv3_AlertRecord(const u_char* data, int len, uint16 version,
SSLv3_Endpoint const* e);
~SSLv3_AlertRecord();
int GetDescription() const;
int GetLevel() const;
/** This method delivers the SSLv3_AlertRecord to the SSLv3_Interpreter passed as
* argument.
*
* @param conn the SSLv3_Interpreter to which this SSLv3_AlertRecord should be deliverd
*/
void Deliver(SSLv3_Interpreter* conn);
int description; ///< holds the alert description
int level; ///< holds the alert level
};
// ----------------------------------------------------------------------------
/** This class represents a change cipher record used in SSLv3.0/SSLv3.1.
*
* type holds the change cipher type used (currently only 1 is valid (rfc 2246))
*/
class SSLv3_ChangeCipherRecord : public SSLv3_Record{
public:
SSLv3_ChangeCipherRecord(const u_char* data, int len, uint16 version,
SSLv3_Endpoint const* e);
~SSLv3_ChangeCipherRecord();
int GetType() const;
/** This method delivers the SSLv3_ChangeCipherRecord to the
* SSLv3_Interpreter passed as argument.
*
* @param conn the SSLv3_Interpreter to which this
* SSLv3_ChangeCipherRecord should be delivered
*/
void Deliver(SSLv3_Interpreter* conn);
int type; ///< holds the change cipher type
};
// ----------------------------------------------------------------------------
/** This class represents an application record used in SSLv3.0/SSLv3.1.
*/
class SSLv3_ApplicationRecord : public SSLv3_Record {
public:
SSLv3_ApplicationRecord(const u_char* data, int len, uint16 version,
SSLv3_Endpoint const* e);
~SSLv3_ApplicationRecord();
/** This method delivers the SSLv3_ApplicationRecord to the
* SSLv3_Interpreter passed as argument.
*
* @param conn the SSLv3_Interpreter to which this
* SSLv3_ApplicationRecord should be deliverd
*/
void Deliver(SSLv3_Interpreter* conn);
};
#endif

View file

@ -1,83 +0,0 @@
// $Id: SSLv3Automaton.cc 80 2004-07-14 20:15:50Z jason $
// ---SSLv3_Automaton----------------------------------------------------------
#include "SSLv3Automaton.h"
SSLv3_Automaton::SSLv3_Automaton(int arg_num_states, int num_trans,
int error_state)
{
num_states = arg_num_states;
states = new SSLv3_State*[num_states];
for ( int i = 0; i < num_states; ++i )
states[i] = new SSLv3_State(num_trans, error_state);
}
SSLv3_Automaton::~SSLv3_Automaton()
{
for ( int i = 0; i < num_states; ++i )
delete states[i];
delete [] states;
}
void SSLv3_Automaton::Describe(ODesc* d) const
{
d->Add("sslAutomaton");
}
void SSLv3_Automaton::setStartState(int state)
{
if ( state < num_states )
startState = state;
}
void SSLv3_Automaton::addTrans(int state1, int trans, int state2)
{
if ( state1 < num_states && state2 < num_states )
states[state1]->addTrans(trans, state2);
}
int SSLv3_Automaton::getNextState(int state, int trans)
{
if ( state < num_states )
return states[state]->getNextState(trans);
else
return 0;
}
int SSLv3_Automaton::getStartState()
{
if (startState >= 0)
return startState;
else
return -1;
}
// ---SSLv3_State--------------------------------------------------------------
SSLv3_State::SSLv3_State(int num_trans, int error_state)
{
this->num_trans = num_trans;
transitions = new int[num_trans];
for ( int i = 0; i < num_trans; ++i )
transitions[i] = error_state;
}
SSLv3_State::~SSLv3_State()
{
delete [] transitions;
}
void SSLv3_State::addTrans(int trans, int state)
{
if ( trans < num_trans )
transitions[trans] = state;
}
int SSLv3_State::getNextState(int trans)
{
if ( trans < num_trans )
return transitions[trans];
else
return 0;
}

View file

@ -1,107 +0,0 @@
// $Id: SSLv3Automaton.h 80 2004-07-14 20:15:50Z jason $
#ifndef ssl_v3_automaton_h
#define ssl_v3_automaton_h
#include "Obj.h"
#include "SSLDefines.h"
class SSLv3_State;
/** Class SSLv3_Automaton is there for holding the transitions of a state machine.
* The States are simply Integer Constants >= 0. Same for the transitions.
* The SSLv3_Automaton holds a pointer to an array of pointers to the
* states of the automaton. The array is indexed by the integer that
* represents the corresponding state.
* By default, the automaton is initialized with every transition leading to
* the error_state.
* By calling addTrans() (done in the SSLv3_Interpreter's BuildAutomaton()-method)
* the proper transitions for the SSL automaton are created.
* When calling getNextState(state, trans), you get the next state of the
* automaton, according to state and trans.
* */
class SSLv3_Automaton : public BroObj {
public:
/* The constructor initialises the states 2-dim. array
* (which's size depends on num_states and num_trans).
* By default, every transition from every state leads to the error_state.
* @param num_states how many states the automaton has
* @param num_trans how many different transitions the automaton has
* @param error_state which Integer the error_state has
*/
SSLv3_Automaton(int num_states, int num_trans, int error_state);
~SSLv3_Automaton();
void Describe(ODesc* d) const;
/* Sets the start state of the automaton.
* @param state the start state
*/
void setStartState(int state);
/* This method is used for building up the automaton and defining
* from which state you get to which state which what transition.
* @param state1 the state from which the transition starts
* @param trans the transition itself
* @param to which state the transition leads
*/
void addTrans(int state1, int trans, int state2);
/* Used for determinig into which state the automaton gets by using the
* given transition in the given state.
* @param state the state from which the transition starts
* @param trans the transition itself
* @return the state to which the transition leads
*/
int getNextState(int state, int trans);
int getStartState();
int OutRef()
{
return RefCnt();
}
protected:
int num_states; ///< how many states the automaton has
SSLv3_State** states; ///< the pointer to the array of pointers that holds the states
int startState; ///< the start state of the automaton
};
// ----------------------------------------------------------------------------
/** This class represents a state of the SSLv3_Automaton.
* It holds a pointer to an array of integers, which corresponds to the
* succeeding states of this state when "taking" a transition.
* The transition array is indexed by the integer-values corresponding to
* the transitions of the automaton.
* */
class SSLv3_State {
public:
/* The constructor initialises the state. By default, every transition
* of the automaton leads to the error_state.
* @param num_trans how many different transitions the automaton has
* @param error_state how many different transitions the automaton has
*/
SSLv3_State(int num_trans, int error_state);
~SSLv3_State();
/* This method is used for building up the automaton and is invoked by
* the SSLv3_Automaton's addTrans()-method. It defines the successing state
* of the automaton by taking the transition trans in this state.
* @param trans the transition,
* @param that leads to the state
*/
void addTrans(int trans, int state);
/* Used for determinig into which state the automaton gets by using the
* given transition in the this state.
* @param trans which transition is to be taken
* @return the resulting state of the automaton
*/
int getNextState(int trans);
protected:
int num_trans; ///< how many transitions the automaton has
int* transitions; ///< the array of successing states of this state by taking the transition that indexes this array
};
#endif

View file

@ -1,265 +0,0 @@
// $Id: X509.cc 6724 2009-06-07 09:23:03Z vern $
#include <openssl/err.h>
#include "X509.h"
#include "config.h"
// ### NOTE: while d2i_X509 does not take a const u_char** pointer,
// here we assume d2i_X509 does not write to <data>, so it is safe to
// convert data to a non-const pointer. Could some X509 guru verify
// this?
X509* d2i_X509_(X509** px, const u_char** in, int len)
{
#ifdef OPENSSL_D2I_X509_USES_CONST_CHAR
return d2i_X509(px, in, len);
#else
return d2i_X509(px, (u_char**)in, len);
#endif
}
X509_STORE* X509_Cert::ctx = 0;
X509_LOOKUP* X509_Cert::lookup = 0;
X509_STORE_CTX X509_Cert::csc;
bool X509_Cert::bInited = false;
// TODO: Check if Key < 768 Bits => Weakness!
// FIXME: Merge verify and verifyChain.
void X509_Cert::sslCertificateEvent(Contents_SSL* e, X509* pCert)
{
EventHandlerPtr event = ssl_certificate;
if ( ! event )
return;
char tmp[256];
RecordVal* pX509Cert = new RecordVal(x509_type);
X509_NAME_oneline(X509_get_issuer_name(pCert), tmp, sizeof tmp);
pX509Cert->Assign(0, new StringVal(tmp));
X509_NAME_oneline(X509_get_subject_name(pCert), tmp, sizeof tmp);
pX509Cert->Assign(1, new StringVal(tmp));
pX509Cert->Assign(2, new AddrVal(e->Conn()->OrigAddr()));
val_list* vl = new val_list;
vl->append(e->BuildConnVal());
vl->append(pX509Cert);
vl->append(new Val(e->IsOrig(), TYPE_BOOL));
e->Conn()->ConnectionEvent(event, e, vl);
}
void X509_Cert::sslCertificateError(Contents_SSL* e, int error_numbe)
{
Val* err_str = new StringVal(X509_verify_cert_error_string(csc.error));
val_list* vl = new val_list;
vl->append(e->BuildConnVal());
vl->append(new Val(csc.error, TYPE_INT));
vl->append(err_str);
e->Conn()->ConnectionEvent(ssl_X509_error, e, vl);
}
int X509_Cert::init()
{
#if 0
OpenSSL_add_all_algorithms();
#endif
ctx = X509_STORE_new();
int flag = 0;
int ret = 0;
if ( x509_trusted_cert_path &&
x509_trusted_cert_path->AsString()->Len() > 0 )
{ // add the path(s) for the local CA's certificates
const BroString* pString = x509_trusted_cert_path->AsString();
lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_hash_dir());
if ( ! lookup )
{
fprintf(stderr, "X509_Cert::init(): initing lookup failed\n");
flag = 1;
}
int i = X509_LOOKUP_add_dir(lookup,
(const char*) pString->Bytes(),
X509_FILETYPE_PEM);
if ( ! i )
{
fprintf( stderr, "X509_Cert::init(): error adding lookup directory\n" );
ret = 0;
}
}
else
{
printf("X509: Using the default trusted cert path.\n");
X509_STORE_set_default_paths(ctx);
}
// Add crl functionality - will only add if defined and
// X509_STORE_add_lookup was successful.
if ( ! flag && x509_crl_file && x509_crl_file->AsString()->Len() > 0 )
{
const BroString* rString = x509_crl_file->AsString();
if ( X509_load_crl_file(lookup, (const char*) rString->Bytes(),
X509_FILETYPE_PEM) != 1 )
{
fprintf(stderr, "X509_Cert::init(): error reading CRL file\n");
ret = 1;
}
#if 0
// Note, openssl version must be > 0.9.7(a).
X509_STORE_set_flags(ctx,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
#endif
}
bInited = true;
return ret;
}
int X509_Cert::verify(Contents_SSL* e, const u_char* data, uint32 len)
{
if ( ! bInited )
init();
X509* pCert = d2i_X509_(NULL, &data, len);
if ( ! pCert )
{
// 5 = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY
sslCertificateError(e, 5);
return -1;
}
sslCertificateEvent(e, pCert);
X509_STORE_CTX_init(&csc, ctx, pCert, 0);
X509_STORE_CTX_set_time(&csc, 0, (time_t) network_time);
int i = X509_verify_cert(&csc);
X509_STORE_CTX_cleanup(&csc);
int ret = 0;
int ext = X509_get_ext_count(pCert);
if ( ext > 0 )
{
TableVal* x509ex = new TableVal(x509_extension);
val_list* vl = new val_list;
char buf[256];
for ( int k = 0; k < ext; ++k )
{
X509_EXTENSION* ex = X509_get_ext(pCert, k);
ASN1_OBJECT* obj = X509_EXTENSION_get_object(ex);
i2t_ASN1_OBJECT(buf, sizeof(buf), obj);
Val* index = new Val(k+1, TYPE_COUNT);
Val* value = new StringVal(strlen(buf), buf);
x509ex->Assign(index, value);
Unref(index);
// later we can do critical extensions like:
// X509_EXTENSION_get_critical(ex);
}
vl->append(e->BuildConnVal());
vl->append(x509ex);
e->Conn()->ConnectionEvent(process_X509_extensions, e, vl);
}
if ( ! i )
{
sslCertificateError(e, csc.error);
ret = csc.error;
}
else
ret = 0;
delete pCert;
return ret;
}
int X509_Cert::verifyChain(Contents_SSL* e, const u_char* data, uint32 len)
{
if ( ! bInited )
init();
// Gets an ssl3x cert chain (could be one single cert, too,
// but in chain format).
// Init the stack.
STACK_OF(X509)* untrustedCerts = sk_X509_new_null();
if ( ! untrustedCerts )
{
// Internal error allocating stack of untrusted certs.
// 11 = X509_V_ERR_OUT_OF_MEM
sslCertificateError(e, 11);
return -1;
}
// NOT AGAIN!!!
// Extract certificates and put them into an OpenSSL Stack.
uint tempLength = 0;
int certCount = 0;
X509* pCert = 0; // base cert, this one is to be verified
while ( tempLength < len )
{
++certCount;
uint32 certLength =
uint32((data[tempLength + 0] << 16) |
data[tempLength + 1] << 8) |
data[tempLength + 2];
// Points to current cert.
const u_char* pCurrentCert = &data[tempLength+3];
X509* pTemp = d2i_X509_(0, &pCurrentCert, certLength);
if ( ! pTemp )
{ // error is somewhat of a misnomer
// 5 = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY
sslCertificateError(e, 5);
//FIXME: free ptrs
return -1;
}
if ( certCount == 1 )
// The first certificate goes directly into the ctx.
pCert = pTemp;
else
// The remaining certificates (if any) are put into
// the list of untrusted certificates
sk_X509_push(untrustedCerts, pTemp);
tempLength += certLength + 3;
}
sslCertificateEvent(e, pCert);
X509_STORE_CTX_init(&csc, ctx, pCert, untrustedCerts);
X509_STORE_CTX_set_time(&csc, 0, (time_t) network_time);
int i = X509_verify_cert(&csc);
X509_STORE_CTX_cleanup(&csc);
//X509_STORE_CTX_free(&csc);
int ret = 0;
if ( ! i )
{
sslCertificateError(e, csc.error);
ret = csc.error;
}
else
ret = 0;
delete pCert;
// Free the stack, incuding. contents.
// FIXME: could this break Bro's memory tracking?
sk_X509_pop_free(untrustedCerts, X509_free);
return ret;
}

View file

@ -1,33 +0,0 @@
// $Id: X509.h 3526 2006-09-12 07:32:21Z vern $
#ifndef X509_H
#define X509_H
#include <sys/types.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include "SSLProxy.h"
class X509_Cert {
public:
static X509_STORE* ctx;
static X509_LOOKUP* lookup;
static X509_STORE_CTX csc;
static bool bInited;
// Initializes the OpenSSL library, which is used for verify().
static int init();
// Wrapper for X.509 error event.
static void sslCertificateError(Contents_SSL* e, int error_numbe);
// Retrieves a DER-encoded X.509 certificate. Returns 0 on failure.
static int verify(Contents_SSL* e, const u_char* data, uint32 len);
static int verifyChain(Contents_SSL* e, const u_char* data, uint32 len);
// Wrapper for the ssl_certificate event.
static void sslCertificateEvent(Contents_SSL* e, X509* pCert);
};
#endif

View file

@ -3348,3 +3348,112 @@ function bro_has_ipv6%(%) : bool
return new Val(0, TYPE_BOOL); return new Val(0, TYPE_BOOL);
#endif #endif
%} %}
%%{
#include <openssl/x509.h>
#include <openssl/asn1.h>
#include <openssl/x509_vfy.h>
// This is the indexed map of X509 certificate stores.
static map<BroString, X509_STORE*> x509_stores;
// ### NOTE: while d2i_X509 does not take a const u_char** pointer,
// here we assume d2i_X509 does not write to <data>, so it is safe to
// convert data to a non-const pointer. Could some X509 guru verify
// this?
X509* d2i_X509_(X509** px, const u_char** in, int len)
{
#ifdef OPENSSL_D2I_X509_USES_CONST_CHAR
return d2i_X509(px, in, len);
#else
return d2i_X509(px, (u_char**)in, len);
#endif
}
%%}
function x509_verify%(der_cert: string, cert_stack: string_vec, root_certs: table_string_of_string%): count
%{
X509_STORE* ctx = 0;
int i = 0;
// If this certificate store was built previously, just reuse the old one.
BroString* s = convert_index_to_string(root_certs);
if ( x509_stores.count(*s) > 0 )
ctx = x509_stores[*s];
if ( ! ctx ) // lookup to see if we have this one built already!
{
ctx = X509_STORE_new();
TableVal* root_certs2 = root_certs->AsTableVal();
ListVal* idxs = root_certs2->ConvertToPureList();
// Build the validation store
for ( i = 0; i < idxs->Length(); ++i )
{
Val* key = idxs->Index(i);
StringVal *sv = root_certs2->Lookup(key)->AsStringVal();
const uint8* data = sv->Bytes();
X509* x = d2i_X509_(NULL, &data, sv->Len());
if ( ! x )
{
builtin_run_time(fmt("Root CA error: %s", ERR_error_string(ERR_peek_last_error(),NULL)));
return new Val((uint64) ERR_get_error(), TYPE_COUNT);
}
X509_STORE_add_cert(ctx, x);
}
// Save the newly constructed certificate store into the cacheing map.
x509_stores[*s] = ctx;
}
STACK_OF(X509)* untrusted_certs = sk_X509_new_null();
if ( ! untrusted_certs )
{
builtin_run_time(fmt("Untrusted certificate stack initialization error: %s", ERR_error_string(ERR_peek_last_error(),NULL)));
return new Val((uint64) ERR_get_error(), TYPE_COUNT);
}
VectorVal *cert_stack_vec = cert_stack->AsVectorVal();
for ( i = 0; i < (int) cert_stack_vec->Size(); ++i )
{
StringVal *sv = cert_stack_vec->Lookup(i)->AsStringVal();
const uint8 *data = sv->Bytes();
X509* x = d2i_X509_(NULL, &data, sv->Len());
if ( ! x )
{
builtin_run_time(fmt("Untrusted certificate stack creation error: %s", ERR_error_string(ERR_peek_last_error(),NULL)));
return new Val((uint64) ERR_get_error(), TYPE_COUNT);
}
sk_X509_push(untrusted_certs, x);
}
const uint8 *cert_data = der_cert->Bytes();
X509_STORE_CTX csc;
X509* cert = d2i_X509_(NULL, &cert_data, der_cert->Len());
if ( ! cert )
{
builtin_run_time(fmt("Certificate error: %s", ERR_error_string(ERR_peek_last_error(),NULL)));
return new Val((uint64) ERR_get_error(), TYPE_COUNT);
}
X509_STORE_CTX_init(&csc, ctx, cert, untrusted_certs);
X509_STORE_CTX_set_time(&csc, 0, (time_t) network_time);
int result = X509_verify_cert(&csc);
X509_STORE_CTX_cleanup(&csc);
if ( untrusted_certs )
sk_X509_pop_free(untrusted_certs, X509_free);
return new Val((uint64) csc.error, TYPE_COUNT);
%}
function x509_err2str%(err_num: count%): string
%{
return new StringVal(X509_verify_cert_error_string(err_num));
%}

View file

@ -265,19 +265,15 @@ event http_stats%(c: connection, stats: http_stats_rec%);
event ssh_client_version%(c: connection, version: string%); event ssh_client_version%(c: connection, version: string%);
event ssh_server_version%(c: connection, version: string%); event ssh_server_version%(c: connection, version: string%);
event ssl_certificate_seen%(c: connection, is_server: bool%); event ssl_client_hello%(c: connection, version: count, possible_ts: time, session_id: string, ciphers: count_set%);
event ssl_certificate%(c: connection, cert: X509, is_server: bool%); event ssl_server_hello%(c: connection, version: count, possible_ts: time, session_id: string, cipher: count, comp_method: count%);
event ssl_conn_attempt%(c: connection, version: count, ciphers: cipher_suites_list%); event ssl_extension%(c: connection, code: count, val: string%);
event ssl_conn_server_reply%(c: connection, version: count, ciphers: cipher_suites_list%); event ssl_established%(c: connection%);
event ssl_conn_established%(c: connection, version: count, cipher_suite: count%); event ssl_alert%(c: connection, level: count, desc: count%);
event ssl_conn_reused%(c: connection, session_id: SSL_sessionID%);
event ssl_conn_alert%(c: connection, version: count, level: count,
description: count%);
event ssl_conn_weak%(name: string, c: connection%);
event ssl_session_insertion%(c: connection, id: SSL_sessionID%); event x509_certificate%(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string%);
event process_X509_extensions%(c: connection, ex: X509_extension%); event x509_extension%(c: connection, data: string%);
event ssl_X509_error%(c: connection, err: int, err_string: string%); event x509_error%(c: connection, err: count%);
event stp_create_endp%(c: connection, e: int, is_orig: bool%); event stp_create_endp%(c: connection, e: int, is_orig: bool%);
event stp_resume_endp%(e: int%); event stp_resume_endp%(e: int%);

View file

@ -1,5 +1,3 @@
# $Id:$
# Analyzer for SSL (Bro-specific part). # Analyzer for SSL (Bro-specific part).
%extern{ %extern{
@ -11,8 +9,7 @@
#include "util.h" #include "util.h"
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/x509_vfy.h> #include <openssl/asn1.h>
#include "X509.h"
%} %}
@ -46,22 +43,20 @@
%} %}
function to_table_val(data : uint8[]) : TableVal function to_string_val(data : uint8[]) : StringVal
%{ %{
TableVal* tv = new TableVal(SSL_sessionID); assert(data->size() <= 32);
for ( unsigned int i = 0; i < data->size(); i += 4 )
{
uint32 temp = 0;
for ( unsigned int j = 0; j < 4; ++j )
if ( i + j < data->size() )
temp |= (*data)[i + j] << (24 - 8 * j);
Val* idx = new Val(i / 4, TYPE_COUNT); char tmp[32];
tv->Assign(idx, new Val((*data)[i], TYPE_COUNT)); memset(tmp, 0, sizeof(tmp));
Unref(idx);
if ( data )
{
for ( unsigned int i = data->size(); i > 0; --i )
tmp[i-1] = (*data)[i-1];
} }
return tv; return new StringVal(32, tmp);
%} %}
function version_ok(vers : uint16) : bool function version_ok(vers : uint16) : bool
@ -101,23 +96,10 @@ function convert_ciphers_uint16(ciph : uint16[]) : int[]
refine analyzer SSLAnalyzer += { refine analyzer SSLAnalyzer += {
%member{ %member{
Analyzer* bro_analyzer_; Analyzer* bro_analyzer_;
vector<uint8>* client_session_id_;
vector<int>* advertised_ciphers_;
int version_;
int cipher_;
%} %}
%init{ %init{
bro_analyzer_ = 0; bro_analyzer_ = 0;
client_session_id_ = 0;
advertised_ciphers_ = new vector<int>;
version_ = -1;
cipher_ = -1;
if ( ! X509_Cert::bInited )
X509_Cert::init();
%} %}
%eof{ %eof{
@ -128,11 +110,6 @@ refine analyzer SSLAnalyzer += {
%} %}
%cleanup{ %cleanup{
delete client_session_id_;
client_session_id_ = 0;
delete advertised_ciphers_;
advertised_ciphers_ = 0;
%} %}
function bro_analyzer() : Analyzer function bro_analyzer() : Analyzer
@ -145,282 +122,203 @@ refine analyzer SSLAnalyzer += {
bro_analyzer_ = a; bro_analyzer_ = a;
%} %}
function check_cipher(cipher : int) : bool function proc_change_cipher_spec(rec: SSLRecord) : bool
%{
if ( ! ssl_compare_cipherspecs )
return true;
if ( std::find(advertised_ciphers_->begin(),
advertised_ciphers_->end(), cipher) ==
advertised_ciphers_->end() )
{
bro_analyzer()->ProtocolViolation("chosen cipher not advertised before");
return false;
}
return true;
%}
function certificate_error(err_num : int) : void
%{
StringVal* err_str =
new StringVal(X509_verify_cert_error_string(err_num));
BifEvent::generate_ssl_X509_error(bro_analyzer_, bro_analyzer_->Conn(),
err_num, err_str);
%}
function proc_change_cipher_spec(msg : ChangeCipherSpec) : bool
%{ %{
if ( state_ == STATE_TRACK_LOST ) if ( state_ == STATE_TRACK_LOST )
bro_analyzer()->ProtocolViolation(fmt("unexpected ChangeCipherSpec from %s at state %s", bro_analyzer()->ProtocolViolation(fmt("unexpected ChangeCipherSpec from %s at state %s",
orig_label(current_record_is_orig_).c_str(), orig_label(${rec.is_orig}).c_str(),
state_label(old_state_).c_str())); state_label(old_state_).c_str()));
return true; return true;
%} %}
function proc_application_data(msg : ApplicationData) : bool function proc_application_data(rec: SSLRecord) : bool
%{ %{
if ( state_ != STATE_CONN_ESTABLISHED ) if ( state_ != STATE_CONN_ESTABLISHED )
bro_analyzer()->ProtocolViolation(fmt("unexpected ApplicationData from %s at state %s", bro_analyzer()->ProtocolViolation(fmt("unexpected ApplicationData from %s at state %s",
orig_label(current_record_is_orig_).c_str(), orig_label(${rec.is_orig}).c_str(),
state_label(old_state_).c_str())); state_label(old_state_).c_str()));
return true; return true;
%} %}
function proc_alert(level : int, description : int) : bool function proc_alert(rec: SSLRecord, level : int, desc : int) : bool
%{ %{
BifEvent::generate_ssl_conn_alert(bro_analyzer_, bro_analyzer_->Conn(), BifEvent::generate_ssl_alert(bro_analyzer_, bro_analyzer_->Conn(),
current_record_version_, level, level, desc);
description);
return true; return true;
%} %}
function proc_client_hello(version : uint16, session_id : uint8[], function proc_client_hello(rec: SSLRecord,
csuits : int[]) : bool version : uint16, ts : double,
session_id : uint8[],
cipher_suites : int[]) : bool
%{ %{
if ( state_ == STATE_TRACK_LOST ) if ( state_ == STATE_TRACK_LOST )
bro_analyzer()->ProtocolViolation(fmt("unexpected client hello message from %s in state %s", bro_analyzer()->ProtocolViolation(fmt("unexpected client hello message from %s in state %s",
orig_label(current_record_is_orig_).c_str(), orig_label(${rec.is_orig}).c_str(),
state_label(old_state_).c_str())); state_label(old_state_).c_str()));
if ( ! version_ok(version) ) if ( ! version_ok(version) )
bro_analyzer()->ProtocolViolation(fmt("unsupported client SSL version 0x%04x", version)); bro_analyzer()->ProtocolViolation(fmt("unsupported client SSL version 0x%04x", version));
delete client_session_id_; if ( ssl_client_hello )
client_session_id_ = new vector<uint8>(*session_id);
TableVal* cipher_table = new TableVal(cipher_suites_list);
for ( unsigned int i = 0; i < csuits->size(); ++i )
{ {
Val* ciph = new Val((*csuits)[i], TYPE_COUNT); BroType* count_t = base_type(TYPE_COUNT);
cipher_table->Assign(ciph, 0); TypeList* set_index = new TypeList(count_t);
set_index->Append(count_t);
SetType* s = new SetType(set_index, 0);
TableVal* cipher_set = new TableVal(s);
for ( unsigned int i = 0; i < cipher_suites->size(); ++i )
{
Val* ciph = new Val((*cipher_suites)[i], TYPE_COUNT);
cipher_set->Assign(ciph, 0);
Unref(ciph); Unref(ciph);
} }
BifEvent::generate_ssl_conn_attempt(bro_analyzer_, bro_analyzer_->Conn(), BifEvent::generate_ssl_client_hello(bro_analyzer_, bro_analyzer_->Conn(),
version, cipher_table); version, ts,
to_string_val(session_id),
if ( ssl_compare_cipherspecs ) cipher_set);
{
delete advertised_ciphers_;
advertised_ciphers_ = csuits;
} }
else
delete csuits;
return true; return true;
%} %}
function proc_server_hello(version : uint16, session_id : uint8[], function proc_server_hello(rec: SSLRecord,
ciphers : int[], v2_sess_hit : int) : bool version : uint16, ts : double,
session_id : uint8[],
cipher_suite : uint16,
comp_method : uint8) : bool
%{ %{
if ( state_ == STATE_TRACK_LOST ) if ( state_ == STATE_TRACK_LOST )
bro_analyzer()->ProtocolViolation(fmt("unexpected server hello message from %s in state %s", bro_analyzer()->ProtocolViolation(fmt("unexpected server hello message from %s in state %s",
orig_label(current_record_is_orig_).c_str(), orig_label(${rec.is_orig}).c_str(),
state_label(old_state_).c_str())); state_label(old_state_).c_str()));
if ( ! version_ok(version) ) if ( ! version_ok(version) )
bro_analyzer()->ProtocolViolation(fmt("unsupported server SSL version 0x%04x", version)); bro_analyzer()->ProtocolViolation(fmt("unsupported server SSL version 0x%04x", version));
version_ = version; if ( ssl_server_hello )
TableVal* chosen_ciphers = new TableVal(cipher_suites_list);
for ( unsigned int i = 0; i < ciphers->size(); ++i )
{ {
Val* ciph = new Val((*ciphers)[i], TYPE_COUNT); BifEvent::generate_ssl_server_hello(bro_analyzer_,
chosen_ciphers->Assign(ciph, 0);
Unref(ciph);
}
BifEvent::generate_ssl_conn_server_reply(bro_analyzer_,
bro_analyzer_->Conn(), bro_analyzer_->Conn(),
version_, chosen_ciphers); version, ts,
to_string_val(session_id),
if ( v2_sess_hit < 0 ) cipher_suite, comp_method);
{ // this is SSLv3
cipher_ = (*ciphers)[0];
check_cipher(cipher_);
TableVal* tv = to_table_val(session_id);
if ( client_session_id_ &&
*client_session_id_ == *session_id )
BifEvent::generate_ssl_conn_reused(bro_analyzer_,
bro_analyzer_->Conn(), tv);
else
BifEvent::generate_ssl_session_insertion(bro_analyzer_,
bro_analyzer_->Conn(), tv);
delete ciphers;
}
else if ( v2_sess_hit > 0 )
{ // this is SSLv2 and a session hit
if ( client_session_id_ )
{
TableVal* tv = to_table_val(client_session_id_);
BifEvent::generate_ssl_conn_reused(bro_analyzer_,
bro_analyzer_->Conn(), tv);
}
// We don't know the chosen cipher, as there is
// no session storage.
BifEvent::generate_ssl_conn_established(bro_analyzer_,
bro_analyzer_->Conn(),
version_, 0xffffffff);
delete ciphers;
}
else
{
// This is SSLv2; we have to set advertised
// ciphers to server ciphers.
if ( ssl_compare_cipherspecs )
{
delete advertised_ciphers_;
advertised_ciphers_ = ciphers;
}
} }
bro_analyzer()->ProtocolConfirmation(); bro_analyzer()->ProtocolConfirmation();
return true; return true;
%} %}
function proc_certificate(certificates : bytestring[]) : bool function proc_ssl_extension(type: int, data: bytestring) : bool
%{
if ( ssl_extension )
BifEvent::generate_ssl_extension(bro_analyzer_,
bro_analyzer_->Conn(), type,
new StringVal(data.length(), (const char*) data.data()));
return true;
%}
function proc_certificate(rec: SSLRecord, certificates : bytestring[]) : bool
%{ %{
if ( state_ == STATE_TRACK_LOST ) if ( state_ == STATE_TRACK_LOST )
bro_analyzer()->ProtocolViolation(fmt("unexpected certificate message from %s in state %s", bro_analyzer()->ProtocolViolation(fmt("unexpected certificate message from %s in state %s",
orig_label(current_record_is_orig_).c_str(), orig_label(${rec.is_orig}).c_str(),
state_label(old_state_).c_str())); state_label(old_state_).c_str()));
if ( ! ssl_analyze_certificates )
return true;
if ( certificates->size() == 0 ) if ( certificates->size() == 0 )
return true; return true;
BifEvent::generate_ssl_certificate_seen(bro_analyzer_, STACK_OF(X509)* untrusted_certs = 0;
bro_analyzer_->Conn(),
! current_record_is_orig_);
const bytestring& cert = (*certificates)[0]; if ( x509_certificate )
const uint8* data = cert.data();
X509* pCert = d2i_X509_binpac(NULL, &data, cert.length());
if ( ! pCert )
{ {
// X509_V_UNABLE_TO_DECRYPT_CERT_SIGNATURE X509* pCert = 0;
certificate_error(4); for ( unsigned int i = 0; i < certificates->size(); ++i )
{
const bytestring& cert = (*certificates)[i];
const uint8* data = cert.data();
X509* pTemp = d2i_X509_binpac(NULL, &data, cert.length());
if ( ! pTemp )
{
BifEvent::generate_x509_error(bro_analyzer_, bro_analyzer_->Conn(),
ERR_get_error());
return false; return false;
} }
RecordVal* pX509Cert = new RecordVal(x509_type); RecordVal* pX509Cert = new RecordVal(x509_type);
char tmp[256]; char tmp[256];
X509_NAME_oneline(X509_get_issuer_name(pCert), tmp, sizeof tmp); BIO *bio = BIO_new(BIO_s_mem());
pX509Cert->Assign(0, new StringVal(tmp));
X509_NAME_oneline(X509_get_subject_name(pCert), tmp, sizeof tmp);
pX509Cert->Assign(1, new StringVal(tmp)); pX509Cert->Assign(0, new Val((uint64) X509_get_version(pTemp), TYPE_COUNT));
pX509Cert->Assign(2, new AddrVal(bro_analyzer_->Conn()->OrigAddr())); i2a_ASN1_INTEGER(bio, X509_get_serialNumber(pTemp));
int len = BIO_read(bio, &(*tmp), sizeof tmp);
pX509Cert->Assign(1, new StringVal(len, tmp));
BifEvent::generate_ssl_certificate(bro_analyzer_, bro_analyzer_->Conn(), X509_NAME_print_ex(bio, X509_get_subject_name(pTemp), 0, XN_FLAG_RFC2253);
pX509Cert, current_record_is_orig_); len = BIO_gets(bio, &(*tmp), sizeof tmp);
pX509Cert->Assign(2, new StringVal(len, tmp));
X509_NAME_print_ex(bio, X509_get_issuer_name(pTemp), 0, XN_FLAG_RFC2253);
len = BIO_gets(bio, &(*tmp), sizeof tmp);
pX509Cert->Assign(3, new StringVal(len, tmp));
BIO_free(bio);
if ( X509_get_ext_count(pCert) > 0 ) pX509Cert->Assign(4, new Val(get_time_from_asn1(X509_get_notBefore(pTemp)), TYPE_TIME));
pX509Cert->Assign(5, new Val(get_time_from_asn1(X509_get_notAfter(pTemp)), TYPE_TIME));
StringVal* der_cert = new StringVal(cert.length(), (const char*) cert.data());
BifEvent::generate_x509_certificate(bro_analyzer_, bro_analyzer_->Conn(),
pX509Cert,
! ${rec.is_orig},
i, certificates->size(),
der_cert);
// Are there any X509 extensions?
if ( x509_extension && X509_get_ext_count(pTemp) > 0 )
{ {
TableVal* x509ex = new TableVal(x509_extension); BroType* count_t = base_type(TYPE_COUNT);
TypeList* set_index = new TypeList(count_t);
for ( int k = 0; k < X509_get_ext_count(pCert); ++k ) set_index->Append(count_t);
SetType* s = new SetType(set_index, 0);
TableVal* x509ex = new TableVal(s);
int num_ext = X509_get_ext_count(pTemp);
for ( int k = 0; k < num_ext; ++k )
{ {
X509_EXTENSION* ex = X509_get_ext(pCert, k); char *pBuffer = 0;
ASN1_OBJECT* obj = X509_EXTENSION_get_object(ex); int length = 0;
char buf[256]; X509_EXTENSION* ex = X509_get_ext(pTemp, k);
i2t_ASN1_OBJECT(buf, sizeof(buf), obj); if (ex)
Val* index = new Val(k+1, TYPE_COUNT); {
Val* value = new StringVal(strlen(buf), buf); ASN1_STRING *pString = X509_EXTENSION_get_data(ex);
x509ex->Assign(index, value); length = ASN1_STRING_to_UTF8((unsigned char**)&pBuffer, pString);
Unref(index); //i2t_ASN1_OBJECT(&pBuffer, length, obj)
}
BifEvent::generate_process_X509_extensions(bro_analyzer_, // -1 indicates an error.
bro_analyzer_->Conn(), x509ex); if ( length < 0 )
} continue;
if ( ssl_verify_certificates ) StringVal* value = new StringVal(length, pBuffer);
{ BifEvent::generate_x509_extension(bro_analyzer_,
STACK_OF(X509)* untrusted_certs = 0; bro_analyzer_->Conn(), value);
if ( certificates->size() > 1 ) OPENSSL_free(pBuffer);
{ }
untrusted_certs = sk_X509_new_null(); }
if ( ! untrusted_certs )
{
// X509_V_ERR_OUT_OF_MEM;
certificate_error(17);
return false;
}
for ( unsigned int i = 1;
i < certificates->size(); ++i )
{
const bytestring& temp =
(*certificates)[i];
const uint8* tdata = temp.data();
X509* pTemp = d2i_X509_binpac(NULL,
&tdata, temp.length());
if ( ! pTemp )
{
// X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
certificate_error(2);
return false;
}
sk_X509_push(untrusted_certs, pTemp);
} }
} }
X509_STORE_CTX csc;
X509_STORE_CTX_init(&csc, X509_Cert::ctx,
pCert, untrusted_certs);
X509_STORE_CTX_set_time(&csc, 0, time_t(network_time()));
if (! X509_verify_cert(&csc))
certificate_error(csc.error);
X509_STORE_CTX_cleanup(&csc);
sk_X509_pop_free(untrusted_certs, X509_free);
} }
X509_free(pCert);
return true; return true;
%} %}
function proc_v2_certificate(cert : bytestring) : bool function proc_v2_certificate(rec: SSLRecord, cert : bytestring) : bool
%{ %{
vector<bytestring>* cert_list = new vector<bytestring>(1,cert); vector<bytestring>* cert_list = new vector<bytestring>(1,cert);
bool ret = proc_certificate(cert_list); bool ret = proc_certificate(rec, cert_list);
delete cert_list; delete cert_list;
return ret; return ret;
%} %}
function proc_v3_certificate(cl : CertificateList) : bool function proc_v3_certificate(rec: SSLRecord, cl : CertificateList) : bool
%{ %{
vector<X509Certificate*>* certs = cl->val(); vector<X509Certificate*>* certs = cl->val();
vector<bytestring>* cert_list = new vector<bytestring>(); vector<bytestring>* cert_list = new vector<bytestring>();
@ -428,65 +326,61 @@ refine analyzer SSLAnalyzer += {
std::transform(certs->begin(), certs->end(), std::transform(certs->begin(), certs->end(),
std::back_inserter(*cert_list), extract_certs()); std::back_inserter(*cert_list), extract_certs());
bool ret = proc_certificate(cert_list); bool ret = proc_certificate(rec, cert_list);
delete cert_list; delete cert_list;
return ret; return ret;
%} %}
function proc_v2_client_master_key(cipher : int) : bool function proc_v2_client_master_key(rec: SSLRecord, cipher_kind: int) : bool
%{ %{
if ( state_ == STATE_TRACK_LOST ) if ( state_ == STATE_TRACK_LOST )
bro_analyzer()->ProtocolViolation(fmt("unexpected v2 client master key message from %s in state %s", bro_analyzer()->ProtocolViolation(fmt("unexpected v2 client master key message from %s in state %s",
orig_label(current_record_is_orig_).c_str(), orig_label(${rec.is_orig}).c_str(),
state_label(old_state_).c_str())); state_label(old_state_).c_str()));
check_cipher(cipher); BifEvent::generate_ssl_established(bro_analyzer_,
BifEvent::generate_ssl_conn_established(bro_analyzer_, bro_analyzer_->Conn());
bro_analyzer_->Conn(), version_, cipher);
return true; return true;
%} %}
function proc_unknown_handshake(msg_type : int) : bool function proc_unknown_handshake(hs: Handshake, is_orig: bool) : bool
%{ %{
bro_analyzer()->ProtocolViolation(fmt("unknown handshake message (%d) from %s", bro_analyzer()->ProtocolViolation(fmt("unknown handshake message (%d) from %s",
msg_type, orig_label(current_record_is_orig_).c_str())); ${hs.msg_type}, orig_label(is_orig).c_str()));
return true; return true;
%} %}
function proc_handshake(msg : Handshake) : bool function proc_handshake(hs: Handshake, is_orig: bool) : bool
%{ %{
if ( state_ == STATE_TRACK_LOST ) if ( state_ == STATE_TRACK_LOST )
bro_analyzer()->ProtocolViolation(fmt("unexpected Handshake message %s from %s in state %s", bro_analyzer()->ProtocolViolation(fmt("unexpected Handshake message %s from %s in state %s",
handshake_type_label(msg->msg_type()).c_str(), handshake_type_label(${hs.msg_type}).c_str(),
orig_label(current_record_is_orig_).c_str(), orig_label(is_orig).c_str(),
state_label(old_state_).c_str())); state_label(old_state_).c_str()));
return true; return true;
%} %}
function proc_unknown_record(msg : UnknownRecord) : bool function proc_unknown_record(rec: SSLRecord) : bool
%{ %{
bro_analyzer()->ProtocolViolation(fmt("unknown SSL record type (%d) from %s", bro_analyzer()->ProtocolViolation(fmt("unknown SSL record type (%d) from %s",
current_record_type_, ${rec.content_type},
orig_label(current_record_is_orig_).c_str())); orig_label(${rec.is_orig}).c_str()));
return true; return true;
%} %}
function proc_ciphertext_record(msg : CiphertextRecord) : bool function proc_ciphertext_record(rec : SSLRecord) : bool
%{ %{
if ( state_ == STATE_TRACK_LOST ) if ( state_ == STATE_TRACK_LOST )
bro_analyzer()->ProtocolViolation(fmt("unexpected ciphertext record from %s in state %s", bro_analyzer()->ProtocolViolation(fmt("unexpected ciphertext record from %s in state %s",
orig_label(current_record_is_orig_).c_str(), orig_label(${rec.is_orig}).c_str(),
state_label(old_state_).c_str())); state_label(old_state_).c_str()));
if ( state_ == STATE_CONN_ESTABLISHED && else if ( state_ == STATE_CONN_ESTABLISHED &&
old_state_ == STATE_COMM_ENCRYPTED ) old_state_ == STATE_COMM_ENCRYPTED )
{ BifEvent::generate_ssl_established(bro_analyzer_,
BifEvent::generate_ssl_conn_established(bro_analyzer_, bro_analyzer_->Conn());
bro_analyzer_->Conn(),
version_, cipher_);
}
return true; return true;
%} %}
@ -494,72 +388,80 @@ refine analyzer SSLAnalyzer += {
}; };
refine typeattr ChangeCipherSpec += &let { refine typeattr ChangeCipherSpec += &let {
proc : bool = $context.analyzer.proc_change_cipher_spec(this) proc : bool = $context.analyzer.proc_change_cipher_spec(rec)
&requires(state_changed); &requires(state_changed);
}; };
refine typeattr Alert += &let { refine typeattr Alert += &let {
proc : bool = $context.analyzer.proc_alert(level, description); proc : bool = $context.analyzer.proc_alert(rec, level, description);
}; };
refine typeattr V2Error += &let { refine typeattr V2Error += &let {
proc : bool = $context.analyzer.proc_alert(-1, error_code); proc : bool = $context.analyzer.proc_alert(rec, -1, error_code);
}; };
refine typeattr ApplicationData += &let { refine typeattr ApplicationData += &let {
proc : bool = $context.analyzer.proc_application_data(this); proc : bool = $context.analyzer.proc_application_data(rec);
}; };
refine typeattr ClientHello += &let { refine typeattr ClientHello += &let {
proc : bool = $context.analyzer.proc_client_hello(client_version, proc : bool = $context.analyzer.proc_client_hello(rec, client_version,
gmt_unix_time,
session_id, convert_ciphers_uint16(csuits)) session_id, convert_ciphers_uint16(csuits))
&requires(state_changed); &requires(state_changed);
}; };
refine typeattr V2ClientHello += &let { refine typeattr V2ClientHello += &let {
proc : bool = $context.analyzer.proc_client_hello(client_version, proc : bool = $context.analyzer.proc_client_hello(rec, client_version, 0,
session_id, convert_ciphers_uint24(ciphers)) session_id, convert_ciphers_uint24(ciphers))
&requires(state_changed); &requires(state_changed);
}; };
refine typeattr ServerHello += &let { refine typeattr ServerHello += &let {
proc : bool = $context.analyzer.proc_server_hello(server_version, proc : bool = $context.analyzer.proc_server_hello(rec, server_version,
session_id, convert_ciphers_uint16(cipher_suite), -1) gmt_unix_time, session_id, cipher_suite,
compression_method)
&requires(state_changed); &requires(state_changed);
}; };
refine typeattr V2ServerHello += &let { refine typeattr V2ServerHello += &let {
proc : bool = $context.analyzer.proc_server_hello(server_version, 0, proc : bool = $context.analyzer.proc_server_hello(rec, server_version, 0, 0,
convert_ciphers_uint24(ciphers), session_id_hit) convert_ciphers_uint24(ciphers)[0], 0)
&requires(state_changed); &requires(state_changed);
cert : bool = $context.analyzer.proc_v2_certificate(cert_data) cert : bool = $context.analyzer.proc_v2_certificate(rec, cert_data)
&requires(proc); &requires(proc);
}; };
refine typeattr Certificate += &let { refine typeattr Certificate += &let {
proc : bool = $context.analyzer.proc_v3_certificate(certificates) proc : bool = $context.analyzer.proc_v3_certificate(rec, certificates)
&requires(state_changed); &requires(state_changed);
}; };
refine typeattr V2ClientMasterKey += &let { refine typeattr V2ClientMasterKey += &let {
proc : bool = $context.analyzer.proc_v2_client_master_key(to_int()(cipher_kind)) proc : bool = $context.analyzer.proc_v2_client_master_key(rec, to_int()(cipher_kind))
&requires(state_changed); &requires(state_changed);
}; };
refine typeattr UnknownHandshake += &let { refine typeattr UnknownHandshake += &let {
proc : bool = $context.analyzer.proc_unknown_handshake(msg_type); proc : bool = $context.analyzer.proc_unknown_handshake(hs, is_orig);
}; };
refine typeattr Handshake += &let { refine typeattr Handshake += &let {
proc : bool = $context.analyzer.proc_handshake(this); proc : bool = $context.analyzer.proc_handshake(this, rec.is_orig);
}; };
refine typeattr UnknownRecord += &let { refine typeattr UnknownRecord += &let {
proc : bool = $context.analyzer.proc_unknown_record(this); proc : bool = $context.analyzer.proc_unknown_record(rec);
}; };
refine typeattr CiphertextRecord += &let { refine typeattr CiphertextRecord += &let {
proc : bool = $context.analyzer.proc_ciphertext_record(this) proc : bool = $context.analyzer.proc_ciphertext_record(rec);
&requires(state_changed); }
refine typeattr SSLExtension += &let {
proc : bool = $context.analyzer.proc_ssl_extension(type, data);
}; };

View file

@ -1,5 +1,3 @@
# $Id:$
# Analyzer for SSL messages (general part). # Analyzer for SSL messages (general part).
# To be used in conjunction with an SSL record-layer analyzer. # To be used in conjunction with an SSL record-layer analyzer.
# Separation is necessary due to possible fragmentation of SSL records. # Separation is necessary due to possible fragmentation of SSL records.
@ -26,6 +24,57 @@ type uint24 = record {
extern type to_int; extern type to_int;
type SSLRecord(is_orig: bool) = record {
head0 : uint8;
head1 : uint8;
head2 : uint8;
head3 : uint8;
head4 : uint8;
rec : RecordText(this, is_orig) &requires(content_type), &restofdata;
} &length = length+5, &byteorder=bigendian,
&let {
version : int =
$context.analyzer.determine_ssl_version(head0, head1, head2);
content_type : int = case version of {
UNKNOWN_VERSION -> 0;
SSLv20 -> head2+300;
default -> head0;
};
length : int = case version of {
UNKNOWN_VERSION -> 0;
SSLv20 -> (((head0 & 0x7f) << 8) | head1) - 3;
default -> (head3 << 8) | head4;
};
};
type RecordText(rec: SSLRecord, is_orig: bool) = case $context.analyzer.state() of {
STATE_ABBREV_SERVER_ENCRYPTED, STATE_CLIENT_ENCRYPTED,
STATE_COMM_ENCRYPTED, STATE_CONN_ESTABLISHED
-> ciphertext : CiphertextRecord(rec, is_orig);
default
-> plaintext : PlaintextRecord(rec, is_orig);
};
type PlaintextRecord(rec: SSLRecord, is_orig: bool) = case rec.content_type of {
CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec(rec);
ALERT -> alert : Alert(rec);
HANDSHAKE -> handshake : Handshake(rec)[];
APPLICATION_DATA -> app_data : ApplicationData(rec);
V2_ERROR -> v2_error : V2Error(rec);
V2_CLIENT_HELLO -> v2_client_hello : V2ClientHello(rec);
V2_CLIENT_MASTER_KEY -> v2_client_master_key : V2ClientMasterKey(rec);
V2_SERVER_HELLO -> v2_server_hello : V2ServerHello(rec);
default -> unknown_record : UnknownRecord(rec);
};
type SSLExtension = record {
type: uint16;
data_len: uint16;
data: bytestring &length=data_len;
};
###################################################################### ######################################################################
# state management according to Section 7.3. in spec # state management according to Section 7.3. in spec
###################################################################### ######################################################################
@ -99,6 +148,104 @@ enum AnalyzerState {
{ {
return string(is_orig ? "originator" :"responder"); return string(is_orig ? "originator" :"responder");
} }
double get_time_from_asn1(const ASN1_TIME * atime)
{
time_t lResult = 0;
char lBuffer[24];
char * pBuffer = lBuffer;
size_t lTimeLength = atime->length;
char * pString = (char *) atime->data;
if ( atime->type == V_ASN1_UTCTIME )
{
if ( lTimeLength < 11 || lTimeLength > 17 )
return 0;
memcpy(pBuffer, pString, 10);
pBuffer += 10;
pString += 10;
}
else
{
if ( lTimeLength < 13 )
return 0;
memcpy(pBuffer, pString, 12);
pBuffer += 12;
pString += 12;
}
if ((*pString == 'Z') || (*pString == '-') || (*pString == '+'))
{
*(pBuffer++) = '0';
*(pBuffer++) = '0';
}
else
{
*(pBuffer++) = *(pString++);
*(pBuffer++) = *(pString++);
// Skip any fractional seconds...
if (*pString == '.')
{
pString++;
while ((*pString >= '0') && (*pString <= '9'))
pString++;
}
}
*(pBuffer++) = 'Z';
*(pBuffer++) = '\0';
time_t lSecondsFromUTC;
if ( *pString == 'Z' )
lSecondsFromUTC = 0;
else
{
if ((*pString != '+') && (pString[5] != '-'))
return 0;
lSecondsFromUTC = ((pString[1]-'0') * 10 + (pString[2]-'0')) * 60;
lSecondsFromUTC += (pString[3]-'0') * 10 + (pString[4]-'0');
if (*pString == '-')
lSecondsFromUTC = -lSecondsFromUTC;
}
tm lTime;
lTime.tm_sec = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0');
lTime.tm_min = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0');
lTime.tm_hour = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0');
lTime.tm_mday = ((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0');
lTime.tm_mon = (((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0')) - 1;
lTime.tm_year = ((lBuffer[0] - '0') * 10) + (lBuffer[1] - '0');
if ( lTime.tm_year < 50 )
lTime.tm_year += 100; // RFC 2459
lTime.tm_wday = 0;
lTime.tm_yday = 0;
lTime.tm_isdst = 0; // No DST adjustment requested
lResult = mktime(&lTime);
if ( lResult )
{
if ( 0 != lTime.tm_isdst )
lResult -= 3600; // mktime may adjust for DST (OS dependent)
lResult += lSecondsFromUTC;
}
else
lResult = 0;
return lResult;
}
%} %}
###################################################################### ######################################################################
@ -115,7 +262,9 @@ enum HandshakeType {
SERVER_HELLO_DONE = 14, SERVER_HELLO_DONE = 14,
CERTIFICATE_VERIFY = 15, CERTIFICATE_VERIFY = 15,
CLIENT_KEY_EXCHANGE = 16, CLIENT_KEY_EXCHANGE = 16,
FINISHED = 20 FINISHED = 20,
CERTIFICATE_URL = 21, # RFC 3546
CERTIFICATE_STATUS = 22, # RFC 3546
}; };
%code{ %code{
@ -132,6 +281,8 @@ enum HandshakeType {
case CERTIFICATE_VERIFY: return string("CERTIFICATE_VERIFY"); case CERTIFICATE_VERIFY: return string("CERTIFICATE_VERIFY");
case CLIENT_KEY_EXCHANGE: return string("CLIENT_KEY_EXCHANGE"); case CLIENT_KEY_EXCHANGE: return string("CLIENT_KEY_EXCHANGE");
case FINISHED: return string("FINISHED"); case FINISHED: return string("FINISHED");
case CERTIFICATE_URL: return string("CERTIFICATE_URL");
case CERTIFICATE_STATUS: return string("CERTIFICATE_STATUS");
default: return string(fmt("UNKNOWN (%d)", type)); default: return string(fmt("UNKNOWN (%d)", type));
} }
} }
@ -142,22 +293,24 @@ enum HandshakeType {
# V3 Change Cipher Spec Protocol (7.1.) # V3 Change Cipher Spec Protocol (7.1.)
###################################################################### ######################################################################
type ChangeCipherSpec = record { type ChangeCipherSpec(rec: SSLRecord) = record {
type : uint8; type : uint8;
} &length = 1, &let { } &length = 1, &let {
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_CLIENT_FINISHED, $context.analyzer.transition(STATE_CLIENT_FINISHED,
STATE_COMM_ENCRYPTED, false) || STATE_COMM_ENCRYPTED, rec.is_orig, false) ||
$context.analyzer.transition(STATE_IN_SERVER_HELLO, $context.analyzer.transition(STATE_IN_SERVER_HELLO,
STATE_ABBREV_SERVER_ENCRYPTED, false) || STATE_ABBREV_SERVER_ENCRYPTED, rec.is_orig, false) ||
$context.analyzer.transition(STATE_CLIENT_KEY_NO_CERT, $context.analyzer.transition(STATE_CLIENT_KEY_NO_CERT,
STATE_CLIENT_ENCRYPTED, true) || STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
$context.analyzer.transition(STATE_CLIENT_CERT_VERIFIED, $context.analyzer.transition(STATE_CLIENT_CERT_VERIFIED,
STATE_CLIENT_ENCRYPTED, true) || STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
#$context.analyzer.transition(STATE_CLIENT_CERT,
# STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
$context.analyzer.transition(STATE_CLIENT_KEY_WITH_CERT, $context.analyzer.transition(STATE_CLIENT_KEY_WITH_CERT,
STATE_CLIENT_ENCRYPTED, true) || STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
$context.analyzer.transition(STATE_ABBREV_SERVER_FINISHED, $context.analyzer.transition(STATE_ABBREV_SERVER_FINISHED,
STATE_COMM_ENCRYPTED, true) || STATE_COMM_ENCRYPTED, rec.is_orig, true) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -166,19 +319,19 @@ type ChangeCipherSpec = record {
# V3 Alert Protocol (7.2.) # V3 Alert Protocol (7.2.)
###################################################################### ######################################################################
type Alert = record { type Alert(rec: SSLRecord) = record {
level : uint8; level : uint8;
description: uint8; description: uint8;
} &length = 2; };
###################################################################### ######################################################################
# V2 Error Records (SSLv2 2.7.) # V2 Error Records (SSLv2 2.7.)
###################################################################### ######################################################################
type V2Error = record { type V2Error(rec: SSLRecord) = record {
error_code : uint16; error_code : uint16;
} &length = 2; };
###################################################################### ######################################################################
@ -187,9 +340,7 @@ type V2Error = record {
# Application data should always be encrypted, so we should not # Application data should always be encrypted, so we should not
# reach this point. # reach this point.
type ApplicationData = empty &let { type ApplicationData(rec: SSLRecord) = empty;
discard: bool = $context.flow.discard_data();
};
###################################################################### ######################################################################
# Handshake Protocol (7.4.) # Handshake Protocol (7.4.)
@ -200,7 +351,7 @@ type ApplicationData = empty &let {
###################################################################### ######################################################################
# Hello Request is empty # Hello Request is empty
type HelloRequest = empty &let { type HelloRequest(rec: SSLRecord) = empty &let {
hr: bool = $context.analyzer.set_hello_requested(true); hr: bool = $context.analyzer.set_hello_requested(true);
}; };
@ -209,7 +360,7 @@ type HelloRequest = empty &let {
# V3 Client Hello (7.4.1.2.) # V3 Client Hello (7.4.1.2.)
###################################################################### ######################################################################
type ClientHello = record { type ClientHello(rec: SSLRecord) = record {
client_version : uint16; client_version : uint16;
gmt_unix_time : uint32; gmt_unix_time : uint32;
random_bytes : bytestring &length = 28 &transient; random_bytes : bytestring &length = 28 &transient;
@ -219,12 +370,16 @@ type ClientHello = record {
csuits : uint16[csuit_len/2]; csuits : uint16[csuit_len/2];
cmeth_len : uint8 &check(cmeth_len > 0); cmeth_len : uint8 &check(cmeth_len > 0);
cmeths : uint8[cmeth_len]; cmeths : uint8[cmeth_len];
# This weirdness is to deal with the possible existence or absence
# of the following fields.
ext_len: uint16[] &until($element == 0 || $element != 0);
extensions : SSLExtension[] &until($input.length() == 0);
} &let { } &let {
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_INITIAL, $context.analyzer.transition(STATE_INITIAL,
STATE_CLIENT_HELLO_RCVD, true) || STATE_CLIENT_HELLO_RCVD, rec.is_orig, true) ||
($context.analyzer.hello_requested() && ($context.analyzer.hello_requested() &&
$context.analyzer.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, true)) || $context.analyzer.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, rec.is_orig, true)) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -233,7 +388,7 @@ type ClientHello = record {
# V2 Client Hello (SSLv2 2.5.) # V2 Client Hello (SSLv2 2.5.)
###################################################################### ######################################################################
type V2ClientHello = record { type V2ClientHello(rec: SSLRecord) = record {
client_version : uint16; client_version : uint16;
csuit_len : uint16; csuit_len : uint16;
session_len : uint16; session_len : uint16;
@ -244,9 +399,9 @@ type V2ClientHello = record {
} &length = 8 + csuit_len + session_len + chal_len, &let { } &length = 8 + csuit_len + session_len + chal_len, &let {
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_INITIAL, $context.analyzer.transition(STATE_INITIAL,
STATE_CLIENT_HELLO_RCVD, true) || STATE_CLIENT_HELLO_RCVD, rec.is_orig, true) ||
($context.analyzer.hello_requested() && ($context.analyzer.hello_requested() &&
$context.analyzer.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, true)) || $context.analyzer.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, rec.is_orig, true)) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -255,18 +410,18 @@ type V2ClientHello = record {
# V3 Server Hello (7.4.1.3.) # V3 Server Hello (7.4.1.3.)
###################################################################### ######################################################################
type ServerHello = record { type ServerHello(rec: SSLRecord) = record {
server_version : uint16; server_version : uint16;
gmt_unix_time : uint32; gmt_unix_time : uint32;
random_bytes : bytestring &length = 28 &transient; random_bytes : bytestring &length = 28 &transient;
session_len : uint8; session_len : uint8;
session_id : uint8[session_len]; session_id : uint8[session_len];
cipher_suite : uint16[1]; cipher_suite : uint16;
compression_method : uint8; compression_method : uint8;
} &let { } &let {
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_CLIENT_HELLO_RCVD, $context.analyzer.transition(STATE_CLIENT_HELLO_RCVD,
STATE_IN_SERVER_HELLO, false) || STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -275,7 +430,7 @@ type ServerHello = record {
# V2 Server Hello (SSLv2 2.6.) # V2 Server Hello (SSLv2 2.6.)
###################################################################### ######################################################################
type V2ServerHello = record { type V2ServerHello(rec: SSLRecord) = record {
session_id_hit : uint8; session_id_hit : uint8;
cert_type : uint8; cert_type : uint8;
server_version : uint16; server_version : uint16;
@ -289,9 +444,9 @@ type V2ServerHello = record {
state_changed : bool = state_changed : bool =
(session_id_hit > 0 ? (session_id_hit > 0 ?
$context.analyzer.transition(STATE_CLIENT_HELLO_RCVD, $context.analyzer.transition(STATE_CLIENT_HELLO_RCVD,
STATE_CONN_ESTABLISHED, false) : STATE_CONN_ESTABLISHED, rec.is_orig, false) :
$context.analyzer.transition(STATE_CLIENT_HELLO_RCVD, $context.analyzer.transition(STATE_CLIENT_HELLO_RCVD,
STATE_V2_CL_MASTER_KEY_EXPECTED, false)) || STATE_V2_CL_MASTER_KEY_EXPECTED, rec.is_orig, false)) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -307,15 +462,15 @@ type X509Certificate = record {
type CertificateList = X509Certificate[] &until($input.length() == 0); type CertificateList = X509Certificate[] &until($input.length() == 0);
type Certificate = record { type Certificate(rec: SSLRecord) = record {
length : uint24; length : uint24;
certificates : CertificateList &length = to_int()(length); certificates : CertificateList &length = to_int()(length);
} &let { } &let {
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_IN_SERVER_HELLO, $context.analyzer.transition(STATE_IN_SERVER_HELLO,
STATE_IN_SERVER_HELLO, false) || STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
$context.analyzer.transition(STATE_SERVER_HELLO_DONE, $context.analyzer.transition(STATE_SERVER_HELLO_DONE,
STATE_CLIENT_CERT, true) || STATE_CLIENT_CERT, rec.is_orig, true) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -325,12 +480,12 @@ type Certificate = record {
###################################################################### ######################################################################
# For now ignore details; just eat up complete message # For now ignore details; just eat up complete message
type ServerKeyExchange = record { type ServerKeyExchange(rec: SSLRecord) = record {
cont : bytestring &restofdata &transient; key : bytestring &restofdata;
} &let { } &let {
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_IN_SERVER_HELLO, $context.analyzer.transition(STATE_IN_SERVER_HELLO,
STATE_IN_SERVER_HELLO, false) || STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -340,12 +495,12 @@ type ServerKeyExchange = record {
###################################################################### ######################################################################
# For now, ignore Certificate Request Details; just eat up message. # For now, ignore Certificate Request Details; just eat up message.
type CertificateRequest = record { type CertificateRequest(rec: SSLRecord) = record {
cont : bytestring &restofdata &transient; cont : bytestring &restofdata &transient;
} &let { } &let {
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_IN_SERVER_HELLO, $context.analyzer.transition(STATE_IN_SERVER_HELLO,
STATE_IN_SERVER_HELLO, false) || STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -355,10 +510,10 @@ type CertificateRequest = record {
###################################################################### ######################################################################
# Server Hello Done is empty # Server Hello Done is empty
type ServerHelloDone = empty &let { type ServerHelloDone(rec: SSLRecord) = empty &let {
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_IN_SERVER_HELLO, $context.analyzer.transition(STATE_IN_SERVER_HELLO,
STATE_SERVER_HELLO_DONE, false) || STATE_SERVER_HELLO_DONE, rec.is_orig, false) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -377,14 +532,16 @@ type ServerHelloDone = empty &let {
# For now ignore details of ClientKeyExchange (most of it is # For now ignore details of ClientKeyExchange (most of it is
# encrypted anyway); just eat up message. # encrypted anyway); just eat up message.
type ClientKeyExchange = record { type ClientKeyExchange(rec: SSLRecord) = record {
cont : bytestring &restofdata &transient; cont : bytestring &restofdata &transient;
} &let { } &let {
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_SERVER_HELLO_DONE, $context.analyzer.transition(STATE_SERVER_HELLO_DONE,
STATE_CLIENT_KEY_NO_CERT, true) || STATE_CLIENT_KEY_NO_CERT, rec.is_orig, true) ||
$context.analyzer.transition(STATE_CLIENT_CERT, $context.analyzer.transition(STATE_CLIENT_CERT,
STATE_CLIENT_KEY_WITH_CERT, true) || STATE_CLIENT_KEY_WITH_CERT, rec.is_orig, true) ||
$context.analyzer.transition(STATE_CLIENT_CERT,
STATE_CLIENT_KEY_WITH_CERT, rec.is_orig, true) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -392,7 +549,7 @@ type ClientKeyExchange = record {
# V2 Client Master Key (SSLv2 2.5.) # V2 Client Master Key (SSLv2 2.5.)
###################################################################### ######################################################################
type V2ClientMasterKey = record { type V2ClientMasterKey(rec: SSLRecord) = record {
cipher_kind : uint24; cipher_kind : uint24;
cl_key_len : uint16; cl_key_len : uint16;
en_key_len : uint16; en_key_len : uint16;
@ -403,7 +560,7 @@ type V2ClientMasterKey = record {
} &length = 9 + cl_key_len + en_key_len + key_arg_len, &let { } &length = 9 + cl_key_len + en_key_len + key_arg_len, &let {
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_V2_CL_MASTER_KEY_EXPECTED, $context.analyzer.transition(STATE_V2_CL_MASTER_KEY_EXPECTED,
STATE_CONN_ESTABLISHED, true) || STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -413,12 +570,12 @@ type V2ClientMasterKey = record {
###################################################################### ######################################################################
# For now, ignore Certificate Verify; just eat up the message. # For now, ignore Certificate Verify; just eat up the message.
type CertificateVerify = record { type CertificateVerify(rec: SSLRecord) = record {
cont : bytestring &restofdata &transient; cont : bytestring &restofdata &transient;
} &let { } &let {
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_CLIENT_KEY_WITH_CERT, $context.analyzer.transition(STATE_CLIENT_KEY_WITH_CERT,
STATE_CLIENT_CERT_VERIFIED, true) || STATE_CLIENT_CERT_VERIFIED, rec.is_orig, true) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -427,35 +584,40 @@ type CertificateVerify = record {
# V3 Finished (7.4.9.) # V3 Finished (7.4.9.)
###################################################################### ######################################################################
# The Finished messages are always sent after encryption is in effect, # The finished messages are always sent after encryption is in effect,
# so we will not be able to read those message # so we will not be able to read those message.
###################################################################### ######################################################################
# V3 Handshake Protocol (7.) # V3 Handshake Protocol (7.)
###################################################################### ######################################################################
type UnknownHandshake(msg_type : uint8) = record { type UnknownHandshake(hs: Handshake, is_orig: bool) = record {
cont : bytestring &restofdata &transient; cont : bytestring &restofdata &transient;
} &let { } &let {
state_changed : bool = $context.analyzer.lost_track(); # TODO: an unknown handshake could just be an encrypted handshake
# before a server sends the change cipher spec message.
# I have no clue why this happens, but it does seem to happen.
# This should be solved in a different way eventually.
#state_changed : bool = $context.analyzer.lost_track();
}; };
type Handshake = record {
type Handshake(rec: SSLRecord) = record {
msg_type : uint8; msg_type : uint8;
length : uint24; length : uint24;
body : case msg_type of { body : case msg_type of {
HELLO_REQUEST -> hello_request : HelloRequest; HELLO_REQUEST -> hello_request : HelloRequest(rec);
CLIENT_HELLO -> client_hello : ClientHello; CLIENT_HELLO -> client_hello : ClientHello(rec);
SERVER_HELLO -> server_hello : ServerHello; SERVER_HELLO -> server_hello : ServerHello(rec);
CERTIFICATE -> certificate : Certificate; CERTIFICATE -> certificate : Certificate(rec);
SERVER_KEY_EXCHANGE -> server_key_exchange : ServerKeyExchange; SERVER_KEY_EXCHANGE -> server_key_exchange : ServerKeyExchange(rec);
CERTIFICATE_REQUEST -> certificate_request : CertificateRequest; CERTIFICATE_REQUEST -> certificate_request : CertificateRequest(rec);
SERVER_HELLO_DONE -> server_hello_done : ServerHelloDone; SERVER_HELLO_DONE -> server_hello_done : ServerHelloDone(rec);
CERTIFICATE_VERIFY -> certificate_verify : CertificateVerify; CERTIFICATE_VERIFY -> certificate_verify : CertificateVerify(rec);
CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange; CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange(rec);
default -> unknown_handshake : UnknownHandshake(msg_type); default -> unknown_handshake : UnknownHandshake(this, rec.is_orig);
}; };
} &length = 4 + to_int()(length); } &length = 4 + to_int()(length);
@ -464,40 +626,26 @@ type Handshake = record {
# Fragmentation (6.2.1.) # Fragmentation (6.2.1.)
###################################################################### ######################################################################
type UnknownRecord = record { type UnknownRecord(rec: SSLRecord) = record {
cont : empty; cont : bytestring &restofdata &transient;
} &let { } &let {
discard : bool = $context.flow.discard_data();
state_changed : bool = $context.analyzer.lost_track(); state_changed : bool = $context.analyzer.lost_track();
}; };
type PlaintextRecord = case $context.analyzer.current_record_type() of { type CiphertextRecord(rec: SSLRecord, is_orig: bool) = empty &let {
CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec;
ALERT -> alert : Alert;
HANDSHAKE -> handshakes : Handshake;
APPLICATION_DATA -> app_data : ApplicationData;
V2_ERROR -> v2_error : V2Error;
V2_CLIENT_HELLO -> v2_client_hello : V2ClientHello;
V2_CLIENT_MASTER_KEY -> v2_client_master_key : V2ClientMasterKey;
V2_SERVER_HELLO -> v2_server_hello : V2ServerHello;
UNKNOWN_OR_V2_ENCRYPTED -> unknown_record : UnknownRecord;
};
type CiphertextRecord = empty &let {
discard : bool = $context.flow.discard_data();
state_changed : bool = state_changed : bool =
$context.analyzer.transition(STATE_ABBREV_SERVER_ENCRYPTED, $context.analyzer.transition(STATE_ABBREV_SERVER_ENCRYPTED,
STATE_ABBREV_SERVER_FINISHED, false) || STATE_ABBREV_SERVER_FINISHED, rec.is_orig, false) ||
$context.analyzer.transition(STATE_CLIENT_ENCRYPTED, $context.analyzer.transition(STATE_CLIENT_ENCRYPTED,
STATE_CLIENT_FINISHED, true) || STATE_CLIENT_FINISHED, rec.is_orig, true) ||
$context.analyzer.transition(STATE_COMM_ENCRYPTED, $context.analyzer.transition(STATE_COMM_ENCRYPTED,
STATE_CONN_ESTABLISHED, false) || STATE_CONN_ESTABLISHED, rec.is_orig, false) ||
$context.analyzer.transition(STATE_COMM_ENCRYPTED, $context.analyzer.transition(STATE_COMM_ENCRYPTED,
STATE_CONN_ESTABLISHED, true) || STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
$context.analyzer.transition(STATE_CONN_ESTABLISHED, $context.analyzer.transition(STATE_CONN_ESTABLISHED,
STATE_CONN_ESTABLISHED, false) || STATE_CONN_ESTABLISHED, rec.is_orig, false) ||
$context.analyzer.transition(STATE_CONN_ESTABLISHED, $context.analyzer.transition(STATE_CONN_ESTABLISHED,
STATE_CONN_ESTABLISHED, true) || STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
$context.analyzer.lost_track(); $context.analyzer.lost_track();
}; };
@ -506,15 +654,9 @@ type CiphertextRecord = empty &let {
# initial datatype for binpac # initial datatype for binpac
###################################################################### ######################################################################
type SSLPDU = case $context.analyzer.state() of { type SSLPDU(is_orig: bool) = record {
STATE_ABBREV_SERVER_ENCRYPTED, STATE_CLIENT_ENCRYPTED, records : SSLRecord(is_orig)[] &until($element == 0);
STATE_COMM_ENCRYPTED, STATE_CONN_ESTABLISHED } &byteorder = bigendian;
-> ciphertext : CiphertextRecord;
default
-> plaintext : PlaintextRecord;
} &byteorder = bigendian, &let {
consumed : bool = $context.flow.consume_data();
};
###################################################################### ######################################################################
@ -526,60 +668,48 @@ analyzer SSLAnalyzer {
downflow = SSLFlow(false); downflow = SSLFlow(false);
%member{ %member{
int current_record_type_;
int current_record_version_;
int current_record_length_;
bool current_record_is_orig_;
int state_; int state_;
int old_state_; int old_state_;
bool hello_requested_; bool hello_requested_;
%} %}
%init{ %init{
current_record_type_ = -1;
current_record_version_ = -1;
current_record_length_ = -1;
current_record_is_orig_ = false;
state_ = STATE_INITIAL; state_ = STATE_INITIAL;
old_state_ = STATE_INITIAL; old_state_ = STATE_INITIAL;
hello_requested_ = false; hello_requested_ = false;
%} %}
function current_record_type() : int function determine_ssl_version(head0 : uint8, head1 : uint8,
%{ return current_record_type_; %} head2 : uint8) : int
function current_record_version() : int
%{ return current_record_version_; %}
function current_record_length() : int
%{ return current_record_length_; %}
function current_record_is_orig() : bool
%{ return current_record_is_orig_; %}
function next_record(rec : const_bytestring, type : int,
version : int, is_orig : bool) : bool
%{ %{
current_record_type_ = type; if ( head0 >= 20 && head0 <= 23 &&
current_record_version_ = version; head1 == 0x03 && head2 < 0x03 )
current_record_length_ = rec.length(); // This is most probably SSL version 3.
current_record_is_orig_ = is_orig; return (head1 << 8) | head2;
NewData(is_orig, rec.begin(), rec.end()); else if ( head0 >= 128 && head2 < 5 && head2 != 3 )
// Not very strong evidence, but we suspect
// this to be SSLv2.
return SSLv20;
return true; else
return UNKNOWN_VERSION;
%} %}
function state() : int %{ return state_; %} function state() : int %{ return state_; %}
function old_state() : int %{ return old_state_; %} function old_state() : int %{ return old_state_; %}
function transition(olds : AnalyzerState, news : AnalyzerState, function transition(olds : AnalyzerState, news : AnalyzerState,
is_orig : bool) : bool current_record_is_orig : bool, is_orig : bool) : bool
%{ %{
if ( (olds != STATE_ANY && olds != state_) || if ( (olds != STATE_ANY && olds != state_) ||
current_record_is_orig_ != is_orig ) current_record_is_orig != is_orig )
return false; return false;
old_state_ = state_; old_state_ = state_;
state_ = news; state_ = news;
//printf("transitioning from %s to %s\n", state_label(old_state()).c_str(), state_label(state()).c_str());
return true; return true;
%} %}
@ -602,29 +732,3 @@ analyzer SSLAnalyzer {
return val; return val;
%} %}
}; };
######################################################################
# binpac flow for SSL
######################################################################
flow SSLFlow(is_orig : bool) {
flowunit = SSLPDU withcontext(connection, this);
function discard_data() : bool
%{
flow_buffer_->DiscardData();
return true;
%}
function data_available() : bool
%{
return flow_buffer_->data_available();
%}
function consume_data() : bool
%{
flow_buffer_->NewFrame(0, false);
return true;
%}
};

View file

@ -1,141 +0,0 @@
# $Id:$
# binpac analyzer representing the SSLv3 record layer
#
# This additional layering in the analyzer hierarchy is necessary due to
# fragmentation that can be introduced in the SSL record layer.
%include binpac.pac
%include bro.pac
analyzer SSLRecordLayer withcontext {
analyzer : SSLRecordLayerAnalyzer;
flow : SSLRecordLayerFlow;
};
%include ssl-defs.pac
%extern{
#include "ssl_pac.h"
using binpac::SSL::SSLAnalyzer;
%}
extern type const_bytestring;
type SSLPDU = record {
head0 : uint8;
head1 : uint8;
head2 : uint8;
head3 : uint8;
head4 : uint8;
fragment : bytestring &restofdata;
} &length = 5 + length, &byteorder = bigendian, &let {
version : int =
$context.analyzer.determine_ssl_version(head0, head1, head2);
length : int = case version of {
UNKNOWN_VERSION -> 0;
SSLv20 -> (((head0 & 0x7f) << 8) | head1) - 3;
default -> (head3 << 8) | head4;
};
fw : bool = case version of {
UNKNOWN_VERSION ->
$context.analyzer.forward_record(const_bytestring(),
UNKNOWN_OR_V2_ENCRYPTED, UNKNOWN_VERSION,
$context.flow.is_orig)
&& $context.flow.discard_data();
SSLv20 -> $context.analyzer.forward_v2_record(head2, head3, head4,
fragment, $context.flow.is_orig);
default -> $context.analyzer.forward_record(fragment, head0,
(head1 << 8) | head2, $context.flow.is_orig);
};
};
# binpac-specific definitions
analyzer SSLRecordLayerAnalyzer {
upflow = SSLRecordLayerFlow(true);
downflow = SSLRecordLayerFlow(false);
%member{
SSLAnalyzer* ssl_analyzer_;
int ssl_version_;
int record_length_;
%}
%init{
ssl_analyzer_ = 0;
ssl_version_ = UNKNOWN_VERSION;
record_length_ = 0;
%}
%eof{
ssl_analyzer_->FlowEOF(true);
ssl_analyzer_->FlowEOF(false);
%}
function set_ssl_analyzer(a : SSLAnalyzer) : void
%{ ssl_analyzer_ = a; %}
function ssl_version() : int %{ return ssl_version_; %}
function record_length() : int %{ return record_length_; %}
function determine_ssl_version(head0 : uint8, head1 : uint8,
head2 : uint8) : int
%{
if ( head0 >= 20 && head0 <= 23 &&
head1 == 0x03 && head2 < 0x03 )
// This is most probably SSL version 3.
ssl_version_ = (head1 << 8) | head2;
else if ( head0 >= 128 && head2 < 5 && head2 != 3 )
// Not very strong evidence, but we suspect
// this to be SSLv2.
ssl_version_ = SSLv20;
else
ssl_version_ = UNKNOWN_VERSION;
return ssl_version_;
%}
function forward_record(fragment : const_bytestring, type : int,
version : uint16, is_orig : bool) : bool
%{
return ssl_analyzer_->next_record(fragment, type,
version, is_orig);
%}
function forward_v2_record(b1 : uint8, b2 : uint8, b3 : uint8,
fragment : const_bytestring,
is_orig : bool) : bool
%{
uint8* buffer = new uint8[2 + fragment.length()];
// Byte 1 is the record type.
buffer[0] = b2;
buffer[1] = b3;
memcpy(buffer + 2, fragment.begin(), fragment.length());
const_bytestring bs(buffer, 2 + fragment.length());
bool ret = ssl_analyzer_->next_record(bs, 300 + b1, SSLv20,
is_orig);
delete [] buffer;
return ret;
%}
};
flow SSLRecordLayerFlow(is_orig : bool) {
flowunit = SSLPDU withcontext(connection, this);
function discard_data() : bool
%{
flow_buffer_->DiscardData();
return true;
%}
};

View file

@ -15,8 +15,10 @@ analyzer SSL withcontext {
flow : SSLFlow; flow : SSLFlow;
}; };
%include ssl-defs.pac
%include ssl-protocol.pac %include ssl-protocol.pac
%include ssl-analyzer.pac %include ssl-analyzer.pac
%include ssl-defs.pac
flow SSLFlow(is_orig : bool) {
flowunit = SSLPDU(is_orig) withcontext(connection, this);
};