Skip to content
API Documentation
BastionGuard™
main.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
9
10#include <csignal>
11#include <cstdlib>
12#include <exception>
13#include <filesystem>
14#include <fstream>
15#include <iostream>
16#include <memory>
17#include <optional>
18#include <stdexcept>
19#include <string>
20#include <string_view>
21#include <sys/stat.h>
22#include <unistd.h>
23
24namespace {
25
26volatile std::sig_atomic_t gStopRequested = 0;
27volatile std::sig_atomic_t gReloadRequested = 0;
28
29extern "C" void handleStopSignal(int) noexcept
30{
32}
33
34extern "C" void handleReloadSignal(int) noexcept
35{
37}
38
39struct Options {
40 std::filesystem::path policyPath{"/etc/bastionguard/rootguard.conf"};
41 std::optional<bastionguard::rootguard::Mode> modeOverride;
43 bool checkOnly{};
44};
45
46void printUsage(const char* executable)
47{
48 std::cerr << "uso: " << executable
49 << " [--policy FILE] [--audit|--enforce]"
50 " [--allow-unsafe-policy] [--check-policy]\n";
51}
52
53Options parseOptions(const int argc, char* argv[])
54{
55 Options options;
56 for (int index = 1; index < argc; ++index) {
57 const std::string_view argument(argv[index]);
58 if (argument == "--policy" && index + 1 < argc) {
59 options.policyPath = argv[++index];
60 } else if (argument == "--audit") {
62 } else if (argument == "--enforce") {
64 } else if (argument == "--allow-unsafe-policy") {
65 options.allowUnsafePolicy = true;
66 } else if (argument == "--check-policy") {
67 options.checkOnly = true;
68 } else {
69 printUsage(argv[0]);
70 throw std::invalid_argument("argomento non riconosciuto");
71 }
72 }
73 return options;
74}
75
77{
78 using namespace bastionguard::rootguard;
79
80 const std::string content = PolicySecurity::read(
81 options.policyPath, !options.allowUnsafePolicy);
82 Policy policy = PolicyLoader::loadFromText(content, options.policyPath);
83 if (options.modeOverride)
84 policy.mode = *options.modeOverride;
85 return policy;
86}
87
89{
90 using namespace bastionguard::rootguard;
91 const std::filesystem::path path{"/run/bastionguard-rootguard.status"};
92 const std::filesystem::path temporary = path.string() + ".tmp." +
93 std::to_string(::getpid());
94 std::ofstream output(temporary, std::ios::trunc);
95 if (!output)
96 return;
97 const bool effectiveServiceBlocking = policy.autoBlockSystemServices &&
98 policy.mode == Mode::Enforce &&
100 output << "mode=" << toString(policy.mode) << '\n'
101 << "metadata_action=" << toString(policy.metadataAction) << '\n'
102 << "global_metadata_action=" << toString(policy.globalMetadataAction) << '\n'
103 << "auto_block_services="
104 << (effectiveServiceBlocking ? "true" : "false") << '\n'
105 << "block_user_home=" << (policy.blockUserHome ? "true" : "false") << '\n';
106 output.close();
107 if (!output)
108 return;
109 ::chmod(temporary.c_str(), 0644);
110 if (::rename(temporary.c_str(), path.c_str()) != 0)
111 std::filesystem::remove(temporary);
112}
113
115{
116 const std::filesystem::path path{"/run/bastionguard-rootguard.policy-view"};
117 const std::filesystem::path temporary = path.string() + ".tmp." +
118 std::to_string(::getpid());
119 std::ofstream output(temporary, std::ios::trunc);
120 if (!output)
121 return;
122 output << "block_user_home=" << (policy.blockUserHome ? "true" : "false") << '\n'
123 << "monitor_global_privilege_metadata=" << (policy.monitorGlobalPrivilegeMetadata ? "true" : "false") << '\n'
124 << "detect_global_metadata_bursts=" << (policy.detectGlobalMetadataBursts ? "true" : "false") << '\n'
125 << "global_metadata_sample_limit=" << policy.globalMetadataSampleLimit << '\n'
126 << "metadata_burst_threshold=" << policy.metadataBurstThreshold << '\n'
127 << "metadata_burst_window_ms=" << policy.metadataBurstWindowMs << '\n'
128 << "metadata_burst_cooldown_ms=" << policy.metadataBurstCooldownMs << '\n';
129 for (const auto& rule : policy.trustedApplications)
130 output << "trusted_application=" << rule.path.string() << '\n';
131 for (const auto& rule : policy.trustedExecutables)
132 output << "trusted_executable=" << rule.path.string() << '\n';
133 for (const auto& rule : policy.deniedExecutables)
134 output << "denied_executable=" << rule.path.string() << '\n';
135 for (const auto& rule : policy.protectedPaths)
136 output << (rule.recursive ? "protected_recursive=" : "protected_path=")
137 << rule.path.string() << '\n';
138 for (const auto& rule : policy.ignoredPaths)
139 output << (rule.recursive ? "ignored_recursive=" : "ignored_path=")
140 << rule.path.string() << '\n';
141 output.close();
142 if (!output)
143 return;
144 ::chmod(temporary.c_str(), 0644);
145 if (::rename(temporary.c_str(), path.c_str()) != 0)
146 std::filesystem::remove(temporary);
147}
148
160
162{
163 using namespace bastionguard::rootguard;
164 std::cerr << "RootGuard policy: mode=" << toString(policy.mode)
165 << " unknown_action=" << toString(policy.unknownAction)
166 << " metadata_action=" << toString(policy.metadataAction)
167 << " global_metadata_action=" << toString(policy.globalMetadataAction)
168 << " credential_action=" << toString(policy.credentialAnomalyAction)
169 << " auto_block_services=" << (policy.autoBlockSystemServices ? "true" : "false")
170 << " block_user_home=" << (policy.blockUserHome ? "true" : "false")
171 << " global_metadata=" << (policy.monitorGlobalPrivilegeMetadata ? "true" : "false")
172 << " metadata_bursts=" << (policy.detectGlobalMetadataBursts ? "true" : "false")
173 << " sample_limit=" << policy.globalMetadataSampleLimit
174 << " burst_threshold=" << policy.metadataBurstThreshold
175 << " trusted_apps=" << policy.trustedApplications.size()
176 << " trusted=" << policy.trustedExecutables.size()
177 << " denied=" << policy.deniedExecutables.size()
178 << " protected=" << policy.protectedPaths.size()
179 << " ignored=" << policy.ignoredPaths.size() << '\n';
180}
181
182} // namespace
183
184int main(const int argc, char* argv[])
185{
186 using namespace bastionguard::rootguard;
187
188 try {
189 const Options options = parseOptions(argc, argv);
190 Policy policy = loadPolicy(options);
191 printPolicySummary(policy);
192
193 if (options.checkOnly)
194 return EXIT_SUCCESS;
195
196 std::signal(SIGINT, handleStopSignal);
197 std::signal(SIGTERM, handleStopSignal);
198 std::signal(SIGHUP, handleReloadSignal);
199
200 ConsoleEventSink consoleSink;
201 MultiEventSink eventSink;
202 eventSink.add(consoleSink);
203
204 std::unique_ptr<JsonEventSink> jsonSink;
205 if (!policy.eventLog.empty()) {
206 if (const auto parent = policy.eventLog.parent_path(); !parent.empty())
207 std::filesystem::create_directories(parent);
208 jsonSink = std::make_unique<JsonEventSink>(policy.eventLog);
209 eventSink.add(*jsonSink);
210 }
211
212 const auto automaticServiceBlockingEnabled = [](const Policy& activePolicy) {
213 return activePolicy.autoBlockSystemServices &&
214 activePolicy.mode == Mode::Enforce &&
215 activePolicy.metadataAction == Action::Block;
216 };
217 AutomaticResponseSink responseSink(
218 eventSink, automaticServiceBlockingEnabled(policy));
219 RootGuardEngine engine(responseSink);
220 PolicyApplyReport report = engine.start(policy);
221 writeRuntimeStatus(policy);
223
224 std::cerr << "BastionGuard RootGuard avviato: trusted-app caricati="
225 << report.trustedApplicationsLoaded << " trusted caricati="
226 << report.trustedLoaded << " denied caricati="
227 << report.deniedLoaded << " protected caricati="
228 << report.protectedLoaded << '\n';
229 for (const auto& warning : report.warnings)
230 std::cerr << "RootGuard policy warning: " << warning << '\n';
231
232 while (gStopRequested == 0) {
233 const int result = engine.pollOnce(250);
234 if (result < 0)
235 throw std::runtime_error("errore ring buffer: " + std::to_string(result));
236
237 engine.runIntegrityCheckIfDue();
238
239 if (gReloadRequested != 0) {
241 bool auditEntered = false;
242 try {
243 /*
244 * Enter audit before opening/parsing the new file. Thus a
245 * security, syntax or path-resolution failure cannot leave
246 * the previous enforcement policy active unexpectedly.
247 */
248 engine.enterAuditMode();
249 auditEntered = true;
250 responseSink.setAutoBlockSystemServices(false);
252 Policy reloaded = loadPolicy(options);
253 const auto previousEventLog = policy.eventLog;
254 const auto reloadedReport = engine.reloadPolicy(reloaded);
255 policy = std::move(reloaded);
256 responseSink.setAutoBlockSystemServices(
257 automaticServiceBlockingEnabled(policy));
258 writeRuntimeStatus(policy);
260 std::cerr << "RootGuard policy ricaricata: trusted-app="
261 << reloadedReport.trustedApplicationsLoaded
262 << " trusted="
263 << reloadedReport.trustedLoaded << " denied="
264 << reloadedReport.deniedLoaded << " protected="
265 << reloadedReport.protectedLoaded << '\n';
266 if (policy.eventLog != previousEventLog) {
267 std::cerr << "nota: una modifica di event_log richiede riavvio\n";
268 }
269 } catch (const std::exception& error) {
270 std::cerr << "ricaricamento policy fallito; "
271 << (auditEntered
272 ? "motore lasciato in audit: "
273 : "impossibile garantire il passaggio in audit: ")
274 << error.what() << '\n';
275 }
276 }
277 }
278 std::filesystem::remove("/run/bastionguard-rootguard.status");
279 } catch (const std::exception& error) {
280 std::filesystem::remove("/run/bastionguard-rootguard.status");
281 std::cerr << "RootGuard: " << error.what() << '\n';
282 return EXIT_FAILURE;
283 }
284
285 return EXIT_SUCCESS;
286}
int main(int argc, char *argv[])
static Policy loadFromText(const std::string &content, const std::filesystem::path &source="<memory>")
static std::string read(const std::filesystem::path &path, bool requireSecureOwnership)
PolicyApplyReport reloadPolicy(const Policy &policy)
int pollOnce(int timeoutMilliseconds) noexcept
PolicyApplyReport start(const Policy &policy)
Options parseOptions(const int argc, char *argv[])
Definition main.cpp:53
void handleStopSignal(int) noexcept
Definition main.cpp:29
bastionguard::rootguard::Policy auditRuntimePolicy(bastionguard::rootguard::Policy policy)
Definition main.cpp:149
volatile std::sig_atomic_t gStopRequested
Definition main.cpp:26
void printPolicySummary(const bastionguard::rootguard::Policy &policy)
Definition main.cpp:161
void printUsage(const char *executable)
Definition main.cpp:46
void writeRuntimePolicyView(const bastionguard::rootguard::Policy &policy)
Definition main.cpp:114
void writeRuntimeStatus(const bastionguard::rootguard::Policy &policy)
Definition main.cpp:88
void handleReloadSignal(int) noexcept
Definition main.cpp:34
volatile std::sig_atomic_t gReloadRequested
Definition main.cpp:27
bastionguard::rootguard::Policy loadPolicy(const Options &options)
Definition main.cpp:76
const char * toString(Mode mode) noexcept
Definition Policy.cpp:6
std::filesystem::path policyPath
Definition main.cpp:40
std::optional< bastionguard::rootguard::Mode > modeOverride
Definition main.cpp:41
std::vector< PolicyPath > trustedExecutables
Definition Policy.hpp:56
std::vector< PolicyPath > deniedExecutables
Definition Policy.hpp:57
std::uint32_t metadataBurstThreshold
Definition Policy.hpp:44
std::vector< PolicyPath > ignoredPaths
Definition Policy.hpp:59
std::uint32_t globalMetadataSampleLimit
Definition Policy.hpp:43
std::filesystem::path eventLog
Definition Policy.hpp:54
std::uint32_t metadataBurstCooldownMs
Definition Policy.hpp:46
std::vector< PolicyPath > protectedPaths
Definition Policy.hpp:58
std::uint32_t metadataBurstWindowMs
Definition Policy.hpp:45
std::vector< PolicyPath > trustedApplications
Definition Policy.hpp:55