From: Ilias Tsitsimpis <i.tsitsimpis@gmail.com>
Date: Tue, 13 Oct 2015 20:24:46 +0300
Subject: Use PAM for authentication

Patch slock to use PAM for authentication instead of querying the shadow
file. This means that slock can authenticate the user using some other
mechanism for login (i.e., NIS passwords, fingerprint readers, etc). In
addition, it doesn't require slock to has the effective user ID.

This patch has been written in a way that is as less intrusive as
possible so as to be easily applied to newer version.

Upstream is not interested in using PAM.

Bug-Debian: https://bugs.debian.org/739629
Forwarded: http://lists.suckless.org/dev/1011/6405.html
---
 slock/config.mk |  4 +--
 slock/slock.c   | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 90 insertions(+), 6 deletions(-)

diff --git a/slock/config.mk b/slock/config.mk
index 4fda095..26a437d 100644
--- a/slock/config.mk
+++ b/slock/config.mk
@@ -12,10 +12,10 @@ X11LIB = /usr/X11R6/lib
 
 # includes and libs
 INCS =
-LIBS = -lc -lcrypt -lX11 -lXext -lXrandr
+LIBS = -lc -lpam -lX11 -lXext -lXrandr
 
 # flags
-CPPFLAGS += -DVERSION=\"${VERSION}\" -DHAVE_SHADOW_H
+CPPFLAGS += -DVERSION=\"${VERSION}\" -DHAVE_PAM
 CFLAGS += -std=c99 -pedantic -Wall ${INCS} ${CPPFLAGS}
 LDFLAGS += ${LIBS}
 
diff --git a/slock/slock.c b/slock/slock.c
index de0cd72..3e15ecd 100644
--- a/slock/slock.c
+++ b/slock/slock.c
@@ -23,6 +23,18 @@
 #include <bsd_auth.h>
 #endif
 
+#ifdef HAVE_PAM
+#include <security/pam_appl.h>
+#define PAM_SERVICE_NAME "slock"
+
+static const char *pam_passwd;
+static pam_handle_t *pamh = NULL;
+
+static void pam_init(void);
+static void pam_destroy(void);
+static int pam_auth(const char *passwd);
+#endif
+
 enum {
 	INIT,
 	INPUT,
@@ -86,7 +98,7 @@ dontkillme(void)
 }
 #endif
 
-#ifndef HAVE_BSD_AUTH
+#if !defined(HAVE_BSD_AUTH) && !defined(HAVE_PAM)
 /* only run as root */
 static const char *
 getpw(void)
@@ -121,7 +133,7 @@ getpw(void)
 #endif
 
 static void
-#ifdef HAVE_BSD_AUTH
+#if defined(HAVE_BSD_AUTH) || defined(HAVE_PAM)
 readpw(Display *dpy)
 #else
 readpw(Display *dpy, const char *pws)
@@ -162,6 +174,8 @@ readpw(Display *dpy, const char *pws)
 				passwd[len] = 0;
 #ifdef HAVE_BSD_AUTH
 				running = !auth_userokay(getlogin(), NULL, "auth-xlock", passwd);
+#elif defined(HAVE_PAM)
+				running = !!pam_auth(passwd);
 #else
 				running = !!strcmp(crypt(passwd, pws), pws);
 #endif
@@ -290,7 +304,7 @@ usage(void)
 
 int
 main(int argc, char **argv) {
-#ifndef HAVE_BSD_AUTH
+#if !defined(HAVE_BSD_AUTH) && !defined(HAVE_PAM)
 	const char *pws;
 #endif
 	Display *dpy;
@@ -309,7 +323,7 @@ main(int argc, char **argv) {
 	if (!getpwuid(getuid()))
 		die("slock: no passwd entry for you\n");
 
-#ifndef HAVE_BSD_AUTH
+#if !defined(HAVE_BSD_AUTH) && !defined(HAVE_PAM)
 	pws = getpw();
 #endif
 
@@ -344,6 +358,10 @@ main(int argc, char **argv) {
 	/* Everything is now blank. Now wait for the correct password. */
 #ifdef HAVE_BSD_AUTH
 	readpw(dpy);
+#elif defined(HAVE_PAM)
+	pam_init();
+	readpw(dpy);
+	pam_destroy();
 #else
 	readpw(dpy, pws);
 #endif
@@ -357,3 +375,69 @@ main(int argc, char **argv) {
 
 	return 0;
 }
+
+
+#ifdef HAVE_PAM
+static int pam_conv(int num_msg, const struct pam_message **msg,
+	struct pam_response **resp, void *appdata_ptr)
+{
+	int i;
+
+	*resp = calloc(num_msg, sizeof(**resp));
+	if (*resp == NULL)
+		return PAM_BUF_ERR;
+
+	for (i = 0; i < num_msg; i++) {
+		/* return code is currently not used but should be set to zero */
+		resp[i]->resp_retcode = 0;
+
+		if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
+		    msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
+			resp[i]->resp = strdup(pam_passwd);
+	}
+
+	return PAM_SUCCESS;
+}
+
+static int pam_auth(const char *passwd)
+{
+	int pamret;
+
+	/* Authenticate user */
+	pam_passwd = passwd;
+	pamret = pam_authenticate(pamh, 0);
+
+	/* Check account status */
+	if (pamret == PAM_SUCCESS)
+		pamret = pam_acct_mgmt(pamh, 0);
+
+	return (pamret == PAM_SUCCESS) ? 0 : 1;
+}
+
+static void pam_init(void)
+{
+	int pamret;
+	struct passwd *pw;
+	struct pam_conv conv = {pam_conv, NULL};
+
+	pw = getpwuid(getuid());
+	if (!pw) {
+		if (errno)
+			die("slock: getpwuid: %s\n", strerror(errno));
+		else
+			die("slock: cannot retrieve username for user ID %d"
+				" from password file entry\n", getuid());
+	}
+
+	/* Start PAM */
+	pamret = pam_start(PAM_SERVICE_NAME, pw->pw_name, &conv, &pamh);
+	if (pamret != PAM_SUCCESS)
+		die("slock: pam_start() failed");
+}
+
+static void pam_destroy(void)
+{
+	/* Close PAM handle */
+	pam_end(pamh, PAM_SUCCESS);
+}
+#endif
