From a57e50da35c4ad67384c8c01ebbde3e2a568ca58 Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Mon, 23 May 2011 14:54:52 -0400 Subject: [PATCH] SSL analyzer changes with accompanying BiF. - Full DER certificates are extracted as strings to be used with corresponding BiFs. - x509_verify function to verify single certs and/or full certificate chains. --- src/bro.bif | 95 ++++++++++++++++++++++++++++++++++++++++ src/event.bif | 2 +- src/ssl-analyzer.pac | 28 +++++++----- src/ssl-record-layer.pac | 84 ----------------------------------- 4 files changed, 114 insertions(+), 95 deletions(-) delete mode 100644 src/ssl-record-layer.pac diff --git a/src/bro.bif b/src/bro.bif index 1bd16a24ad..f07042241d 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -3322,3 +3322,98 @@ function entropy_test_finish%(index: any%): entropy_test_result delete s; return ent_result; %} + + +%%{ + +// ### NOTE: while d2i_X509 does not take a const u_char** pointer, +// here we assume d2i_X509 does not write to , so it is safe to +// convert data to a non-const pointer. Could some X509 guru verify +// this? + +X509* d2i_X509_(X509** px, const u_char** in, int len) + { + // This is the indexed map of X509 certificate stores. + static map x509_stores; + +#ifdef OPENSSL_D2I_X509_USES_CONST_CHAR + return d2i_X509(px, in, len); +#else + return d2i_X509(px, (u_char**)in, len); +#endif + } +%%} + + +function x509_verify%(der_cert: string, cert_stack: string_vec, root_certs: table_s_of_s%): bool + %{ + //BroString* s = convert_index_to_string(index); + + X509_STORE* ctx = 0; + + int i = 0; + + if ( ! ctx ) // lookup to see if we have this one built already! + { + ctx = X509_STORE_new(); + TableVal* root_certs2 = root_certs->AsTableVal(); + ListVal* idxs = root_certs2->ConvertToPureList(); + + // Build the validation store + for ( i = 0; i < idxs->Length(); ++i ) + { + Val* key = idxs->Index(i); + StringVal *sv = root_certs2->Lookup(key)->AsStringVal(); + const uint8* data = sv->Bytes(); + X509* x = d2i_X509_(NULL, &data, sv->Len()); + if ( ! x ) + { + builtin_run_time(fmt("Root CA error: %s", ERR_error_string(ERR_peek_last_error(),NULL))); + return new Val(false, TYPE_BOOL); + } + X509_STORE_add_cert(ctx, x); + } + } + + STACK_OF(X509)* untrusted_certs = sk_X509_new_null(); + //if ( ! untrusted_certs ) + // certificate_error(X509_V_ERR_OUT_OF_MEM); + VectorVal *cert_stack_vec = cert_stack->AsVectorVal(); + for ( i = 1; i < (int) cert_stack_vec->Size(); ++i ) + { + StringVal *sv = cert_stack_vec->Lookup(i)->AsStringVal(); + const uint8 *data = sv->Bytes(); + X509* x = d2i_X509_(NULL, &data, sv->Len()); + if ( ! x ) + { + builtin_run_time(fmt("Untrusted certificate stack creation error: %s", ERR_error_string(ERR_peek_last_error(),NULL))); + return new Val(false, TYPE_BOOL); + } + sk_X509_push(untrusted_certs, x); + } + // Verify first cert or full chain upon + // reaching the last cert. + //if ( certificates->size() == i+1 ) + // { + + const uint8 *cert_data = der_cert->Bytes(); + + X509_STORE_CTX csc; + X509* cert = d2i_X509_(NULL, &cert_data, der_cert->Len()); + if ( ! cert ) + { + builtin_run_time(fmt("Certificate error: %s", ERR_error_string(ERR_peek_last_error(),NULL))); + return new Val(false, TYPE_BOOL); + } + X509_STORE_CTX_init(&csc, ctx, cert, untrusted_certs); + X509_STORE_CTX_set_time(&csc, 0, (time_t) network_time); + + int result = X509_verify_cert(&csc); + // certificate_error(csc.error); + X509_STORE_CTX_cleanup(&csc); + if ( untrusted_certs ) + sk_X509_pop_free(untrusted_certs, X509_free); + return new Val(result, TYPE_BOOL); + %} + + \ No newline at end of file diff --git a/src/event.bif b/src/event.bif index cb4fd899c1..3e9e2ee6a4 100644 --- a/src/event.bif +++ b/src/event.bif @@ -270,7 +270,7 @@ 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_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: int, err_string: string%); event x509_cert_validated%(c: connection%); diff --git a/src/ssl-analyzer.pac b/src/ssl-analyzer.pac index 3c5d0e00ed..571fe2aee4 100644 --- a/src/ssl-analyzer.pac +++ b/src/ssl-analyzer.pac @@ -263,7 +263,7 @@ refine analyzer SSLAnalyzer += { if ( ! pTemp ) { // X509_V_UNABLE_TO_DECRYPT_CERT_SIGNATURE - certificate_error(4); + certificate_error(ERR_get_error()); return false; } @@ -271,18 +271,26 @@ refine analyzer SSLAnalyzer += { char tmp[256]; pX509Cert->Assign(0, new Val((uint64) X509_get_version(pTemp), TYPE_COUNT)); pX509Cert->Assign(1, new StringVal(16, (const char*) X509_get_serialNumber(pTemp)->data)); - 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)); + + BIO *bio = BIO_new(BIO_s_mem()); + X509_NAME_print_ex(bio, X509_get_subject_name(pTemp), 0, XN_FLAG_RFC2253); + int len = BIO_gets(bio, &(*tmp), sizeof tmp); + pX509Cert->Assign(2, new StringVal(len, tmp)); + X509_NAME_print_ex(bio, X509_get_issuer_name(pTemp), 0, XN_FLAG_RFC2253); + len = BIO_gets(bio, &(*tmp), sizeof tmp); + pX509Cert->Assign(3, new StringVal(len, tmp)); + BIO_free(bio); + pX509Cert->Assign(4, new Val(get_time_from_asn1(X509_get_notBefore(pTemp)), TYPE_TIME)); pX509Cert->Assign(5, new Val(get_time_from_asn1(X509_get_notAfter(pTemp)), TYPE_TIME)); - + StringVal* der_cert = new StringVal(cert.length(), (const char*) cert.data()); + BifEvent::generate_x509_certificate(bro_analyzer_, bro_analyzer_->Conn(), - pX509Cert, - ${rec.is_orig}, - i, certificates->size()-1); - + pX509Cert, + ! ${rec.is_orig}, + i, certificates->size()-1, + der_cert); + // Are there any X509 extensions? if ( x509_extension && X509_get_ext_count(pTemp) > 0 ) { diff --git a/src/ssl-record-layer.pac b/src/ssl-record-layer.pac deleted file mode 100644 index d70fbcc1ea..0000000000 --- a/src/ssl-record-layer.pac +++ /dev/null @@ -1,84 +0,0 @@ -# $Id:$ - -# binpac analyzer representing the SSLv3 record layer -# -# This additional layering in the analyzer hierarchy is necessary due to -# fragmentation that can be introduced in the SSL record layer. - -%include binpac.pac -%include bro.pac - -analyzer SSLRecordLayer withcontext { - analyzer : SSLRecordLayerAnalyzer; - flow : SSLRecordLayerFlow; -}; - -%include ssl-defs.pac - -%extern{ -#include "ssl_pac.h" -using binpac::SSL::SSLAnalyzer; -%} - -extern type const_bytestring; - - -# binpac-specific definitions - -analyzer SSLRecordLayerAnalyzer { - upflow = SSLRecordLayerFlow(true); - downflow = SSLRecordLayerFlow(false); - - %member{ - SSLAnalyzer* ssl_analyzer_; - %} - - %init{ - ssl_analyzer_ = 0; - %} - - %eof{ - ssl_analyzer_->FlowEOF(true); - ssl_analyzer_->FlowEOF(false); - %} - - function set_ssl_analyzer(a : SSLAnalyzer) : void - %{ ssl_analyzer_ = a; %} - - - function forward_record(fragment : const_bytestring, type : int, - version : uint16, is_orig : bool) : bool - %{ - return ssl_analyzer_->next_record(fragment, type, - version, is_orig); - %} - - function forward_v2_record(b1 : uint8, b2 : uint8, b3 : uint8, - fragment : const_bytestring, - is_orig : bool) : bool - %{ - uint8* buffer = new uint8[2 + fragment.length()]; - - // Byte 1 is the record type. - buffer[0] = b2; - buffer[1] = b3; - - memcpy(buffer + 2, fragment.begin(), fragment.length()); - const_bytestring bs(buffer, 2 + fragment.length()); - - bool ret = ssl_analyzer_->next_record(bs, 300 + b1, SSLv20, - is_orig); - delete [] buffer; - return ret; - %} -}; - -flow SSLRecordLayerFlow(is_orig : bool) { - flowunit = SSLPDU withcontext(connection, this); - - function discard_data() : bool - %{ - flow_buffer_->DiscardData(); - return true; - %} -};