866 lines
19 KiB
C
866 lines
19 KiB
C
/* $NetBSD: filter.c,v 1.24 2003/11/28 21:53:32 provos Exp $ */
|
|
/* $OpenBSD: filter.c,v 1.16 2002/08/08 21:18:20 provos Exp $ */
|
|
/*
|
|
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Niels Provos.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include <sys/cdefs.h>
|
|
__RCSID("$NetBSD: filter.c,v 1.24 2003/11/28 21:53:32 provos Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/tree.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <regex.h>
|
|
#include <errno.h>
|
|
#include <fnmatch.h>
|
|
#include <err.h>
|
|
|
|
#include "intercept.h"
|
|
#include "systrace.h"
|
|
#include "filter.h"
|
|
#include "util.h"
|
|
|
|
extern int allow;
|
|
extern int noalias;
|
|
extern int connected;
|
|
extern int cradle;
|
|
extern char cwd[];
|
|
extern char home[];
|
|
extern char username[];
|
|
extern char *guipath;
|
|
|
|
int requestor_restart = 0;
|
|
|
|
static void logic_free(struct logic *);
|
|
static int filter_match(struct intercept_pid *, struct intercept_tlq *,
|
|
struct logic *);
|
|
static void filter_review(struct filterq *);
|
|
static void filter_templates(const char *);
|
|
static int filter_template(int, struct policy *, int);
|
|
static int filter_quickpredicate(struct filter *);
|
|
static void filter_policyrecord(struct policy *, struct filter *, const char *,
|
|
const char *, char *);
|
|
static void filter_replace(char *, size_t, char *, char *);
|
|
|
|
static int
|
|
filter_match(struct intercept_pid *icpid, struct intercept_tlq *tls,
|
|
struct logic *logic)
|
|
{
|
|
struct intercept_translate *tl;
|
|
int off = 0, res;
|
|
|
|
switch (logic->op) {
|
|
case LOGIC_NOT:
|
|
return (!filter_match(icpid, tls, logic->left));
|
|
case LOGIC_OR:
|
|
if (filter_match(icpid, tls, logic->left))
|
|
return (1);
|
|
return (filter_match(icpid, tls, logic->right));
|
|
case LOGIC_AND:
|
|
if (!filter_match(icpid, tls, logic->left))
|
|
return (0);
|
|
return (filter_match(icpid, tls, logic->right));
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Now we just have a logic single */
|
|
if (logic->type == NULL)
|
|
goto match;
|
|
|
|
if (tls == NULL)
|
|
errx(1, "filter_match has no translators");
|
|
|
|
TAILQ_FOREACH(tl, tls, next) {
|
|
if (!tl->trans_valid)
|
|
return (0);
|
|
|
|
if (strcasecmp(tl->name, logic->type))
|
|
continue;
|
|
|
|
if (logic->typeoff == -1 || logic->typeoff == off)
|
|
break;
|
|
|
|
off++;
|
|
}
|
|
|
|
if (tl == NULL)
|
|
return (0);
|
|
|
|
match:
|
|
/* We need to do dynamic expansion on the data */
|
|
if (logic->filterdata && (logic->flags & LOGIC_NEEDEXPAND)) {
|
|
char *old = logic->filterdata;
|
|
size_t oldlen = logic->filterlen;
|
|
|
|
logic->filterdata = filter_dynamicexpand(icpid, old);
|
|
logic->filterlen = strlen(logic->filterdata) + 1;
|
|
|
|
res = logic->filter_match(tl, logic);
|
|
|
|
logic->filterdata = old;
|
|
logic->filterlen = oldlen;
|
|
} else
|
|
res = logic->filter_match(tl, logic);
|
|
|
|
return (res);
|
|
}
|
|
|
|
/* Evaluate filter predicate */
|
|
|
|
int
|
|
filter_predicate(struct intercept_pid *icpid, struct predicate *pdc)
|
|
{
|
|
int pidnr, pdcnr;
|
|
int res = 0;
|
|
|
|
if (!pdc->p_flags)
|
|
return (1);
|
|
|
|
if (pdc->p_flags & PREDIC_UID) {
|
|
pidnr = icpid->uid;
|
|
pdcnr = pdc->p_uid;
|
|
} else {
|
|
pidnr = icpid->gid;
|
|
pdcnr = pdc->p_gid;
|
|
}
|
|
|
|
switch (pdc->p_flags & PREDIC_MASK) {
|
|
case PREDIC_NEGATIVE:
|
|
res = pidnr != pdcnr;
|
|
break;
|
|
case PREDIC_LESSER:
|
|
res = pidnr < pdcnr;
|
|
break;
|
|
case PREDIC_GREATER:
|
|
res = pidnr > pdcnr;
|
|
break;
|
|
default:
|
|
res = pidnr == pdcnr;
|
|
break;
|
|
}
|
|
|
|
return (res);
|
|
}
|
|
|
|
short
|
|
filter_evaluate(struct intercept_tlq *tls, struct filterq *fls,
|
|
struct intercept_pid *icpid)
|
|
{
|
|
struct filter *filter, *last = NULL;
|
|
short action;
|
|
|
|
TAILQ_FOREACH(filter, fls, next) {
|
|
action = filter->match_action;
|
|
|
|
if (filter_predicate(icpid, &filter->match_predicate) &&
|
|
filter_match(icpid, tls, filter->logicroot)) {
|
|
/* Profile feedback optimization */
|
|
filter->match_count++;
|
|
if (last != NULL && last->match_action == action &&
|
|
last->match_flags == filter->match_flags &&
|
|
filter->match_count > last->match_count) {
|
|
TAILQ_REMOVE(fls, last, next);
|
|
TAILQ_INSERT_AFTER(fls, filter, last, next);
|
|
}
|
|
|
|
if (action == ICPOLICY_NEVER)
|
|
action = filter->match_error;
|
|
icpid->uflags = filter->match_flags;
|
|
|
|
/* Policy requests privilege elevation */
|
|
if (filter->elevate.e_flags)
|
|
icpid->elevate = &filter->elevate;
|
|
return (action);
|
|
}
|
|
|
|
/* Keep track of last processed filtered in a group */
|
|
last = filter;
|
|
}
|
|
|
|
return (ICPOLICY_ASK);
|
|
}
|
|
|
|
static void
|
|
logic_free(struct logic *logic)
|
|
{
|
|
if (logic->left)
|
|
logic_free(logic->left);
|
|
if (logic->right)
|
|
logic_free(logic->right);
|
|
if (logic->type)
|
|
free(logic->type);
|
|
if (logic->filterdata)
|
|
free(logic->filterdata);
|
|
free(logic);
|
|
}
|
|
|
|
void
|
|
filter_free(struct filter *filter)
|
|
{
|
|
if (filter->logicroot)
|
|
logic_free(filter->logicroot);
|
|
if (filter->rule)
|
|
free(filter->rule);
|
|
free(filter);
|
|
}
|
|
|
|
static void
|
|
filter_review(struct filterq *fls)
|
|
{
|
|
struct filter *filter;
|
|
int i = 0;
|
|
|
|
printf("Filter review:\n");
|
|
|
|
TAILQ_FOREACH(filter, fls, next) {
|
|
i++;
|
|
printf("%d. %s\n", i, filter->rule);
|
|
}
|
|
}
|
|
|
|
static void
|
|
filter_templates(const char *emulation)
|
|
{
|
|
extern struct tmplqueue templates;
|
|
struct template *template;
|
|
int i = 0;
|
|
|
|
printf("Available Templates:\n");
|
|
|
|
TAILQ_FOREACH(template, &templates, next) {
|
|
if (strcmp(template->emulation, emulation))
|
|
continue;
|
|
|
|
i++;
|
|
printf("%d. %s - %s\n", i,
|
|
template->name, template->description);
|
|
}
|
|
}
|
|
|
|
/* Inserts a policy from a template */
|
|
|
|
static int
|
|
filter_template(int fd, struct policy *policy, int count)
|
|
{
|
|
extern struct tmplqueue templates;
|
|
struct template *template;
|
|
int i = 0;
|
|
|
|
TAILQ_FOREACH(template, &templates, next) {
|
|
if (strcmp(template->emulation, policy->emulation))
|
|
continue;
|
|
|
|
i++;
|
|
if (i == count)
|
|
break;
|
|
}
|
|
|
|
if (i != count)
|
|
return (-1);
|
|
|
|
template = systrace_readtemplate(template->filename, policy, template);
|
|
if (template == NULL)
|
|
return (-1);
|
|
|
|
if (filter_prepolicy(fd, policy) == -1)
|
|
return (-1);
|
|
|
|
/* We inserted new statements into the policy */
|
|
policy->flags |= POLICY_CHANGED;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
filter_policyrecord(struct policy *policy, struct filter *filter,
|
|
const char *emulation, const char *name, char *rule)
|
|
{
|
|
/* Record the filter in the policy */
|
|
filter = calloc(1, sizeof(struct filter));
|
|
if (filter == NULL)
|
|
err(1, "%s:%d: calloc", __func__, __LINE__);
|
|
if ((filter->rule = strdup(rule)) == NULL)
|
|
err(1, "%s:%d: strdup", __func__, __LINE__);
|
|
|
|
strlcpy(filter->name, name, sizeof(filter->name));
|
|
strlcpy(filter->emulation, emulation, sizeof(filter->emulation));
|
|
|
|
TAILQ_INSERT_TAIL(&policy->filters, filter, policy_next);
|
|
policy->nfilters++;
|
|
|
|
policy->flags |= POLICY_CHANGED;
|
|
}
|
|
|
|
int
|
|
filter_parse(char *line, struct filter **pfilter)
|
|
{
|
|
char *rule;
|
|
|
|
if (parse_filter(line, pfilter) == -1)
|
|
return (-1);
|
|
|
|
if ((rule = strdup(line)) == NULL)
|
|
err(1, "%s:%d: strdup", __func__, __LINE__);
|
|
|
|
(*pfilter)->rule = rule;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Translate a simple action like "permit" or "deny[einval]" to numbers */
|
|
|
|
int
|
|
filter_parse_simple(char *rule, short *paction, short *pfuture)
|
|
{
|
|
char buf[_POSIX2_LINE_MAX];
|
|
int isfuture = 1;
|
|
char *line, *p;
|
|
|
|
if (strlcpy(buf, rule, sizeof(buf)) >= sizeof(buf))
|
|
return (-1);
|
|
|
|
line = buf;
|
|
|
|
if (!strcmp("permit", line)) {
|
|
*paction = *pfuture = ICPOLICY_PERMIT;
|
|
return (0);
|
|
} else if (!strcmp("permit-now", line)) {
|
|
*paction = ICPOLICY_PERMIT;
|
|
return (0);
|
|
} else if (strncmp("deny", line, 4))
|
|
return (-1);
|
|
|
|
line +=4 ;
|
|
if (!strncmp("-now", line, 4)) {
|
|
line += 4;
|
|
isfuture = 0;
|
|
}
|
|
|
|
*paction = ICPOLICY_NEVER;
|
|
|
|
switch (line[0]) {
|
|
case '\0':
|
|
break;
|
|
case '[':
|
|
line++;
|
|
p = strsep(&line, "]");
|
|
if (line == NULL || *line != '\0')
|
|
return (-1);
|
|
|
|
*paction = systrace_error_translate(p);
|
|
if (*paction == -1)
|
|
return (-1);
|
|
break;
|
|
default:
|
|
return (-1);
|
|
}
|
|
|
|
if (isfuture)
|
|
*pfuture = *paction;
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
filter_modifypolicy(int fd, int policynr, const char *emulation,
|
|
const char *name, short future)
|
|
{
|
|
struct systrace_revalias *reverse = NULL;
|
|
|
|
if (!noalias)
|
|
reverse = systrace_find_reverse(emulation, name);
|
|
if (reverse == NULL) {
|
|
if (systrace_modifypolicy(fd, policynr, name, future) == -1)
|
|
errx(1, "%s:%d: modify policy for %s-%s",
|
|
__func__, __LINE__, emulation, name);
|
|
} else {
|
|
struct systrace_alias *alias;
|
|
|
|
/* For every system call associated with this alias
|
|
* set the permanent in-kernel policy.
|
|
*/
|
|
TAILQ_FOREACH(alias, &reverse->revl, next) {
|
|
if(systrace_modifypolicy(fd, policynr,
|
|
alias->name, future) == -1)
|
|
errx(1, "%s:%d: modify policy for %s-%s",
|
|
__func__, __LINE__,
|
|
emulation, alias->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* In non-root case, evaluate predicates early */
|
|
|
|
static int
|
|
filter_quickpredicate(struct filter *filter)
|
|
{
|
|
struct predicate *pdc;
|
|
struct intercept_pid icpid;
|
|
|
|
pdc = &filter->match_predicate;
|
|
if (!pdc->p_flags)
|
|
return (1);
|
|
|
|
intercept_setpid(&icpid, getuid(), getgid());
|
|
|
|
if (!filter_predicate(&icpid, pdc))
|
|
return (0);
|
|
|
|
memset(pdc, 0, sizeof(filter->match_predicate));
|
|
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
filter_prepolicy(int fd, struct policy *policy)
|
|
{
|
|
int res;
|
|
struct filter *filter, *parsed;
|
|
struct filterq *fls;
|
|
short action, future;
|
|
extern int iamroot;
|
|
|
|
/* Commit all matching pre-filters */
|
|
for (filter = TAILQ_FIRST(&policy->prefilters);
|
|
filter; filter = TAILQ_FIRST(&policy->prefilters)) {
|
|
future = ICPOLICY_ASK;
|
|
|
|
TAILQ_REMOVE(&policy->prefilters, filter, policy_next);
|
|
|
|
res = 0;
|
|
parsed = NULL;
|
|
/* Special rules that are not real filters */
|
|
if (filter_parse_simple(filter->rule, &action, &future) == -1)
|
|
res = filter_parse(filter->rule, &parsed);
|
|
if (res == -1)
|
|
errx(1, "%s:%d: can not parse \"%s\"",
|
|
__func__, __LINE__, filter->rule);
|
|
|
|
if (future == ICPOLICY_ASK) {
|
|
if (iamroot || filter_quickpredicate(parsed)) {
|
|
fls = systrace_policyflq(policy,
|
|
policy->emulation, filter->name);
|
|
TAILQ_INSERT_TAIL(fls, parsed, next);
|
|
}
|
|
} else {
|
|
filter_modifypolicy(fd, policy->policynr,
|
|
policy->emulation, filter->name, future);
|
|
}
|
|
filter_policyrecord(policy, parsed, policy->emulation,
|
|
filter->name, filter->rule);
|
|
|
|
filter_free(filter);
|
|
}
|
|
|
|
/* Existing policy applied undo changed flag */
|
|
policy->flags &= ~POLICY_CHANGED;
|
|
|
|
return (0);
|
|
}
|
|
|
|
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)
|
|
{
|
|
char line[2*MAXPATHLEN], *p;
|
|
char compose[2*MAXPATHLEN];
|
|
struct filter *filter;
|
|
struct policy *policy;
|
|
short action;
|
|
int first = 1, isalias, isprompt = 0;
|
|
|
|
*pfuture = ICPOLICY_ASK;
|
|
|
|
isalias = systrace_find_reverse(emulation, name) != NULL;
|
|
|
|
if ((policy = systrace_findpolnr(policynr)) == NULL)
|
|
errx(1, "%s:%d: no policy %d", __func__, __LINE__, policynr);
|
|
|
|
if (!allow)
|
|
printf("%s\n", output);
|
|
else {
|
|
/* Automatically allow */
|
|
if (tls != NULL) {
|
|
struct intercept_translate *tl;
|
|
char *l, *lst = NULL;
|
|
int set = 0;
|
|
|
|
/* Explicitly match every component */
|
|
line[0] = '\0';
|
|
TAILQ_FOREACH(tl, tls, next) {
|
|
if (!tl->trans_valid)
|
|
break;
|
|
l = intercept_translate_print(tl);
|
|
if (l == NULL)
|
|
continue;
|
|
|
|
snprintf(compose, sizeof(compose),
|
|
"%s%s eq \"%s\"",
|
|
tl->name,
|
|
lst && !strcmp(tl->name, lst) ? "[1]" : "",
|
|
strescape(l));
|
|
|
|
lst = tl->name;
|
|
|
|
if (set)
|
|
strlcat(line, " and ",
|
|
sizeof(line));
|
|
else
|
|
set = 1;
|
|
strlcat(line, compose, sizeof(line));
|
|
}
|
|
if (!set)
|
|
strlcpy(line, "true", sizeof(line));
|
|
strlcat(line, " then permit", sizeof(line));
|
|
} else
|
|
strlcpy(line, "permit", sizeof(line));
|
|
}
|
|
|
|
while (1) {
|
|
/* Special policy active that allows only yes or no */
|
|
if (icpid->uflags & PROCESS_PROMPT) {
|
|
fprintf(stderr, "isprompt\n");
|
|
isprompt = 1;
|
|
}
|
|
filter = NULL;
|
|
|
|
if (!allow) {
|
|
/* Ask for a policy */
|
|
if (!connected)
|
|
printf("Answer: ");
|
|
else {
|
|
/* Do not prompt the first time */
|
|
if (!first) {
|
|
printf("WRONG\n");
|
|
}
|
|
}
|
|
|
|
if (fgets(line, sizeof(line), stdin) == NULL) {
|
|
if (connected && !cradle && errno == EPIPE &&
|
|
!requestor_restart) {
|
|
requestor_start(guipath, 0);
|
|
clearerr(stdin);
|
|
clearerr(stdout);
|
|
requestor_restart = 1;
|
|
printf("%s\n", output);
|
|
continue;
|
|
}
|
|
err(1, "EOF on policy input request");
|
|
}
|
|
p = line;
|
|
strsep(&p, "\n");
|
|
} else if (!first) {
|
|
/* Error with filter */
|
|
errx(1, "Filter generation error: %s", line);
|
|
}
|
|
first = 0;
|
|
requestor_restart = 0;
|
|
|
|
/* Simple keywords */
|
|
if (!strcasecmp(line, "detach")) {
|
|
if (policy->nfilters) {
|
|
policy->flags |= POLICY_UNSUPERVISED;
|
|
action = ICPOLICY_NEVER;
|
|
} else {
|
|
policy->flags |= POLICY_DETACHED;
|
|
policy->flags |= POLICY_CHANGED;
|
|
action = ICPOLICY_PERMIT;
|
|
}
|
|
goto out;
|
|
} else if (!strcasecmp(line, "kill")) {
|
|
action = ICPOLICY_KILL;
|
|
goto out;
|
|
} else if (!strcasecmp(line, "review") && fls != NULL) {
|
|
filter_review(fls);
|
|
continue;
|
|
} else if (!strcasecmp(line, "templates")) {
|
|
filter_templates(emulation);
|
|
continue;
|
|
} else if (!strncasecmp(line, "template ", 9)) {
|
|
int count = atoi(line + 9);
|
|
|
|
if (count == 0 ||
|
|
filter_template(fd, policy, count) == -1) {
|
|
printf("Syntax error.\n");
|
|
continue;
|
|
}
|
|
|
|
if (fls != NULL)
|
|
action = filter_evaluate(tls, fls, icpid);
|
|
else
|
|
action = ICPOLICY_PERMIT;
|
|
if (action == ICPOLICY_ASK) {
|
|
printf("Filter unmatched.\n");
|
|
continue;
|
|
}
|
|
|
|
goto out;
|
|
}
|
|
|
|
if (filter_parse_simple(line, &action, pfuture) != -1) {
|
|
/* Yes or no, no in-kernel policy allowed */
|
|
if (isprompt)
|
|
*pfuture = ICPOLICY_ASK;
|
|
if (*pfuture == ICPOLICY_ASK)
|
|
goto out;
|
|
/* We have a policy decision */
|
|
if (!isalias)
|
|
break;
|
|
|
|
/* No in-kernel policy for aliases */
|
|
strlcpy(compose, line, sizeof(compose));
|
|
|
|
/* Change into userland rule */
|
|
snprintf(line, sizeof(line), "true then %s", compose);
|
|
}
|
|
|
|
if (isprompt) {
|
|
printf("Answer only \"permit\" or \"deny\". "
|
|
"This is a prompt.\n");
|
|
continue;
|
|
}
|
|
|
|
if (fls == NULL) {
|
|
printf("Syntax error.\n");
|
|
continue;
|
|
}
|
|
|
|
if (filter_parse(line, &filter) == -1) {
|
|
printf("Parse error.\n");
|
|
continue;
|
|
}
|
|
|
|
TAILQ_INSERT_TAIL(fls, filter, next);
|
|
action = filter_evaluate(tls, fls, icpid);
|
|
|
|
/* If we get a prompt flag here, we ask again */
|
|
if (icpid->uflags & PROCESS_PROMPT) {
|
|
filter_policyrecord(policy, filter, emulation, name, line);
|
|
printf("Answer only \"permit\" or \"deny\". "
|
|
"This is a prompt.\n");
|
|
continue;
|
|
}
|
|
if (action == ICPOLICY_ASK) {
|
|
TAILQ_REMOVE(fls, filter, next);
|
|
printf("Filter unmatched. Freeing it\n");
|
|
filter_free(filter);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
filter_policyrecord(policy, filter, emulation, name, line);
|
|
|
|
out:
|
|
if (connected)
|
|
printf("OKAY\n");
|
|
return (action);
|
|
|
|
}
|
|
|
|
static void
|
|
filter_replace(char *buf, size_t buflen, char *match, char *repl)
|
|
{
|
|
while (strrpl(buf, buflen, match, repl) != NULL)
|
|
;
|
|
}
|
|
|
|
char *
|
|
filter_expand(char *data)
|
|
{
|
|
static char expand[2*MAXPATHLEN];
|
|
|
|
strlcpy(expand, data, sizeof(expand));
|
|
|
|
filter_replace(expand, sizeof(expand), "$HOME", home);
|
|
filter_replace(expand, sizeof(expand), "$USER", username);
|
|
filter_replace(expand, sizeof(expand), "$CWD", cwd);
|
|
|
|
return (expand);
|
|
}
|
|
|
|
char *
|
|
filter_dynamicexpand(struct intercept_pid *icpid, char *data)
|
|
{
|
|
extern char cwd[];
|
|
static char expand[2*MAXPATHLEN];
|
|
|
|
strlcpy(expand, data, sizeof(expand));
|
|
|
|
filter_replace(expand, sizeof(expand), "$HOME", icpid->home);
|
|
filter_replace(expand, sizeof(expand), "$USER", icpid->username);
|
|
filter_replace(expand, sizeof(expand), "$CWD", cwd);
|
|
|
|
return (expand);
|
|
}
|
|
|
|
/* Checks if the string needs expansion */
|
|
|
|
int
|
|
filter_needexpand(char *data)
|
|
{
|
|
if (strstr(data, "$HOME") != NULL)
|
|
return (1);
|
|
if (strstr(data, "$USER") != NULL)
|
|
return (1);
|
|
if (strstr(data, "$CWD") != NULL)
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
filter_fnmatch(struct intercept_translate *tl, struct logic *logic)
|
|
{
|
|
int res;
|
|
char *line;
|
|
|
|
if ((line = intercept_translate_print(tl)) == NULL)
|
|
return (0);
|
|
res = fnmatch(logic->filterdata, line, FNM_PATHNAME | FNM_LEADING_DIR);
|
|
|
|
return (res == 0);
|
|
}
|
|
|
|
int
|
|
filter_substrmatch(struct intercept_translate *tl, struct logic *logic)
|
|
{
|
|
char *line;
|
|
|
|
if ((line = intercept_translate_print(tl)) == NULL)
|
|
return (0);
|
|
|
|
return (strstr(line, logic->filterdata) != NULL);
|
|
}
|
|
|
|
int
|
|
filter_negsubstrmatch(struct intercept_translate *tl, struct logic *logic)
|
|
{
|
|
char *line;
|
|
|
|
if ((line = intercept_translate_print(tl)) == NULL)
|
|
return (0);
|
|
|
|
return (strstr(line, logic->filterdata) == NULL);
|
|
}
|
|
|
|
int
|
|
filter_stringmatch(struct intercept_translate *tl, struct logic *logic)
|
|
{
|
|
char *line;
|
|
|
|
if ((line = intercept_translate_print(tl)) == NULL)
|
|
return (0);
|
|
|
|
return (!strcasecmp(line, logic->filterdata));
|
|
}
|
|
|
|
int
|
|
filter_negstringmatch(struct intercept_translate *tl, struct logic *logic)
|
|
{
|
|
char *line;
|
|
|
|
if ((line = intercept_translate_print(tl)) == NULL)
|
|
return (1);
|
|
|
|
return (strcasecmp(line, logic->filterdata) != 0);
|
|
}
|
|
|
|
int
|
|
filter_inpath(struct intercept_translate *tl, struct logic *logic)
|
|
{
|
|
char *line, c;
|
|
int len;
|
|
|
|
if ((line = intercept_translate_print(tl)) == NULL)
|
|
return (0);
|
|
|
|
len = strlen(line);
|
|
if (len == 0 || len > strlen(logic->filterdata))
|
|
return (0);
|
|
|
|
/* Root is always in path */
|
|
if (len == 1)
|
|
return (line[0] == '/');
|
|
|
|
/* Complete filename needs to fit */
|
|
if (strncmp(line, logic->filterdata, len))
|
|
return (0);
|
|
|
|
/* Termination has to be \0 or / */
|
|
c = ((char *)logic->filterdata)[len];
|
|
if (c != '/' && c != '\0')
|
|
return (0);
|
|
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
filter_regex(struct intercept_translate *tl, struct logic *logic)
|
|
{
|
|
regex_t tmpre, *re;
|
|
char *line;
|
|
int res;
|
|
|
|
if ((line = intercept_translate_print(tl)) == NULL)
|
|
return (0);
|
|
|
|
re = logic->filterarg;
|
|
if (re == NULL) {
|
|
/* If regex does not compute, we just do not match */
|
|
if (regcomp(&tmpre, logic->filterdata,
|
|
REG_EXTENDED | REG_NOSUB) != 0)
|
|
return (0);
|
|
re = &tmpre;
|
|
}
|
|
|
|
res = regexec(re, line, 0, NULL, 0);
|
|
|
|
/* Clean up temporary memory associated with regex */
|
|
if (re == &tmpre)
|
|
regfree(re);
|
|
|
|
return (res == 0);
|
|
}
|
|
|
|
int
|
|
filter_true(struct intercept_translate *tl, struct logic *logic)
|
|
{
|
|
return (1);
|
|
}
|