#include "super.h" #include "version.h" /* glibc says to use TCSASOFT if it's available. */ #ifndef TCSASOFT #define TCSASOFT 0 #endif #ifdef HAVE_TERMIOS_H static struct termios old, new; #ifdef HAVE_TCGETATTR #define Gtty(fd, arg) tcgetattr(fd, arg) #else #ifdef TIOCGETA #define Gtty(fd, arg) ioctl(fd, TIOCGETA, arg) #else #define Gtty(fd, arg) ioctl(fd, TCGETA, arg) #endif #endif #ifdef HAVE_TCSETATTR #define Stty(fd, arg) tcsetattr(fd, TCSAFLUSH|TCSASOFT, arg) #else #define Stty(fd, arg) ioctl(fd, TCSAFLUSH, arg) #endif /* From Gordon Lack: do not turn off ICANON (erase and kill processing): * on some OS's, chars will come through in bunches of 4; and * it is incompatible with getpass(), which does erase and kill * processing anyway. */ #define ECHO_OFF(tios) ((tios).c_lflag &= ~ECHO) #else #ifdef HAVE_TERMIO_H static struct termio old, new; #define Gtty(fd, arg) ioctl(fd, TCGETA, arg) #define Stty(fd, arg) ioctl(fd, TCSETAF, arg) #define ECHO_OFF(tio) ((tio).c_lflag &= ~ECHO) #else #ifdef HAVE_SGTTY_H static struct sgttyb old, new; #define ECHO_OFF(tty) ((tty).sg_flags &= ~(ECHO)) #define Gtty(fd, arg) gtty(fd, arg) #define Stty(fd, arg) stty(fd, arg) #else #error "You do not have any of termios.h, termio.h, sgtty.h!" #endif #endif #endif /* We want to catch signals, so that we can restore terminal * settings before responding to signals. */ static void (*oldHUP)(), (*oldINT)(), (*oldQUIT)(); static void (*oldALRM)(), (*oldTERM)(), (*oldPIPE)(); static int fdin=0, fdout=1; static void restoresig(); static void setsighdl(); static void sighdl(); /* * In order to stay compatible with (very) old Unixes, we stick * with the old signal() interface. So, it won't do the right thing * if multiple signals are received quickly, but that's just tough * luck for the (ab)user -- all we really want to do is restore * terminal settings when a signal is received, and if a user is * trying to be too clever by half with signals, all they will get * is an aborted super() call and a non-echoing terminal. */ static void sighdl(sig) int sig; { /* Restore everything, then resignal */ Stty(fdin, &old); restoresig(); kill(getpid(), sig); } static void setsighdl() { oldHUP = (void (*)()) signal(SIGHUP, sighdl); oldINT = (void (*)()) signal(SIGINT, sighdl); oldQUIT = (void (*)()) signal(SIGQUIT, sighdl); oldALRM = (void (*)()) signal(SIGALRM, sighdl); oldTERM = (void (*)()) signal(SIGTERM, sighdl); oldPIPE = (void (*)()) signal(SIGPIPE, sighdl); } static void restoresig() { (void) signal(SIGHUP, oldHUP); (void) signal(SIGINT, oldINT); (void) signal(SIGQUIT, oldQUIT); (void) signal(SIGALRM, oldALRM); (void) signal(SIGTERM, oldTERM); (void) signal(SIGPIPE, oldPIPE); } int readl(fd, buf, buflen) int fd; char *buf; int buflen; /* must be > 0 */ { int n; char *s, *end; for (s=buf, end=buf+buflen; s < end; s++) { n = read(fd, s, 1); if (n != 1 || *s == '\n' || *s == '\r') { *s = '\0'; return s-buf; } } return s-buf; } /* * Returns: -1 (and print message) if there was a problem adjusting * tty settings, else number of characters in password. */ int s_getpass(prompt, use_stdin, buf, n_buf) char *prompt; /* If !null, printed as prompt. * Uses /dev/tty unless use_stdin is set, or * if /dev/tty can't be opened, in which case * it uses stderr. */ int use_stdin; /* Directs input to come from stdin instead of * /dev/tty. */ char *buf; /* Where the password is placed */ int n_buf; /* size of buf. Up to n_buf characters are written * into *buf, and then nul-terminated. */ { int nread, gtty_status; if (use_stdin || (fdin = open("/dev/tty", O_RDWR)) == -1) { fdin = 0; fdout = 2; /* use stderr for prompting when input is stdin */ } else { fdout = fdin; } /* * Turn echoing off */ gtty_status = Gtty(fdin, &old); new = old; ECHO_OFF(new); setsighdl(); if (gtty_status == 0) { Stty(fdin, &new); } if (prompt) { write(fdout, prompt, strlen(prompt)); } /* * Read password */ nread = readl(fdin, buf, n_buf); if (nread >= 0) { buf[(nread < n_buf) ? nread : (n_buf-1)] = '\0'; } if (prompt) { write(fdout, "\n", 1); } /* * Restore terminal and signal handling. */ if (gtty_status == 0) { Stty(fdin, &old); } if (fdin != 0) { close(fdin); } restoresig(); return nread; }