-
Notifications
You must be signed in to change notification settings - Fork 384
/
Copy pathbpf_execve_bprm_commit_creds.c
162 lines (144 loc) · 6.34 KB
/
bpf_execve_bprm_commit_creds.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright Authors of Tetragon */
#include "vmlinux.h"
#include "api.h"
#include "bpf_tracing.h"
#include "compiler.h"
#include "common.h"
#include "process.h"
char _license[] __attribute__((section("license"), used)) = "Dual BSD/GPL";
/*
* Process execution is installing the new credentials and security attributes
* related to the new exec.
*
* This program will check the current process credentials against the new
* credentials that were adjusted by the capability LSM and will be applied to
* current task part of the execve call.
* For such case this hook must be when we are committing the new credentials
* to the task being executed.
* For such the hook must run after:
* bprm_creds_from_file()
* |__cap_bprm_creds_from_file() capability LSM where the bprm is properly set.
*
* It reads the linux_bprm->per_clear flags that are the personality flags to clear
* when we are executing a privilged program. Normally we should check the
* bprm->secureexec bit field if set to 1 or not. If bprm->secureexec is 1 then:
* The AT_SECURE of auxv will have value of 1 which means executable should be treated
* securely. Most commonly, 1 indicates that the process is executing a set-user-ID
* or set-group-ID binary (so that its real and effective UIDs or GIDs differ
* from one another), or that it gained capabilities by executing a binary file
* that has capabilities (see capabilities(7)).
* Alternatively, a nonzero value may be triggered by a Linux Security Module.
* When this value is nonzero, the dynamic linker disables the use of certain
* environment variables.
*
* However since bprm->secreexec is a bit field we have to assume and compute its
* offset to locate it first. An alternative way is to check the brpm->per_clear
* personality flags that will also be set if it is a privileged execution.
*
* After that we check the credential fields and guess which privileged execution.
* Example: if linux_bprm->cred->{euid,egid} differ from current uid and gid
* then this is probably a set-user-id or set-group-id execution.
*/
__attribute__((section("kprobe/security_bprm_committing_creds"), used)) void
BPF_KPROBE(tg_kp_bprm_committing_creds, struct linux_binprm *bprm)
{
struct execve_map_value *curr;
struct execve_heap *heap;
struct task_struct *task;
struct file *file;
__u32 pid, ruid, euid, uid, egid, gid, sec = 0, zero = 0;
__u64 tid, permitted, new_permitted, new_ambient = 0;
tid = get_current_pid_tgid();
pid = (tid >> 32);
curr = execve_map_get_noinit(pid);
if (!curr)
return;
heap = map_lookup_elem(&execve_heap, &zero);
if (!heap)
return;
memset(&heap->info, 0, sizeof(struct execve_info));
/* Read binary file information */
if (BPF_CORE_READ_INTO(&file, bprm, file) != 0)
return;
if (BPF_CORE_READ_INTO(&heap->info.i_nlink, file, f_inode, __i_nlink) != 0)
return;
if (BPF_CORE_READ_INTO(&heap->info.i_ino, file, f_inode, i_ino) != 0)
return;
/* If no flags to clear then this is not a privileged execution */
if (BPF_CORE_READ_INTO(&sec, bprm, per_clear) != 0 || sec == 0)
goto out;
/* Check if this is a setuid or setgid */
euid = BPF_CORE_READ(bprm, cred, euid.val);
egid = BPF_CORE_READ(bprm, cred, egid.val);
task = (struct task_struct *)get_current_task();
uid = BPF_CORE_READ(task, cred, uid.val);
gid = BPF_CORE_READ(task, cred, gid.val);
/* Is setuid? */
if (euid != uid) {
heap->info.secureexec |= EXEC_SETUID;
/* If euid is being changed to root? */
ruid = BPF_CORE_READ(bprm, cred, uid.val);
if (!__is_uid_global_root(ruid) && __is_uid_global_root(euid))
/* If we executed from a non root and became global effective root
* then set the EXEC_FS_SETUID to indicate that there was a privilege
* elevation through binary suid root.
* Now it is possible that the root 0 does not have capabilities
* meaning it is running with SECURE_NOROOT Sec bit set, but we still
* handle it as privileged change since running with uid root allows
* to access files, ptrace root binaries, etc. From Tetragon euid==0
* is still raising privileges.
*
* Note: there is the case of a uid being in a user namespace
* and it is mapped to uid 0 root inside that namespace that we do
* not detect now, since we do not do user ids translation into
* user namespaces. For such case we may not report if the binary
* gained privileges through setuid. To be fixed in the future.
*/
heap->info.secureexec |= EXEC_SETUID_ROOT;
}
/* Is setgid? */
if (egid != gid) {
heap->info.secureexec |= EXEC_SETGID;
/* Is egid is being changed to real root? */
gid = BPF_CORE_READ(bprm, cred, gid.val);
if (!__is_uid_global_root(gid) && __is_uid_global_root(egid))
/* If we executed a setgid to root binary then this is a
* privilege elevation since it can now access root files, etc
*/
heap->info.secureexec |= EXEC_SETGID_ROOT;
}
/* Ensure that ambient capabilities are not set since they clash with:
* setuid/setgid on the binary.
* file capabilities on the binary.
*
* This is an extra guard. Since if the new ambient capabilities are set then
* there is no way the binary could provide extra capabilities, they cancel
* each other.
*/
BPF_CORE_READ_INTO(&new_ambient, bprm, cred, cap_ambient);
if (new_ambient)
goto out;
/* Did we gain new capabilities through execve?
*
* To determin if we gained new capabilities we compare the current permitted
* set with the new set. This can happen if:
* (1) The setuid of binary is the _mapped_ root id in current or parent owning namespace.
* This is already handled above in the setuid code path.
* (2) The file capabilities are set on the binary. If the setuid bit is not set
* then the gained capabilities are from file capabilities execution.
*/
BPF_CORE_READ_INTO(&permitted, task, cred, cap_permitted);
BPF_CORE_READ_INTO(&new_permitted, bprm, cred, cap_permitted);
if (__cap_gained(new_permitted, permitted) && euid == uid) {
/* If the setuid bit is not set then this is probably a file cap execution. */
heap->info.secureexec |= EXEC_FILE_CAPS;
}
out:
/* We cache the entry if:
* 1. Privileged execution so secureexec will be set.
* 2. Execution of an unlinked binary
*/
if (heap->info.secureexec != 0 || (heap->info.i_nlink == 0 && heap->info.i_ino != 0))
execve_joined_info_map_set(tid, &heap->info);
}