DF-2769 / film_victim.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | /* * DF-2769 film rig — VICTIM actor (runs as root). * * Models the verified scenario: a root admin session living on a unix98 * pty inside a "terminal" (xterm / `ssh -tt` / script(1)). The session * leader is a root shell with nohup semantics (SIGHUP ignored, EOF * tolerant) — exactly the class of session that survives its terminal * dying: crashed xterm, dropped ssh connection, killed script/tmux * server with a nohup'd leader on the pty. * * parent = the terminal: relays master<->stdout for the LIVE phase, * then "crashes" (close(master), exit) at a fixed beat. * child = the root session leader on the slave: prompt + system() * loop, SIGHUP ignored; goes silent (EOF loop) when the * terminal dies; executes whatever arrives on the tty. * * Output transcript goes to stdout (the film driver's pipe) prefixed * with [terminal]; the session's own terminal bytes are relayed raw. */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/select.h> #include <sys/stat.h> #include <sys/wait.h> #include <errno.h> #include <fcntl.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <termios.h> #include <unistd.h> #ifndef TIOCSCTTY #define TIOCSCTTY _IO('t', 132) #endif static int drain_master(int fd, int usec) { char buf[1024]; ssize_t l; struct timeval tv; fd_set rfds; for (;;) { tv.tv_sec = 0; tv.tv_usec = usec; FD_ZERO(&rfds); FD_SET(fd, &rfds); if (select(fd + 1, &rfds, NULL, NULL, &tv) <= 0) break; l = read(fd, buf, sizeof(buf) - 1); if (l <= 0) break; buf[l] = 0; printf("%s", buf); /* raw relay: this is the terminal's screen */ fflush(stdout); } return 0; } int main(void) { int mfd, sfd; char *slavepath; pid_t pid; FILE *info; const char *demo[] = { "whoami", "hostname", "id", "echo ROOT-SESSION-ALIVE" }; int i; setvbuf(stdout, NULL, _IOLBF, 0); mfd = posix_openpt(O_RDWR | O_NOCTTY); if (mfd < 0) { perror("victim: posix_openpt"); exit(1); } if (grantpt(mfd) != 0) { perror("victim: grantpt"); exit(1); } if (unlockpt(mfd) != 0) { perror("victim: unlockpt"); exit(1); } slavepath = ptsname(mfd); if (slavepath == NULL) { perror("victim: ptsname"); exit(1); } pid = fork(); if (pid < 0) { perror("fork"); exit(1); } if (pid == 0) { /* the root session leader on the slave tty */ char line[512]; close(mfd); setsid(); sfd = open(slavepath, O_RDWR); if (sfd < 0) _exit(1); ioctl(sfd, TIOCSCTTY, (void *)0); dup2(sfd, 0); dup2(sfd, 1); dup2(sfd, 2); if (sfd > 2) close(sfd); signal(SIGHUP, SIG_IGN); /* nohup semantics: survives HUP */ for (;;) { printf("root@dfbsd:~ # "); fflush(stdout); errno = 0; if (!fgets(line, sizeof(line), stdin)) { /* terminal died: ZOMBIE tty reads EOF forever; * stay alive and wait (silent, no prompt spam) */ clearerr(stdin); usleep(150000); continue; } line[strcspn(line, "\r\n")] = 0; if (line[0] == 0) continue; if (!strcmp(line, "exit") || !strcmp(line, "logout")) break; system(line); /* executed as root */ } _exit(0); } /* parent: the terminal emulator around this session */ info = fopen("/tmp/film/victim.info", "w"); if (info) { fprintf(info, "UNIT=%s\nPID=%d\n", slavepath, (int)pid); fclose(info); } printf("[terminal] root admin session opened: pty %s, session pid %d\n", slavepath, (int)pid); printf("[terminal] terminal fd (pty master) = %d\n", mfd); fflush(stdout); usleep(500000); drain_master(mfd, 300000); /* first prompt appears */ /* ---- LIVE phase: prove the root session is ALIVE + INTERACTIVE ---- */ for (i = 0; i < 4; i++) { if (write(mfd, demo[i], strlen(demo[i])) < 0) perror("terminal write"); write(mfd, "\n", 1); usleep(350000); drain_master(mfd, 600000); } usleep(300000); /* ---- CRASH phase: the terminal dies, the session stays ---- */ printf("\n[terminal] *** TERMINAL CRASH *** (xterm killed / ssh connection dropped)\n"); printf("[terminal] close(master=%d) -> kernel ptcclose(): gates disarmed,\n", mfd); printf("[terminal] /dev/ptm/N left registered 0666 root:root, tty goes ZOMBIE\n"); printf("[terminal] SIGHUP -> ignored by session leader -> session SURVIVES\n"); close(mfd); /* == terminal death */ printf("[terminal] terminal process exiting; root session left behind on %s\n", slavepath); fflush(stdout); return 0; } |