mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 22:58:20 +00:00
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).
This commit is contained in:
parent
e5a589dbfe
commit
df552ca87d
9 changed files with 202 additions and 33 deletions
|
@ -12,3 +12,18 @@ event x509_cert(f: fa_file, cert: X509::Certificate)
|
||||||
print cert;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -2736,9 +2736,27 @@ export {
|
||||||
key_length: count &optional; ##< key-length in bits
|
key_length: count &optional; ##< key-length in bits
|
||||||
exponent: string &optional; ##< exponent, if RSA-certificate
|
exponent: string &optional; ##< exponent, if RSA-certificate
|
||||||
curve: string &optional; ##< curve, if EC-certificate
|
curve: string &optional; ##< curve, if EC-certificate
|
||||||
ca: bool &optional; ##< indicates the CA 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
|
#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;
|
module SOCKS;
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
{
|
{
|
||||||
return ( ssl_client_hello || ssl_server_hello ||
|
return ( ssl_client_hello || ssl_server_hello ||
|
||||||
ssl_established || ssl_extension || ssl_alert ||
|
ssl_established || ssl_extension || ssl_alert ||
|
||||||
x509_certificate || x509_extension || x509_error );
|
x509_certificate || x509_error );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -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
|
## .. bro:see:: ssl_alert ssl_client_hello ssl_established ssl_extension
|
||||||
## ssl_server_hello x509_certificate x509_error x509_verify
|
## 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.
|
## Generated when errors occur during parsing an X509 certificate.
|
||||||
##
|
##
|
||||||
|
|
|
@ -296,34 +296,6 @@ refine connection SSL_Conn += {
|
||||||
i, certificates->size(),
|
i, certificates->size(),
|
||||||
der_cert);
|
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);
|
X509_free(pTemp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,27 @@ bool file_analysis::X509::EndOfFile()
|
||||||
return false;
|
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
|
char buf[256]; // we need a buffer for some of the openssl functions
|
||||||
memset(buf, 0, 256);
|
memset(buf, 0, 256);
|
||||||
|
|
||||||
|
@ -117,8 +138,138 @@ bool file_analysis::X509::EndOfFile()
|
||||||
vl->append(pX509Cert);
|
vl->append(pX509Cert);
|
||||||
|
|
||||||
mgr.QueueEvent(x509_cert, vl);
|
mgr.QueueEvent(x509_cert, vl);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
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);
|
||||||
|
|
||||||
|
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)
|
StringVal* file_analysis::X509::key_curve(EVP_PKEY *key)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "../File.h"
|
#include "../File.h"
|
||||||
#include "Analyzer.h"
|
#include "Analyzer.h"
|
||||||
|
|
||||||
|
#include <openssl/x509.h>
|
||||||
#include <openssl/asn1.h>
|
#include <openssl/asn1.h>
|
||||||
|
|
||||||
namespace file_analysis {
|
namespace file_analysis {
|
||||||
|
@ -30,6 +31,11 @@ private:
|
||||||
static StringVal* key_curve(EVP_PKEY *key);
|
static StringVal* key_curve(EVP_PKEY *key);
|
||||||
static unsigned int key_length(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;
|
std::string cert_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1 +1,4 @@
|
||||||
event x509_cert%(f: fa_file, cert: X509::Certificate%);
|
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%);
|
||||||
|
|
|
@ -1 +1,5 @@
|
||||||
type X509::Certificate: record;
|
type X509::Certificate: record;
|
||||||
|
type X509::Extension: record;
|
||||||
|
type X509::BasicConstraints: record;
|
||||||
|
type X509::SubjectAlternativeName: record;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue