aboutsummaryrefslogtreecommitdiffstats
path: root/gasroot-setuid.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gasroot-setuid.c263
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;
+}