mirror of
https://github.com/zeek/zeek.git
synced 2025-10-08 01:28:20 +00:00
Complete rewrite to SSL analyzer.
* I haven't removed handwritten analyzer code yet although it isn't built anymore. * The ssl.bro script is just an example and doesn't keep any state yet.
This commit is contained in:
parent
871eff9f90
commit
7faf3e0f3b
15 changed files with 1225 additions and 1020 deletions
|
@ -11,6 +11,7 @@ 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;
|
||||||
|
|
||||||
|
@ -917,15 +918,19 @@ global dns_max_queries = 5;
|
||||||
const ssl_max_cipherspec_size = 68 &redef;
|
const ssl_max_cipherspec_size = 68 &redef;
|
||||||
|
|
||||||
# SSL and X.509 types.
|
# SSL and X.509 types.
|
||||||
type cipher_suites_list: set[count];
|
type X509Extensions: table[count] of string;
|
||||||
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;
|
||||||
|
|
167
policy/ssl-mozilla-CAs.bro
Normal file
167
policy/ssl-mozilla-CAs.bro
Normal file
File diff suppressed because one or more lines are too long
983
policy/ssl.bro
983
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"
|
||||||
|
|
||||||
// Keep same order here as in AnalyzerTag definition!
|
// Keep same order here as in AnalyzerTag definition!
|
||||||
|
@ -113,8 +112,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 },
|
||||||
|
|
||||||
|
@ -133,7 +130,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 },
|
||||||
|
|
||||||
|
@ -165,7 +162,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()
|
||||||
|
|
|
@ -29,12 +29,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,
|
HTTP_BINPAC, RPC_UDP_BINPAC, SSL,
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
File, Backdoor, InterConn, SteppingStone, TCPStats,
|
File, Backdoor, InterConn, SteppingStone, TCPStats,
|
||||||
|
@ -43,7 +42,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)
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
## bro target
|
## bro target
|
||||||
|
@ -222,8 +220,10 @@ set(dns_SRCS nb_dns.c nb_dns.h)
|
||||||
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
|
#set(openssl_SRCS X509.cc SSLCiphers.cc SSLInterpreter.cc SSLProxy.cc
|
||||||
SSLv2.cc SSLv3.cc SSLv3Automaton.cc)
|
# SSLv2.cc SSLv3.cc SSLv3Automaton.cc)
|
||||||
|
set(openssl_SRCS)
|
||||||
|
|
||||||
|
|
||||||
if (USE_NMALLOC)
|
if (USE_NMALLOC)
|
||||||
set(malloc_SRCS malloc.c)
|
set(malloc_SRCS malloc.c)
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -366,10 +365,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,5 +1,3 @@
|
||||||
// $Id:$
|
|
||||||
|
|
||||||
#ifndef ssl_binpac_h
|
#ifndef ssl_binpac_h
|
||||||
#define ssl_binpac_h
|
#define ssl_binpac_h
|
||||||
|
|
||||||
|
@ -23,11 +21,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 +31,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
|
||||||
|
|
|
@ -264,19 +264,17 @@ 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%);
|
||||||
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: int, err_string: string%);
|
||||||
|
event x509_cert_validated%(c: connection%);
|
||||||
|
event x509_cert_body%(c: connection, cert: string%);
|
||||||
|
|
||||||
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{
|
||||||
|
@ -12,7 +10,7 @@
|
||||||
|
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
#include <openssl/x509_vfy.h>
|
#include <openssl/x509_vfy.h>
|
||||||
#include "X509.h"
|
#include <openssl/asn1.h>
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,22 +44,15 @@
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
|
||||||
function to_table_val(data : uint8[]) : TableVal
|
function to_string_val(data : uint8[]) : StringVal
|
||||||
%{
|
%{
|
||||||
TableVal* tv = new TableVal(SSL_sessionID);
|
char tmp[32];
|
||||||
for ( unsigned int i = 0; i < data->size(); i += 4 )
|
memset(tmp, 0, sizeof(tmp));
|
||||||
{
|
if ( data )
|
||||||
uint32 temp = 0;
|
for ( unsigned int i = data->size(); i > 0; --i )
|
||||||
for ( unsigned int j = 0; j < 4; ++j )
|
tmp[i-1] = (*data)[i-1];
|
||||||
if ( i + j < data->size() )
|
|
||||||
temp |= (*data)[i + j] << (24 - 8 * j);
|
|
||||||
|
|
||||||
Val* idx = new Val(i / 4, TYPE_COUNT);
|
return new StringVal(32, tmp);
|
||||||
tv->Assign(idx, new Val((*data)[i], TYPE_COUNT));
|
|
||||||
Unref(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tv;
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function version_ok(vers : uint16) : bool
|
function version_ok(vers : uint16) : bool
|
||||||
|
@ -101,23 +92,27 @@ function convert_ciphers_uint16(ciph : uint16[]) : int[]
|
||||||
refine analyzer SSLAnalyzer += {
|
refine analyzer SSLAnalyzer += {
|
||||||
%member{
|
%member{
|
||||||
Analyzer* bro_analyzer_;
|
Analyzer* bro_analyzer_;
|
||||||
|
X509_STORE* ctx;
|
||||||
vector<uint8>* client_session_id_;
|
|
||||||
vector<int>* advertised_ciphers_;
|
|
||||||
int version_;
|
|
||||||
int cipher_;
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%init{
|
%init{
|
||||||
bro_analyzer_ = 0;
|
bro_analyzer_ = 0;
|
||||||
|
ctx = 0;
|
||||||
|
if ( !ctx )
|
||||||
|
{
|
||||||
|
ctx = X509_STORE_new();
|
||||||
|
TableVal* root_certs = opt_internal_table("root_ca_certs")->AsTableVal();
|
||||||
|
ListVal* idxs = root_certs->ConvertToPureList();
|
||||||
|
|
||||||
client_session_id_ = 0;
|
for ( int i = 0; i < idxs->Length(); ++i )
|
||||||
advertised_ciphers_ = new vector<int>;
|
{
|
||||||
version_ = -1;
|
Val* key = idxs->Index(i);
|
||||||
cipher_ = -1;
|
StringVal *sv = root_certs->Lookup(key)->AsStringVal();
|
||||||
|
const uint8* data = sv->Bytes();
|
||||||
if ( ! X509_Cert::bInited )
|
X509* x = d2i_X509_binpac(NULL, &data, sv->Len());
|
||||||
X509_Cert::init();
|
X509_STORE_add_cert(ctx, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%eof{
|
%eof{
|
||||||
|
@ -128,11 +123,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,186 +135,132 @@ refine analyzer SSLAnalyzer += {
|
||||||
bro_analyzer_ = a;
|
bro_analyzer_ = a;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function check_cipher(cipher : int) : 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
|
function certificate_error(err_num : int) : void
|
||||||
%{
|
%{
|
||||||
StringVal* err_str =
|
StringVal* err_str =
|
||||||
new StringVal(X509_verify_cert_error_string(err_num));
|
new StringVal(X509_verify_cert_error_string(err_num));
|
||||||
BifEvent::generate_ssl_X509_error(bro_analyzer_, bro_analyzer_->Conn(),
|
BifEvent::generate_x509_error(bro_analyzer_, bro_analyzer_->Conn(),
|
||||||
err_num, err_str);
|
err_num, err_str);
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_change_cipher_spec(msg : ChangeCipherSpec) : bool
|
function proc_change_cipher_spec(rec: SSLRecord) : bool
|
||||||
%{
|
%{
|
||||||
if ( state_ == STATE_TRACK_LOST )
|
if ( state_ == STATE_TRACK_LOST )
|
||||||
bro_analyzer()->ProtocolViolation(fmt("unexpected ChangeCipherSpec from %s at state %s",
|
bro_analyzer()->ProtocolViolation(fmt("unexpected ChangeCipherSpec from %s at state %s",
|
||||||
orig_label(current_record_is_orig_).c_str(),
|
orig_label(${rec.is_orig}).c_str(),
|
||||||
state_label(old_state_).c_str()));
|
state_label(old_state_).c_str()));
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_application_data(msg : ApplicationData) : bool
|
function proc_application_data(rec: SSLRecord) : bool
|
||||||
%{
|
%{
|
||||||
if ( state_ != STATE_CONN_ESTABLISHED )
|
if ( state_ != STATE_CONN_ESTABLISHED )
|
||||||
bro_analyzer()->ProtocolViolation(fmt("unexpected ApplicationData from %s at state %s",
|
bro_analyzer()->ProtocolViolation(fmt("unexpected ApplicationData from %s at state %s",
|
||||||
orig_label(current_record_is_orig_).c_str(),
|
orig_label(${rec.is_orig}).c_str(),
|
||||||
state_label(old_state_).c_str()));
|
state_label(old_state_).c_str()));
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_alert(level : int, description : int) : bool
|
function proc_alert(rec: SSLRecord, level : int, desc : int) : bool
|
||||||
%{
|
%{
|
||||||
BifEvent::generate_ssl_conn_alert(bro_analyzer_, bro_analyzer_->Conn(),
|
BifEvent::generate_ssl_alert(bro_analyzer_, bro_analyzer_->Conn(),
|
||||||
current_record_version_, level,
|
level, desc);
|
||||||
description);
|
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_client_hello(version : uint16, session_id : uint8[],
|
function proc_client_hello(rec: SSLRecord,
|
||||||
csuits : int[]) : bool
|
version : uint16, ts : double,
|
||||||
|
session_id : uint8[],
|
||||||
|
cipher_suites : int[]) : bool
|
||||||
%{
|
%{
|
||||||
if ( state_ == STATE_TRACK_LOST )
|
if ( state_ == STATE_TRACK_LOST )
|
||||||
bro_analyzer()->ProtocolViolation(fmt("unexpected client hello message from %s in state %s",
|
bro_analyzer()->ProtocolViolation(fmt("unexpected client hello message from %s in state %s",
|
||||||
orig_label(current_record_is_orig_).c_str(),
|
orig_label(${rec.is_orig}).c_str(),
|
||||||
state_label(old_state_).c_str()));
|
state_label(old_state_).c_str()));
|
||||||
|
|
||||||
if ( ! version_ok(version) )
|
if ( ! version_ok(version) )
|
||||||
bro_analyzer()->ProtocolViolation(fmt("unsupported client SSL version 0x%04x", version));
|
bro_analyzer()->ProtocolViolation(fmt("unsupported client SSL version 0x%04x", version));
|
||||||
|
|
||||||
delete client_session_id_;
|
if ( ssl_client_hello )
|
||||||
client_session_id_ = new vector<uint8>(*session_id);
|
|
||||||
|
|
||||||
TableVal* cipher_table = new TableVal(cipher_suites_list);
|
|
||||||
for ( unsigned int i = 0; i < csuits->size(); ++i )
|
|
||||||
{
|
{
|
||||||
Val* ciph = new Val((*csuits)[i], TYPE_COUNT);
|
BroType* count_t = base_type(TYPE_COUNT);
|
||||||
cipher_table->Assign(ciph, 0);
|
TypeList* set_index = new TypeList(count_t);
|
||||||
|
set_index->Append(count_t);
|
||||||
|
SetType* s = new SetType(set_index, 0);
|
||||||
|
TableVal* cipher_set = new TableVal(s);
|
||||||
|
for ( unsigned int i = 0; i < cipher_suites->size(); ++i )
|
||||||
|
{
|
||||||
|
Val* ciph = new Val((*cipher_suites)[i], TYPE_COUNT);
|
||||||
|
cipher_set->Assign(ciph, 0);
|
||||||
Unref(ciph);
|
Unref(ciph);
|
||||||
}
|
}
|
||||||
|
|
||||||
BifEvent::generate_ssl_conn_attempt(bro_analyzer_, bro_analyzer_->Conn(),
|
BifEvent::generate_ssl_client_hello(bro_analyzer_, bro_analyzer_->Conn(),
|
||||||
version, cipher_table);
|
version, ts,
|
||||||
|
to_string_val(session_id),
|
||||||
if ( ssl_compare_cipherspecs )
|
cipher_set);
|
||||||
{
|
|
||||||
delete advertised_ciphers_;
|
|
||||||
advertised_ciphers_ = csuits;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
delete csuits;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_server_hello(version : uint16, session_id : uint8[],
|
function proc_server_hello(rec: SSLRecord,
|
||||||
ciphers : int[], v2_sess_hit : int) : bool
|
version : uint16, ts : double,
|
||||||
|
session_id : uint8[],
|
||||||
|
cipher_suite : uint16,
|
||||||
|
comp_method : uint8) : bool
|
||||||
%{
|
%{
|
||||||
if ( state_ == STATE_TRACK_LOST )
|
if ( state_ == STATE_TRACK_LOST )
|
||||||
bro_analyzer()->ProtocolViolation(fmt("unexpected server hello message from %s in state %s",
|
bro_analyzer()->ProtocolViolation(fmt("unexpected server hello message from %s in state %s",
|
||||||
orig_label(current_record_is_orig_).c_str(),
|
orig_label(${rec.is_orig}).c_str(),
|
||||||
state_label(old_state_).c_str()));
|
state_label(old_state_).c_str()));
|
||||||
|
|
||||||
if ( ! version_ok(version) )
|
if ( ! version_ok(version) )
|
||||||
bro_analyzer()->ProtocolViolation(fmt("unsupported server SSL version 0x%04x", version));
|
bro_analyzer()->ProtocolViolation(fmt("unsupported server SSL version 0x%04x", version));
|
||||||
|
|
||||||
version_ = version;
|
if ( ssl_server_hello )
|
||||||
|
|
||||||
TableVal* chosen_ciphers = new TableVal(cipher_suites_list);
|
|
||||||
for ( unsigned int i = 0; i < ciphers->size(); ++i )
|
|
||||||
{
|
{
|
||||||
Val* ciph = new Val((*ciphers)[i], TYPE_COUNT);
|
BifEvent::generate_ssl_server_hello(bro_analyzer_,
|
||||||
chosen_ciphers->Assign(ciph, 0);
|
|
||||||
Unref(ciph);
|
|
||||||
}
|
|
||||||
|
|
||||||
BifEvent::generate_ssl_conn_server_reply(bro_analyzer_,
|
|
||||||
bro_analyzer_->Conn(),
|
bro_analyzer_->Conn(),
|
||||||
version_, chosen_ciphers);
|
version, ts,
|
||||||
|
to_string_val(session_id),
|
||||||
if ( v2_sess_hit < 0 )
|
cipher_suite, comp_method);
|
||||||
{ // this is SSLv3
|
|
||||||
cipher_ = (*ciphers)[0];
|
|
||||||
check_cipher(cipher_);
|
|
||||||
TableVal* tv = to_table_val(session_id);
|
|
||||||
if ( client_session_id_ &&
|
|
||||||
*client_session_id_ == *session_id )
|
|
||||||
BifEvent::generate_ssl_conn_reused(bro_analyzer_,
|
|
||||||
bro_analyzer_->Conn(), tv);
|
|
||||||
else
|
|
||||||
BifEvent::generate_ssl_session_insertion(bro_analyzer_,
|
|
||||||
bro_analyzer_->Conn(), tv);
|
|
||||||
|
|
||||||
delete ciphers;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( v2_sess_hit > 0 )
|
|
||||||
{ // this is SSLv2 and a session hit
|
|
||||||
if ( client_session_id_ )
|
|
||||||
{
|
|
||||||
TableVal* tv = to_table_val(client_session_id_);
|
|
||||||
BifEvent::generate_ssl_conn_reused(bro_analyzer_,
|
|
||||||
bro_analyzer_->Conn(), tv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't know the chosen cipher, as there is
|
|
||||||
// no session storage.
|
|
||||||
BifEvent::generate_ssl_conn_established(bro_analyzer_,
|
|
||||||
bro_analyzer_->Conn(),
|
|
||||||
version_, 0xffffffff);
|
|
||||||
delete ciphers;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// This is SSLv2; we have to set advertised
|
|
||||||
// ciphers to server ciphers.
|
|
||||||
if ( ssl_compare_cipherspecs )
|
|
||||||
{
|
|
||||||
delete advertised_ciphers_;
|
|
||||||
advertised_ciphers_ = ciphers;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bro_analyzer()->ProtocolConfirmation();
|
bro_analyzer()->ProtocolConfirmation();
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_certificate(certificates : bytestring[]) : bool
|
function proc_ssl_extension(type: int, data: bytestring) : bool
|
||||||
|
%{
|
||||||
|
if ( ssl_extension )
|
||||||
|
BifEvent::generate_ssl_extension(bro_analyzer_,
|
||||||
|
bro_analyzer_->Conn(), type,
|
||||||
|
new StringVal(data.length(), (const char*) data.data()));
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function proc_certificate(rec: SSLRecord, certificates : bytestring[]) : bool
|
||||||
%{
|
%{
|
||||||
if ( state_ == STATE_TRACK_LOST )
|
if ( state_ == STATE_TRACK_LOST )
|
||||||
bro_analyzer()->ProtocolViolation(fmt("unexpected certificate message from %s in state %s",
|
bro_analyzer()->ProtocolViolation(fmt("unexpected certificate message from %s in state %s",
|
||||||
orig_label(current_record_is_orig_).c_str(),
|
orig_label(${rec.is_orig}).c_str(),
|
||||||
state_label(old_state_).c_str()));
|
state_label(old_state_).c_str()));
|
||||||
|
|
||||||
if ( ! ssl_analyze_certificates )
|
|
||||||
return true;
|
|
||||||
if ( certificates->size() == 0 )
|
if ( certificates->size() == 0 )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
BifEvent::generate_ssl_certificate_seen(bro_analyzer_,
|
STACK_OF(X509)* untrusted_certs = 0;
|
||||||
bro_analyzer_->Conn(),
|
|
||||||
! current_record_is_orig_);
|
|
||||||
|
|
||||||
const bytestring& cert = (*certificates)[0];
|
if ( x509_certificate )
|
||||||
|
{
|
||||||
|
X509* pCert = 0;
|
||||||
|
for ( unsigned int i = 0; i < certificates->size(); ++i )
|
||||||
|
{
|
||||||
|
const bytestring& cert = (*certificates)[i];
|
||||||
const uint8* data = cert.data();
|
const uint8* data = cert.data();
|
||||||
|
X509* pTemp = d2i_X509_binpac(NULL, &data, cert.length());
|
||||||
X509* pCert = d2i_X509_binpac(NULL, &data, cert.length());
|
if ( ! pTemp )
|
||||||
if ( ! pCert )
|
|
||||||
{
|
{
|
||||||
// X509_V_UNABLE_TO_DECRYPT_CERT_SIGNATURE
|
// X509_V_UNABLE_TO_DECRYPT_CERT_SIGNATURE
|
||||||
certificate_error(4);
|
certificate_error(4);
|
||||||
|
@ -332,95 +268,117 @@ refine analyzer SSLAnalyzer += {
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordVal* pX509Cert = new RecordVal(x509_type);
|
RecordVal* pX509Cert = new RecordVal(x509_type);
|
||||||
|
|
||||||
char tmp[256];
|
char tmp[256];
|
||||||
X509_NAME_oneline(X509_get_issuer_name(pCert), tmp, sizeof tmp);
|
pX509Cert->Assign(0, new Val((uint64) X509_get_version(pTemp), TYPE_COUNT));
|
||||||
pX509Cert->Assign(0, new StringVal(tmp));
|
pX509Cert->Assign(1, new StringVal(16, (const char*) X509_get_serialNumber(pTemp)->data));
|
||||||
X509_NAME_oneline(X509_get_subject_name(pCert), tmp, sizeof tmp);
|
X509_NAME_oneline(X509_get_subject_name(pTemp), tmp, sizeof tmp);
|
||||||
|
pX509Cert->Assign(2, new StringVal(tmp));
|
||||||
|
X509_NAME_oneline(X509_get_issuer_name(pTemp), tmp, sizeof tmp);
|
||||||
|
pX509Cert->Assign(3, new StringVal(tmp));
|
||||||
|
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));
|
||||||
|
|
||||||
pX509Cert->Assign(1, new StringVal(tmp));
|
BifEvent::generate_x509_certificate(bro_analyzer_, bro_analyzer_->Conn(),
|
||||||
pX509Cert->Assign(2, new AddrVal(bro_analyzer_->Conn()->OrigAddr()));
|
pX509Cert,
|
||||||
|
${rec.is_orig},
|
||||||
|
i, certificates->size()-1);
|
||||||
|
|
||||||
BifEvent::generate_ssl_certificate(bro_analyzer_, bro_analyzer_->Conn(),
|
// Are there any X509 extensions?
|
||||||
pX509Cert, current_record_is_orig_);
|
if ( x509_extension && X509_get_ext_count(pTemp) > 0 )
|
||||||
|
|
||||||
if ( X509_get_ext_count(pCert) > 0 )
|
|
||||||
{
|
{
|
||||||
TableVal* x509ex = new TableVal(x509_extension);
|
BroType* count_t = base_type(TYPE_COUNT);
|
||||||
|
TypeList* set_index = new TypeList(count_t);
|
||||||
for ( int k = 0; k < X509_get_ext_count(pCert); ++k )
|
set_index->Append(count_t);
|
||||||
|
SetType* s = new SetType(set_index, 0);
|
||||||
|
TableVal* x509ex = new TableVal(s);
|
||||||
|
int num_ext = X509_get_ext_count(pTemp);
|
||||||
|
for ( int k = 0; k < num_ext; ++k )
|
||||||
{
|
{
|
||||||
X509_EXTENSION* ex = X509_get_ext(pCert, k);
|
char *pBuffer = 0;
|
||||||
ASN1_OBJECT* obj = X509_EXTENSION_get_object(ex);
|
int length = 0;
|
||||||
|
|
||||||
char buf[256];
|
X509_EXTENSION* ex = X509_get_ext(pTemp, k);
|
||||||
i2t_ASN1_OBJECT(buf, sizeof(buf), obj);
|
if (ex)
|
||||||
Val* index = new Val(k+1, TYPE_COUNT);
|
{
|
||||||
Val* value = new StringVal(strlen(buf), buf);
|
ASN1_STRING *pString = X509_EXTENSION_get_data(ex);
|
||||||
x509ex->Assign(index, value);
|
length = ASN1_STRING_to_UTF8((unsigned char**)&pBuffer, pString);
|
||||||
Unref(index);
|
//i2t_ASN1_OBJECT(&pBuffer, length, obj)
|
||||||
|
|
||||||
|
// -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BifEvent::generate_process_X509_extensions(bro_analyzer_,
|
// Only grab the cert body for the first
|
||||||
bro_analyzer_->Conn(), x509ex);
|
// certificate.
|
||||||
|
if ( x509_cert_body && i == 0 )
|
||||||
|
{
|
||||||
|
StringVal* der = new StringVal(cert.length(), (const char*) cert.data());
|
||||||
|
BifEvent::generate_x509_cert_body(bro_analyzer_,
|
||||||
|
bro_analyzer_->Conn(), der);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ssl_verify_certificates )
|
if ( ssl_verify_certificates )
|
||||||
{
|
{
|
||||||
STACK_OF(X509)* untrusted_certs = 0;
|
if ( i == 0 )
|
||||||
if ( certificates->size() > 1 )
|
// Store the first cert
|
||||||
|
pCert = pTemp;
|
||||||
|
|
||||||
|
else if ( i == 1 )
|
||||||
{
|
{
|
||||||
|
// Init the cert stack on the
|
||||||
|
// second cert seen.
|
||||||
untrusted_certs = sk_X509_new_null();
|
untrusted_certs = sk_X509_new_null();
|
||||||
if ( ! untrusted_certs )
|
if ( ! untrusted_certs )
|
||||||
{
|
certificate_error(X509_V_ERR_OUT_OF_MEM);
|
||||||
// X509_V_ERR_OUT_OF_MEM;
|
|
||||||
certificate_error(17);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( unsigned int i = 1;
|
|
||||||
i < certificates->size(); ++i )
|
|
||||||
{
|
|
||||||
const bytestring& temp =
|
|
||||||
(*certificates)[i];
|
|
||||||
const uint8* tdata = temp.data();
|
|
||||||
X509* pTemp = d2i_X509_binpac(NULL,
|
|
||||||
&tdata, temp.length());
|
|
||||||
if ( ! pTemp )
|
|
||||||
{
|
|
||||||
// X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
|
|
||||||
certificate_error(2);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( i > 0 )
|
||||||
|
// If this isn't the first cert,
|
||||||
|
// push it onto the cert stack.
|
||||||
sk_X509_push(untrusted_certs, pTemp);
|
sk_X509_push(untrusted_certs, pTemp);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Verify first cert or full chain upon
|
||||||
|
// reaching the last cert.
|
||||||
|
if ( certificates->size() == i+1 )
|
||||||
|
{
|
||||||
X509_STORE_CTX csc;
|
X509_STORE_CTX csc;
|
||||||
X509_STORE_CTX_init(&csc, X509_Cert::ctx,
|
X509_STORE_CTX_init(&csc, ctx,
|
||||||
pCert, untrusted_certs);
|
pCert,
|
||||||
X509_STORE_CTX_set_time(&csc, 0, time_t(network_time()));
|
untrusted_certs);
|
||||||
if (! X509_verify_cert(&csc))
|
X509_STORE_CTX_set_time(&csc, 0, (time_t) network_time());
|
||||||
certificate_error(csc.error);
|
|
||||||
X509_STORE_CTX_cleanup(&csc);
|
|
||||||
|
|
||||||
|
if ( X509_verify_cert(&csc) )
|
||||||
|
BifEvent::generate_x509_cert_validated(bro_analyzer_,
|
||||||
|
bro_analyzer_->Conn());
|
||||||
|
else
|
||||||
|
certificate_error(csc.error);
|
||||||
|
|
||||||
|
X509_STORE_CTX_cleanup(&csc);
|
||||||
|
if ( untrusted_certs )
|
||||||
sk_X509_pop_free(untrusted_certs, X509_free);
|
sk_X509_pop_free(untrusted_certs, X509_free);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
X509_free(pCert);
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_v2_certificate(cert : bytestring) : bool
|
function proc_v2_certificate(rec: SSLRecord, cert : bytestring) : bool
|
||||||
%{
|
%{
|
||||||
vector<bytestring>* cert_list = new vector<bytestring>(1,cert);
|
vector<bytestring>* cert_list = new vector<bytestring>(1,cert);
|
||||||
bool ret = proc_certificate(cert_list);
|
bool ret = proc_certificate(rec, cert_list);
|
||||||
delete cert_list;
|
delete cert_list;
|
||||||
return ret;
|
return ret;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_v3_certificate(cl : CertificateList) : bool
|
function proc_v3_certificate(rec: SSLRecord, cl : CertificateList) : bool
|
||||||
%{
|
%{
|
||||||
vector<X509Certificate*>* certs = cl->val();
|
vector<X509Certificate*>* certs = cl->val();
|
||||||
vector<bytestring>* cert_list = new vector<bytestring>();
|
vector<bytestring>* cert_list = new vector<bytestring>();
|
||||||
|
@ -428,65 +386,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 +448,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,96 @@ 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 +254,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 +273,8 @@ enum HandshakeType {
|
||||||
case CERTIFICATE_VERIFY: return string("CERTIFICATE_VERIFY");
|
case CERTIFICATE_VERIFY: return string("CERTIFICATE_VERIFY");
|
||||||
case CLIENT_KEY_EXCHANGE: return string("CLIENT_KEY_EXCHANGE");
|
case CLIENT_KEY_EXCHANGE: return string("CLIENT_KEY_EXCHANGE");
|
||||||
case FINISHED: return string("FINISHED");
|
case FINISHED: return string("FINISHED");
|
||||||
|
case CERTIFICATE_URL: return string("CERTIFICATE_URL");
|
||||||
|
case CERTIFICATE_STATUS: return string("CERTIFICATE_STATUS");
|
||||||
default: return string(fmt("UNKNOWN (%d)", type));
|
default: return string(fmt("UNKNOWN (%d)", type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,22 +285,24 @@ enum HandshakeType {
|
||||||
# V3 Change Cipher Spec Protocol (7.1.)
|
# V3 Change Cipher Spec Protocol (7.1.)
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
type ChangeCipherSpec = record {
|
type ChangeCipherSpec(rec: SSLRecord) = record {
|
||||||
type : uint8;
|
type : uint8;
|
||||||
} &length = 1, &let {
|
} &length = 1, &let {
|
||||||
state_changed : bool =
|
state_changed : bool =
|
||||||
$context.analyzer.transition(STATE_CLIENT_FINISHED,
|
$context.analyzer.transition(STATE_CLIENT_FINISHED,
|
||||||
STATE_COMM_ENCRYPTED, false) ||
|
STATE_COMM_ENCRYPTED, rec.is_orig, false) ||
|
||||||
$context.analyzer.transition(STATE_IN_SERVER_HELLO,
|
$context.analyzer.transition(STATE_IN_SERVER_HELLO,
|
||||||
STATE_ABBREV_SERVER_ENCRYPTED, false) ||
|
STATE_ABBREV_SERVER_ENCRYPTED, rec.is_orig, false) ||
|
||||||
$context.analyzer.transition(STATE_CLIENT_KEY_NO_CERT,
|
$context.analyzer.transition(STATE_CLIENT_KEY_NO_CERT,
|
||||||
STATE_CLIENT_ENCRYPTED, true) ||
|
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||||
$context.analyzer.transition(STATE_CLIENT_CERT_VERIFIED,
|
$context.analyzer.transition(STATE_CLIENT_CERT_VERIFIED,
|
||||||
STATE_CLIENT_ENCRYPTED, true) ||
|
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||||
|
#$context.analyzer.transition(STATE_CLIENT_CERT,
|
||||||
|
# STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||||
$context.analyzer.transition(STATE_CLIENT_KEY_WITH_CERT,
|
$context.analyzer.transition(STATE_CLIENT_KEY_WITH_CERT,
|
||||||
STATE_CLIENT_ENCRYPTED, true) ||
|
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||||
$context.analyzer.transition(STATE_ABBREV_SERVER_FINISHED,
|
$context.analyzer.transition(STATE_ABBREV_SERVER_FINISHED,
|
||||||
STATE_COMM_ENCRYPTED, true) ||
|
STATE_COMM_ENCRYPTED, rec.is_orig, true) ||
|
||||||
$context.analyzer.lost_track();
|
$context.analyzer.lost_track();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -166,19 +311,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 +332,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 +343,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 +352,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 +362,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 +380,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 +391,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 +402,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 +422,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 +436,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 +454,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 +472,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 +487,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 +502,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 +524,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 +541,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 +552,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 +562,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();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -435,27 +584,32 @@ type CertificateVerify = record {
|
||||||
# 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 +618,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 +646,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 +660,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;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -603,28 +725,3 @@ analyzer SSLAnalyzer {
|
||||||
%}
|
%}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
# 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;
|
|
||||||
%}
|
|
||||||
};
|
|
||||||
|
|
|
@ -23,37 +23,6 @@ using binpac::SSL::SSLAnalyzer;
|
||||||
extern type const_bytestring;
|
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
|
# binpac-specific definitions
|
||||||
|
|
||||||
analyzer SSLRecordLayerAnalyzer {
|
analyzer SSLRecordLayerAnalyzer {
|
||||||
|
@ -62,15 +31,10 @@ analyzer SSLRecordLayerAnalyzer {
|
||||||
|
|
||||||
%member{
|
%member{
|
||||||
SSLAnalyzer* ssl_analyzer_;
|
SSLAnalyzer* ssl_analyzer_;
|
||||||
|
|
||||||
int ssl_version_;
|
|
||||||
int record_length_;
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%init{
|
%init{
|
||||||
ssl_analyzer_ = 0;
|
ssl_analyzer_ = 0;
|
||||||
ssl_version_ = UNKNOWN_VERSION;
|
|
||||||
record_length_ = 0;
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%eof{
|
%eof{
|
||||||
|
@ -81,27 +45,6 @@ analyzer SSLRecordLayerAnalyzer {
|
||||||
function set_ssl_analyzer(a : SSLAnalyzer) : void
|
function set_ssl_analyzer(a : SSLAnalyzer) : void
|
||||||
%{ ssl_analyzer_ = a; %}
|
%{ 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,
|
function forward_record(fragment : const_bytestring, type : int,
|
||||||
version : uint16, is_orig : bool) : bool
|
version : uint16, is_orig : bool) : bool
|
||||||
|
|
|
@ -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