1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | /* |
4 | * Copyright 2020 Google LLC. |
5 | */ |
6 | |
7 | #include "vmlinux.h" |
8 | #include <errno.h> |
9 | #include <bpf/bpf_helpers.h> |
10 | #include <bpf/bpf_tracing.h> |
11 | |
12 | char _license[] SEC("license" ) = "GPL" ; |
13 | |
14 | #define DUMMY_STORAGE_VALUE 0xdeadbeef |
15 | |
16 | __u32 monitored_pid = 0; |
17 | int inode_storage_result = -1; |
18 | int sk_storage_result = -1; |
19 | int task_storage_result = -1; |
20 | |
21 | struct local_storage { |
22 | struct inode *exec_inode; |
23 | __u32 value; |
24 | }; |
25 | |
26 | struct { |
27 | __uint(type, BPF_MAP_TYPE_INODE_STORAGE); |
28 | __uint(map_flags, BPF_F_NO_PREALLOC); |
29 | __type(key, int); |
30 | __type(value, struct local_storage); |
31 | } inode_storage_map SEC(".maps" ); |
32 | |
33 | struct { |
34 | __uint(type, BPF_MAP_TYPE_SK_STORAGE); |
35 | __uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE); |
36 | __type(key, int); |
37 | __type(value, struct local_storage); |
38 | } sk_storage_map SEC(".maps" ); |
39 | |
40 | struct { |
41 | __uint(type, BPF_MAP_TYPE_SK_STORAGE); |
42 | __uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE); |
43 | __type(key, int); |
44 | __type(value, struct local_storage); |
45 | } sk_storage_map2 SEC(".maps" ); |
46 | |
47 | struct { |
48 | __uint(type, BPF_MAP_TYPE_TASK_STORAGE); |
49 | __uint(map_flags, BPF_F_NO_PREALLOC); |
50 | __type(key, int); |
51 | __type(value, struct local_storage); |
52 | } task_storage_map SEC(".maps" ); |
53 | |
54 | struct { |
55 | __uint(type, BPF_MAP_TYPE_TASK_STORAGE); |
56 | __uint(map_flags, BPF_F_NO_PREALLOC); |
57 | __type(key, int); |
58 | __type(value, struct local_storage); |
59 | } task_storage_map2 SEC(".maps" ); |
60 | |
61 | SEC("lsm/inode_unlink" ) |
62 | int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim) |
63 | { |
64 | __u32 pid = bpf_get_current_pid_tgid() >> 32; |
65 | struct bpf_local_storage *local_storage; |
66 | struct local_storage *storage; |
67 | struct task_struct *task; |
68 | bool is_self_unlink; |
69 | |
70 | if (pid != monitored_pid) |
71 | return 0; |
72 | |
73 | task = bpf_get_current_task_btf(); |
74 | if (!task) |
75 | return 0; |
76 | |
77 | task_storage_result = -1; |
78 | |
79 | storage = bpf_task_storage_get(&task_storage_map, task, 0, 0); |
80 | if (!storage) |
81 | return 0; |
82 | |
83 | /* Don't let an executable delete itself */ |
84 | is_self_unlink = storage->exec_inode == victim->d_inode; |
85 | |
86 | storage = bpf_task_storage_get(&task_storage_map2, task, 0, |
87 | BPF_LOCAL_STORAGE_GET_F_CREATE); |
88 | if (!storage || storage->value) |
89 | return 0; |
90 | |
91 | if (bpf_task_storage_delete(&task_storage_map, task)) |
92 | return 0; |
93 | |
94 | /* Ensure that the task_storage_map is disconnected from the storage. |
95 | * The storage memory should not be freed back to the |
96 | * bpf_mem_alloc. |
97 | */ |
98 | local_storage = task->bpf_storage; |
99 | if (!local_storage || local_storage->smap) |
100 | return 0; |
101 | |
102 | task_storage_result = 0; |
103 | |
104 | return is_self_unlink ? -EPERM : 0; |
105 | } |
106 | |
107 | SEC("lsm.s/inode_rename" ) |
108 | int BPF_PROG(inode_rename, struct inode *old_dir, struct dentry *old_dentry, |
109 | struct inode *new_dir, struct dentry *new_dentry, |
110 | unsigned int flags) |
111 | { |
112 | struct local_storage *storage; |
113 | int err; |
114 | |
115 | /* new_dentry->d_inode can be NULL when the inode is renamed to a file |
116 | * that did not exist before. The helper should be able to handle this |
117 | * NULL pointer. |
118 | */ |
119 | bpf_inode_storage_get(&inode_storage_map, new_dentry->d_inode, 0, |
120 | BPF_LOCAL_STORAGE_GET_F_CREATE); |
121 | |
122 | storage = bpf_inode_storage_get(&inode_storage_map, old_dentry->d_inode, |
123 | 0, 0); |
124 | if (!storage) |
125 | return 0; |
126 | |
127 | if (storage->value != DUMMY_STORAGE_VALUE) |
128 | inode_storage_result = -1; |
129 | |
130 | err = bpf_inode_storage_delete(&inode_storage_map, old_dentry->d_inode); |
131 | if (!err) |
132 | inode_storage_result = err; |
133 | |
134 | return 0; |
135 | } |
136 | |
137 | SEC("lsm.s/socket_bind" ) |
138 | int BPF_PROG(socket_bind, struct socket *sock, struct sockaddr *address, |
139 | int addrlen) |
140 | { |
141 | __u32 pid = bpf_get_current_pid_tgid() >> 32; |
142 | struct local_storage *storage; |
143 | |
144 | if (pid != monitored_pid) |
145 | return 0; |
146 | |
147 | storage = bpf_sk_storage_get(&sk_storage_map, sock->sk, 0, 0); |
148 | if (!storage) |
149 | return 0; |
150 | |
151 | sk_storage_result = -1; |
152 | if (storage->value != DUMMY_STORAGE_VALUE) |
153 | return 0; |
154 | |
155 | /* This tests that we can associate multiple elements |
156 | * with the local storage. |
157 | */ |
158 | storage = bpf_sk_storage_get(&sk_storage_map2, sock->sk, 0, |
159 | BPF_LOCAL_STORAGE_GET_F_CREATE); |
160 | if (!storage) |
161 | return 0; |
162 | |
163 | if (bpf_sk_storage_delete(&sk_storage_map2, sock->sk)) |
164 | return 0; |
165 | |
166 | storage = bpf_sk_storage_get(&sk_storage_map2, sock->sk, 0, |
167 | BPF_LOCAL_STORAGE_GET_F_CREATE); |
168 | if (!storage) |
169 | return 0; |
170 | |
171 | if (bpf_sk_storage_delete(&sk_storage_map, sock->sk)) |
172 | return 0; |
173 | |
174 | /* Ensure that the sk_storage_map is disconnected from the storage. */ |
175 | if (!sock->sk->sk_bpf_storage || sock->sk->sk_bpf_storage->smap) |
176 | return 0; |
177 | |
178 | sk_storage_result = 0; |
179 | return 0; |
180 | } |
181 | |
182 | SEC("lsm.s/socket_post_create" ) |
183 | int BPF_PROG(socket_post_create, struct socket *sock, int family, int type, |
184 | int protocol, int kern) |
185 | { |
186 | __u32 pid = bpf_get_current_pid_tgid() >> 32; |
187 | struct local_storage *storage; |
188 | |
189 | if (pid != monitored_pid) |
190 | return 0; |
191 | |
192 | storage = bpf_sk_storage_get(&sk_storage_map, sock->sk, 0, |
193 | BPF_LOCAL_STORAGE_GET_F_CREATE); |
194 | if (!storage) |
195 | return 0; |
196 | |
197 | storage->value = DUMMY_STORAGE_VALUE; |
198 | |
199 | return 0; |
200 | } |
201 | |
202 | /* This uses the local storage to remember the inode of the binary that a |
203 | * process was originally executing. |
204 | */ |
205 | SEC("lsm.s/bprm_committed_creds" ) |
206 | void BPF_PROG(exec, struct linux_binprm *bprm) |
207 | { |
208 | __u32 pid = bpf_get_current_pid_tgid() >> 32; |
209 | struct local_storage *storage; |
210 | |
211 | if (pid != monitored_pid) |
212 | return; |
213 | |
214 | storage = bpf_task_storage_get(&task_storage_map, |
215 | bpf_get_current_task_btf(), 0, |
216 | BPF_LOCAL_STORAGE_GET_F_CREATE); |
217 | if (storage) |
218 | storage->exec_inode = bprm->file->f_inode; |
219 | |
220 | storage = bpf_inode_storage_get(&inode_storage_map, bprm->file->f_inode, |
221 | 0, BPF_LOCAL_STORAGE_GET_F_CREATE); |
222 | if (!storage) |
223 | return; |
224 | |
225 | storage->value = DUMMY_STORAGE_VALUE; |
226 | } |
227 | |