mirror of
https://github.com/zeek/zeek.git
synced 2025-10-09 01:58:20 +00:00
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:
commit
4bdb94955d
31 changed files with 1242 additions and 8148 deletions
|
@ -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
131
policy/ssl-mozilla-CAs.bro
Normal file
File diff suppressed because one or more lines are too long
979
policy/ssl.bro
979
policy/ssl.bro
File diff suppressed because it is too large
Load diff
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 ()
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
1073
src/SSLCiphers.cc
1073
src/SSLCiphers.cc
File diff suppressed because it is too large
Load diff
367
src/SSLCiphers.h
367
src/SSLCiphers.h
|
@ -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
|
|
|
@ -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
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
830
src/SSLProxy.cc
830
src/SSLProxy.cc
|
@ -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;
|
|
||||||
}
|
|
287
src/SSLProxy.h
287
src/SSLProxy.h
|
@ -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
|
|
946
src/SSLv2.cc
946
src/SSLv2.cc
|
@ -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);
|
|
||||||
}
|
|
239
src/SSLv2.h
239
src/SSLv2.h
|
@ -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
|
|
1471
src/SSLv3.cc
1471
src/SSLv3.cc
File diff suppressed because it is too large
Load diff
590
src/SSLv3.h
590
src/SSLv3.h
|
@ -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
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
265
src/X509.cc
265
src/X509.cc
|
@ -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;
|
|
||||||
}
|
|
33
src/X509.h
33
src/X509.h
|
@ -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
|
|
109
src/bro.bif
109
src/bro.bif
|
@ -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));
|
||||||
|
%}
|
||||||
|
|
|
@ -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%);
|
||||||
|
|
|
@ -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
|
||||||
|
@ -83,7 +78,7 @@ function convert_ciphers_uint24(ciph : uint24[]) : int[]
|
||||||
vector<int>* newciph = new vector<int>();
|
vector<int>* newciph = new vector<int>();
|
||||||
|
|
||||||
std::transform(ciph->begin(), ciph->end(),
|
std::transform(ciph->begin(), ciph->end(),
|
||||||
std::back_inserter(*newciph), to_int());
|
std::back_inserter(*newciph), to_int());
|
||||||
|
|
||||||
return newciph;
|
return newciph;
|
||||||
%}
|
%}
|
||||||
|
@ -93,7 +88,7 @@ function convert_ciphers_uint16(ciph : uint16[]) : int[]
|
||||||
vector<int>* newciph = new vector<int>();
|
vector<int>* newciph = new vector<int>();
|
||||||
|
|
||||||
std::copy(ciph->begin(), ciph->end(),
|
std::copy(ciph->begin(), ciph->end(),
|
||||||
std::back_inserter(*newciph));
|
std::back_inserter(*newciph));
|
||||||
|
|
||||||
return newciph;
|
return newciph;
|
||||||
%}
|
%}
|
||||||
|
@ -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);
|
||||||
Unref(ciph);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
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(),
|
|
||||||
version_, chosen_ciphers);
|
|
||||||
|
|
||||||
if ( v2_sess_hit < 0 )
|
|
||||||
{ // 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(),
|
bro_analyzer_->Conn(),
|
||||||
version_, 0xffffffff);
|
version, ts,
|
||||||
delete ciphers;
|
to_string_val(session_id),
|
||||||
}
|
cipher_suite, comp_method);
|
||||||
|
|
||||||
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 )
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RecordVal* pX509Cert = new RecordVal(x509_type);
|
|
||||||
|
|
||||||
char tmp[256];
|
|
||||||
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(bro_analyzer_->Conn()->OrigAddr()));
|
|
||||||
|
|
||||||
BifEvent::generate_ssl_certificate(bro_analyzer_, bro_analyzer_->Conn(),
|
|
||||||
pX509Cert, current_record_is_orig_);
|
|
||||||
|
|
||||||
if ( X509_get_ext_count(pCert) > 0 )
|
|
||||||
{
|
|
||||||
TableVal* x509ex = new TableVal(x509_extension);
|
|
||||||
|
|
||||||
for ( int k = 0; k < X509_get_ext_count(pCert); ++k )
|
|
||||||
{
|
{
|
||||||
X509_EXTENSION* ex = X509_get_ext(pCert, k);
|
const bytestring& cert = (*certificates)[i];
|
||||||
ASN1_OBJECT* obj = X509_EXTENSION_get_object(ex);
|
const uint8* data = cert.data();
|
||||||
|
X509* pTemp = d2i_X509_binpac(NULL, &data, cert.length());
|
||||||
char buf[256];
|
if ( ! pTemp )
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
BifEvent::generate_process_X509_extensions(bro_analyzer_,
|
|
||||||
bro_analyzer_->Conn(), x509ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ssl_verify_certificates )
|
|
||||||
{
|
|
||||||
STACK_OF(X509)* untrusted_certs = 0;
|
|
||||||
if ( certificates->size() > 1 )
|
|
||||||
{
|
|
||||||
untrusted_certs = sk_X509_new_null();
|
|
||||||
if ( ! untrusted_certs )
|
|
||||||
{
|
{
|
||||||
// X509_V_ERR_OUT_OF_MEM;
|
BifEvent::generate_x509_error(bro_analyzer_, bro_analyzer_->Conn(),
|
||||||
certificate_error(17);
|
ERR_get_error());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( unsigned int i = 1;
|
RecordVal* pX509Cert = new RecordVal(x509_type);
|
||||||
i < certificates->size(); ++i )
|
char tmp[256];
|
||||||
{
|
BIO *bio = BIO_new(BIO_s_mem());
|
||||||
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);
|
pX509Cert->Assign(0, new Val((uint64) X509_get_version(pTemp), TYPE_COUNT));
|
||||||
|
i2a_ASN1_INTEGER(bio, X509_get_serialNumber(pTemp));
|
||||||
|
int len = BIO_read(bio, &(*tmp), sizeof tmp);
|
||||||
|
pX509Cert->Assign(1, new StringVal(len, tmp));
|
||||||
|
|
||||||
|
X509_NAME_print_ex(bio, X509_get_subject_name(pTemp), 0, XN_FLAG_RFC2253);
|
||||||
|
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);
|
||||||
|
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
BroType* count_t = base_type(TYPE_COUNT);
|
||||||
|
TypeList* set_index = new TypeList(count_t);
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
char *pBuffer = 0;
|
||||||
|
int length = 0;
|
||||||
|
|
||||||
|
X509_EXTENSION* ex = X509_get_ext(pTemp, k);
|
||||||
|
if (ex)
|
||||||
|
{
|
||||||
|
ASN1_STRING *pString = X509_EXTENSION_get_data(ex);
|
||||||
|
length = ASN1_STRING_to_UTF8((unsigned char**)&pBuffer, pString);
|
||||||
|
//i2t_ASN1_OBJECT(&pBuffer, length, obj)
|
||||||
|
|
||||||
|
// -1 indicates an error.
|
||||||
|
if ( length < 0 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
StringVal* value = new StringVal(length, pBuffer);
|
||||||
|
BifEvent::generate_x509_extension(bro_analyzer_,
|
||||||
|
bro_analyzer_->Conn(), value);
|
||||||
|
OPENSSL_free(pBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
X509_free(pCert);
|
|
||||||
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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,23 +293,25 @@ 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_KEY_WITH_CERT,
|
#$context.analyzer.transition(STATE_CLIENT_CERT,
|
||||||
STATE_CLIENT_ENCRYPTED, true) ||
|
# STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||||
$context.analyzer.transition(STATE_ABBREV_SERVER_FINISHED,
|
$context.analyzer.transition(STATE_CLIENT_KEY_WITH_CERT,
|
||||||
STATE_COMM_ENCRYPTED, true) ||
|
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||||
$context.analyzer.lost_track();
|
$context.analyzer.transition(STATE_ABBREV_SERVER_FINISHED,
|
||||||
|
STATE_COMM_ENCRYPTED, rec.is_orig, true) ||
|
||||||
|
$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;
|
|
||||||
%}
|
|
||||||
};
|
|
||||||
|
|
|
@ -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;
|
|
||||||
%}
|
|
||||||
};
|
|
|
@ -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);
|
||||||
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue