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.
This commit is contained in:
Seth Hall 2011-05-23 14:54:52 -04:00
parent 297a2cb9c5
commit a57e50da35
4 changed files with 114 additions and 95 deletions

View file

@ -3322,3 +3322,98 @@ function entropy_test_finish%(index: any%): entropy_test_result
delete s; delete s;
return ent_result; return ent_result;
%} %}
%%{
// ### NOTE: while d2i_X509 does not take a const u_char** pointer,
// here we assume d2i_X509 does not write to <data>, so it is safe to
// convert data to a non-const pointer. Could some X509 guru verify
// this?
X509* d2i_X509_(X509** px, const u_char** in, int len)
{
// This is the indexed map of X509 certificate stores.
static map<BroString, X509_STORE*> 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);
%}

View file

@ -270,7 +270,7 @@ event ssl_extension%(c: connection, code: count, val: string%);
event ssl_established%(c: connection%); event ssl_established%(c: connection%);
event ssl_alert%(c: connection, level: count, desc: count%); 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_extension%(c: connection, data: string%);
event 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_validated%(c: connection%);

View file

@ -263,7 +263,7 @@ refine analyzer SSLAnalyzer += {
if ( ! pTemp ) if ( ! pTemp )
{ {
// X509_V_UNABLE_TO_DECRYPT_CERT_SIGNATURE // X509_V_UNABLE_TO_DECRYPT_CERT_SIGNATURE
certificate_error(4); certificate_error(ERR_get_error());
return false; return false;
} }
@ -271,18 +271,26 @@ refine analyzer SSLAnalyzer += {
char tmp[256]; char tmp[256];
pX509Cert->Assign(0, new Val((uint64) X509_get_version(pTemp), TYPE_COUNT)); 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)); 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)); BIO *bio = BIO_new(BIO_s_mem());
X509_NAME_oneline(X509_get_issuer_name(pTemp), tmp, sizeof tmp); X509_NAME_print_ex(bio, X509_get_subject_name(pTemp), 0, XN_FLAG_RFC2253);
pX509Cert->Assign(3, new StringVal(tmp)); 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(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(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(), BifEvent::generate_x509_certificate(bro_analyzer_, bro_analyzer_->Conn(),
pX509Cert, pX509Cert,
${rec.is_orig}, ! ${rec.is_orig},
i, certificates->size()-1); i, certificates->size()-1,
der_cert);
// Are there any X509 extensions? // Are there any X509 extensions?
if ( x509_extension && X509_get_ext_count(pTemp) > 0 ) if ( x509_extension && X509_get_ext_count(pTemp) > 0 )
{ {

View file

@ -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;
%}
};