/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #ifdef WITH_LIBPASSPHRASE # include #endif #include "arg.h" #define EXIT_ERROR 125 #define EXIT_EXEC 126 #define EXIT_NOENT 127 #ifndef RETRY_SLEEP # define RETRY_SLEEP 1 #endif #ifndef PROMPT # define PROMPT "%s (%s@%s) password: ", argv0, pwd->pw_name, hostname #endif char *argv0; static void usage(void) { fprintf(stderr, "usage: %s [-e] command [argument] ...\n", argv0); exit(EXIT_ERROR); } static void set_environ(void) { char *str; size_t len; struct passwd *pw; errno = 0; pw = getpwuid(0); if (!pw) { if (errno) fprintf(stderr, "%s: getpwuid 0: %s\n", argv0, strerror(errno)); else fprintf(stderr, "%s: cannot find root user\n", argv0); exit(EXIT_ERROR); } libenv_select_variable_list((const char **)(void *)environ, LIBENV_SU_SAFE, LIBENV_END); if (pw->pw_dir && *pw->pw_dir) { if (setenv("HOME", pw->pw_dir, 1)) fprintf(stderr, "%s: setenv HOME %s 1: %s\n", argv0, pw->pw_dir, strerror(errno)); } if (pw->pw_name && *pw->pw_name) { if (setenv("LOGNAME", pw->pw_name, 1)) fprintf(stderr, "%s: setenv LOGNAME %s 1: %s\n", argv0, pw->pw_name, strerror(errno)); if (setenv("USER", pw->pw_name, 1)) fprintf(stderr, "%s: setenv USER %s 1: %s\n", argv0, pw->pw_name, strerror(errno)); len = sizeof("/var/spool/mail/") + strlen(pw->pw_name); str = malloc(len); if (!str) fprintf(stderr, "%s: malloc %zu: %s\n", argv0, len, strerror(errno)); stpcpy(stpcpy(str, "/var/spool/mail/"), pw->pw_name); if (setenv("MAIL", str, 1)) fprintf(stderr, "%s: setenv MAIL %s 1: %s\n", argv0, str, strerror(errno)); free(str); } if (pw->pw_shell && *pw->pw_shell) { if (setenv("SHELL", pw->pw_shell, 1)) fprintf(stderr, "%s: setenv SHELL %s 1: %s\n", argv0, pw->pw_shell, strerror(errno)); } } #ifndef WITH_LIBPASSPHRASE static char * read_passphrase(int fd) { char *passphrase = NULL, *new; size_t size = 0, len = 0; ssize_t r; for (;;) { if (len == size) { new = realloc(passphrase, size += 32); if (!new) { fprintf(stderr, "\n%s: realloc %zu: %s\n", argv0, size, strerror(errno)); if (passphrase) { memset(passphrase, 0, len); free(passphrase); } return NULL; } passphrase = new; } r = read(fd, &passphrase[len], 1); if (r < 0) { fprintf(stderr, "\n%s: read /dev/tty 1: %s\n", argv0, strerror(errno)); memset(passphrase, 0, len); free(passphrase); return NULL; } else if (!r || passphrase[len] == '\n') { break; } else { len += 1; } } fprintf(stderr, "\n"); passphrase[len] = 0; return passphrase; } #endif static void check_password(void) { struct passwd *pwd; struct spwd *shdw; const char *expected, *got; char *hostname = NULL; char *passphrase; size_t size = 8; #ifndef WITH_LIBPASSPHRASE struct termios stty_original; #endif struct termios stty_enter; #if RETRY_SLEEP > 0 struct termios stty_sleep; #endif int fd; errno = 0; for (;;) { hostname = realloc(hostname, size *= 2); if (!hostname) { fprintf(stderr, "%s: realloc %zu: %s\n", argv0, size, strerror(errno)); } *hostname = 0; if (!gethostname(hostname, size)) { if (strnlen(hostname, size) < size - 1) break; } else if (errno != ENAMETOOLONG) { fprintf(stderr, "%s: gethostname %zu: %s\n", argv0, size, strerror(errno)); exit(EXIT_ERROR); } } errno = 0; pwd = getpwuid(getuid()); if (!pwd || !pwd->pw_name || !*pwd->pw_name) { if (errno) fprintf(stderr, "%s: getpwuid: %s\n", argv0, strerror(errno)); else fprintf(stderr, "%s: your user does not exist\n", argv0); exit(EXIT_ERROR); } shdw = getspnam(pwd->pw_name); if (!shdw || !shdw->sp_pwdp) { if (errno) fprintf(stderr, "%s: getspnam: %s\n", argv0, strerror(errno)); else fprintf(stderr, "%s: your user does not have a shadow entry\n", argv0); exit(EXIT_ERROR); } expected = shdw->sp_pwdp; if (!*expected) { fprintf(stderr, "%s: your user does not have a password\n", argv0); exit(EXIT_ERROR); } else if (expected[expected[0] == '!'] == '*' || expected[expected[0] == '!'] == 'x') { fprintf(stderr, "%s: login to your account is disabled\n", argv0); exit(EXIT_ERROR); } else if (expected[0] == '!') { fprintf(stderr, "%s: your account is currently locked\n", argv0); exit(EXIT_ERROR); } fd = open("/dev/tty", O_RDWR); if (fd < 0) { fprintf(stderr, "%s: open /dev/tty O_RDWR: %s\n", argv0, strerror(errno)); exit(EXIT_ERROR); } #ifdef WITH_LIBPASSPHRASE passphrase_disable_echo1(fd); memset(&stty_enter, 0, sizeof(stty_enter)); if (tcgetattr(fd, &stty_enter)) { fprintf(stderr, "%s: tcgetattr /dev/tty: %s\n", argv0, strerror(errno)); passphrase_reenable_echo1(fd); close(fd); exit(EXIT_ERROR); } #else memset(&stty_original, 0, sizeof(stty_original)); if (tcgetattr(fd, &stty_original)) { fprintf(stderr, "%s: tcgetattr /dev/tty: %s\n", argv0, strerror(errno)); close(fd); exit(EXIT_ERROR); } memcpy(&stty_enter, &stty_original, sizeof(stty_original)); stty_enter.c_lflag &= (tcflag_t)~ECHO; tcsetattr(fd, TCSAFLUSH, &stty_enter); #endif #if RETRY_SLEEP > 0 memcpy(&stty_sleep, &stty_enter, sizeof(stty_enter)); stty_sleep.c_lflag = 0; #endif again: fprintf(stderr, PROMPT); fflush(stderr); #ifdef WITH_LIBPASSPHRASE passphrase = passphrase_read2(fd, PASSPHRASE_READ_EXISTING); if (!passphrase) { fprintf(stderr, "%s: passphrase_read2 /dev/tty PASSPHRASE_READ_EXISTING: %s\n", argv0, strerror(errno)); passphrase_reenable_echo1(fd); close(fd); exit(EXIT_ERROR); } #else passphrase = read_passphrase(fd); if (!passphrase) { tcsetattr(fd, TCSAFLUSH, &stty_original); close(fd); exit(EXIT_ERROR); } #endif got = crypt(passphrase, expected); #ifdef WITH_LIBPASSPHRASE passphrase_wipe1(passphrase); #else memset(passphrase, 0, strlen(passphrase)); #endif free(passphrase); if (strcmp(got, expected)) { fprintf(stderr, "%s: incorrect password, please try again\n", argv0); #if RETRY_SLEEP > 0 tcsetattr(fd, TCSAFLUSH, &stty_sleep); sleep(RETRY_SLEEP); tcsetattr(fd, TCSAFLUSH, &stty_enter); #endif goto again; } #ifdef WITH_LIBPASSPHRASE passphrase_reenable_echo1(fd); #else tcsetattr(fd, TCSAFLUSH, &stty_original); #endif close(fd); } int main(int argc, char *argv[]) { int keep_env = 0; ARGBEGIN { case 'e': keep_env = 1; break; default: usage(); } ARGEND; if (!argc) usage(); check_password(); if (!keep_env) set_environ(); if (setgid(0)) { fprintf(stderr, "%s: setgid 0: %s\n", argv0, strerror(errno)); exit(EXIT_ERROR); } if (setuid(0)) { fprintf(stderr, "%s: setuid 0: %s\n", argv0, strerror(errno)); exit(EXIT_ERROR); } execvp(argv[0], argv); fprintf(stderr, "%s: execvpe %s: %s\n", argv0, argv[0], strerror(errno)); return errno == ENOENT ? EXIT_NOENT : EXIT_EXEC; }