aboutsummaryrefslogtreecommitdiffstats
path: root/src/firemon/procevent.c
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@yahoo.com>2015-08-08 19:12:30 -0400
committerLibravatar netblue30 <netblue30@yahoo.com>2015-08-08 19:12:30 -0400
commit1379851360349d6617ad32944a25ee5e2bb74fc2 (patch)
treef69b48e90708bfa3c2723d5a27ed3e024c827b43 /src/firemon/procevent.c
parentdelete files (diff)
downloadfirejail-1379851360349d6617ad32944a25ee5e2bb74fc2.tar.gz
firejail-1379851360349d6617ad32944a25ee5e2bb74fc2.tar.zst
firejail-1379851360349d6617ad32944a25ee5e2bb74fc2.zip
Baseline firejail 0.9.28
Diffstat (limited to 'src/firemon/procevent.c')
-rw-r--r--src/firemon/procevent.c377
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
33static 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 }
65doexit:
66 fclose(fp);
67 free(file);
68 return rv;
69}
70
71
72static 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
123static 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
346static 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
356void 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}