1 /*	$OpenBSD: filter.c,v 1.33 2006/07/02 12:34:15 sturm 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 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/wait.h>
35 #include <sys/tree.h>
36 #include <limits.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <stdio.h>
41 #include <fcntl.h>
42 #include <regex.h>
43 #include <errno.h>
44 #include <fnmatch.h>
45 #include <err.h>
46 
47 #include "intercept.h"
48 #include "systrace.h"
49 #include "filter.h"
50 #include "util.h"
51 
52 extern int allow;
53 extern int noalias;
54 extern int connected;
55 extern int cradle;
56 extern char cwd[];
57 extern char home[];
58 extern char username[];
59 extern char *guipath;
60 
61 int requestor_restart = 0;
62 
63 static void logic_free(struct logic *);
64 static int filter_match(struct intercept_pid *, struct intercept_tlq *,
65     struct logic *);
66 static void filter_review(struct filterq *);
67 static void filter_templates(const char *);
68 static int filter_template(int, struct policy *, int);
69 static int filter_quickpredicate(struct filter *);
70 static void filter_policyrecord(struct policy *, struct filter *, const char *,
71     const char *, char *);
72 static void filter_replace(char *, size_t, char *, char *);
73 
74 static int
filter_match(struct intercept_pid * icpid,struct intercept_tlq * tls,struct logic * logic)75 filter_match(struct intercept_pid *icpid, struct intercept_tlq *tls,
76     struct logic *logic)
77 {
78 	struct intercept_translate *tl;
79 	int off = 0, res;
80 
81 	switch (logic->op) {
82 	case LOGIC_NOT:
83 		return (!filter_match(icpid, tls, logic->left));
84 	case LOGIC_OR:
85 		if (filter_match(icpid, tls, logic->left))
86 			return (1);
87 		return (filter_match(icpid, tls, logic->right));
88 	case LOGIC_AND:
89 		if (!filter_match(icpid, tls, logic->left))
90 			return (0);
91 		return (filter_match(icpid, tls, logic->right));
92 	default:
93 		break;
94 	}
95 
96 	/* Now we just have a logic single */
97 	if (logic->type == NULL)
98 		goto match;
99 
100 	if (tls == NULL)
101 		errx(1, "filter_match has no translators");
102 
103 	TAILQ_FOREACH(tl, tls, next) {
104 		if (!tl->trans_valid)
105 			continue;
106 
107 		if (strcasecmp(tl->name, logic->type))
108 			continue;
109 
110 		if (logic->typeoff == -1 || logic->typeoff == off)
111 			break;
112 
113 		off++;
114 	}
115 
116 	if (tl == NULL)
117 		return (0);
118 
119  match:
120 	/* We need to do dynamic expansion on the data */
121 	if (logic->filterdata && (logic->flags & LOGIC_NEEDEXPAND)) {
122 		char *old = logic->filterdata;
123 		size_t oldlen = logic->filterlen;
124 
125 		logic->filterdata = filter_dynamicexpand(icpid, old);
126 		logic->filterlen = strlen(logic->filterdata) + 1;
127 
128 		res = logic->filter_match(tl, logic);
129 
130 		logic->filterdata = old;
131 		logic->filterlen = oldlen;
132 	} else
133 		res = logic->filter_match(tl, logic);
134 
135 	return (res);
136 }
137 
138 /* Evaluate filter predicate */
139 
140 int
filter_predicate(struct intercept_pid * icpid,struct predicate * pdc)141 filter_predicate(struct intercept_pid *icpid, struct predicate *pdc)
142 {
143 	id_t pidnr, pdcnr;
144 	int res = 0;
145 
146 	if (!pdc->p_flags)
147 		return (1);
148 
149 	if (pdc->p_flags & PREDIC_UID) {
150 		pidnr = icpid->uid;
151 		pdcnr = pdc->p_uid;
152 	} else {
153 		pidnr = icpid->gid;
154 		pdcnr = pdc->p_gid;
155 	}
156 
157 	switch (pdc->p_flags & PREDIC_MASK) {
158 	case PREDIC_NEGATIVE:
159 		res = pidnr != pdcnr;
160 		break;
161 	case PREDIC_LESSER:
162 		res = pidnr < pdcnr;
163 		break;
164 	case PREDIC_GREATER:
165 		res = pidnr > pdcnr;
166 		break;
167 	default:
168 		res = pidnr == pdcnr;
169 		break;
170 	}
171 
172 	return (res);
173 }
174 
175 short
filter_evaluate(struct intercept_tlq * tls,struct filterq * fls,struct intercept_pid * icpid)176 filter_evaluate(struct intercept_tlq *tls, struct filterq *fls,
177     struct intercept_pid *icpid)
178 {
179 	struct filter *filter, *last = NULL;
180 	short action;
181 
182 	TAILQ_FOREACH(filter, fls, next) {
183 		action = filter->match_action;
184 
185 		if (filter_predicate(icpid, &filter->match_predicate) &&
186 		    filter_match(icpid, tls, filter->logicroot)) {
187 			/* Profile feedback optimization */
188 			filter->match_count++;
189 			if (last != NULL && last->match_action == action &&
190 			    last->match_flags == filter->match_flags &&
191 			    filter->match_count > last->match_count) {
192 				TAILQ_REMOVE(fls, last, next);
193 				TAILQ_INSERT_AFTER(fls, filter, last, next);
194 			}
195 
196 			if (action == ICPOLICY_NEVER)
197 				action = filter->match_error;
198 			icpid->uflags = filter->match_flags;
199 
200 			/* Policy requests privilege elevation */
201 			if (filter->elevate.e_flags)
202 				icpid->elevate = &filter->elevate;
203 			return (action);
204 		}
205 
206 		/* Keep track of last processed filtered in a group */
207 		last = filter;
208 	}
209 
210 	return (ICPOLICY_ASK);
211 }
212 
213 static void
logic_free(struct logic * logic)214 logic_free(struct logic *logic)
215 {
216 	if (logic->left)
217 		logic_free(logic->left);
218 	if (logic->right)
219 		logic_free(logic->right);
220 	if (logic->type)
221 		free(logic->type);
222 	if (logic->filterdata)
223 		free(logic->filterdata);
224 	free(logic);
225 }
226 
227 void
filter_free(struct filter * filter)228 filter_free(struct filter *filter)
229 {
230 	if (filter->logicroot)
231 		logic_free(filter->logicroot);
232 	if (filter->rule)
233 		free(filter->rule);
234 	free(filter);
235 }
236 
237 static void
filter_review(struct filterq * fls)238 filter_review(struct filterq *fls)
239 {
240 	struct filter *filter;
241 	int i = 0;
242 
243 	printf("Filter review:\n");
244 
245 	TAILQ_FOREACH(filter, fls, next) {
246 		i++;
247 		printf("%d. %s\n", i, filter->rule);
248 	}
249 }
250 
251 static void
filter_templates(const char * emulation)252 filter_templates(const char *emulation)
253 {
254 	extern struct tmplqueue templates;
255 	struct template *template;
256 	int i = 0;
257 
258 	printf("Available Templates:\n");
259 
260 	TAILQ_FOREACH(template, &templates, next) {
261 		if (strcmp(template->emulation, emulation))
262 			continue;
263 
264 		i++;
265 		printf("%d. %s - %s\n", i,
266 		    template->name, template->description);
267 	}
268 }
269 
270 /* Inserts a policy from a template */
271 
272 static int
filter_template(int fd,struct policy * policy,int count)273 filter_template(int fd, struct policy *policy, int count)
274 {
275 	extern struct tmplqueue templates;
276 	struct template *template;
277 	int i = 0;
278 
279 	TAILQ_FOREACH(template, &templates, next) {
280 		if (strcmp(template->emulation, policy->emulation))
281 			continue;
282 
283 		i++;
284 		if (i == count)
285 			break;
286 	}
287 
288 	if (i != count)
289 		return (-1);
290 
291 	template = systrace_readtemplate(template->filename, policy, template);
292 	if (template == NULL)
293 		return (-1);
294 
295 	if (filter_prepolicy(fd, policy) == -1)
296 		return (-1);
297 
298 	/* We inserted new statements into the policy */
299 	policy->flags |= POLICY_CHANGED;
300 
301 	return (0);
302 }
303 
304 static void
filter_policyrecord(struct policy * policy,struct filter * filter,const char * emulation,const char * name,char * rule)305 filter_policyrecord(struct policy *policy, struct filter *filter,
306     const char *emulation, const char *name, char *rule)
307 {
308 	/* Record the filter in the policy */
309 	filter = calloc(1, sizeof(struct filter));
310 	if (filter == NULL)
311 		err(1, "%s:%d: calloc", __func__, __LINE__);
312 	if ((filter->rule = strdup(rule)) == NULL)
313 		err(1, "%s:%d: strdup", __func__, __LINE__);
314 
315 	strlcpy(filter->name, name, sizeof(filter->name));
316 	strlcpy(filter->emulation, emulation, sizeof(filter->emulation));
317 
318 	TAILQ_INSERT_TAIL(&policy->filters, filter, policy_next);
319 	policy->nfilters++;
320 
321 	policy->flags |= POLICY_CHANGED;
322 }
323 
324 int
filter_parse(char * line,struct filter ** pfilter)325 filter_parse(char *line, struct filter **pfilter)
326 {
327 	char *rule;
328 
329 	if (parse_filter(line, pfilter) == -1)
330 		return (-1);
331 
332 	if ((rule = strdup(line)) == NULL)
333 		err(1, "%s:%d: strdup", __func__, __LINE__);
334 
335 	(*pfilter)->rule = rule;
336 
337 	return (0);
338 }
339 
340 /* Translate a simple action like "permit" or "deny[einval]" to numbers */
341 
342 int
filter_parse_simple(char * rule,short * paction,short * pfuture)343 filter_parse_simple(char *rule, short *paction, short *pfuture)
344 {
345 	char buf[_POSIX2_LINE_MAX];
346 	int isfuture = 1;
347 	char *line, *p;
348 
349 	if (strlcpy(buf, rule, sizeof(buf)) >= sizeof(buf))
350 		return (-1);
351 
352 	line = buf;
353 
354 	if (!strcmp("permit", line)) {
355 		*paction = *pfuture = ICPOLICY_PERMIT;
356 		return (0);
357 	} else if (!strcmp("permit-now", line)) {
358 		*paction = ICPOLICY_PERMIT;
359 		return (0);
360 	} else if (strncmp("deny", line, 4))
361 		return (-1);
362 
363 	line +=4 ;
364 	if (!strncmp("-now", line, 4)) {
365 		line += 4;
366 		isfuture = 0;
367 	}
368 
369 	*paction = ICPOLICY_NEVER;
370 
371 	switch (line[0]) {
372 	case '\0':
373 		break;
374 	case '[':
375 		line++;
376 		p = strsep(&line, "]");
377 		if (line == NULL || *line != '\0')
378 			return (-1);
379 
380 		*paction = systrace_error_translate(p);
381 		if (*paction == -1)
382 			return (-1);
383 		break;
384 	default:
385 		return (-1);
386 	}
387 
388 	if (isfuture)
389 		*pfuture = *paction;
390 
391 	return (0);
392 }
393 
394 void
filter_modifypolicy(int fd,int policynr,const char * emulation,const char * name,short future)395 filter_modifypolicy(int fd, int policynr, const char *emulation,
396     const char *name, short future)
397 {
398 	struct systrace_revalias *reverse = NULL;
399 
400 	/*
401 	 * Check if we are dealing with a system call that really
402 	 * is an alias for something else.
403 	 */
404 	if (!noalias)
405 		reverse = systrace_find_reverse(emulation, name);
406 	if (reverse == NULL) {
407 		if (systrace_modifypolicy(fd, policynr, name, future) == -1)
408 			errx(1, "%s:%d: modify policy for %s-%s",
409 			    __func__, __LINE__, emulation, name);
410 	} else {
411 		struct systrace_alias *alias;
412 
413 		/* For every system call associated with this alias
414 		 * set the permanent in-kernel policy.
415 		 */
416 		TAILQ_FOREACH(alias, &reverse->revl, next) {
417 			if(systrace_modifypolicy(fd, policynr,
418 			       alias->name, future) == -1)
419 				errx(1, "%s:%d: modify policy for %s-%s",
420 				    __func__, __LINE__,
421 				    emulation, alias->name);
422 		}
423 	}
424 }
425 
426 /* In non-root case, evaluate predicates early */
427 
428 static int
filter_quickpredicate(struct filter * filter)429 filter_quickpredicate(struct filter *filter)
430 {
431 	struct predicate *pdc;
432 	struct intercept_pid icpid;
433 
434 	pdc = &filter->match_predicate;
435 	if (!pdc->p_flags)
436 		return (1);
437 
438 	intercept_setpid(&icpid, getuid(), getgid());
439 
440 	if (!filter_predicate(&icpid, pdc))
441 		return (0);
442 
443 	memset(pdc, 0, sizeof(filter->match_predicate));
444 
445 	return (1);
446 }
447 
448 /*
449  * Processes the filters for a policy that have not been applied yet.
450  * Pre-filters get installed when reading a policy.  This function
451  * installs a fast-path in the kernel.
452  */
453 
454 int
filter_prepolicy(int fd,struct policy * policy)455 filter_prepolicy(int fd, struct policy *policy)
456 {
457 	int res;
458 	struct filter *filter, *parsed;
459 	struct filterq *fls;
460 	short action, future;
461 	extern int iamroot;
462 
463 	/* Commit all matching pre-filters */
464 	for (filter = TAILQ_FIRST(&policy->prefilters);
465 	    filter; filter = TAILQ_FIRST(&policy->prefilters)) {
466 		future = ICPOLICY_ASK;
467 
468 		TAILQ_REMOVE(&policy->prefilters, filter, policy_next);
469 
470 		res = 0;
471 		parsed = NULL;
472 		/* Special rules that are not real filters */
473 		if (filter_parse_simple(filter->rule, &action, &future) == -1)
474 			res = filter_parse(filter->rule, &parsed);
475 		if (res == -1)
476 			errx(1, "%s:%d: can not parse \"%s\"",
477 			    __func__, __LINE__, filter->rule);
478 
479 		if (future == ICPOLICY_ASK) {
480 			if (iamroot || filter_quickpredicate(parsed)) {
481 				fls = systrace_policyflq(policy,
482 				    policy->emulation, filter->name);
483 				TAILQ_INSERT_TAIL(fls, parsed, next);
484 			}
485 		} else {
486 			filter_modifypolicy(fd, policy->policynr,
487 			    policy->emulation, filter->name, future);
488 		}
489 		filter_policyrecord(policy, parsed, policy->emulation,
490 		    filter->name, filter->rule);
491 
492 		filter_free(filter);
493 	}
494 
495 	/* Existing policy applied undo changed flag */
496 	policy->flags &= ~POLICY_CHANGED;
497 
498 	return (0);
499 }
500 
501 short
filter_ask(int fd,struct intercept_tlq * tls,struct filterq * fls,int policynr,const char * emulation,const char * name,char * output,short * pfuture,struct intercept_pid * icpid)502 filter_ask(int fd, struct intercept_tlq *tls, struct filterq *fls,
503     int policynr, const char *emulation, const char *name,
504     char *output, short *pfuture, struct intercept_pid *icpid)
505 {
506 	char line[2*MAXPATHLEN], *p;
507 	char compose[2*MAXPATHLEN];
508 	struct filter *filter;
509 	struct policy *policy;
510 	short action;
511 	int first = 1, isalias, isprompt = 0;
512 
513 	*pfuture = ICPOLICY_ASK;
514 
515 	isalias = systrace_find_reverse(emulation, name) != NULL;
516 
517 	if ((policy = systrace_findpolnr(policynr)) == NULL)
518 		errx(1, "%s:%d: no policy %d", __func__, __LINE__, policynr);
519 
520 	if (!allow)
521 		printf("%s\n", output);
522 	else {
523 		/* Automatically allow */
524 		if (tls != NULL) {
525 			struct intercept_translate *tl;
526 			char *l, *lst = NULL;
527 			int set = 0;
528 
529 			/* Explicitly match every component */
530 			line[0] = '\0';
531 			TAILQ_FOREACH(tl, tls, next) {
532 				if (!tl->trans_valid)
533 					continue;
534 				l = intercept_translate_print(tl);
535 				if (l == NULL)
536 					continue;
537 
538 				snprintf(compose, sizeof(compose),
539 				    "%s%s eq \"%s\"",
540 				    tl->name,
541 				    lst && !strcmp(tl->name, lst) ? "[1]" : "",
542 				    strescape(l));
543 
544 				lst = tl->name;
545 
546 				if (set)
547 					strlcat(line, " and ",
548 					    sizeof(line));
549 				else
550 					set = 1;
551 				strlcat(line, compose, sizeof(line));
552 			}
553 			if (!set)
554 				strlcpy(line, "true", sizeof(line));
555 			strlcat(line, " then permit", sizeof(line));
556 		} else
557 			strlcpy(line, "permit", sizeof(line));
558 	}
559 
560 	while (1) {
561 		/* Special policy active that allows only yes or no */
562 		if (icpid->uflags & PROCESS_PROMPT)
563 			isprompt = 1;
564 		filter = NULL;
565 
566 		if (!allow) {
567 			/* Ask for a policy */
568 			if (!connected)
569 				printf("Answer: ");
570 			else {
571 				/* Do not prompt the first time */
572 				if (!first) {
573 					printf("WRONG\n");
574 				}
575 			}
576 
577 			if (fgets(line, sizeof(line), stdin) == NULL) {
578 				if (connected && !cradle && errno == EPIPE &&
579 				    !requestor_restart) {
580 					requestor_start(guipath, 0);
581 					clearerr(stdin);
582 					clearerr(stdout);
583 					requestor_restart = 1;
584 					printf("%s\n", output);
585 					continue;
586 				}
587 				err(1, "EOF on policy input request");
588 			}
589 			p = line;
590 			strsep(&p, "\n");
591 		} else if (!first) {
592 			/* Error with filter */
593 			errx(1, "Filter generation error: %s", line);
594 		}
595 		first = 0;
596 		requestor_restart = 0;
597 
598 		/* Simple keywords */
599 		if (!strcasecmp(line, "detach")) {
600 			if (policy->nfilters) {
601 				policy->flags |= POLICY_UNSUPERVISED;
602 				action = ICPOLICY_NEVER;
603 			} else {
604 				policy->flags |= POLICY_DETACHED;
605 				policy->flags |= POLICY_CHANGED;
606 				action = ICPOLICY_PERMIT;
607 			}
608 			goto out;
609 		} else if (!strcasecmp(line, "kill")) {
610 			action = ICPOLICY_KILL;
611 			goto out;
612 		} else if (!strcasecmp(line, "review") && fls != NULL) {
613 			filter_review(fls);
614 			continue;
615 		} else if (!strcasecmp(line, "templates")) {
616 			filter_templates(emulation);
617 			continue;
618 		} else if (!strncasecmp(line, "template ", 9)) {
619 			int count = atoi(line + 9);
620 
621 			if (count == 0 ||
622 			    filter_template(fd, policy, count) == -1) {
623 				printf("Syntax error.\n");
624 				continue;
625 			}
626 
627 			if (fls != NULL)
628 				action = filter_evaluate(tls, fls, icpid);
629 			else
630 				action = ICPOLICY_PERMIT;
631 			if (action == ICPOLICY_ASK) {
632 				printf("Filter unmatched.\n");
633 				continue;
634 			}
635 
636 			goto out;
637 		}
638 
639 		if (filter_parse_simple(line, &action, pfuture) != -1) {
640 			/* Yes or no, no in-kernel policy allowed */
641 			if (isprompt)
642 				*pfuture = ICPOLICY_ASK;
643 			if (*pfuture == ICPOLICY_ASK)
644 				goto out;
645 			/* We have a policy decision */
646 			if (!isalias)
647 				break;
648 
649 			/* No in-kernel policy for aliases */
650 			strlcpy(compose, line, sizeof(compose));
651 
652 			/* Change into userland rule */
653 			snprintf(line, sizeof(line), "true then %s", compose);
654 		}
655 
656 		if (isprompt) {
657 			printf("Answer only \"permit\" or \"deny\". "
658 			    "This is a prompt.\n");
659 			continue;
660 		}
661 
662 		if (fls == NULL) {
663 			printf("Syntax error.\n");
664 			continue;
665 		}
666 
667 		if (filter_parse(line, &filter) == -1) {
668 			printf("Parse error.\n");
669 			continue;
670 		}
671 
672 		TAILQ_INSERT_TAIL(fls, filter, next);
673 		action = filter_evaluate(tls, fls, icpid);
674 
675 		/* If we get a prompt flag here, we ask again */
676 		if (icpid->uflags & PROCESS_PROMPT) {
677 			filter_policyrecord(policy, filter, emulation, name, line);
678 			printf("Answer only \"permit\" or \"deny\". "
679 			    "This is a prompt.\n");
680 			continue;
681 		}
682 		if (action == ICPOLICY_ASK) {
683 			TAILQ_REMOVE(fls, filter, next);
684 			printf("Filter unmatched. Freeing it\n");
685 			filter_free(filter);
686 			continue;
687 		}
688 
689 		break;
690 	}
691 
692 	filter_policyrecord(policy, filter, emulation, name, line);
693 
694  out:
695 	if (connected)
696 		printf("OKAY\n");
697 	return (action);
698 
699 }
700 
701 static void
filter_replace(char * buf,size_t buflen,char * match,char * repl)702 filter_replace(char *buf, size_t buflen, char *match, char *repl)
703 {
704 	while (strrpl(buf, buflen, match, repl) != NULL)
705 		;
706 }
707 
708 char *
filter_expand(char * data)709 filter_expand(char *data)
710 {
711 	static char expand[2*MAXPATHLEN];
712 
713 	strlcpy(expand, data, sizeof(expand));
714 
715 	filter_replace(expand, sizeof(expand), "$HOME", home);
716 	filter_replace(expand, sizeof(expand), "$USER", username);
717 	filter_replace(expand, sizeof(expand), "$CWD", cwd);
718 
719 	return (expand);
720 }
721 
722 char *
filter_dynamicexpand(struct intercept_pid * icpid,char * data)723 filter_dynamicexpand(struct intercept_pid *icpid, char *data)
724 {
725 	extern char cwd[];
726 	static char expand[2*MAXPATHLEN];
727 
728 	strlcpy(expand, data, sizeof(expand));
729 
730 	filter_replace(expand, sizeof(expand), "$HOME", icpid->home);
731 	filter_replace(expand, sizeof(expand), "$USER", icpid->username);
732 	filter_replace(expand, sizeof(expand), "$CWD", cwd);
733 
734 	return (expand);
735 }
736 
737 /* Checks if the string needs expansion */
738 
739 int
filter_needexpand(char * data)740 filter_needexpand(char *data)
741 {
742 	if (strstr(data, "$HOME") != NULL)
743 		return (1);
744 	if (strstr(data, "$USER") != NULL)
745 		return (1);
746 	if (strstr(data, "$CWD") != NULL)
747 		return (1);
748 
749 	return (0);
750 }
751 
752 int
filter_fnmatch(struct intercept_translate * tl,struct logic * logic)753 filter_fnmatch(struct intercept_translate *tl, struct logic *logic)
754 {
755 	int res;
756 	char *line;
757 
758 	if ((line = intercept_translate_print(tl)) == NULL)
759 		return (0);
760 	res = fnmatch(logic->filterdata, line, FNM_PATHNAME | FNM_LEADING_DIR);
761 
762 	return (res == 0);
763 }
764 
765 int
filter_substrmatch(struct intercept_translate * tl,struct logic * logic)766 filter_substrmatch(struct intercept_translate *tl, struct logic *logic)
767 {
768 	char *line;
769 
770 	if ((line = intercept_translate_print(tl)) == NULL)
771 		return (0);
772 
773 	return (strstr(line, logic->filterdata) != NULL);
774 }
775 
776 int
filter_negsubstrmatch(struct intercept_translate * tl,struct logic * logic)777 filter_negsubstrmatch(struct intercept_translate *tl, struct logic *logic)
778 {
779 	char *line;
780 
781 	if ((line = intercept_translate_print(tl)) == NULL)
782 		return (0);
783 
784 	return (strstr(line, logic->filterdata) == NULL);
785 }
786 
787 int
filter_stringmatch(struct intercept_translate * tl,struct logic * logic)788 filter_stringmatch(struct intercept_translate *tl, struct logic *logic)
789 {
790 	char *line;
791 
792 	if ((line = intercept_translate_print(tl)) == NULL)
793 		return (0);
794 
795 	return (!strcasecmp(line, logic->filterdata));
796 }
797 
798 int
filter_negstringmatch(struct intercept_translate * tl,struct logic * logic)799 filter_negstringmatch(struct intercept_translate *tl, struct logic *logic)
800 {
801 	char *line;
802 
803 	if ((line = intercept_translate_print(tl)) == NULL)
804 		return (1);
805 
806 	return (strcasecmp(line, logic->filterdata) != 0);
807 }
808 
809 int
filter_inpath(struct intercept_translate * tl,struct logic * logic)810 filter_inpath(struct intercept_translate *tl, struct logic *logic)
811 {
812 	char *line, c;
813 	int len;
814 
815 	if ((line = intercept_translate_print(tl)) == NULL)
816 		return (0);
817 
818 	len = strlen(line);
819 	if (len == 0 || len > strlen(logic->filterdata))
820 		return (0);
821 
822 	/* Root is always in path */
823 	if (len == 1)
824 		return (line[0] == '/');
825 
826 	/* Complete filename needs to fit */
827 	if (strncmp(line, logic->filterdata, len))
828 		return (0);
829 
830 	/* Termination has to be \0 or / */
831 	c = ((char *)logic->filterdata)[len];
832 	if (c != '/' && c != '\0')
833 		return (0);
834 
835 	return (1);
836 }
837 
838 int
filter_regex(struct intercept_translate * tl,struct logic * logic)839 filter_regex(struct intercept_translate *tl, struct logic *logic)
840 {
841 	regex_t tmpre, *re;
842 	char *line;
843 	int res;
844 
845 	if ((line = intercept_translate_print(tl)) == NULL)
846 		return (0);
847 
848 	re = logic->filterarg;
849 	if (re == NULL) {
850 		/* If regex does not compute, we just do not match */
851 		if (regcomp(&tmpre, logic->filterdata,
852 			REG_EXTENDED | REG_NOSUB) != 0)
853 			return (0);
854 		re = &tmpre;
855 	}
856 
857 	res = regexec(re, line, 0, NULL, 0);
858 
859 	/* Clean up temporary memory associated with regex */
860 	if (re == &tmpre)
861 		regfree(re);
862 
863 	return (res == 0);
864 }
865 
866 /* ARGSUSED */
867 int
filter_true(struct intercept_translate * tl,struct logic * logic)868 filter_true(struct intercept_translate *tl, struct logic *logic)
869 {
870 	return (1);
871 }
872