/* See LICENSE file for copyright and license details. */ #include "common.h" static const char * get_expected(void) { struct passwd *pwd; struct spwd *shdw; const char *expected; 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); } return expected; } 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)); } } static int get_passphrase_fd(void) { const char *env; long int r; char *end; int fd, value; env = getenv("GASROOT_PASSFD"); if (!env) { fprintf(stderr, "%s: GASROOT_PASSFD environment variable not set\n", argv0); exit(EXIT_ERROR); } if (!isdigit(env[0])) { invalid: fprintf(stderr, "%s: GASROOT_PASSFD environment variable is improperly configured\n", argv0); exit(EXIT_ERROR); } errno = 0; r = strtol(env, &end, 10); if (errno || (*end && *end != ':') || r < 0) goto invalid; #if INT_MAX < LONG_MAX if (r > INT_MAX) goto invalid; #endif fd = (int)r; if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &value, &(socklen_t){(socklen_t)sizeof(value)})) { if (errno == EBADF) goto invalid; fprintf(stderr, "%s: getsockopt ${GASROOT_PASSFD} SOL_SOCKET SO_DOMAIN: %s\n", argv0, strerror(errno)); exit(EXIT_ERROR); } if (value != PF_UNIX) goto invalid; if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &value, &(socklen_t){(socklen_t)sizeof(value)})) { if (errno == EBADF) goto invalid; fprintf(stderr, "%s: getsockopt ${GASROOT_PASSFD} SOL_SOCKET SO_TYPE: %s\n", argv0, strerror(errno)); exit(EXIT_ERROR); } if (value != SOCK_STREAM) goto invalid; if (!*end) { unsetenv("GASROOT_PASSFD"); } else if (setenv("GASROOT_PASSFD", &end[1], 1)) { fprintf(stderr, "%s: setenv: %s\n", argv0, strerror(errno)); exit(EXIT_ERROR); } return fd; } static void senddata(int fd, const char *text, size_t len) { ssize_t r; while (len) { r = send(fd, text, len, 0); if (r < 0) { if (errno == EINTR) continue; fprintf(stderr, "%s: send %zu 0: %s\n", argv0, len, strerror(errno)); exit(EXIT_ERROR); } text = &text[r]; len -= (size_t)r; } } #define sendtext(FD, TEXT) senddata(FD, TEXT, sizeof(TEXT)) static void recvtext(int fd, char **bufp, size_t *sizep) { size_t len = 0; ssize_t r; do { if (len + 2048UL > *sizep) { *sizep = len + 2048UL; *bufp = realloc(*bufp, *sizep); if (!*bufp) { fprintf(stderr, "%s: realloc %zu: %s\n", argv0, *sizep, strerror(errno)); exit(EXIT_ERROR); } } again: r = recv(fd, &(*bufp)[len], *sizep - len, 0); if (r < 0) { if (errno == EINTR) goto again; fprintf(stderr, "%s: recv %zu 0: %s\n", argv0, *sizep - len, strerror(errno)); exit(EXIT_ERROR); } else if (!r) { exit(EXIT_ERROR); } if (memchr(&(*bufp)[len], 0, (size_t)r - 1U)) { fprintf(stderr, "%s: protocol error in communication with graphical interface\n", argv0); exit(EXIT_ERROR); } len += (size_t)r; } while ((*bufp)[len - 1U]); } int main(int argc, char *argv[]) { int keep_env = 0; int fd; char *passphrase = NULL; size_t passphrase_size = 0; const char *expected; ARGBEGIN { case 'e': keep_env = 1; break; default: usage(); } ARGEND; if (!argc) usage(); fd = get_passphrase_fd(); expected = get_expected(); sendtext(fd, "HELLO"); for (;;) { recvtext(fd, &passphrase, &passphrase_size); if (!strcmp(crypt(passphrase, expected), expected)) break; wipe(passphrase); #if RETRY_SLEEP > 0 sendtext(fd, "BLOCK"); sleep(RETRY_SLEEP); #endif sendtext(fd, "RETRY"); } wipe(passphrase); free(passphrase); sendtext(fd, "OK"); close(fd); 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; }