DF-3442 / df3442.c
/* * 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; } |