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. 237
      us.c

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

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

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

237
us.c

@ -20,6 +20,8 @@
#define _DEFAULT_SOURCE #define _DEFAULT_SOURCE
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -28,24 +30,32 @@
#include <unistd.h> #include <unistd.h>
#include <limits.h> #include <limits.h>
#include <grp.h> #include <grp.h>
#include <security/pam_appl.h> #include <termios.h>
#include <security/pam_misc.h> #include <stdarg.h>
static void usage (void); #if !defined(_XOPEN_CRYPT) || _XOPEN_CRYPT == -1
static int perm_set (struct passwd *, struct group *); #include <crypt.h>
static int authenticate (const char *); #endif
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 *);
// FIXME: misc_conv is a separate library, should stick to plain PAM or make #if defined(__linux__)
// our own pam module #include <shadow.h>
static struct pam_conv conv = {misc_conv, NULL}; #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; 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; char *t_usr = "root", *t_grp = NULL;
struct passwd *t_pw; struct passwd *t_pw;
struct group *t_gr; struct group *t_gr;
@ -81,7 +91,6 @@ int main (int argc, char *argv[])
} }
/* Get user info */ /* Get user info */
const char *uname;
char *shell; char *shell;
uid_t ruid = getuid(); uid_t ruid = getuid();
struct passwd *my_pw = getpwuid(ruid); struct passwd *my_pw = getpwuid(ruid);
@ -89,19 +98,16 @@ int main (int argc, char *argv[])
fprintf(stderr, "getpwid: %s\n", strerror(errno)); fprintf(stderr, "getpwid: %s\n", strerror(errno));
return errno; return errno;
} }
uname = my_pw->pw_name;
/* Authenticate */ /* Authenticate, we will be root from now on */
if (authenticate(uname) != PAM_SUCCESS) if (authenticate(my_pw->pw_uid, 0))
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
/* Get target user and group info */ /* Get target user and group info */
t_pw = user_to_passwd(t_usr); t_pw = user_to_passwd(t_usr);
if (!t_pw) { if (!t_pw)
fprintf(stderr, "user_to_passwd: %s\n", strerror(errno)); die("usr_to_passwd:");
return errno;
}
t_gr = group_to_grp(t_grp); t_gr = group_to_grp(t_grp);
/* Get target user's shell */ /* Get target user's shell */
@ -117,29 +123,21 @@ int main (int argc, char *argv[])
char **c_argv; char **c_argv;
if (c_argc) { if (c_argc) {
c_argv = malloc(sizeof(char *) * (c_argc + 1)); c_argv = malloc(sizeof(char *) * (c_argc + 1));
if (!c_argv) { if (!c_argv)
fprintf(stderr, "malloc: %s\n", strerror(errno)); die("malloc:");
exit(errno);
}
for (int i = 0; optind < argc; optind++, i++) { for (int i = 0; optind < argc; optind++, i++) {
c_argv[i] = strdup(argv[optind]); c_argv[i] = strdup(argv[optind]);
if (!c_argv[i]) { if (!c_argv[i])
fprintf(stderr, "strdup: %s\n", strerror(errno)); die("strdup:");
exit(errno);
}
} }
} else { } else {
c_argc = 1; c_argc = 1;
c_argv = malloc(sizeof(char *) * (c_argc + 1)); c_argv = malloc(sizeof(char *) * (c_argc + 1));
if (!c_argv) { if (!c_argv)
fprintf(stderr, "malloc: %s\n", strerror(errno)); die("malloc:");
exit(errno);
}
c_argv[0] = strdup(shell); c_argv[0] = strdup(shell);
if (!c_argv[0]) { if (!c_argv[0])
fprintf(stderr, "strdup: %s\n", strerror(errno)); die("strdup:");
exit(errno);
}
} }
c_argv[c_argc] = NULL; c_argv[c_argc] = NULL;
@ -204,7 +202,7 @@ int main (int argc, char *argv[])
/* Execute the command */ /* Execute the command */
err = execvp(c_argv[0], c_argv); err = execvp(c_argv[0], c_argv);
if (err == -1) if (err == -1)
fprintf(stderr, "execl: %s\n", strerror(errno)); fprintf(stderr, "execvp: %s\n", strerror(errno));
/* Cleanup and return */ /* Cleanup and return */
fail_end: fail_end:
@ -215,7 +213,7 @@ int main (int argc, char *argv[])
return errno; return errno;
} }
static inline void usage (void) static inline void usage(void)
{ {
// TODO: planned options // TODO: planned options
// -a [program]: like sudo's askpass // -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"); 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) { if (!pw) {
errno = EINVAL; errno = EINVAL;
@ -268,44 +266,124 @@ static int perm_set (struct passwd *pw, struct group *gr)
return 0; return 0;
} }
static int authenticate (const char *uname) static int authenticate(uid_t uid, char ask)
{ {
pam_handle_t *pamh; // TODO: implement u2f compat
int pam_err, count = 0; /* get the encrypted password */
struct passwd *pw = getpwuid(uid);
do { char *hash_p, hash[MAX_HASH];
pam_err = pam_start("User Switcher", uname, &conv, &pamh); char *p = pw->pw_passwd;
if (pam_err != PAM_SUCCESS) { int tty_fd = STDOUT_FILENO;
fprintf(stderr, "pam_start: %s\n", pam_strerror(pamh, pam_err));
return pam_err; // 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;
} }
} else if (ask) {
pam_err = pam_authenticate(pamh, 0); die("askpass: no valid askpass specified");
if (pam_err == PAM_SUCCESS) { } else {
pam_err = pam_acct_mgmt(pamh, 0); 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 (pam_err != PAM_SUCCESS) { if (ask) {
printf("Auth failed: %s\n", pam_strerror(pamh, pam_err)); close(fd);
pam_end(pamh, pam_err); } else {
if (tcsetattr(tty_fd, TCSANOW, &tio_before) == -1)
die("tcsetattr:");
} }
count++; char *enc = crypt(pass, hash);
} while (pam_err != PAM_SUCCESS && count < 3); memset(pass, 0, 1024);
if (strcmp(enc, hash)) {
if (pam_err != PAM_SUCCESS) { printf("Authentication failure\n");
fprintf(stderr, "better luck next time\n"); setuid(uid);
return pam_err; return -1;
} }
// FIXME: check again for the validity of the login for more security printf("\n");
// as in: https://docs.oracle.com/cd/E19120-01/open.solaris/819-2145/pam-20/index.html return 0;
// FIXME: ^C [SIGINT] will interrupt this call possibly causing a
// vulnerability
return pam_end(pamh, pam_err);
} }
static struct passwd* user_to_passwd (const char *user) static struct passwd* user_to_passwd(const char *user)
{ {
if (!user) { if (!user) {
errno = EINVAL; errno = EINVAL;
@ -335,7 +413,7 @@ static struct passwd* user_to_passwd (const char *user)
return pw; return pw;
} }
static struct group* group_to_grp (const char *group) static struct group* group_to_grp(const char *group)
{ {
if (!group) { if (!group) {
errno = EINVAL; errno = EINVAL;
@ -363,3 +441,22 @@ static struct group* group_to_grp (const char *group)
} }
return gr; 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