As I was working on the ssl.bro script last week, I realized that the core SSL analyzers (hand written and binpac) generated really strange events that seemed very different than the events that most of the other core analyzers generate. It turns out that those analyzers were storing some state about existing SSL sessions and generating events based on that information. I think it was storing too much internally and not even making all of it available at the scripting layer. I'll go through bullet points of changes and where I think a bit more work needs to be done.
- SSL & X509 related events are completely rewritten.
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_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)
event x509_extension(c: connection, data: 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)
- In ssl_client_hello, I'm not currently extracting the compression methods supported for some reason, but I'll try to remember to add that soon.
- The possible_ts fields in both "hello" events are the first 32bits of the random data section which some SSL libraries populate with a current time stamp.
- I changed the session_id to a string instead of a table[count] of count like it was before (since it can be up to 32 bytes long). The table was really difficult to work with but it's much easier now that it's a string.
- There is a small issue where the ssl_extension event is be generated before the ssl_client_hello event due to how the parser is parsing the hello record which is a little backward since the extensions are at the end of the hello record. Should I just include the SSL extensions in the hello events as a table? The follow up question is if it makes sense to just include the raw data for the extension even though there is a data structure there? Here's an example of printing the server_name extension (i have to rip off the first 5 bytes which is a piece of the data structure for the extension)...
event ssl_extension(c: connection, code: count, val: string)
{
if ( code == 0 )
print fmt("ssl extension %d %s", code, sub(val, /^...../, ""));
}
- The X509 data type has had some extra fields added to it about the certificate (like the serial number, notValidBefore, and notValidAfter fields).
- The chain_idx and chain_len values indicate which certificate is being looked at if it's part of a certificate full signing chain. The server's certificate will be chain_idx == 1 and the root signing key will be chain_idx==chain_len. Intermediate certs will be somewhere between. Is there a better way to represent this in the script land? I doesn't look very clean and isn't terribly obvious but I wasn't coming up with anything better.
- Certificate extraction now happens in scripting land with the x509_cert_body event (as a DER certificate). It can work on a cluster deployment this way (by printing remotely) and it's just generally more flexible. My initial testing doesn't actually show much difference in pushing the cert to script land or not pushing it there.
- Certificate and certificate chain validation can be controlled at the scripting land a bit more easily because I added a table with root CAs. Sites can even add local CAs to Bro's root CA list if they want to trust locally signed keys. It's just a table that yields DER encoded certificates. I have a script that can automatically generate the Bro script with all of Mozilla's root CAs too so we can ship the same trust model as Mozilla. The problem is that I'm building this X509_STACK (OpenSSL terminology) with each instantiation of the SSL analyzer and it should probably only be done when certs are added or removed from the scripting land table and stored globally. Any guidance I could get on where/how to do this would help.
There is probably more, but this is all I can think of at the moment. Any and all feedback would be great! I'll get the code committed and pushed today.
Thanks!
.Seth