Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to libddwaf 1.23.0 #175

Open
wants to merge 1 commit into
base: glopes/block-resp
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libddwaf
Submodule libddwaf updated 207 files
193 changes: 155 additions & 38 deletions src/security/library.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "library.h"

#include <atomic>
#include <string>

extern "C" {
Expand All @@ -13,14 +14,12 @@ extern "C" {
#include <rapidjson/schema.h>

#include <fstream>
#include <numeric>
#include <optional>
#include <stdexcept>
#include <string_view>
#include <utility>

#include "blocking.h"
#include "context.h"
#include "ddwaf_obj.h"
#include "util.h"

Expand Down Expand Up @@ -145,6 +144,9 @@ DDWAF_LOG_LEVEL ngx_log_level_to_ddwaf(int level) noexcept {
case NGX_LOG_EMERG:
return DDWAF_LOG_ERROR;
default:
if (level >= NGX_LOG_DEBUG_FIRST) {
return DDWAF_LOG_DEBUG;
}
return DDWAF_LOG_ERROR;
}
}
Expand Down Expand Up @@ -223,6 +225,97 @@ std::string ddwaf_subdiagnostics_to_str(const dnsec::ddwaf_map_obj &top,
ret += ")}";
return ret;
}

struct DdwafBuilderFreeFunctor {
void operator()(ddwaf_builder b) {
if (b != nullptr) {
ddwaf_builder_destroy(b);
}
}
};
class OwnedDdwafBuilder
: public dnsec::FreeableResource<ddwaf_builder, DdwafBuilderFreeFunctor> {
using FreeableResource::FreeableResource;
};

class UpdateableWafInstance {
public:
using Diagnostics = dnsec::Library::Diagnostics;

bool init(const dnsec::ddwaf_map_obj &ruleset, ddwaf_config &config,
Diagnostics &diagnostics);

std::shared_ptr<dnsec::OwnedDdwafHandle> cur_handle() {
return std::atomic_load_explicit(&cur_handle_, std::memory_order_acquire);
}

[[nodiscard]] bool add_or_update_config(std::string_view path,
const dnsec::ddwaf_map_obj &ruleset,
Diagnostics &diagnostics);

[[nodiscard]] bool remove_config(std::string_view path);

[[nodiscard]] bool update();

[[nodiscard]] bool live() { return builder_.get() != nullptr; }

private:
std::mutex builder_mut_;
OwnedDdwafBuilder builder_{nullptr};

std::shared_ptr<dnsec::OwnedDdwafHandle> cur_handle_;
};

[[nodiscard]] bool UpdateableWafInstance::init(
const dnsec::ddwaf_map_obj &ruleset, ddwaf_config &config,
Diagnostics &diagnostics) {
assert(!live());
OwnedDdwafBuilder builder{ddwaf_builder_init(&config)};
if (builder.get() == nullptr) {
return false;
}

bool res = ddwaf_builder_add_or_update_config(
*builder, dnsec::Library::kBundledRuleset.data(),
dnsec::Library::kBundledRuleset.length(),
const_cast<dnsec::ddwaf_map_obj *>(&ruleset), &*diagnostics);
if (!res) {
return false;
}

builder_ = std::move(builder);

return update();
}

[[nodiscard]] bool UpdateableWafInstance::add_or_update_config(
std::string_view path, const dnsec::ddwaf_map_obj &ruleset,
Diagnostics &diagnostics) {
std::lock_guard guard{builder_mut_};
return ddwaf_builder_add_or_update_config(
builder_.get(), path.data(), path.length(),
const_cast<dnsec::ddwaf_map_obj *>(&ruleset), &*diagnostics);
}

[[nodiscard]] bool UpdateableWafInstance::remove_config(std::string_view path) {
std::lock_guard guard{builder_mut_};
return ddwaf_builder_remove_config(builder_.get(), path.data(),
path.length());
}

[[nodiscard]] bool UpdateableWafInstance::update() {
std::lock_guard guard{builder_mut_};
ddwaf_handle new_instance = ddwaf_builder_build_instance(builder_.get());
if (!new_instance) {
return false;
}

std::shared_ptr<dnsec::OwnedDdwafHandle> new_sp =
std::make_shared<dnsec::OwnedDdwafHandle>(new_instance);
std::atomic_store_explicit(&cur_handle_, new_sp, std::memory_order::release);

return true;
}
} // namespace

namespace datadog::nginx::security {
Expand Down Expand Up @@ -479,7 +572,8 @@ std::string FinalizedConfigSettings::normalize_configured_header(
return result;
}

std::shared_ptr<OwnedDdwafHandle> Library::handle_{nullptr};
std::unique_ptr<UpdateableWafInstance> upd_waf_instance{
new UpdateableWafInstance{}};
std::atomic<bool> Library::active_{true};
std::unique_ptr<FinalizedConfigSettings> Library::config_settings_;

Expand All @@ -505,11 +599,11 @@ std::optional<ddwaf_owned_map> Library::initialize_security_library(
conf.appsec_obfuscation_value_regex().c_str();

ddwaf_owned_map ruleset = read_ruleset(conf.ruleset_file());
libddwaf_ddwaf_owned_obj<ddwaf_map_obj> diag{{}};
OwnedDdwafHandle h =
OwnedDdwafHandle{ddwaf_init(&ruleset.get(), &waf_config, &diag.get())};
if (!h.get()) {
throw std::runtime_error{"call to ddwaf_init failed:" +

Diagnostics diag{{}};
bool res = upd_waf_instance->init(ruleset.get(), waf_config, diag);
if (!res) {
throw std::runtime_error{"creation of original WAF handle failed: " +
ddwaf_diagnostics_to_str(diag.get())};
}

Expand All @@ -532,8 +626,6 @@ std::optional<ddwaf_owned_map> Library::initialize_security_library(
&source);
}

Library::handle_ = std::make_shared<OwnedDdwafHandle>(std::move(h));

BlockingService::initialize(conf.blocked_template_html(),
conf.blocked_template_json());

Expand All @@ -554,50 +646,75 @@ bool Library::active() noexcept {
return active_.load(std::memory_order_relaxed);
}

void Library::set_handle(OwnedDdwafHandle &&handle) {
std::shared_ptr<OwnedDdwafHandle> handle_sp{
std::make_shared<OwnedDdwafHandle>(std::move(handle))};
std::atomic_store_explicit(&handle_, handle_sp, std::memory_order_release);
ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, "WAF configuration updated");
}
[[nodiscard]] bool Library::update_waf_config(std::string_view path,
const ddwaf_map_obj &spec,
Diagnostics &diagnostics) {
if (!upd_waf_instance->live()) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"Attempt to update non-live WAF config");
return false;
}
bool res = upd_waf_instance->add_or_update_config(path, spec, diagnostics);

bool Library::update_ruleset(const ddwaf_map_obj &spec) {
std::shared_ptr<OwnedDdwafHandle> cur_h{get_handle_uncond()};
if (res && ngx_cycle->log->log_level & NGX_LOG_DEBUG_HTTP) {
std::string diag_str = ddwaf_diagnostics_to_str(*diagnostics);
ngx_str_t str = ngx_stringv(diag_str);
ngx_log_debug(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"ddwaf_update succeeded: %V", &str);
} else if (!res) {
std::string diag_str = ddwaf_diagnostics_to_str(*diagnostics);
ngx_str_t str = ngx_stringv(diag_str);
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "ddwaf_update failed: %V",
&str);
}

if (!cur_h) {
throw std::runtime_error{"no handle to update"};
return res;
}

[[nodiscard]] bool Library::remove_waf_config(std::string_view path) {
if (!upd_waf_instance->live()) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"Attempt to update non-live WAF config");
return false;
}
bool res = upd_waf_instance->remove_config(path);

libddwaf_ddwaf_owned_obj<ddwaf_map_obj> diag{{}};
auto new_h = OwnedDdwafHandle{ddwaf_update(cur_h->get(), &spec, &diag.get())};
if (!new_h.get()) {
throw std::runtime_error{"call to ddwaf_update failed:" +
ddwaf_diagnostics_to_str(diag.get())};
if (res) {
auto npath{ngx_stringv(path)};
ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
"WAF configuration removed for %V", &npath);
} else {
auto npath{ngx_stringv(path)};
ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,
"WAF configuration removal failed for %V", &npath);
}
return res;
}

if (ngx_cycle->log->log_level & NGX_LOG_DEBUG_HTTP) {
std::string diag_str = ddwaf_diagnostics_to_str(diag.get());
ngx_str_t str = ngx_stringv(diag_str);
ngx_log_debug(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"ddwaf_update succeeded: %V", &str);
[[nodiscard]] bool Library::regenerate_handle() {
if (!upd_waf_instance->live()) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"Attempt to regenerate handle with non-live WAF config");
return false;
}

set_handle(std::move(new_h));
return true;
bool res = upd_waf_instance->update();
if (res) {
ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, "WAF configuration updated");
} else {
ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,
"WAF configuration update failed");
}
return res;
}

std::shared_ptr<OwnedDdwafHandle> Library::get_handle() {
if (active_.load(std::memory_order_relaxed)) {
return get_handle_uncond();
return upd_waf_instance->cur_handle();
}
return {};
}

std::shared_ptr<OwnedDdwafHandle> Library::get_handle_uncond() {
return std::atomic_load_explicit(&Library::handle_,
std::memory_order_acquire);
}

std::optional<HashedStringView> Library::custom_ip_header() {
return config_settings_->custom_ip_header();
}
Expand Down
19 changes: 10 additions & 9 deletions src/security/library.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include <atomic>
#include <memory>
#include <string>
#include <string_view>

#include "../datadog_conf.h"
Expand All @@ -24,17 +23,23 @@ struct HashedStringView {

class Library {
public:
using Diagnostics = libddwaf_ddwaf_owned_obj<ddwaf_map_obj>;

static constexpr std::string_view kBundledRuleset =
"datadog/0/NONE/none/bundled_rule_data";

static std::optional<ddwaf_owned_map> initialize_security_library(
const datadog_main_conf_t &conf);

static bool update_ruleset(const ddwaf_map_obj &spec);
[[nodiscard]] static bool update_waf_config(std::string_view path,
const ddwaf_map_obj &spec,
Diagnostics &diagnostics);
[[nodiscard]] static bool remove_waf_config(std::string_view path);
[[nodiscard]] static bool regenerate_handle();

// returns the handle if active, otherwise an empty shared_ptr
static std::shared_ptr<OwnedDdwafHandle> get_handle();

// returns the handle unconditionally. It can still be an empty shared_ptr
static std::shared_ptr<OwnedDdwafHandle> get_handle_uncond();

static void set_active(bool value) noexcept;
static bool active() noexcept;

Expand All @@ -46,10 +51,6 @@ class Library {
static std::optional<std::size_t> max_saved_output_data();

protected:
static void set_handle(OwnedDdwafHandle &&handle);

// must be handled atomically!
static std::shared_ptr<OwnedDdwafHandle> handle_; // NOLINT
static std::atomic<bool> active_; // NOLINT
static std::unique_ptr<FinalizedConfigSettings> config_settings_; // NOLINT
};
Expand Down
Loading