Skip to content
API Documentation
BastionGuard™
BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp} Namespace Reference

Classes

struct  CommandResult
struct  IgnoredPathRule

Functions

CommandResult run_command (const std::vector< std::string > &arguments)
std::string trim (std::string value)
bool parse_bool (const std::string &value)
ServiceStatus parse_status (const std::string &text)
bool path_starts_with (const std::filesystem::path &path, const std::filesystem::path &prefix)
std::vector< IgnoredPathRule > read_ignored_paths ()
bool is_ignored_event_path (const std::string &filename, const std::vector< IgnoredPathRule > &rules)
ApplicationRules read_application_rules ()
std::vector< Event > read_recent_events (const std::size_t maximum)
std::string octal_argument (const std::uint32_t value)
bool command_success (const CommandResult &result, const std::string &successFallback, std::string &message)

Function Documentation

◆ command_success()

bool BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::command_success ( const CommandResult & result,
const std::string & successFallback,
std::string & message )

Definition at line 280 of file RootGuardClient.cpp.

282{
283 message = trim(result.output);
284 const bool success = result.exit_code == 0;
285 if (message.empty()) {
286 if (success) {
287 message = successFallback;
288 } else {
289 message = "RootGuard command failed with exit code " +
290 std::to_string(result.exit_code) + ".";
291 }
292 }
293 return success;
294}

References command_success(), BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::CommandResult::exit_code, BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::CommandResult::output, and trim().

Referenced by command_success().

◆ is_ignored_event_path()

bool BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::is_ignored_event_path ( const std::string & filename,
const std::vector< IgnoredPathRule > & rules )

Definition at line 197 of file RootGuardClient.cpp.

199{
200 if (filename.empty() || filename == "--" || filename.front() != '/')
201 return false;
202 const std::filesystem::path path(filename);
203 for (const auto& rule : rules) {
204 if (rule.recursive ? path_starts_with(path, rule.path)
205 : path.lexically_normal() == rule.path)
206 return true;
207 }
208 return false;
209}
bool path_starts_with(const std::filesystem::path &path, const std::filesystem::path &prefix)

References is_ignored_event_path(), and path_starts_with().

Referenced by is_ignored_event_path(), and read_recent_events().

◆ octal_argument()

std::string BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::octal_argument ( const std::uint32_t value)

Definition at line 273 of file RootGuardClient.cpp.

274{
275 std::ostringstream output;
276 output << std::oct << (value & 07777U);
277 return output.str();
278}

References octal_argument().

Referenced by octal_argument().

◆ parse_bool()

bool BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::parse_bool ( const std::string & value)

Definition at line 117 of file RootGuardClient.cpp.

118{
119 return value == "true" || value == "1" || value == "yes" || value == "on";
120}

References parse_bool().

Referenced by parse_bool(), and parse_status().

◆ parse_status()

ServiceStatus BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::parse_status ( const std::string & text)

Definition at line 122 of file RootGuardClient.cpp.

123{
124 ServiceStatus status;
125 std::istringstream input(text);
126 std::string line;
127 while (std::getline(input, line)) {
128 const auto separator = line.find('=');
129 if (separator == std::string::npos)
130 continue;
131 const std::string key = trim(line.substr(0, separator));
132 const std::string value = trim(line.substr(separator + 1));
133 if (key == "active") status.active = parse_bool(value);
134 else if (key == "init") status.init_system = value;
135 else if (key == "state") status.state = value;
136 else if (key == "mode") status.mode = value;
137 else if (key == "metadata_action") status.metadata_action = value;
138 else if (key == "global_metadata_action") status.global_metadata_action = value;
139 else if (key == "auto_block_services") status.autoBlockServices = parse_bool(value);
140 else if (key == "block_user_home") status.blockUserHome = parse_bool(value);
141 else if (key == "pid") status.pid = value;
142 }
143 status.immediateProtection = status.mode == "enforce" &&
144 status.metadata_action == "block";
145 return status;
146}

References BastionGuard::RootGuard::ServiceStatus::active, BastionGuard::RootGuard::ServiceStatus::autoBlockServices, BastionGuard::RootGuard::ServiceStatus::blockUserHome, BastionGuard::RootGuard::ServiceStatus::global_metadata_action, BastionGuard::RootGuard::ServiceStatus::immediateProtection, BastionGuard::RootGuard::ServiceStatus::init_system, BastionGuard::RootGuard::ServiceStatus::metadata_action, BastionGuard::RootGuard::ServiceStatus::mode, parse_bool(), parse_status(), BastionGuard::RootGuard::ServiceStatus::pid, BastionGuard::RootGuard::ServiceStatus::state, and trim().

Referenced by parse_status().

◆ path_starts_with()

bool BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::path_starts_with ( const std::filesystem::path & path,
const std::filesystem::path & prefix )

Definition at line 154 of file RootGuardClient.cpp.

156{
157 const auto normalized_path = path.lexically_normal();
158 const auto normalized_prefix = prefix.lexically_normal();
159 auto path_iterator = normalized_path.begin();
160 for (auto prefix_iterator = normalized_prefix.begin();
161 prefix_iterator != normalized_prefix.end();
162 ++prefix_iterator, ++path_iterator) {
163 if (path_iterator == normalized_path.end() ||
164 *path_iterator != *prefix_iterator)
165 return false;
166 }
167 return true;
168}

References path_starts_with().

Referenced by is_ignored_event_path(), and path_starts_with().

◆ read_application_rules()

ApplicationRules BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::read_application_rules ( )

Definition at line 211 of file RootGuardClient.cpp.

212{
213 std::ifstream input(BASTIONGUARD_ROOTGUARD_POLICY_VIEW);
214 ApplicationRules rules;
215 if (!input)
216 return rules;
217 std::string line;
218 while (std::getline(input, line)) {
219 const auto separator = line.find('=');
220 if (separator == std::string::npos)
221 continue;
222 const std::string key = trim(line.substr(0, separator));
223 const std::string value = trim(line.substr(separator + 1));
224 if (value.empty())
225 continue;
226 if (key == "trusted_application")
227 rules.trustedApplications.push_back(value);
228 else if (key == "trusted_executable")
229 rules.trustedExecutables.push_back(value);
230 else if (key == "denied_executable")
231 rules.deniedExecutables.push_back(value);
232 }
233 return rules;
234}
#define BASTIONGUARD_ROOTGUARD_POLICY_VIEW
std::vector< std::string > deniedExecutables
std::vector< std::string > trustedExecutables
std::vector< std::string > trustedApplications

References BASTIONGUARD_ROOTGUARD_POLICY_VIEW, BastionGuard::RootGuard::ApplicationRules::deniedExecutables, read_application_rules(), trim(), BastionGuard::RootGuard::ApplicationRules::trustedApplications, and BastionGuard::RootGuard::ApplicationRules::trustedExecutables.

Referenced by read_application_rules().

◆ read_ignored_paths()

std::vector< IgnoredPathRule > BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::read_ignored_paths ( )

Definition at line 170 of file RootGuardClient.cpp.

171{
172 std::ifstream input(BASTIONGUARD_ROOTGUARD_POLICY_VIEW);
173 if (!input)
174 return {};
175
176 std::vector<IgnoredPathRule> rules;
177 std::string line;
178 while (std::getline(input, line)) {
179 const auto separator = line.find('=');
180 if (separator == std::string::npos)
181 continue;
182 const std::string key = trim(line.substr(0, separator));
183 const std::string value = trim(line.substr(separator + 1));
184 if (value.empty() || value.front() != '/')
185 continue;
186 if (key == "ignored_path") {
187 rules.push_back(IgnoredPathRule{
188 std::filesystem::path(value).lexically_normal(), false});
189 } else if (key == "ignored_recursive") {
190 rules.push_back(IgnoredPathRule{
191 std::filesystem::path(value).lexically_normal(), true});
192 }
193 }
194 return rules;
195}

References BASTIONGUARD_ROOTGUARD_POLICY_VIEW, read_ignored_paths(), and trim().

Referenced by read_ignored_paths(), and read_recent_events().

◆ read_recent_events()

std::vector< Event > BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::read_recent_events ( const std::size_t maximum)

Definition at line 236 of file RootGuardClient.cpp.

237{
238 const auto ignored_paths = read_ignored_paths();
239 std::ifstream stream(BASTIONGUARD_ROOTGUARD_EVENT_LOG, std::ios::binary);
240 if (!stream)
241 return {};
242
243 constexpr std::streamoff kMaximumTailBytes = 2 * 1024 * 1024;
244 stream.seekg(0, std::ios::end);
245 const std::streamoff end = stream.tellg();
246 if (end < 0)
247 return {};
248 const std::streamoff start = end > kMaximumTailBytes
249 ? end - kMaximumTailBytes : 0;
250 stream.seekg(start, std::ios::beg);
251 std::string line;
252 if (start > 0)
253 std::getline(stream, line); // discard a potentially partial JSON line
254
255 std::deque<std::string> lines;
256 while (std::getline(stream, line)) {
257 lines.push_back(std::move(line));
258 if (lines.size() > maximum)
259 lines.pop_front();
260 }
261
262 std::vector<Event> events;
263 events.reserve(lines.size());
264 for (const auto& item : lines) {
265 if (auto event = parse_event_json(item)) {
266 if (!is_ignored_event_path(event->filename, ignored_paths))
267 events.push_back(std::move(*event));
268 }
269 }
270 return events;
271}
#define BASTIONGUARD_ROOTGUARD_EVENT_LOG
bool is_ignored_event_path(const std::string &filename, const std::vector< IgnoredPathRule > &rules)
std::optional< Event > parse_event_json(const std::string &line)

References BASTIONGUARD_ROOTGUARD_EVENT_LOG, is_ignored_event_path(), BastionGuard::RootGuard::parse_event_json(), read_ignored_paths(), and read_recent_events().

Referenced by read_recent_events().

◆ run_command()

CommandResult BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::run_command ( const std::vector< std::string > & arguments)

Definition at line 51 of file RootGuardClient.cpp.

52{
53 CommandResult result;
54 if (arguments.empty())
55 return result;
56
57 int descriptors[2]{};
58 if (::pipe(descriptors) != 0)
59 return result;
60
61 const pid_t child = ::fork();
62 if (child < 0) {
63 ::close(descriptors[0]);
64 ::close(descriptors[1]);
65 return result;
66 }
67
68 if (child == 0) {
69 ::close(descriptors[0]);
70 ::dup2(descriptors[1], STDOUT_FILENO);
71 ::dup2(descriptors[1], STDERR_FILENO);
72 ::close(descriptors[1]);
73
74 std::vector<char*> argv;
75 argv.reserve(arguments.size() + 1);
76 for (const auto& argument : arguments)
77 argv.push_back(const_cast<char*>(argument.c_str()));
78 argv.push_back(nullptr);
79
80 ::execv(argv.front(), argv.data());
81 _exit(127);
82 }
83
84 ::close(descriptors[1]);
85 std::array<char, 4096> buffer{};
86 for (;;) {
87 const ssize_t count = ::read(descriptors[0], buffer.data(), buffer.size());
88 if (count > 0) {
89 result.output.append(buffer.data(), static_cast<std::size_t>(count));
90 continue;
91 }
92 if (count < 0 && errno == EINTR)
93 continue;
94 break;
95 }
96 ::close(descriptors[0]);
97
98 int status = 0;
99 while (::waitpid(child, &status, 0) < 0 && errno == EINTR) {
100 }
101 if (WIFEXITED(status))
102 result.exit_code = WEXITSTATUS(status);
103 else if (WIFSIGNALED(status))
104 result.exit_code = 128 + WTERMSIG(status);
105 return result;
106}

References BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::CommandResult::exit_code, BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::CommandResult::output, and run_command().

Referenced by run_command().

◆ trim()

std::string BastionGuard::RootGuard::anonymous_namespace{RootGuardClient.cpp}::trim ( std::string value)

Definition at line 108 of file RootGuardClient.cpp.

109{
110 const auto first = value.find_first_not_of(" \t\r\n");
111 if (first == std::string::npos)
112 return {};
113 const auto last = value.find_last_not_of(" \t\r\n");
114 return value.substr(first, last - first + 1);
115}

References trim().

Referenced by command_success(), parse_status(), read_application_rules(), read_ignored_paths(), and trim().