-
Notifications
You must be signed in to change notification settings - Fork 418
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Data filter in kernel #4324
base: main
Are you sure you want to change the base?
Data filter in kernel #4324
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a first pass (skimming) review.
Tested and working with:
sudo ./dist/tracee -e security_file_open.data.pathname=/etc/passwd
sudo ./dist/tracee -e security_file_open.data.pathname='/etc/passwd*'
sudo ./dist/tracee -e security_file_open.data.pathname='*passwd-'
Amazing, @rscampos! 👏🏼
ca7b783
to
5187326
Compare
@geyslan I've pushed some changes to how we retrieve the string from args in args_buffer_t. To make it work, I added a field to args_buffer_t and modified the save_str_to_buf function. |
295957e
to
4b53fe8
Compare
This is important, it could have been a security vulnerability. |
Thank you for commenting on this, @itaysk. If filtering in the kernel isn't possible, I'll definitely try this solution. |
4b53fe8
to
20842f5
Compare
f82b46d
to
a600fb5
Compare
There is no need to filter in userspace for such a corner case. It is possible to define the result of the longest prefix match to contain the results of the policies with the sub strings as well, since the return value of the map is matched policies (or matched rules in the future) |
e491453
to
c87ab81
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good Raphael!
I did a first review on this draft, looking forward to see it merged
pkg/ebpf/c/common/buffer.h
Outdated
if (size > MAX_PATH_PREF_SIZE) { | ||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case - why do we return?
Shouldn't we set size to be MAX_PATH_PREF_SIZE and continue with copying the string to the key?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Upon reviewing the logic, it appears that the condition should account for the NULL character as follows:
if (size > MAX_PATH_PREF_SIZE - 1) {
return 0;
}
Considering your point: If we only copy part of the string, this approach would work for prefix matching but could result in incorrect matches for exact and suffix matches. Here's an example:
Assume: MAX_PATH_PREF_SIZE=12
:
Using all three filter types:
exact match: /etc/passwd
prefix match: /etc/pass*
suffix match: *passwd
An access path like /etc/passwd123
would truncate to /etc/passwd
(size 11), which would:
- Falsely match an exact match filter for
/etc/passwd
. - Incorrectly align with a suffix match filter for
*passwd
.
Does it make sense?
I think we have two possible options here:
- Only allow paths with a maximum length of
MAX_PATH_PREF_SIZE - 1
. - Allow partial strings only for prefix matching, while requiring the entire string for exact and suffix matching.
Option 1 is complete, but Option 2 is also possible and requires some minor adjustments to the function and its caller.
0cf3a5d
to
9097b76
Compare
c6469ce
to
86b4d0f
Compare
@@ -10,6 +10,7 @@ | |||
// PROTOTYPES | |||
|
|||
statfunc buf_t *get_buf(int); | |||
statfunc data_filter_lpm_key_t *get_data_filter_buf(int); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this data filter is specific to string filter, maybe we should rename to get_string_data_filter_buf
, WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah... today, since we only have string filters, the data filter is very tightly coupled with the string filter, so get_string_data_filter_buf
make sense... but on other hand, we soon will add other field type filters, so maybe we should let it generic as get_data_filter_buf
? WDYT?
for (i = 0; i < len; i++) { | ||
if (i >= MAX_DATA_FILTER_STR_SIZE) { | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we put the two checks in the for loop to make it shorter?
for (i = 0; i < len, i < MAX_DATA_FILTER_STR_SIZE; i++) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tried it for (i = 0; i < len && i < MAX_DATA_FILTER_STR_SIZE; i++) {
but for some reason the eBPF Verify complain about it. For some reason the if condition that I've added was necessary just for one of the kernels I've tested - x86 Ubuntu 22.04.4 LTS (kernel 5.15.0-124-generic).
86b4d0f
to
88809d1
Compare
- function load_str_from_buf created to retrieve str value based on index; - function reverse_string created to revert an string in order to enable suffix; - function evaluate_data_filters/match_data_filters created to apply: exact, prefix and suffix match; - eBPF maps for exact, prefix and suffix. eBPF map for hold temporary LPM TRI key; - add fields in config_map for exact, prefix and suffix match; - save offset at the specified index in the function save_str_to_buf.
- how to enable data filter in the eBPF program using the function evaluate_data_filters.
d943cf9
to
4b4e0c5
Compare
- eBPF map definition for exact, prefix, suffix match; - create updateDataFilterLPMBPF and updateDataFilterBPF to populate eBPF maps; - config map fields for exact, prefix and suffix.
- method equalities created for data filter; - handle corner case when one policy uses a substring (path) of another policy; - disable data filter (only pathname) for selected events.
4b4e0c5
to
44b7db8
Compare
- Add MatchTypes{} in cmp.AllowUnexported
44b7db8
to
9d1c99b
Compare
1. Explain what the PR does
44b7db8 Tracee kernel data filter test
817743a Tracee data filter equalities
ad4d7f0 eBPF data filter (user-space)
a9fcb0f Enable data filter in eBPF program
de54cbb eBPF data filter (kernel-space)
00eb34b Enable BPF_F_NO_PREALLOC for LPM TRIE
44b7db8 Tracee kernel data filter test
817743a Tracee data filter equalities
ad4d7f0 eBPF data filter (user-space)
a9fcb0f Enable data filter in eBPF program
de54cbb eBPF data filter (kernel-space)
2. Explain how to test it
The method for defining data filters in Tracee remains the same. However, for the
security_file_open
andmagic_write
events, if the pathname is used as a filter, the event is now filtered at the eBPF data plane, preventing it from being sent to user space for filtering.Notes for the reviewer: The following sections contain commands I used to test with policies. The results for each test group are also included. Both the policies and results are located in the zip file provided in each section.
Only exactly match
Tracee
Maps
Cmds
The results of each of the following lines are in the JSON file (results_exactly.json):
exactly_policies_results.zip
Only prefix match
Tracee
Maps
Cmds
The results of each of the following lines are in the JSON file (results_prefix.json):
prefix_policies_results.zip
Only suffix match
Tracee
Maps
Cmds
The results of each of the following lines are in the JSON file (results_suffix.json):
suffix_policies_results.zip
Mixed (exactly/prefix/suffix) match
In this section, you can see all string matches working together. The command
cat /etc/netconfig
triggers three policies simultaneously, while the commandcat /etc/host.conf
triggers two policies.Tracee
Maps
Cmds
The results of each of the following lines are in the JSON file (results_mixed.json):
mixed_policies_results.zip
Ensuring Multiple Policy Matches when LPM Trie is used - Prefix
Corner case description: When using the LPM Trie, it always returns the longest matching string. A corner case arises when one policy (e.g., policy1) uses a substring of another policy (e.g., policy2). For instance, if policy1 covers
/etc/net*
and policy2 covers/etc/netconf*
, a lookup for/etc/netconfig
currently only returns policy2 because it is the longest match. However, it should return both policy1 and policy2.A potential solution (implemented): If one suffix or prefix overlaps with another, we can simply combine their bitmaps in user space. No additional logic is required in kernel space to handle this corner case.
Tracee
Maps
Note: Policy 4 includes lines to exclude library entries from the output. These lines are solely for cleaning up the output to simplify the testing in this section.
Policy 2 (prefix /etc/net) overlaps with Policy 1 (prefix /etc/n), as /etc/n is a substring of /etc/net. This is why the key with the path "/etc/net" has equality_set_in_scopes=3, indicating that both Policy 1 and Policy 2 are part of the same equality set. Additionally, equal_in_scopes=3 shows that Policy 1 and Policy 2 are considered equal in their scopes.
Policy 3 (prefix /etc/network) encompasses both Policy 1 (prefix /etc/n) and Policy 2 (prefix /etc/net). Consequently, the key with the path "/etc/network" has equality_set_in_scopes=7, which signifies that all three policies are present within the same scope. Similarly, equal_in_scopes=7 indicates that Policy 1, Policy 2, and Policy 3 are equal in scopes.
Policy 4 (prefix /etc/netconf) also includes both Policy 1 (prefix /etc/n) and Policy 2 (prefix /etc/net). Therefore, the key with the path "/etc/netconf" has equality_set_in_scopes=11, which means that Policy 1, Policy 2, and Policy 4 are all part of the same scope. However, because Policy 4 was defined with the condition data.pathname!=/etc/netconf, equal_in_scopes=3, meaning that only Policy 1 and Policy 2 are considered equal in scopes, while Policy 4 is excluded from that equality.
In summary, Policy 2, Policy 3, and Policy 4 derive bits from other policies, reflecting their interdependencies and overlaps in scope.
Cmds
The results of each of the following lines are in the JSON file (results_corner_case_prefix.json):
cc_prefix_policies_results.zip
Ensuring Multiple Policy Matches when LPM Trie is used - Suffix
Tracee
Maps
Note: Policy 3 includes lines to exclude library entries from the output. These lines are solely for cleaning up the output to simplify the testing in this section.
Policy 1 (suffix netconfig) overlaps with Policy 2 (suffix config), as config is a substring of netconfig. This is why the key with the path "gifnocten" (which is a reversed representation of netconfig) has equality_set_in_scopes=3 and this indicates that both Policy 1 and Policy 2 are contained within the same scope. However, equal_in_scopes=3 shows that Policy 1 and Policy 2 are equal in scopes.
Policy 2 (suffix config) only contains the bitmap of Policy 2 (equality_set_in_scopes=2 and equal_in_scopes=2).
Policy 3 (suffix etc/netconfig) contains both Policy 1 (suffix netconfig) and Policy 2 (suffix config). Therefore, the key with the path "gifnocten/cte" (representing the reverse of etc/netconfig) has equality_set_in_scopes=7. This value indicates that all three policies are present within the scope. However, equal_in_scopes=3 shows that only Policy 1 and Policy 2 are equal in scopes, whereas Policy 3 is disabled in this scope because it was defined using data.pathname!=etc/netconfig.
Cmds
The results of each of the following lines are in the JSON file (results_corner_case_suffix.json):
cc_suffix_policies_results.zip
3. Other comments
TODO
First Phase:
save_str_to_buf()
, add the argument offset based on its index to facilitate direct access for theload_str_from_buf()
function./etc/net*
and policy2 covers/etc/netconf*
, a lookup for/etc/netconfig
currently only returns policy2 because it is the longest match. However, it should return both policy1 and policy2. A potential solution (work in progress) is to combine equality when such a corner case is detected.Second Phase: