X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/yaid/blobdiff_plain/02591975fddfa9b0cd823d2b1e0fa049e7dc890b..4234b0890a403cc56f8598cf618c13a4040aa4f2:/policy.c diff --git a/policy.c b/policy.c index 3d3eedb..9cb9af9 100644 --- a/policy.c +++ b/policy.c @@ -80,6 +80,12 @@ static void print_portpat(const struct portpat *pp) static void print_sockpat(const struct addrops *ao, const struct sockpat *sp) { print_addrpat(ao, &sp->addr); putchar(' '); print_portpat(&sp->port); } +static void print_userpat(const struct userpat *up) +{ + if (up->lo == up->hi) printf("%d", up->lo); + else printf("%u-%u", up->lo, up->hi); +} + static const char *const acttab[] = { #define DEFACT(tag, name) name, ACTIONS(DEFACT) @@ -109,6 +115,7 @@ void print_policy(const struct policy *p) { print_sockpat(p->ao, &p->sp[L]); putchar(' '); print_sockpat(p->ao, &p->sp[R]); putchar(' '); + print_userpat(&p->up); putchar(' '); print_action(&p->act); putchar('\n'); } @@ -126,12 +133,17 @@ static int match_sockpat(const struct addrops *ao, match_portpat(&sp->port, s->port)); } +/* Return true if the uid matches the pattern. */ +static int match_userpat(const struct userpat *up, uid_t u) + { unsigned uu = u; return (up->lo <= uu && uu <= up->hi); } + /* Return true if the query matches the patterns in the policy rule. */ int match_policy(const struct policy *p, const struct query *q) { return ((!p->ao || p->ao == q->ao) && match_sockpat(q->ao, &p->sp[L], &q->s[L]) && - match_sockpat(q->ao, &p->sp[R], &q->s[R])); + match_sockpat(q->ao, &p->sp[R], &q->s[R]) && + match_userpat(&p->up, q->u.uid)); } /*----- Parsing -----------------------------------------------------------*/ @@ -385,6 +397,27 @@ static int parse_sockpat(FILE *fp, const struct addrops **aop, return (T_OK); } +/* Parse a user pattern, writing it to UP. */ +static int parse_userpat(FILE *fp, struct userpat *up) +{ + struct passwd *pw; + char buf[32]; + int t; + char *delim; + + if ((t = scan(fp, buf, sizeof(buf))) != 0) return (t); + if (!strcmp(buf, "*")) { up->lo = 0; up->hi = UINT_MAX; } + else if ((pw = getpwnam(buf)) != 0) up->lo = up->hi = pw->pw_uid; + else { + if ((delim = strchr(buf, '-')) != 0) *delim++ = 0; + up->lo = strtoul(buf, 0, 0); + if (!delim) up->hi = up->lo; + else if (!*delim) up->hi = UINT_MAX; + else up->hi = strtoul(delim, 0, 0); + } + return (0); +} + /* Parse a policy rule line, writing it to P. */ static int parse_policy(FILE *fp, struct policy *p) { @@ -395,6 +428,7 @@ static int parse_policy(FILE *fp, struct policy *p) if ((t = parse_sockpat(fp, &p->ao, &p->sp[L])) != 0) goto fail; if ((t = parse_sockpat(fp, &p->ao, &p->sp[R])) != 0) goto err; + if ((t = parse_userpat(fp, &p->up)) != 0) goto err; if ((t = parse_action(fp, &p->act)) != 0) goto err; return (0); @@ -407,14 +441,34 @@ fail: /* Open a policy file by NAME. The description WHAT and query Q are used for * formatting error messages for the log. + * + * This function is somewhat careful only to read from actual regular files, + * though (if the filesystem object identified by NAME is a symlink, say) it + * might open a device node or other exotic thing without reading it. This + * is likely harmless, since we're running as an unprivileged user anyway. */ int open_policy_file(struct policy_file *pf, const char *name, - const char *what, const struct query *q) + const char *what, const struct query *q, unsigned f) { + struct stat st; + if ((pf->fp = fopen(name, "r")) == 0) { - logmsg(q, LOG_ERR, "failed to open %s `%s': %s", + if (errno != ENOENT || !(f & OPF_NOENTOK)) { + logmsg(q, LOG_ERR, "failed to open %s `%s': %s", + what, name, strerror(errno)); + } + goto err_0; + } + + if (fstat(fileno(pf->fp), &st)) { + logmsg(q, LOG_ERR, "failed to read information about %s `%s': %s", what, name, strerror(errno)); - return (-1); + goto err_1; + } + if (!S_ISREG(st.st_mode)) { + logmsg(q, LOG_ERR, "object `%s', used as %s, is not a regular file", + name, what); + goto err_1; } pf->name = name; @@ -424,6 +478,11 @@ int open_policy_file(struct policy_file *pf, const char *name, pf->lno = 0; init_policy(&pf->p); return (0); + +err_1: + fclose(pf->fp); +err_0: + return (-1); } /* Read a policy rule from the file, storing it in PF->p. Return one of the @@ -438,22 +497,20 @@ int read_policy_file(struct policy_file *pf) t = parse_policy(pf->fp, &pf->p); switch (t) { case T_OK: + case T_EOL: nextline(pf->fp); - return (0); + return (t); case T_ERROR: logmsg(pf->q, LOG_ERR, "%s:%d: parse error in %s", pf->name, pf->lno, pf->what); pf->err = 1; - break; + return (t); case T_EOF: if (ferror(pf->fp)) { logmsg(pf->q, LOG_ERR, "failed to read %s `%s': %s", pf->what, pf->name, strerror(errno)); } - return (-1); - case T_EOL: - nextline(pf->fp); - break; + return (t); default: abort(); } @@ -476,12 +533,15 @@ int load_policy_file(const char *file, policy_v *pv) { struct policy_file pf; policy_v v = DA_INIT; + int t = 0; - if (open_policy_file(&pf, file, "policy file", 0)) + if (open_policy_file(&pf, file, "policy file", 0, 0)) return (-1); - while (!read_policy_file(&pf)) { - DA_PUSH(&v, pf.p); - init_policy(&pf.p); + while ((t = read_policy_file(&pf)) < T_EOF) { + if (t == T_OK) { + DA_PUSH(&v, pf.p); + init_policy(&pf.p); + } } close_policy_file(&pf); if (!pf.err) {