From df552ca87d842ca66a9bd234f0feea2ac8174929 Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Thu, 19 Sep 2013 14:35:11 -0700 Subject: [PATCH] parse out extension. One event for general extensions (just returns the openssl-parsed string-value), one event for basicconstraints (is a certificate a CA or not) and one event for subject-alternative-names (only DNS parts). --- scripts/base/files/x509/main.bro | 15 ++ scripts/base/init-bare.bro | 22 ++- src/analyzer/protocol/ssl/SSL.h | 2 +- src/analyzer/protocol/ssl/events.bif | 2 +- src/analyzer/protocol/ssl/ssl-analyzer.pac | 28 ---- src/file_analysis/analyzer/x509/X509.cc | 153 ++++++++++++++++++++- src/file_analysis/analyzer/x509/X509.h | 6 + src/file_analysis/analyzer/x509/events.bif | 3 + src/file_analysis/analyzer/x509/types.bif | 4 + 9 files changed, 202 insertions(+), 33 deletions(-) diff --git a/scripts/base/files/x509/main.bro b/scripts/base/files/x509/main.bro index 205b8fbd25..458a389934 100644 --- a/scripts/base/files/x509/main.bro +++ b/scripts/base/files/x509/main.bro @@ -12,3 +12,18 @@ event x509_cert(f: fa_file, cert: X509::Certificate) print cert; } +event x509_extension(f: fa_file, ext: X509::Extension) +{ +print ext; +} + +event x509_ext_basic_constraints(f: fa_file, ext: X509::BasicConstraints) +{ +print ext; +} + +event x509_ext_subject_alternative_name(f: fa_file, ext: X509::SubjectAlternativeName) +{ +print ext; +} + diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 5d7914dc6b..c8e2e52b8a 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -2736,9 +2736,27 @@ export { key_length: count &optional; ##< key-length in bits exponent: string &optional; ##< exponent, if RSA-certificate curve: string &optional; ##< curve, if EC-certificate - ca: bool &optional; ##< indicates the CA value in the X509v3 BasicConstraints extension - path_len: count &optional; ##< indicates the path_length value in the X509v3 BasicConstraints extension + #ca: bool &optional; ##< indicates the CA value in the X509v3 BasicConstraints extension + #path_len: count &optional; ##< indicates the path_length value in the X509v3 BasicConstraints extension }; + + type X509::Extension: record { + name: string; ##< long name of extension. oid if name not known + short_name: string &optional; ##< short name of extension if known. + oid: string; ##< oid of extension + critical: bool; ##< true if extension is critical + value: string; ##< extension content parsed to string for known extensions. Raw data otherwise. + }; + + type X509::BasicConstraints: record { + ca: bool; ##< CA flag set? + path_len: count &optional; + }; + + type X509::SubjectAlternativeName: record { + names: vector of string; + }; + } module SOCKS; diff --git a/src/analyzer/protocol/ssl/SSL.h b/src/analyzer/protocol/ssl/SSL.h index 6423d1b155..5749066780 100644 --- a/src/analyzer/protocol/ssl/SSL.h +++ b/src/analyzer/protocol/ssl/SSL.h @@ -28,7 +28,7 @@ public: { return ( ssl_client_hello || ssl_server_hello || ssl_established || ssl_extension || ssl_alert || - x509_certificate || x509_extension || x509_error ); + x509_certificate || x509_error ); } protected: diff --git a/src/analyzer/protocol/ssl/events.bif b/src/analyzer/protocol/ssl/events.bif index 3d0c7e9d6a..b7586561fc 100644 --- a/src/analyzer/protocol/ssl/events.bif +++ b/src/analyzer/protocol/ssl/events.bif @@ -176,7 +176,7 @@ event x509_certificate%(c: connection, is_orig: bool, cert: X509, chain_idx: cou ## ## .. bro:see:: ssl_alert ssl_client_hello ssl_established ssl_extension ## ssl_server_hello x509_certificate x509_error x509_verify -event x509_extension%(c: connection, is_orig: bool, data: string%); +#event x509_extension%(c: connection, is_orig: bool, data: string%); ## Generated when errors occur during parsing an X509 certificate. ## diff --git a/src/analyzer/protocol/ssl/ssl-analyzer.pac b/src/analyzer/protocol/ssl/ssl-analyzer.pac index 4cd7599ef7..43e2ac5c73 100644 --- a/src/analyzer/protocol/ssl/ssl-analyzer.pac +++ b/src/analyzer/protocol/ssl/ssl-analyzer.pac @@ -296,34 +296,6 @@ refine connection SSL_Conn += { i, certificates->size(), der_cert); - // Are there any X509 extensions? - //printf("Number of x509 extensions: %d\n", X509_get_ext_count(pTemp)); - if ( x509_extension && X509_get_ext_count(pTemp) > 0 ) - { - int num_ext = X509_get_ext_count(pTemp); - for ( int k = 0; k < num_ext; ++k ) - { - unsigned char *pBuffer = 0; - int length = 0; - - X509_EXTENSION* ex = X509_get_ext(pTemp, k); - if (ex) - { - ASN1_STRING *pString = X509_EXTENSION_get_data(ex); - length = ASN1_STRING_to_UTF8(&pBuffer, pString); - //i2t_ASN1_OBJECT(&pBuffer, length, obj) - // printf("extension length: %d\n", length); - // -1 indicates an error. - if ( length >= 0 ) - { - StringVal* value = new StringVal(length, (char*)pBuffer); - BifEvent::generate_x509_extension(bro_analyzer(), - bro_analyzer()->Conn(), ${rec.is_orig}, value); - } - OPENSSL_free(pBuffer); - } - } - } X509_free(pTemp); } } diff --git a/src/file_analysis/analyzer/x509/X509.cc b/src/file_analysis/analyzer/x509/X509.cc index 78d746ac9b..3d7871be9a 100644 --- a/src/file_analysis/analyzer/x509/X509.cc +++ b/src/file_analysis/analyzer/x509/X509.cc @@ -47,6 +47,27 @@ bool file_analysis::X509::EndOfFile() return false; } + ParseCertificate(ssl_cert); + + // after parsing the certificate - parse the extensions... + + int num_ext = X509_get_ext_count(ssl_cert); + for ( int k = 0; k < num_ext; ++k ) + { + X509_EXTENSION* ex = X509_get_ext(ssl_cert, k); + if ( !ex ) + continue; + + ParseExtension(ex); + } + + X509_free(ssl_cert); + + return false; + } + +void file_analysis::X509::ParseCertificate(::X509* ssl_cert) + { char buf[256]; // we need a buffer for some of the openssl functions memset(buf, 0, 256); @@ -117,8 +138,138 @@ bool file_analysis::X509::EndOfFile() vl->append(pX509Cert); mgr.QueueEvent(x509_cert, vl); + } + +void file_analysis::X509::ParseExtension(X509_EXTENSION* ex) + { + char name[256]; + char oid[256]; + + ASN1_OBJECT* ext_asn = X509_EXTENSION_get_object(ex); + const char* short_name = OBJ_nid2sn(OBJ_obj2nid(ext_asn)); + + OBJ_obj2txt(name, 255, ext_asn, 0); + OBJ_obj2txt(oid, 255, ext_asn, 1); + + int critical = 0; + if ( X509_EXTENSION_get_critical(ex) != 0 ) + critical = 1; + + BIO *bio = BIO_new(BIO_s_mem()); + if(!X509V3_EXT_print(bio, ex, 0, 0)) + M_ASN1_OCTET_STRING_print(bio,ex->value); - return false; + BIO_flush(bio); + int length = BIO_pending(bio); + char *buffer = new char[length]; + BIO_read(bio, (void*)buffer, length); + StringVal* ext_val = new StringVal(length, buffer); + delete(buffer); + BIO_free_all(bio); + + RecordVal* pX509Ext = new RecordVal(BifType::Record::X509::Extension); + pX509Ext->Assign(0, new StringVal(name)); + if ( short_name and strlen(short_name) > 0 ) + pX509Ext->Assign(1, new StringVal(short_name)); + pX509Ext->Assign(2, new StringVal(oid)); + pX509Ext->Assign(3, new Val(critical, TYPE_BOOL)); + pX509Ext->Assign(4, ext_val); + + // send off generic extension event + // + // and then look if we have a specialized event for the extension we just + // parsed. And if we have it, we send the specialized event on top of the + // generic event that we just had. I know, that is... kind of not nice, + // but I am not sure if there is a better way to do it... + val_list* vl = new val_list(); + vl->append(GetFile()->GetVal()->Ref()); + vl->append(pX509Ext); + + mgr.QueueEvent(x509_extension, vl); + + + // look if we have a specialized handler for this event... + if ( OBJ_obj2nid(ext_asn) == NID_basic_constraints ) + ParseBasicConstraints(ex); + else if ( OBJ_obj2nid(ext_asn) == NID_subject_alt_name ) + ParseSAN(ex); + + + + } +void file_analysis::X509::ParseBasicConstraints(X509_EXTENSION* ex) + { + assert(OBJ_obj2nid(X509_EXTENSION_get_object(ex)) == NID_basic_constraints); + + RecordVal* pBasicConstraint = new RecordVal(BifType::Record::X509::BasicConstraints); + BASIC_CONSTRAINTS *constr = (BASIC_CONSTRAINTS *) X509V3_EXT_d2i(ex); + if ( !constr ) + { + reporter->Error("Certificate with invalid BasicConstraint"); + } + else + { + pBasicConstraint->Assign(0, new Val(constr->ca ? 1 : 0, TYPE_BOOL)); + if ( constr->pathlen ) { + pBasicConstraint->Assign(1, new Val((int32_t) ASN1_INTEGER_get(constr->pathlen), TYPE_COUNT)); + } + val_list* vl = new val_list(); + vl->append(GetFile()->GetVal()->Ref()); + vl->append(pBasicConstraint); + + mgr.QueueEvent(x509_ext_basic_constraints, vl); + + } + + } + +void file_analysis::X509::ParseSAN(X509_EXTENSION* ext) + { + assert(OBJ_obj2nid(X509_EXTENSION_get_object(ext)) == NID_subject_alt_name); + + GENERAL_NAMES *altname = (GENERAL_NAMES*)X509V3_EXT_d2i(ext); + if ( !altname ) + { + reporter->Error("could not parse subject alternative names"); + return; + } + + VectorVal* names = new VectorVal(internal_type("string_vec")->AsVectorType()); + + int j = 0; + for ( int i = 0; i < sk_GENERAL_NAME_num(altname); i++ ) + { + GENERAL_NAME *gen = sk_GENERAL_NAME_value(altname, i); + assert(gen); + + if ( gen->type == GEN_DNS ) + { + if (ASN1_STRING_type(gen->d.ia5) != V_ASN1_IA5STRING) + { + reporter->Error("DNS-field does not contain an IA5String"); + continue; + } + const char* name = (const char*) ASN1_STRING_data(gen->d.ia5); + StringVal* bs = new StringVal(name); + names->Assign(j, bs); + j++; + } + else + { + // we should perhaps sometime parse out ip-addresses + reporter->Error("Subject alternative name contained non-dns fields"); + continue; + } + } + + RecordVal* pSan = new RecordVal(BifType::Record::X509::SubjectAlternativeName); + pSan->Assign(0, names); + + val_list* vl = new val_list(); + vl->append(GetFile()->GetVal()->Ref()); + vl->append(pSan); + + mgr.QueueEvent(x509_ext_basic_constraints, vl); } StringVal* file_analysis::X509::key_curve(EVP_PKEY *key) diff --git a/src/file_analysis/analyzer/x509/X509.h b/src/file_analysis/analyzer/x509/X509.h index ce74190b69..cc0131afac 100644 --- a/src/file_analysis/analyzer/x509/X509.h +++ b/src/file_analysis/analyzer/x509/X509.h @@ -7,6 +7,7 @@ #include "../File.h" #include "Analyzer.h" +#include #include namespace file_analysis { @@ -30,6 +31,11 @@ private: static StringVal* key_curve(EVP_PKEY *key); static unsigned int key_length(EVP_PKEY *key); + void ParseCertificate(::X509* ssl_cert); + void ParseExtension(X509_EXTENSION* ex); + void ParseBasicConstraints(X509_EXTENSION* ex); + void ParseSAN(X509_EXTENSION* ex); + std::string cert_data; }; diff --git a/src/file_analysis/analyzer/x509/events.bif b/src/file_analysis/analyzer/x509/events.bif index 3c3049559d..148d09ec00 100644 --- a/src/file_analysis/analyzer/x509/events.bif +++ b/src/file_analysis/analyzer/x509/events.bif @@ -1 +1,4 @@ event x509_cert%(f: fa_file, cert: X509::Certificate%); +event x509_extension%(f: fa_file, ext: X509::Extension%); +event x509_ext_basic_constraints%(f: fa_file, ext: X509::BasicConstraints%); +event x509_ext_subject_alternative_name%(f: fa_file, ext: X509::SubjectAlternativeName%); diff --git a/src/file_analysis/analyzer/x509/types.bif b/src/file_analysis/analyzer/x509/types.bif index 9e4fd48420..49a915c7fc 100644 --- a/src/file_analysis/analyzer/x509/types.bif +++ b/src/file_analysis/analyzer/x509/types.bif @@ -1 +1,5 @@ type X509::Certificate: record; +type X509::Extension: record; +type X509::BasicConstraints: record; +type X509::SubjectAlternativeName: record; +