Merge remote branch 'origin/topic/seth/ssl-updates-for-2.0'

* origin/topic/seth/ssl-updates-for-2.0:
  Added is_orig fields to the SSL events and adapted script.

Closes #692.
This commit is contained in:
Robin Sommer 2011-12-18 15:15:57 -08:00
commit f3c2811e14
8 changed files with 118 additions and 67 deletions

View file

@ -13,6 +13,44 @@ export {
[TLSv11] = "TLSv11",
} &default="UNKNOWN";
const alert_levels: table[count] of string = {
[1] = "warning",
[2] = "fatal",
} &default=function(i: count):string { return fmt("unknown-%d", i); };
const alert_descriptions: table[count] of string = {
[0] = "close_notify",
[10] = "unexpected_message",
[20] = "bad_record_mac",
[21] = "decryption_failed",
[22] = "record_overflow",
[30] = "decompression_failure",
[40] = "handshake_failure",
[41] = "no_certificate",
[42] = "bad_certificate",
[43] = "unsupported_certificate",
[44] = "certificate_revoked",
[45] = "certificate_expired",
[46] = "certificate_unknown",
[47] = "illegal_parameter",
[48] = "unknown_ca",
[49] = "access_denied",
[50] = "decode_error",
[51] = "decrypt_error",
[60] = "export_restriction",
[70] = "protocol_version",
[71] = "insufficient_security",
[80] = "internal_error",
[90] = "user_canceled",
[100] = "no_renegotiation",
[110] = "unsupported_extension",
[111] = "certificate_unobtainable",
[112] = "unrecognized_name",
[113] = "bad_certificate_status_response",
[114] = "bad_certificate_hash_value",
[115] = "unknown_psk_identity",
} &default=function(i: count):string { return fmt("unknown-%d", i); };
# http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xml
const extensions: table[count] of string = {
[0] = "server_name",
@ -526,8 +564,7 @@ export {
[30] = "akid issuer serial mismatch",
[31] = "keyusage no certsign",
[32] = "unable to get crl issuer",
[33] = "unhandled critical extension"
[33] = "unhandled critical extension",
};
}

View file

@ -16,32 +16,33 @@ export {
subject: string &log &optional;
not_valid_before: time &log &optional;
not_valid_after: time &log &optional;
last_alert: string &log &optional;
cert: string &optional;
cert_chain: vector of string &optional;
## This stores the analyzer id used for the analyzer instance attached
## to each connection. It is not used for logging since it's a
## to each connection. It is not used for logging since it's a
## meaningless arbitrary number.
analyzer_id: count &optional;
};
## This is where the default root CA bundle is defined. By loading the
## mozilla-ca-list.bro script it will be set to Mozilla's root CA list.
const root_certs: table[string] of string = {} &redef;
## If true, detach the SSL analyzer from the connection to prevent
## If true, detach the SSL analyzer from the connection to prevent
## continuing to process encrypted traffic. Helps with performance
## (especially with large file transfers).
const disable_analyzer_after_detection = T &redef;
## The openssl command line utility. If it's in the path the default
## value will work, otherwise a full path string can be supplied for the
## utility.
const openssl_util = "openssl" &redef;
global log_ssl: event(rec: Info);
const ports = {
443/tcp, 563/tcp, 585/tcp, 614/tcp, 636/tcp,
989/tcp, 990/tcp, 992/tcp, 993/tcp, 995/tcp, 5223/tcp
@ -86,7 +87,7 @@ function set_session(c: connection)
if ( ! c?$ssl )
c$ssl = [$ts=network_time(), $uid=c$uid, $id=c$id, $cert_chain=vector()];
}
function finish(c: connection)
{
Log::write(SSL::LOG, c$ssl);
@ -98,29 +99,33 @@ function finish(c: connection)
event ssl_client_hello(c: connection, version: count, possible_ts: time, session_id: string, ciphers: count_set) &priority=5
{
set_session(c);
# Save the session_id if there is one set.
if ( session_id != /^\x00{32}$/ )
c$ssl$session_id = bytestring_to_hexstr(session_id);
}
event ssl_server_hello(c: connection, version: count, possible_ts: time, session_id: string, cipher: count, comp_method: count) &priority=5
{
set_session(c);
c$ssl$version = version_strings[version];
c$ssl$cipher = cipher_desc[cipher];
}
event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string) &priority=5
event x509_certificate(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string) &priority=5
{
set_session(c);
# We aren't doing anything with client certificates yet.
if ( is_orig )
return;
if ( chain_idx == 0 )
{
# Save the primary cert.
c$ssl$cert = der_cert;
# Also save other certificate information about the primary cert.
c$ssl$subject = cert$subject;
c$ssl$not_valid_before = cert$not_valid_before;
@ -132,20 +137,27 @@ event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: co
c$ssl$cert_chain[|c$ssl$cert_chain|] = der_cert;
}
}
event ssl_extension(c: connection, code: count, val: string) &priority=5
event ssl_extension(c: connection, is_orig: bool, code: count, val: string) &priority=5
{
set_session(c);
if ( extensions[code] == "server_name" )
if ( is_orig && extensions[code] == "server_name" )
c$ssl$server_name = sub_bytes(val, 6, |val|);
}
event ssl_alert(c: connection, is_orig: bool, level: count, desc: count) &priority=5
{
set_session(c);
c$ssl$last_alert = alert_descriptions[desc];
}
event ssl_established(c: connection) &priority=5
{
set_session(c);
}
event ssl_established(c: connection) &priority=-5
{
finish(c);
@ -163,4 +175,4 @@ event protocol_violation(c: connection, atype: count, aid: count,
{
if ( c?$ssl )
finish(c);
}
}

View file

@ -10,11 +10,11 @@ export {
};
}
event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string) &priority=4
event x509_certificate(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string) &priority=4
{
# We aren't tracking client certificates yet and we are also only tracking
# the primary cert. Watch that this came from an SSL analyzed session too.
if ( ! is_server || chain_idx != 0 || ! c?$ssl )
if ( is_orig || chain_idx != 0 || ! c?$ssl )
return;
c$ssl$cert_hash = md5_hash(der_cert);

View file

@ -33,10 +33,11 @@ export {
const notify_when_cert_expiring_in = 30days &redef;
}
event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string) &priority=3
event x509_certificate(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string) &priority=3
{
# If this isn't the host cert or we aren't interested in the server, just return.
if ( chain_idx != 0 ||
if ( is_orig ||
chain_idx != 0 ||
! c$ssl?$cert_hash ||
! addr_matches_host(c$id$resp_h, notify_certs_expiration) )
return;

View file

@ -44,10 +44,10 @@ event bro_init() &priority=5
Log::create_stream(Known::CERTS_LOG, [$columns=CertsInfo, $ev=log_known_certs]);
}
event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string) &priority=3
event x509_certificate(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string) &priority=3
{
# Make sure this is the server cert and we have a hash for it.
if ( chain_idx != 0 || ! c$ssl?$cert_hash )
if ( is_orig || chain_idx != 0 || ! c$ssl?$cert_hash )
return;
local host = c$id$resp_h;

View file

@ -279,13 +279,13 @@ event ssh_server_version%(c: connection, version: string%);
event ssl_client_hello%(c: connection, version: count, possible_ts: time, session_id: string, ciphers: count_set%);
event ssl_server_hello%(c: connection, version: count, possible_ts: time, session_id: string, cipher: count, comp_method: count%);
event ssl_extension%(c: connection, code: count, val: string%);
event ssl_extension%(c: connection, is_orig: bool, code: count, val: string%);
event ssl_alert%(c: connection, is_orig: bool, level: count, desc: count%);
event ssl_established%(c: connection%);
event ssl_alert%(c: connection, level: count, desc: count%);
event x509_certificate%(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string%);
event x509_extension%(c: connection, data: string%);
event x509_error%(c: connection, err: count%);
event x509_certificate%(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string%);
event x509_extension%(c: connection, is_orig: bool, data: string%);
event x509_error%(c: connection, is_orig: bool, err: count%);
event stp_create_endp%(c: connection, e: int, is_orig: bool%);
event stp_resume_endp%(e: int%);

View file

@ -22,11 +22,17 @@
}
};
string orig_label(bool is_orig);
void free_X509(void *);
X509* d2i_X509_binpac(X509** px, const uint8** in, int len);
%}
%code{
string orig_label(bool is_orig)
{
return string(is_orig ? "originator" :"responder");
}
void free_X509(void* cert)
{
X509_free((X509*) cert);
@ -117,14 +123,14 @@ refine connection SSL_Conn += {
function proc_alert(rec: SSLRecord, level : int, desc : int) : bool
%{
BifEvent::generate_ssl_alert(bro_analyzer(), bro_analyzer()->Conn(),
level, desc);
${rec.is_orig}, level, desc);
return true;
%}
function proc_client_hello(rec: SSLRecord,
version : uint16, ts : double,
session_id : uint8[],
cipher_suites16 : uint16[],
cipher_suites16 : uint16[],
cipher_suites24 : uint24[]) : bool
%{
if ( state_ == STATE_TRACK_LOST )
@ -150,15 +156,15 @@ refine connection SSL_Conn += {
cipher_set->Assign(ciph, 0);
Unref(ciph);
}
BifEvent::generate_ssl_client_hello(bro_analyzer(), bro_analyzer()->Conn(),
version, ts,
to_string_val(session_id),
cipher_set);
delete cipher_suites;
}
return true;
%}
@ -187,24 +193,24 @@ refine connection SSL_Conn += {
std::copy(cipher_suites16->begin(), cipher_suites16->end(), std::back_inserter(*ciphers));
else
std::transform(cipher_suites24->begin(), cipher_suites24->end(), std::back_inserter(*ciphers), to_int());
BifEvent::generate_ssl_server_hello(bro_analyzer(),
bro_analyzer()->Conn(),
version, ts,
to_string_val(session_id),
ciphers->size()==0 ? 0 : ciphers->at(0), comp_method);
delete ciphers;
}
return true;
%}
function proc_ssl_extension(type: int, data: bytestring) : bool
function proc_ssl_extension(rec: SSLRecord, type: int, data: bytestring) : bool
%{
if ( ssl_extension )
BifEvent::generate_ssl_extension(bro_analyzer(),
bro_analyzer()->Conn(), type,
bro_analyzer()->Conn(), ${rec.is_orig}, type,
new StringVal(data.length(), (const char*) data.data()));
return true;
%}
@ -222,7 +228,7 @@ refine connection SSL_Conn += {
if ( x509_certificate )
{
STACK_OF(X509)* untrusted_certs = 0;
for ( unsigned int i = 0; i < certificates->size(); ++i )
{
const bytestring& cert = (*certificates)[i];
@ -231,7 +237,7 @@ refine connection SSL_Conn += {
if ( ! pTemp )
{
BifEvent::generate_x509_error(bro_analyzer(), bro_analyzer()->Conn(),
ERR_get_error());
${rec.is_orig}, ERR_get_error());
return false;
}
@ -257,8 +263,8 @@ refine connection SSL_Conn += {
StringVal* der_cert = new StringVal(cert.length(), (const char*) cert.data());
BifEvent::generate_x509_certificate(bro_analyzer(), bro_analyzer()->Conn(),
${rec.is_orig},
pX509Cert,
! ${rec.is_orig},
i, certificates->size(),
der_cert);
@ -284,7 +290,7 @@ refine connection SSL_Conn += {
StringVal* value = new StringVal(length, (char*)pBuffer);
BifEvent::generate_x509_extension(bro_analyzer(),
bro_analyzer()->Conn(), value);
bro_analyzer()->Conn(), ${rec.is_orig}, value);
OPENSSL_free(pBuffer);
}
}
@ -445,5 +451,5 @@ refine typeattr CiphertextRecord += &let {
}
refine typeattr SSLExtension += &let {
proc : bool = $context.connection.proc_ssl_extension(type, data);
proc : bool = $context.connection.proc_ssl_extension(rec, type, data);
};

View file

@ -22,7 +22,6 @@ type uint24 = record {
};
string state_label(int state_nr);
string orig_label(bool is_orig);
double get_time_from_asn1(const ASN1_TIME * atime);
string handshake_type_label(int type);
%}
@ -35,7 +34,7 @@ type SSLRecord(is_orig: bool) = record {
head2 : uint8;
head3 : uint8;
head4 : uint8;
rec : RecordText(this, is_orig)[] &length=length, &requires(content_type);
rec : RecordText(this)[] &length=length, &requires(content_type);
} &length = length+5, &byteorder=bigendian,
&let {
version : int =
@ -54,25 +53,25 @@ type SSLRecord(is_orig: bool) = record {
};
};
type RecordText(rec: SSLRecord, is_orig: bool) = case $context.connection.state() of {
type RecordText(rec: SSLRecord) = case $context.connection.state() of {
STATE_ABBREV_SERVER_ENCRYPTED, STATE_CLIENT_ENCRYPTED,
STATE_COMM_ENCRYPTED, STATE_CONN_ESTABLISHED
-> ciphertext : CiphertextRecord(rec, is_orig);
-> ciphertext : CiphertextRecord(rec);
default
-> plaintext : PlaintextRecord(rec, is_orig);
-> plaintext : PlaintextRecord(rec);
};
type PossibleEncryptedHandshake(rec: SSLRecord, is_orig: bool) = case $context.connection.state() of {
type PossibleEncryptedHandshake(rec: SSLRecord) = case $context.connection.state() of {
# Deal with encrypted handshakes before the server cipher spec change.
STATE_CLIENT_FINISHED, STATE_CLIENT_ENCRYPTED
-> ct : CiphertextRecord(rec, is_orig);
-> ct : CiphertextRecord(rec);
default -> hs : Handshake(rec);
};
type PlaintextRecord(rec: SSLRecord, is_orig: bool) = case rec.content_type of {
type PlaintextRecord(rec: SSLRecord) = case rec.content_type of {
CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec(rec);
ALERT -> alert : Alert(rec);
HANDSHAKE -> handshake : PossibleEncryptedHandshake(rec, is_orig);
HANDSHAKE -> handshake : PossibleEncryptedHandshake(rec);
APPLICATION_DATA -> app_data : ApplicationData(rec);
V2_ERROR -> v2_error : V2Error(rec);
V2_CLIENT_HELLO -> v2_client_hello : V2ClientHello(rec);
@ -81,7 +80,7 @@ type PlaintextRecord(rec: SSLRecord, is_orig: bool) = case rec.content_type of {
default -> unknown_record : UnknownRecord(rec);
};
type SSLExtension = record {
type SSLExtension(rec: SSLRecord) = record {
type: uint16;
data_len: uint16;
data: bytestring &length=data_len;
@ -156,10 +155,6 @@ enum AnalyzerState {
}
}
string orig_label(bool is_orig)
{
return string(is_orig ? "originator" :"responder");
}
double get_time_from_asn1(const ASN1_TIME * atime)
{
@ -389,7 +384,7 @@ type ClientHello(rec: SSLRecord) = record {
# 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);
extensions : SSLExtension(rec)[] &until($input.length() == 0);
} &let {
state_changed : bool =
$context.connection.transition(STATE_INITIAL,
@ -663,7 +658,7 @@ type UnknownRecord(rec: SSLRecord) = record {
state_changed : bool = $context.connection.lost_track();
};
type CiphertextRecord(rec: SSLRecord, is_orig: bool) = record {
type CiphertextRecord(rec: SSLRecord) = record {
cont : bytestring &restofdata &transient;
} &let {
state_changed : bool =