/* rd - privilege elevator * Copyright (C) 2022-2023 Olive * see LICENCE file for licensing information */ #include #include #include #include #include #include #include #include #ifdef PASS #include #include #include #ifdef SAVE #include #endif /* SAVE */ #ifdef TERM #include #include #endif /* TERM */ #if defined(SAVE) || defined(TERM) #include #include #endif /* SAVE || TERM */ #endif /* PASS */ static void die(const char *fmt, ...); #ifdef PASS static char *readpw(void); #ifdef TERM static int getctty(void); #endif /* TERM */ #endif /* PASS */ #ifdef VARS extern char **environ; #endif /* VARS */ static void die(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); if (fmt[strlen(fmt) - 1] != '\n') perror(NULL); exit(127); } #ifdef PASS #ifdef TERM static int getctty(void) { int fd; if ((fd = open("/proc/self/stat", O_RDONLY | O_NOFOLLOW)) == -1) die("rd: unable to open file: "); ssize_t ret, len = 0; char data[76] = { 0 }, *ptr; while ((ret = read(fd, data + len, 76 - len)) > 0 && (len += ret) < 76); if (ret == -1) die("rd: unable to read file: "); close(fd); for (ptr = &data[28]; *ptr != ')'; --ptr); for (ret = 0, ++ptr; ret < 4; ret += (*++ptr == ' ')); dev_t term; if ((term = strtoul(++ptr, NULL, 10)) == 0) die("rd: unable to find controlling terminal\n"); memcpy(data, major(term) == 4 ? "/dev/tty" : "/dev/pts/", 9); ret = minor(term), ptr = &data[9 - (major(term) == 4)], len = 0; do ptr[len++] = '0' + ret % 10; while ((ret /= 10) > 0); for (ret = 0; ret < len / 2; ++ret) #define SWAP(num1, num2) num1 ^= num2, num2 ^= num1, num1 ^= num2 SWAP(ptr[ret], ptr[len - ret - 1]); ptr[len] = '\0'; if ((fd = open(data, O_RDWR | O_NOCTTY)) == -1) die("rd: unable to open controlling terminal: "); return fd; } #endif /* TERM */ static char * readpw(void) { #ifdef TERM int in = getctty(); #define out in #else #define in STDIN_FILENO #define out STDOUT_FILENO #endif /* TERM */ struct termios term; if (tcgetattr(in, &term) == -1) die("rd: unable to get terminal attributes: "); term.c_lflag &= ~ECHO; if (tcsetattr(in, TCSAFLUSH, &term) == -1) die("rd: unable to set terminal attributes: "); write(out, "rd: enter password: ", 20); char *pass; ssize_t ret, len = 0; if ((pass = malloc(50)) == NULL) die("\nrd: unable to allocate memory: "); while ((ret = read(in, pass + len, 50)) == 50) if (pass[len + 49] == '\n') break; /* prevents empty read */ else if ((pass = realloc(pass, (len += 50) + 50)) == NULL) die("\nrd: unable to allocate memory: "); if (ret == -1) die("\nrd: unable to read from stdin: "); pass[len + ret - 1] = '\0'; term.c_lflag |= ECHO; if (tcsetattr(in, TCSAFLUSH, &term) == -1) die("\nrd: unable to set terminal attributes: "); write(out, "\n", 1); return pass; } #endif /* PASS */ int main(int argc, char **argv) { if (getuid() != 0 && geteuid() != 0) die("rd: insufficient privileges\n"); const char *user = "root"; #if defined(VARS) || defined (USER) int add = 0; #ifdef VARS int state = 0; if (argc > 1 && argv[1][0] == '-' && strchr(argv[1], 'c') != NULL) state = add = 1; #endif /* VARS */ #ifdef USER if (argc > 2 && argv[1][0] == '-' && strchr(argv[1], 'u') != NULL) add = 2, user = argv[2]; #endif /* USER */ argv = &argv[add]; #endif /* VARS || USER */ if (argv[1] == NULL) die("rd: no program given\n"); struct passwd *pw; if ((pw = getpwnam(user)) == NULL) die("rd: unable to get password file entry: "); #ifdef PASS #ifdef SAVE struct stat at; if (stat("/etc/rd", &at) == -1 || at.st_mtime + PTIME < time(NULL)) { #endif /* SAVE */ if (!strcmp(pw->pw_passwd, "x")) { struct spwd *sp; if ((sp = getspnam(user)) == NULL) die("rd: unable to get shadow file entry: "); pw->pw_passwd = sp->sp_pwdp; } if (pw->pw_passwd[0] == '!') die("rd: password is locked\n"); if (pw->pw_passwd[0] != '\0') { char *hash; if ((hash = crypt(readpw(), pw->pw_passwd)) == NULL) die("rd: unable to hash input: "); if (strcmp(pw->pw_passwd, hash)) die("rd: incorrect password\n"); } #ifdef SAVE } int file; if ((file = creat("/etc/rd", S_IWUSR)) != -1) write(file, "", 1), close(file); /* update file mod time */ #endif /* SAVE */ #endif /* PASS */ if (initgroups(user, pw->pw_gid) == -1) die("rd: unable to set groups: "); if (setgid(pw->pw_gid) == -1) die("rd: unable to set group id: "); if (setuid(pw->pw_uid) == -1) die("rd: unable to set user id: "); #ifdef VARS if (state) { const char *term = getenv("TERM"), *path = getenv("PATH"); environ = NULL; setenv("TERM", term, 1); setenv("PATH", path, 1); } #endif /* VARS */ setenv("HOME", pw->pw_dir, 1); setenv("SHELL", pw->pw_shell[0] != '\0' ? pw->pw_shell : "/bin/sh", 1); setenv("USER", pw->pw_name, 1); setenv("LOGNAME", pw->pw_name, 1); execvp(argv[1], &argv[1]); if (errno == ENOENT) die("rd: unable to run %s: no such command\n", argv[1]); die("rd: unable to run %s: ", argv[1]); }