Skip to content
API Documentation
BastionGuard™
bsc::rule::anonymous_namespace{Rule.cpp} Namespace Reference

Functions

std::string trim (std::string s)
std::string digest_file (const std::string &path, const EVP_MD *(*algo)())
std::string to_lower (std::string s)
std::string normalize_operand (std::string op)
std::vector< std::string > field_values (const bsc::model::Connection &c, const std::string &operand)
bool parse_ipv4 (const std::string &ip, std::uint32_t &out)
bool parse_ipv6 (const std::string &ip, in6_addr &out)
bool ip_in_cidr (const std::string &ip, const std::string &cidr)
std::optional< std::pair< unsigned long long, unsigned long long > > parse_range (const std::string &data)
std::unordered_set< std::string > load_simple_list_file (const fs::path &path, bool sensitive)
Operator parse_operator (const json &j)
json to_json_op (const Operator &op)

Variables

std::map< std::string, std::vector< std::string > > g_aliases

Function Documentation

◆ digest_file()

std::string bsc::rule::anonymous_namespace{Rule.cpp}::digest_file ( const std::string & path,
const EVP_MD * algo (*)() )

Definition at line 30 of file Rule.cpp.

30 {
31 if (path.empty()) return {};
32 std::ifstream in(path, std::ios::binary);
33 if (!in) return {};
34 EVP_MD_CTX* ctx = EVP_MD_CTX_new();
35 if (!ctx) return {};
36 unsigned char md[EVP_MAX_MD_SIZE]{};
37 unsigned int md_len = 0;
38 std::array<char, 8192> buf{};
39 std::string out;
40 if (EVP_DigestInit_ex(ctx, algo(), nullptr) == 1) {
41 while (in.read(buf.data(), buf.size()) || in.gcount() > 0) EVP_DigestUpdate(ctx, buf.data(), static_cast<size_t>(in.gcount()));
42 if (EVP_DigestFinal_ex(ctx, md, &md_len) == 1) {
43 static const char* hex = "0123456789abcdef";
44 out.reserve(md_len * 2);
45 for (unsigned int i = 0; i < md_len; ++i) { out.push_back(hex[md[i] >> 4]); out.push_back(hex[md[i] & 0x0f]); }
46 }
47 }
48 EVP_MD_CTX_free(ctx);
49 return out;
50}

References digest_file().

Referenced by digest_file(), and field_values().

◆ field_values()

std::vector< std::string > bsc::rule::anonymous_namespace{Rule.cpp}::field_values ( const bsc::model::Connection & c,
const std::string & operand )

Definition at line 80 of file Rule.cpp.

80 {
81 const auto op = normalize_operand(operand);
82 if (op == "true") return {"true"};
83 if (op == "protocol") return {c.protocol};
84 if (op == "source.ip") return {c.src_ip};
85 if (op == "dest.ip") return {c.dst_ip};
86 if (op == "dest.host") return {c.dst_host};
87 if (op == "process.path") return {c.process_path};
88 if (op == "process.name") return {c.process_comm};
89 if (op == "process.cwd") return {c.process_cwd};
90 if (op == "user.id") return {std::to_string(c.user_id)};
91 if (op == "user.name") {
92 if (passwd* pw = getpwuid(c.user_id)) return {pw->pw_name ? std::string(pw->pw_name) : std::string{}};
93 return {""};
94 }
95 if (op == "process.id") return {std::to_string(c.process_id)};
96 if (op == "source.port") return {std::to_string(c.src_port)};
97 if (op == "dest.port") return {std::to_string(c.dst_port)};
98 if (op == "process.command") {
99 std::ostringstream oss;
100 for (std::size_t i = 0; i < c.process_args.size(); ++i) {
101 if (i) oss << ' ';
102 oss << c.process_args[i];
103 }
104 return {oss.str()};
105 }
106 if (op == "process.parent.path") {
107 std::vector<std::string> out;
108 for (std::size_t i = 1; i < c.process_tree.size(); ++i) out.push_back(c.process_tree[i].first);
109 return out;
110 }
111 if (op.rfind("process.env.", 0) == 0) {
112 const auto key = op.substr(12);
113 if (const auto it = c.process_env.find(key); it != c.process_env.end()) return {it->second};
114 return {""};
115 }
116 if (op == "process.hash.sha256") {
117 if (const auto it = c.process_checksums.find("sha256"); it != c.process_checksums.end()) return {it->second};
118 return {""};
119 }
120 if (op == "process.hash.md5") {
121 if (const auto it = c.process_checksums.find("md5"); it != c.process_checksums.end()) return {it->second};
122 if (!c.process_path.empty()) return {digest_file(c.process_path, EVP_md5)};
123 return {""};
124 }
125 if (op == "process.hash.sha1") {
126 if (const auto it = c.process_checksums.find("sha1"); it != c.process_checksums.end()) return {it->second};
127 if (!c.process_path.empty()) return {digest_file(c.process_path, EVP_sha1)};
128 return {""};
129 }
130 if (op == "iface.in" || op == "iface.out") return {""};
131 return {""};
132}
std::string normalize_operand(std::string op)
Definition Rule.cpp:56
std::string digest_file(const std::string &path, const EVP_MD *(*algo)())
Definition Rule.cpp:30
std::vector< std::pair< std::string, std::uint32_t > > process_tree
std::uint32_t process_id
std::vector< std::string > process_args
std::uint32_t dst_port
std::uint32_t src_port
std::map< std::string, std::string > process_checksums
std::map< std::string, std::string > process_env
std::uint32_t user_id

References digest_file(), bsc::model::Connection::dst_host, bsc::model::Connection::dst_ip, bsc::model::Connection::dst_port, field_values(), normalize_operand(), bsc::model::Connection::process_args, bsc::model::Connection::process_checksums, bsc::model::Connection::process_comm, bsc::model::Connection::process_cwd, bsc::model::Connection::process_env, bsc::model::Connection::process_id, bsc::model::Connection::process_path, bsc::model::Connection::process_tree, bsc::model::Connection::protocol, bsc::model::Connection::src_ip, bsc::model::Connection::src_port, and bsc::model::Connection::user_id.

Referenced by field_values().

◆ ip_in_cidr()

bool bsc::rule::anonymous_namespace{Rule.cpp}::ip_in_cidr ( const std::string & ip,
const std::string & cidr )

Definition at line 142 of file Rule.cpp.

142 {
143 const auto slash = cidr.find('/');
144 if (slash == std::string::npos) return to_lower(ip) == to_lower(cidr);
145 const auto net = cidr.substr(0, slash);
146 const auto bits = std::stoi(cidr.substr(slash + 1));
147 std::uint32_t ip4{}, net4{};
148 if (parse_ipv4(ip, ip4) && parse_ipv4(net, net4)) {
149 if (bits < 0 || bits > 32) return false;
150 const std::uint32_t mask = bits == 0 ? 0u : 0xFFFFFFFFu << (32 - bits);
151 return (ip4 & mask) == (net4 & mask);
152 }
153 in6_addr ip6{}, net6{};
154 if (parse_ipv6(ip, ip6) && parse_ipv6(net, net6)) {
155 if (bits < 0 || bits > 128) return false;
156 const int full_bytes = bits / 8;
157 const int extra_bits = bits % 8;
158 if (full_bytes && std::memcmp(ip6.s6_addr, net6.s6_addr, full_bytes) != 0) return false;
159 if (extra_bits == 0) return true;
160 const std::uint8_t mask = static_cast<std::uint8_t>(0xFF << (8 - extra_bits));
161 return (ip6.s6_addr[full_bytes] & mask) == (net6.s6_addr[full_bytes] & mask);
162 }
163 return false;
164}
bool parse_ipv6(const std::string &ip, in6_addr &out)
Definition Rule.cpp:139
std::string to_lower(std::string s)
Definition Rule.cpp:52
bool parse_ipv4(const std::string &ip, std::uint32_t &out)
Definition Rule.cpp:133

References ip_in_cidr(), parse_ipv4(), parse_ipv6(), and to_lower().

Referenced by ip_in_cidr().

◆ load_simple_list_file()

std::unordered_set< std::string > bsc::rule::anonymous_namespace{Rule.cpp}::load_simple_list_file ( const fs::path & path,
bool sensitive )

Definition at line 176 of file Rule.cpp.

176 {
177 std::unordered_set<std::string> out;
178 std::ifstream in(path);
179 std::string line;
180 while (std::getline(in, line)) {
181 if (const auto comment = line.find('#'); comment != std::string::npos) line.resize(comment);
182 line.erase(std::remove_if(line.begin(), line.end(), [](unsigned char c) { return c == '\r' || c == '\n'; }), line.end());
183 while (!line.empty() && std::isspace(static_cast<unsigned char>(line.back()))) line.pop_back();
184 std::size_t start = 0; while (start < line.size() && std::isspace(static_cast<unsigned char>(line[start]))) ++start;
185 if (start >= line.size()) continue;
186 auto item = line.substr(start);
187 if (!sensitive) item = to_lower(item);
188 out.insert(std::move(item));
189 }
190 return out;
191}

References load_simple_list_file(), and to_lower().

Referenced by load_simple_list_file().

◆ normalize_operand()

std::string bsc::rule::anonymous_namespace{Rule.cpp}::normalize_operand ( std::string op)

Definition at line 56 of file Rule.cpp.

56 {
57 std::replace(op.begin(), op.end(), '_', '.');
58 if (op == "process.command" || op == "process.cmd") return "process.command";
59 if (op == "process.hash.md5") return op;
60 if (op == "process.hash.sha1") return op;
61 if (op == "user.name") return op;
62 if (op == "iface.in") return op;
63 if (op == "iface.out") return op;
64 if (op == "process.parent.path") return op;
65 if (op == "process.name" || op == "process.comm") return "process.name";
66 if (op == "process.path") return op;
67 if (op == "process.cwd") return op;
68 if (op == "process.id") return op;
69 if (op == "protocol" || op == "true" || op == "list") return op;
70 if (op == "source.ip" || op == "src.ip") return "source.ip";
71 if (op == "source.port" || op == "src.port") return "source.port";
72 if (op == "source.network" || op == "src.network") return "source.network";
73 if (op == "dest.ip" || op == "dst.ip") return "dest.ip";
74 if (op == "dest.host" || op == "dst.host") return "dest.host";
75 if (op == "dest.port" || op == "dst.port") return "dest.port";
76 if (op == "dest.network" || op == "dst.network") return "dest.network";
77 if (op == "user.id") return op;
78 return op;
79}

References normalize_operand().

Referenced by field_values(), normalize_operand(), and parse_operator().

◆ parse_ipv4()

bool bsc::rule::anonymous_namespace{Rule.cpp}::parse_ipv4 ( const std::string & ip,
std::uint32_t & out )

Definition at line 133 of file Rule.cpp.

133 {
134 in_addr addr{};
135 if (inet_pton(AF_INET, ip.c_str(), &addr) != 1) return false;
136 out = ntohl(addr.s_addr);
137 return true;
138}
#define AF_INET
Definition bsc_ebpf.c:56

References AF_INET, and parse_ipv4().

Referenced by ip_in_cidr(), and parse_ipv4().

◆ parse_ipv6()

bool bsc::rule::anonymous_namespace{Rule.cpp}::parse_ipv6 ( const std::string & ip,
in6_addr & out )

Definition at line 139 of file Rule.cpp.

139 {
140 return inet_pton(AF_INET6, ip.c_str(), &out) == 1;
141}
#define AF_INET6
Definition bsc_ebpf.c:57

References AF_INET6, and parse_ipv6().

Referenced by ip_in_cidr(), and parse_ipv6().

◆ parse_operator()

Operator bsc::rule::anonymous_namespace{Rule.cpp}::parse_operator ( const json & j)

Definition at line 192 of file Rule.cpp.

192 {
193 Operator op;
194 const auto type = j.value("type", "unknown");
195 if (type == "simple") op.type = OperatorType::Simple;
196 else if (type == "regexp") op.type = OperatorType::Regexp;
197 else if (type == "network") op.type = OperatorType::Network;
198 else if (type == "range") op.type = OperatorType::Range;
199 else if (type == "list") op.type = OperatorType::List;
200 else if (type == "lists") op.type = OperatorType::Lists;
201 op.operand = normalize_operand(j.value("operand", ""));
202 op.data = j.value("data", "");
203 op.sensitive = j.value("sensitive", false);
204 if (j.contains("list") && j["list"].is_array()) {
205 for (const auto& item : j["list"]) op.list.push_back(parse_operator(item));
206 } else if (op.type == OperatorType::List && !op.data.empty() && op.data.front() == '[') {
207 try {
208 for (const auto& item : json::parse(op.data)) op.list.push_back(parse_operator(item));
209 } catch (...) {}
210 }
211 return op;
212}
Operator parse_operator(const json &j)
Definition Rule.cpp:192
std::string operand
Definition Rule.hpp:15
std::string data
Definition Rule.hpp:16
OperatorType type
Definition Rule.hpp:14
std::vector< Operator > list
Definition Rule.hpp:18

References bsc::rule::Operator::data, bsc::rule::List, bsc::rule::Operator::list, bsc::rule::Lists, bsc::rule::Network, normalize_operand(), bsc::rule::Operator::operand, parse_operator(), bsc::rule::Range, bsc::rule::Regexp, bsc::rule::Operator::sensitive, bsc::rule::Simple, and bsc::rule::Operator::type.

Referenced by parse_operator().

◆ parse_range()

std::optional< std::pair< unsigned long long, unsigned long long > > bsc::rule::anonymous_namespace{Rule.cpp}::parse_range ( const std::string & data)

Definition at line 165 of file Rule.cpp.

165 {
166 const auto pos = data.find('-');
167 if (pos == std::string::npos) return std::nullopt;
168 const auto a = data.substr(0, pos);
169 const auto b = data.substr(pos + 1);
170 unsigned long long min = 0, max = 0;
171 auto [p1, ec1] = std::from_chars(a.data(), a.data() + a.size(), min);
172 auto [p2, ec2] = std::from_chars(b.data(), b.data() + b.size(), max);
173 if (ec1 != std::errc{} || ec2 != std::errc{} || min > max) return std::nullopt;
174 return {{min, max}};
175}

References parse_range().

Referenced by parse_range().

◆ to_json_op()

json bsc::rule::anonymous_namespace{Rule.cpp}::to_json_op ( const Operator & op)

Definition at line 213 of file Rule.cpp.

213 {
214 std::string t = "unknown";
215 switch (op.type) {
216 case OperatorType::Simple: t = "simple"; break;
217 case OperatorType::Regexp: t = "regexp"; break;
218 case OperatorType::Network: t = "network"; break;
219 case OperatorType::Range: t = "range"; break;
220 case OperatorType::List: t = "list"; break;
221 case OperatorType::Lists: t = "lists"; break;
222 default: break;
223 }
224 json j{{"type", t}, {"operand", op.operand}, {"data", op.data}, {"sensitive", op.sensitive}, {"list", json::array()}};
225 for (const auto& child : op.list) j["list"].push_back(to_json_op(child));
226 return j;
227}
json to_json_op(const Operator &op)
Definition Rule.cpp:213
nlohmann::json json
Definition Rule.cpp:20

References bsc::rule::Operator::data, bsc::rule::List, bsc::rule::Operator::list, bsc::rule::Lists, bsc::rule::Network, bsc::rule::Operator::operand, bsc::rule::Range, bsc::rule::Regexp, bsc::rule::Operator::sensitive, bsc::rule::Simple, to_json_op(), and bsc::rule::Operator::type.

Referenced by to_json_op().

◆ to_lower()

std::string bsc::rule::anonymous_namespace{Rule.cpp}::to_lower ( std::string s)

Definition at line 52 of file Rule.cpp.

52 {
53 std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
54 return s;
55}

References to_lower().

Referenced by ip_in_cidr(), load_simple_list_file(), and to_lower().

◆ trim()

std::string bsc::rule::anonymous_namespace{Rule.cpp}::trim ( std::string s)

Definition at line 24 of file Rule.cpp.

24 {
25 while (!s.empty() && std::isspace(static_cast<unsigned char>(s.front()))) s.erase(s.begin());
26 while (!s.empty() && std::isspace(static_cast<unsigned char>(s.back()))) s.pop_back();
27 return s;
28}

References trim().

Referenced by trim().

Variable Documentation

◆ g_aliases

std::map<std::string, std::vector<std::string> > bsc::rule::anonymous_namespace{Rule.cpp}::g_aliases

Definition at line 23 of file Rule.cpp.