authenticate using /etc/shadow

completely remove the dependency on PAM, it is unnecessary in this sort of
simple program
master
Alessandro Mauri 3 years ago
parent 074ebfec6c
commit fb6c872dc7
  1. 5
      TODO
  2. 4
      makefile
  3. 2
      us.1
  4. 249
      us.c

@ -1,2 +1,3 @@
- Allow for pam-less (native) login, for use with distros wo pam (Alpine) and
BSDs
- Complete manpage
- settle on a fucking config file scheme and implement a parser already

@ -1,14 +1,14 @@
CC ?= gcc
CFLAGS = -Wall -pedantic --std=c99 -O2
DBG_CFLAGS = -Wall -Werror -pedantic --std=c99 -O0 -g
LDFLAGS = -lpam -lpam_misc
LDFLAGS = -lcrypt
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
us: us.c
dbg:
gcc ${LDFLAGS} ${DBG_CFLAGS} us.c -o us
${CC} ${LDFLAGS} ${DBG_CFLAGS} us.c -o us
install: us
mkdir -p ${DESTDIR}${PREFIX}/bin

@ -0,0 +1,2 @@
US-0.1

249
us.c

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

Loading…
Cancel
Save