1 /* $OpenBSD: intercept.c,v 1.53 2006/09/19 10:48:41 otto Exp $ */
2 /*
3 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Niels Provos.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/tree.h>
36 #include <sys/wait.h>
37 #include <sys/stat.h>
38 #include <limits.h>
39 #include <signal.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <poll.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <err.h>
48 #include <libgen.h>
49 #include <pwd.h>
50
51 #include "intercept.h"
52
53 void simplify_path(char *);
54
55 struct intercept_syscall {
56 SPLAY_ENTRY(intercept_syscall) node;
57
58 char name[64];
59 char emulation[16];
60
61 short (*cb)(int, pid_t, int, const char *, int, const char *, void *,
62 int, struct intercept_replace *, struct intercept_tlq *, void *);
63 void *cb_arg;
64
65 struct intercept_tlq tls;
66 };
67
68 static int sccompare(struct intercept_syscall *, struct intercept_syscall *);
69 static int pidcompare(struct intercept_pid *, struct intercept_pid *);
70 static struct intercept_syscall *intercept_sccb_find(const char *,
71 const char *);
72 static void sigusr1_handler(int);
73
74 static SPLAY_HEAD(pidtree, intercept_pid) pids;
75 static SPLAY_HEAD(sctree, intercept_syscall) scroot;
76
77 static volatile int got_sigusr1 = 0;
78
79 /* Generic callback functions */
80
81 void (*intercept_newimagecb)(int, pid_t, int, const char *, const char *, void *) = NULL;
82 void *intercept_newimagecbarg = NULL;
83 short (*intercept_gencb)(int, pid_t, int, const char *, int, const char *, void *, int, void *) = NULL;
84 void *intercept_gencbarg = NULL;
85 void (*intercept_pfreecb)(int, void*);
86 void *intercept_pfreearg = NULL;
87
88 int
sccompare(struct intercept_syscall * a,struct intercept_syscall * b)89 sccompare(struct intercept_syscall *a, struct intercept_syscall *b)
90 {
91 int diff;
92
93 diff = strcmp(a->emulation, b->emulation);
94 if (diff)
95 return (diff);
96 return (strcmp(a->name, b->name));
97 }
98
99 int
pidcompare(struct intercept_pid * a,struct intercept_pid * b)100 pidcompare(struct intercept_pid *a, struct intercept_pid *b)
101 {
102 int diff = a->pid - b->pid;
103
104 if (diff == 0)
105 return (0);
106 if (diff > 0)
107 return (1);
108 return (-1);
109 }
110
111 SPLAY_PROTOTYPE(sctree, intercept_syscall, node, sccompare)
112 SPLAY_GENERATE(sctree, intercept_syscall, node, sccompare)
113
114 SPLAY_PROTOTYPE(pidtree, intercept_pid, next, pidcompare)
115 SPLAY_GENERATE(pidtree, intercept_pid, next, pidcompare)
116
117 extern struct intercept_system intercept;
118 int ic_abort;
119
120 int
intercept_init(void)121 intercept_init(void)
122 {
123 SPLAY_INIT(&pids);
124 SPLAY_INIT(&scroot);
125
126 intercept_newimagecb = NULL;
127 intercept_gencb = NULL;
128
129 return (intercept.init());
130 }
131
132 struct intercept_syscall *
intercept_sccb_find(const char * emulation,const char * name)133 intercept_sccb_find(const char *emulation, const char *name)
134 {
135 struct intercept_syscall tmp;
136
137 strlcpy(tmp.name, name, sizeof(tmp.name));
138 strlcpy(tmp.emulation, emulation, sizeof(tmp.emulation));
139 return (SPLAY_FIND(sctree, &scroot, &tmp));
140 }
141
142 struct intercept_translate *
intercept_register_translation(char * emulation,char * name,int offset,struct intercept_translate * tl)143 intercept_register_translation(char *emulation, char *name, int offset,
144 struct intercept_translate *tl)
145 {
146 struct intercept_syscall *tmp;
147 struct intercept_translate *tlnew;
148
149 if (offset >= INTERCEPT_MAXSYSCALLARGS)
150 errx(1, "%s: %s-%s: offset too large",
151 __func__, emulation, name);
152
153 tmp = intercept_sccb_find(emulation, name);
154 if (tmp == NULL)
155 errx(1, "%s: %s-%s: can't find call back",
156 __func__, emulation, name);
157
158 tlnew = malloc(sizeof(struct intercept_translate));
159 if (tlnew == NULL)
160 err(1, "%s: %s-%s: malloc",
161 __func__, emulation, name);
162
163 memcpy(tlnew, tl, sizeof(struct intercept_translate));
164 tlnew->off = offset;
165
166 TAILQ_INSERT_TAIL(&tmp->tls, tlnew, next);
167
168 return (tlnew);
169 }
170
171 void *
intercept_sccb_cbarg(char * emulation,char * name)172 intercept_sccb_cbarg(char *emulation, char *name)
173 {
174 struct intercept_syscall *tmp;
175
176 if ((tmp = intercept_sccb_find(emulation, name)) == NULL)
177 return (NULL);
178
179 return (tmp->cb_arg);
180 }
181
182 int
intercept_register_sccb(char * emulation,char * name,short (* cb)(int,pid_t,int,const char *,int,const char *,void *,int,struct intercept_replace *,struct intercept_tlq *,void *),void * cbarg)183 intercept_register_sccb(char *emulation, char *name,
184 short (*cb)(int, pid_t, int, const char *, int, const char *, void *, int,
185 struct intercept_replace *, struct intercept_tlq *, void *),
186 void *cbarg)
187 {
188 struct intercept_syscall *tmp;
189
190 if (intercept_sccb_find(emulation, name))
191 return (-1);
192
193 if (intercept.getsyscallnumber(emulation, name) == -1) {
194 warnx("%s: %d: unknown syscall: %s-%s", __func__, __LINE__,
195 emulation, name);
196 return (-1);
197 }
198
199 if ((tmp = calloc(1, sizeof(struct intercept_syscall))) == NULL) {
200 warn("%s:%d: malloc", __func__, __LINE__);
201 return (-1);
202 }
203
204 TAILQ_INIT(&tmp->tls);
205 strlcpy(tmp->name, name, sizeof(tmp->name));
206 strlcpy(tmp->emulation, emulation, sizeof(tmp->emulation));
207 tmp->cb = cb;
208 tmp->cb_arg = cbarg;
209
210 SPLAY_INSERT(sctree, &scroot, tmp);
211
212 return (0);
213 }
214
215 int
intercept_register_gencb(short (* cb)(int,pid_t,int,const char *,int,const char *,void *,int,void *),void * arg)216 intercept_register_gencb(short (*cb)(int, pid_t, int, const char *, int, const char *, void *, int, void *), void *arg)
217 {
218 intercept_gencb = cb;
219 intercept_gencbarg = arg;
220
221 return (0);
222 }
223
224 int
intercept_register_execcb(void (* cb)(int,pid_t,int,const char *,const char *,void *),void * arg)225 intercept_register_execcb(void (*cb)(int, pid_t, int, const char *, const char *, void *), void *arg)
226 {
227 intercept_newimagecb = cb;
228 intercept_newimagecbarg = arg;
229
230 return (0);
231 }
232
233 int
intercept_register_pfreecb(void (* cb)(int,void *),void * arg)234 intercept_register_pfreecb(void (*cb)(int, void *), void *arg)
235 {
236 intercept_pfreecb = cb;
237 intercept_pfreearg = arg;
238
239 return (0);
240 }
241
242 /* ARGSUSED */
243 static void
sigusr1_handler(int signum)244 sigusr1_handler(int signum)
245 {
246 /* all we need to do is pretend to handle it */
247 got_sigusr1 = 1;
248 }
249
250 void
intercept_setpid(struct intercept_pid * icpid,uid_t uid,gid_t gid)251 intercept_setpid(struct intercept_pid *icpid, uid_t uid, gid_t gid)
252 {
253 struct passwd *pw;
254
255 icpid->uid = uid;
256 icpid->gid = gid;
257 if ((pw = getpwuid(icpid->uid)) == NULL) {
258 snprintf(icpid->username, sizeof(icpid->username),
259 "unknown(%d)", icpid->uid);
260 strlcpy(icpid->home, "/var/empty", sizeof(icpid->home));
261 } else {
262 strlcpy(icpid->username, pw->pw_name, sizeof(icpid->username));
263 strlcpy(icpid->home, pw->pw_dir, sizeof(icpid->home));
264 }
265 }
266
267 pid_t
intercept_run(int bg,int fd,uid_t uid,gid_t gid,char * path,char * const argv[])268 intercept_run(int bg, int fd, uid_t uid, gid_t gid,
269 char *path, char *const argv[])
270 {
271 struct intercept_pid *icpid;
272 sigset_t none, set, oset;
273 sig_t ohandler;
274 pid_t pid, cpid;
275 int status;
276
277 /* Block signals so that timing on signal delivery does not matter */
278 sigemptyset(&none);
279 sigemptyset(&set);
280 sigaddset(&set, SIGUSR1);
281 if (sigprocmask(SIG_BLOCK, &set, &oset) == -1)
282 err(1, "sigprocmask");
283 ohandler = signal(SIGUSR1, sigusr1_handler);
284 if (ohandler == SIG_ERR)
285 err(1, "signal");
286
287 pid = getpid();
288 cpid = fork();
289 if (cpid == -1)
290 return (-1);
291
292 /*
293 * If the systrace process should be in the background and we're
294 * the parent, or vice versa.
295 */
296 if ((!bg && cpid == 0) || (bg && cpid != 0)) {
297 /* Needs to be closed */
298 close(fd);
299
300 if (bg) {
301 /* Wait for child to "detach" */
302 cpid = wait(&status);
303 if (cpid == -1)
304 err(1, "wait");
305 if (status != 0)
306 errx(1, "wait: child gave up");
307 }
308
309 /* Sleep */
310 sigsuspend(&none);
311
312 if (!got_sigusr1)
313 errx(1, "wrong signal");
314
315 /*
316 * Woken up, restore signal handling state.
317 *
318 * Note that there is either no child or we have no idea
319 * what pid it might have at this point. If we fail.
320 */
321 if (signal(SIGUSR1, ohandler) == SIG_ERR)
322 err(1, "signal");
323 if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1)
324 err(1, "sigprocmask");
325
326 /* Change to different user */
327 if (uid || gid) {
328 if (setresgid(gid, gid, gid) == -1)
329 err(1, "setresgid");
330 if (setgroups(1, &gid) == -1)
331 err(1, "setgroups");
332 if (setresuid(uid, uid, uid) == -1)
333 err(1, "setresuid");
334 }
335 execvp(path, argv);
336
337 /* Error */
338 err(1, "execvp");
339 }
340
341 /* Choose the pid of the systraced process */
342 pid = bg ? pid : cpid;
343
344 icpid = intercept_getpid(pid);
345
346 /* Set up user related information */
347 if (!uid && !gid) {
348 uid = getuid();
349 gid = getgid();
350 }
351 intercept_setpid(icpid, uid, gid);
352
353 /* Setup done, restore signal handling state */
354 if (signal(SIGUSR1, ohandler) == SIG_ERR) {
355 kill(pid, SIGKILL);
356 err(1, "signal");
357 }
358 if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1) {
359 kill(pid, SIGKILL);
360 err(1, "sigprocmask");
361 }
362
363 if (bg) {
364 if (daemon(1, 1) == -1) {
365 kill(pid, SIGKILL);
366 err(1, "daemon");
367 }
368 }
369
370 return (pid);
371 }
372
373 int
intercept_existpids(void)374 intercept_existpids(void)
375 {
376 return (SPLAY_ROOT(&pids) != NULL);
377 }
378
379 void
intercept_freepid(pid_t pidnr)380 intercept_freepid(pid_t pidnr)
381 {
382 struct intercept_pid *pid, tmp2;
383
384 tmp2.pid = pidnr;
385 pid = SPLAY_FIND(pidtree, &pids, &tmp2);
386 if (pid == NULL)
387 return;
388
389 intercept.freepid(pid);
390
391 SPLAY_REMOVE(pidtree, &pids, pid);
392 if (pid->name)
393 free(pid->name);
394 if (pid->newname)
395 free(pid->newname);
396 free(pid);
397 }
398
399 struct intercept_pid *
intercept_findpid(pid_t pid)400 intercept_findpid(pid_t pid)
401 {
402 struct intercept_pid *tmp, tmp2;
403
404 tmp2.pid = pid;
405 tmp = SPLAY_FIND(pidtree, &pids, &tmp2);
406
407 return (tmp);
408 }
409
410 struct intercept_pid *
intercept_getpid(pid_t pid)411 intercept_getpid(pid_t pid)
412 {
413 struct intercept_pid *tmp, tmp2;
414
415 tmp2.pid = pid;
416 tmp = SPLAY_FIND(pidtree, &pids, &tmp2);
417
418 if (tmp)
419 return (tmp);
420
421 if ((tmp = malloc(sizeof(struct intercept_pid))) == NULL)
422 err(1, "%s: malloc", __func__);
423
424 memset(tmp, 0, sizeof(struct intercept_pid));
425 tmp->pid = pid;
426
427 SPLAY_INSERT(pidtree, &pids, tmp);
428
429 return (tmp);
430 }
431
432 int
intercept_open(void)433 intercept_open(void)
434 {
435 int fd;
436
437 if ((fd = intercept.open()) == -1)
438 return (-1);
439
440 if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1)
441 warn("fcntl(O_NONBLOCK)");
442
443 return (fd);
444 }
445
446 int
intercept_attach(int fd,pid_t pid)447 intercept_attach(int fd, pid_t pid)
448 {
449 return (intercept.attach(fd, pid));
450 }
451
452 int
intercept_attachpid(int fd,pid_t pid,char * name)453 intercept_attachpid(int fd, pid_t pid, char *name)
454 {
455 struct intercept_pid *icpid;
456 int res;
457
458 res = intercept.attach(fd, pid);
459 if (res == -1)
460 return (-1);
461
462 icpid = intercept_getpid(pid);
463
464 if ((icpid->newname = strdup(name)) == NULL)
465 err(1, "strdup");
466
467 if (intercept.report(fd, pid) == -1)
468 return (-1);
469
470 /* Indicates a running attach */
471 icpid->execve_code = -1;
472
473 return (0);
474 }
475
476 int
intercept_detach(int fd,pid_t pid)477 intercept_detach(int fd, pid_t pid)
478 {
479 int res;
480
481 res = intercept.detach(fd, pid);
482 if (res != -1)
483 intercept_freepid(pid);
484 return (res);
485 }
486
487 int
intercept_read(int fd)488 intercept_read(int fd)
489 {
490 struct pollfd pollfd;
491 int n;
492
493 pollfd.fd = fd;
494 pollfd.events = POLLIN;
495
496 do {
497 n = poll(&pollfd, 1, -1);
498 if (n == -1) {
499 if (errno != EINTR && errno != EAGAIN)
500 return (-1);
501 }
502 } while (n <= 0);
503
504 if (!(pollfd.revents & (POLLIN|POLLRDNORM)))
505 return (-1);
506
507 return (intercept.read(fd));
508 }
509
510 int
intercept_replace_init(struct intercept_replace * repl)511 intercept_replace_init(struct intercept_replace *repl)
512 {
513 memset(repl, 0, sizeof(struct intercept_replace));
514
515 return (0);
516 }
517
518 int
intercept_replace_add(struct intercept_replace * repl,int off,u_char * addr,size_t len,u_int flags)519 intercept_replace_add(struct intercept_replace *repl, int off,
520 u_char *addr, size_t len, u_int flags)
521 {
522 int ind = repl->num;
523
524 if (ind >= INTERCEPT_MAXSYSCALLARGS)
525 return (-1);
526
527 repl->ind[ind] = off;
528 repl->address[ind] = addr;
529 repl->len[ind] = len;
530 repl->flags[ind] = flags;
531
532 repl->num++;
533
534 return (0);
535 }
536
537 int
intercept_replace(int fd,pid_t pid,u_int16_t seqnr,struct intercept_replace * repl)538 intercept_replace(int fd, pid_t pid, u_int16_t seqnr,
539 struct intercept_replace *repl)
540 {
541 if (repl->num == 0)
542 return (0);
543
544 return (intercept.replace(fd, pid, seqnr, repl));
545 }
546
547 char *
intercept_get_string(int fd,pid_t pid,void * addr)548 intercept_get_string(int fd, pid_t pid, void *addr)
549 {
550 static char name[8192];
551 int off = 0, done = 0, stride;
552
553 if (addr == NULL)
554 return (NULL);
555
556 stride = 32;
557 do {
558 if (intercept.io(fd, pid, INTERCEPT_READ, (char *)addr + off,
559 &name[off], stride) == -1) {
560 /* Did the current system call get interrupted? */
561 if (errno == EBUSY)
562 return (NULL);
563 if (errno != EINVAL || stride == 4) {
564 warn("%s: ioctl", __func__);
565 return (NULL);
566 }
567
568 /* Try smaller stride */
569 stride /= 2;
570 continue;
571 }
572
573 off += stride;
574 name[off] = '\0';
575 if (strlen(name) < off)
576 done = 1;
577
578 } while (!done && off + stride + 1 < sizeof(name));
579
580 if (!done) {
581 warnx("%s: string too long", __func__);
582 return (NULL);
583 }
584
585 return (name);
586 }
587
588 char *
intercept_filename(int fd,pid_t pid,void * addr,int userp,char * before)589 intercept_filename(int fd, pid_t pid, void *addr, int userp, char *before)
590 {
591 char *name;
592
593 if ((name = intercept_get_string(fd, pid, addr)) == NULL)
594 goto abort;
595
596 if (before != NULL)
597 strlcpy(before, name, MAXPATHLEN);
598
599 if ((name = normalize_filename(fd, pid, name, userp)) == NULL)
600 goto abort;
601
602 return (name);
603
604 abort:
605 ic_abort = 1;
606 return (NULL);
607 }
608
609 /*
610 * Normalizes a pathname so that Systrace policies entries are
611 * invariant to symlinks.
612 */
613
614 char *
normalize_filename(int fd,pid_t pid,char * name,int userp)615 normalize_filename(int fd, pid_t pid, char *name, int userp)
616 {
617 static char cwd[2*MAXPATHLEN];
618 int havecwd = 0;
619
620 /*
621 * The empty filename does not receive normalization.
622 * System calls are supposed to fail on it.
623 */
624 if (strcmp(name, "") == 0)
625 return (name);
626
627 if (fd != -1 && intercept.setcwd(fd, pid) == -1) {
628 if (errno == EBUSY)
629 return (NULL);
630 getcwderr:
631 if (strcmp(name, "/") == 0)
632 return (name);
633
634 err(1, "%s: getcwd", __func__);
635 }
636
637 if (userp == ICLINK_NONE) {
638 if (getcwd(cwd, sizeof(cwd)) == NULL)
639 goto getcwderr;
640 havecwd = 1;
641 }
642
643 if (havecwd && name[0] != '/') {
644 if (strlcat(cwd, "/", sizeof(cwd)) >= sizeof(cwd))
645 return (NULL);
646 if (strlcat(cwd, name, sizeof(cwd)) >= sizeof(cwd))
647 return (NULL);
648 } else {
649 if (strlcpy(cwd, name, sizeof(cwd)) >= sizeof(cwd))
650 return (NULL);
651 }
652
653 if (userp != ICLINK_NONE) {
654 static char rcwd[2*MAXPATHLEN];
655 char *base = basename(cwd);
656 int failed = 0;
657
658 /* The dot maybe used by rmdir("/tmp/something/.") */
659 if (strcmp(base, ".") == 0)
660 goto nolast;
661
662 if (userp == ICLINK_NOLAST) {
663 /* Check if the last component has special meaning */
664 if (strcmp(base, "..") == 0 || strcmp(base, "/") == 0)
665 userp = ICLINK_ALL;
666 else
667 goto nolast;
668 }
669
670 /* If realpath fails then the filename does not exist,
671 * or we are supposed to not resolve the last component */
672 if (realpath(cwd, rcwd) == NULL) {
673 char *dir, *file;
674 struct stat st;
675
676 if (errno != EACCES) {
677 failed = 1;
678 goto out;
679 }
680
681 nolast:
682 /* Component of path could not be entered */
683 if (strlcpy(rcwd, cwd, sizeof(rcwd)) >= sizeof(rcwd))
684 goto error;
685 if ((file = basename(rcwd)) == NULL)
686 goto error;
687 if ((dir = dirname(rcwd)) == NULL)
688 goto error;
689
690 /* So, try again */
691 if (realpath(dir, rcwd) == NULL) {
692 failed = 1;
693 goto out;
694 }
695 /* If path is not "/" append a "/" */
696 if (strlen(rcwd) > 1 &&
697 strlcat(rcwd, "/", sizeof(rcwd)) >= sizeof(rcwd))
698 goto error;
699 if (strlcat(rcwd, file, sizeof(rcwd)) >= sizeof(rcwd))
700 goto error;
701 /*
702 * At this point, filename has to exist and has to
703 * be a directory.
704 */
705 if (userp != ICLINK_NOLAST) {
706 if (lstat(rcwd, &st) == -1 ||
707 !S_ISDIR(st.st_mode))
708 failed = 1;
709 }
710 }
711 out:
712 if (failed)
713 snprintf(rcwd, sizeof(rcwd),
714 "/<non-existent filename>: %s", cwd);
715 name = rcwd;
716 } else {
717 simplify_path(cwd);
718 name = cwd;
719 }
720
721
722 /* Restore working directory and change root space after realpath */
723 if (fd != -1 && intercept.restcwd(fd) == -1)
724 err(1, "%s: restcwd", __func__);
725
726 return (name);
727
728 error:
729 errx(1, "%s: filename too long", __func__);
730 /* NOTREACHED */
731 }
732
733 void
intercept_syscall(int fd,pid_t pid,u_int16_t seqnr,int policynr,const char * name,int code,const char * emulation,void * args,int argsize)734 intercept_syscall(int fd, pid_t pid, u_int16_t seqnr, int policynr,
735 const char *name, int code, const char *emulation, void *args, int argsize)
736 {
737 short action, flags = 0;
738 struct intercept_syscall *sc;
739 struct intercept_pid *icpid;
740 struct elevate *elevate = NULL;
741 int error = 0;
742
743 action = ICPOLICY_PERMIT;
744 flags = 0;
745
746 icpid = intercept_getpid(pid);
747
748 /* Special handling for the exec call */
749 if (!strcmp(name, "execve")) {
750 void *addr;
751 char *argname, before[MAXPATHLEN];
752
753 icpid->execve_code = code;
754 icpid->policynr = policynr;
755
756 if (icpid->newname)
757 free(icpid->newname);
758
759 intercept.getarg(0, args, argsize, &addr);
760 argname = intercept_filename(fd, pid, addr, ICLINK_ALL, before);
761 if (argname == NULL)
762 err(1, "%s:%d: intercept_filename",
763 __func__, __LINE__);
764
765 if (intercept.scriptname(fd, pid, before) != 0)
766 err(1, "%s:%d: ioctl", __func__, __LINE__);
767
768 icpid->newname = strdup(argname);
769 if (icpid->newname == NULL)
770 err(1, "%s:%d: strdup", __func__, __LINE__);
771
772 /* We need to know the result from this system call */
773 flags = ICFLAGS_RESULT;
774 }
775
776 icpid->elevate = NULL;
777
778 sc = intercept_sccb_find(emulation, name);
779 if (sc != NULL) {
780 struct intercept_translate *tl;
781
782 ic_abort = 0;
783 TAILQ_FOREACH(tl, &sc->tls, next) {
784 if (intercept_translate(tl, fd, pid, tl->off,
785 args, argsize) == -1)
786 break;
787 }
788
789 if (!ic_abort) {
790 struct intercept_replace repl;
791
792 intercept_replace_init(&repl);
793
794 action = (*sc->cb)(fd, pid, policynr, name, code,
795 emulation, args, argsize, &repl,
796 &sc->tls, sc->cb_arg);
797
798 if (action < ICPOLICY_NEVER) {
799 /* if we can not rewrite the arguments,
800 * system call fails.
801 */
802 if (intercept_replace(fd, pid, seqnr, &repl) == -1)
803 action = ICPOLICY_NEVER;
804 }
805 } else
806 action = ICPOLICY_NEVER;
807 } else if (intercept_gencb != NULL)
808 action = (*intercept_gencb)(fd, pid, policynr, name, code,
809 emulation, args, argsize, intercept_gencbarg);
810
811 if (action > 0) {
812 error = action;
813 action = ICPOLICY_NEVER;
814 } else {
815 icpid = intercept_findpid(pid);
816 if (icpid != NULL)
817 elevate = icpid->elevate;
818 else
819 elevate = NULL;
820 }
821
822 /* Resume execution of the process */
823 intercept.answer(fd, pid, seqnr, action, error, flags, elevate);
824 }
825
826 void
intercept_syscall_result(int fd,pid_t pid,u_int16_t seqnr,int policynr,const char * name,int code,const char * emulation,void * args,int argsize,int result,void * rval)827 intercept_syscall_result(int fd, pid_t pid, u_int16_t seqnr, int policynr,
828 const char *name, int code, const char *emulation, void *args, int argsize,
829 int result, void *rval)
830 {
831 struct intercept_pid *icpid;
832
833 if (result > 0)
834 goto out;
835
836 icpid = intercept_getpid(pid);
837 if (!strcmp("execve", name)) {
838 intercept_newimage(fd, pid, policynr,
839 emulation, icpid->newname, icpid);
840 /* we might have detached by now */
841 if (intercept_findpid(pid) == NULL)
842 return;
843 }
844
845 out:
846 /* Resume execution of the process */
847 intercept.answer(fd, pid, seqnr, 0, 0, 0, NULL);
848 }
849
850 void
intercept_newimage(int fd,pid_t pid,int policynr,const char * emulation,char * newname,struct intercept_pid * icpid)851 intercept_newimage(int fd, pid_t pid, int policynr,
852 const char *emulation, char *newname, struct intercept_pid *icpid)
853 {
854 if (icpid == NULL)
855 icpid = intercept_getpid(pid);
856
857 if (icpid->name)
858 free(icpid->name);
859 if ((icpid->name = strdup(newname)) == NULL)
860 err(1, "%s:%d: strdup", __func__, __LINE__);
861
862 if (icpid->newname != NULL) {
863 free(icpid->newname);
864 icpid->newname = NULL;
865 }
866
867 if (intercept_newimagecb != NULL)
868 (*intercept_newimagecb)(fd, pid, policynr, emulation,
869 icpid->name, intercept_newimagecbarg);
870 }
871
872 int
intercept_newpolicy(int fd)873 intercept_newpolicy(int fd)
874 {
875 int policynr;
876
877 policynr = intercept.newpolicy(fd);
878
879 return (policynr);
880 }
881
882 int
intercept_assignpolicy(int fd,pid_t pid,int policynr)883 intercept_assignpolicy(int fd, pid_t pid, int policynr)
884 {
885 return (intercept.assignpolicy(fd, pid, policynr));
886 }
887
888 int
intercept_modifypolicy_nr(int fd,int policynr,int code,short policy)889 intercept_modifypolicy_nr(int fd, int policynr, int code, short policy)
890 {
891 return (intercept.policy(fd, policynr, code, policy));
892 }
893
894 int
intercept_modifypolicy(int fd,int policynr,const char * emulation,const char * name,short policy)895 intercept_modifypolicy(int fd, int policynr, const char *emulation,
896 const char *name, short policy)
897 {
898 int code;
899
900 code = intercept.getsyscallnumber(emulation, name);
901 if (code == -1)
902 return (-1);
903
904 return (intercept.policy(fd, policynr, code, policy));
905 }
906
907 void
intercept_child_info(pid_t opid,pid_t npid)908 intercept_child_info(pid_t opid, pid_t npid)
909 {
910 struct intercept_pid *ipid, *inpid, tmp;
911
912 /* A child just died on us */
913 if (npid == -1) {
914 intercept_freepid(opid);
915 return;
916 }
917
918 tmp.pid = opid;
919 ipid = SPLAY_FIND(pidtree, &pids, &tmp);
920 if (ipid == NULL)
921 return;
922
923 inpid = intercept_getpid(npid);
924
925 inpid->policynr = ipid->policynr;
926 if (ipid->name != NULL) {
927 inpid->name = strdup(ipid->name);
928 if (inpid->name == NULL)
929 err(1, "%s:%d: strdup", __func__, __LINE__);
930 }
931
932 /* Process tree */
933 inpid->ppid = opid;
934
935 /* Copy some information */
936 inpid->uid = ipid->uid;
937 inpid->gid = ipid->gid;
938 strlcpy(inpid->username, ipid->username, sizeof(inpid->username));
939 strlcpy(inpid->home, ipid->home, sizeof(inpid->home));
940
941 /* XXX - keeps track of emulation */
942 intercept.clonepid(ipid, inpid);
943 }
944
945 void
intercept_ugid(struct intercept_pid * icpid,uid_t uid,gid_t gid)946 intercept_ugid(struct intercept_pid *icpid, uid_t uid, gid_t gid)
947 {
948 /* Update current home dir */
949 if (icpid->uid != uid) {
950 struct passwd *pw;
951
952 if ((pw = getpwuid(uid)) == NULL) {
953 snprintf(icpid->username, sizeof(icpid->username),
954 "uid %d", uid);
955 strlcpy(icpid->home, "/", sizeof(icpid->home));
956 } else {
957 strlcpy(icpid->username, pw->pw_name,
958 sizeof(icpid->username));
959 strlcpy(icpid->home, pw->pw_dir, sizeof(icpid->home));
960 }
961 }
962
963 icpid->uid = uid;
964 icpid->gid = gid;
965 }
966
967 /*
968 * Returns the number of a system call
969 */
970
971 int
intercept_getsyscallnumber(const char * emulation,const char * name)972 intercept_getsyscallnumber(const char *emulation, const char *name)
973 {
974 int nr = intercept.getsyscallnumber(emulation, name);
975
976 if (nr >= INTERCEPT_MAXSYSCALLNR)
977 err(1, "%s: system call number too high: %d", __func__, nr);
978
979 return (nr);
980 }
981
982 /*
983 * Checks if the given emulation has a certain system call.
984 * This is a very slow function.
985 */
986
987 int
intercept_isvalidsystemcall(const char * emulation,const char * name)988 intercept_isvalidsystemcall(const char *emulation, const char *name)
989 {
990 int res;
991
992 res = intercept.getsyscallnumber(emulation, name);
993
994 return (res != -1);
995 }
996
997 /*
998 * Call back when a user has exhausted the number of allowed policies
999 * in the kernel. The kernel returns the policy number of a policy
1000 * that has been purged.
1001 */
1002
1003 void
intercept_policy_free(int policynr)1004 intercept_policy_free(int policynr)
1005 {
1006 (*intercept_pfreecb)(policynr, intercept_pfreearg);
1007 }
1008