micro-utils/src/su.c
2024-09-30 16:11:43 +03:00

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;
}