mirror of
https://github.com/zeek/zeek.git
synced 2025-10-05 16:18:19 +00:00
OCSP: parse signed certificate timestamps
With this change, we also parse signed certificate timestamps from OCSP replies. This introduces a common base class between the OCSP and X509 analyzer, which now share a bit of common code. The event for signed certificate timestamps is raised by both and thus renamed do: x509_ocsp_ext_signed_certificate_timestamp
This commit is contained in:
parent
b061a5db1a
commit
c550521221
11 changed files with 404 additions and 391 deletions
|
@ -1,157 +0,0 @@
|
||||||
static double GetTimeFromAsn1(const ASN1_TIME* atime, const char* arg_fid, Reporter* reporter)
|
|
||||||
{
|
|
||||||
const char *fid = arg_fid ? arg_fid : "";
|
|
||||||
time_t lResult = 0;
|
|
||||||
|
|
||||||
char lBuffer[26];
|
|
||||||
char* pBuffer = lBuffer;
|
|
||||||
|
|
||||||
const char *pString = (const char *) atime->data;
|
|
||||||
unsigned int remaining = atime->length;
|
|
||||||
|
|
||||||
if ( atime->type == V_ASN1_UTCTIME )
|
|
||||||
{
|
|
||||||
if ( remaining < 11 || remaining > 17 )
|
|
||||||
{
|
|
||||||
reporter->Weird(fmt("Could not parse time in X509 certificate (fuid %s) -- UTCTime has wrong length", fid));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pString[remaining-1] != 'Z' )
|
|
||||||
{
|
|
||||||
// not valid according to RFC 2459 4.1.2.5.1
|
|
||||||
reporter->Weird(fmt("Could not parse UTC time in non-YY-format in X509 certificate (x509 %s)", fid));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// year is first two digits in YY format. Buffer expects YYYY format.
|
|
||||||
if ( pString[0] < '5' ) // RFC 2459 4.1.2.5.1
|
|
||||||
{
|
|
||||||
*(pBuffer++) = '2';
|
|
||||||
*(pBuffer++) = '0';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*(pBuffer++) = '1';
|
|
||||||
*(pBuffer++) = '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(pBuffer, pString, 10);
|
|
||||||
pBuffer += 10;
|
|
||||||
pString += 10;
|
|
||||||
remaining -= 10;
|
|
||||||
}
|
|
||||||
else if ( atime->type == V_ASN1_GENERALIZEDTIME )
|
|
||||||
{
|
|
||||||
// generalized time. We apparently ignore the YYYYMMDDHH case
|
|
||||||
// for now and assume we always have minutes and seconds.
|
|
||||||
// This should be ok because it is specified as a requirement in RFC 2459 4.1.2.5.2
|
|
||||||
|
|
||||||
if ( remaining < 12 || remaining > 23 )
|
|
||||||
{
|
|
||||||
reporter->Weird(fmt("Could not parse time in X509 certificate (fuid %s) -- Generalized time has wrong length", fid));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(pBuffer, pString, 12);
|
|
||||||
pBuffer += 12;
|
|
||||||
pString += 12;
|
|
||||||
remaining -= 12;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
reporter->Weird(fmt("Invalid time type in X509 certificate (fuid %s)", fid));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (remaining == 0) || (*pString == 'Z') || (*pString == '-') || (*pString == '+') )
|
|
||||||
{
|
|
||||||
*(pBuffer++) = '0';
|
|
||||||
*(pBuffer++) = '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( remaining >= 2 )
|
|
||||||
{
|
|
||||||
*(pBuffer++) = *(pString++);
|
|
||||||
*(pBuffer++) = *(pString++);
|
|
||||||
|
|
||||||
remaining -= 2;
|
|
||||||
|
|
||||||
// Skip any fractional seconds...
|
|
||||||
if ( (remaining > 0) && (*pString == '.') )
|
|
||||||
{
|
|
||||||
pString++;
|
|
||||||
remaining--;
|
|
||||||
|
|
||||||
while ( (remaining > 0) && (*pString >= '0') && (*pString <= '9') )
|
|
||||||
{
|
|
||||||
pString++;
|
|
||||||
remaining--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
reporter->Weird(fmt("Could not parse time in X509 certificate (fuid %s) -- additional char after time", fid));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*(pBuffer++) = 'Z';
|
|
||||||
*(pBuffer++) = '\0';
|
|
||||||
|
|
||||||
time_t lSecondsFromUTC;
|
|
||||||
|
|
||||||
if ( remaining == 0 || *pString == 'Z' )
|
|
||||||
lSecondsFromUTC = 0;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( remaining < 5 )
|
|
||||||
{
|
|
||||||
reporter->Weird(fmt("Could not parse time in X509 certificate (fuid %s) -- not enough bytes remaining for offset", fid));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((*pString != '+') && (*pString != '-'))
|
|
||||||
{
|
|
||||||
reporter->Weird(fmt("Could not parse time in X509 certificate (fuid %s) -- unknown offset type", fid));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lSecondsFromUTC = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60;
|
|
||||||
lSecondsFromUTC += (pString[3] - '0') * 10 + (pString[4] - '0');
|
|
||||||
|
|
||||||
if (*pString == '-')
|
|
||||||
lSecondsFromUTC = -lSecondsFromUTC;
|
|
||||||
}
|
|
||||||
|
|
||||||
tm lTime;
|
|
||||||
lTime.tm_sec = ((lBuffer[12] - '0') * 10) + (lBuffer[13] - '0');
|
|
||||||
lTime.tm_min = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0');
|
|
||||||
lTime.tm_hour = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0');
|
|
||||||
lTime.tm_mday = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0');
|
|
||||||
lTime.tm_mon = (((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0')) - 1;
|
|
||||||
lTime.tm_year = (lBuffer[0] - '0') * 1000 + (lBuffer[1] - '0') * 100 + ((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0');
|
|
||||||
|
|
||||||
if ( lTime.tm_year > 1900)
|
|
||||||
lTime.tm_year -= 1900;
|
|
||||||
|
|
||||||
lTime.tm_wday = 0;
|
|
||||||
lTime.tm_yday = 0;
|
|
||||||
lTime.tm_isdst = 0; // No DST adjustment requested
|
|
||||||
|
|
||||||
lResult = mktime(&lTime);
|
|
||||||
|
|
||||||
if ( lResult )
|
|
||||||
{
|
|
||||||
if ( lTime.tm_isdst != 0 )
|
|
||||||
lResult -= 3600; // mktime may adjust for DST (OS dependent)
|
|
||||||
|
|
||||||
lResult += lSecondsFromUTC;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
lResult = 0;
|
|
||||||
|
|
||||||
return lResult;
|
|
||||||
}
|
|
|
@ -5,7 +5,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${CMAKE_CURRENT_BINARY_DIR})
|
${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
bro_plugin_begin(Bro X509)
|
bro_plugin_begin(Bro X509)
|
||||||
bro_plugin_cc(X509.cc OCSP.cc Plugin.cc)
|
bro_plugin_cc(X509Common.cc X509.cc OCSP.cc Plugin.cc)
|
||||||
bro_plugin_bif(events.bif types.bif functions.bif ocsp_events.bif)
|
bro_plugin_bif(events.bif types.bif functions.bif ocsp_events.bif)
|
||||||
bro_plugin_pac(x509-extension.pac x509-signed_certificate_timestamp.pac)
|
bro_plugin_pac(x509-extension.pac x509-signed_certificate_timestamp.pac)
|
||||||
bro_plugin_end()
|
bro_plugin_end()
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include <openssl/opensslconf.h>
|
#include <openssl/opensslconf.h>
|
||||||
|
|
||||||
#include "file_analysis/analyzer/x509/X509.h"
|
#include "file_analysis/analyzer/x509/X509.h"
|
||||||
#include "Asn1Time.h"
|
|
||||||
|
|
||||||
// helper function of sk_X509_value to avoid namespace problem
|
// helper function of sk_X509_value to avoid namespace problem
|
||||||
// sk_X509_value(X,Y) = > SKM_sk_value(X509,X,Y)
|
// sk_X509_value(X,Y) = > SKM_sk_value(X509,X,Y)
|
||||||
|
@ -87,7 +86,7 @@ file_analysis::Analyzer* OCSP::InstantiateReply(RecordVal* args, File* file)
|
||||||
}
|
}
|
||||||
|
|
||||||
file_analysis::OCSP::OCSP(RecordVal* args, file_analysis::File* file, bool arg_request)
|
file_analysis::OCSP::OCSP(RecordVal* args, file_analysis::File* file, bool arg_request)
|
||||||
: file_analysis::Analyzer(file_mgr->GetComponentTag("OCSP"), args, file), request(arg_request)
|
: file_analysis::X509Common::X509Common(file_mgr->GetComponentTag("OCSP"), args, file), request(arg_request)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,7 +294,7 @@ void file_analysis::OCSP::ParseResponse(OCSP_RESPVal *resp_val, const char* fid)
|
||||||
if ( ! ex )
|
if ( ! ex )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ParseExtension(ex, false);
|
ParseExtension(ex, ocsp_extension, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,63 +333,23 @@ void file_analysis::OCSP::ParseResponse(OCSP_RESPVal *resp_val, const char* fid)
|
||||||
if ( ! ex )
|
if ( ! ex )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ParseExtension(ex, true);
|
ParseExtension(ex, ocsp_extension, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
clean_up:
|
clean_up:
|
||||||
if (basic_resp)
|
if (basic_resp)
|
||||||
OCSP_BASICRESP_free(basic_resp);
|
OCSP_BASICRESP_free(basic_resp);
|
||||||
BIO_free(bio);
|
BIO_free(bio);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a near copy from X509
|
void file_analysis::OCSP::ParseExtensionsSpecific(X509_EXTENSION* ex, bool global, ASN1_OBJECT* ext_asn, const char* oid)
|
||||||
void file_analysis::OCSP::ParseExtension(X509_EXTENSION* ex, bool global)
|
|
||||||
{
|
{
|
||||||
char name[256];
|
#ifdef NID_ct_cert_scts
|
||||||
char oid[256];
|
if ( OBJ_obj2nid(ext_asn) == NID_ct_cert_scts || OBJ_obj2nid(ext_asn) == NID_ct_precert_scts )
|
||||||
|
#else
|
||||||
ASN1_OBJECT* ext_asn = X509_EXTENSION_get_object(ex);
|
if ( strcmp(oid, "1.3.6.1.4.1.11129.2.4.2") == 0 || strcmp(oid, "1.3.6.1.4.1.11129.2.4.4") == 0 )
|
||||||
const char* short_name = OBJ_nid2sn(OBJ_obj2nid(ext_asn));
|
#endif
|
||||||
|
ParseSignedCertificateTimestamps(ex);
|
||||||
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);
|
|
||||||
|
|
||||||
StringVal* ext_val = X509::GetExtensionFromBIO(bio);
|
|
||||||
|
|
||||||
if ( ! ext_val )
|
|
||||||
ext_val = new StringVal(0, "");
|
|
||||||
|
|
||||||
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);
|
|
||||||
vl->append(new Val(global ? 1 : 0, TYPE_BOOL));
|
|
||||||
|
|
||||||
mgr.QueueEvent(ocsp_extension, vl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OCSP_RESPVal::OCSP_RESPVal(OCSP_RESPONSE* arg_ocsp_resp) : OpaqueVal(ocsp_resp_opaque_type)
|
OCSP_RESPVal::OCSP_RESPVal(OCSP_RESPONSE* arg_ocsp_resp) : OpaqueVal(ocsp_resp_opaque_type)
|
||||||
|
|
|
@ -8,20 +8,19 @@
|
||||||
#include "Val.h"
|
#include "Val.h"
|
||||||
#include "../File.h"
|
#include "../File.h"
|
||||||
#include "Analyzer.h"
|
#include "Analyzer.h"
|
||||||
|
#include "X509Common.h"
|
||||||
|
|
||||||
#include <openssl/ocsp.h>
|
#include <openssl/ocsp.h>
|
||||||
#include <openssl/x509.h>
|
|
||||||
#include <openssl/asn1.h>
|
|
||||||
|
|
||||||
namespace file_analysis {
|
namespace file_analysis {
|
||||||
|
|
||||||
class OCSP_RESPVal;
|
class OCSP_RESPVal;
|
||||||
|
|
||||||
class OCSP : public file_analysis::Analyzer {
|
class OCSP : public file_analysis::X509Common {
|
||||||
public:
|
public:
|
||||||
virtual bool DeliverStream(const u_char* data, uint64 len);
|
bool DeliverStream(const u_char* data, uint64 len) override;
|
||||||
virtual bool Undelivered(uint64 offset, uint64 len);
|
bool Undelivered(uint64 offset, uint64 len) override;
|
||||||
virtual bool EndOfFile();
|
bool EndOfFile() override;
|
||||||
|
|
||||||
static file_analysis::Analyzer* InstantiateRequest(RecordVal* args, File* file);
|
static file_analysis::Analyzer* InstantiateRequest(RecordVal* args, File* file);
|
||||||
static file_analysis::Analyzer* InstantiateReply(RecordVal* args, File* file);
|
static file_analysis::Analyzer* InstantiateReply(RecordVal* args, File* file);
|
||||||
|
@ -32,7 +31,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
void ParseResponse(OCSP_RESPVal *, const char* fid = 0);
|
void ParseResponse(OCSP_RESPVal *, const char* fid = 0);
|
||||||
void ParseRequest(OCSP_REQUEST *, const char* fid = 0);
|
void ParseRequest(OCSP_REQUEST *, const char* fid = 0);
|
||||||
void ParseExtension(X509_EXTENSION*, bool global);
|
void ParseExtensionsSpecific(X509_EXTENSION* ex, bool, ASN1_OBJECT*, const char*) override;
|
||||||
|
|
||||||
std::string ocsp_data;
|
std::string ocsp_data;
|
||||||
bool request = false; // true if ocsp request, false if reply
|
bool request = false; // true if ocsp request, false if reply
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
#include "X509.h"
|
#include "X509.h"
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "x509-extension_pac.h"
|
|
||||||
|
|
||||||
#include "events.bif.h"
|
#include "events.bif.h"
|
||||||
#include "types.bif.h"
|
#include "types.bif.h"
|
||||||
|
@ -17,14 +16,12 @@
|
||||||
#include <openssl/opensslconf.h>
|
#include <openssl/opensslconf.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
|
||||||
#include "Asn1Time.h"
|
|
||||||
|
|
||||||
using namespace file_analysis;
|
using namespace file_analysis;
|
||||||
|
|
||||||
IMPLEMENT_SERIAL(X509Val, SER_X509_VAL);
|
IMPLEMENT_SERIAL(X509Val, SER_X509_VAL);
|
||||||
|
|
||||||
file_analysis::X509::X509(RecordVal* args, file_analysis::File* file)
|
file_analysis::X509::X509(RecordVal* args, file_analysis::File* file)
|
||||||
: file_analysis::Analyzer(file_mgr->GetComponentTag("X509"), args, file)
|
: file_analysis::X509Common::X509Common(file_mgr->GetComponentTag("X509"), args, file)
|
||||||
{
|
{
|
||||||
cert_data.clear();
|
cert_data.clear();
|
||||||
}
|
}
|
||||||
|
@ -75,7 +72,7 @@ bool file_analysis::X509::EndOfFile()
|
||||||
if ( ! ex )
|
if ( ! ex )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ParseExtension(ex);
|
ParseExtension(ex, x509_extension, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// X509_free(ssl_cert); We do _not_ free the certificate here. It is refcounted
|
// X509_free(ssl_cert); We do _not_ free the certificate here. It is refcounted
|
||||||
|
@ -208,152 +205,6 @@ RecordVal* file_analysis::X509::ParseCertificate(X509Val* cert_val, const char*
|
||||||
return pX509Cert;
|
return pX509Cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringVal* file_analysis::X509::GetExtensionFromBIO(BIO* bio)
|
|
||||||
{
|
|
||||||
BIO_flush(bio);
|
|
||||||
ERR_clear_error();
|
|
||||||
int length = BIO_pending(bio);
|
|
||||||
|
|
||||||
if ( ERR_peek_error() != 0 )
|
|
||||||
{
|
|
||||||
char tmp[120];
|
|
||||||
ERR_error_string_n(ERR_get_error(), tmp, sizeof(tmp));
|
|
||||||
reporter->Weird(fmt("X509::GetExtensionFromBIO: %s", tmp));
|
|
||||||
BIO_free_all(bio);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( length == 0 )
|
|
||||||
{
|
|
||||||
BIO_free_all(bio);
|
|
||||||
return new StringVal("");
|
|
||||||
}
|
|
||||||
|
|
||||||
char* buffer = (char*) malloc(length);
|
|
||||||
|
|
||||||
if ( ! buffer )
|
|
||||||
{
|
|
||||||
// Just emit an error here and try to continue instead of aborting
|
|
||||||
// because it's unclear the length value is very reliable.
|
|
||||||
reporter->Error("X509::GetExtensionFromBIO malloc(%d) failed", length);
|
|
||||||
BIO_free_all(bio);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
BIO_read(bio, (void*) buffer, length);
|
|
||||||
StringVal* ext_val = new StringVal(length, buffer);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
BIO_free_all(bio);
|
|
||||||
|
|
||||||
return ext_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is nearly replicated in the OCSP analyzer
|
|
||||||
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);
|
|
||||||
|
|
||||||
StringVal* ext_val = GetExtensionFromBIO(bio);
|
|
||||||
|
|
||||||
if ( ! ext_val )
|
|
||||||
ext_val = new StringVal(0, "");
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
#ifdef NID_ct_cert_scts
|
|
||||||
else if ( OBJ_obj2nid(ext_asn) == NID_ct_cert_scts || OBJ_obj2nid(ext_asn) == NID_ct_precert_scts )
|
|
||||||
#else
|
|
||||||
else if ( strcmp(oid, "1.3.6.1.4.1.11129.2.4.2") == 0 || strcmp(oid, "1.3.6.1.4.1.11129.2.4.4") == 0 )
|
|
||||||
#endif
|
|
||||||
ParseSignedCertificateTimestamps(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void file_analysis::X509::ParseSignedCertificateTimestamps(X509_EXTENSION* ext)
|
|
||||||
{
|
|
||||||
// Ok, signed certificate timestamps are a bit of an odd case out; we don't
|
|
||||||
// want to use the (basically nonexistant) OpenSSL functionality to parse them.
|
|
||||||
// Instead we have our own, self-written binpac parser to parse just them,
|
|
||||||
// which we will initialize here and tear down immediately again.
|
|
||||||
|
|
||||||
ASN1_OCTET_STRING* ext_val = X509_EXTENSION_get_data(ext);
|
|
||||||
// the octet string of the extension contains the octet string which in turn
|
|
||||||
// contains the SCT. Obviously.
|
|
||||||
|
|
||||||
unsigned char* ext_val_copy = (unsigned char*) OPENSSL_malloc(ext_val->length);
|
|
||||||
unsigned char* ext_val_second_pointer = ext_val_copy;
|
|
||||||
memcpy(ext_val_copy, ext_val->data, ext_val->length);
|
|
||||||
|
|
||||||
ASN1_OCTET_STRING* inner = d2i_ASN1_OCTET_STRING(NULL, (const unsigned char**) &ext_val_copy, ext_val->length);
|
|
||||||
if ( !inner )
|
|
||||||
{
|
|
||||||
reporter->Error("X509::ParseSignedCertificateTimestamps could not parse inner octet string");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
binpac::X509Extension::MockConnection* conn = new binpac::X509Extension::MockConnection(this);
|
|
||||||
binpac::X509Extension::SignedCertTimestampExt* interp = new binpac::X509Extension::SignedCertTimestampExt(conn);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
interp->NewData(inner->data, inner->data + inner->length);
|
|
||||||
}
|
|
||||||
catch( const binpac::Exception& e )
|
|
||||||
{
|
|
||||||
// throw a warning or sth
|
|
||||||
reporter->Error("X509::ParseSignedCertificateTimestamps could not parse SCT");
|
|
||||||
}
|
|
||||||
|
|
||||||
OPENSSL_free(ext_val_second_pointer);
|
|
||||||
|
|
||||||
interp->FlowEOF();
|
|
||||||
|
|
||||||
delete interp;
|
|
||||||
delete conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
void file_analysis::X509::ParseBasicConstraints(X509_EXTENSION* ex)
|
void file_analysis::X509::ParseBasicConstraints(X509_EXTENSION* ex)
|
||||||
{
|
{
|
||||||
assert(OBJ_obj2nid(X509_EXTENSION_get_object(ex)) == NID_basic_constraints);
|
assert(OBJ_obj2nid(X509_EXTENSION_get_object(ex)) == NID_basic_constraints);
|
||||||
|
@ -380,6 +231,23 @@ void file_analysis::X509::ParseBasicConstraints(X509_EXTENSION* ex)
|
||||||
reporter->Weird(fmt("Certificate with invalid BasicConstraint. fuid %s", GetFile()->GetID().c_str()));
|
reporter->Weird(fmt("Certificate with invalid BasicConstraint. fuid %s", GetFile()->GetID().c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void file_analysis::X509::ParseExtensionsSpecific(X509_EXTENSION* ex, bool global, ASN1_OBJECT* ext_asn, const char* oid)
|
||||||
|
{
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
#ifdef NID_ct_cert_scts
|
||||||
|
else if ( OBJ_obj2nid(ext_asn) == NID_ct_cert_scts || OBJ_obj2nid(ext_asn) == NID_ct_precert_scts )
|
||||||
|
#else
|
||||||
|
else if ( strcmp(oid, "1.3.6.1.4.1.11129.2.4.2") == 0 || strcmp(oid, "1.3.6.1.4.1.11129.2.4.4") == 0 )
|
||||||
|
#endif
|
||||||
|
ParseSignedCertificateTimestamps(ex);
|
||||||
|
}
|
||||||
|
|
||||||
void file_analysis::X509::ParseSAN(X509_EXTENSION* ext)
|
void file_analysis::X509::ParseSAN(X509_EXTENSION* ext)
|
||||||
{
|
{
|
||||||
assert(OBJ_obj2nid(X509_EXTENSION_get_object(ext)) == NID_subject_alt_name);
|
assert(OBJ_obj2nid(X509_EXTENSION_get_object(ext)) == NID_subject_alt_name);
|
||||||
|
|
|
@ -6,21 +6,18 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Val.h"
|
#include "Val.h"
|
||||||
#include "../File.h"
|
#include "X509Common.h"
|
||||||
#include "Analyzer.h"
|
|
||||||
|
|
||||||
#include <openssl/x509.h>
|
|
||||||
#include <openssl/asn1.h>
|
|
||||||
|
|
||||||
namespace file_analysis {
|
namespace file_analysis {
|
||||||
|
|
||||||
class X509Val;
|
class X509Val;
|
||||||
|
|
||||||
class X509 : public file_analysis::Analyzer {
|
class X509 : public file_analysis::X509Common {
|
||||||
public:
|
public:
|
||||||
virtual bool DeliverStream(const u_char* data, uint64 len);
|
bool DeliverStream(const u_char* data, uint64 len) override;
|
||||||
virtual bool Undelivered(uint64 offset, uint64 len);
|
bool Undelivered(uint64 offset, uint64 len) override;
|
||||||
virtual bool EndOfFile();
|
bool EndOfFile() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an X509 certificate into a \c X509::Certificate record
|
* Converts an X509 certificate into a \c X509::Certificate record
|
||||||
|
@ -40,25 +37,13 @@ public:
|
||||||
static file_analysis::Analyzer* Instantiate(RecordVal* args, File* file)
|
static file_analysis::Analyzer* Instantiate(RecordVal* args, File* file)
|
||||||
{ return new X509(args, file); }
|
{ return new X509(args, file); }
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an X509 extension value from an OpenSSL BIO to which it was
|
|
||||||
* written.
|
|
||||||
*
|
|
||||||
* @param bio the OpenSSL BIO to read. It will be freed by the function,
|
|
||||||
* including when an error occurs.
|
|
||||||
*
|
|
||||||
* @return The X509 extension value.
|
|
||||||
*/
|
|
||||||
static StringVal* GetExtensionFromBIO(BIO* bio);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
X509(RecordVal* args, File* file);
|
X509(RecordVal* args, File* file);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ParseExtension(X509_EXTENSION* ex);
|
|
||||||
void ParseBasicConstraints(X509_EXTENSION* ex);
|
void ParseBasicConstraints(X509_EXTENSION* ex);
|
||||||
void ParseSAN(X509_EXTENSION* ex);
|
void ParseSAN(X509_EXTENSION* ex);
|
||||||
void ParseSignedCertificateTimestamps(X509_EXTENSION* ext);
|
void ParseExtensionsSpecific(X509_EXTENSION* ex, bool, ASN1_OBJECT*, const char*) override;
|
||||||
|
|
||||||
std::string cert_data;
|
std::string cert_data;
|
||||||
|
|
||||||
|
|
315
src/file_analysis/analyzer/x509/X509Common.cc
Normal file
315
src/file_analysis/analyzer/x509/X509Common.cc
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
// See the file "COPYING" in the main distribution directory for copyright.
|
||||||
|
|
||||||
|
#include "X509Common.h"
|
||||||
|
#include "x509-extension_pac.h"
|
||||||
|
|
||||||
|
#include "events.bif.h"
|
||||||
|
#include "ocsp_events.bif.h"
|
||||||
|
#include "types.bif.h"
|
||||||
|
|
||||||
|
#include <openssl/x509.h>
|
||||||
|
#include <openssl/x509v3.h>
|
||||||
|
#include <openssl/asn1.h>
|
||||||
|
#include <openssl/opensslconf.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
|
||||||
|
using namespace file_analysis;
|
||||||
|
|
||||||
|
X509Common::X509Common(file_analysis::Tag arg_tag, RecordVal* arg_args, File* arg_file)
|
||||||
|
: file_analysis::Analyzer(arg_tag, arg_args, arg_file)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
double X509Common::GetTimeFromAsn1(const ASN1_TIME* atime, const char* arg_fid, Reporter* reporter)
|
||||||
|
{
|
||||||
|
const char *fid = arg_fid ? arg_fid : "";
|
||||||
|
time_t lResult = 0;
|
||||||
|
|
||||||
|
char lBuffer[26];
|
||||||
|
char* pBuffer = lBuffer;
|
||||||
|
|
||||||
|
const char *pString = (const char *) atime->data;
|
||||||
|
unsigned int remaining = atime->length;
|
||||||
|
|
||||||
|
if ( atime->type == V_ASN1_UTCTIME )
|
||||||
|
{
|
||||||
|
if ( remaining < 11 || remaining > 17 )
|
||||||
|
{
|
||||||
|
reporter->Weird(fmt("Could not parse time in X509 certificate (fuid %s) -- UTCTime has wrong length", fid));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pString[remaining-1] != 'Z' )
|
||||||
|
{
|
||||||
|
// not valid according to RFC 2459 4.1.2.5.1
|
||||||
|
reporter->Weird(fmt("Could not parse UTC time in non-YY-format in X509 certificate (x509 %s)", fid));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// year is first two digits in YY format. Buffer expects YYYY format.
|
||||||
|
if ( pString[0] < '5' ) // RFC 2459 4.1.2.5.1
|
||||||
|
{
|
||||||
|
*(pBuffer++) = '2';
|
||||||
|
*(pBuffer++) = '0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*(pBuffer++) = '1';
|
||||||
|
*(pBuffer++) = '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pBuffer, pString, 10);
|
||||||
|
pBuffer += 10;
|
||||||
|
pString += 10;
|
||||||
|
remaining -= 10;
|
||||||
|
}
|
||||||
|
else if ( atime->type == V_ASN1_GENERALIZEDTIME )
|
||||||
|
{
|
||||||
|
// generalized time. We apparently ignore the YYYYMMDDHH case
|
||||||
|
// for now and assume we always have minutes and seconds.
|
||||||
|
// This should be ok because it is specified as a requirement in RFC 2459 4.1.2.5.2
|
||||||
|
|
||||||
|
if ( remaining < 12 || remaining > 23 )
|
||||||
|
{
|
||||||
|
reporter->Weird(fmt("Could not parse time in X509 certificate (fuid %s) -- Generalized time has wrong length", fid));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pBuffer, pString, 12);
|
||||||
|
pBuffer += 12;
|
||||||
|
pString += 12;
|
||||||
|
remaining -= 12;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reporter->Weird(fmt("Invalid time type in X509 certificate (fuid %s)", fid));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (remaining == 0) || (*pString == 'Z') || (*pString == '-') || (*pString == '+') )
|
||||||
|
{
|
||||||
|
*(pBuffer++) = '0';
|
||||||
|
*(pBuffer++) = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ( remaining >= 2 )
|
||||||
|
{
|
||||||
|
*(pBuffer++) = *(pString++);
|
||||||
|
*(pBuffer++) = *(pString++);
|
||||||
|
|
||||||
|
remaining -= 2;
|
||||||
|
|
||||||
|
// Skip any fractional seconds...
|
||||||
|
if ( (remaining > 0) && (*pString == '.') )
|
||||||
|
{
|
||||||
|
pString++;
|
||||||
|
remaining--;
|
||||||
|
|
||||||
|
while ( (remaining > 0) && (*pString >= '0') && (*pString <= '9') )
|
||||||
|
{
|
||||||
|
pString++;
|
||||||
|
remaining--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reporter->Weird(fmt("Could not parse time in X509 certificate (fuid %s) -- additional char after time", fid));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(pBuffer++) = 'Z';
|
||||||
|
*(pBuffer++) = '\0';
|
||||||
|
|
||||||
|
time_t lSecondsFromUTC;
|
||||||
|
|
||||||
|
if ( remaining == 0 || *pString == 'Z' )
|
||||||
|
lSecondsFromUTC = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( remaining < 5 )
|
||||||
|
{
|
||||||
|
reporter->Weird(fmt("Could not parse time in X509 certificate (fuid %s) -- not enough bytes remaining for offset", fid));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*pString != '+') && (*pString != '-'))
|
||||||
|
{
|
||||||
|
reporter->Weird(fmt("Could not parse time in X509 certificate (fuid %s) -- unknown offset type", fid));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lSecondsFromUTC = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60;
|
||||||
|
lSecondsFromUTC += (pString[3] - '0') * 10 + (pString[4] - '0');
|
||||||
|
|
||||||
|
if (*pString == '-')
|
||||||
|
lSecondsFromUTC = -lSecondsFromUTC;
|
||||||
|
}
|
||||||
|
|
||||||
|
tm lTime;
|
||||||
|
lTime.tm_sec = ((lBuffer[12] - '0') * 10) + (lBuffer[13] - '0');
|
||||||
|
lTime.tm_min = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0');
|
||||||
|
lTime.tm_hour = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0');
|
||||||
|
lTime.tm_mday = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0');
|
||||||
|
lTime.tm_mon = (((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0')) - 1;
|
||||||
|
lTime.tm_year = (lBuffer[0] - '0') * 1000 + (lBuffer[1] - '0') * 100 + ((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0');
|
||||||
|
|
||||||
|
if ( lTime.tm_year > 1900)
|
||||||
|
lTime.tm_year -= 1900;
|
||||||
|
|
||||||
|
lTime.tm_wday = 0;
|
||||||
|
lTime.tm_yday = 0;
|
||||||
|
lTime.tm_isdst = 0; // No DST adjustment requested
|
||||||
|
|
||||||
|
lResult = mktime(&lTime);
|
||||||
|
|
||||||
|
if ( lResult )
|
||||||
|
{
|
||||||
|
if ( lTime.tm_isdst != 0 )
|
||||||
|
lResult -= 3600; // mktime may adjust for DST (OS dependent)
|
||||||
|
|
||||||
|
lResult += lSecondsFromUTC;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
lResult = 0;
|
||||||
|
|
||||||
|
return lResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_analysis::X509Common::ParseSignedCertificateTimestamps(X509_EXTENSION* ext)
|
||||||
|
{
|
||||||
|
// Ok, signed certificate timestamps are a bit of an odd case out; we don't
|
||||||
|
// want to use the (basically nonexistant) OpenSSL functionality to parse them.
|
||||||
|
// Instead we have our own, self-written binpac parser to parse just them,
|
||||||
|
// which we will initialize here and tear down immediately again.
|
||||||
|
|
||||||
|
ASN1_OCTET_STRING* ext_val = X509_EXTENSION_get_data(ext);
|
||||||
|
// the octet string of the extension contains the octet string which in turn
|
||||||
|
// contains the SCT. Obviously.
|
||||||
|
|
||||||
|
unsigned char* ext_val_copy = (unsigned char*) OPENSSL_malloc(ext_val->length);
|
||||||
|
unsigned char* ext_val_second_pointer = ext_val_copy;
|
||||||
|
memcpy(ext_val_copy, ext_val->data, ext_val->length);
|
||||||
|
|
||||||
|
ASN1_OCTET_STRING* inner = d2i_ASN1_OCTET_STRING(NULL, (const unsigned char**) &ext_val_copy, ext_val->length);
|
||||||
|
if ( !inner )
|
||||||
|
{
|
||||||
|
reporter->Error("X509::ParseSignedCertificateTimestamps could not parse inner octet string");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
binpac::X509Extension::MockConnection* conn = new binpac::X509Extension::MockConnection(this);
|
||||||
|
binpac::X509Extension::SignedCertTimestampExt* interp = new binpac::X509Extension::SignedCertTimestampExt(conn);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
interp->NewData(inner->data, inner->data + inner->length);
|
||||||
|
}
|
||||||
|
catch( const binpac::Exception& e )
|
||||||
|
{
|
||||||
|
// throw a warning or sth
|
||||||
|
reporter->Error("X509::ParseSignedCertificateTimestamps could not parse SCT");
|
||||||
|
}
|
||||||
|
|
||||||
|
OPENSSL_free(ext_val_second_pointer);
|
||||||
|
|
||||||
|
interp->FlowEOF();
|
||||||
|
|
||||||
|
delete interp;
|
||||||
|
delete conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_analysis::X509Common::ParseExtension(X509_EXTENSION* ex, EventHandlerPtr h, bool global)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
StringVal* ext_val = GetExtensionFromBIO(bio);
|
||||||
|
|
||||||
|
if ( ! ext_val )
|
||||||
|
ext_val = new StringVal(0, "");
|
||||||
|
|
||||||
|
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);
|
||||||
|
if ( h == ocsp_extension )
|
||||||
|
vl->append(new Val(global ? 1 : 0, TYPE_BOOL));
|
||||||
|
|
||||||
|
mgr.QueueEvent(h, vl);
|
||||||
|
|
||||||
|
// let individual analyzers parse more.
|
||||||
|
ParseExtensionsSpecific(ex, global, ext_asn, oid);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringVal* file_analysis::X509Common::GetExtensionFromBIO(BIO* bio)
|
||||||
|
{
|
||||||
|
BIO_flush(bio);
|
||||||
|
ERR_clear_error();
|
||||||
|
int length = BIO_pending(bio);
|
||||||
|
|
||||||
|
if ( ERR_peek_error() != 0 )
|
||||||
|
{
|
||||||
|
char tmp[120];
|
||||||
|
ERR_error_string_n(ERR_get_error(), tmp, sizeof(tmp));
|
||||||
|
reporter->Weird(fmt("X509::GetExtensionFromBIO: %s", tmp));
|
||||||
|
BIO_free_all(bio);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( length == 0 )
|
||||||
|
{
|
||||||
|
BIO_free_all(bio);
|
||||||
|
return new StringVal("");
|
||||||
|
}
|
||||||
|
|
||||||
|
char* buffer = (char*) malloc(length);
|
||||||
|
|
||||||
|
if ( ! buffer )
|
||||||
|
{
|
||||||
|
// Just emit an error here and try to continue instead of aborting
|
||||||
|
// because it's unclear the length value is very reliable.
|
||||||
|
reporter->Error("X509::GetExtensionFromBIO malloc(%d) failed", length);
|
||||||
|
BIO_free_all(bio);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BIO_read(bio, (void*) buffer, length);
|
||||||
|
StringVal* ext_val = new StringVal(length, buffer);
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
BIO_free_all(bio);
|
||||||
|
|
||||||
|
return ext_val;
|
||||||
|
}
|
44
src/file_analysis/analyzer/x509/X509Common.h
Normal file
44
src/file_analysis/analyzer/x509/X509Common.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// See the file "COPYING" in the main distribution directory for copyright.
|
||||||
|
|
||||||
|
// Common base class for the X509 and OCSP analyzer, which share a fair amount of
|
||||||
|
// code
|
||||||
|
|
||||||
|
#ifndef FILE_ANALYSIS_X509_COMMON
|
||||||
|
#define FILE_ANALYSIS_X509_COMMON
|
||||||
|
|
||||||
|
#include "../File.h"
|
||||||
|
#include "Analyzer.h"
|
||||||
|
|
||||||
|
#include <openssl/x509.h>
|
||||||
|
#include <openssl/asn1.h>
|
||||||
|
|
||||||
|
namespace file_analysis {
|
||||||
|
|
||||||
|
class X509Common : public file_analysis::Analyzer {
|
||||||
|
public:
|
||||||
|
virtual ~X509Common() {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an X509 extension value from an OpenSSL BIO to which it was
|
||||||
|
* written.
|
||||||
|
*
|
||||||
|
* @param bio the OpenSSL BIO to read. It will be freed by the function,
|
||||||
|
* including when an error occurs.
|
||||||
|
*
|
||||||
|
* @return The X509 extension value.
|
||||||
|
*/
|
||||||
|
static StringVal* GetExtensionFromBIO(BIO* bio);
|
||||||
|
|
||||||
|
static double GetTimeFromAsn1(const ASN1_TIME* atime, const char* arg_fid, Reporter* reporter);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
X509Common(file_analysis::Tag arg_tag, RecordVal* arg_args, File* arg_file);
|
||||||
|
|
||||||
|
void ParseExtension(X509_EXTENSION* ex, EventHandlerPtr h, bool global);
|
||||||
|
void ParseSignedCertificateTimestamps(X509_EXTENSION* ext);
|
||||||
|
virtual void ParseExtensionsSpecific(X509_EXTENSION* ex, bool, ASN1_OBJECT*, const char*) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* FILE_ANALYSIS_X509_COMMON */
|
|
@ -73,4 +73,4 @@ event x509_ext_subject_alternative_name%(f: fa_file, ext: X509::SubjectAlternati
|
||||||
## digitally_signed struct
|
## digitally_signed struct
|
||||||
##
|
##
|
||||||
## signature: signature part of the digitally_signed struct
|
## signature: signature part of the digitally_signed struct
|
||||||
event x509_ext_signed_certificate_timestamp%(f: fa_file, version: count, logid: string, timestamp: time, hash_algorithm: count, signature_algorithm: count, signature: string%);
|
event x509_ocsp_ext_signed_certificate_timestamp%(f: fa_file, version: count, logid: string, timestamp: time, hash_algorithm: count, signature_algorithm: count, signature: string%);
|
||||||
|
|
|
@ -35,7 +35,7 @@ refine connection MockConnection += {
|
||||||
|
|
||||||
function proc_signedcertificatetimestamp(rec: HandshakeRecord, version: uint8, logid: const_bytestring, timestamp: uint64, digitally_signed_algorithms: SignatureAndHashAlgorithm, digitally_signed_signature: const_bytestring) : bool
|
function proc_signedcertificatetimestamp(rec: HandshakeRecord, version: uint8, logid: const_bytestring, timestamp: uint64, digitally_signed_algorithms: SignatureAndHashAlgorithm, digitally_signed_signature: const_bytestring) : bool
|
||||||
%{
|
%{
|
||||||
BifEvent::generate_x509_ext_signed_certificate_timestamp((analyzer::Analyzer *) bro_analyzer(),
|
BifEvent::generate_x509_ocsp_ext_signed_certificate_timestamp((analyzer::Analyzer *) bro_analyzer(),
|
||||||
bro_analyzer()->GetFile()->GetVal()->Ref(),
|
bro_analyzer()->GetFile()->GetVal()->Ref(),
|
||||||
version,
|
version,
|
||||||
new StringVal(logid.length(), reinterpret_cast<const char*>(logid.begin())),
|
new StringVal(logid.length(), reinterpret_cast<const char*>(logid.begin())),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# @TEST-EXEC: bro -r $TRACES/tls/certificate-with-sct.pcap %INPUT
|
# @TEST-EXEC: bro -r $TRACES/tls/certificate-with-sct.pcap %INPUT
|
||||||
# @TEST-EXEC: btest-diff .stdout
|
# @TEST-EXEC: btest-diff .stdout
|
||||||
|
|
||||||
event x509_ext_signed_certificate_timestamp(f: fa_file, version: count, logid: string, timestamp: time, hash_algorithm: count, signature_algorithm: count, signature: string)
|
event x509_ocsp_ext_signed_certificate_timestamp(f: fa_file, version: count, logid: string, timestamp: time, hash_algorithm: count, signature_algorithm: count, signature: string)
|
||||||
{
|
{
|
||||||
print version, timestamp, hash_algorithm, signature_algorithm;
|
print version, timestamp, hash_algorithm, signature_algorithm;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue