/* $OpenBSD: changerule.c,v 1.1 2021/11/11 12:49:53 sashan Exp $ */ /* * Copyright (c) 2021 Alexandr Nedvedicky * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * changerule - simple tool to test DIOCCHANGERULE functionality (see pf(4)) * Tool reads firewall rules from stdin only. If more rules are passed, then * only the first one is being used. Examples: * echo 'pass all' | changerule -a test -i 0 * inserts a rule to the first position in ruleset test * * echo 'pass all' | changerule -a test -i -1 * inserts a rule to the last position in ruleset test * * echo 'pass all' | changerule -a test -i 3 * inserts a rule before existing No. 3 rule (rules numbering * starts with 0) in ruleset test * * echo 'pass all' | changerule -a test -I 3 * inserts a rule after existing No. 3 rule (rules numbering * starts with 0) in ruleset test * * changerule -a test -r 3 * removes existing No. 3 rule from ruleset test * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pfctl_parser.h" #include "pfctl.h" void changerule_usage(void); int do_chng_cmd(char *, int, int); extern int dev; extern char *anchoropt; extern char *pf_device; __dead void changerule_usage(void) { extern char *__progname; fprintf(stderr, "usage: %s", __progname); fprintf(stderr, "[-a anchor] [ -i ruleNo ] [ -I ruleNo ]\n"); exit(1); } int do_chng_cmd(char *anchorname, int cmd, int rule_no) { struct pfctl pf; struct pf_anchor rs_anchor; struct pf_ruleset *rs = &rs_anchor.ruleset; struct pfioc_rule pcr; memset(&pf, 0, sizeof(pf)); memset(&rs_anchor, 0, sizeof(rs_anchor)); pf.anchor = &rs_anchor; pf_init_ruleset(rs); if (strlcpy(pf.anchor->path, anchorname, sizeof(pf.anchor->path)) >= sizeof (pf.anchor->path)) errx(1, "%s: strlcpy\n", __func__); pf.astack[0] = pf.anchor; pf.asd = 0; pf.dev = dev; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchorname, sizeof(pcr.anchor)); pcr.action = PF_CHANGE_GET_TICKET; if (ioctl(dev, DIOCCHANGERULE, &pcr) < 0) errx(1, "ioctl(ticket) @ %s", __func__); pcr.action = cmd; pcr.nr = rule_no; if (cmd != PF_CHANGE_REMOVE) { if (parse_config("-", &pf) < 0) { errx(1, "Syntax error in rule"); return (1); } if (TAILQ_FIRST(rs->rules.active.ptr) != NULL) memcpy(&pcr.rule, TAILQ_FIRST(rs->rules.active.ptr), sizeof(pcr.rule)); else errx(1, "no rule"); } if (ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { errx(1, "ioctl(commit) @ %s", __func__); } return (0); } int main(int argc, char *argv[]) { char anchorname[PATH_MAX]; const char *errstr; int ch; int rule_no; int chng_cmd; int after = 0; if (argc < 2) changerule_usage(); while ((ch = getopt(argc, argv, "a:i:I:r:")) != -1) { switch (ch) { case 'a': anchoropt = optarg; break; case 'I': after = 1; /* FALLTHROUGH */ case 'i': rule_no = strtonum(optarg, -1, 0x7fffffff, &errstr); if (errstr != NULL) { warnx("Rule number outside of range <%d, %d\n", -1, 0x7fffffff); exit(1); } switch (rule_no) { case 0: chng_cmd = PF_CHANGE_ADD_HEAD; break; case -1: chng_cmd = PF_CHANGE_ADD_TAIL; break; default: if (after) chng_cmd = PF_CHANGE_ADD_AFTER; else chng_cmd = PF_CHANGE_ADD_BEFORE; } break; case 'r': rule_no = strtonum(optarg, -1, 0x7fffffff, &errstr); if (errstr != NULL) { warnx("Rule number outside of range <%d, %d\n", -1, 0x7fffffff); exit(1); } chng_cmd = PF_CHANGE_REMOVE; break; default: changerule_usage(); /* NOTREACHED */ } } if (argc != optind) { warnx("unknown command line argument: %s ...", argv[optind]); changerule_usage(); /* NOTREACHED */ } memset(anchorname, 0, sizeof(anchorname)); if (anchoropt != NULL) { if (anchoropt[0] == '\0') errx(1, "anchor name must not be empty"); if (anchoropt[0] == '_' || strstr(anchoropt, "/_") != NULL) errx(1, "anchor names beginning with '_' cannot " "be modified from the command line"); int len = strlen(anchoropt); if (anchoropt[len - 1] == '*') { warnx("wildcard anchors not supported\n"); changerule_usage(); } if (strlcpy(anchorname, anchoropt, sizeof(anchorname)) >= sizeof(anchorname)) errx(1, "anchor name '%s' too long", anchoropt); } dev = open(pf_device, O_RDWR); if (dev == -1) err(1, "/dev/pf"); return (do_chng_cmd(anchoropt, chng_cmd, rule_no)); }