diff options
Diffstat (limited to '')
| -rw-r--r-- | gasroot-setuid.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/gasroot-setuid.c b/gasroot-setuid.c new file mode 100644 index 0000000..b699b32 --- /dev/null +++ b/gasroot-setuid.c @@ -0,0 +1,263 @@ +/* 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 <graphical interface> %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 <graphical interface> %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; +} |
