Reformat Zeek in Spicy style

This largely copies over Spicy's `.clang-format` configuration file. The
one place where we deviate is header include order since Zeek depends on
headers being included in a certain order.
This commit is contained in:
Benjamin Bannier 2023-10-10 21:13:34 +02:00
parent 7b8e7ed72c
commit f5a76c1aed
786 changed files with 131714 additions and 153609 deletions

View file

@ -10,57 +10,49 @@
#include "zeek/file_analysis/Manager.h"
#include "zeek/util.h"
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
DataEvent::DataEvent(RecordValPtr args, file_analysis::File* file, EventHandlerPtr ce,
EventHandlerPtr se)
: file_analysis::Analyzer(file_mgr->GetComponentTag("DATA_EVENT"), std::move(args), file),
chunk_event(ce), stream_event(se)
{
}
DataEvent::DataEvent(RecordValPtr args, file_analysis::File* file, EventHandlerPtr ce, EventHandlerPtr se)
: file_analysis::Analyzer(file_mgr->GetComponentTag("DATA_EVENT"), std::move(args), file),
chunk_event(ce),
stream_event(se) {}
file_analysis::Analyzer* DataEvent::Instantiate(RecordValPtr args, file_analysis::File* file)
{
const auto& chunk_val = args->GetField("chunk_event");
const auto& stream_val = args->GetField("stream_event");
file_analysis::Analyzer* DataEvent::Instantiate(RecordValPtr args, file_analysis::File* file) {
const auto& chunk_val = args->GetField("chunk_event");
const auto& stream_val = args->GetField("stream_event");
if ( ! chunk_val && ! stream_val )
return nullptr;
if ( ! chunk_val && ! stream_val )
return nullptr;
EventHandlerPtr chunk;
EventHandlerPtr stream;
EventHandlerPtr chunk;
EventHandlerPtr stream;
if ( chunk_val )
chunk = event_registry->Lookup(chunk_val->AsFunc()->Name());
if ( chunk_val )
chunk = event_registry->Lookup(chunk_val->AsFunc()->Name());
if ( stream_val )
stream = event_registry->Lookup(stream_val->AsFunc()->Name());
if ( stream_val )
stream = event_registry->Lookup(stream_val->AsFunc()->Name());
return new DataEvent(std::move(args), file, chunk, stream);
}
return new DataEvent(std::move(args), file, chunk, stream);
}
bool DataEvent::DeliverChunk(const u_char* data, uint64_t len, uint64_t offset)
{
if ( ! chunk_event )
return true;
bool DataEvent::DeliverChunk(const u_char* data, uint64_t len, uint64_t offset) {
if ( ! chunk_event )
return true;
event_mgr.Enqueue(chunk_event, GetFile()->ToVal(),
make_intrusive<StringVal>(new String(data, len, false)),
val_mgr->Count(offset));
event_mgr.Enqueue(chunk_event, GetFile()->ToVal(), make_intrusive<StringVal>(new String(data, len, false)),
val_mgr->Count(offset));
return true;
}
return true;
}
bool DataEvent::DeliverStream(const u_char* data, uint64_t len)
{
if ( ! stream_event )
return true;
bool DataEvent::DeliverStream(const u_char* data, uint64_t len) {
if ( ! stream_event )
return true;
event_mgr.Enqueue(stream_event, GetFile()->ToVal(),
make_intrusive<StringVal>(new String(data, len, false)));
event_mgr.Enqueue(stream_event, GetFile()->ToVal(), make_intrusive<StringVal>(new String(data, len, false)));
return true;
}
return true;
}
} // namespace zeek::file_analysis::detail
} // namespace zeek::file_analysis::detail

View file

@ -9,58 +9,56 @@
#include "zeek/file_analysis/Analyzer.h"
#include "zeek/file_analysis/File.h"
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
/**
* An analyzer to send file data to script-layer via events.
*/
class DataEvent : public file_analysis::Analyzer
{
class DataEvent : public file_analysis::Analyzer {
public:
/**
* Generates the event, if any, specified by the "chunk_event" field of this
* analyzer's \c AnalyzerArgs. This is for non-sequential file data input.
* @param data pointer to start of file data chunk.
* @param len number of bytes in the data chunk.
* @param offset number of bytes from start of file at which chunk occurs.
* @return always true
*/
bool DeliverChunk(const u_char* data, uint64_t len, uint64_t offset) override;
/**
* Generates the event, if any, specified by the "chunk_event" field of this
* analyzer's \c AnalyzerArgs. This is for non-sequential file data input.
* @param data pointer to start of file data chunk.
* @param len number of bytes in the data chunk.
* @param offset number of bytes from start of file at which chunk occurs.
* @return always true
*/
bool DeliverChunk(const u_char* data, uint64_t len, uint64_t offset) override;
/**
* Generates the event, if any, specified by the "stream_event" field of
* this analyzer's \c AnalyzerArgs. This is for sequential file data input.
* @param data pointer to start of file data chunk.
* @param len number of bytes in the data chunk.
* @return always true
*/
bool DeliverStream(const u_char* data, uint64_t len) override;
/**
* Generates the event, if any, specified by the "stream_event" field of
* this analyzer's \c AnalyzerArgs. This is for sequential file data input.
* @param data pointer to start of file data chunk.
* @param len number of bytes in the data chunk.
* @return always true
*/
bool DeliverStream(const u_char* data, uint64_t len) override;
/**
* Create a new instance of a DataEvent analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new DataEvent analyzer instance or a null pointer if
* no "chunk_event" or "stream_event" field was specified in \a args.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file);
/**
* Create a new instance of a DataEvent analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new DataEvent analyzer instance or a null pointer if
* no "chunk_event" or "stream_event" field was specified in \a args.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file);
protected:
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @param ce pointer to event handler which will be called to receive
* non-sequential file data.
* @param se pointer to event handler which will be called to receive
* sequential file data.
*/
DataEvent(RecordValPtr args, file_analysis::File* file, EventHandlerPtr ce, EventHandlerPtr se);
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @param ce pointer to event handler which will be called to receive
* non-sequential file data.
* @param se pointer to event handler which will be called to receive
* sequential file data.
*/
DataEvent(RecordValPtr args, file_analysis::File* file, EventHandlerPtr ce, EventHandlerPtr se);
private:
EventHandlerPtr chunk_event;
EventHandlerPtr stream_event;
};
EventHandlerPtr chunk_event;
EventHandlerPtr stream_event;
};
} // namespace zeek::file_analysis::detail
} // namespace zeek::file_analysis::detail

View file

@ -5,22 +5,19 @@
#include "zeek/file_analysis/Component.h"
#include "zeek/file_analysis/analyzer/data_event/DataEvent.h"
namespace zeek::plugin::detail::Zeek_FileDataEvent
{
namespace zeek::plugin::detail::Zeek_FileDataEvent {
class Plugin : public zeek::plugin::Plugin
{
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure() override
{
AddComponent(new zeek::file_analysis::Component(
"DATA_EVENT", zeek::file_analysis::detail::DataEvent::Instantiate));
zeek::plugin::Configuration Configure() override {
AddComponent(
new zeek::file_analysis::Component("DATA_EVENT", zeek::file_analysis::detail::DataEvent::Instantiate));
zeek::plugin::Configuration config;
config.name = "Zeek::FileDataEvent";
config.description = "Delivers file content";
return config;
}
} plugin;
zeek::plugin::Configuration config;
config.name = "Zeek::FileDataEvent";
config.description = "Delivers file content";
return config;
}
} plugin;
} // namespace zeek::plugin::detail::Zeek_FileDataEvent
} // namespace zeek::plugin::detail::Zeek_FileDataEvent

View file

@ -8,67 +8,55 @@
#include "zeek/file_analysis/Manager.h"
#include "zeek/util.h"
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
Entropy::Entropy(RecordValPtr args, file_analysis::File* file)
: file_analysis::Analyzer(file_mgr->GetComponentTag("ENTROPY"), std::move(args), file)
{
entropy = new EntropyVal;
fed = false;
}
: file_analysis::Analyzer(file_mgr->GetComponentTag("ENTROPY"), std::move(args), file) {
entropy = new EntropyVal;
fed = false;
}
Entropy::~Entropy()
{
Unref(entropy);
}
Entropy::~Entropy() { Unref(entropy); }
file_analysis::Analyzer* Entropy::Instantiate(RecordValPtr args, file_analysis::File* file)
{
return new Entropy(std::move(args), file);
}
file_analysis::Analyzer* Entropy::Instantiate(RecordValPtr args, file_analysis::File* file) {
return new Entropy(std::move(args), file);
}
bool Entropy::DeliverStream(const u_char* data, uint64_t len)
{
if ( ! fed )
fed = len > 0;
bool Entropy::DeliverStream(const u_char* data, uint64_t len) {
if ( ! fed )
fed = len > 0;
entropy->Feed(data, len);
return true;
}
entropy->Feed(data, len);
return true;
}
bool Entropy::EndOfFile()
{
Finalize();
return false;
}
bool Entropy::EndOfFile() {
Finalize();
return false;
}
bool Entropy::Undelivered(uint64_t offset, uint64_t len)
{
return false;
}
bool Entropy::Undelivered(uint64_t offset, uint64_t len) { return false; }
void Entropy::Finalize()
{
if ( ! fed )
return;
void Entropy::Finalize() {
if ( ! fed )
return;
if ( ! file_entropy )
return;
if ( ! file_entropy )
return;
double montepi, scc, ent, mean, chisq;
montepi = scc = ent = mean = chisq = 0.0;
entropy->Get(&ent, &chisq, &mean, &montepi, &scc);
double montepi, scc, ent, mean, chisq;
montepi = scc = ent = mean = chisq = 0.0;
entropy->Get(&ent, &chisq, &mean, &montepi, &scc);
static auto entropy_test_result = id::find_type<RecordType>("entropy_test_result");
auto ent_result = make_intrusive<RecordVal>(entropy_test_result);
ent_result->Assign(0, ent);
ent_result->Assign(1, chisq);
ent_result->Assign(2, mean);
ent_result->Assign(3, montepi);
ent_result->Assign(4, scc);
static auto entropy_test_result = id::find_type<RecordType>("entropy_test_result");
auto ent_result = make_intrusive<RecordVal>(entropy_test_result);
ent_result->Assign(0, ent);
ent_result->Assign(1, chisq);
ent_result->Assign(2, mean);
ent_result->Assign(3, montepi);
ent_result->Assign(4, scc);
event_mgr.Enqueue(file_entropy, GetFile()->ToVal(), std::move(ent_result));
}
event_mgr.Enqueue(file_entropy, GetFile()->ToVal(), std::move(ent_result));
}
} // namespace zeek::file_analysis::detail
} // namespace zeek::file_analysis::detail

View file

@ -10,71 +10,69 @@
#include "zeek/file_analysis/File.h"
#include "zeek/file_analysis/analyzer/entropy/events.bif.h"
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
/**
* An analyzer to produce entropy of file contents.
*/
class Entropy : public file_analysis::Analyzer
{
class Entropy : public file_analysis::Analyzer {
public:
/**
* Destructor.
*/
~Entropy() override;
/**
* Destructor.
*/
~Entropy() override;
/**
* Create a new instance of an Entropy analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new Entropy analyzer instance or a null pointer if the
* the "extraction_file" field of \a args wasn't set.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file);
/**
* Create a new instance of an Entropy analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new Entropy analyzer instance or a null pointer if the
* the "extraction_file" field of \a args wasn't set.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file);
/**
* Calculate entropy of next chunk of file contents.
* @param data pointer to start of a chunk of a file data.
* @param len number of bytes in the data chunk.
* @return false if the digest is in an invalid state, else true.
*/
bool DeliverStream(const u_char* data, uint64_t len) override;
/**
* Calculate entropy of next chunk of file contents.
* @param data pointer to start of a chunk of a file data.
* @param len number of bytes in the data chunk.
* @return false if the digest is in an invalid state, else true.
*/
bool DeliverStream(const u_char* data, uint64_t len) override;
/**
* Finalizes the calculation and raises a "file_entropy_test" event.
* @return always false so analyze will be detached from file.
*/
bool EndOfFile() override;
/**
* Finalizes the calculation and raises a "file_entropy_test" event.
* @return always false so analyze will be detached from file.
*/
bool EndOfFile() override;
/**
* Missing data can't be handled, so just indicate the this analyzer should
* be removed from receiving further data. The entropy will not be finalized.
* @param offset byte offset in file at which missing chunk starts.
* @param len number of missing bytes.
* @return always false so analyzer will detach from file.
*/
bool Undelivered(uint64_t offset, uint64_t len) override;
/**
* Missing data can't be handled, so just indicate the this analyzer should
* be removed from receiving further data. The entropy will not be finalized.
* @param offset byte offset in file at which missing chunk starts.
* @param len number of missing bytes.
* @return always false so analyzer will detach from file.
*/
bool Undelivered(uint64_t offset, uint64_t len) override;
protected:
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @param hv specific hash calculator object.
* @param kind human readable name of the hash algorithm to use.
*/
Entropy(RecordValPtr args, file_analysis::File* file);
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @param hv specific hash calculator object.
* @param kind human readable name of the hash algorithm to use.
*/
Entropy(RecordValPtr args, file_analysis::File* file);
/**
* If some file contents have been seen, finalizes the entropy of them and
* raises the "file_entropy" event with the results.
*/
void Finalize();
/**
* If some file contents have been seen, finalizes the entropy of them and
* raises the "file_entropy" event with the results.
*/
void Finalize();
private:
EntropyVal* entropy;
bool fed;
};
EntropyVal* entropy;
bool fed;
};
} // namespace zeek::file_analysis::detail
} // namespace zeek::file_analysis::detail

View file

@ -5,22 +5,18 @@
#include "zeek/file_analysis/Component.h"
#include "zeek/file_analysis/analyzer/entropy/Entropy.h"
namespace zeek::plugin::detail::Zeek_FileEntropy
{
namespace zeek::plugin::detail::Zeek_FileEntropy {
class Plugin : public zeek::plugin::Plugin
{
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure() override
{
AddComponent(new zeek::file_analysis::Component(
"ENTROPY", zeek::file_analysis::detail::Entropy::Instantiate));
zeek::plugin::Configuration Configure() override {
AddComponent(new zeek::file_analysis::Component("ENTROPY", zeek::file_analysis::detail::Entropy::Instantiate));
zeek::plugin::Configuration config;
config.name = "Zeek::FileEntropy";
config.description = "Entropy test file content";
return config;
}
} plugin;
zeek::plugin::Configuration config;
config.name = "Zeek::FileEntropy";
config.description = "Entropy test file content";
return config;
}
} plugin;
} // namespace zeek::plugin::detail::Zeek_FileEntropy
} // namespace zeek::plugin::detail::Zeek_FileEntropy

View file

@ -9,67 +9,59 @@
#include "zeek/file_analysis/Manager.h"
#include "zeek/util.h"
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
Extract::Extract(RecordValPtr args, file_analysis::File* file, const std::string& arg_filename,
uint64_t arg_limit, bool arg_limit_includes_missing)
: file_analysis::Analyzer(file_mgr->GetComponentTag("EXTRACT"), std::move(args), file),
filename(arg_filename), limit(arg_limit), written(0),
limit_includes_missing(arg_limit_includes_missing)
{
char buf[128];
file_stream = fopen(filename.data(), "wb");
Extract::Extract(RecordValPtr args, file_analysis::File* file, const std::string& arg_filename, uint64_t arg_limit,
bool arg_limit_includes_missing)
: file_analysis::Analyzer(file_mgr->GetComponentTag("EXTRACT"), std::move(args), file),
filename(arg_filename),
limit(arg_limit),
written(0),
limit_includes_missing(arg_limit_includes_missing) {
char buf[128];
file_stream = fopen(filename.data(), "wb");
if ( file_stream )
{
// Try to ensure full buffering.
if ( util::detail::setvbuf(file_stream, nullptr, _IOFBF, BUFSIZ) )
{
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Warning("cannot set buffering mode for %s: %s", filename.data(), buf);
}
}
else
{
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Error("cannot open %s: %s", filename.c_str(), buf);
}
}
if ( file_stream ) {
// Try to ensure full buffering.
if ( util::detail::setvbuf(file_stream, nullptr, _IOFBF, BUFSIZ) ) {
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Warning("cannot set buffering mode for %s: %s", filename.data(), buf);
}
}
else {
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Error("cannot open %s: %s", filename.c_str(), buf);
}
}
Extract::~Extract()
{
if ( file_stream && fclose(file_stream) )
{
char buf[128];
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Error("cannot close %s: %s", filename.data(), buf);
}
}
Extract::~Extract() {
if ( file_stream && fclose(file_stream) ) {
char buf[128];
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Error("cannot close %s: %s", filename.data(), buf);
}
}
static ValPtr get_extract_field_val(const RecordValPtr& args, const char* name)
{
const auto& rval = args->GetField(name);
static ValPtr get_extract_field_val(const RecordValPtr& args, const char* name) {
const auto& rval = args->GetField(name);
if ( ! rval )
reporter->Error("File extraction analyzer missing arg field: %s", name);
if ( ! rval )
reporter->Error("File extraction analyzer missing arg field: %s", name);
return rval;
}
return rval;
}
file_analysis::Analyzer* Extract::Instantiate(RecordValPtr args, file_analysis::File* file)
{
const auto& fname = get_extract_field_val(args, "extract_filename");
const auto& limit = get_extract_field_val(args, "extract_limit");
const auto& extract_limit_includes_missing = get_extract_field_val(
args, "extract_limit_includes_missing");
file_analysis::Analyzer* Extract::Instantiate(RecordValPtr args, file_analysis::File* file) {
const auto& fname = get_extract_field_val(args, "extract_filename");
const auto& limit = get_extract_field_val(args, "extract_limit");
const auto& extract_limit_includes_missing = get_extract_field_val(args, "extract_limit_includes_missing");
if ( ! fname || ! limit || ! extract_limit_includes_missing )
return nullptr;
if ( ! fname || ! limit || ! extract_limit_includes_missing )
return nullptr;
return new Extract(std::move(args), file, fname->AsString()->CheckString(), limit->AsCount(),
extract_limit_includes_missing->AsBool());
}
return new Extract(std::move(args), file, fname->AsString()->CheckString(), limit->AsCount(),
extract_limit_includes_missing->AsBool());
}
/**
* Check if we are exceeding the write limit with this write.
@ -79,118 +71,102 @@ file_analysis::Analyzer* Extract::Instantiate(RecordValPtr args, file_analysis::
* @param n number of bytes to write to keep within limit
* @returns true if limit exceeded
*/
static bool check_limit_exceeded(uint64_t lim, uint64_t written, uint64_t len, uint64_t* n)
{
if ( lim == 0 )
{
*n = len;
return false;
}
static bool check_limit_exceeded(uint64_t lim, uint64_t written, uint64_t len, uint64_t* n) {
if ( lim == 0 ) {
*n = len;
return false;
}
if ( written >= lim )
{
*n = 0;
return true;
}
else if ( written + len > lim )
{
*n = lim - written;
return true;
}
else
{
*n = len;
}
if ( written >= lim ) {
*n = 0;
return true;
}
else if ( written + len > lim ) {
*n = lim - written;
return true;
}
else {
*n = len;
}
return false;
}
return false;
}
bool Extract::DeliverStream(const u_char* data, uint64_t len)
{
if ( ! file_stream )
return false;
bool Extract::DeliverStream(const u_char* data, uint64_t len) {
if ( ! file_stream )
return false;
uint64_t towrite = 0;
bool limit_exceeded = check_limit_exceeded(limit, written, len, &towrite);
uint64_t towrite = 0;
bool limit_exceeded = check_limit_exceeded(limit, written, len, &towrite);
if ( limit_exceeded && file_extraction_limit )
{
file_analysis::File* f = GetFile();
f->FileEvent(file_extraction_limit,
{f->ToVal(), GetArgs(), val_mgr->Count(limit), val_mgr->Count(len)});
if ( limit_exceeded && file_extraction_limit ) {
file_analysis::File* f = GetFile();
f->FileEvent(file_extraction_limit, {f->ToVal(), GetArgs(), val_mgr->Count(limit), val_mgr->Count(len)});
// Limit may have been modified by a BIF, re-check it.
limit_exceeded = check_limit_exceeded(limit, written, len, &towrite);
}
// Limit may have been modified by a BIF, re-check it.
limit_exceeded = check_limit_exceeded(limit, written, len, &towrite);
}
char buf[128];
char buf[128];
if ( towrite > 0 )
{
if ( fwrite(data, towrite, 1, file_stream) != 1 )
{
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Error("failed to write to extracted file %s: %s", filename.data(), buf);
fclose(file_stream);
file_stream = nullptr;
return false;
}
if ( towrite > 0 ) {
if ( fwrite(data, towrite, 1, file_stream) != 1 ) {
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Error("failed to write to extracted file %s: %s", filename.data(), buf);
fclose(file_stream);
file_stream = nullptr;
return false;
}
written += towrite;
}
written += towrite;
}
// Assume we may not try to write anything more for a while due to reaching
// the extraction limit and the file analysis File still proceeding to
// do other analysis without destructing/closing this one until the very end,
// so flush anything currently buffered.
if ( limit_exceeded && fflush(file_stream) )
{
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Warning("cannot fflush extracted file %s: %s", filename.data(), buf);
}
// Assume we may not try to write anything more for a while due to reaching
// the extraction limit and the file analysis File still proceeding to
// do other analysis without destructing/closing this one until the very end,
// so flush anything currently buffered.
if ( limit_exceeded && fflush(file_stream) ) {
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Warning("cannot fflush extracted file %s: %s", filename.data(), buf);
}
return (! limit_exceeded);
}
return (! limit_exceeded);
}
bool Extract::Undelivered(uint64_t offset, uint64_t len)
{
if ( ! file_stream )
return false;
bool Extract::Undelivered(uint64_t offset, uint64_t len) {
if ( ! file_stream )
return false;
if ( limit_includes_missing )
{
uint64_t towrite = 0;
bool limit_exceeded = check_limit_exceeded(limit, written, len, &towrite);
// if the limit is exceeded, we have to raise the event. This gives scripts the opportunity
// to raise the limit.
if ( limit_exceeded && file_extraction_limit )
{
file_analysis::File* f = GetFile();
f->FileEvent(file_extraction_limit,
{f->ToVal(), GetArgs(), val_mgr->Count(limit), val_mgr->Count(len)});
// we have to check again if the limit is still exceedee
limit_exceeded = check_limit_exceeded(limit, written, len, &towrite);
}
if ( limit_includes_missing ) {
uint64_t towrite = 0;
bool limit_exceeded = check_limit_exceeded(limit, written, len, &towrite);
// if the limit is exceeded, we have to raise the event. This gives scripts the opportunity
// to raise the limit.
if ( limit_exceeded && file_extraction_limit ) {
file_analysis::File* f = GetFile();
f->FileEvent(file_extraction_limit, {f->ToVal(), GetArgs(), val_mgr->Count(limit), val_mgr->Count(len)});
// we have to check again if the limit is still exceedee
limit_exceeded = check_limit_exceeded(limit, written, len, &towrite);
}
// if the limit is exceeded, abort and don't do anything - no reason to seek.
if ( limit_exceeded )
return false;
// if the limit is exceeded, abort and don't do anything - no reason to seek.
if ( limit_exceeded )
return false;
// if we don't skip holes, count this hole against the write limit
written += len;
}
// if we don't skip holes, count this hole against the write limit
written += len;
}
if ( fseek(file_stream, len + offset, SEEK_SET) != 0 )
{
char buf[128];
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Error("failed to seek in extracted file %s: %s", filename.data(), buf);
fclose(file_stream);
file_stream = nullptr;
return false;
}
if ( fseek(file_stream, len + offset, SEEK_SET) != 0 ) {
char buf[128];
util::zeek_strerror_r(errno, buf, sizeof(buf));
reporter->Error("failed to seek in extracted file %s: %s", filename.data(), buf);
fclose(file_stream);
file_stream = nullptr;
return false;
}
return true;
}
return true;
}
} // namespace zeek::file_analysis::detail
} // namespace zeek::file_analysis::detail

View file

@ -10,72 +10,70 @@
#include "zeek/file_analysis/File.h"
#include "zeek/file_analysis/analyzer/extract/events.bif.h"
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
/**
* An analyzer to extract content of files to local disk.
*/
class Extract : public file_analysis::Analyzer
{
class Extract : public file_analysis::Analyzer {
public:
/**
* Destructor. Will close the file that was used for data extraction.
*/
~Extract() override;
/**
* Destructor. Will close the file that was used for data extraction.
*/
~Extract() override;
/**
* Write a chunk of file data to the local extraction file.
* @param data pointer to a chunk of file data.
* @param len number of bytes in the data chunk.
* @return false if there was no extraction file open and the data couldn't
* be written, else true.
*/
bool DeliverStream(const u_char* data, uint64_t len) override;
/**
* Write a chunk of file data to the local extraction file.
* @param data pointer to a chunk of file data.
* @param len number of bytes in the data chunk.
* @return false if there was no extraction file open and the data couldn't
* be written, else true.
*/
bool DeliverStream(const u_char* data, uint64_t len) override;
/**
* Report undelivered bytes.
* @param offset distance into the file where the gap occurred.
* @param len number of bytes undelivered.
* @return true
*/
bool Undelivered(uint64_t offset, uint64_t len) override;
/**
* Report undelivered bytes.
* @param offset distance into the file where the gap occurred.
* @param len number of bytes undelivered.
* @return true
*/
bool Undelivered(uint64_t offset, uint64_t len) override;
/**
* Create a new instance of an Extract analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new Extract analyzer instance or a null pointer if the
* the "extraction_file" field of \a args wasn't set.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file);
/**
* Create a new instance of an Extract analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new Extract analyzer instance or a null pointer if the
* the "extraction_file" field of \a args wasn't set.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file);
/**
* Sets the maximum allowed extracted file size. A value of zero means
* "no limit".
* @param bytes number of bytes allowed to be extracted
*/
void SetLimit(uint64_t bytes) { limit = bytes; }
/**
* Sets the maximum allowed extracted file size. A value of zero means
* "no limit".
* @param bytes number of bytes allowed to be extracted
*/
void SetLimit(uint64_t bytes) { limit = bytes; }
protected:
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @param arg_filename a file system path which specifies the local file
* to which the contents of the file will be extracted/written.
* @param arg_limit the maximum allowed file size.
* @param arg_limit_includes_missing missing bytes count towards limit if true.
*/
Extract(RecordValPtr args, file_analysis::File* file, const std::string& arg_filename,
uint64_t arg_limit, bool arg_limit_includes_missing);
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @param arg_filename a file system path which specifies the local file
* to which the contents of the file will be extracted/written.
* @param arg_limit the maximum allowed file size.
* @param arg_limit_includes_missing missing bytes count towards limit if true.
*/
Extract(RecordValPtr args, file_analysis::File* file, const std::string& arg_filename, uint64_t arg_limit,
bool arg_limit_includes_missing);
private:
std::string filename;
FILE* file_stream;
uint64_t limit; // the file extraction limit
uint64_t written; // how many bytes we have written so far
bool limit_includes_missing; // do count missing bytes against limit if true
};
std::string filename;
FILE* file_stream;
uint64_t limit; // the file extraction limit
uint64_t written; // how many bytes we have written so far
bool limit_includes_missing; // do count missing bytes against limit if true
};
} // namespace zeek::file_analysis::detail
} // namespace zeek::file_analysis::detail

View file

@ -5,22 +5,18 @@
#include "zeek/file_analysis/Component.h"
#include "zeek/file_analysis/analyzer/extract/Extract.h"
namespace zeek::plugin::detail::Zeek_FileExtract
{
namespace zeek::plugin::detail::Zeek_FileExtract {
class Plugin : public zeek::plugin::Plugin
{
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure() override
{
AddComponent(new zeek::file_analysis::Component(
"EXTRACT", zeek::file_analysis::detail::Extract::Instantiate));
zeek::plugin::Configuration Configure() override {
AddComponent(new zeek::file_analysis::Component("EXTRACT", zeek::file_analysis::detail::Extract::Instantiate));
zeek::plugin::Configuration config;
config.name = "Zeek::FileExtract";
config.description = "Extract file content";
return config;
}
} plugin;
zeek::plugin::Configuration config;
config.name = "Zeek::FileExtract";
config.description = "Extract file content";
return config;
}
} plugin;
} // namespace zeek::plugin::detail::Zeek_FileExtract
} // namespace zeek::plugin::detail::Zeek_FileExtract

View file

@ -8,58 +8,49 @@
#include "zeek/file_analysis/Manager.h"
#include "zeek/util.h"
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
StringValPtr MD5::kind_val = make_intrusive<StringVal>("md5");
StringValPtr SHA1::kind_val = make_intrusive<StringVal>("sha1");
StringValPtr SHA256::kind_val = make_intrusive<StringVal>("sha256");
Hash::Hash(RecordValPtr args, file_analysis::File* file, HashVal* hv, StringValPtr arg_kind)
: file_analysis::Analyzer(file_mgr->GetComponentTag(util::to_upper(arg_kind->ToStdString())),
std::move(args), file),
hash(hv), fed(false), kind(std::move(arg_kind))
{
hash->Init();
}
: file_analysis::Analyzer(file_mgr->GetComponentTag(util::to_upper(arg_kind->ToStdString())), std::move(args),
file),
hash(hv),
fed(false),
kind(std::move(arg_kind)) {
hash->Init();
}
Hash::~Hash()
{
Unref(hash);
}
Hash::~Hash() { Unref(hash); }
bool Hash::DeliverStream(const u_char* data, uint64_t len)
{
if ( ! hash->IsValid() )
return false;
bool Hash::DeliverStream(const u_char* data, uint64_t len) {
if ( ! hash->IsValid() )
return false;
if ( ! fed )
fed = len > 0;
if ( ! fed )
fed = len > 0;
hash->Feed(data, len);
return true;
}
hash->Feed(data, len);
return true;
}
bool Hash::EndOfFile()
{
Finalize();
return false;
}
bool Hash::EndOfFile() {
Finalize();
return false;
}
bool Hash::Undelivered(uint64_t offset, uint64_t len)
{
return false;
}
bool Hash::Undelivered(uint64_t offset, uint64_t len) { return false; }
void Hash::Finalize()
{
if ( ! hash->IsValid() || ! fed )
return;
void Hash::Finalize() {
if ( ! hash->IsValid() || ! fed )
return;
if ( ! file_hash )
return;
if ( ! file_hash )
return;
event_mgr.Enqueue(file_hash, GetFile()->ToVal(), kind, hash->Get());
}
event_mgr.Enqueue(file_hash, GetFile()->ToVal(), kind, hash->Get());
}
} // namespace zeek::file_analysis::detail
} // namespace zeek::file_analysis::detail

View file

@ -10,159 +10,143 @@
#include "zeek/file_analysis/File.h"
#include "zeek/file_analysis/analyzer/hash/events.bif.h"
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
/**
* An analyzer to produce a hash of file contents.
*/
class Hash : public file_analysis::Analyzer
{
class Hash : public file_analysis::Analyzer {
public:
/**
* Destructor.
*/
~Hash() override;
/**
* Destructor.
*/
~Hash() override;
/**
* Incrementally hash next chunk of file contents.
* @param data pointer to start of a chunk of a file data.
* @param len number of bytes in the data chunk.
* @return false if the digest is in an invalid state, else true.
*/
bool DeliverStream(const u_char* data, uint64_t len) override;
/**
* Incrementally hash next chunk of file contents.
* @param data pointer to start of a chunk of a file data.
* @param len number of bytes in the data chunk.
* @return false if the digest is in an invalid state, else true.
*/
bool DeliverStream(const u_char* data, uint64_t len) override;
/**
* Finalizes the hash and raises a "file_hash" event.
* @return always false so analyze will be detached from file.
*/
bool EndOfFile() override;
/**
* Finalizes the hash and raises a "file_hash" event.
* @return always false so analyze will be detached from file.
*/
bool EndOfFile() override;
/**
* Missing data can't be handled, so just indicate the this analyzer should
* be removed from receiving further data. The hash will not be finalized.
* @param offset byte offset in file at which missing chunk starts.
* @param len number of missing bytes.
* @return always false so analyzer will detach from file.
*/
bool Undelivered(uint64_t offset, uint64_t len) override;
/**
* Missing data can't be handled, so just indicate the this analyzer should
* be removed from receiving further data. The hash will not be finalized.
* @param offset byte offset in file at which missing chunk starts.
* @param len number of missing bytes.
* @return always false so analyzer will detach from file.
*/
bool Undelivered(uint64_t offset, uint64_t len) override;
protected:
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @param hv specific hash calculator object.
* @param kind human readable name of the hash algorithm to use.
*/
Hash(RecordValPtr args, file_analysis::File* file, HashVal* hv, StringValPtr kind);
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @param hv specific hash calculator object.
* @param kind human readable name of the hash algorithm to use.
*/
Hash(RecordValPtr args, file_analysis::File* file, HashVal* hv, StringValPtr kind);
/**
* If some file contents have been seen, finalizes the hash of them and
* raises the "file_hash" event with the results.
*/
void Finalize();
/**
* If some file contents have been seen, finalizes the hash of them and
* raises the "file_hash" event with the results.
*/
void Finalize();
private:
HashVal* hash;
bool fed;
StringValPtr kind;
};
HashVal* hash;
bool fed;
StringValPtr kind;
};
/**
* An analyzer to produce an MD5 hash of file contents.
*/
class MD5 final : public Hash
{
class MD5 final : public Hash {
public:
/**
* Create a new instance of the MD5 hashing file analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new MD5 analyzer instance or a null pointer if there's no
* handler for the "file_hash" event.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file)
{
return file_hash ? new MD5(std::move(args), file) : nullptr;
}
/**
* Create a new instance of the MD5 hashing file analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new MD5 analyzer instance or a null pointer if there's no
* handler for the "file_hash" event.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file) {
return file_hash ? new MD5(std::move(args), file) : nullptr;
}
private:
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
*/
MD5(RecordValPtr args, file_analysis::File* file)
: Hash(std::move(args), file, new MD5Val(), MD5::kind_val)
{
}
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
*/
MD5(RecordValPtr args, file_analysis::File* file) : Hash(std::move(args), file, new MD5Val(), MD5::kind_val) {}
static StringValPtr kind_val;
};
static StringValPtr kind_val;
};
/**
* An analyzer to produce a SHA1 hash of file contents.
*/
class SHA1 final : public Hash
{
class SHA1 final : public Hash {
public:
/**
* Create a new instance of the SHA1 hashing file analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new MD5 analyzer instance or a null pointer if there's no
* handler for the "file_hash" event.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file)
{
return file_hash ? new SHA1(std::move(args), file) : nullptr;
}
/**
* Create a new instance of the SHA1 hashing file analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new MD5 analyzer instance or a null pointer if there's no
* handler for the "file_hash" event.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file) {
return file_hash ? new SHA1(std::move(args), file) : nullptr;
}
private:
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
*/
SHA1(RecordValPtr args, file_analysis::File* file)
: Hash(std::move(args), file, new SHA1Val(), SHA1::kind_val)
{
}
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
*/
SHA1(RecordValPtr args, file_analysis::File* file) : Hash(std::move(args), file, new SHA1Val(), SHA1::kind_val) {}
static StringValPtr kind_val;
};
static StringValPtr kind_val;
};
/**
* An analyzer to produce a SHA256 hash of file contents.
*/
class SHA256 final : public Hash
{
class SHA256 final : public Hash {
public:
/**
* Create a new instance of the SHA256 hashing file analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new MD5 analyzer instance or a null pointer if there's no
* handler for the "file_hash" event.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file)
{
return file_hash ? new SHA256(std::move(args), file) : nullptr;
}
/**
* Create a new instance of the SHA256 hashing file analyzer.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
* @return the new MD5 analyzer instance or a null pointer if there's no
* handler for the "file_hash" event.
*/
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file) {
return file_hash ? new SHA256(std::move(args), file) : nullptr;
}
private:
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
*/
SHA256(RecordValPtr args, file_analysis::File* file)
: Hash(std::move(args), file, new SHA256Val(), SHA256::kind_val)
{
}
/**
* Constructor.
* @param args the \c AnalyzerArgs value which represents the analyzer.
* @param file the file to which the analyzer will be attached.
*/
SHA256(RecordValPtr args, file_analysis::File* file)
: Hash(std::move(args), file, new SHA256Val(), SHA256::kind_val) {}
static StringValPtr kind_val;
};
static StringValPtr kind_val;
};
} // namespace zeek::file_analysis
} // namespace zeek::file_analysis::detail

View file

@ -5,26 +5,20 @@
#include "zeek/file_analysis/Component.h"
#include "zeek/file_analysis/analyzer/hash/Hash.h"
namespace zeek::plugin::detail::Zeek_FileHash
{
namespace zeek::plugin::detail::Zeek_FileHash {
class Plugin : public zeek::plugin::Plugin
{
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure() override
{
AddComponent(new zeek::file_analysis::Component(
"MD5", zeek::file_analysis::detail::MD5::Instantiate));
AddComponent(new zeek::file_analysis::Component(
"SHA1", zeek::file_analysis::detail::SHA1::Instantiate));
AddComponent(new zeek::file_analysis::Component(
"SHA256", zeek::file_analysis::detail::SHA256::Instantiate));
zeek::plugin::Configuration Configure() override {
AddComponent(new zeek::file_analysis::Component("MD5", zeek::file_analysis::detail::MD5::Instantiate));
AddComponent(new zeek::file_analysis::Component("SHA1", zeek::file_analysis::detail::SHA1::Instantiate));
AddComponent(new zeek::file_analysis::Component("SHA256", zeek::file_analysis::detail::SHA256::Instantiate));
zeek::plugin::Configuration config;
config.name = "Zeek::FileHash";
config.description = "Hash file content";
return config;
}
} plugin;
zeek::plugin::Configuration config;
config.name = "Zeek::FileHash";
config.description = "Hash file content";
return config;
}
} plugin;
} // namespace zeek::plugin::detail::Zeek_FileHash
} // namespace zeek::plugin::detail::Zeek_FileHash

View file

@ -2,44 +2,34 @@
#include "zeek/file_analysis/Manager.h"
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
PE::PE(RecordValPtr args, file_analysis::File* file)
: file_analysis::Analyzer(file_mgr->GetComponentTag("PE"), std::move(args), file)
{
conn = new binpac::PE::MockConnection(this);
interp = new binpac::PE::File(conn);
done = false;
}
: file_analysis::Analyzer(file_mgr->GetComponentTag("PE"), std::move(args), file) {
conn = new binpac::PE::MockConnection(this);
interp = new binpac::PE::File(conn);
done = false;
}
PE::~PE()
{
delete interp;
delete conn;
}
PE::~PE() {
delete interp;
delete conn;
}
bool PE::DeliverStream(const u_char* data, uint64_t len)
{
if ( conn->is_done() )
return false;
bool PE::DeliverStream(const u_char* data, uint64_t len) {
if ( conn->is_done() )
return false;
try
{
interp->NewData(data, data + len);
}
catch ( const binpac::Exception& e )
{
AnalyzerViolation(util::fmt("Binpac exception: %s", e.c_msg()));
return false;
}
try {
interp->NewData(data, data + len);
} catch ( const binpac::Exception& e ) {
AnalyzerViolation(util::fmt("Binpac exception: %s", e.c_msg()));
return false;
}
return ! conn->is_done();
}
return ! conn->is_done();
}
bool PE::EndOfFile()
{
return false;
}
bool PE::EndOfFile() { return false; }
} // namespace zeek::file_analysis::detail
} // namespace zeek::file_analysis::detail

View file

@ -7,31 +7,28 @@
#include "file_analysis/analyzer/pe/pe_pac.h"
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
/**
* Analyze Portable Executable files
*/
class PE : public file_analysis::Analyzer
{
class PE : public file_analysis::Analyzer {
public:
~PE();
~PE();
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file)
{
return new PE(std::move(args), file);
}
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file) {
return new PE(std::move(args), file);
}
virtual bool DeliverStream(const u_char* data, uint64_t len);
virtual bool DeliverStream(const u_char* data, uint64_t len);
virtual bool EndOfFile();
virtual bool EndOfFile();
protected:
PE(RecordValPtr args, file_analysis::File* file);
binpac::PE::File* interp;
binpac::PE::MockConnection* conn;
bool done;
};
PE(RecordValPtr args, file_analysis::File* file);
binpac::PE::File* interp;
binpac::PE::MockConnection* conn;
bool done;
};
} // namespace zeek::file_analysis::detail
} // namespace zeek::file_analysis::detail

View file

@ -5,22 +5,18 @@
#include "zeek/file_analysis/Component.h"
#include "zeek/file_analysis/analyzer/pe/PE.h"
namespace zeek::plugin::detail::Zeek_PE
{
namespace zeek::plugin::detail::Zeek_PE {
class Plugin : public zeek::plugin::Plugin
{
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure() override
{
AddComponent(
new zeek::file_analysis::Component("PE", zeek::file_analysis::detail::PE::Instantiate));
zeek::plugin::Configuration Configure() override {
AddComponent(new zeek::file_analysis::Component("PE", zeek::file_analysis::detail::PE::Instantiate));
zeek::plugin::Configuration config;
config.name = "Zeek::PE";
config.description = "Portable Executable analyzer";
return config;
}
} plugin;
zeek::plugin::Configuration config;
config.name = "Zeek::PE";
config.description = "Portable Executable analyzer";
return config;
}
} plugin;
} // namespace zeek::plugin::detail::Zeek_PE
} // namespace zeek::plugin::detail::Zeek_PE

File diff suppressed because it is too large Load diff

View file

@ -7,36 +7,32 @@
#include "zeek/file_analysis/analyzer/x509/X509Common.h"
namespace zeek::file_analysis
{
namespace zeek::file_analysis {
class File;
namespace detail
{
namespace detail {
class OCSP : public file_analysis::detail::X509Common
{
class OCSP : public file_analysis::detail::X509Common {
public:
bool DeliverStream(const u_char* data, uint64_t len) override;
bool Undelivered(uint64_t offset, uint64_t len) override;
bool EndOfFile() override;
bool DeliverStream(const u_char* data, uint64_t len) override;
bool Undelivered(uint64_t offset, uint64_t len) override;
bool EndOfFile() override;
static file_analysis::Analyzer* InstantiateRequest(RecordValPtr args,
file_analysis::File* file);
static file_analysis::Analyzer* InstantiateReply(RecordValPtr args, file_analysis::File* file);
static file_analysis::Analyzer* InstantiateRequest(RecordValPtr args, file_analysis::File* file);
static file_analysis::Analyzer* InstantiateReply(RecordValPtr args, file_analysis::File* file);
protected:
OCSP(RecordValPtr args, file_analysis::File* file, bool request);
OCSP(RecordValPtr args, file_analysis::File* file, bool request);
private:
void ParseResponse(OCSP_RESPONSE*);
void ParseRequest(OCSP_REQUEST*);
void ParseExtensionsSpecific(X509_EXTENSION* ex, bool, ASN1_OBJECT*, const char*) override;
void ParseResponse(OCSP_RESPONSE*);
void ParseRequest(OCSP_REQUEST*);
void ParseExtensionsSpecific(X509_EXTENSION* ex, bool, ASN1_OBJECT*, const char*) override;
std::string ocsp_data;
bool request = false; // true if ocsp request, false if reply
};
std::string ocsp_data;
bool request = false; // true if ocsp request, false if reply
};
} // namespace detail
} // namespace zeek::file_analysis
} // namespace detail
} // namespace zeek::file_analysis

View file

@ -6,32 +6,27 @@
#include "zeek/file_analysis/analyzer/x509/OCSP.h"
#include "zeek/file_analysis/analyzer/x509/X509.h"
namespace zeek::plugin::detail::Zeek_X509
{
namespace zeek::plugin::detail::Zeek_X509 {
class Plugin : public zeek::plugin::Plugin
{
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure() override
{
AddComponent(new zeek::file_analysis::Component(
"X509", zeek::file_analysis::detail::X509::Instantiate));
AddComponent(new zeek::file_analysis::Component(
"OCSP_REQUEST", zeek::file_analysis::detail::OCSP::InstantiateRequest));
AddComponent(new zeek::file_analysis::Component(
"OCSP_REPLY", zeek::file_analysis::detail::OCSP::InstantiateReply));
zeek::plugin::Configuration Configure() override {
AddComponent(new zeek::file_analysis::Component("X509", zeek::file_analysis::detail::X509::Instantiate));
AddComponent(
new zeek::file_analysis::Component("OCSP_REQUEST", zeek::file_analysis::detail::OCSP::InstantiateRequest));
AddComponent(
new zeek::file_analysis::Component("OCSP_REPLY", zeek::file_analysis::detail::OCSP::InstantiateReply));
zeek::plugin::Configuration config;
config.name = "Zeek::X509";
config.description = "X509 and OCSP analyzer";
return config;
}
zeek::plugin::Configuration config;
config.name = "Zeek::X509";
config.description = "X509 and OCSP analyzer";
return config;
}
void Done() override
{
zeek::plugin::Plugin::Done();
zeek::file_analysis::detail::X509::FreeRootStore();
}
} plugin;
void Done() override {
zeek::plugin::Plugin::Done();
zeek::file_analysis::detail::X509::FreeRootStore();
}
} plugin;
} // namespace zeek::plugin::detail::Zeek_X509
} // namespace zeek::plugin::detail::Zeek_X509

File diff suppressed because it is too large Load diff

View file

@ -30,122 +30,113 @@
#define OCSP_SINGLERESP_get0_id(s) (s)->certId
static X509* X509_OBJECT_get0_X509(const X509_OBJECT* a)
{
if ( a == nullptr || a->type != X509_LU_X509 )
return nullptr;
return a->data.x509;
}
static X509* X509_OBJECT_get0_X509(const X509_OBJECT* a) {
if ( a == nullptr || a->type != X509_LU_X509 )
return nullptr;
return a->data.x509;
}
static void DSA_get0_pqg(const DSA* d, const BIGNUM** p, const BIGNUM** q, const BIGNUM** g)
{
if ( p != nullptr )
*p = d->p;
if ( q != nullptr )
*q = d->q;
if ( g != nullptr )
*g = d->g;
}
static void DSA_get0_pqg(const DSA* d, const BIGNUM** p, const BIGNUM** q, const BIGNUM** g) {
if ( p != nullptr )
*p = d->p;
if ( q != nullptr )
*q = d->q;
if ( g != nullptr )
*g = d->g;
}
static void RSA_get0_key(const RSA* r, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d)
{
if ( n != nullptr )
*n = r->n;
if ( e != nullptr )
*e = r->e;
if ( d != nullptr )
*d = r->d;
}
static void RSA_get0_key(const RSA* r, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d) {
if ( n != nullptr )
*n = r->n;
if ( e != nullptr )
*e = r->e;
if ( d != nullptr )
*d = r->d;
}
#endif
#endif
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
class X509Val;
class X509 : public file_analysis::detail::X509Common
{
class X509 : public file_analysis::detail::X509Common {
public:
bool DeliverStream(const u_char* data, uint64_t len) override;
bool Undelivered(uint64_t offset, uint64_t len) override;
bool EndOfFile() override;
bool DeliverStream(const u_char* data, uint64_t len) override;
bool Undelivered(uint64_t offset, uint64_t len) override;
bool EndOfFile() override;
/**
* Converts an X509 certificate into a \c X509::Certificate record
* value. This is a static function that can be called from external,
* it doesn't depend on the state of any particular file analyzer.
*
* @param cert_val The certificate to converts.
*
* @param f A file associated with the certificate, if any
* (primarily for error reporting).
*
* @param Returns the new record value and passes ownership to
* caller.
*/
static RecordValPtr ParseCertificate(X509Val* cert_val, file_analysis::File* file = nullptr);
/**
* Converts an X509 certificate into a \c X509::Certificate record
* value. This is a static function that can be called from external,
* it doesn't depend on the state of any particular file analyzer.
*
* @param cert_val The certificate to converts.
*
* @param f A file associated with the certificate, if any
* (primarily for error reporting).
*
* @param Returns the new record value and passes ownership to
* caller.
*/
static RecordValPtr ParseCertificate(X509Val* cert_val, file_analysis::File* file = nullptr);
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file)
{
return new X509(std::move(args), file);
}
static file_analysis::Analyzer* Instantiate(RecordValPtr args, file_analysis::File* file) {
return new X509(std::move(args), file);
}
/**
* Retrieves OpenSSL's representation of an X509 certificate store
* associated with a script-layer certificate root table variable/value.
* The underlying X509 store will be created if it has not been already,
* else the previously allocated one for the same table will be returned.
*
* @param root_certs The script-layer certificate root table value.
*
* @return OpenSSL's X509 store associated with the table value.
*/
static X509_STORE* GetRootStore(TableVal* root_certs);
/**
* Retrieves OpenSSL's representation of an X509 certificate store
* associated with a script-layer certificate root table variable/value.
* The underlying X509 store will be created if it has not been already,
* else the previously allocated one for the same table will be returned.
*
* @param root_certs The script-layer certificate root table value.
*
* @return OpenSSL's X509 store associated with the table value.
*/
static X509_STORE* GetRootStore(TableVal* root_certs);
/**
* Frees memory obtained from OpenSSL that is associated with the global
* X509 certificate store used by the Zeek scripting-layer. This primarily
* exists so leak checkers like LeakSanitizer don't count the
* globally-allocated mapping as a leak. Would be easy to suppress/ignore
* it, but that could accidentally silence cases where some new code
* mistakenly overwrites a table element without freeing it.
*/
static void FreeRootStore();
/**
* Frees memory obtained from OpenSSL that is associated with the global
* X509 certificate store used by the Zeek scripting-layer. This primarily
* exists so leak checkers like LeakSanitizer don't count the
* globally-allocated mapping as a leak. Would be easy to suppress/ignore
* it, but that could accidentally silence cases where some new code
* mistakenly overwrites a table element without freeing it.
*/
static void FreeRootStore();
/**
* Sets the table[string] that used as the certificate cache inside of Zeek.
*/
static void SetCertificateCache(TableValPtr cache) { certificate_cache = std::move(cache); }
/**
* Sets the table[string] that used as the certificate cache inside of Zeek.
*/
static void SetCertificateCache(TableValPtr cache) { certificate_cache = std::move(cache); }
/**
* Sets the callback when a certificate cache hit is encountered
*/
static void SetCertificateCacheHitCallback(FuncPtr func)
{
cache_hit_callback = std::move(func);
}
/**
* Sets the callback when a certificate cache hit is encountered
*/
static void SetCertificateCacheHitCallback(FuncPtr func) { cache_hit_callback = std::move(func); }
protected:
X509(RecordValPtr args, file_analysis::File* file);
X509(RecordValPtr args, file_analysis::File* file);
private:
void ParseBasicConstraints(X509_EXTENSION* ex);
void ParseSAN(X509_EXTENSION* ex);
void ParseExtensionsSpecific(X509_EXTENSION* ex, bool, ASN1_OBJECT*, const char*) override;
void ParseBasicConstraints(X509_EXTENSION* ex);
void ParseSAN(X509_EXTENSION* ex);
void ParseExtensionsSpecific(X509_EXTENSION* ex, bool, ASN1_OBJECT*, const char*) override;
std::string cert_data;
std::string cert_data;
// Helpers for ParseCertificate.
static StringValPtr KeyCurve(EVP_PKEY* key);
static unsigned int KeyLength(EVP_PKEY* key);
/** X509 stores associated with global script-layer values */
inline static std::map<Val*, X509_STORE*> x509_stores = std::map<Val*, X509_STORE*>();
inline static TableValPtr certificate_cache = nullptr;
inline static FuncPtr cache_hit_callback = nullptr;
};
// Helpers for ParseCertificate.
static StringValPtr KeyCurve(EVP_PKEY* key);
static unsigned int KeyLength(EVP_PKEY* key);
/** X509 stores associated with global script-layer values */
inline static std::map<Val*, X509_STORE*> x509_stores = std::map<Val*, X509_STORE*>();
inline static TableValPtr certificate_cache = nullptr;
inline static FuncPtr cache_hit_callback = nullptr;
};
/**
* This class wraps an OpenSSL X509 data structure.
@ -154,49 +145,48 @@ private:
* script-land. Otherwise, we cannot verify certificates from Zeek
* scriptland
*/
class X509Val : public OpaqueVal
{
class X509Val : public OpaqueVal {
public:
/**
* Construct an X509Val.
*
* @param certificate specifies the wrapped OpenSSL certificate
*
* @return A newly initialized X509Val.
*/
explicit X509Val(::X509* certificate);
/**
* Construct an X509Val.
*
* @param certificate specifies the wrapped OpenSSL certificate
*
* @return A newly initialized X509Val.
*/
explicit X509Val(::X509* certificate);
/**
* Clone an X509Val
*
* @param state certifies the state of the clone operation (duplicate tracking)
*
* @return A cloned X509Val.
*/
ValPtr DoClone(CloneState* state) override;
/**
* Clone an X509Val
*
* @param state certifies the state of the clone operation (duplicate tracking)
*
* @return A cloned X509Val.
*/
ValPtr DoClone(CloneState* state) override;
/**
* Destructor.
*/
~X509Val() override;
/**
* Destructor.
*/
~X509Val() override;
/**
* Get the wrapped X509 certificate. Please take care, that the
* internal OpenSSL reference counting stays the same.
*
* @return The wrapped OpenSSL X509 certificate.
*/
::X509* GetCertificate() const;
/**
* Get the wrapped X509 certificate. Please take care, that the
* internal OpenSSL reference counting stays the same.
*
* @return The wrapped OpenSSL X509 certificate.
*/
::X509* GetCertificate() const;
protected:
/**
* Construct an empty X509Val. Only used for deserialization
*/
X509Val();
/**
* Construct an empty X509Val. Only used for deserialization
*/
X509Val();
DECLARE_OPAQUE_VALUE(X509Val)
DECLARE_OPAQUE_VALUE(X509Val)
private:
::X509* certificate; // the wrapped certificate
};
::X509* certificate; // the wrapped certificate
};
} // namespace zeek::file_analysis::detail
} // namespace zeek::file_analysis::detail

View file

@ -14,334 +14,295 @@
#include "zeek/file_analysis/analyzer/x509/types.bif.h"
#include "zeek/file_analysis/analyzer/x509/x509-extension_pac.h"
namespace zeek::file_analysis::detail
{
namespace zeek::file_analysis::detail {
X509Common::X509Common(const zeek::Tag& arg_tag, RecordValPtr arg_args,
file_analysis::File* arg_file)
: file_analysis::Analyzer(arg_tag, std::move(arg_args), arg_file)
{
}
X509Common::X509Common(const zeek::Tag& arg_tag, RecordValPtr arg_args, file_analysis::File* arg_file)
: file_analysis::Analyzer(arg_tag, std::move(arg_args), arg_file) {}
static void EmitWeird(const char* name, file_analysis::File* file, const char* addl = "")
{
if ( file )
reporter->Weird(file, name, addl);
else
reporter->Weird(name);
}
static void EmitWeird(const char* name, file_analysis::File* file, const char* addl = "") {
if ( file )
reporter->Weird(file, name, addl);
else
reporter->Weird(name);
}
double X509Common::GetTimeFromAsn1(const ASN1_TIME* atime, file_analysis::File* f,
Reporter* reporter)
{
time_t lResult = 0;
double X509Common::GetTimeFromAsn1(const ASN1_TIME* atime, file_analysis::File* f, Reporter* reporter) {
time_t lResult = 0;
char lBuffer[26];
char* pBuffer = lBuffer;
char lBuffer[26];
char* pBuffer = lBuffer;
const char* pString = (const char*)atime->data;
unsigned int remaining = atime->length;
const char* pString = (const char*)atime->data;
unsigned int remaining = atime->length;
if ( atime->type == V_ASN1_UTCTIME )
{
if ( remaining < 11 || remaining > 17 )
{
EmitWeird("x509_utc_length", f);
return 0;
}
if ( atime->type == V_ASN1_UTCTIME ) {
if ( remaining < 11 || remaining > 17 ) {
EmitWeird("x509_utc_length", f);
return 0;
}
if ( pString[remaining - 1] != 'Z' )
{
// not valid according to RFC 2459 4.1.2.5.1
EmitWeird("x509_utc_format", f);
return 0;
}
if ( pString[remaining - 1] != 'Z' ) {
// not valid according to RFC 2459 4.1.2.5.1
EmitWeird("x509_utc_format", f);
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';
}
// 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
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 )
{
EmitWeird("x509_gen_time_length", f);
return 0;
}
if ( remaining < 12 || remaining > 23 ) {
EmitWeird("x509_gen_time_length", f);
return 0;
}
memcpy(pBuffer, pString, 12);
pBuffer += 12;
pString += 12;
remaining -= 12;
}
else
{
EmitWeird("x509_invalid_time_type", f);
return 0;
}
memcpy(pBuffer, pString, 12);
pBuffer += 12;
pString += 12;
remaining -= 12;
}
else {
EmitWeird("x509_invalid_time_type", f);
return 0;
}
if ( (remaining == 0) || (*pString == 'Z') || (*pString == '-') || (*pString == '+') )
{
*(pBuffer++) = '0';
*(pBuffer++) = '0';
}
if ( (remaining == 0) || (*pString == 'Z') || (*pString == '-') || (*pString == '+') ) {
*(pBuffer++) = '0';
*(pBuffer++) = '0';
}
else if ( remaining >= 2 )
{
*(pBuffer++) = *(pString++);
*(pBuffer++) = *(pString++);
else if ( remaining >= 2 ) {
*(pBuffer++) = *(pString++);
*(pBuffer++) = *(pString++);
remaining -= 2;
remaining -= 2;
// Skip any fractional seconds...
if ( (remaining > 0) && (*pString == '.') )
{
pString++;
remaining--;
// Skip any fractional seconds...
if ( (remaining > 0) && (*pString == '.') ) {
pString++;
remaining--;
while ( (remaining > 0) && (*pString >= '0') && (*pString <= '9') )
{
pString++;
remaining--;
}
}
}
while ( (remaining > 0) && (*pString >= '0') && (*pString <= '9') ) {
pString++;
remaining--;
}
}
}
else
{
EmitWeird("x509_time_add_char", f);
return 0;
}
else {
EmitWeird("x509_time_add_char", f);
return 0;
}
*(pBuffer++) = 'Z';
*(pBuffer++) = '\0';
*(pBuffer++) = 'Z';
*(pBuffer++) = '\0';
time_t lSecondsFromUTC;
time_t lSecondsFromUTC;
if ( remaining == 0 || *pString == 'Z' )
lSecondsFromUTC = 0;
else
{
if ( remaining < 5 )
{
EmitWeird("x509_time_offset_underflow", f);
return 0;
}
if ( remaining == 0 || *pString == 'Z' )
lSecondsFromUTC = 0;
else {
if ( remaining < 5 ) {
EmitWeird("x509_time_offset_underflow", f);
return 0;
}
if ( (*pString != '+') && (*pString != '-') )
{
EmitWeird("x509_time_offset_type", f);
return 0;
}
if ( (*pString != '+') && (*pString != '-') ) {
EmitWeird("x509_time_offset_type", f);
return 0;
}
lSecondsFromUTC = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60;
lSecondsFromUTC += (pString[3] - '0') * 10 + (pString[4] - '0');
lSecondsFromUTC = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60;
lSecondsFromUTC += (pString[3] - '0') * 10 + (pString[4] - '0');
if ( *pString == '-' )
lSecondsFromUTC = -lSecondsFromUTC;
}
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');
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;
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
lTime.tm_wday = 0;
lTime.tm_yday = 0;
lTime.tm_isdst = 0; // No DST adjustment requested
lResult = mktime(&lTime);
lResult = mktime(&lTime);
if ( lResult )
{
if ( lTime.tm_isdst != 0 )
lResult -= 3600; // mktime may adjust for DST (OS dependent)
if ( lResult ) {
if ( lTime.tm_isdst != 0 )
lResult -= 3600; // mktime may adjust for DST (OS dependent)
lResult += lSecondsFromUTC;
}
lResult += lSecondsFromUTC;
}
else
lResult = 0;
else
lResult = 0;
return lResult;
}
return lResult;
}
void 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 nonexistent) 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.
void 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 nonexistent) 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.
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);
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 )
{
OPENSSL_free(ext_val_second_pointer);
reporter->Error(
"X509::ParseSignedCertificateTimestamps could not parse inner octet string");
return;
}
ASN1_OCTET_STRING* inner = d2i_ASN1_OCTET_STRING(NULL, (const unsigned char**)&ext_val_copy, ext_val->length);
if ( ! inner ) {
OPENSSL_free(ext_val_second_pointer);
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);
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");
}
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");
}
ASN1_OCTET_STRING_free(inner);
OPENSSL_free(ext_val_second_pointer);
ASN1_OCTET_STRING_free(inner);
OPENSSL_free(ext_val_second_pointer);
interp->FlowEOF();
interp->FlowEOF();
delete interp;
delete conn;
}
delete interp;
delete conn;
}
void X509Common::ParseExtension(X509_EXTENSION* ex, const EventHandlerPtr& h, bool global)
{
char name[256];
char oid[256];
void X509Common::ParseExtension(X509_EXTENSION* ex, const 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));
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);
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;
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) )
{
unsigned char* buf = nullptr;
int len = i2d_ASN1_OCTET_STRING(X509_EXTENSION_get_data(ex), &buf);
if ( len >= 0 )
{
BIO_write(bio, buf, len);
OPENSSL_free(buf);
}
}
BIO* bio = BIO_new(BIO_s_mem());
if ( ! X509V3_EXT_print(bio, ex, 0, 0) ) {
unsigned char* buf = nullptr;
int len = i2d_ASN1_OCTET_STRING(X509_EXTENSION_get_data(ex), &buf);
if ( len >= 0 ) {
BIO_write(bio, buf, len);
OPENSSL_free(buf);
}
}
auto ext_val = GetExtensionFromBIO(bio, GetFile());
auto ext_val = GetExtensionFromBIO(bio, GetFile());
if ( ! h )
{
// let individual analyzers parse more.
ParseExtensionsSpecific(ex, global, ext_asn, oid);
return;
}
if ( ! h ) {
// let individual analyzers parse more.
ParseExtensionsSpecific(ex, global, ext_asn, oid);
return;
}
if ( ! ext_val )
ext_val = make_intrusive<StringVal>(0, "");
if ( ! ext_val )
ext_val = make_intrusive<StringVal>(0, "");
auto pX509Ext = make_intrusive<RecordVal>(BifType::Record::X509::Extension);
pX509Ext->Assign(0, name);
auto pX509Ext = make_intrusive<RecordVal>(BifType::Record::X509::Extension);
pX509Ext->Assign(0, name);
if ( short_name && strlen(short_name) > 0 )
pX509Ext->Assign(1, short_name);
if ( short_name && strlen(short_name) > 0 )
pX509Ext->Assign(1, short_name);
pX509Ext->Assign(2, oid);
pX509Ext->Assign(3, critical);
pX509Ext->Assign(4, ext_val);
pX509Ext->Assign(2, oid);
pX509Ext->Assign(3, critical);
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...
// 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...
if ( h == ocsp_extension )
event_mgr.Enqueue(h, GetFile()->ToVal(), std::move(pX509Ext), val_mgr->Bool(global));
else
event_mgr.Enqueue(h, GetFile()->ToVal(), std::move(pX509Ext));
if ( h == ocsp_extension )
event_mgr.Enqueue(h, GetFile()->ToVal(), std::move(pX509Ext), val_mgr->Bool(global));
else
event_mgr.Enqueue(h, GetFile()->ToVal(), std::move(pX509Ext));
// let individual analyzers parse more.
ParseExtensionsSpecific(ex, global, ext_asn, oid);
}
// let individual analyzers parse more.
ParseExtensionsSpecific(ex, global, ext_asn, oid);
}
StringValPtr X509Common::GetExtensionFromBIO(BIO* bio, file_analysis::File* f)
{
BIO_flush(bio);
ERR_clear_error();
int length = BIO_pending(bio);
StringValPtr X509Common::GetExtensionFromBIO(BIO* bio, file_analysis::File* f) {
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));
EmitWeird("x509_get_ext_from_bio", f, tmp);
BIO_free_all(bio);
return nullptr;
}
if ( ERR_peek_error() != 0 ) {
char tmp[120];
ERR_error_string_n(ERR_get_error(), tmp, sizeof(tmp));
EmitWeird("x509_get_ext_from_bio", f, tmp);
BIO_free_all(bio);
return nullptr;
}
if ( length == 0 )
{
BIO_free_all(bio);
return val_mgr->EmptyString();
}
if ( length == 0 ) {
BIO_free_all(bio);
return val_mgr->EmptyString();
}
char* buffer = (char*)malloc(length);
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 nullptr;
}
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 nullptr;
}
BIO_read(bio, (void*)buffer, length);
auto ext_val = make_intrusive<StringVal>(length, buffer);
BIO_read(bio, (void*)buffer, length);
auto ext_val = make_intrusive<StringVal>(length, buffer);
free(buffer);
BIO_free_all(bio);
free(buffer);
BIO_free_all(bio);
return ext_val;
}
return ext_val;
}
} // namespace zeek::file_analysis::detail
} // namespace zeek::file_analysis::detail

View file

@ -10,52 +10,48 @@
#include "zeek/file_analysis/Analyzer.h"
namespace zeek
{
namespace zeek {
class EventHandlerPtr;
class Reporter;
class StringVal;
template <class T> class IntrusivePtr;
template<class T>
class IntrusivePtr;
using StringValPtr = IntrusivePtr<StringVal>;
namespace file_analysis
{
namespace file_analysis {
class File;
namespace detail
{
namespace detail {
class X509Common : public file_analysis::Analyzer
{
class X509Common : public file_analysis::Analyzer {
public:
~X509Common() override{};
~X509Common() override{};
/**
* 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.
*
* @param f an associated file, if any (used for error reporting).
*
* @return The X509 extension value.
*/
static StringValPtr GetExtensionFromBIO(BIO* bio, file_analysis::File* f = nullptr);
/**
* 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.
*
* @param f an associated file, if any (used for error reporting).
*
* @return The X509 extension value.
*/
static StringValPtr GetExtensionFromBIO(BIO* bio, file_analysis::File* f = nullptr);
static double GetTimeFromAsn1(const ASN1_TIME* atime, file_analysis::File* f,
Reporter* reporter);
static double GetTimeFromAsn1(const ASN1_TIME* atime, file_analysis::File* f, Reporter* reporter);
protected:
X509Common(const zeek::Tag& arg_tag, RecordValPtr arg_args, file_analysis::File* arg_file);
X509Common(const zeek::Tag& arg_tag, RecordValPtr arg_args, file_analysis::File* arg_file);
void ParseExtension(X509_EXTENSION* ex, const EventHandlerPtr& h, bool global);
void ParseSignedCertificateTimestamps(X509_EXTENSION* ext);
virtual void ParseExtensionsSpecific(X509_EXTENSION* ex, bool, ASN1_OBJECT*, const char*) = 0;
};
void ParseExtension(X509_EXTENSION* ex, const EventHandlerPtr& h, bool global);
void ParseSignedCertificateTimestamps(X509_EXTENSION* ext);
virtual void ParseExtensionsSpecific(X509_EXTENSION* ex, bool, ASN1_OBJECT*, const char*) = 0;
};
} // namespace detail
} // namespace file_analysis
} // namespace zeek
} // namespace detail
} // namespace file_analysis
} // namespace zeek