⬢ DragonFlyBSD Kernel Audit
DF-3260 / poc_regression.c
← back to finding ↓ download raw
/* DF-3260 fix regression check: valid rules must still install & work */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define O_IP_SRC	1
#define O_PROBE_STATE	36
#define O_KEEP_STATE	37
#define O_LOG		40
#define O_ACCEPT	43

#define IP_FW_ADD	50
#define IP_FW_GET	54

struct ioc_rule {
	uint16_t	act_ofs, cmd_len, rulenum;
	uint8_t		set, usr_flags;
	uint32_t	set_disable, static_count, static_len;
	uint64_t	pcnt, bcnt;
	uint32_t	timestamp;
	uint8_t		reserved[16];
	uint32_t	cmd[64];
};
#define INSN(op, len, arg1)	((op) | ((len) << 8) | ((arg1) << 16))

int
main(void)
{
	static unsigned char buf[65536];
	struct ioc_rule r;
	struct sockaddr_in src, dst;
	socklen_t len;
	int s, us, i, nvalid = 0;

	s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if (s < 0) { perror("raw"); exit(1); }

	/* 1. same shape as PoC1 rule, but act_ofs=4 (the O_ACCEPT) */
	memset(&r, 0, sizeof(r));
	r.rulenum = 1000; r.cmd_len = 5; r.act_ofs = 4;
	r.cmd[0] = INSN(O_PROBE_STATE, 1, 0);
	r.cmd[1] = INSN(O_IP_SRC, 2, 0);
	r.cmd[2] = 0xffff007f;
	r.cmd[3] = INSN(O_KEEP_STATE, 1, 0);
	r.cmd[4] = INSN(O_ACCEPT, 1, 0);
	i = setsockopt(s, IPPROTO_IP, IP_FW_ADD, &r, 60 + 20);
	printf("[%c] rule 1000 (act_ofs=4, action boundary): %s\n",
	    i ? '-' : '+', i ? "REJECTED (regression!)" : "installed");
	if (i == 0) nvalid++;

	/* 2. log rule with act_ofs=0 (the O_LOG boundary) */
	memset(&r, 0, sizeof(r));
	r.rulenum = 2000; r.cmd_len = 4; r.act_ofs = 0;
	r.cmd[0] = INSN(O_LOG, 3, 0);
	r.cmd[1] = 0x00000128;
	r.cmd[2] = 0x00000128;
	r.cmd[3] = INSN(O_ACCEPT, 1, 0);
	i = setsockopt(s, IPPROTO_IP, IP_FW_ADD, &r, 60 + 16);
	printf("[%c] rule 2000 (log rule, act_ofs=0): %s\n",
	    i ? '-' : '+', i ? "REJECTED (regression!)" : "installed");
	if (i == 0) nvalid++;

	/* 3. traffic still flows & state rule works (2 pkts, no panic) */
	us = socket(AF_INET, SOCK_DGRAM, 0);
	memset(&src, 0, sizeof(src));
	src.sin_family = AF_INET; src.sin_port = htons(54321);
	src.sin_addr.s_addr = inet_addr("127.0.255.255");
	memset(&dst, 0, sizeof(dst));
	dst.sin_family = AF_INET; dst.sin_port = htons(9);
	dst.sin_addr.s_addr = inet_addr("127.0.0.1");
	if (bind(us, (struct sockaddr *)&src, sizeof(src)) != 0) {
		/* alias missing: fall back to 127.0.0.1 (rule won't match,
		 * but traffic must still pass) */
		src.sin_addr.s_addr = inet_addr("127.0.0.1");
		bind(us, (struct sockaddr *)&src, sizeof(src));
	}
	for (i = 0; i < 2; i++)
		sendto(us, buf, 8, 0, (struct sockaddr *)&dst, sizeof(dst));
	close(us);
	sleep(1);

	len = sizeof(buf);
	if (getsockopt(s, IPPROTO_IP, IP_FW_GET, buf, &len) == 0)
		printf("[+] IP_FW_GET ok (%u bytes) -- kernel alive, "
		    "no panic, no wedge\n", (unsigned)len);
	else
		printf("[-] IP_FW_GET failed\n");

	printf("%s\n", nvalid == 2 ?
	    "PASS: valid rules accepted, traffic flows" :
	    "FAIL: valid rule rejected (regression)");
	return nvalid == 2 ? 0 : 1;
}