151 lines
2.6 KiB
C
151 lines
2.6 KiB
C
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <termios.h>
|
|
#include "pw_check.h"
|
|
#include "unused.h"
|
|
|
|
#define ECHOFLAGS (ECHO | ECHOE | ECHOK | ECHONL)
|
|
|
|
static char *c_flag = NULL;
|
|
static char *s_flag = NULL;
|
|
static char p_flag;
|
|
|
|
static int hide_input(int fd, int flag) {
|
|
struct termios term;
|
|
if (tcgetattr(fd, &term) < 0)
|
|
return 1;
|
|
|
|
if (flag)
|
|
term.c_lflag &= ~ECHOFLAGS;
|
|
|
|
else
|
|
term.c_lflag |= ECHOFLAGS;
|
|
|
|
if (tcsetattr(fd, TCSAFLUSH, &term) < 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void su(const struct passwd *pw) {
|
|
char *shell = s_flag;
|
|
if (s_flag == NULL)
|
|
shell = (pw->pw_shell[0] == '\0') ? "/bin/sh" : pw->pw_shell;
|
|
|
|
if (!p_flag) {
|
|
setenv("HOME", pw->pw_dir, 1);
|
|
setenv("SHELL", shell, 1);
|
|
setenv("USER", pw->pw_name, 1);
|
|
setenv("LOGNAME", pw->pw_name, 1);
|
|
setenv("PATH", "/bin", 1);
|
|
}
|
|
|
|
if (c_flag)
|
|
execlp(shell, shell, "-c", c_flag, NULL);
|
|
|
|
else
|
|
execlp(shell, shell, "-l", NULL);
|
|
}
|
|
|
|
static int password(const struct passwd *pw) {
|
|
static char psswd[512];
|
|
|
|
printf("Password: ");
|
|
fflush(stdout);
|
|
|
|
if (hide_input(STDIN_FILENO, 1)) {
|
|
fprintf(stderr, "\nsu: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
off_t ret = 0;
|
|
if ((ret = read(STDIN_FILENO, psswd, sizeof(psswd))) <= 0) {
|
|
fprintf(stderr, "\nsu: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
putchar('\n');
|
|
|
|
psswd[ret - 1] = '\0';
|
|
if (pw_check("su", pw, psswd)) {
|
|
memset(psswd, '\0', sizeof(psswd));
|
|
return 1;
|
|
}
|
|
|
|
memset(psswd, '\0', sizeof(psswd));
|
|
return 0;
|
|
}
|
|
|
|
static void endwin(int sig) {
|
|
UNUSED(sig);
|
|
|
|
hide_input(STDIN_FILENO, 0);
|
|
exit(1);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
int opt;
|
|
while ((opt = getopt(argc, argv, "c:s:p")) != -1) {
|
|
switch (opt) {
|
|
case 'c':
|
|
c_flag = optarg;
|
|
break;
|
|
|
|
case 's':
|
|
s_flag = optarg;
|
|
break;
|
|
|
|
case 'p':
|
|
p_flag = 1;
|
|
break;
|
|
|
|
default:
|
|
puts("su [cps] [user Default: root]\n\t-c CMD Command to pass to \'sh -c\'\n\t-s SHELL Shell to use instead of user's default\n\t-p Do not set new env");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
char *user = "root";
|
|
if (argv[0])
|
|
user = argv[0];
|
|
|
|
struct passwd *pw = getpwnam(user);
|
|
if (!pw) {
|
|
fprintf(stderr, "su: Incorrent username\n");
|
|
return 1;
|
|
}
|
|
|
|
signal(SIGINT, endwin);
|
|
|
|
if (getuid() != 0) {
|
|
int ret = password(pw);
|
|
hide_input(STDIN_FILENO, 0);
|
|
|
|
if (ret)
|
|
return 1;
|
|
}
|
|
|
|
/* Start */
|
|
if (setgid(pw->pw_gid) < 0) {
|
|
fprintf(stderr, "su: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
if (setuid(pw->pw_uid) < 0) {
|
|
fprintf(stderr, "su: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
su(pw);
|
|
return 0;
|
|
}
|