mirror of
https://github.com/zeek/zeek.git
synced 2025-10-05 16:18:19 +00:00
Free the global X509 certificate root store on shutdown
Otherwise LeakSanitizer reports its contents as leaked.
This commit is contained in:
parent
447c3712cf
commit
bf90587cb8
4 changed files with 78 additions and 40 deletions
|
@ -22,6 +22,12 @@ public:
|
||||||
config.description = "X509 and OCSP analyzer";
|
config.description = "X509 and OCSP analyzer";
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Done() override
|
||||||
|
{
|
||||||
|
plugin::Plugin::Done();
|
||||||
|
::file_analysis::X509::FreeRootStore();
|
||||||
|
}
|
||||||
} plugin;
|
} plugin;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
#include <openssl/opensslconf.h>
|
#include <openssl/opensslconf.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
|
||||||
|
namespace file_analysis {
|
||||||
|
std::map<Val*, X509_STORE*> X509::x509_stores;
|
||||||
|
}
|
||||||
|
|
||||||
using namespace file_analysis;
|
using namespace file_analysis;
|
||||||
|
|
||||||
file_analysis::X509::X509(RecordVal* args, file_analysis::File* file)
|
file_analysis::X509::X509(RecordVal* args, file_analysis::File* file)
|
||||||
|
@ -213,6 +217,47 @@ RecordVal* file_analysis::X509::ParseCertificate(X509Val* cert_val, File* f)
|
||||||
return pX509Cert;
|
return pX509Cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
X509_STORE* file_analysis::X509::GetRootStore(TableVal* root_certs)
|
||||||
|
{
|
||||||
|
// If this certificate store was built previously, just reuse the old one.
|
||||||
|
if ( x509_stores.count(root_certs) > 0 )
|
||||||
|
return x509_stores[root_certs];
|
||||||
|
|
||||||
|
X509_STORE* ctx = X509_STORE_new();
|
||||||
|
ListVal* idxs = root_certs->ConvertToPureList();
|
||||||
|
|
||||||
|
// Build the validation store
|
||||||
|
for ( int i = 0; i < idxs->Length(); ++i )
|
||||||
|
{
|
||||||
|
Val* key = idxs->Index(i);
|
||||||
|
StringVal *sv = root_certs->Lookup(key)->AsStringVal();
|
||||||
|
assert(sv);
|
||||||
|
const uint8_t* data = sv->Bytes();
|
||||||
|
::X509* x = d2i_X509(NULL, &data, sv->Len());
|
||||||
|
if ( ! x )
|
||||||
|
{
|
||||||
|
builtin_error(fmt("Root CA error: %s", ERR_error_string(ERR_get_error(),NULL)));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
X509_STORE_add_cert(ctx, x);
|
||||||
|
X509_free(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete idxs;
|
||||||
|
|
||||||
|
// Save the newly constructed certificate store into the cacheing map.
|
||||||
|
x509_stores[root_certs] = ctx;
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_analysis::X509::FreeRootStore()
|
||||||
|
{
|
||||||
|
for ( const auto& e : x509_stores )
|
||||||
|
X509_STORE_free(e.second);
|
||||||
|
}
|
||||||
|
|
||||||
void file_analysis::X509::ParseBasicConstraints(X509_EXTENSION* ex)
|
void file_analysis::X509::ParseBasicConstraints(X509_EXTENSION* ex)
|
||||||
{
|
{
|
||||||
assert(OBJ_obj2nid(X509_EXTENSION_get_object(ex)) == NID_basic_constraints);
|
assert(OBJ_obj2nid(X509_EXTENSION_get_object(ex)) == NID_basic_constraints);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "OpaqueVal.h"
|
#include "OpaqueVal.h"
|
||||||
#include "X509Common.h"
|
#include "X509Common.h"
|
||||||
|
@ -89,6 +90,28 @@ public:
|
||||||
static file_analysis::Analyzer* Instantiate(RecordVal* args, File* file)
|
static file_analysis::Analyzer* Instantiate(RecordVal* args, File* file)
|
||||||
{ return new X509(args, file); }
|
{ return new X509(args, file); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
X509(RecordVal* args, File* file);
|
X509(RecordVal* args, File* file);
|
||||||
|
|
||||||
|
@ -102,6 +125,8 @@ private:
|
||||||
// Helpers for ParseCertificate.
|
// Helpers for ParseCertificate.
|
||||||
static StringVal* KeyCurve(EVP_PKEY *key);
|
static StringVal* KeyCurve(EVP_PKEY *key);
|
||||||
static unsigned int KeyLength(EVP_PKEY *key);
|
static unsigned int KeyLength(EVP_PKEY *key);
|
||||||
|
/** X509 stores associated with global script-layer values */
|
||||||
|
static std::map<Val*, X509_STORE*> x509_stores;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -10,9 +10,6 @@
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
|
||||||
// This is the indexed map of X509 certificate stores.
|
|
||||||
static map<Val*, X509_STORE*> x509_stores;
|
|
||||||
|
|
||||||
// construct an error record
|
// construct an error record
|
||||||
RecordVal* x509_result_record(uint64_t num, const char* reason, Val* chainVector = 0)
|
RecordVal* x509_result_record(uint64_t num, const char* reason, Val* chainVector = 0)
|
||||||
{
|
{
|
||||||
|
@ -26,41 +23,6 @@ RecordVal* x509_result_record(uint64_t num, const char* reason, Val* chainVector
|
||||||
return rrecord;
|
return rrecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
X509_STORE* x509_get_root_store(TableVal* root_certs)
|
|
||||||
{
|
|
||||||
// If this certificate store was built previously, just reuse the old one.
|
|
||||||
if ( x509_stores.count(root_certs) > 0 )
|
|
||||||
return x509_stores[root_certs];
|
|
||||||
|
|
||||||
X509_STORE* ctx = X509_STORE_new();
|
|
||||||
ListVal* idxs = root_certs->ConvertToPureList();
|
|
||||||
|
|
||||||
// Build the validation store
|
|
||||||
for ( int i = 0; i < idxs->Length(); ++i )
|
|
||||||
{
|
|
||||||
Val* key = idxs->Index(i);
|
|
||||||
StringVal *sv = root_certs->Lookup(key)->AsStringVal();
|
|
||||||
assert(sv);
|
|
||||||
const uint8_t* data = sv->Bytes();
|
|
||||||
X509* x = d2i_X509(NULL, &data, sv->Len());
|
|
||||||
if ( ! x )
|
|
||||||
{
|
|
||||||
builtin_error(fmt("Root CA error: %s", ERR_error_string(ERR_get_error(),NULL)));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
X509_STORE_add_cert(ctx, x);
|
|
||||||
X509_free(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete idxs;
|
|
||||||
|
|
||||||
// Save the newly constructed certificate store into the cacheing map.
|
|
||||||
x509_stores[root_certs] = ctx;
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get all cretificates starting at the second one (assuming the first one is the host certificate)
|
// get all cretificates starting at the second one (assuming the first one is the host certificate)
|
||||||
STACK_OF(X509)* x509_get_untrusted_stack(VectorVal* certs_vec)
|
STACK_OF(X509)* x509_get_untrusted_stack(VectorVal* certs_vec)
|
||||||
{
|
{
|
||||||
|
@ -254,7 +216,7 @@ function x509_get_certificate_string%(cert: opaque of x509, pem: bool &default=F
|
||||||
function x509_ocsp_verify%(certs: x509_opaque_vector, ocsp_reply: string, root_certs: table_string_of_string, verify_time: time &default=network_time()%): X509::Result
|
function x509_ocsp_verify%(certs: x509_opaque_vector, ocsp_reply: string, root_certs: table_string_of_string, verify_time: time &default=network_time()%): X509::Result
|
||||||
%{
|
%{
|
||||||
RecordVal* rval = 0;
|
RecordVal* rval = 0;
|
||||||
X509_STORE* ctx = x509_get_root_store(root_certs->AsTableVal());
|
X509_STORE* ctx = ::file_analysis::X509::GetRootStore(root_certs->AsTableVal());
|
||||||
if ( ! ctx )
|
if ( ! ctx )
|
||||||
return x509_result_record(-1, "Problem initializing root store");
|
return x509_result_record(-1, "Problem initializing root store");
|
||||||
|
|
||||||
|
@ -540,7 +502,7 @@ x509_ocsp_cleanup:
|
||||||
## x509_get_certificate_string x509_ocsp_verify sct_verify
|
## x509_get_certificate_string x509_ocsp_verify sct_verify
|
||||||
function x509_verify%(certs: x509_opaque_vector, root_certs: table_string_of_string, verify_time: time &default=network_time()%): X509::Result
|
function x509_verify%(certs: x509_opaque_vector, root_certs: table_string_of_string, verify_time: time &default=network_time()%): X509::Result
|
||||||
%{
|
%{
|
||||||
X509_STORE* ctx = x509_get_root_store(root_certs->AsTableVal());
|
X509_STORE* ctx = ::file_analysis::X509::GetRootStore(root_certs->AsTableVal());
|
||||||
if ( ! ctx )
|
if ( ! ctx )
|
||||||
return x509_result_record(-1, "Problem initializing root store");
|
return x509_result_record(-1, "Problem initializing root store");
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue