|
|
|
@ -20,6 +20,8 @@ |
|
|
|
|
#define _DEFAULT_SOURCE |
|
|
|
|
|
|
|
|
|
#include <sys/types.h> |
|
|
|
|
#include <sys/wait.h> |
|
|
|
|
|
|
|
|
|
#include <stdio.h> |
|
|
|
|
#include <stdlib.h> |
|
|
|
|
#include <string.h> |
|
|
|
@ -28,24 +30,32 @@ |
|
|
|
|
#include <unistd.h> |
|
|
|
|
#include <limits.h> |
|
|
|
|
#include <grp.h> |
|
|
|
|
#include <security/pam_appl.h> |
|
|
|
|
#include <security/pam_misc.h> |
|
|
|
|
|
|
|
|
|
static void usage (void); |
|
|
|
|
static int perm_set (struct passwd *, struct group *); |
|
|
|
|
static int authenticate (const char *); |
|
|
|
|
static struct passwd* user_to_passwd (const char *); |
|
|
|
|
static struct group* group_to_grp (const char *); |
|
|
|
|
//static int execvpe(const char *, char *const *, char *const *);
|
|
|
|
|
#include <termios.h> |
|
|
|
|
#include <stdarg.h> |
|
|
|
|
|
|
|
|
|
#if !defined(_XOPEN_CRYPT) || _XOPEN_CRYPT == -1 |
|
|
|
|
#include <crypt.h> |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
// FIXME: misc_conv is a separate library, should stick to plain PAM or make
|
|
|
|
|
// our own pam module
|
|
|
|
|
static struct pam_conv conv = {misc_conv, NULL}; |
|
|
|
|
#if defined(__linux__) |
|
|
|
|
#include <shadow.h> |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#define MAX_HASH 1024 |
|
|
|
|
|
|
|
|
|
static void usage(void); |
|
|
|
|
static void die(const char *, ...); |
|
|
|
|
static int perm_set(struct passwd *, struct group *); |
|
|
|
|
static int authenticate(uid_t, char); |
|
|
|
|
static struct passwd* user_to_passwd(const char *); |
|
|
|
|
static struct group* group_to_grp(const char *); |
|
|
|
|
//static int execvpe(const char *, char *const *, char *const *);
|
|
|
|
|
|
|
|
|
|
extern char **environ; |
|
|
|
|
|
|
|
|
|
int main (int argc, char *argv[]) |
|
|
|
|
int main(int argc, char *argv[]) |
|
|
|
|
{ |
|
|
|
|
// TODO: modify signal handlers to ignore sigchld to not make zombies
|
|
|
|
|
char *t_usr = "root", *t_grp = NULL; |
|
|
|
|
struct passwd *t_pw; |
|
|
|
|
struct group *t_gr; |
|
|
|
@ -81,7 +91,6 @@ int main (int argc, char *argv[]) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Get user info */ |
|
|
|
|
const char *uname; |
|
|
|
|
char *shell; |
|
|
|
|
uid_t ruid = getuid(); |
|
|
|
|
struct passwd *my_pw = getpwuid(ruid); |
|
|
|
@ -89,19 +98,16 @@ int main (int argc, char *argv[]) |
|
|
|
|
fprintf(stderr, "getpwid: %s\n", strerror(errno)); |
|
|
|
|
return errno; |
|
|
|
|
} |
|
|
|
|
uname = my_pw->pw_name; |
|
|
|
|
|
|
|
|
|
/* Authenticate */ |
|
|
|
|
if (authenticate(uname) != PAM_SUCCESS) |
|
|
|
|
/* Authenticate, we will be root from now on */ |
|
|
|
|
if (authenticate(my_pw->pw_uid, 0)) |
|
|
|
|
exit(EXIT_FAILURE); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Get target user and group info */ |
|
|
|
|
t_pw = user_to_passwd(t_usr); |
|
|
|
|
if (!t_pw) { |
|
|
|
|
fprintf(stderr, "user_to_passwd: %s\n", strerror(errno)); |
|
|
|
|
return errno; |
|
|
|
|
} |
|
|
|
|
if (!t_pw) |
|
|
|
|
die("usr_to_passwd:"); |
|
|
|
|
t_gr = group_to_grp(t_grp); |
|
|
|
|
|
|
|
|
|
/* Get target user's shell */ |
|
|
|
@ -117,29 +123,21 @@ int main (int argc, char *argv[]) |
|
|
|
|
char **c_argv; |
|
|
|
|
if (c_argc) { |
|
|
|
|
c_argv = malloc(sizeof(char *) * (c_argc + 1)); |
|
|
|
|
if (!c_argv) { |
|
|
|
|
fprintf(stderr, "malloc: %s\n", strerror(errno)); |
|
|
|
|
exit(errno); |
|
|
|
|
} |
|
|
|
|
if (!c_argv) |
|
|
|
|
die("malloc:"); |
|
|
|
|
for (int i = 0; optind < argc; optind++, i++) { |
|
|
|
|
c_argv[i] = strdup(argv[optind]); |
|
|
|
|
if (!c_argv[i]) { |
|
|
|
|
fprintf(stderr, "strdup: %s\n", strerror(errno)); |
|
|
|
|
exit(errno); |
|
|
|
|
} |
|
|
|
|
if (!c_argv[i]) |
|
|
|
|
die("strdup:"); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
c_argc = 1; |
|
|
|
|
c_argv = malloc(sizeof(char *) * (c_argc + 1)); |
|
|
|
|
if (!c_argv) { |
|
|
|
|
fprintf(stderr, "malloc: %s\n", strerror(errno)); |
|
|
|
|
exit(errno); |
|
|
|
|
} |
|
|
|
|
if (!c_argv) |
|
|
|
|
die("malloc:"); |
|
|
|
|
c_argv[0] = strdup(shell); |
|
|
|
|
if (!c_argv[0]) { |
|
|
|
|
fprintf(stderr, "strdup: %s\n", strerror(errno)); |
|
|
|
|
exit(errno); |
|
|
|
|
} |
|
|
|
|
if (!c_argv[0]) |
|
|
|
|
die("strdup:"); |
|
|
|
|
} |
|
|
|
|
c_argv[c_argc] = NULL; |
|
|
|
|
|
|
|
|
@ -177,7 +175,7 @@ int main (int argc, char *argv[]) |
|
|
|
|
if (err == -1) { |
|
|
|
|
fprintf(stderr, "setenv: %s\n", strerror(errno)); |
|
|
|
|
goto fail_end; |
|
|
|
|
}
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (envflag) { |
|
|
|
@ -188,7 +186,7 @@ int main (int argc, char *argv[]) |
|
|
|
|
fprintf(stderr, "setenv: %s\n", strerror(errno)); |
|
|
|
|
goto fail_end; |
|
|
|
|
} |
|
|
|
|
}
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// do not override, we might be under more levels of 'us'
|
|
|
|
@ -204,7 +202,7 @@ int main (int argc, char *argv[]) |
|
|
|
|
/* Execute the command */ |
|
|
|
|
err = execvp(c_argv[0], c_argv); |
|
|
|
|
if (err == -1) |
|
|
|
|
fprintf(stderr, "execl: %s\n", strerror(errno)); |
|
|
|
|
fprintf(stderr, "execvp: %s\n", strerror(errno)); |
|
|
|
|
|
|
|
|
|
/* Cleanup and return */ |
|
|
|
|
fail_end: |
|
|
|
@ -215,7 +213,7 @@ int main (int argc, char *argv[]) |
|
|
|
|
return errno; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static inline void usage (void) |
|
|
|
|
static inline void usage(void) |
|
|
|
|
{ |
|
|
|
|
// TODO: planned options
|
|
|
|
|
// -a [program]: like sudo's askpass
|
|
|
|
@ -228,7 +226,7 @@ static inline void usage (void) |
|
|
|
|
printf("usage: us [-se] [-u user] [-g group] command [args]\n"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int perm_set (struct passwd *pw, struct group *gr) |
|
|
|
|
static int perm_set(struct passwd *pw, struct group *gr) |
|
|
|
|
{ |
|
|
|
|
if (!pw) { |
|
|
|
|
errno = EINVAL; |
|
|
|
@ -259,7 +257,7 @@ static int perm_set (struct passwd *pw, struct group *gr) |
|
|
|
|
printf("setregid failed\n"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (setreuid(uid, uid) == -1) { |
|
|
|
|
printf("setreuid failed\n"); |
|
|
|
|
return -1; |
|
|
|
@ -268,44 +266,124 @@ static int perm_set (struct passwd *pw, struct group *gr) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int authenticate (const char *uname) |
|
|
|
|
static int authenticate(uid_t uid, char ask) |
|
|
|
|
{ |
|
|
|
|
pam_handle_t *pamh; |
|
|
|
|
int pam_err, count = 0; |
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
pam_err = pam_start("User Switcher", uname, &conv, &pamh); |
|
|
|
|
if (pam_err != PAM_SUCCESS) { |
|
|
|
|
fprintf(stderr, "pam_start: %s\n", pam_strerror(pamh, pam_err)); |
|
|
|
|
return pam_err; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pam_err = pam_authenticate(pamh, 0); |
|
|
|
|
if (pam_err == PAM_SUCCESS) { |
|
|
|
|
pam_err = pam_acct_mgmt(pamh, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (pam_err != PAM_SUCCESS) { |
|
|
|
|
printf("Auth failed: %s\n", pam_strerror(pamh, pam_err)); |
|
|
|
|
pam_end(pamh, pam_err); |
|
|
|
|
// TODO: implement u2f compat
|
|
|
|
|
/* get the encrypted password */ |
|
|
|
|
struct passwd *pw = getpwuid(uid); |
|
|
|
|
char *hash_p, hash[MAX_HASH]; |
|
|
|
|
char *p = pw->pw_passwd; |
|
|
|
|
int tty_fd = STDOUT_FILENO; |
|
|
|
|
|
|
|
|
|
// but we have to be root
|
|
|
|
|
if (setuid(0) == -1) |
|
|
|
|
die("setreuid:"); |
|
|
|
|
if (!strcmp(p, "x") || *p == '*' || *p == '!') { |
|
|
|
|
#if defined(__linux__) |
|
|
|
|
// get exclusive access to shadow
|
|
|
|
|
if (lckpwdf() == -1) |
|
|
|
|
die("lckpwdf"); |
|
|
|
|
setspent(); |
|
|
|
|
struct spwd *sp = getspnam(pw->pw_name); |
|
|
|
|
if (sp == NULL) |
|
|
|
|
die("getspnam"); |
|
|
|
|
hash_p = sp->sp_pwdp; |
|
|
|
|
endspent(); |
|
|
|
|
ulckpwdf(); |
|
|
|
|
// if (setuid(uid) == -1)
|
|
|
|
|
// die("setreuid:");
|
|
|
|
|
#elif defined(__OpenBSD__) |
|
|
|
|
// TODO: openbsd has getpwuid_passwd
|
|
|
|
|
struct passwd *op = getpwuid_shadow(uid); |
|
|
|
|
if (!op) |
|
|
|
|
die("getpwuid_shadow:"); |
|
|
|
|
hash_p = op->pw_passwd; |
|
|
|
|
exit(EXIT_FAILURE); |
|
|
|
|
#endif |
|
|
|
|
} else { |
|
|
|
|
hash_p = pw->pw_passwd; |
|
|
|
|
} |
|
|
|
|
strncpy(hash, hash_p, MAX_HASH - 2); |
|
|
|
|
hash[MAX_HASH - 1] = '\0'; |
|
|
|
|
if (strlen(hash) >= MAX_HASH - 3) |
|
|
|
|
die("password hash too long :^)"); |
|
|
|
|
|
|
|
|
|
int fd = STDIN_FILENO; |
|
|
|
|
char *askpass = getenv("US_ASKPASS"); |
|
|
|
|
char pass[1024] = {0}; |
|
|
|
|
struct termios tio_before, tio_pass; |
|
|
|
|
if (ask && askpass) { |
|
|
|
|
pid_t pid; |
|
|
|
|
int pipefd[2]; |
|
|
|
|
if (pipe(pipefd) == -1) |
|
|
|
|
die("pipe:"); |
|
|
|
|
switch (pid = fork()) { |
|
|
|
|
case -1: |
|
|
|
|
die("fork:"); |
|
|
|
|
break; |
|
|
|
|
case 0: |
|
|
|
|
if (dup2(pipefd[1], STDOUT_FILENO) == -1) { |
|
|
|
|
// TODO: send signal to parent dup failed
|
|
|
|
|
die("dup2:"); |
|
|
|
|
} |
|
|
|
|
close(pipefd[0]); |
|
|
|
|
execl(askpass, "", (char *)NULL); |
|
|
|
|
die("execl:"); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
fd = pipefd[0]; |
|
|
|
|
close(pipefd[1]); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
count++; |
|
|
|
|
} while (pam_err != PAM_SUCCESS && count < 3); |
|
|
|
|
|
|
|
|
|
if (pam_err != PAM_SUCCESS) { |
|
|
|
|
fprintf(stderr, "better luck next time\n"); |
|
|
|
|
return pam_err; |
|
|
|
|
} else if (ask) { |
|
|
|
|
die("askpass: no valid askpass specified"); |
|
|
|
|
} else { |
|
|
|
|
printf("Password: "); |
|
|
|
|
fflush(stdout); |
|
|
|
|
if (tcgetattr(tty_fd, &tio_before) == -1) |
|
|
|
|
die("tcgetattr:"); |
|
|
|
|
tio_pass = tio_before; |
|
|
|
|
/* Do not echo and accept when enter is pressed */ |
|
|
|
|
tio_pass.c_lflag &= ~ECHO; |
|
|
|
|
tio_pass.c_lflag |= ICANON; |
|
|
|
|
if (tcsetattr(tty_fd, TCSANOW, &tio_pass) == -1) |
|
|
|
|
die("tcsetattr:"); |
|
|
|
|
} |
|
|
|
|
int r = read(fd, pass, 1023); |
|
|
|
|
if (!r || r == -1) { |
|
|
|
|
if (errno) |
|
|
|
|
fprintf(stderr, "read: %s\n", strerror(errno)); |
|
|
|
|
else |
|
|
|
|
printf("Password can't be zero length\n"); |
|
|
|
|
// we may have been interrupted, wait askpass before bailing
|
|
|
|
|
waitpid(-1, NULL, 0); |
|
|
|
|
exit(EXIT_FAILURE); |
|
|
|
|
} |
|
|
|
|
pass[1023] = '\0'; |
|
|
|
|
// remove \n
|
|
|
|
|
int l = strlen(pass); |
|
|
|
|
if (pass[l-1] == '\n') |
|
|
|
|
pass[--l] = '\0'; |
|
|
|
|
|
|
|
|
|
if (ask) { |
|
|
|
|
close(fd); |
|
|
|
|
} else { |
|
|
|
|
if (tcsetattr(tty_fd, TCSANOW, &tio_before) == -1) |
|
|
|
|
die("tcsetattr:"); |
|
|
|
|
} |
|
|
|
|
// FIXME: check again for the validity of the login for more security
|
|
|
|
|
// as in: https://docs.oracle.com/cd/E19120-01/open.solaris/819-2145/pam-20/index.html
|
|
|
|
|
// FIXME: ^C [SIGINT] will interrupt this call possibly causing a
|
|
|
|
|
// vulnerability
|
|
|
|
|
|
|
|
|
|
return pam_end(pamh, pam_err); |
|
|
|
|
char *enc = crypt(pass, hash); |
|
|
|
|
memset(pass, 0, 1024); |
|
|
|
|
if (strcmp(enc, hash)) { |
|
|
|
|
printf("Authentication failure\n"); |
|
|
|
|
setuid(uid); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
printf("\n"); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static struct passwd* user_to_passwd (const char *user) |
|
|
|
|
static struct passwd* user_to_passwd(const char *user) |
|
|
|
|
{ |
|
|
|
|
if (!user) { |
|
|
|
|
errno = EINVAL; |
|
|
|
@ -335,7 +413,7 @@ static struct passwd* user_to_passwd (const char *user) |
|
|
|
|
return pw; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static struct group* group_to_grp (const char *group) |
|
|
|
|
static struct group* group_to_grp(const char *group) |
|
|
|
|
{ |
|
|
|
|
if (!group) { |
|
|
|
|
errno = EINVAL; |
|
|
|
@ -363,3 +441,22 @@ static struct group* group_to_grp (const char *group) |
|
|
|
|
} |
|
|
|
|
return gr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void die(const char *fmt, ...) |
|
|
|
|
{ |
|
|
|
|
va_list ap; |
|
|
|
|
va_start(ap, fmt); |
|
|
|
|
|
|
|
|
|
vfprintf(stderr, fmt, ap); |
|
|
|
|
|
|
|
|
|
if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { |
|
|
|
|
fputc(' ', stderr); |
|
|
|
|
perror(NULL); |
|
|
|
|
} else { |
|
|
|
|
fputc('\n', stderr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
va_end(ap); |
|
|
|
|
exit(errno ? errno : EXIT_FAILURE); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|