OCSP: rewrite events and data structures.

Instead of having a big event, that tries to parse all the data into a
huge datastructure, we do the more common thing and use a series of
smaller events to parse requests and responses.

The new events are:

ocsp_request -> raised for an ocsp request, giving version and requestor

ocsp_request_certificate -> raised n times per request, once per cert

ocsp_response_status -> raised for each ocsp response, giving status

ocsp_response_bytes -> raised for each ocsp response with information

ocsp_response_certificate -> raised for each cert in an ocsp response
This commit is contained in:
Johanna Amann 2017-02-09 15:43:37 -08:00
parent 9c6cebf324
commit e1bcc4509f
12 changed files with 90 additions and 277 deletions

View file

@ -6,7 +6,6 @@
#include "Event.h"
#include "ocsp_events.bif.h"
#include "ocsp_types.bif.h"
#include "file_analysis/Manager.h"
@ -28,7 +27,6 @@ X509 *helper_sk_X509_value(STACK_OF(X509) *certs, int i)
using namespace file_analysis;
IMPLEMENT_SERIAL(OCSP_REQVal, SER_OCSP_REQ_VAL);
IMPLEMENT_SERIAL(OCSP_RESPVal, SER_OCSP_RESP_VAL);
#define OCSP_STRING_BUF_SIZE 2048
@ -51,32 +49,29 @@ static void OCSP_RESPID_bio(OCSP_RESPID *resp_id, BIO* bio)
i2a_ASN1_STRING(bio, resp_id->value.byKey, V_ASN1_OCTET_STRING);
}
static RecordVal* ocsp_fill_cert_id(OCSP_CERTID *cert_id, RecordType* type, BIO* bio)
void ocsp_add_cert_id(OCSP_CERTID *cert_id, val_list* vl, BIO* bio)
{
RecordVal *d = new RecordVal(type);
char buf[OCSP_STRING_BUF_SIZE];
memset(buf, 0, sizeof(buf));
i2a_ASN1_OBJECT(bio, cert_id->hashAlgorithm->algorithm);
int len = BIO_read(bio, buf, sizeof(buf));
d->Assign(0, new StringVal(len, buf));
vl->append(new StringVal(len, buf));
BIO_reset(bio);
i2a_ASN1_STRING(bio, cert_id->issuerNameHash, V_ASN1_OCTET_STRING);
len = BIO_read(bio, buf, sizeof(buf));
d->Assign(1, new StringVal(len, buf));
vl->append(new StringVal(len, buf));
BIO_reset(bio);
i2a_ASN1_STRING(bio, cert_id->issuerKeyHash, V_ASN1_OCTET_STRING);
len = BIO_read(bio, buf, sizeof(buf));
d->Assign(2, new StringVal(len, buf));
vl->append(new StringVal(len, buf));
BIO_reset(bio);
i2a_ASN1_INTEGER(bio, cert_id->serialNumber);
d->Assign(3, new StringVal(len, buf));
vl->append(new StringVal(len, buf));
BIO_reset(bio);
return d;
}
file_analysis::Analyzer* OCSP::Instantiate(RecordVal* args, File* file)
@ -123,16 +118,8 @@ bool file_analysis::OCSP::EndOfFile()
return false;
}
OCSP_REQVal* req_val = new OCSP_REQVal(req); // req_val takes ownership
RecordVal* req_record = ParseRequest(req_val, GetFile()->GetID().c_str());
// and send the record on to scriptland
val_list* vl = new val_list();
vl->append(GetFile()->GetVal()->Ref());
vl->append(req_val);
vl->append(req_record);
mgr.QueueEvent(ocsp_request, vl);
ParseRequest(req, GetFile()->GetID().c_str());
OCSP_REQUEST_free(req);
}
else if (ocsp_type == "response")
{
@ -144,14 +131,8 @@ bool file_analysis::OCSP::EndOfFile()
}
OCSP_RESPVal* resp_val = new OCSP_RESPVal(resp); // resp_val takes ownership
RecordVal* resp_record = ParseResponse(resp_val, GetFile()->GetID().c_str());
// and send the record on to scriptland
val_list* vl = new val_list();
vl->append(GetFile()->GetVal()->Ref());
vl->append(resp_val);
vl->append(resp_record);
mgr.QueueEvent(ocsp_response, vl);
ParseResponse(resp_val, GetFile()->GetID().c_str());
Unref(resp_val);
}
else
{
@ -162,46 +143,48 @@ bool file_analysis::OCSP::EndOfFile()
return true;
}
RecordVal *file_analysis::OCSP::ParseRequest(OCSP_REQVal *req_val, const char* fid)
void file_analysis::OCSP::ParseRequest(OCSP_REQUEST *req, const char* fid)
{
OCSP_REQUEST *req = req_val->GetReq();
OCSP_REQINFO *inf = req->tbsRequest;
char buf[OCSP_STRING_BUF_SIZE]; // we need a buffer for some of the openssl functions
memset(buf, 0, sizeof(buf));
RecordVal* ocsp_req_record = new RecordVal(BifType::Record::OCSP::Request);
ocsp_req_record->Assign(0, new Val((uint64)ASN1_INTEGER_get(inf->version), TYPE_COUNT));
// build up our response as we go along...
val_list* vl = new val_list();
vl->append(GetFile()->GetVal()->Ref());
vl->append(new Val((uint64)ASN1_INTEGER_get(inf->version), TYPE_COUNT));
BIO *bio = BIO_new(BIO_s_mem());
if (inf->requestorName != NULL)
{
GENERAL_NAME_print(bio, inf->requestorName);
int len = BIO_read(bio, buf, sizeof(buf));
ocsp_req_record->Assign(1, new StringVal(len, buf));
vl->append(new StringVal(len, buf));
BIO_reset(bio);
}
else
vl->append(new StringVal(0, ""));
VectorVal* all_req_bro = new VectorVal(internal_type("ocsp_req_vec")->AsVectorType());
ocsp_req_record->Assign(2, all_req_bro);
mgr.QueueEvent(ocsp_request, vl);
int req_count = OCSP_request_onereq_count(req);
for ( int i=0; i<req_count; i++ )
{
val_list* rvl = new val_list();
rvl->append(GetFile()->GetVal()->Ref());
OCSP_ONEREQ *one_req = OCSP_request_onereq_get0(req, i);
OCSP_CERTID *cert_id = OCSP_onereq_get0_id(one_req);
RecordVal* one_req_bro = ocsp_fill_cert_id(cert_id, BifType::Record::OCSP::OneReq, bio);
all_req_bro->Assign(i, one_req_bro);
ocsp_add_cert_id(cert_id, rvl, bio);
mgr.QueueEvent(ocsp_request_certificate, rvl);
}
BIO_free(bio);
return ocsp_req_record;
}
RecordVal *file_analysis::OCSP::ParseResponse(OCSP_RESPVal *resp_val, const char* fid)
void file_analysis::OCSP::ParseResponse(OCSP_RESPVal *resp_val, const char* fid)
{
OCSP_RESPONSE *resp = resp_val->GetResp();
OCSP_RESPBYTES *resp_bytes = resp->responseBytes;
@ -210,24 +193,31 @@ RecordVal *file_analysis::OCSP::ParseResponse(OCSP_RESPVal *resp_val, const char
OCSP_RESPID *resp_id = nullptr;
int resp_count = 0;
VectorVal *all_resp_bro = nullptr;
VectorVal *certs_vector = nullptr;
int len = 0;
char buf[OCSP_STRING_BUF_SIZE];
memset(buf, 0, sizeof(buf));
RecordVal *ocsp_resp_record = new RecordVal(BifType::Record::OCSP::Response);
val_list* vl = new val_list();
vl->append(GetFile()->GetVal()->Ref());
const char *status_str = OCSP_response_status_str(OCSP_response_status(resp));
ocsp_resp_record->Assign(0, new StringVal(strlen(status_str), status_str));
StringVal* status_val = new StringVal(strlen(status_str), status_str);
vl->append(status_val->Ref());
mgr.QueueEvent(ocsp_response_status, vl);
vl = nullptr;
if (!resp_bytes)
return ocsp_resp_record;
{
Unref(status_val);
return;
}
BIO *bio = BIO_new(BIO_s_mem());
i2a_ASN1_OBJECT(bio, resp_bytes->responseType);
int len = BIO_read(bio, buf, sizeof(buf));
ocsp_resp_record->Assign(1, new StringVal(len, buf));
BIO_reset(bio);
//i2a_ASN1_OBJECT(bio, resp_bytes->responseType);
//int len = BIO_read(bio, buf, sizeof(buf));
//BIO_reset(bio);
// get the basic response
basic_resp = OCSP_response_get1_basic(resp);
@ -238,19 +228,21 @@ RecordVal *file_analysis::OCSP::ParseResponse(OCSP_RESPVal *resp_val, const char
if ( !resp_data )
goto clean_up;
ocsp_resp_record->Assign(2, new Val((uint64)ASN1_INTEGER_get(resp_data->version), TYPE_COUNT));
vl = new val_list();
vl->append(GetFile()->GetVal()->Ref());
vl->append(resp_val->Ref());
vl->append(status_val);
vl->append(new Val((uint64)ASN1_INTEGER_get(resp_data->version), TYPE_COUNT));
// responderID
resp_id = resp_data->responderId;
OCSP_RESPID_bio(resp_id, bio);
len = BIO_read(bio, buf, sizeof(buf));
ocsp_resp_record->Assign(3, new StringVal(len, buf));
vl->append(new StringVal(len, buf));
BIO_reset(bio);
// producedAt
ocsp_resp_record->Assign(4, new Val(GetTimeFromAsn1(resp_data->producedAt, fid, reporter), TYPE_TIME));
all_resp_bro = new VectorVal(internal_type("ocsp_resp_vec")->AsVectorType());
ocsp_resp_record->Assign(5, all_resp_bro);
vl->append(new Val(GetTimeFromAsn1(resp_data->producedAt, fid, reporter), TYPE_TIME));
// responses
resp_count = sk_OCSP_SINGLERESP_num(resp_data->responses);
@ -260,39 +252,51 @@ RecordVal *file_analysis::OCSP::ParseResponse(OCSP_RESPVal *resp_val, const char
if ( !single_resp )
continue;
val_list* rvl = new val_list();
rvl->append(GetFile()->GetVal()->Ref());
// cert id
OCSP_CERTID *cert_id = single_resp->certId;
RecordVal *single_resp_bro = ocsp_fill_cert_id(cert_id, BifType::Record::OCSP::SingleResp, bio);
ocsp_add_cert_id(cert_id, rvl, bio);
BIO_reset(bio);
// certStatus
OCSP_CERTSTATUS *cert_status = single_resp->certStatus;
const char* cert_status_str = OCSP_cert_status_str(cert_status->type);
single_resp_bro->Assign(4, new StringVal(strlen(cert_status_str), cert_status_str));
rvl->append(new StringVal(strlen(cert_status_str), cert_status_str));
// revocation time and reason if revoked
if ( cert_status->type == V_OCSP_CERTSTATUS_REVOKED )
{
OCSP_REVOKEDINFO *revoked_info = cert_status->value.revoked;
single_resp_bro->Assign(5, new Val(GetTimeFromAsn1(revoked_info->revocationTime, fid, reporter), TYPE_TIME));
rvl->append(new Val(GetTimeFromAsn1(revoked_info->revocationTime, fid, reporter), TYPE_TIME));
if ( revoked_info->revocationReason )
{
const char* revoke_reason = OCSP_crl_reason_str(ASN1_ENUMERATED_get(revoked_info->revocationReason));
single_resp_bro->Assign(6, new StringVal(strlen(revoke_reason), revoke_reason));
rvl->append(new StringVal(strlen(revoke_reason), revoke_reason));
}
else
rvl->append(new StringVal(0, ""));
}
else
{
rvl->append(new Val(0, TYPE_TIME));
rvl->append(new StringVal(0, ""));
}
single_resp_bro->Assign(7, new Val(GetTimeFromAsn1(single_resp->thisUpdate, fid, reporter), TYPE_TIME));
rvl->append(new Val(GetTimeFromAsn1(single_resp->thisUpdate, fid, reporter), TYPE_TIME));
if ( single_resp->nextUpdate )
single_resp_bro->Assign(8, new Val(GetTimeFromAsn1(single_resp->nextUpdate, fid, reporter), TYPE_TIME));
rvl->append(new Val(GetTimeFromAsn1(single_resp->nextUpdate, fid, reporter), TYPE_TIME));
else
rvl->append(new Val(0, TYPE_TIME));
all_resp_bro->Assign(i, single_resp_bro);
mgr.QueueEvent(ocsp_response_certificate, rvl);
}
i2a_ASN1_OBJECT(bio, basic_resp->signatureAlgorithm->algorithm);
len = BIO_read(bio, buf, sizeof(buf));
ocsp_resp_record->Assign(6, new StringVal(len, buf));
vl->append(new StringVal(len, buf));
BIO_reset(bio);
//i2a_ASN1_OBJECT(bio, basic_resp->signature);
@ -300,10 +304,10 @@ RecordVal *file_analysis::OCSP::ParseResponse(OCSP_RESPVal *resp_val, const char
//ocsp_resp_record->Assign(7, new StringVal(len, buf));
//BIO_reset(bio);
//certs
certs_vector = new VectorVal(internal_type("x509_opaque_vector")->AsVectorType());
vl->append(certs_vector);
if ( basic_resp->certs )
{
VectorVal *certs_vector = new VectorVal(internal_type("x509_opaque_vector")->AsVectorType());
int num_certs = sk_X509_num(basic_resp->certs);
for ( int i=0; i<num_certs; i++ )
{
@ -314,68 +318,15 @@ RecordVal *file_analysis::OCSP::ParseResponse(OCSP_RESPVal *resp_val, const char
else
reporter->Weird("OpenSSL returned null certificate");
}
ocsp_resp_record->Assign(7, certs_vector);
}
mgr.QueueEvent(ocsp_response_bytes, vl);
clean_up:
if (basic_resp)
OCSP_BASICRESP_free(basic_resp);
BIO_free(bio);
return ocsp_resp_record;
}
OCSP_REQVal::OCSP_REQVal(OCSP_REQUEST* arg_ocsp_req) : OpaqueVal(ocsp_req_opaque_type)
{
ocsp_req = arg_ocsp_req;
}
OCSP_REQVal::OCSP_REQVal() : OpaqueVal(ocsp_req_opaque_type)
{
ocsp_req = nullptr;
}
OCSP_REQVal::~OCSP_REQVal()
{
if (ocsp_req)
OCSP_REQUEST_free(ocsp_req);
}
OCSP_REQUEST* OCSP_REQVal::GetReq() const
{
return ocsp_req;
}
bool OCSP_REQVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_OCSP_REQ_VAL, OpaqueVal);
unsigned char *buf = nullptr;
int length = i2d_OCSP_REQUEST(ocsp_req, &buf);
if ( length < 0 )
return false;
bool res = SERIALIZE_STR(reinterpret_cast<const char*>(buf), length);
OPENSSL_free(buf);
return res;
}
bool OCSP_REQVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(OpaqueVal)
int length;
unsigned char *ocsp_req_buf, *opensslbuf;
if ( ! UNSERIALIZE_STR(reinterpret_cast<char **>(&ocsp_req_buf), &length) )
return false;
opensslbuf = ocsp_req_buf; // OpenSSL likes to shift pointers around. really.
ocsp_req = d2i_OCSP_REQUEST(nullptr, const_cast<const unsigned char**>(&opensslbuf), length);
delete[] ocsp_req_buf;
if ( !ocsp_req )
return false;
return true;
}
//OCSP_RESPVal
OCSP_RESPVal::OCSP_RESPVal(OCSP_RESPONSE* arg_ocsp_resp) : OpaqueVal(ocsp_resp_opaque_type)
{
ocsp_resp = arg_ocsp_resp;