ttysnoop-0.12d/ 40755 144 144 0 6512757624 11515 5ustar carlcarlttysnoop-0.12d/ttysnoops.c100644 144 144 27755 6512757545 14102 0ustar carlcarl/* ttysnoops.c ttysnoops sets up a pseudo-tty for the specified program and redirects its stdin/stdout/stderr to/from the current ctty and a specified snoop device (usually another tty or a socket) v0.00 4-1-94 Carl Declerck - first version v0.01 6-1-94 "" - added /etc/snooptab file v0.02 9-1-94 "" - added socket support v0.10 8-8-94 "" - various bug fixes v0.11 9-8-94 "" - password authentication added v0.11a 23-8-94 Ulrich Callmeier - shadow support hacked in v0.12 6-9-94 Carl Declerck - added suspend/resume key v0.12a 6-8-95 Josh Bailey - fixed lingering utmp-entry bug v0.12b 6-3-96 Carl Declerck - minor cleanups v0.12c 21-3-96 Carl Declerck - fixed bug to exit when telnetd exits v0.12d 8-4-98 Carl Declerck - updated #includes a bit */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SHADOW_PWD #include #endif #include "config.h" #include "common.h" #define BUFF_SIZE 256 char buff[BUFF_SIZE]; int snoopfd = -1, authfd = -1; int pgmpid = -1, authpid = -1, servpid = -1; int use_socket = 0, fdmax = 0, proctype = DEAD_PROCESS; char snoopdev[32], ptynam[32], childproc[128], sockname[128]; /* read a single line from a stream, ignoring all irrelevant stuff */ int read_line (char *buff, FILE *f) { int b, i; *buff = 0; do { while ((b = fgetc(f)) != EOF && isspace(b)); if (b == EOF) return (0); for (i = 0; b != EOF && b != '\n' && b != '\r'; i++) { buff[i] = (b == '\t') ? ' ': b; b = fgetc(f); } buff[i] = 0; } while (*buff == '#'); return (1); } /* extract the first word from a string */ char *parse_arg (char *arg, char *buff) { int i = 0, j = 0, quote = 0; *arg = 0; if (buff == NULL) return (NULL); while (buff[i] && buff[i] == ' ') i++; while (buff[i] && (buff[i] != ' ' || quote)) { switch (buff[i]) { case '\\' : arg[j++] = buff[++i]; break; case '"' : quote = !quote; break; default : arg[j++] = buff[i]; } i++; } while (buff[i] && buff[i] == ' ') i++; arg[j] = 0; return (j ? buff + i : NULL); } /* process the snooptab file */ int process_snooptab (void) { FILE *f; char line[1024], arg[128], tty[16], *tail; if ((f = fopen(SNOOPTAB, "r")) == NULL) errorf ("can't open %s\n", SNOOPTAB); strcpy (tty, leafname(ttyname(STDIN_FILENO))); while (read_line(line, f)) { tail = parse_arg(arg, line); if ((strcmp(arg, tty) == 0) || (*arg == '*')) { tail = parse_arg(snoopdev, tail); tail = parse_arg(arg, tail); tail = parse_arg(childproc, tail); if (strcmp(arg, "init") == 0) proctype = INIT_PROCESS; else if (strcmp(arg, "login") == 0) proctype = LOGIN_PROCESS; else if (strcmp(arg, "user") == 0) proctype = USER_PROCESS; if (strcmp(snoopdev, "socket") == 0) use_socket = 1; fclose (f); return (1); } } fclose (f); errorf ("no entry for %s in %s\n", tty, SNOOPTAB); } /* find & open a pty to be used by the pty-master */ int find_ptyxx (char *ptyname) { int fd, i, j; static char *s1 = "pqrs", *s2 = "0123456789abcdef"; strcpy (ptyname, "/dev/ptyxx"); for (i = 0; s1[i]; i++) { ptyname[8] = s1[i]; for (j = 0; s2[j]; j++) { ptyname[9] = s2[j]; if ((fd = open(ptyname, O_RDWR)) >= 0) { ptyname[5] = 't'; return (fd); } if (errno == ENOENT) return (-1); } } return (-1); } /* find & open a pty (tty) to be used by pty-client */ int find_ttyxx (char *ttyname, int ptyfd) { struct group *grp; int gid, fd; if ((grp = getgrnam("tty")) != NULL) gid = grp->gr_gid; else gid = -1; chown (ttyname, getuid(), gid); chmod (ttyname, S_IRUSR | S_IWUSR | S_IWGRP); if ((fd = open(ttyname, O_RDWR)) >= 0) return (fd); close (ptyfd); return (-1); } /* fork off the pty-client and redirect its stdin/out/err to the pty */ int fork_pty (int *ptyfd, char *ttynam) { struct termios term; struct winsize twin; int ttyfd, pid; char name[32]; tcgetattr (STDIN_FILENO, &term); ioctl (STDIN_FILENO, TIOCGWINSZ, (char *) &twin); if ((*ptyfd = find_ptyxx(name)) < 0) errorf ("can't open pty\n"); strcpy (ttynam, leafname(name)); if ((pid = fork()) < 0) errorf ("can't fork\n"); if (pid == 0) /* child */ { if (setsid() < 0) errorf ("setsid failed\n"); if ((ttyfd = find_ttyxx(name, *ptyfd)) < 0) errorf ("can't open tty\n"); close (*ptyfd); if (tcsetattr(ttyfd, TCSANOW, &term) < 0) errorf ("can't set termios\n"); if (ioctl(ttyfd, TIOCSWINSZ, &twin) < 0) errorf ("can't set winsize\n"); if (dup2(ttyfd, STDIN_FILENO) != STDIN_FILENO) errorf ("can't dup2 into stdin\n"); if (dup2(ttyfd, STDOUT_FILENO) != STDOUT_FILENO) errorf ("can't dup2 into stdout\n"); if (dup2(ttyfd, STDERR_FILENO) != STDERR_FILENO) errorf ("can't dup2 into stderr\n"); if (ttyfd > STDERR_FILENO) close (ttyfd); } return (pid); } void beep (void) { fputc (7, stdout); fflush (stdout); } void sigalrm (int sig) { sig = sig; printf ("\r\nPassword timeout\r\n"); exit (0); } int inputs (char *buff, int max, FILE *f) { int b, l; *buff = 0; max--; signal (SIGALRM, sigalrm); while (1) { alarm (10); if ((b = fgetc(f)) != EOF) switch (b) { case '\r' : alarm (0); return (0); case 8 : case 127 : if (*buff == 0) beep (); else buff[strlen(buff) - 1] = 0; break; case 0 : break; default : if (b >= 32 && b < 127) if ((l = strlen(buff)) < max) { buff[l] = b; buff[l + 1] = 0; } else beep (); } } return (0); } void authenticate (int fd) { struct passwd *pw; #ifdef SHADOW_PWD struct spwd *spw; #endif int ret = 0; char buff[16], *pwbuff; if ((authpid = fork()) == 0) /* authentication child */ { dup2 (fd, STDIN_FILENO); dup2 (fd, STDOUT_FILENO); dup2 (fd, STDERR_FILENO); if (fd > STDERR_FILENO) close (fd); if ((pw = getpwnam(SNOOPUSER)) == NULL) exit (1); #ifdef SHADOW_PWD if ((spw = getspnam(SNOOPUSER)) == NULL) exit(1); #endif printf ("Connected to %s snoop server...\r\n", ptynam); printf ("%s (ASCII %d) to suspend, %s (ASCII %d) to terminate.\r\n", SC_STRING, SUSP_CHAR, TC_STRING, TERM_CHAR); printf ("Snoop password:"); fflush (stdout); if (inputs(buff, 16, stdin) == 0) { #ifndef SHADOW_PWD if (strcmp(pw->pw_passwd, crypt(buff, pw->pw_passwd)) == 0) #else if (strcmp(spw->sp_pwdp, crypt(buff, spw->sp_pwdp)) == 0) #endif { printf ("\r\nVerified OK... Snoop started.\r\n"); ret = 1; } else printf ("\r\nPassword incorrect.\r\n"); } fflush (stdout); exit (ret); } } /* This function added by Josh Bailey, josh@tutor.co.nz, 6 Aug 1995. Previous versions of ttysnoop did not remove the pseudo-terminal entry from utmp after log out... Meaning people showed up as still logged in when they weren't. */ void cleanup_utmp (char ptynam[]) { struct utmp utmp; setutent (); strcpy (utmp.ut_line, ptynam); utmp = *(getutline(&utmp)); *utmp.ut_user = 0; pututline (&utmp); endutent (); } /* do a graceful closedown */ void closedown (void) { if (servpid == getpid()) /* only server must clear utmp entry */ cleanup_utmp (ptynam); stty_orig (); } /* signal handlers */ void sighup (int sig) { sig = sig; closedown (); } void sigpipe (int sig) { sig = sig; snoopfd = -1; signal (SIGPIPE, sigpipe); } void sigchld (int sig) { int status, pid; sig = sig; if ((pid = wait(&status)) == authpid) { if (((status >> 8) & 0xff) == 1) { snoopfd = authfd; fdmax = max(fdmax, snoopfd); syslog (LOG_INFO, "snoop on %s (%s)", ttyname(STDIN_FILENO), leafname(sockname)); } else close (authfd); authpid = authfd = -1; } else if (pid == pgmpid) raise (SIGHUP); signal (SIGCHLD, sigchld); } /* main program */ int main (int argc, char *argv[]) { struct sockaddr_un serv_addr, cli_addr; fd_set readset; struct utmp utmp; int ptyfd, servfd, len, n, sel, susp = 0; if (!isatty(STDIN_FILENO)) errorf ("stdin is not a tty\n"); /* do init stuff */ servpid = getpid(); *sockname = 0; signal (SIGPIPE, sigpipe); signal (SIGCHLD, sigchld); signal (SIGHUP, sighup); stty_initstore (); process_snooptab (); openlog ("ttysnoops", LOG_PID, LOG_AUTH); atexit (closedown); /* fork off the client and load the new image */ if ((pgmpid = fork_pty(&ptyfd, ptynam)) == 0) /* child */ { /* should we update utmp to reflect the change to ttypX ? */ if (proctype == LOGIN_PROCESS) { strncopy (utmp.ut_line, ptynam); strncopy (utmp.ut_id, ptynam + 3); *utmp.ut_host = 0; utmp.ut_addr = 0; strncopy (utmp.ut_user, "LOGIN"); utmp.ut_pid = getpid(); utmp.ut_type = proctype; utmp.ut_time = time(NULL); pututline (&utmp); } /* exec the real pty-client program */ argv[0] = childproc; execv (childproc, &argv[0]); errorf ("can't exec %s\n", childproc); } /* put stdin into raw mode */ stty_raw (STDIN_FILENO); /* calc (initial) max file descriptor to use in select() */ fdmax = max(STDIN_FILENO, ptyfd); /* is the snoop-device a socket or tty ? */ if (use_socket) { /* create the main server socket */ if ((servfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) errorf ("can't create server socket\n"); sprintf (sockname, "%s/%s", SPOOLDIR, ptynam); unlink (sockname); serv_addr.sun_family = AF_UNIX; strncopy (serv_addr.sun_path, sockname); if (bind(servfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) errorf ("can't bind server socket (%s)\n", sockname); if (listen(servfd, 5) < 0) errorf ("can't set up listen buffers for server socket\n"); /* update fdmax */ fdmax = max(fdmax, servfd); } else /* snoop-dev is a tty */ { /* open snoop-device and put it into raw mode */ if ((snoopfd = open(snoopdev, O_RDWR)) < 0) errorf ("can't open snoop device %s\n", snoopdev); if (isatty(snoopfd)) stty_raw (snoopfd); /* update fdmax */ fdmax = max(fdmax, snoopfd); } /* do the main server loop */ while (1) { do { FD_ZERO (&readset); FD_SET (STDIN_FILENO, &readset); FD_SET (ptyfd, &readset); if (snoopfd >= 0) FD_SET (snoopfd, &readset); else if (use_socket && authfd == -1) FD_SET (servfd, &readset); sel = select(fdmax + 1, &readset, NULL, NULL, NULL); } while (sel == -1 && errno == EINTR); if (sel == -1 && errno != EINTR) errorf ("select failed. errno = %d\n", errno); if (FD_ISSET(STDIN_FILENO, &readset)) { if ((n = read(STDIN_FILENO, buff, BUFF_SIZE)) < 1) exit (0); write (ptyfd, buff, n); } if ((snoopfd >= 0) && FD_ISSET(snoopfd, &readset)) { n = read(snoopfd, buff, BUFF_SIZE); if (!n || ((*buff == TERM_CHAR) && (n == 1) && use_socket)) { close (snoopfd); snoopfd = -1; } else if ((*buff == SUSP_CHAR) && (n == 1) && use_socket) { if (susp = !susp) fdprintf (snoopfd, "\r\nSnoop suspended. %s (ASCII %d) to resume.\r\n", SC_STRING, SUSP_CHAR); else fdprintf (snoopfd, "Snoop resumes...\r\n"); } else write (ptyfd, buff, n); } if (FD_ISSET(ptyfd, &readset)) { if ((n = read(ptyfd, buff, BUFF_SIZE)) < 1) exit (0); write (STDOUT_FILENO, buff, n); if (!susp && (snoopfd >= 0)) write (snoopfd, buff, n); } if (use_socket && (snoopfd < 0) && (authfd < 0) && FD_ISSET(servfd, &readset)) { /* a ttysnoop client wants to connect, create socket */ if ((authfd = accept(servfd, (struct sockaddr *) &cli_addr, &len)) < 0) errorf ("can't accept on server socket\n"); else { susp = 0; authenticate (authfd); } /* update fdmax */ /* fdmax = max(fdmax, snoopfd); */ } } } ttysnoop-0.12d/README100644 144 144 3322 6512757266 12474 0ustar carlcarl README for ttysnoop 0.12d (alpha) -- 08-04-98 This is version 0.12 of ttysnoop for Linux. It introduces one new feature and now also has support for shadow passwords. (Thanx to those who mailed me the patches) The package allows you to snoop on login tty's through another tty-device or pseudo-tty. The snoop-tty becomes a 'clone' of the original tty, redirecting both input and output from/to it. To install, untar the archive, customize Makefile and config.h and type 'make'. If all goes well, type 'make install' next and copy the example snooptab.dist file to /etc/snooptab. Edit this new copy and tell your getty/telnetd to execute /sbin/ttysnoops as the login program (instead of /bin/login). You should then be able to snoop upon the tty's listed in /etc/snooptab whevener a user logs in on them. Mail any bug-reports/patches/comments to carl@miskatonic.inbe.net (preferred) or carl@miskatonic.nfe.be. History log: * From v0.11 onwards, user authentication is performed via a password prompt whenever snooping is done through a pty. * From v0.12 onwards, the ttysnoop client supports a suspend/resume key (default ctrl-\) which suspends/resumes snooping when it is pressed. Might come in handy when snooping rz/sz sessions. ;) * Version 0.12c is a bugfix release for 0.12. It mainly addresses the lingering utmp-entries bug that caused users to "remain online" when doing a 'w' or 'who' command. Apart from a few other minor cleanups, this version is identical to v0.12. * Version 0.12d is a few cleanups/updates in the #includes department so that it compiles OK again on newer systems such as RH5. Also check the LIBS= value in Makefile if you're still on a system without libcrypt. Carl Declerck. ttysnoop-0.12d/ttysnoop.c100644 144 144 3742 6512741010 13641 0ustar carlcarl/* ttysnoop.c ttysnoop is the client to ttysnoops. it can only be used on ptys that are snooped via a socket 'device' in /etc/snooptab. v0.00 9-1-94 Carl Declerck - first version v0.10 8-8-94 "" - bug fixes v0.11 9-8-94 "" - pwd authentication v0.12 6-9-94 "" - see ttysnoops.c */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "common.h" #define BUFF_SIZE 256 char buff[BUFF_SIZE]; int main (int argc, char *argv[]) { fd_set readset; struct sockaddr_un sock_addr; int sockfd, fdmax, quit = 0, n; char sockname[128]; if (argc < 2) errorf ("Usage: ttysnoop \n"); /* create the client socket */ if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) errorf ("can't create client socket\n"); sprintf (sockname, "%s/%s", SPOOLDIR, argv[1]); sock_addr.sun_family = AF_UNIX; strncopy (sock_addr.sun_path, sockname); if (connect(sockfd, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) errorf ("can't connect to server\n"); /* put stdin into raw mode */ stty_initstore (); atexit (stty_orig); if (isatty(STDIN_FILENO)) stty_raw (STDIN_FILENO); /* calc max file descriptor for select() */ fdmax = max(STDIN_FILENO, sockfd); /* do our thing */ while (!quit) { FD_ZERO (&readset); FD_SET (STDIN_FILENO, &readset); FD_SET (sockfd, &readset); select (fdmax + 1, &readset, NULL, NULL, NULL); if (FD_ISSET(STDIN_FILENO, &readset)) { n = read(STDIN_FILENO, buff, BUFF_SIZE); if (write(sockfd, buff, n) < 0) quit = 1; } if (FD_ISSET(sockfd, &readset)) { if ((n = read(sockfd, buff, BUFF_SIZE)) < 1) quit = 1; if (n > 0) write (STDOUT_FILENO, buff, n); } } printf ("\r\nBack at local tty.\r\n"); } ttysnoop-0.12d/common.c100644 144 144 3147 5633331345 13242 0ustar carlcarl/* common.c common stuff for the ttysnoops & ttysnoop programs */ #include #include #include #include "common.h" #define TTY_STORE 16 static struct termios orig_tty_state[TTY_STORE]; static int sttyfds[TTY_STORE]; void errorf (char *fmt, ...) { va_list args; va_start (args, fmt); vfprintf (stderr, fmt, args); exit (1); } int fdprintf (int fd, char *fmt, ...) { va_list args; int r; char str[256]; va_start (args, fmt); r = vsprintf(str, fmt, args); write (fd, str, r); return (r); } char *leafname (char *path) { int i = 0, j; for (j = 0; path[j]; j++) if (path[j] == '/') i = j + 1; return (path + i); } /* init the stty store array */ void stty_initstore (void) { int i; for (i = 0; i < TTY_STORE; i++) sttyfds[i] = -1; } /* set tty on fd into raw mode */ int stty_raw (int fd) { struct termios tty_state; int i; if (tcgetattr(fd, &tty_state) < 0) return (-1); for (i = 0; i < TTY_STORE; i++) if (sttyfds[i] == -1) { orig_tty_state[i] = tty_state; sttyfds[i] = fd; break; } tty_state.c_lflag &= ~(ICANON | IEXTEN | ISIG | ECHO); tty_state.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON | BRKINT); tty_state.c_oflag &= ~OPOST; tty_state.c_cflag |= CS8; tty_state.c_cc[VMIN] = 1; tty_state.c_cc[VTIME] = 0; if (tcsetattr(fd, TCSAFLUSH, &tty_state) < 0) return (-1); return (0); } /* restore all altered ttys to their original state */ void stty_orig (void) { int i; for (i = 0; i < TTY_STORE; i++) if (sttyfds[i] != -1) { tcsetattr (sttyfds[i], TCSAFLUSH, &orig_tty_state[i]); sttyfds[i] = -1; } } ttysnoop-0.12d/common.h100644 144 144 430 5633331364 13220 0ustar carlcarl/* common.h */ #define max(x,y) ((x) > (y) ? (x) : (y)) #define strncopy(x,y) strncpy (x, y, sizeof(x)) void errorf (char *fmt, ...); int fdprintf (int fd, char *fmt, ...); char *leafname (char *path); void stty_initstore (void); int stty_raw (int fd); void stty_orig (void); ttysnoop-0.12d/Makefile100644 144 144 1746 6512757601 13255 0ustar carlcarl# # Makefile for the ttysnoop programs # CC = gcc # Without shadow support CCOPTS = -O2 LIBS = -lcrypt # remove -lcrypt if your system doesn't have it # For shadow support #CCOPTS = -O2 -DSHADOW_PWD #LIBS = -lshadow SERVEROBJS = ttysnoops.o common.o CLIENTOBJS = ttysnoop.o common.o SERVERSRCS = ttysnoops.c CLIENTSRCS = ttysnoop.c INCLUDES = config.h common.h all: ttysnoops ttysnoop ttysnoops: $(SERVEROBJS) $(CC) $(SERVEROBJS) -o ttysnoops $(LIBS) ttysnoop: $(CLIENTOBJS) $(CC) $(CLIENTOBJS) -o ttysnoop $(LIBS) ttysnoops.o: $(SERVERSRCS) $(INCLUDES) $(CC) $(CCOPTS) -c -o ttysnoops.o $(SERVERSRCS) ttysnoop.o: $(CLIENTSRCS) $(INCLUDES) $(CC) $(CCOPTS) -c -o ttysnoop.o $(CLIENTSRCS) common.o: common.c common.h $(CC) $(CCOPTS) -c -o common.o common.c clean: rm -f *.o core ttysnoop ttysnoops install: install -s ttysnoop /sbin install -s ttysnoops /sbin install -m 644 ttysnoop.8 /usr/man/man8/ @echo ... copy snooptab.dist to /etc/snooptab and edit it ... ttysnoop-0.12d/config.h100644 144 144 1010 5633153367 13215 0ustar carlcarl/* config.h edit this to suit your own taste */ /* character to terminate ttysnoop client */ #define TERM_CHAR 31 /* ctrl + '-' */ #define TC_STRING "Ctrl+'-'" /* character to suspend/resume ttysnoop client */ #define SUSP_CHAR 28 /* ctrl + '\' */ #define SC_STRING "Ctrl+'\\'" /* location of snooptab and named fifos (unix domain sockets) */ #define SNOOPTAB "/etc/snooptab" #define SPOOLDIR "/var/spool/ttysnoop" /* which user's password to use for ttysnoop authentication */ #define SNOOPUSER "root" ttysnoop-0.12d/ttysnoop.8100644 144 144 5357 6117375174 13611 0ustar carlcarl.Dd August 8 1994 .Dt TTYSNOOP 8 .Os .Sh NAME .Nm ttysnoop .Nd snoop on a user's tty .Sh SYNOPSIS .Nm ttysnoop .Op Ar pty .Nm ttysnoops .Op Ar loginname .Sh DESCRIPTION The .Nm ttysnoop / .Nm ttysnoops client-server combo can be used to snoop (watch) on a user's login tty. The server .Pq Nm ttysnoops is usually started by getty(8) or telnetd(8) and reads the file .Nm /etc/snooptab to find out which tty's should be cloned and which programs to run on them (usually /bin/login). A tty may be snooped through a pre-determined (ie. fixed) device, or through a dynamically allocated pseudo-tty (pty). This is also specified in the .Nm /etc/snooptab file. To connect to the pty, the .Nm ttysnoop client should be used. .Ss Format of /etc/snooptab The .Nm /etc/snooptab file may contain comment lines (starting with a '#'), empty lines, or entries for tty's that should be snooped upon. The format of such an entry is as follows: .Pp .Bd -literal -offset indent tty snoop-device type program .Ed .Pp where .Pa tty is the leaf-name of the tty that should be snooped upon (eg. ttyS2, not /dev/ttyS2) OR the wildcard '*', which matches ANY tty. .Pa snoop-device is the device through which .Pa tty should be snooped (eg. /dev/tty8) OR the literal constant "socket". The latter is used to tell .Pa ttysnoops that the snoop-device will be a dynamically allocated pty. .Pa type specifies the type of program that should be run, currently recognized types are "init", "user" and "login" altough the former two aren't really needed. Finally, .Pa program is the full pathname to the program to run when .Pa ttysnoops has cloned .Pa tty onto .Pa snoop-device . .Sh EXAMPLE The following example .Pa /etc/snooptab file should illustrate the typical use of .Pa ttysnoop / .Pa ttysnoops : .Pp .Bd -literal -offset indent # # example /etc/snooptab # ttyS0 /dev/tty7 login /bin/login ttyS1 /dev/tty8 login /bin/login # # the wildcard tty should always be the last one in the file # * socket login /bin/login # # example end # .Ed .Pp With the above example, whenever a user logs in on /dev/ttyS0 or /dev/ttyS1, either tty will be snooped through /dev/tty7 or /dev/tty8 respectively. Any other tty's will be snooped through a pty that will be allocated at the time of login. The system-administrator can then run .Pa ttysnoop Ar pty to snoop through the pty. Note that it is up to the system-administrator to setup getty and/or telnetd so that they execute .Pa ttysnoops instead of /bin/login. .Sh SEE ALSO .Xr getty 8 , .Xr telnetd 8 .Sh FILES /etc/snooptab .Sh BUGS The program is unable to do any terminal control-code translations for the original tty and the snoop-device. I doubt it will ever do this. .Sh AUTHOR Carl Declerck, carl@miskatonic.inbe.net ttysnoop-0.12d/snooptab.dist100644 144 144 707 5514632000 14265 0ustar carlcarl# # /etc/snooptab # # tty snoopdev type execpgm # ttyS1 /dev/tty7 login /bin/login ttyS2 /dev/tty8 login /bin/login # # remember to inform your gettys on the above lines # that /etc/ttysnoops is the login program now # # the 'socket' snoop-device is for use with the # ttysnoop client # (any tty not listed above will match the wildcard) # * socket login /bin/login # # remember to inform your telnetd that /etc/ttysnoops # is the login program now