mirror of
https://github.com/zeek/zeek.git
synced 2025-10-13 12:08:20 +00:00
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:
parent
297a2cb9c5
commit
a57e50da35
4 changed files with 114 additions and 95 deletions
95
src/bro.bif
95
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 <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);
|
||||
%}
|
||||
|
||||
|
|
@ -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%);
|
||||
|
|
|
@ -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,17 +271,25 @@ 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);
|
||||
! ${rec.is_orig},
|
||||
i, certificates->size()-1,
|
||||
der_cert);
|
||||
|
||||
// Are there any X509 extensions?
|
||||
if ( x509_extension && X509_get_ext_count(pTemp) > 0 )
|
||||
|
|
|
@ -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;
|
||||
%}
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue