/*
 * DF-3442 harness -- iface_match() calls kfnmatch(cmd->name, ...) on a
 * rule-supplied name that the kernel never validates for NUL
 * termination (ip_fw3_basic.c:134-141):
 *
 *	if (cmd->name[0] != '\0') {
 *		if (cmd->p_glob) {
 *			if (kfnmatch(cmd->name, ifp->if_xname, 0) == 0)
 *
 * ipfw_insn_if.name[IFNAMSIZ] lives inside the rule instruction; the
 * stock ipfw3(8) CLI always terminates it (libipfw3 fill_iface uses
 * strlcpy), but the kernel-side rule loader (setsockopt IP_FW3)
 * enforces no termination -- a rule blob crafted by a (root) caller
 * with a 16-byte unterminated glob name drives kfnmatch past the
 * instruction/rule allocation until it finds a NUL.
 */
#include "guard.h"
#include <fnmatch.h>
#include <stdio.h>
#include <string.h>

#define	kfnmatch	pattern, string, flags	/* fnmatch(3) == kfnmatch semantics */

struct insn_if {
	int		o_module, o_opcode, o_len;
	int		p_glob;
	char		name[16];	/* IFNAMSIZ; placed at chunk end */
};

static int
iface_match(const struct insn_if *cmd, const char *if_xname)
{
	if (cmd->name[0] == '\0')
		return 0;
	if (cmd->p_glob) {
		if (fnmatch(cmd->name, if_xname, 0) == 0)	/* kfnmatch */
			return 1;
	}
	return 0;
}

	static struct insn_if *cmd;

static void gu_case(void)
{
	iface_match(cmd, "vtnet0");
}

static struct insn_if *cmd;

int
main(int argc, char **argv)
{
	int oob = (argc > 1 && argv[1][0] == 'o');

	printf("DF-3442 (kfnmatch on rule-supplied non-terminated name,"
	    " ip_fw3_basic.c:134-141)\n");
	cmd = (struct insn_if *)guard_buf(sizeof(*cmd));
	memset(cmd, 0, sizeof(*cmd));
	cmd->p_glob = 1;
	memcpy(cmd->name, "********", 8);
	if (!oob)
		cmd->name[7] = 0;			/* control: terminated */

	else
		memset(cmd->name + 8, '*', 8);		/* 16 bytes, no NUL */
	printf("  name: %s, glob=1\n", oob ? "16 bytes, NOT terminated" : "terminated");
	fflush(stdout);
#ifdef FIXED
	/* DF-3442 fix: reject unterminated interface names at rule load */
	{
		int t;
		for (t = 0; t < 16; t++)
			if (cmd->name[t] == 0)
				break;
		if (t == 16) {
			printf("FIXED: unterminated name rejected\n");
			return 0;
		}
	}
#endif
	iface_match(cmd, "vtnet0");
	printf("  ran (terminated)\n");
	return 0;
}
