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