diff options
Diffstat (limited to 'src/firemon/procevent.c')
-rw-r--r-- | src/firemon/procevent.c | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/src/firemon/procevent.c b/src/firemon/procevent.c new file mode 100644 index 000000000..d2b5f7bbf --- /dev/null +++ b/src/firemon/procevent.c | |||
@@ -0,0 +1,377 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | #include "firemon.h" | ||
21 | #include <sys/socket.h> | ||
22 | #include <linux/connector.h> | ||
23 | #include <linux/netlink.h> | ||
24 | #include <linux/cn_proc.h> | ||
25 | #include <sys/types.h> | ||
26 | #include <sys/stat.h> | ||
27 | #include <unistd.h> | ||
28 | #include <arpa/inet.h> | ||
29 | #include <time.h> | ||
30 | #define PIDS_BUFLEN 4096 | ||
31 | #define SERVER_PORT 889 // 889-899 is left unassigned by IANA | ||
32 | |||
33 | static int pid_is_firejail(pid_t pid) { | ||
34 | uid_t rv = 0; | ||
35 | |||
36 | // open stat file | ||
37 | char *file; | ||
38 | if (asprintf(&file, "/proc/%u/status", pid) == -1) { | ||
39 | perror("asprintf"); | ||
40 | exit(1); | ||
41 | } | ||
42 | FILE *fp = fopen(file, "r"); | ||
43 | if (!fp) { | ||
44 | free(file); | ||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | // look for firejail executable name | ||
49 | char buf[PIDS_BUFLEN]; | ||
50 | while (fgets(buf, PIDS_BUFLEN - 1, fp)) { | ||
51 | if (strncmp(buf, "Name:", 5) == 0) { | ||
52 | char *ptr = buf + 5; | ||
53 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { | ||
54 | ptr++; | ||
55 | } | ||
56 | if (*ptr == '\0') | ||
57 | goto doexit; | ||
58 | if (strncmp(ptr, "firejail", 8) == 0) | ||
59 | rv = 1; | ||
60 | // if (strncmp(ptr, "lxc-execute", 11) == 0) | ||
61 | // rv = 1; | ||
62 | break; | ||
63 | } | ||
64 | } | ||
65 | doexit: | ||
66 | fclose(fp); | ||
67 | free(file); | ||
68 | return rv; | ||
69 | } | ||
70 | |||
71 | |||
72 | static int procevent_netlink_setup(void) { | ||
73 | // open socket for process event connector | ||
74 | int sock; | ||
75 | if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR)) < 0) { | ||
76 | fprintf(stderr, "Error: cannot open netlink socket\n"); | ||
77 | exit(1); | ||
78 | } | ||
79 | |||
80 | // bind socket | ||
81 | struct sockaddr_nl addr; | ||
82 | memset(&addr, 0, sizeof(addr)); | ||
83 | addr.nl_pid = getpid(); | ||
84 | addr.nl_family = AF_NETLINK; | ||
85 | addr.nl_groups = CN_IDX_PROC; | ||
86 | if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { | ||
87 | fprintf(stderr, "Error: cannot bind to netlink socket\n"); | ||
88 | exit(1); | ||
89 | } | ||
90 | |||
91 | // send monitoring message | ||
92 | struct nlmsghdr nlmsghdr; | ||
93 | memset(&nlmsghdr, 0, sizeof(nlmsghdr)); | ||
94 | nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct cn_msg) + sizeof(enum proc_cn_mcast_op)); | ||
95 | nlmsghdr.nlmsg_pid = getpid(); | ||
96 | nlmsghdr.nlmsg_type = NLMSG_DONE; | ||
97 | |||
98 | struct cn_msg cn_msg; | ||
99 | memset(&cn_msg, 0, sizeof(cn_msg)); | ||
100 | cn_msg.id.idx = CN_IDX_PROC; | ||
101 | cn_msg.id.val = CN_VAL_PROC; | ||
102 | cn_msg.len = sizeof(enum proc_cn_mcast_op); | ||
103 | |||
104 | struct iovec iov[3]; | ||
105 | iov[0].iov_base = &nlmsghdr; | ||
106 | iov[0].iov_len = sizeof(nlmsghdr); | ||
107 | iov[1].iov_base = &cn_msg; | ||
108 | iov[1].iov_len = sizeof(cn_msg); | ||
109 | |||
110 | enum proc_cn_mcast_op op = PROC_CN_MCAST_LISTEN; | ||
111 | iov[2].iov_base = &op; | ||
112 | iov[2].iov_len = sizeof(op); | ||
113 | |||
114 | if (writev(sock, iov, 3) == -1) { | ||
115 | fprintf(stderr, "Error: cannot write to netlink socket\n"); | ||
116 | exit(1); | ||
117 | } | ||
118 | |||
119 | return sock; | ||
120 | } | ||
121 | |||
122 | |||
123 | static int procevent_monitor(const int sock, pid_t mypid) { | ||
124 | ssize_t len; | ||
125 | struct nlmsghdr *nlmsghdr; | ||
126 | |||
127 | // timeout in order to re-enable firejail module trace | ||
128 | struct timeval tv; | ||
129 | tv.tv_sec = 30; | ||
130 | tv.tv_usec = 0; | ||
131 | |||
132 | while (1) { | ||
133 | #define BUFFSIZE 4096 | ||
134 | char __attribute__ ((aligned(NLMSG_ALIGNTO)))buf[BUFFSIZE]; | ||
135 | |||
136 | fd_set readfds; | ||
137 | int max; | ||
138 | FD_ZERO(&readfds); | ||
139 | FD_SET(sock, &readfds); | ||
140 | max = sock; | ||
141 | max++; | ||
142 | |||
143 | int rv = select(max, &readfds, NULL, NULL, &tv); | ||
144 | if (rv == -1) { | ||
145 | fprintf(stderr, "recv: %s\n", strerror(errno)); | ||
146 | return -1; | ||
147 | } | ||
148 | |||
149 | // timeout | ||
150 | if (rv == 0) { | ||
151 | tv.tv_sec = 30; | ||
152 | tv.tv_usec = 0; | ||
153 | continue; | ||
154 | } | ||
155 | |||
156 | |||
157 | if ((len = recv(sock, buf, sizeof(buf), 0)) == 0) { | ||
158 | return 0; | ||
159 | } | ||
160 | if (len == -1) { | ||
161 | if (errno == EINTR) { | ||
162 | return 0; | ||
163 | } else { | ||
164 | fprintf(stderr,"recv: %s\n", strerror(errno)); | ||
165 | return -1; | ||
166 | } | ||
167 | } | ||
168 | |||
169 | for (nlmsghdr = (struct nlmsghdr *)buf; | ||
170 | NLMSG_OK (nlmsghdr, len); | ||
171 | nlmsghdr = NLMSG_NEXT (nlmsghdr, len)) { | ||
172 | |||
173 | struct cn_msg *cn_msg; | ||
174 | struct proc_event *proc_ev; | ||
175 | struct tm tm; | ||
176 | time_t now; | ||
177 | |||
178 | if ((nlmsghdr->nlmsg_type == NLMSG_ERROR) || | ||
179 | (nlmsghdr->nlmsg_type == NLMSG_NOOP)) | ||
180 | continue; | ||
181 | |||
182 | cn_msg = NLMSG_DATA(nlmsghdr); | ||
183 | if ((cn_msg->id.idx != CN_IDX_PROC) || | ||
184 | (cn_msg->id.val != CN_VAL_PROC)) | ||
185 | continue; | ||
186 | |||
187 | (void)time(&now); | ||
188 | (void)localtime_r(&now, &tm); | ||
189 | char line[PIDS_BUFLEN]; | ||
190 | char *lineptr = line; | ||
191 | sprintf(lineptr, "%2.2d:%2.2d:%2.2d", tm.tm_hour, tm.tm_min, tm.tm_sec); | ||
192 | lineptr += strlen(lineptr); | ||
193 | |||
194 | proc_ev = (struct proc_event *)cn_msg->data; | ||
195 | pid_t pid = 0; | ||
196 | pid_t child = 0; | ||
197 | int remove_pid = 0; | ||
198 | switch (proc_ev->what) { | ||
199 | case PROC_EVENT_FORK: | ||
200 | if (proc_ev->event_data.fork.child_pid != | ||
201 | proc_ev->event_data.fork.child_tgid) | ||
202 | continue; // this is a thread, not a process | ||
203 | pid = proc_ev->event_data.fork.parent_tgid; | ||
204 | if (pids[pid].level > 0) { | ||
205 | child = proc_ev->event_data.fork.child_tgid; | ||
206 | child %= max_pids; | ||
207 | pids[child].level = pids[pid].level + 1; | ||
208 | pids[child].uid = pid_get_uid(child); | ||
209 | } | ||
210 | sprintf(lineptr, " fork"); | ||
211 | break; | ||
212 | case PROC_EVENT_EXEC: | ||
213 | pid = proc_ev->event_data.exec.process_tgid; | ||
214 | sprintf(lineptr, " exec"); | ||
215 | break; | ||
216 | |||
217 | case PROC_EVENT_EXIT: | ||
218 | if (proc_ev->event_data.exit.process_pid != | ||
219 | proc_ev->event_data.exit.process_tgid) | ||
220 | continue; // this is a thread, not a process | ||
221 | |||
222 | pid = proc_ev->event_data.exit.process_tgid; | ||
223 | remove_pid = 1; | ||
224 | sprintf(lineptr, " exit"); | ||
225 | break; | ||
226 | |||
227 | case PROC_EVENT_UID: | ||
228 | pid = proc_ev->event_data.id.process_tgid; | ||
229 | sprintf(lineptr, " uid "); | ||
230 | break; | ||
231 | |||
232 | case PROC_EVENT_GID: | ||
233 | pid = proc_ev->event_data.id.process_tgid; | ||
234 | sprintf(lineptr, " gid "); | ||
235 | break; | ||
236 | |||
237 | case PROC_EVENT_SID: | ||
238 | pid = proc_ev->event_data.sid.process_tgid; | ||
239 | sprintf(lineptr, " sid "); | ||
240 | break; | ||
241 | |||
242 | default: | ||
243 | sprintf(lineptr, "\n"); | ||
244 | continue; | ||
245 | } | ||
246 | |||
247 | int add_new = 0; | ||
248 | if (pids[pid].level < 0) // not a firejail process | ||
249 | continue; | ||
250 | else if (pids[pid].level == 0) { // new porcess, do we track it? | ||
251 | if (pid_is_firejail(pid) && mypid == 0) { | ||
252 | pids[pid].level = 1; | ||
253 | add_new = 1; | ||
254 | } | ||
255 | else { | ||
256 | pids[pid].level = -1; | ||
257 | continue; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | lineptr += strlen(lineptr); | ||
262 | sprintf(lineptr, " %u", pid); | ||
263 | lineptr += strlen(lineptr); | ||
264 | |||
265 | char *user = pids[pid].user; | ||
266 | if (!user) | ||
267 | user = pid_get_user_name(pids[pid].uid); | ||
268 | if (user) { | ||
269 | pids[pid].user = user; | ||
270 | sprintf(lineptr, " (%s)", user); | ||
271 | lineptr += strlen(lineptr); | ||
272 | } | ||
273 | |||
274 | |||
275 | int sandbox_closed = 0; // exit sandbox flag | ||
276 | char *cmd = pids[pid].cmd; | ||
277 | if (!cmd) { | ||
278 | cmd = pid_proc_cmdline(pid); | ||
279 | } | ||
280 | if (add_new) { | ||
281 | if (!cmd) | ||
282 | sprintf(lineptr, " NEW SANDBOX\n"); | ||
283 | else | ||
284 | sprintf(lineptr, " NEW SANDBOX: %s\n", cmd); | ||
285 | lineptr += strlen(lineptr); | ||
286 | } | ||
287 | else if (proc_ev->what == PROC_EVENT_EXIT && pids[pid].level == 1) { | ||
288 | sprintf(lineptr, " EXIT SANDBOX\n"); | ||
289 | lineptr += strlen(lineptr); | ||
290 | if (mypid == pid) | ||
291 | sandbox_closed = 1; | ||
292 | } | ||
293 | else { | ||
294 | if (!cmd) { | ||
295 | cmd = pid_proc_cmdline(pid); | ||
296 | } | ||
297 | if (cmd == NULL) | ||
298 | sprintf(lineptr, "\n"); | ||
299 | else { | ||
300 | sprintf(lineptr, " %s\n", cmd); | ||
301 | free(cmd); | ||
302 | } | ||
303 | lineptr += strlen(lineptr); | ||
304 | } | ||
305 | (void) lineptr; | ||
306 | |||
307 | // print the event | ||
308 | printf("%s", line); | ||
309 | fflush(0); | ||
310 | |||
311 | // unflag pid for exit events | ||
312 | if (remove_pid) { | ||
313 | if (pids[pid].user) | ||
314 | free(pids[pid].user); | ||
315 | if (pids[pid].cmd) | ||
316 | free(pids[pid].cmd); | ||
317 | memset(&pids[pid], 0, sizeof(Process)); | ||
318 | } | ||
319 | |||
320 | // print forked child | ||
321 | if (child) { | ||
322 | cmd = pid_proc_cmdline(child); | ||
323 | if (cmd) { | ||
324 | printf("\tchild %u %s\n", child, cmd); | ||
325 | free(cmd); | ||
326 | } | ||
327 | else | ||
328 | printf("\tchild %u\n", child); | ||
329 | } | ||
330 | |||
331 | // on uid events the uid is changing | ||
332 | if (proc_ev->what == PROC_EVENT_UID) { | ||
333 | if (pids[pid].user) | ||
334 | free(pids[pid].user); | ||
335 | pids[pid].user = 0; | ||
336 | pids[pid].uid = pid_get_uid(pid); | ||
337 | } | ||
338 | |||
339 | if (sandbox_closed) | ||
340 | exit(0); | ||
341 | } | ||
342 | } | ||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static void procevent_print_pids(void) { | ||
347 | // print files | ||
348 | int i; | ||
349 | for (i = 0; i < max_pids; i++) { | ||
350 | if (pids[i].level == 1) | ||
351 | pid_print_tree(i, 0, 1); | ||
352 | } | ||
353 | printf("\n"); | ||
354 | } | ||
355 | |||
356 | void procevent(pid_t pid) { | ||
357 | // need to be root for this | ||
358 | if (getuid() != 0) { | ||
359 | fprintf(stderr, "Error: you need to be root to get process events\n"); | ||
360 | exit(1); | ||
361 | } | ||
362 | |||
363 | // read and print sandboxed processes | ||
364 | pid_read(pid); | ||
365 | procevent_print_pids(); | ||
366 | |||
367 | // monitor using netlink | ||
368 | int sock = procevent_netlink_setup(); | ||
369 | if (sock < 0) { | ||
370 | fprintf(stderr, "Error: cannot open netlink socket\n"); | ||
371 | exit(1); | ||
372 | } | ||
373 | |||
374 | procevent_monitor(sock, pid); // it will never return from here | ||
375 | assert(0); | ||
376 | close(sock); // quiet static analyzers | ||
377 | } | ||