Skip to content
API Documentation
BastionGuard™
AutomaticResponseSink.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
3
5
6#include <algorithm>
7#include <cstring>
8
10namespace {
11
12bool isActionableMetadataEvent(const rg_event& event) noexcept
13{
14 switch (event.event_type) {
23 break;
24 default:
25 return false;
26 }
27
28 /*
29 * Automatic containment is a state-changing operation and must never run
30 * for audit/alert events. It is allowed only after the kernel has denied
31 * the metadata change and reported RG_VERDICT_BLOCKED.
32 */
33 return event.verdict == RG_VERDICT_BLOCKED;
34}
35
36void copyString(char* destination, const std::size_t size,
37 const std::string& source) noexcept
38{
39 if (!destination || size == 0)
40 return;
41 const auto count = std::min(size - 1, source.size());
42 std::memcpy(destination, source.data(), count);
43 destination[count] = '\0';
44}
45
46} // namespace
47
49 const bool autoBlockSystemServices)
50 : downstream_(downstream),
51 autoBlockSystemServices_(autoBlockSystemServices),
53{
54}
55
57{
58 {
59 std::lock_guard lock(mutex_);
60 stopping_ = true;
61 }
62 condition_.notify_one();
63 if (worker_.joinable())
64 worker_.join();
65}
66
68{
69 autoBlockSystemServices_.store(enabled, std::memory_order_release);
70}
71
72void AutomaticResponseSink::onEvent(const rg_event& event) noexcept
73{
74 try {
75 downstream_.onEvent(event);
76
77 if (!autoBlockSystemServices_.load(std::memory_order_acquire) ||
78 !isActionableMetadataEvent(event))
79 return;
80
81 const std::filesystem::path path(event.filename);
82 const auto target = identifySystemService(path);
83 if (!target)
84 return;
85
86 /*
87 * The LSM denied the operation, therefore the service definition
88 * remains unchanged. Stopping it cannot execute attacker-controlled
89 * replacement content.
90 */
91 enqueueService(path, event);
92 } catch (...) {
93 // Automatic reaction must never terminate RootGuard.
94 }
95}
96
97void AutomaticResponseSink::enqueueService(const std::filesystem::path& path,
98 const rg_event& source) noexcept
99{
100 try {
101 const std::string key = path.lexically_normal().string();
102 std::lock_guard lock(mutex_);
103 if (stopping_ || !queuedPaths_.insert(key).second)
104 return;
105 queue_.push_back(PendingService{path, source});
106 condition_.notify_one();
107 } catch (...) {
108 }
109}
110
112{
113 for (;;) {
114 PendingService pending;
115 {
116 std::unique_lock lock(mutex_);
117 condition_.wait(lock, [this] { return stopping_ || !queue_.empty(); });
118 if (stopping_ && queue_.empty())
119 break;
120 pending = std::move(queue_.front());
121 queue_.pop_front();
122 }
123
124 const auto result = blockSystemService(pending.path);
125 emitServiceResult(pending.path, pending.source, result.success);
126
127 std::lock_guard lock(mutex_);
128 queuedPaths_.erase(pending.path.lexically_normal().string());
129 }
130}
131
132void AutomaticResponseSink::emitServiceResult(const std::filesystem::path& path,
133 const rg_event& source,
134 const bool success) noexcept
135{
136 try {
137 rg_event event = source;
139 event.verdict = success ? RG_VERDICT_BLOCKED : RG_VERDICT_ALERT;
140 copyString(event.comm, sizeof(event.comm), "rootguard-react");
141 copyString(event.filename, sizeof(event.filename), path.string());
142 downstream_.onEvent(event);
143 } catch (...) {
144 }
145}
146
147} // namespace bastionguard::rootguard
void emitServiceResult(const std::filesystem::path &path, const rg_event &source, bool success) noexcept
AutomaticResponseSink(IEventSink &downstream, bool autoBlockSystemServices)
void enqueueService(const std::filesystem::path &path, const rg_event &source) noexcept
void onEvent(const rg_event &event) noexcept override
void copyString(char *destination, const std::size_t size, const std::string &source) noexcept
std::optional< ServiceTarget > identifySystemService(const std::filesystem::path &path) noexcept
ServiceActionResult blockSystemService(const std::filesystem::path &path) noexcept
@ RG_EVENT_PROTECTED_RENAME
@ RG_EVENT_OWNER_CHANGE
@ RG_EVENT_ACL_CHANGE
@ RG_EVENT_PROTECTED_UNLINK
@ RG_EVENT_SERVICE_BLOCKED
@ RG_EVENT_INTEGRITY_DRIFT
@ RG_EVENT_PROTECTED_HARDLINK
@ RG_EVENT_XATTR_CHANGE
@ RG_EVENT_PERMISSION_CHANGE
@ RG_VERDICT_ALERT
@ RG_VERDICT_BLOCKED