Skip to content

Commit

Permalink
Upgrade to libddwaf 1.23.0
Browse files Browse the repository at this point in the history
  • Loading branch information
cataphract committed Feb 26, 2025
1 parent 7cfce28 commit 291cb10
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 519 deletions.
2 changes: 1 addition & 1 deletion libddwaf
Submodule libddwaf updated 207 files
197 changes: 158 additions & 39 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,100 @@ 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<ddwaf_object *>(static_cast<const ddwaf_object *>(&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 +575,7 @@ 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 +601,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 +628,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 +648,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);

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);
}

bool Library::update_ruleset(const ddwaf_map_obj &spec) {
std::shared_ptr<OwnedDdwafHandle> cur_h{get_handle_uncond()};
return res;
}

if (!cur_h) {
throw std::runtime_error{"no handle to update"};
[[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();
if (active_.load(std::memory_order_relaxed)) {
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

0 comments on commit 291cb10

Please sign in to comment.