diff --git a/scripts/base/files/ocsp/README b/scripts/base/files/ocsp/README new file mode 100644 index 0000000000..beabe8a3fc --- /dev/null +++ b/scripts/base/files/ocsp/README @@ -0,0 +1 @@ +Support for ocsp file analysis framework. diff --git a/scripts/base/files/ocsp/__load__.bro b/scripts/base/files/ocsp/__load__.bro new file mode 100644 index 0000000000..d551be57d3 --- /dev/null +++ b/scripts/base/files/ocsp/__load__.bro @@ -0,0 +1 @@ +@load ./main \ No newline at end of file diff --git a/scripts/base/files/ocsp/main.bro b/scripts/base/files/ocsp/main.bro new file mode 100644 index 0000000000..1b3dfdeffc --- /dev/null +++ b/scripts/base/files/ocsp/main.bro @@ -0,0 +1,11 @@ +@load base/frameworks/files +@load base/utils/paths + +module FileOCSP; + +export { + ## add one more argument to indicate is ocsp response or request + redef record Files::AnalyzerArgs += { + ocsp_type: string &optional; + }; +} diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 23f4fd43dd..fdc05f2df4 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -2927,6 +2927,50 @@ export { }; } + +module OCSP; +export { + type OneReq: record { + hashAlgorithm: string &log; + issuerNameHash: string &log; + issuerKeyHash: string &log; + serialNumber: string &log; + #RequestExtensions: xxx + }; + type Request: record { + version: count &log &optional; + requestorName: string &log &optional; + requestList: vector of OneReq; + #requestExtensions: xxx + }; + type SingleResp: record { + hashAlgorithm: string &log; + issuerNameHash: string &log; + issuerKeyHash: string &log; + serialNumber: string &log; + certStatus: string &log; + thisUpdate: string &log; + nextUpdate: string &log &optional; + #Extensions: xxx + }; + type Response: record { + responseStatus: string &log; + responseType: string &log; + version: count &log; + responderID: string &log; + producedAt: string &log; + responses: vector of SingleResp; + #responseExtensions:xxx + signatureAlgorithm: string &log; + signature: string; #&log; + }; + } + +module GLOBAL; +type ocsp_req_vec: vector of OCSP::OneReq; +type ocsp_resp_vec: vector of OCSP::SingleResp; + + module SOCKS; export { ## This record is for a SOCKS client or server to provide either a diff --git a/scripts/base/init-default.bro b/scripts/base/init-default.bro index 473d94fc84..61cd41a189 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -65,6 +65,7 @@ @load base/files/extract @load base/files/unified2 @load base/files/x509 +@load base/files/ocsp @load base/misc/find-checksum-offloading @load base/misc/find-filtered-trace diff --git a/src/SerialTypes.h b/src/SerialTypes.h index cf2c52a08b..6b81227464 100644 --- a/src/SerialTypes.h +++ b/src/SerialTypes.h @@ -115,6 +115,8 @@ SERIAL_VAL(CARDINALITY_VAL, 22) SERIAL_VAL(X509_VAL, 23) SERIAL_VAL(COMM_STORE_HANDLE_VAL, 24) SERIAL_VAL(COMM_DATA_VAL, 25) +SERIAL_VAL(OCSP_REQ_VAL, 26) +SERIAL_VAL(OCSP_RESP_VAL, 27) #define SERIAL_EXPR(name, val) SERIAL_CONST(name, val, EXPR) SERIAL_EXPR(EXPR, 1) diff --git a/src/Type.h b/src/Type.h index f902b0d907..3f886f55dc 100644 --- a/src/Type.h +++ b/src/Type.h @@ -627,6 +627,8 @@ extern OpaqueType* cardinality_type; extern OpaqueType* topk_type; extern OpaqueType* bloomfilter_type; extern OpaqueType* x509_opaque_type; +extern OpaqueType* ocsp_req_opaque_type; +extern OpaqueType* ocsp_resp_opaque_type; // Returns the Bro basic (non-parameterized) type with the given type. // The reference count of the type is not increased. diff --git a/src/file_analysis/analyzer/CMakeLists.txt b/src/file_analysis/analyzer/CMakeLists.txt index 225504c56a..bd5428a49e 100644 --- a/src/file_analysis/analyzer/CMakeLists.txt +++ b/src/file_analysis/analyzer/CMakeLists.txt @@ -4,3 +4,4 @@ add_subdirectory(hash) add_subdirectory(pe) add_subdirectory(unified2) add_subdirectory(x509) +add_subdirectory(ocsp) \ No newline at end of file diff --git a/src/file_analysis/analyzer/ocsp/CMakeLists.txt b/src/file_analysis/analyzer/ocsp/CMakeLists.txt new file mode 100644 index 0000000000..d1cff9d157 --- /dev/null +++ b/src/file_analysis/analyzer/ocsp/CMakeLists.txt @@ -0,0 +1,10 @@ + +include(BroPlugin) + +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR}) + +bro_plugin_begin(Bro OCSP) +bro_plugin_cc(OCSP.cc Plugin.cc) +bro_plugin_bif(events.bif types.bif) +bro_plugin_end() diff --git a/src/file_analysis/analyzer/ocsp/OCSP.cc b/src/file_analysis/analyzer/ocsp/OCSP.cc new file mode 100644 index 0000000000..08df3e4ce6 --- /dev/null +++ b/src/file_analysis/analyzer/ocsp/OCSP.cc @@ -0,0 +1,637 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +#include + +#include "OCSP.h" +#include "Event.h" + +#include "events.bif.h" +#include "types.bif.h" + +#include "file_analysis/Manager.h" + +#include +#include +#include +#include + +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 + +//this function is copied from src/file_analysis/analyzer/extract/Extract.cc +static Val* get_extract_field_val(RecordVal* args, const char* name) + { + Val* rval = args->Lookup(name); + if ( ! rval ) + reporter->Error("File extraction analyzer missing arg field: %s", name); + return rval; + } + +//convert different ANS1 type to c string +static int ANS1_to_cstr(char *buf, int buf_len, void *data, int type) + { + if (data == NULL || buf == NULL || buf_len <=0) + return -1; + int new_len = -1; + BIO *bio = BIO_new(BIO_s_mem()); + memset(buf, 0, buf_len); + + if (type == V_ASN1_OCTET_STRING) + { + if (i2a_ASN1_STRING(bio, (ASN1_STRING *)data, V_ASN1_OCTET_STRING) <= 0) + goto err; + } + else if (type == V_ASN1_BIT_STRING) + { + if (i2a_ASN1_STRING(bio, (ASN1_STRING *)data, V_ASN1_BIT_STRING) <= 0) + goto err; + } + else if (type == V_ASN1_INTEGER) + { + if (i2a_ASN1_INTEGER(bio, (ASN1_INTEGER *)data) <= 0) + goto err; + } + else if (type == V_ASN1_OBJECT) + { + if (i2a_ASN1_OBJECT(bio, (ASN1_OBJECT *)data) <= 0) + goto err; + } + else if (type == V_ASN1_GENERALIZEDTIME) + { + // TODO: convert ASN1_GENERALIZEDTIME to epoch time? + // new API: ASN1_TIME_diff() requires openssl 1.0.2 + // epoch time might be better for post processing + + // NOTE: this is for human readable time format + //if (!ASN1_GENERALIZEDTIME_print(bio, (ASN1_GENERALIZEDTIME *)data)) + // goto err; + + // NOTE: this is printing the raw string which is also understandable + // since this is smaller, let's keep ASN1_GENERALIZEDTIME as this for now? + ASN1_GENERALIZEDTIME *tmp = (ASN1_GENERALIZEDTIME *)data; + BIO_write(bio, tmp->data, tmp->length); + } + + else + goto err; + + new_len = BIO_read(bio, buf, buf_len); +err: + BIO_free_all(bio); + return new_len; + } + +//ANS1 OCTET string to c string +static int ASN1_OCTET_STRING_to_cstr(char *buf, int len, void *data) + { + return ANS1_to_cstr(buf, len, data, V_ASN1_OCTET_STRING); + } + +//ANS1 BIT string to c string +static int ASN1_BIT_STRING_to_cstr(char *buf, int len, void *data) + { + return ANS1_to_cstr(buf, len, data, V_ASN1_BIT_STRING); + } + +//ANS1 integer to c string +static int ASN1_INTEGER_to_cstr(char *buf, int len, void *data) + { + return ANS1_to_cstr(buf, len, data, V_ASN1_INTEGER); + } + +//ANS1 object to c string +static int ASN1_OBJECT_to_cstr(char *buf, int len, void *data) + { + return ANS1_to_cstr(buf, len, data, V_ASN1_OBJECT); + } + +//ASN1_GENERALIZEDTIME to c string +static int ASN1_GENERALIZEDTIME_to_cstr(char *buf, int len, void *data) + { + return ANS1_to_cstr(buf, len, data, V_ASN1_GENERALIZEDTIME); + } + +//CENERAL XXX to c string +static int GENERAL_NAME_to_cstr(char *buf, int buf_len, void *data) + { + if (data == NULL || buf == NULL || buf_len <= 0) + return -1; + int new_len = -1; + BIO *bio = BIO_new(BIO_s_mem()); + memset(buf, 0, buf_len); + if (GENERAL_NAME_print(bio, (GENERAL_NAME *)data) <= 0) + goto err; + new_len = BIO_read(bio, buf, buf_len); +err: + BIO_free_all(bio); + return new_len; + } + +//OCSP respond id to c string +static int OCSP_RESPID_to_cstr(char *buf, int buf_len, OCSP_RESPID *resp_id) + { + if (resp_id == NULL || buf == NULL || buf_len <= 0) + return -1; + int new_len = -1; + BIO *bio = BIO_new(BIO_s_mem()); + memset(buf, 0, buf_len); + if (resp_id->type == V_OCSP_RESPID_NAME) + { + if (X509_NAME_print_ex(bio, resp_id->value.byName, 0, XN_FLAG_ONELINE) <=0) + goto err; + } + else if (resp_id->type == V_OCSP_RESPID_KEY) + { + if (i2a_ASN1_STRING(bio, resp_id->value.byKey, V_ASN1_OCTET_STRING) <= 0) + goto err; + } + else + goto err; + new_len = BIO_read(bio, buf, buf_len); +err: + BIO_free_all(bio); + return new_len; + } + +//print out a cert id for debug +static void ocsp_print_cert_id(OCSP_CERTID *cid) + { + if (cid == NULL) + return; + char buf[OCSP_STRING_BUF_SIZE]; + int len = sizeof(buf); + memset(buf, 0, len); + int new_len = -1; + + //print hashAlgorithm + new_len = ASN1_OBJECT_to_cstr(buf, len, (void *)(cid->hashAlgorithm->algorithm)); + StringVal hashAlgorithm = StringVal(new_len, buf); + printf("[%d]hashAlgorithm: %s\n", new_len, hashAlgorithm.CheckString()); + + //print issuerNameHash + new_len = ASN1_OCTET_STRING_to_cstr(buf, len, (void *)(cid->issuerNameHash)); + StringVal issuerNameHash = StringVal(new_len, buf); + printf("[%d]issuerNameHash: %s\n", new_len, issuerNameHash.CheckString()); + + //print issuerKeyHash + new_len = ASN1_OCTET_STRING_to_cstr(buf, len, (void *)(cid->issuerKeyHash)); + StringVal issuerKeyHash = StringVal(new_len, buf); + printf("[%d]issuerKeyHash: %s\n", new_len, issuerKeyHash.CheckString()); + + //print serialNumber + new_len = ASN1_INTEGER_to_cstr(buf, len, (void *)(cid->issuerKeyHash)); + StringVal serialNumber = StringVal(new_len, buf); + printf("[%d]serialNumber: %s\n", new_len, serialNumber.CheckString()); + } + +//fill in cert id +static void ocsp_fill_cert_id(OCSP_CERTID *cert_id, RecordVal *d) + { + if (d == NULL || cert_id == NULL) + return; + char buf[OCSP_STRING_BUF_SIZE]; + int buf_len = sizeof(buf); + memset(buf, 0, buf_len); + + //hashAlgorithm + int len = -1; + len = ASN1_OBJECT_to_cstr(buf, buf_len, (void *)(cert_id->hashAlgorithm->algorithm)); + if (len > 0) + d->Assign(0, new StringVal(len, buf)); + + //issuerNameHash + len = -1; + len = ASN1_OCTET_STRING_to_cstr(buf, buf_len, (void *)(cert_id->issuerNameHash)); + if (len > 0) + d->Assign(1, new StringVal(len, buf)); + + //issuerKeyHash + len = -1; + len = ASN1_OCTET_STRING_to_cstr(buf, buf_len, (void *)(cert_id->issuerKeyHash)); + if (len > 0) + d->Assign(2, new StringVal(len, buf)); + + //serialNumber + len = -1; + len = ASN1_INTEGER_to_cstr(buf, buf_len, (void *)(cert_id->issuerKeyHash)); + if (len > 0) + d->Assign(3, new StringVal(len, buf)); + } + +file_analysis::Analyzer* OCSP::Instantiate(RecordVal* args, File* file) + { + Val* ocsp_type = get_extract_field_val(args, "ocsp_type"); + if (! ocsp_type ) + return 0; + return new OCSP(args, file, ocsp_type->AsString()->CheckString()); + } + +file_analysis::OCSP::OCSP(RecordVal* args, file_analysis::File* file, const string& arg_ocsp_type) + : file_analysis::Analyzer(file_mgr->GetComponentTag("OCSP"), args, file) + { + ocsp_type = arg_ocsp_type; + ocsp_data.clear(); + } + +bool file_analysis::OCSP::DeliverStream(const u_char* data, uint64 len) + { + ocsp_data.append(reinterpret_cast(data), len); + return true; + } + +bool file_analysis::OCSP::Undelivered(uint64 offset, uint64 len) + { + return false; + } + +// parse OCSP request or response and send data to bro scriptland +bool file_analysis::OCSP::EndOfFile() + { + OCSP_REQUEST *req = NULL; + OCSP_RESPONSE *resp = NULL; + + const unsigned char* ocsp_char = reinterpret_cast(ocsp_data.data()); + + if (ocsp_type == "request") + { + req = d2i_OCSP_REQUEST(NULL, &ocsp_char, ocsp_data.size()); + if (!req) + { + reporter->Weird(fmt("OPENSSL Could not parse OCSP request (fuid %s)", GetFile()->GetID().c_str())); + goto ocsp_cleanup; + } + + //parse request into record + OCSP_REQVal* req_val = new OCSP_REQVal(req); + RecordVal* req_record = ParseRequest(req_val); + if (!req_record) + { + reporter->Weird(fmt("Internal fail to parse OCSP request (fuid %s)", GetFile()->GetID().c_str())); + Unref(req_val); + goto ocsp_cleanup; + } + + // and send the record on to scriptland + val_list* vl = new val_list(); + vl->append(GetFile()->GetVal()->Ref()); + vl->append(req_val->Ref()); + vl->append(req_record->Ref()); + mgr.QueueEvent(ocsp_request, vl); + + Unref(req_val); + Unref(req_record); + } + else if (ocsp_type == "response") + { + resp = d2i_OCSP_RESPONSE(NULL, &ocsp_char, ocsp_data.size()); + if (!resp) + { + reporter->Weird(fmt("OPENSSL Could not parse OCSP response (fuid %s)", GetFile()->GetID().c_str())); + goto ocsp_cleanup; + } + + //parse request into record + OCSP_RESPVal* resp_val = new OCSP_RESPVal(resp); + RecordVal* resp_record = ParseResponse(resp_val); + if (!resp_record) + { + reporter->Weird(fmt("Internal fail to parse OCSP response (fuid %s)", GetFile()->GetID().c_str())); + Unref(resp_val); + goto ocsp_cleanup; + } + + // and send the record on to scriptland + val_list* vl = new val_list(); + vl->append(GetFile()->GetVal()->Ref()); + vl->append(resp_val->Ref()); + vl->append(resp_record->Ref()); + mgr.QueueEvent(ocsp_response, vl); + + Unref(resp_val); + Unref(resp_record); + } + else + reporter->Weird(fmt("the given argument of ocsp_type (%s) is not recognized", ocsp_type.c_str())); +ocsp_cleanup: + //if (resp) + // OCSP_RESPONSE_free(resp); + //if (req) + // OCSP_REQUEST_free(req); + return false; +} + +// parse OCSP request and trigger event +RecordVal *file_analysis::OCSP::ParseRequest(OCSP_REQVal *req_val) + { + if (req_val == NULL) + return NULL; + OCSP_REQUEST *req = NULL; + OCSP_ONEREQ *one_req = NULL; + OCSP_CERTID *cert_id = NULL; + OCSP_REQINFO *inf = NULL; + //OCSP_SIGNATURE *sig = NULL; + + RecordVal* ocsp_req_record = NULL; + VectorVal* all_req_bro = NULL; + + int req_count = -1, i = -1, len = -1; + long version = -1; + + req = req_val->GetReq(); + if (req == NULL) + return NULL; + + char buf[OCSP_STRING_BUF_SIZE]; + int buf_len = sizeof(buf); + memset(buf, 0, buf_len); + + inf = req->tbsRequest; + //sig = req->optionalSignature; + if (inf == NULL) + return NULL; + + ocsp_req_record = new RecordVal(BifType::Record::OCSP::Request); + if (!ocsp_req_record) + { + reporter->Error("Cannot create OCSP request structure: Internal memory error"); + return NULL; + } + + //version + version = ASN1_INTEGER_get(inf->version); + if (version != -1) + ocsp_req_record->Assign(0, new Val((uint64)version, TYPE_COUNT)); + + //requestorName + if (inf->requestorName != NULL) + { + len = -1; + len = GENERAL_NAME_to_cstr(buf, buf_len, (void *)(inf->requestorName)); + if (len > 1) + ocsp_req_record->Assign(1, new StringVal(len, buf)); + } + + //deal with details of the request + req_count = OCSP_request_onereq_count(req); + if (req_count <= 0) + goto clean_up; + for (i=0; iAsVectorType()); + RecordVal *one_req_bro = new RecordVal(BifType::Record::OCSP::OneReq); + + ocsp_fill_cert_id(cert_id, one_req_bro); + all_req_bro->Assign(all_req_bro->Size(), one_req_bro); + } + + if (all_req_bro != NULL) + ocsp_req_record->Assign(2, all_req_bro); +clean_up: + return ocsp_req_record; +} + +// parse OCSP response and trigger event +RecordVal *file_analysis::OCSP::ParseResponse(OCSP_RESPVal *resp_val) + { + if (resp_val == NULL) + return NULL; + + OCSP_RESPONSE *resp = NULL; + OCSP_RESPBYTES *resp_bytes = NULL; + OCSP_CERTID *cert_id = NULL; + OCSP_BASICRESP *basic_resp = NULL; + OCSP_RESPDATA *resp_data = NULL; + OCSP_RESPID *resp_id = NULL; + OCSP_SINGLERESP *single_resp = NULL; + + //OCSP_CERTSTATUS *cst = NULL; + //OCSP_REVOKEDINFO *rev = NULL; + + RecordVal *ocsp_resp_record = NULL; + VectorVal *all_resp_bro = NULL; + + int resp_count = -1, status = -1, i = -1, len = -1; + long version = -1; + + resp = resp_val->GetResp(); + if (resp == NULL) + return NULL; + + char buf[OCSP_STRING_BUF_SIZE]; + int buf_len = sizeof(buf); + memset(buf, 0, buf_len); + + ocsp_resp_record = new RecordVal(BifType::Record::OCSP::Response); + if (!ocsp_resp_record) + { + reporter->Error("Cannot create OCSP response structure: Internal memory error"); + return NULL; + } + + //responseStatus + status = OCSP_response_status(resp); + const char *status_str = OCSP_response_status_str(status); + ocsp_resp_record->Assign(0, new StringVal(strlen(status_str), status_str)); + + //responseType + resp_bytes = resp->responseBytes; + if (!resp_bytes) + goto clean_up; + len = -1; + len = ASN1_OBJECT_to_cstr(buf, buf_len, (void *)(resp_bytes->responseType)); + if (len > 0) + ocsp_resp_record->Assign(1, new StringVal(len, buf)); + + //get the basic response + basic_resp = OCSP_response_get1_basic(resp); + if (!basic_resp) + goto clean_up; + resp_data = basic_resp->tbsResponseData; + if (!resp_data) + goto clean_up; + + //version + version = ASN1_INTEGER_get(resp_data->version); + if (version != -1) + ocsp_resp_record->Assign(2, new Val((uint64)version, TYPE_COUNT)); + + //responderID + resp_id = resp_data->responderId; + len = -1; + len = OCSP_RESPID_to_cstr(buf, buf_len, resp_id); + if (len > 0) + ocsp_resp_record->Assign(3, new StringVal(len, buf)); + + //producedAt + len = -1; + len = ASN1_GENERALIZEDTIME_to_cstr(buf, buf_len, (void *)(resp_data->producedAt)); + if (len > 0) + ocsp_resp_record->Assign(4, new StringVal(len, buf)); + + //responses + resp_count = sk_OCSP_SINGLERESP_num(resp_data->responses); + if (resp_count <= 0) + goto clean_up; + for (i=0; iresponses, i); + if (!single_resp) + continue; + if (all_resp_bro == NULL) + all_resp_bro = new VectorVal(internal_type("ocsp_resp_vec")->AsVectorType()); + RecordVal *single_resp_bro = new RecordVal(BifType::Record::OCSP::SingleResp); + + //cert id + cert_id = single_resp->certId; + ocsp_fill_cert_id(cert_id, single_resp_bro); + + //certStatus + const char *cert_status_str = OCSP_cert_status_str(single_resp->certStatus->type); + single_resp_bro->Assign(4, new StringVal(strlen(cert_status_str), cert_status_str)); + + //thisUpdate + len = -1; + len = ASN1_GENERALIZEDTIME_to_cstr(buf, buf_len, (void *)(single_resp->thisUpdate)); + if (len > 0) + single_resp_bro->Assign(5, new StringVal(len, buf)); + + //nextUpdate + len = -1; + len = ASN1_GENERALIZEDTIME_to_cstr(buf, buf_len, (void *)(single_resp->nextUpdate)); + if (len > 0) + single_resp_bro->Assign(6, new StringVal(len, buf)); + + all_resp_bro->Assign(all_resp_bro->Size(), single_resp_bro); + } + if (all_resp_bro != NULL) + ocsp_resp_record->Assign(5, all_resp_bro); + + //signatureAlgorithm + if (basic_resp->signatureAlgorithm) + { + len = -1; + len = ASN1_OBJECT_to_cstr(buf, buf_len, (void *)(basic_resp->signatureAlgorithm->algorithm)); + if (len > 0) + ocsp_resp_record->Assign(6, new StringVal(len, buf)); + } + //signature + if (basic_resp->signature) + { + len = -1; + len = ASN1_BIT_STRING_to_cstr(buf, buf_len, (void *)(basic_resp->signature)); + if (len > 0) + ocsp_resp_record->Assign(7, new StringVal(len, buf)); + } +clean_up: + return ocsp_resp_record; +} + +//OCSP_REQVal +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 = NULL; + } + +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 = NULL; + int length = i2d_OCSP_REQUEST(ocsp_req, &buf); + if ( length < 0 ) + return false; + bool res = SERIALIZE_STR(reinterpret_cast(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(&ocsp_req_buf), &length) ) + return false; + opensslbuf = ocsp_req_buf; // OpenSSL likes to shift pointers around. really. + ocsp_req = d2i_OCSP_REQUEST(NULL, const_cast(&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; + } + +OCSP_RESPVal::OCSP_RESPVal() : OpaqueVal(ocsp_resp_opaque_type) + { + ocsp_resp = NULL; + } + +OCSP_RESPVal::~OCSP_RESPVal() + { + if (ocsp_resp) + OCSP_RESPONSE_free(ocsp_resp); + } + +OCSP_RESPONSE* OCSP_RESPVal::GetResp() const + { + return ocsp_resp; + } + +bool OCSP_RESPVal::DoSerialize(SerialInfo* info) const + { + DO_SERIALIZE(SER_OCSP_RESP_VAL, OpaqueVal); + unsigned char *buf = NULL; + int length = i2d_OCSP_RESPONSE(ocsp_resp, &buf); + if ( length < 0 ) + return false; + bool res = SERIALIZE_STR(reinterpret_cast(buf), length); + OPENSSL_free(buf); + return res; + } + +bool OCSP_RESPVal::DoUnserialize(UnserialInfo* info) + { + DO_UNSERIALIZE(OpaqueVal) + + int length; + unsigned char *ocsp_resp_buf, *opensslbuf; + + if ( ! UNSERIALIZE_STR(reinterpret_cast(&ocsp_resp_buf), &length) ) + return false; + opensslbuf = ocsp_resp_buf; // OpenSSL likes to shift pointers around. really. + ocsp_resp = d2i_OCSP_RESPONSE(NULL, const_cast(&opensslbuf), length); + delete[] ocsp_resp_buf; + if ( !ocsp_resp ) + return false; + return true; + } diff --git a/src/file_analysis/analyzer/ocsp/OCSP.h b/src/file_analysis/analyzer/ocsp/OCSP.h new file mode 100644 index 0000000000..ecdba6582d --- /dev/null +++ b/src/file_analysis/analyzer/ocsp/OCSP.h @@ -0,0 +1,66 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +#ifndef FILE_ANALYSIS_OCSP_H +#define FILE_ANALYSIS_OCSP_H + +#include + +#include "Val.h" +#include "../File.h" +#include "Analyzer.h" + +#include +#include +#include + +namespace file_analysis { + +class OCSP_REQVal; +class OCSP_RESPVal; + +class OCSP : public file_analysis::Analyzer { +public: + virtual bool DeliverStream(const u_char* data, uint64 len); + virtual bool Undelivered(uint64 offset, uint64 len); + virtual bool EndOfFile(); + + static RecordVal *ParseResponse(OCSP_RESPVal *); + static RecordVal *ParseRequest(OCSP_REQVal *); + + static file_analysis::Analyzer* Instantiate(RecordVal* args, File* file); + +protected: + OCSP(RecordVal* args, File* file, const string& ocsp_type); + +private: + std::string ocsp_data; + std::string ocsp_type; +}; + +class OCSP_REQVal: public OpaqueVal { +public: + explicit OCSP_REQVal(OCSP_REQUEST *); + ~OCSP_REQVal(); + OCSP_REQUEST *GetReq() const; +protected: + OCSP_REQVal(); +private: + OCSP_REQUEST *ocsp_req; + DECLARE_SERIAL(OCSP_REQVal); +}; + +class OCSP_RESPVal: public OpaqueVal { +public: + explicit OCSP_RESPVal(OCSP_RESPONSE *); + ~OCSP_RESPVal(); + OCSP_RESPONSE *GetResp() const; +protected: + OCSP_RESPVal(); +private: + OCSP_RESPONSE *ocsp_resp; + DECLARE_SERIAL(OCSP_RESPVal); +}; + +} + +#endif diff --git a/src/file_analysis/analyzer/ocsp/Plugin.cc b/src/file_analysis/analyzer/ocsp/Plugin.cc new file mode 100644 index 0000000000..f7edbd1d6d --- /dev/null +++ b/src/file_analysis/analyzer/ocsp/Plugin.cc @@ -0,0 +1,25 @@ +// See the file in the main distribution directory for copyright. + + +#include "plugin/Plugin.h" + +#include "OCSP.h" + +namespace plugin { +namespace Bro_OCSP { + +class Plugin : public plugin::Plugin { +public: + plugin::Configuration Configure() + { + AddComponent(new ::file_analysis::Component("OCSP", ::file_analysis::OCSP::Instantiate)); + + plugin::Configuration config; + config.name = "Bro::OCSP"; + config.description = "OCSP analyzer"; + return config; + } +} plugin; + +} +} diff --git a/src/file_analysis/analyzer/ocsp/events.bif b/src/file_analysis/analyzer/ocsp/events.bif new file mode 100644 index 0000000000..b956e2c12e --- /dev/null +++ b/src/file_analysis/analyzer/ocsp/events.bif @@ -0,0 +1,23 @@ +## Generated for encountered OCSP request +## +## +## f: The file. +## +## req_ref: An opaque pointer to the underlying OpenSSL data structure of the +## OCSP request +## +## req: The parsed OCSP request information. +## +event ocsp_request%(f: fa_file, req_ref: opaque of ocsp_req, req: OCSP::Request%); + +## Generated for encountered OCSP response +## +## +## f: The file. +## +## req_ref: An opaque pointer to the underlying OpenSSL data structure of the +## OCSP response +## +## req: The parsed OCSP response information. +## +event ocsp_response%(f: fa_file, resp_ref: opaque of ocsp_resp, resp: OCSP::Response%); diff --git a/src/file_analysis/analyzer/ocsp/types.bif b/src/file_analysis/analyzer/ocsp/types.bif new file mode 100644 index 0000000000..e005f1274e --- /dev/null +++ b/src/file_analysis/analyzer/ocsp/types.bif @@ -0,0 +1,4 @@ +type OCSP::Request: record; +type OCSP::Response: record; +type OCSP::OneReq: record; +type OCSP::SingleResp: record; \ No newline at end of file diff --git a/src/main.cc b/src/main.cc index 61cc35f198..be38f6ca39 100644 --- a/src/main.cc +++ b/src/main.cc @@ -131,6 +131,8 @@ OpaqueType* cardinality_type = 0; OpaqueType* topk_type = 0; OpaqueType* bloomfilter_type = 0; OpaqueType* x509_opaque_type = 0; +OpaqueType* ocsp_req_opaque_type = 0; +OpaqueType* ocsp_resp_opaque_type = 0; // Keep copy of command line int bro_argc; @@ -873,7 +875,9 @@ int main(int argc, char** argv) topk_type = new OpaqueType("topk"); bloomfilter_type = new OpaqueType("bloomfilter"); x509_opaque_type = new OpaqueType("x509"); - + ocsp_req_opaque_type = new OpaqueType("ocsp_req"); + ocsp_resp_opaque_type = new OpaqueType("ocsp_resp"); + // The leak-checker tends to produce some false // positives (memory which had already been // allocated before we start the checking is