Index: pam-1.1.8/modules/pam_extrausers/Makefile.am
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/Makefile.am
@@ -0,0 +1,70 @@
+#
+# Copyright (c) 2005, 2006, 2009, 2011 Thorsten Kukuk <kukuk@suse.de>
+#
+
+CLEANFILES = *~
+MAINTAINERCLEANFILES = $(MANS)
+
+EXTRA_DIST = md5.c md5_crypt.c lckpwdf.-c $(MANS) \
+		tst-pam_extrausers $(XMLS)
+
+man_MANS = pam_extrausers.8
+XMLS = pam_extrausers.8.xml
+
+#TESTS = tst-pam_extrausers
+
+securelibdir = $(SECUREDIR)
+secureconfdir = $(SCONFIGDIR)
+
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
+	-DCHKPWD_HELPER=\"$(sbindir)/pam_extrausers_chkpwd\" \
+	-DUPDATE_HELPER=\"$(sbindir)/pam_extrausers_update\" \
+	$(NIS_CFLAGS)
+
+if HAVE_LIBSELINUX
+  AM_CFLAGS += -D"WITH_SELINUX"
+endif
+
+pam_extrausers_la_LDFLAGS = -no-undefined -avoid-version -module
+if HAVE_VERSIONING
+  pam_extrausers_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
+endif
+pam_extrausers_la_LIBADD = $(top_builddir)/libpam/libpam.la \
+	@LIBCRYPT@ @LIBSELINUX@ $(NIS_LIBS) \
+	../pam_securetty/tty_secure.lo
+
+securelib_LTLIBRARIES = pam_extrausers.la
+
+noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h passverify.h \
+		 pam_unix_static.h
+
+sbin_PROGRAMS = pam_extrausers_chkpwd pam_extrausers_update
+
+noinst_PROGRAMS = bigcrypt
+
+pam_extrausers_la_SOURCES = bigcrypt.c pam_unix_acct.c \
+	pam_unix_auth.c pam_unix_passwd.c pam_unix_sess.c support.c \
+	passverify.c yppasswd_xdr.c md5_good.c md5_broken.c obscure.c
+if STATIC_MODULES
+pam_extrausers_la_SOURCES += pam_unix_static.c
+endif
+
+bigcrypt_SOURCES = bigcrypt.c bigcrypt_main.c
+bigcrypt_CFLAGS = $(AM_CFLAGS)
+bigcrypt_LDADD = @LIBCRYPT@
+
+pam_extrausers_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c \
+	passverify.c
+pam_extrausers_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"pam_extrausers_chkpwd\"
+pam_extrausers_chkpwd_LDFLAGS = @PIE_LDFLAGS@
+pam_extrausers_chkpwd_LDADD = @LIBCRYPT@ @LIBSELINUX@ @LIBAUDIT@
+
+pam_extrausers_update_SOURCES = unix_update.c md5_good.c md5_broken.c bigcrypt.c \
+	passverify.c
+pam_extrausers_update_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"pam_extrausers_update\"
+pam_extrausers_update_LDFLAGS = @PIE_LDFLAGS@
+pam_extrausers_update_LDADD = @LIBCRYPT@ @LIBSELINUX@
+
+if ENABLE_REGENERATE_MAN
+-include $(top_srcdir)/Make.xml.rules
+endif
Index: pam-1.1.8/modules/pam_extrausers/README
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/README
@@ -0,0 +1,5 @@
+This is a simple fork of pam_unix, but with the following changes:
+
+ - The expected namespace changes
+ - References to /etc or /etc/secure are replaced with /var/lib/extrausers
+ - Unconditionally use our custom lckpwdf methods and namespace them
Index: pam-1.1.8/modules/pam_extrausers/bigcrypt.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/bigcrypt.c
@@ -0,0 +1,159 @@
+/*
+ * This function implements the "bigcrypt" algorithm specifically for
+ * Linux-PAM.
+ *
+ * This algorithm is algorithm 0 (default) shipped with the C2 secure
+ * implementation of Digital UNIX.
+ *
+ * Disclaimer: This work is not based on the source code to Digital
+ * UNIX, nor am I connected to Digital Equipment Corp, in any way
+ * other than as a customer. This code is based on published
+ * interfaces and reasonable guesswork.
+ *
+ * Description: The cleartext is divided into blocks of SEGMENT_SIZE=8
+ * characters or less. Each block is encrypted using the standard UNIX
+ * libc crypt function. The result of the encryption for one block
+ * provides the salt for the suceeding block.
+ *
+ * Restrictions: The buffer used to hold the encrypted result is
+ * statically allocated. (see MAX_PASS_LEN below).  This is necessary,
+ * as the returned pointer points to "static data that are overwritten
+ * by each call", (XPG3: XSI System Interface + Headers pg 109), and
+ * this is a drop in replacement for crypt();
+ *
+ * Andy Phillips <atp@mssl.ucl.ac.uk>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <security/_pam_macros.h>
+#ifdef HAVE_LIBXCRYPT
+#include <xcrypt.h>
+#elif defined(HAVE_CRYPT_H)
+#include <crypt.h>
+#endif
+
+#include "bigcrypt.h"
+
+/*
+ * Max cleartext password length in segments of 8 characters this
+ * function can deal with (16 segments of 8 chars= max 128 character
+ * password).
+ */
+
+#define MAX_PASS_LEN       16
+#define SEGMENT_SIZE       8
+#define SALT_SIZE          2
+#define KEYBUF_SIZE        ((MAX_PASS_LEN*SEGMENT_SIZE)+SALT_SIZE)
+#define ESEGMENT_SIZE      11
+#define CBUF_SIZE          ((MAX_PASS_LEN*ESEGMENT_SIZE)+SALT_SIZE+1)
+
+char *bigcrypt(const char *key, const char *salt)
+{
+	char *dec_c2_cryptbuf;
+#ifdef HAVE_CRYPT_R
+	struct crypt_data *cdata;
+#endif
+	unsigned long int keylen, n_seg, j;
+	char *cipher_ptr, *plaintext_ptr, *tmp_ptr, *salt_ptr;
+	char keybuf[KEYBUF_SIZE + 1];
+
+	D(("called with key='%s', salt='%s'.", key, salt));
+
+	/* reset arrays */
+	dec_c2_cryptbuf = malloc(CBUF_SIZE);
+	if (!dec_c2_cryptbuf) {
+		return NULL;
+	}
+#ifdef HAVE_CRYPT_R
+	cdata = malloc(sizeof(*cdata));
+	if(!cdata) {
+		free(dec_c2_cryptbuf);
+		return NULL;
+	}
+	cdata->initialized = 0;
+#endif
+	memset(keybuf, 0, KEYBUF_SIZE + 1);
+	memset(dec_c2_cryptbuf, 0, CBUF_SIZE);
+
+	/* fill KEYBUF_SIZE with key */
+	strncpy(keybuf, key, KEYBUF_SIZE);
+
+	/* deal with case that we are doing a password check for a
+	   conventially encrypted password: the salt will be
+	   SALT_SIZE+ESEGMENT_SIZE long. */
+	if (strlen(salt) == (SALT_SIZE + ESEGMENT_SIZE))
+		keybuf[SEGMENT_SIZE] = '\0';	/* terminate password early(?) */
+
+	keylen = strlen(keybuf);
+
+	if (!keylen) {
+		n_seg = 1;
+	} else {
+		/* work out how many segments */
+		n_seg = 1 + ((keylen - 1) / SEGMENT_SIZE);
+	}
+
+	if (n_seg > MAX_PASS_LEN)
+		n_seg = MAX_PASS_LEN;	/* truncate at max length */
+
+	/* set up some pointers */
+	cipher_ptr = dec_c2_cryptbuf;
+	plaintext_ptr = keybuf;
+
+	/* do the first block with supplied salt */
+#ifdef HAVE_CRYPT_R
+	tmp_ptr = crypt_r(plaintext_ptr, salt, cdata);	/* libc crypt_r() */
+#else
+	tmp_ptr = crypt(plaintext_ptr, salt);	/* libc crypt() */
+#endif
+	if (tmp_ptr == NULL) {
+		free(dec_c2_cryptbuf);
+		return NULL;
+	}
+	/* and place in the static area */
+	strncpy(cipher_ptr, tmp_ptr, 13);
+	cipher_ptr += ESEGMENT_SIZE + SALT_SIZE;
+	plaintext_ptr += SEGMENT_SIZE;	/* first block of SEGMENT_SIZE */
+
+	/* change the salt (1st 2 chars of previous block) - this was found
+	   by dowsing */
+
+	salt_ptr = cipher_ptr - ESEGMENT_SIZE;
+
+	/* so far this is identical to "return crypt(key, salt);", if
+	   there is more than one block encrypt them... */
+
+	if (n_seg > 1) {
+		for (j = 2; j <= n_seg; j++) {
+
+#ifdef HAVE_CRYPT_R
+			tmp_ptr = crypt_r(plaintext_ptr, salt_ptr, cdata);
+#else
+			tmp_ptr = crypt(plaintext_ptr, salt_ptr);
+#endif
+			if (tmp_ptr == NULL) {
+				_pam_overwrite(dec_c2_cryptbuf);
+				free(dec_c2_cryptbuf);
+				return NULL;
+			}
+
+			/* skip the salt for seg!=0 */
+			strncpy(cipher_ptr, (tmp_ptr + SALT_SIZE), ESEGMENT_SIZE);
+
+			cipher_ptr += ESEGMENT_SIZE;
+			plaintext_ptr += SEGMENT_SIZE;
+			salt_ptr = cipher_ptr - ESEGMENT_SIZE;
+		}
+	}
+	D(("key=|%s|, salt=|%s|\nbuf=|%s|\n", key, salt, dec_c2_cryptbuf));
+
+#ifdef HAVE_CRYPT_R
+	free(cdata);
+#endif
+
+	/* this is the <NUL> terminated encrypted password */
+	return dec_c2_cryptbuf;
+}
Index: pam-1.1.8/modules/pam_extrausers/bigcrypt.h
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/bigcrypt.h
@@ -0,0 +1 @@
+extern char *bigcrypt(const char *key, const char *salt);
Index: pam-1.1.8/modules/pam_extrausers/bigcrypt_main.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/bigcrypt_main.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "bigcrypt.h"
+
+int
+main(int argc, char **argv)
+{
+	if (argc < 3) {
+		fprintf(stderr, "Usage: %s password salt\n",
+			strchr(argv[0], '/') ?
+			(strchr(argv[0], '/') + 1) :
+			argv[0]);
+		return 0;
+	}
+	fprintf(stdout, "%s\n", bigcrypt(argv[1], argv[2]));
+	return 0;
+}
Index: pam-1.1.8/modules/pam_extrausers/lckpwdf.-c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/lckpwdf.-c
@@ -0,0 +1,142 @@
+/*
+ * This is a hack, but until libc and glibc both include this function
+ * by default (libc only includes it if nys is not being used, at the
+ * moment, and glibc doesn't appear to have it at all) we need to have
+ * it here, too.  :-(
+ *
+ * This should not become an official part of PAM.
+ *
+ * BEGIN_HACK
+ */
+
+/*
+ * lckpwdf.c -- prevent simultaneous updates of password files
+ *
+ * Before modifying any of the password files, call lckpwdf().  It may block
+ * for up to 15 seconds trying to get the lock.  Return value is 0 on success
+ * or -1 on failure.  When you are done, call ulckpwdf() to release the lock.
+ * The lock is also released automatically when the process exits.  Only one
+ * process at a time may hold the lock.
+ *
+ * These functions are supposed to be conformant with AT&T SVID Issue 3.
+ *
+ * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
+ * public domain.
+ */
+
+#include <fcntl.h>
+#include <signal.h>
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+#define LOCKFILE "/var/lib/extrausers/.pwd.lock"
+#define TIMEOUT 15
+
+static int lockfd = -1;
+
+static int set_close_on_exec(int fd)
+{
+	int flags = fcntl(fd, F_GETFD, 0);
+	if (flags == -1)
+		return -1;
+	flags |= FD_CLOEXEC;
+	return fcntl(fd, F_SETFD, flags);
+}
+
+static int do_lock(int fd)
+{
+	struct flock fl;
+
+	memset(&fl, 0, sizeof fl);
+	fl.l_type = F_WRLCK;
+	fl.l_whence = SEEK_SET;
+	return fcntl(fd, F_SETLKW, &fl);
+}
+
+static void alarm_catch(int sig)
+{
+/* does nothing, but fcntl F_SETLKW will fail with EINTR */
+}
+
+static int extrausers_lckpwdf(void)
+{
+	struct sigaction act, oldact;
+	sigset_t set, oldset;
+
+	if (lockfd != -1)
+		return -1;
+
+#ifdef WITH_SELINUX
+	if(is_selinux_enabled()>0)
+	{
+		lockfd = open(LOCKFILE, O_WRONLY);
+		if(lockfd == -1 && errno == ENOENT)
+		{
+			security_context_t create_context;
+			int rc;
+
+			if(getfilecon("/var/lib/extrausers/passwd", &create_context))
+				return -1;
+			rc = setfscreatecon(create_context);
+			freecon(create_context);
+			if(rc)
+				return -1;
+			lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
+			if(setfscreatecon(NULL))
+				return -1;
+		}
+	}
+	else
+#endif
+	lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
+	if (lockfd == -1)
+		return -1;
+	if (set_close_on_exec(lockfd) == -1)
+		goto cleanup_fd;
+
+	memset(&act, 0, sizeof act);
+	act.sa_handler = alarm_catch;
+	act.sa_flags = 0;
+	sigfillset(&act.sa_mask);
+	if (sigaction(SIGALRM, &act, &oldact) == -1)
+		goto cleanup_fd;
+
+	sigemptyset(&set);
+	sigaddset(&set, SIGALRM);
+	if (sigprocmask(SIG_UNBLOCK, &set, &oldset) == -1)
+		goto cleanup_sig;
+
+	alarm(TIMEOUT);
+	if (do_lock(lockfd) == -1)
+		goto cleanup_alarm;
+	alarm(0);
+	sigprocmask(SIG_SETMASK, &oldset, NULL);
+	sigaction(SIGALRM, &oldact, NULL);
+	return 0;
+
+      cleanup_alarm:
+	alarm(0);
+	sigprocmask(SIG_SETMASK, &oldset, NULL);
+      cleanup_sig:
+	sigaction(SIGALRM, &oldact, NULL);
+      cleanup_fd:
+	close(lockfd);
+	lockfd = -1;
+	return -1;
+}
+
+static int extrausers_ulckpwdf(void)
+{
+	unlink(LOCKFILE);
+	if (lockfd == -1)
+		return -1;
+
+	if (close(lockfd) == -1) {
+		lockfd = -1;
+		return -1;
+	}
+	lockfd = -1;
+	return 0;
+}
+/* END_HACK */
Index: pam-1.1.8/modules/pam_extrausers/md5.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/md5.c
@@ -0,0 +1,255 @@
+/*
+ * $Id$
+ *
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ */
+
+#include <string.h>
+#include "md5.h"
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len)	/* Nothing */
+#else
+static void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+	uint32 t;
+	do {
+		t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+		    ((unsigned) buf[1] << 8 | buf[0]);
+		*(uint32 *) buf = t;
+		buf += 4;
+	} while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Name(MD5Init)(struct MD5Context *ctx)
+{
+	ctx->buf[0] = 0x67452301U;
+	ctx->buf[1] = 0xefcdab89U;
+	ctx->buf[2] = 0x98badcfeU;
+	ctx->buf[3] = 0x10325476U;
+
+	ctx->bits[0] = 0;
+	ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Name(MD5Update)(struct MD5Context *ctx, unsigned const char *buf, unsigned len)
+{
+	uint32 t;
+
+	/* Update bitcount */
+
+	t = ctx->bits[0];
+	if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+		ctx->bits[1]++;	/* Carry from low to high */
+	ctx->bits[1] += len >> 29;
+
+	t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+	/* Handle any leading odd-sized chunks */
+
+	if (t) {
+		unsigned char *p = (unsigned char *) ctx->in + t;
+
+		t = 64 - t;
+		if (len < t) {
+			memcpy(p, buf, len);
+			return;
+		}
+		memcpy(p, buf, t);
+		byteReverse(ctx->in, 16);
+		MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+		buf += t;
+		len -= t;
+	}
+	/* Process data in 64-byte chunks */
+
+	while (len >= 64) {
+		memcpy(ctx->in, buf, 64);
+		byteReverse(ctx->in, 16);
+		MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+		buf += 64;
+		len -= 64;
+	}
+
+	/* Handle any remaining bytes of data. */
+
+	memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx)
+{
+	unsigned count;
+	unsigned char *p;
+
+	/* Compute number of bytes mod 64 */
+	count = (ctx->bits[0] >> 3) & 0x3F;
+
+	/* Set the first char of padding to 0x80.  This is safe since there is
+	   always at least one byte free */
+	p = ctx->in + count;
+	*p++ = 0x80;
+
+	/* Bytes of padding needed to make 64 bytes */
+	count = 64 - 1 - count;
+
+	/* Pad out to 56 mod 64 */
+	if (count < 8) {
+		/* Two lots of padding:  Pad the first block to 64 bytes */
+		memset(p, 0, count);
+		byteReverse(ctx->in, 16);
+		MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+
+		/* Now fill the next block with 56 bytes */
+		memset(ctx->in, 0, 56);
+	} else {
+		/* Pad block to 56 bytes */
+		memset(p, 0, count - 8);
+	}
+	byteReverse(ctx->in, 14);
+
+	/* Append length in bits and transform */
+	memcpy((uint32 *)ctx->in + 14, ctx->bits, 2*sizeof(uint32));
+
+	MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+	byteReverse((unsigned char *) ctx->buf, 4);
+	memcpy(digest, ctx->buf, 16);
+	memset(ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Name(MD5Transform)(uint32 buf[4], uint32 const in[16])
+{
+	register uint32 a, b, c, d;
+
+	a = buf[0];
+	b = buf[1];
+	c = buf[2];
+	d = buf[3];
+
+	MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478U, 7);
+	MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756U, 12);
+	MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbU, 17);
+	MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22);
+	MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU, 7);
+	MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aU, 12);
+	MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613U, 17);
+	MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501U, 22);
+	MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8U, 7);
+	MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afU, 12);
+	MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17);
+	MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22);
+	MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U, 7);
+	MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12);
+	MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17);
+	MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22);
+
+	MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562U, 5);
+	MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340U, 9);
+	MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14);
+	MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20);
+	MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dU, 5);
+	MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U, 9);
+	MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14);
+	MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20);
+	MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6U, 5);
+	MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U, 9);
+	MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87U, 14);
+	MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edU, 20);
+	MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U, 5);
+	MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9);
+	MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9U, 14);
+	MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20);
+
+	MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942U, 4);
+	MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681U, 11);
+	MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16);
+	MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23);
+	MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44U, 4);
+	MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11);
+	MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16);
+	MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23);
+	MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U, 4);
+	MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faU, 11);
+	MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085U, 16);
+	MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05U, 23);
+	MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039U, 4);
+	MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11);
+	MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16);
+	MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665U, 23);
+
+	MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244U, 6);
+	MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97U, 10);
+	MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15);
+	MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039U, 21);
+	MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U, 6);
+	MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10);
+	MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15);
+	MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1U, 21);
+	MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6);
+	MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10);
+	MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314U, 15);
+	MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21);
+	MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82U, 6);
+	MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10);
+	MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15);
+	MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391U, 21);
+
+	buf[0] += a;
+	buf[1] += b;
+	buf[2] += c;
+	buf[3] += d;
+}
+
+#endif
Index: pam-1.1.8/modules/pam_extrausers/md5.h
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/md5.h
@@ -0,0 +1,31 @@
+
+#ifndef MD5_H
+#define MD5_H
+
+typedef unsigned int uint32;
+
+struct MD5Context {
+	uint32 buf[4];
+	uint32 bits[2];
+	unsigned char in[64];
+};
+
+void GoodMD5Init(struct MD5Context *);
+void GoodMD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void GoodMD5Final(unsigned char digest[16], struct MD5Context *);
+void GoodMD5Transform(uint32 buf[4], uint32 const in[16]);
+void BrokenMD5Init(struct MD5Context *);
+void BrokenMD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void BrokenMD5Final(unsigned char digest[16], struct MD5Context *);
+void BrokenMD5Transform(uint32 buf[4], uint32 const in[16]);
+
+char *Goodcrypt_md5(const char *pw, const char *salt);
+char *Brokencrypt_md5(const char *pw, const char *salt);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+
+typedef struct MD5Context MD5_CTX;
+
+#endif				/* MD5_H */
Index: pam-1.1.8/modules/pam_extrausers/md5_broken.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/md5_broken.c
@@ -0,0 +1,4 @@
+#define MD5Name(x) Broken##x
+
+#include "md5.c"
+#include "md5_crypt.c"
Index: pam-1.1.8/modules/pam_extrausers/md5_crypt.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/md5_crypt.c
@@ -0,0 +1,154 @@
+/*
+ * $Id$
+ *
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * Origin: Id: crypt.c,v 1.3 1995/05/30 05:42:22 rgrimes Exp
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include "md5.h"
+
+static unsigned char itoa64[] =	/* 0 ... 63 => ascii - 64 */
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void to64(char *s, unsigned long v, int n)
+{
+	while (--n >= 0) {
+		*s++ = itoa64[v & 0x3f];
+		v >>= 6;
+	}
+}
+
+/*
+ * UNIX password
+ *
+ * Use MD5 for what it is best at...
+ */
+
+char *MD5Name(crypt_md5)(const char *pw, const char *salt)
+{
+	const char *magic = "$1$";
+	/* This string is magic for this algorithm.  Having
+	 * it this way, we can get get better later on */
+	char *passwd, *p;
+	const char *sp, *ep;
+	unsigned char final[16];
+	int sl, pl, i, j;
+	MD5_CTX ctx, ctx1;
+	unsigned long l;
+
+	/* Refine the Salt first */
+	sp = salt;
+
+	/* TODO: now that we're using malloc'ed memory, get rid of the
+	   strange constant buffer size. */
+	passwd = malloc(120);
+
+	/* If it starts with the magic string, then skip that */
+	if (!strncmp(sp, magic, strlen(magic)))
+		sp += strlen(magic);
+
+	/* It stops at the first '$', max 8 chars */
+	for (ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++)
+		continue;
+
+	/* get the length of the true salt */
+	sl = ep - sp;
+
+	MD5Name(MD5Init)(&ctx);
+
+	/* The password first, since that is what is most unknown */
+	MD5Name(MD5Update)(&ctx,(unsigned const char *)pw,strlen(pw));
+
+	/* Then our magic string */
+	MD5Name(MD5Update)(&ctx,(unsigned const char *)magic,strlen(magic));
+
+	/* Then the raw salt */
+	MD5Name(MD5Update)(&ctx,(unsigned const char *)sp,sl);
+
+	/* Then just as many characters of the MD5(pw,salt,pw) */
+	MD5Name(MD5Init)(&ctx1);
+	MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+	MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
+	MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+	MD5Name(MD5Final)(final,&ctx1);
+	for (pl = strlen(pw); pl > 0; pl -= 16)
+		MD5Name(MD5Update)(&ctx,(unsigned const char *)final,pl>16 ? 16 : pl);
+
+	/* Don't leave anything around in vm they could use. */
+	memset(final, 0, sizeof final);
+
+	/* Then something really weird... */
+	for (j = 0, i = strlen(pw); i; i >>= 1)
+		if (i & 1)
+			MD5Name(MD5Update)(&ctx, (unsigned const char *)final+j, 1);
+		else
+			MD5Name(MD5Update)(&ctx, (unsigned const char *)pw+j, 1);
+
+	/* Now make the output string */
+	strcpy(passwd, magic);
+	strncat(passwd, sp, sl);
+	strcat(passwd, "$");
+
+	MD5Name(MD5Final)(final,&ctx);
+
+	/*
+	 * and now, just to make sure things don't run too fast
+	 * On a 60 Mhz Pentium this takes 34 msec, so you would
+	 * need 30 seconds to build a 1000 entry dictionary...
+	 */
+	for (i = 0; i < 1000; i++) {
+		MD5Name(MD5Init)(&ctx1);
+		if (i & 1)
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+		else
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
+
+		if (i % 3)
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
+
+		if (i % 7)
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+
+		if (i & 1)
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
+		else
+			MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+		MD5Name(MD5Final)(final,&ctx1);
+	}
+
+	p = passwd + strlen(passwd);
+
+	l = (final[0] << 16) | (final[6] << 8) | final[12];
+	to64(p, l, 4);
+	p += 4;
+	l = (final[1] << 16) | (final[7] << 8) | final[13];
+	to64(p, l, 4);
+	p += 4;
+	l = (final[2] << 16) | (final[8] << 8) | final[14];
+	to64(p, l, 4);
+	p += 4;
+	l = (final[3] << 16) | (final[9] << 8) | final[15];
+	to64(p, l, 4);
+	p += 4;
+	l = (final[4] << 16) | (final[10] << 8) | final[5];
+	to64(p, l, 4);
+	p += 4;
+	l = final[11];
+	to64(p, l, 2);
+	p += 2;
+	*p = '\0';
+
+	/* Don't leave anything around in vm they could use. */
+	memset(final, 0, sizeof final);
+
+	return passwd;
+}
Index: pam-1.1.8/modules/pam_extrausers/md5_good.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/md5_good.c
@@ -0,0 +1,5 @@
+#define HIGHFIRST
+#define MD5Name(x) Good##x
+
+#include "md5.c"
+#include "md5_crypt.c"
Index: pam-1.1.8/modules/pam_extrausers/obscure.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/obscure.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright 1989 - 1994, Julianne Frances Haugh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+
+#include "support.h"
+
+/* can't be a palindrome - like `R A D A R' or `M A D A M' */
+static int palindrome(const char *old, const char *new) {
+	int	i, j;
+
+	i = strlen (new);
+
+	for (j = 0;j < i;j++)
+		if (new[i - j - 1] != new[j])
+			return 0;
+
+	return 1;
+}
+
+/* more than half of the characters are different ones. */
+static int similar(const char *old, const char *new) {
+	int i, j;
+
+	/*
+	 * XXX - sometimes this fails when changing from a simple password
+	 * to a really long one (MD5).  For now, I just return success if
+	 * the new password is long enough.  Please feel free to suggest
+	 * something better...  --marekm
+	 */
+	if (strlen(new) >= 8)
+		return 0;
+
+	for (i = j = 0; new[i] && old[i]; i++)
+		if (strchr(new, old[i]))
+			j++;
+
+	if (i >= j * 2)
+		return 0;
+
+	return 1;
+}
+
+/* a nice mix of characters. */
+static int simple(const char *old, const char *new) {
+	int	digits = 0;
+	int	uppers = 0;
+	int	lowers = 0;
+	int	others = 0;
+	int	size;
+	int	i;
+
+	for (i = 0;new[i];i++) {
+		if (isdigit (new[i]))
+			digits++;
+		else if (isupper (new[i]))
+			uppers++;
+		else if (islower (new[i]))
+			lowers++;
+		else
+			others++;
+	}
+
+	/*
+	 * The scam is this - a password of only one character type
+	 * must be 8 letters long.  Two types, 7, and so on.
+	 */
+
+	size = 9;
+	if (digits) size--;
+	if (uppers) size--;
+	if (lowers) size--;
+	if (others) size--;
+
+	if (size <= i)
+		return 0;
+
+	return 1;
+}
+
+static char *str_lower(char *string) {
+	char *cp;
+
+	for (cp = string; *cp; cp++)
+		*cp = tolower(*cp);
+	return string;
+}
+
+static const char * password_check(const char *old, const char *new,
+				   const struct passwd *pwdp) {
+	const char *msg = NULL;
+	char *oldmono, *newmono, *wrapped;
+
+	if (strcmp(new, old) == 0)
+		return _("Bad: new password must be different than the old one");
+
+	newmono = str_lower(strdup(new));
+	oldmono = str_lower(strdup(old));
+	wrapped = (char *)malloc(strlen(oldmono) * 2 + 1);
+	strcpy (wrapped, oldmono);
+	strcat (wrapped, oldmono);
+
+	if (palindrome(oldmono, newmono)) {
+		msg = _("Bad: new password cannot be a palindrome");
+	} else if (strcmp(oldmono, newmono) == 0) {
+		msg = _("Bad: new and old password must differ by more than just case");
+	} else if (similar(oldmono, newmono)) {
+		msg = _("Bad: new and old password are too similar");
+	} else if (simple(old, new)) {
+		msg = _("Bad: new password is too simple");
+	} else if (strstr(wrapped, newmono)) {
+		msg = _("Bad: new password is just a wrapped version of the old one");
+	}
+
+	_pam_delete(newmono);
+	_pam_delete(oldmono);
+	_pam_delete(wrapped);
+
+	return msg;
+}
+
+const char *obscure_msg(const char *old, const char *new,
+			       const struct passwd *pwdp, unsigned int ctrl) {
+	int oldlen, newlen;
+	char *new1, *old1;
+	const char *msg;
+
+	if (old == NULL)
+		return NULL; /* no check if old is NULL */
+
+	oldlen = strlen(old);
+	newlen = strlen(new);
+
+	/* Remaining checks are optional. */
+	if (off(UNIX_OBSCURE_CHECKS,ctrl))
+		return NULL;
+
+	if ((msg = password_check(old, new, pwdp)) != NULL)
+		return msg;
+
+	/* The traditional crypt() truncates passwords to 8 chars.  It is
+	   possible to circumvent the above checks by choosing an easy
+	   8-char password and adding some random characters to it...
+	   Example: "password$%^&*123".  So check it again, this time
+	   truncated to the maximum length.  Idea from npasswd.  --marekm */
+
+	if (!UNIX_DES_CRYPT(ctrl))
+		return NULL;  /* unlimited password length */
+
+	if (oldlen <= 8 && newlen <= 8)
+		return NULL;
+
+	new1 = strndup(new,8);
+	old1 = strndup(old,8);
+
+	msg = password_check(old1, new1, pwdp);
+
+	_pam_delete(new1);
+	_pam_delete(old1);
+
+	return msg;
+}
Index: pam-1.1.8/modules/pam_extrausers/pam_unix_acct.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/pam_unix_acct.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright Elliot Lee, 1996.  All rights reserved.
+ * Copyright Jan R\EAkorajski, 1999.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <time.h>		/* for time() */
+#include <errno.h>
+#include <sys/wait.h>
+
+#include <security/_pam_macros.h>
+
+/* indicate that the following groups are defined */
+
+#ifdef PAM_STATIC
+# include "pam_unix_static.h"
+#else
+# define PAM_SM_ACCOUNT
+#endif
+
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+#include <security/pam_modutil.h>
+
+#include "support.h"
+#include "passverify.h"
+
+int _unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl,
+	const char *user, int *daysleft)
+{
+  int retval=0, child, fds[2];
+  struct sigaction newsa, oldsa;
+  D(("running verify_binary"));
+
+  /* create a pipe for the messages */
+  if (pipe(fds) != 0) {
+    D(("could not make pipe"));
+    pam_syslog(pamh, LOG_ERR, "Could not make pipe: %m");
+    return PAM_AUTH_ERR;
+  }
+  D(("called."));
+
+  if (off(UNIX_NOREAP, ctrl)) {
+    /*
+     * This code arranges that the demise of the child does not cause
+     * the application to receive a signal it is not expecting - which
+     * may kill the application or worse.
+     *
+     * The "noreap" module argument is provided so that the admin can
+     * override this behavior.
+     */
+     memset(&newsa, '\0', sizeof(newsa));
+     newsa.sa_handler = SIG_DFL;
+     sigaction(SIGCHLD, &newsa, &oldsa);
+  }
+
+  /* fork */
+  child = fork();
+  if (child == 0) {
+    int i=0;
+    struct rlimit rlim;
+    static char *envp[] = { NULL };
+    char *args[] = { NULL, NULL, NULL, NULL };
+
+    /* reopen stdout as pipe */
+    dup2(fds[1], STDOUT_FILENO);
+
+    /* XXX - should really tidy up PAM here too */
+
+    if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
+      if (rlim.rlim_max >= MAX_FD_NO)
+        rlim.rlim_max = MAX_FD_NO;
+      for (i=0; i < (int)rlim.rlim_max; i++) {
+	if (i != STDOUT_FILENO) {
+	  close(i);
+	}
+      }
+    }
+
+    if (geteuid() == 0) {
+      /* must set the real uid to 0 so the helper will not error
+         out if pam is called from setuid binary (su, sudo...) */
+      if (setuid(0) == -1) {
+          pam_syslog(pamh, LOG_ERR, "setuid failed: %m");
+          printf("-1\n");
+          fflush(stdout);
+          _exit(PAM_AUTHINFO_UNAVAIL);
+      }
+    }
+
+    /* exec binary helper */
+    args[0] = x_strdup(CHKPWD_HELPER);
+    args[1] = x_strdup(user);
+    args[2] = x_strdup("chkexpiry");
+
+    execve(CHKPWD_HELPER, args, envp);
+
+    pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %m");
+    /* should not get here: exit with error */
+    D(("helper binary is not available"));
+    printf("-1\n");
+    fflush(stdout);
+    _exit(PAM_AUTHINFO_UNAVAIL);
+  } else {
+    close(fds[1]);
+    if (child > 0) {
+      char buf[32];
+      int rc=0;
+      /* wait for helper to complete: */
+      while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
+      if (rc<0) {
+	pam_syslog(pamh, LOG_ERR, "pam_extrausers_chkpwd waitpid returned %d: %m", rc);
+	retval = PAM_AUTH_ERR;
+      } else if (!WIFEXITED(retval)) {
+        pam_syslog(pamh, LOG_ERR, "pam_extrausers_chkpwd abnormal exit: %d", retval);
+        retval = PAM_AUTH_ERR;
+      } else {
+	retval = WEXITSTATUS(retval);
+        rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
+	if(rc > 0) {
+	      buf[rc] = '\0';
+	      if (sscanf(buf,"%d", daysleft) != 1 )
+	        retval = PAM_AUTH_ERR;
+	    }
+	else {
+	    pam_syslog(pamh, LOG_ERR, "read pam_extrausers_chkpwd output error %d: %m", rc);
+	    retval = PAM_AUTH_ERR;
+	  }
+      }
+    } else {
+      pam_syslog(pamh, LOG_ERR, "Fork failed: %m");
+      D(("fork failed"));
+      retval = PAM_AUTH_ERR;
+    }
+    close(fds[0]);
+  }
+
+  if (off(UNIX_NOREAP, ctrl)) {
+        sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
+  }
+
+  D(("Returning %d",retval));
+  return retval;
+}
+
+/*
+ * PAM framework looks for this entry-point to pass control to the
+ * account management module.
+ */
+
+int
+pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	unsigned int ctrl;
+	const void *void_uname;
+	const char *uname;
+	int retval, daysleft;
+	struct spwd *spent;
+	struct passwd *pwent;
+	char buf[256];
+
+	D(("called."));
+
+	ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv);
+
+	retval = pam_get_item(pamh, PAM_USER, &void_uname);
+	uname = void_uname;
+	D(("user = `%s'", uname));
+	if (retval != PAM_SUCCESS || uname == NULL) {
+		pam_syslog(pamh, LOG_ALERT,
+			 "could not identify user (from uid=%lu)",
+			 (unsigned long int)getuid());
+		return PAM_USER_UNKNOWN;
+	}
+
+	retval = get_account_info(pamh, uname, &pwent, &spent);
+	if (retval == PAM_USER_UNKNOWN) {
+		pam_syslog(pamh, LOG_ALERT,
+			 "could not identify user (from getpwnam(%s))",
+			 uname);
+		return retval;
+	}
+
+	if (retval == PAM_SUCCESS && spent == NULL)
+		return PAM_SUCCESS;
+
+	if (retval == PAM_UNIX_RUN_HELPER) {
+		retval = _unix_run_verify_binary(pamh, ctrl, uname, &daysleft);
+		if (retval == PAM_AUTHINFO_UNAVAIL &&
+			on(UNIX_BROKEN_SHADOW, ctrl))
+			return PAM_SUCCESS;
+	} else if (retval != PAM_SUCCESS) {
+		if (on(UNIX_BROKEN_SHADOW,ctrl))
+			return PAM_SUCCESS;
+		else
+			return retval;
+	} else
+		retval = check_shadow_expiry(pamh, spent, &daysleft);
+
+	switch (retval) {
+	case PAM_ACCT_EXPIRED:
+		pam_syslog(pamh, LOG_NOTICE,
+			"account %s has expired (account expired)",
+			uname);
+		_make_remark(pamh, ctrl, PAM_ERROR_MSG,
+			_("Your account has expired; please contact your system administrator"));
+		break;
+	case PAM_NEW_AUTHTOK_REQD:
+		if (daysleft == 0) {
+			pam_syslog(pamh, LOG_NOTICE,
+				"expired password for user %s (root enforced)",
+				uname);
+			_make_remark(pamh, ctrl, PAM_ERROR_MSG,
+				_("You are required to change your password immediately (root enforced)"));
+		} else {
+			pam_syslog(pamh, LOG_DEBUG,
+				"expired password for user %s (password aged)",
+				uname);
+			_make_remark(pamh, ctrl, PAM_ERROR_MSG,
+				_("You are required to change your password immediately (password aged)"));
+		}
+		break;
+	case PAM_AUTHTOK_EXPIRED:
+		pam_syslog(pamh, LOG_NOTICE,
+			"account %s has expired (failed to change password)",
+			uname);
+		_make_remark(pamh, ctrl, PAM_ERROR_MSG,
+			_("Your account has expired; please contact your system administrator"));
+		break;
+	case PAM_AUTHTOK_ERR:
+		retval = PAM_SUCCESS;
+		/* fallthrough */
+	case PAM_SUCCESS:
+		if (daysleft >= 0) {
+			pam_syslog(pamh, LOG_DEBUG,
+				"password for user %s will expire in %d days",
+				uname, daysleft);
+#if defined HAVE_DNGETTEXT && defined ENABLE_NLS
+			snprintf (buf, sizeof (buf),
+				dngettext(PACKAGE,
+				  "Warning: your password will expire in %d day",
+				  "Warning: your password will expire in %d days",
+				  daysleft),
+				daysleft);
+#else
+			if (daysleft == 1)
+			    snprintf(buf, sizeof (buf),
+				_("Warning: your password will expire in %d day"),
+				daysleft);
+			else
+			    snprintf(buf, sizeof (buf),
+			    /* TRANSLATORS: only used if dngettext is not supported */
+				_("Warning: your password will expire in %d days"),
+				daysleft);
+#endif
+			_make_remark(pamh, ctrl, PAM_TEXT_INFO, buf);
+		}
+	}
+
+	D(("all done"));
+
+	return retval;
+}
Index: pam-1.1.8/modules/pam_extrausers/pam_unix_auth.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/pam_unix_auth.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright Alexander O. Yuriev, 1996.  All rights reserved.
+ * NIS+ support by Thorsten Kukuk <kukuk@weber.uni-paderborn.de>
+ * Copyright Jan R\EAkorajski, 1999.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+/* indicate the following groups are defined */
+
+#ifdef PAM_STATIC
+# include "pam_unix_static.h"
+#else
+# define PAM_SM_AUTH
+#endif
+
+#define _PAM_EXTERN_FUNCTIONS
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+
+#include "support.h"
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * authentication module.
+ */
+
+/* Fun starts here :)
+
+ * pam_sm_authenticate() performs UNIX/shadow authentication
+ *
+ *      First, if shadow support is available, attempt to perform
+ *      authentication using shadow passwords. If shadow is not
+ *      available, or user does not have a shadow password, fallback
+ *      onto a normal UNIX authentication
+ */
+
+#define _UNIX_AUTHTOK  "-UN*X-PASS"
+
+#define AUTH_RETURN						\
+do {								\
+	if (on(UNIX_LIKE_AUTH, ctrl) && ret_data) {		\
+		D(("recording return code for next time [%d]",	\
+					retval));		\
+		*ret_data = retval;				\
+		pam_set_data(pamh, "unix_setcred_return",	\
+		             (void *) ret_data, setcred_free);	\
+	} else if (ret_data)					\
+	  free (ret_data);                                      \
+	D(("done. [%s]", pam_strerror(pamh, retval)));		\
+	return retval;						\
+} while (0)
+
+
+static void
+setcred_free (pam_handle_t *pamh UNUSED, void *ptr, int err UNUSED)
+{
+	if (ptr)
+		free (ptr);
+}
+
+int
+pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	unsigned int ctrl;
+	int retval, *ret_data = NULL;
+	const char *name;
+	const void *p;
+
+	D(("called."));
+
+	ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv);
+
+	/* Get a few bytes so we can pass our return value to
+	   pam_sm_setcred(). */
+	if (on(UNIX_LIKE_AUTH, ctrl))
+		ret_data = malloc(sizeof(int));
+
+	/* get the user'name' */
+
+	retval = pam_get_user(pamh, &name, NULL);
+	if (retval == PAM_SUCCESS) {
+		/*
+		 * Various libraries at various times have had bugs related to
+		 * '+' or '-' as the first character of a user name. Don't
+		 * allow this characters here.
+		 */
+		if (name == NULL || name[0] == '-' || name[0] == '+') {
+			pam_syslog(pamh, LOG_ERR, "bad username [%s]", name);
+			retval = PAM_USER_UNKNOWN;
+			AUTH_RETURN;
+		}
+		if (on(UNIX_DEBUG, ctrl))
+			D(("username [%s] obtained", name));
+	} else {
+		D(("trouble reading username"));
+		if (retval == PAM_CONV_AGAIN) {
+			D(("pam_get_user/conv() function is not ready yet"));
+			/* it is safe to resume this function so we translate this
+			 * retval to the value that indicates we're happy to resume.
+			 */
+			retval = PAM_INCOMPLETE;
+		}
+		AUTH_RETURN;
+	}
+
+	/* if this user does not have a password... */
+
+	if (_unix_blankpasswd(pamh, ctrl, name)) {
+		D(("user '%s' has blank passwd", name));
+		name = NULL;
+		retval = PAM_SUCCESS;
+		AUTH_RETURN;
+	}
+	/* get this user's authentication token */
+
+	retval = _unix_read_password(pamh, ctrl, NULL, _("Password: "), NULL
+				     ,_UNIX_AUTHTOK, &p);
+	if (retval != PAM_SUCCESS) {
+		if (retval != PAM_CONV_AGAIN) {
+			pam_syslog(pamh, LOG_CRIT,
+			    "auth could not identify password for [%s]", name);
+		} else {
+			D(("conversation function is not ready yet"));
+			/*
+			 * it is safe to resume this function so we translate this
+			 * retval to the value that indicates we're happy to resume.
+			 */
+			retval = PAM_INCOMPLETE;
+		}
+		name = NULL;
+		AUTH_RETURN;
+	}
+	D(("user=%s, password=[%s]", name, p));
+
+	/* verify the password of this user */
+	retval = _unix_verify_password(pamh, name, p, ctrl);
+	name = p = NULL;
+
+	AUTH_RETURN;
+}
+
+
+/*
+ * The only thing _pam_set_credentials_unix() does is initialization of
+ * UNIX group IDs.
+ *
+ * Well, everybody but me on linux-pam is convinced that it should not
+ * initialize group IDs, so I am not doing it but don't say that I haven't
+ * warned you. -- AOY
+ */
+
+int
+pam_sm_setcred (pam_handle_t *pamh, int flags UNUSED,
+		int argc UNUSED, const char **argv UNUSED)
+{
+	int retval;
+	const void *pretval = NULL;
+
+	D(("called."));
+
+	retval = PAM_SUCCESS;
+
+	D(("recovering return code from auth call"));
+	/* We will only find something here if UNIX_LIKE_AUTH is set --
+	   don't worry about an explicit check of argv. */
+	if (pam_get_data(pamh, "unix_setcred_return", &pretval) == PAM_SUCCESS
+	    && pretval) {
+	        retval = *(const int *)pretval;
+		pam_set_data(pamh, "unix_setcred_return", NULL, NULL);
+		D(("recovered data indicates that old retval was %d", retval));
+	}
+
+	return retval;
+}
Index: pam-1.1.8/modules/pam_extrausers/pam_unix_passwd.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/pam_unix_passwd.c
@@ -0,0 +1,843 @@
+/*
+ * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
+ * Copyright (C) 1996.
+ * Copyright (c) Jan Rêkorajski, 1999.
+ * Copyright (c) Red Hat, Inc., 2007, 2008.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <shadow.h>
+#include <time.h>		/* for time() */
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+
+#include <security/_pam_macros.h>
+
+/* indicate the following groups are defined */
+
+#ifdef PAM_STATIC
+# include "pam_unix_static.h"
+#else
+# define PAM_SM_PASSWORD
+#endif
+
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+#include <security/pam_modutil.h>
+
+#include "md5.h"
+#include "support.h"
+#include "passverify.h"
+#include "bigcrypt.h"
+
+#if (HAVE_YP_GET_DEFAULT_DOMAIN || HAVE_GETDOMAINNAME) && HAVE_YP_MASTER
+# define HAVE_NIS
+#endif
+
+#ifdef HAVE_NIS
+# include <rpc/rpc.h>
+
+# if HAVE_RPCSVC_YP_PROT_H
+#  include <rpcsvc/yp_prot.h>
+# endif
+
+# if HAVE_RPCSVC_YPCLNT_H
+#  include <rpcsvc/ypclnt.h>
+# endif
+
+# include "yppasswd.h"
+
+# if !HAVE_DECL_GETRPCPORT
+extern int getrpcport(const char *host, unsigned long prognum,
+		      unsigned long versnum, unsigned int proto);
+# endif				/* GNU libc 2.1 */
+#endif
+
+extern const char *obscure_msg(const char *, const char *, const struct passwd *,
+			       unsigned int);
+
+/*
+   How it works:
+   Gets in username (has to be done) from the calling program
+   Does authentication of user (only if we are not running as root)
+   Gets new password/checks for sanity
+   Sets it.
+ */
+
+/* data tokens */
+
+#define _UNIX_OLD_AUTHTOK	"-UN*X-OLD-PASS"
+#define _UNIX_NEW_AUTHTOK	"-UN*X-NEW-PASS"
+
+#define MAX_PASSWD_TRIES	3
+
+#ifdef HAVE_NIS
+static char *getNISserver(pam_handle_t *pamh, unsigned int ctrl)
+{
+	char *master;
+	char *domainname;
+	int port, err;
+
+#ifdef HAVE_YP_GET_DEFAULT_DOMAIN
+	if ((err = yp_get_default_domain(&domainname)) != 0) {
+		pam_syslog(pamh, LOG_WARNING, "can't get local yp domain: %s",
+			 yperr_string(err));
+		return NULL;
+	}
+#elif defined(HAVE_GETDOMAINNAME)
+	char domainname_res[256];
+
+	if (getdomainname (domainname_res, sizeof (domainname_res)) == 0)
+	  {
+	    if (strcmp (domainname_res, "(none)") == 0)
+	      {
+		/* If domainname is not set, some systems will return "(none)" */
+		domainname_res[0] = '\0';
+	      }
+	    domainname = domainname_res;
+	  }
+	else domainname = NULL;
+#endif
+
+	if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
+		pam_syslog(pamh, LOG_WARNING, "can't find the master ypserver: %s",
+			 yperr_string(err));
+		return NULL;
+	}
+	port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
+	if (port == 0) {
+		pam_syslog(pamh, LOG_WARNING,
+		         "yppasswdd not running on NIS master host");
+		return NULL;
+	}
+	if (port >= IPPORT_RESERVED) {
+		pam_syslog(pamh, LOG_WARNING,
+		         "yppasswd daemon running on illegal port");
+		return NULL;
+	}
+	if (on(UNIX_DEBUG, ctrl)) {
+	  pam_syslog(pamh, LOG_DEBUG, "Use NIS server on %s with port %d",
+		     master, port);
+	}
+	return master;
+}
+#endif
+
+#ifdef WITH_SELINUX
+
+static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user,
+    const char *fromwhat, const char *towhat, int remember)
+{
+    int retval, child, fds[2];
+    struct sigaction newsa, oldsa;
+
+    D(("called."));
+    /* create a pipe for the password */
+    if (pipe(fds) != 0) {
+	D(("could not make pipe"));
+	return PAM_AUTH_ERR;
+    }
+
+    if (off(UNIX_NOREAP, ctrl)) {
+	/*
+	 * This code arranges that the demise of the child does not cause
+	 * the application to receive a signal it is not expecting - which
+	 * may kill the application or worse.
+	 *
+	 * The "noreap" module argument is provided so that the admin can
+	 * override this behavior.
+	 */
+        memset(&newsa, '\0', sizeof(newsa));
+        newsa.sa_handler = SIG_DFL;
+        sigaction(SIGCHLD, &newsa, &oldsa);
+    }
+
+    /* fork */
+    child = fork();
+    if (child == 0) {
+        int i=0;
+        struct rlimit rlim;
+	static char *envp[] = { NULL };
+	char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
+        char buffer[16];
+
+	/* XXX - should really tidy up PAM here too */
+
+	/* reopen stdin as pipe */
+	dup2(fds[0], STDIN_FILENO);
+
+	if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
+	  if (rlim.rlim_max >= MAX_FD_NO)
+	    rlim.rlim_max = MAX_FD_NO;
+	  for (i=0; i < (int)rlim.rlim_max; i++) {
+	    if (i != STDIN_FILENO)
+		close(i);
+	  }
+	}
+
+	/* exec binary helper */
+	args[0] = x_strdup(UPDATE_HELPER);
+	args[1] = x_strdup(user);
+	args[2] = x_strdup("update");
+	if (on(UNIX_SHADOW, ctrl))
+		args[3] = x_strdup("1");
+	else
+		args[3] = x_strdup("0");
+
+        snprintf(buffer, sizeof(buffer), "%d", remember);
+        args[4] = x_strdup(buffer);
+
+	execve(UPDATE_HELPER, args, envp);
+
+	/* should not get here: exit with error */
+	D(("helper binary is not available"));
+	_exit(PAM_AUTHINFO_UNAVAIL);
+    } else if (child > 0) {
+	/* wait for child */
+	/* if the stored password is NULL */
+        int rc=0;
+	if (fromwhat)
+	  pam_modutil_write(fds[1], fromwhat, strlen(fromwhat)+1);
+	else
+	  pam_modutil_write(fds[1], "", 1);
+	if (towhat) {
+	  pam_modutil_write(fds[1], towhat, strlen(towhat)+1);
+	}
+	else
+	  pam_modutil_write(fds[1], "", 1);
+
+	close(fds[0]);       /* close here to avoid possible SIGPIPE above */
+	close(fds[1]);
+	/* wait for helper to complete: */
+	while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
+	if (rc<0) {
+	  pam_syslog(pamh, LOG_ERR, "pam_extrausers_update waitpid failed: %m");
+	  retval = PAM_AUTHTOK_ERR;
+	} else if (!WIFEXITED(retval)) {
+          pam_syslog(pamh, LOG_ERR, "pam_extrausers_update abnormal exit: %d", retval);
+          retval = PAM_AUTHTOK_ERR;
+        } else {
+	  retval = WEXITSTATUS(retval);
+	}
+    } else {
+	D(("fork failed"));
+	close(fds[0]);
+	close(fds[1]);
+	retval = PAM_AUTH_ERR;
+    }
+
+    if (off(UNIX_NOREAP, ctrl)) {
+        sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
+    }
+
+    return retval;
+}
+#endif
+
+static int check_old_password(const char *forwho, const char *newpass)
+{
+	static char buf[16384];
+	char *s_luser, *s_uid, *s_npas, *s_pas;
+	int retval = PAM_SUCCESS;
+	FILE *opwfile;
+	size_t len = strlen(forwho);
+
+	opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+	if (opwfile == NULL)
+		return PAM_ABORT;
+
+	while (fgets(buf, 16380, opwfile)) {
+		if (!strncmp(buf, forwho, len) && (buf[len] == ':' ||
+			buf[len] == ',')) {
+			char *sptr;
+			buf[strlen(buf) - 1] = '\0';
+			s_luser = strtok_r(buf, ":,", &sptr);
+			s_uid = strtok_r(NULL, ":,", &sptr);
+			s_npas = strtok_r(NULL, ":,", &sptr);
+			s_pas = strtok_r(NULL, ":,", &sptr);
+			while (s_pas != NULL) {
+				char *md5pass = Goodcrypt_md5(newpass, s_pas);
+				if (!strcmp(md5pass, s_pas)) {
+					_pam_delete(md5pass);
+					retval = PAM_AUTHTOK_ERR;
+					break;
+				}
+				s_pas = strtok_r(NULL, ":,", &sptr);
+				_pam_delete(md5pass);
+			}
+			break;
+		}
+	}
+	fclose(opwfile);
+
+	return retval;
+}
+
+static int _do_setpass(pam_handle_t* pamh, const char *forwho,
+		       const char *fromwhat,
+		       char *towhat, unsigned int ctrl, int remember)
+{
+	struct passwd *pwd = NULL;
+	int retval = 0;
+	int unlocked = 0;
+	char *master = NULL;
+
+	D(("called"));
+
+	pwd = getpwnam(forwho);
+
+	if (pwd == NULL) {
+		retval = PAM_AUTHTOK_ERR;
+		goto done;
+	}
+
+	if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) {
+#ifdef HAVE_NIS
+	  if ((master=getNISserver(pamh, ctrl)) != NULL) {
+		struct timeval timeout;
+		struct yppasswd yppwd;
+		CLIENT *clnt;
+		int status;
+		enum clnt_stat err;
+
+		/* Unlock passwd file to avoid deadlock */
+		unlock_pwdf();
+		unlocked = 1;
+
+		/* Initialize password information */
+		yppwd.newpw.pw_passwd = pwd->pw_passwd;
+		yppwd.newpw.pw_name = pwd->pw_name;
+		yppwd.newpw.pw_uid = pwd->pw_uid;
+		yppwd.newpw.pw_gid = pwd->pw_gid;
+		yppwd.newpw.pw_gecos = pwd->pw_gecos;
+		yppwd.newpw.pw_dir = pwd->pw_dir;
+		yppwd.newpw.pw_shell = pwd->pw_shell;
+		yppwd.oldpass = fromwhat ? strdup (fromwhat) : strdup ("");
+		yppwd.newpw.pw_passwd = towhat;
+
+		D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
+
+		/* The yppasswd.x file said `unix authentication required',
+		 * so I added it. This is the only reason it is in here.
+		 * My yppasswdd doesn't use it, but maybe some others out there
+		 * do.                                        --okir
+		 */
+		clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
+		clnt->cl_auth = authunix_create_default();
+		memset((char *) &status, '\0', sizeof(status));
+		timeout.tv_sec = 25;
+		timeout.tv_usec = 0;
+		err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
+				(xdrproc_t) xdr_yppasswd, (char *) &yppwd,
+				(xdrproc_t) xdr_int, (char *) &status,
+				timeout);
+
+		free (yppwd.oldpass);
+
+		if (err) {
+			_make_remark(pamh, ctrl, PAM_TEXT_INFO,
+				clnt_sperrno(err));
+		} else if (status) {
+			D(("Error while changing NIS password.\n"));
+		}
+		D(("The password has%s been changed on %s.",
+		   (err || status) ? " not" : "", master));
+		pam_syslog(pamh, LOG_NOTICE, "password%s changed for %s on %s",
+			 (err || status) ? " not" : "", pwd->pw_name, master);
+
+		auth_destroy(clnt->cl_auth);
+		clnt_destroy(clnt);
+		if (err || status) {
+			_make_remark(pamh, ctrl, PAM_TEXT_INFO,
+				_("NIS password could not be changed."));
+			retval = PAM_TRY_AGAIN;
+		}
+#ifdef PAM_DEBUG
+		sleep(5);
+#endif
+	    } else {
+		    retval = PAM_TRY_AGAIN;
+	    }
+#else
+          if (on(UNIX_DEBUG, ctrl)) {
+            pam_syslog(pamh, LOG_DEBUG, "No NIS support available");
+          }
+
+          retval = PAM_TRY_AGAIN;
+#endif
+	}
+
+	if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
+		if(unlocked) {
+			if (lock_pwdf() != PAM_SUCCESS) {
+				return PAM_AUTHTOK_LOCK_BUSY;
+			}
+		}
+#ifdef WITH_SELINUX
+	        if (unix_selinux_confined())
+			  return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember);
+#endif
+		/* first, save old password */
+		if (save_old_password(pamh, forwho, fromwhat, remember)) {
+			retval = PAM_AUTHTOK_ERR;
+			goto done;
+		}
+		if (on(UNIX_SHADOW, ctrl) || is_pwd_shadowed(pwd)) {
+			retval = unix_update_shadow(pamh, forwho, towhat);
+			if (retval == PAM_SUCCESS)
+				if (!is_pwd_shadowed(pwd))
+					retval = unix_update_passwd(pamh, forwho, "x");
+		} else {
+			retval = unix_update_passwd(pamh, forwho, towhat);
+		}
+	}
+
+
+done:
+	unlock_pwdf();
+
+	return retval;
+}
+
+static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl)
+{
+	struct passwd *pwent = NULL;	/* Password and shadow password */
+	struct spwd *spent = NULL;	/* file entries for the user */
+	int daysleft;
+	int retval;
+
+	retval = get_account_info(pamh, user, &pwent, &spent);
+	if (retval == PAM_USER_UNKNOWN) {
+		return retval;
+	}
+
+	if (retval == PAM_SUCCESS && spent == NULL)
+		return PAM_SUCCESS;
+
+	if (retval == PAM_UNIX_RUN_HELPER) {
+		retval = _unix_run_verify_binary(pamh, ctrl, user, &daysleft);
+		if (retval == PAM_AUTH_ERR || retval == PAM_USER_UNKNOWN)
+			return retval;
+	}
+	else if (retval == PAM_SUCCESS)
+		retval = check_shadow_expiry(pamh, spent, &daysleft);
+
+	if (on(UNIX__IAMROOT, ctrl) || retval == PAM_NEW_AUTHTOK_REQD)
+		return PAM_SUCCESS;
+
+	return retval;
+}
+
+static int _pam_unix_approve_pass(pam_handle_t * pamh
+				  ,unsigned int ctrl
+				  ,const char *pass_old
+				  ,const char *pass_new,
+                                  int pass_min_len)
+{
+	const void *user;
+	const char *remark = NULL;
+	int retval = PAM_SUCCESS;
+
+	D(("&new=%p, &old=%p", pass_old, pass_new));
+	D(("new=[%s]", pass_new));
+	D(("old=[%s]", pass_old));
+
+	if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
+		if (on(UNIX_DEBUG, ctrl)) {
+			pam_syslog(pamh, LOG_DEBUG, "bad authentication token");
+		}
+		_make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
+			_("No password supplied") : _("Password unchanged"));
+		return PAM_AUTHTOK_ERR;
+	}
+	/*
+	 * if one wanted to hardwire authentication token strength
+	 * checking this would be the place - AGM
+	 */
+
+	retval = pam_get_item(pamh, PAM_USER, &user);
+	if (retval != PAM_SUCCESS) {
+		if (on(UNIX_DEBUG, ctrl)) {
+			pam_syslog(pamh, LOG_ERR, "Can not get username");
+			return PAM_AUTHTOK_ERR;
+		}
+	}
+	if (off(UNIX__IAMROOT, ctrl)) {
+		if (strlen(pass_new) < pass_min_len)
+		  remark = _("You must choose a longer password");
+		D(("length check [%s]", remark));
+		if (on(UNIX_REMEMBER_PASSWD, ctrl)) {
+			if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR)
+			  remark = _("Password has been already used. Choose another.");
+			if (retval == PAM_ABORT) {
+				pam_syslog(pamh, LOG_ERR, "can't open %s file to check old passwords",
+					OLD_PASSWORDS_FILE);
+				return retval;
+			}
+		}
+		if (!remark && pass_old != NULL) { /* only check if we don't already have a failure */
+			struct passwd *pwd;
+			pwd = pam_modutil_getpwnam(pamh, user);
+			remark = (char *)obscure_msg(pass_old,pass_new,pwd,ctrl); /* do obscure checks */
+		}
+	}
+	if (remark) {
+		_make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
+		retval = PAM_AUTHTOK_ERR;
+	}
+	return retval;
+}
+
+int
+pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	unsigned int ctrl, lctrl;
+	int retval;
+	int remember = -1;
+	int rounds = -1;
+	int pass_min_len = 6;
+
+	/* <DO NOT free() THESE> */
+	const char *user;
+	const void *pass_old, *pass_new;
+	/* </DO NOT free() THESE> */
+
+	D(("called."));
+
+	ctrl = _set_ctrl(pamh, flags, &remember, &rounds, &pass_min_len,
+	                 argc, argv);
+
+	/*
+	 * First get the name of a user
+	 */
+	retval = pam_get_user(pamh, &user, NULL);
+	if (retval == PAM_SUCCESS) {
+		/*
+		 * Various libraries at various times have had bugs related to
+		 * '+' or '-' as the first character of a user name. Don't
+		 * allow them.
+		 */
+		if (user == NULL || user[0] == '-' || user[0] == '+') {
+			pam_syslog(pamh, LOG_ERR, "bad username [%s]", user);
+			return PAM_USER_UNKNOWN;
+		}
+		if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
+			pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained",
+			         user);
+	} else {
+		if (on(UNIX_DEBUG, ctrl))
+			pam_syslog(pamh, LOG_DEBUG,
+			         "password - could not identify user");
+		return retval;
+	}
+
+	D(("Got username of %s", user));
+
+	/*
+	 * Before we do anything else, check to make sure that the user's
+	 * info is in one of the databases we can modify from this module,
+	 * which currently is 'files' and 'nis'.  We have to do this because
+	 * getpwnam() doesn't tell you *where* the information it gives you
+	 * came from, nor should it.  That's our job.
+	 */
+	if (_unix_comesfromsource(pamh, user, 1, on(UNIX_NIS, ctrl)) == 0) {
+		pam_syslog(pamh, LOG_DEBUG,
+			 "user \"%s\" does not exist in /var/lib/extrausers/passwd%s",
+			 user, on(UNIX_NIS, ctrl) ? " or NIS" : "");
+		return PAM_USER_UNKNOWN;
+	} else {
+		struct passwd *pwd;
+		_unix_getpwnam(pamh, user, 1, on(UNIX_NIS, ctrl), &pwd);
+		if (pwd == NULL) {
+			pam_syslog(pamh, LOG_DEBUG,
+				"user \"%s\" has corrupted passwd entry",
+				user);
+			return PAM_USER_UNKNOWN;
+		}
+	}
+
+	/*
+	 * This is not an AUTH module!
+	 */
+	if (on(UNIX__NONULL, ctrl))
+		set(UNIX__NULLOK, ctrl);
+
+	if (on(UNIX__PRELIM, ctrl)) {
+		/*
+		 * obtain and verify the current password (OLDAUTHTOK) for
+		 * the user.
+		 */
+		char *Announce;
+
+		D(("prelim check"));
+
+		if (_unix_blankpasswd(pamh, ctrl, user)) {
+			return PAM_SUCCESS;
+		} else if (off(UNIX__IAMROOT, ctrl) || on(UNIX_NIS, ctrl)) {
+			/* instruct user what is happening */
+			if (asprintf(&Announce, _("Changing password for %s."),
+				user) < 0) {
+				pam_syslog(pamh, LOG_CRIT,
+				         "password - out of memory");
+				return PAM_BUF_ERR;
+			}
+
+			lctrl = ctrl;
+			set(UNIX__OLD_PASSWD, lctrl);
+			retval = _unix_read_password(pamh, lctrl
+						     ,Announce
+					     ,(on(UNIX__IAMROOT, ctrl)
+			                       ? _("NIS server root password: ")
+			                       : _("(current) UNIX password: "))
+						     ,NULL
+						     ,_UNIX_OLD_AUTHTOK
+					     ,&pass_old);
+			free(Announce);
+
+			if (retval != PAM_SUCCESS) {
+				pam_syslog(pamh, LOG_NOTICE,
+				    "password - (old) token not obtained");
+				return retval;
+			}
+			/* verify that this is the password for this user
+			 * if we're not using NIS */
+
+			if (off(UNIX_NIS, ctrl)) {
+				retval = _unix_verify_password(pamh, user, pass_old, ctrl);
+			}
+		} else {
+			D(("process run by root so do nothing this time around"));
+			pass_old = NULL;
+			retval = PAM_SUCCESS;	/* root doesn't have too */
+		}
+
+		if (retval != PAM_SUCCESS) {
+			D(("Authentication failed"));
+			pass_old = NULL;
+			return retval;
+		}
+		retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
+		pass_old = NULL;
+		if (retval != PAM_SUCCESS) {
+			pam_syslog(pamh, LOG_CRIT,
+			         "failed to set PAM_OLDAUTHTOK");
+		}
+		retval = _unix_verify_shadow(pamh,user, ctrl);
+		if (retval == PAM_AUTHTOK_ERR) {
+			if (off(UNIX__IAMROOT, ctrl))
+				_make_remark(pamh, ctrl, PAM_ERROR_MSG,
+					     _("You must wait longer to change your password"));
+			else
+				retval = PAM_SUCCESS;
+		}
+	} else if (on(UNIX__UPDATE, ctrl)) {
+		/*
+		 * tpass is used below to store the _pam_md() return; it
+		 * should be _pam_delete()'d.
+		 */
+
+		char *tpass = NULL;
+		int retry = 0;
+
+		/*
+		 * obtain the proposed password
+		 */
+
+		D(("do update"));
+
+		/*
+		 * get the old token back. NULL was ok only if root [at this
+		 * point we assume that this has already been enforced on a
+		 * previous call to this function].
+		 */
+
+		if (off(UNIX_NOT_SET_PASS, ctrl)) {
+			retval = pam_get_item(pamh, PAM_OLDAUTHTOK
+					      ,&pass_old);
+		} else {
+			retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
+					      ,&pass_old);
+			if (retval == PAM_NO_MODULE_DATA) {
+				retval = PAM_SUCCESS;
+				pass_old = NULL;
+			}
+		}
+		D(("pass_old [%s]", pass_old));
+
+		if (retval != PAM_SUCCESS) {
+			pam_syslog(pamh, LOG_NOTICE, "user not authenticated");
+			return retval;
+		}
+
+		D(("get new password now"));
+
+		lctrl = ctrl;
+
+		if (on(UNIX_USE_AUTHTOK, lctrl)) {
+			set(UNIX_USE_FIRST_PASS, lctrl);
+		}
+		retry = 0;
+		retval = PAM_AUTHTOK_ERR;
+		while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
+			/*
+			 * use_authtok is to force the use of a previously entered
+			 * password -- needed for pluggable password strength checking
+			 */
+
+			retval = _unix_read_password(pamh, lctrl
+						     ,NULL
+					     ,_("Enter new UNIX password: ")
+					    ,_("Retype new UNIX password: ")
+						     ,_UNIX_NEW_AUTHTOK
+					     ,&pass_new);
+
+			if (retval != PAM_SUCCESS) {
+				if (on(UNIX_DEBUG, ctrl)) {
+					pam_syslog(pamh, LOG_ALERT,
+						 "password - new password not obtained");
+				}
+				pass_old = NULL;	/* tidy up */
+				return retval;
+			}
+			D(("returned to _unix_chauthtok"));
+
+			/*
+			 * At this point we know who the user is and what they
+			 * propose as their new password. Verify that the new
+			 * password is acceptable.
+			 */
+
+			if (*(const char *)pass_new == '\0') {	/* "\0" password = NULL */
+				pass_new = NULL;
+			}
+			retval = _pam_unix_approve_pass(pamh, ctrl, pass_old,
+			                                pass_new, pass_min_len);
+
+			if (retval != PAM_SUCCESS && off(UNIX_NOT_SET_PASS, ctrl)) {
+				pam_set_item(pamh, PAM_AUTHTOK, NULL);
+			}
+		}
+
+		if (retval != PAM_SUCCESS) {
+			pam_syslog(pamh, LOG_NOTICE,
+			         "new password not acceptable");
+			pass_new = pass_old = NULL;	/* tidy up */
+			return retval;
+		}
+		if (lock_pwdf() != PAM_SUCCESS) {
+			return PAM_AUTHTOK_LOCK_BUSY;
+		}
+
+		if (pass_old) {
+			retval = _unix_verify_password(pamh, user, pass_old, ctrl);
+			if (retval != PAM_SUCCESS) {
+				pam_syslog(pamh, LOG_NOTICE, "user password changed by another process");
+				unlock_pwdf();
+				return retval;
+			}
+		}
+
+		retval = _unix_verify_shadow(pamh, user, ctrl);
+		if (retval != PAM_SUCCESS) {
+			pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired");
+			unlock_pwdf();
+			return retval;
+		}
+
+		retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new,
+		                                pass_min_len);
+		if (retval != PAM_SUCCESS) {
+			pam_syslog(pamh, LOG_NOTICE,
+			         "new password not acceptable 2");
+			pass_new = pass_old = NULL;	/* tidy up */
+			unlock_pwdf();
+			return retval;
+		}
+
+		/*
+		 * By reaching here we have approved the passwords and must now
+		 * rebuild the password database file.
+		 */
+
+		/*
+		 * First we encrypt the new password.
+		 */
+
+		tpass = create_password_hash(pamh, pass_new, ctrl, rounds);
+		if (tpass == NULL) {
+			pam_syslog(pamh, LOG_CRIT,
+				"crypt() failure or out of memory for password");
+			pass_new = pass_old = NULL;	/* tidy up */
+			unlock_pwdf();
+			return PAM_BUF_ERR;
+		}
+
+		D(("password processed"));
+
+		/* update the password database(s) -- race conditions..? */
+
+		retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
+		                     remember);
+	        /* _do_setpass has called unlock_pwdf for us */
+
+		_pam_delete(tpass);
+		pass_old = pass_new = NULL;
+	} else {		/* something has broken with the module */
+		pam_syslog(pamh, LOG_ALERT,
+		         "password received unknown request");
+		retval = PAM_ABORT;
+	}
+
+	D(("retval was %d", retval));
+
+	return retval;
+}
Index: pam-1.1.8/modules/pam_extrausers/pam_unix_sess.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/pam_unix_sess.c
@@ -0,0 +1,133 @@
+/*
+ * $Id$
+ *
+ * Copyright Alexander O. Yuriev, 1996.  All rights reserved.
+ * Copyright Jan R\EAkorajski, 1999.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* indicate the following groups are defined */
+
+#ifdef PAM_STATIC
+# include "pam_unix_static.h"
+#else
+# define PAM_SM_SESSION
+#endif
+
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+#include <security/pam_modutil.h>
+
+#include "support.h"
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * session module.
+ */
+
+int
+pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	char *user_name, *service;
+	unsigned int ctrl;
+	int retval;
+    const char *login_name;
+
+	D(("called."));
+
+	ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv);
+
+	retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
+	if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
+		pam_syslog(pamh, LOG_CRIT,
+		         "open_session - error recovering username");
+		return PAM_SESSION_ERR;		/* How did we get authenticated with
+						   no username?! */
+	}
+	retval = pam_get_item(pamh, PAM_SERVICE, (void *) &service);
+	if (service == NULL || *service == '\0' || retval != PAM_SUCCESS) {
+		pam_syslog(pamh, LOG_CRIT,
+		         "open_session - error recovering service");
+		return PAM_SESSION_ERR;
+	}
+	login_name = pam_modutil_getlogin(pamh);
+	if (login_name == NULL) {
+	    login_name = "";
+	}
+	pam_syslog(pamh, LOG_INFO, "session opened for user %s by %s(uid=%lu)",
+		 user_name, login_name, (unsigned long)getuid());
+
+	return PAM_SUCCESS;
+}
+
+int
+pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	char *user_name, *service;
+	unsigned int ctrl;
+	int retval;
+
+	D(("called."));
+
+	ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv);
+
+	retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
+	if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
+		pam_syslog(pamh, LOG_CRIT,
+		         "close_session - error recovering username");
+		return PAM_SESSION_ERR;		/* How did we get authenticated with
+						   no username?! */
+	}
+	retval = pam_get_item(pamh, PAM_SERVICE, (void *) &service);
+	if (service == NULL || *service == '\0' || retval != PAM_SUCCESS) {
+		pam_syslog(pamh, LOG_CRIT,
+		         "close_session - error recovering service");
+		return PAM_SESSION_ERR;
+	}
+	pam_syslog(pamh, LOG_INFO, "session closed for user %s",
+		user_name);
+
+	return PAM_SUCCESS;
+}
Index: pam-1.1.8/modules/pam_extrausers/pam_unix_static.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/pam_unix_static.c
@@ -0,0 +1,23 @@
+#include "config.h"
+
+#ifdef PAM_STATIC
+
+#define static extern
+#define PAM_SM_ACCOUNT
+#define PAM_SM_AUTH
+#define PAM_SM_PASSWORD
+#define PAM_SM_SESSION
+#include "pam_unix_static.h"
+#include <security/pam_modules.h>
+
+struct pam_module _pam_extrausers_modstruct = {
+	"pam_extrausers",
+	pam_sm_authenticate,
+	pam_sm_setcred,
+	pam_sm_acct_mgmt,
+	pam_sm_open_session,
+	pam_sm_close_session,
+	pam_sm_chauthtok,
+};
+
+#endif
Index: pam-1.1.8/modules/pam_extrausers/pam_unix_static.h
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/pam_unix_static.h
@@ -0,0 +1,6 @@
+#define pam_sm_acct_mgmt _pam_unix_sm_acct_mgmt
+#define pam_sm_authenticate _pam_unix_sm_authenticate
+#define pam_sm_setcred _pam_unix_sm_setcred
+#define pam_sm_chauthtok _pam_unix_sm_chauthtok
+#define pam_sm_open_session _pam_unix_sm_open_session
+#define pam_sm_close_session _pam_unix_sm_close_session
Index: pam-1.1.8/modules/pam_extrausers/passverify.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/passverify.c
@@ -0,0 +1,1164 @@
+/*
+ * Copyright information at end of file.
+ */
+#include "config.h"
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+#include "support.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef HAVE_LIBXCRYPT
+#include <xcrypt.h>
+#elif defined(HAVE_CRYPT_H)
+#include <crypt.h>
+#endif
+
+#include "md5.h"
+#include "bigcrypt.h"
+#include "passverify.h"
+
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#define SELINUX_ENABLED is_selinux_enabled()>0
+#else
+#define SELINUX_ENABLED 0
+#endif
+
+#ifdef HELPER_COMPILE
+#define pam_modutil_getpwnam(h,n) getpwnam(n)
+#define pam_modutil_getspnam(h,n) getspnam(n)
+#define pam_syslog(h,a,b,c) helper_log_err(a,b,c)
+#else
+#include <security/pam_modutil.h>
+#include <security/pam_ext.h>
+#endif
+
+#if defined(USE_LCKPWDF)
+# include "./lckpwdf.-c"
+#endif
+
+static void
+strip_hpux_aging(char *hash)
+{
+	static const char valid[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+		"abcdefghijklmnopqrstuvwxyz"
+		"0123456789./";
+	if ((*hash != '$') && (strlen(hash) > 13)) {
+		for (hash += 13; *hash != '\0'; hash++) {
+			if (strchr(valid, *hash) == NULL) {
+				*hash = '\0';
+				break;
+			}
+		}
+	}
+}
+
+int
+verify_pwd_hash(const char *p, char *hash, unsigned int nullok)
+{
+	size_t hash_len;
+	char *pp = NULL;
+	int retval;
+	D(("called"));
+
+	strip_hpux_aging(hash);
+	hash_len = strlen(hash);
+	if (!hash_len) {
+		/* the stored password is NULL */
+		if (nullok) { /* this means we've succeeded */
+			D(("user has empty password - access granted"));
+			retval = PAM_SUCCESS;
+		} else {
+			D(("user has empty password - access denied"));
+			retval = PAM_AUTH_ERR;
+		}
+	} else if (!p || *hash == '*' || *hash == '!') {
+		retval = PAM_AUTH_ERR;
+	} else {
+		if (!strncmp(hash, "$1$", 3)) {
+			pp = Goodcrypt_md5(p, hash);
+			if (pp && strcmp(pp, hash) != 0) {
+				_pam_delete(pp);
+				pp = Brokencrypt_md5(p, hash);
+			}
+		} else if (*hash != '$' && hash_len >= 13) {
+			pp = bigcrypt(p, hash);
+			if (pp && hash_len == 13 && strlen(pp) > hash_len) {
+				_pam_overwrite(pp + hash_len);
+			}
+		} else {
+			/*
+			 * Ok, we don't know the crypt algorithm, but maybe
+			 * libcrypt knows about it? We should try it.
+			 */
+#ifdef HAVE_CRYPT_R
+			struct crypt_data *cdata;
+			cdata = malloc(sizeof(*cdata));
+			if (cdata != NULL) {
+				cdata->initialized = 0;
+				pp = x_strdup(crypt_r(p, hash, cdata));
+				memset(cdata, '\0', sizeof(*cdata));
+				free(cdata);
+			}
+#else
+			pp = x_strdup(crypt(p, hash));
+#endif
+		}
+		p = NULL;		/* no longer needed here */
+
+		/* the moment of truth -- do we agree with the password? */
+		D(("comparing state of pp[%s] and hash[%s]", pp, hash));
+
+		if (pp && strcmp(pp, hash) == 0) {
+			retval = PAM_SUCCESS;
+		} else {
+			retval = PAM_AUTH_ERR;
+		}
+	}
+
+	if (pp)
+		_pam_delete(pp);
+	D(("done [%d].", retval));
+
+	return retval;
+}
+
+int
+is_pwd_shadowed(const struct passwd *pwd)
+{
+	if (pwd != NULL) {
+		if (strcmp(pwd->pw_passwd, "x") == 0) {
+			return 1;
+		}
+		if ((pwd->pw_passwd[0] == '#') &&
+		    (pwd->pw_passwd[1] == '#') &&
+		    (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+PAMH_ARG_DECL(int get_account_info,
+	const char *name, struct passwd **pwd, struct spwd **spwdent)
+{
+	/* UNIX passwords area */
+	*pwd = pam_modutil_getpwnam(pamh, name);	/* Get password file entry... */
+	*spwdent = NULL;
+
+	if (*pwd != NULL) {
+		if (strcmp((*pwd)->pw_passwd, "*NP*") == 0)
+		{ /* NIS+ */
+#ifdef HELPER_COMPILE
+			uid_t save_euid, save_uid;
+
+			save_euid = geteuid();
+			save_uid = getuid();
+			if (save_uid == (*pwd)->pw_uid)
+				setreuid(save_euid, save_uid);
+			else  {
+				setreuid(0, -1);
+				if (setreuid(-1, (*pwd)->pw_uid) == -1) {
+					setreuid(-1, 0);
+					setreuid(0, -1);
+					if(setreuid(-1, (*pwd)->pw_uid) == -1)
+						return PAM_CRED_INSUFFICIENT;
+				}
+			}
+
+			*spwdent = pam_modutil_getspnam(pamh, name);
+			if (save_uid == (*pwd)->pw_uid)
+				setreuid(save_uid, save_euid);
+			else {
+				setreuid(-1, 0);
+				setreuid(save_uid, -1);
+				setreuid(-1, save_euid);
+			}
+
+			if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
+				return PAM_AUTHINFO_UNAVAIL;
+#else
+			/* we must run helper for NIS+ passwords */
+			return PAM_UNIX_RUN_HELPER;
+#endif
+		} else if (is_pwd_shadowed(*pwd)) {
+			/*
+			 * ...and shadow password file entry for this user,
+			 * if shadowing is enabled
+			 */
+			*spwdent = pam_modutil_getspnam(pamh, name);
+#ifndef HELPER_COMPILE
+			if (*spwdent == NULL && (geteuid() || SELINUX_ENABLED))
+				return PAM_UNIX_RUN_HELPER;
+#endif
+			if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
+				return PAM_AUTHINFO_UNAVAIL;
+		}
+	} else {
+		return PAM_USER_UNKNOWN;
+	}
+	return PAM_SUCCESS;
+}
+
+PAMH_ARG_DECL(int get_pwd_hash,
+	const char *name, struct passwd **pwd, char **hash)
+{
+	int retval;
+	struct spwd *spwdent = NULL;
+
+	retval = get_account_info(PAMH_ARG(name, pwd, &spwdent));
+	if (retval != PAM_SUCCESS) {
+		return retval;
+	}
+
+	if (spwdent)
+		*hash = x_strdup(spwdent->sp_pwdp);
+	else
+		*hash = x_strdup((*pwd)->pw_passwd);
+	if (*hash == NULL)
+		return PAM_BUF_ERR;
+
+	return PAM_SUCCESS;
+}
+
+PAMH_ARG_DECL(int check_shadow_expiry,
+	struct spwd *spent, int *daysleft)
+{
+	long int curdays;
+	*daysleft = -1;
+	curdays = (long int)(time(NULL) / (60 * 60 * 24));
+	D(("today is %d, last change %d", curdays, spent->sp_lstchg));
+	if ((curdays >= spent->sp_expire) && (spent->sp_expire != -1)) {
+		D(("account expired"));
+		return PAM_ACCT_EXPIRED;
+	}
+	if (spent->sp_lstchg == 0) {
+		D(("need a new password"));
+		*daysleft = 0;
+		return PAM_NEW_AUTHTOK_REQD;
+	}
+	if (curdays < spent->sp_lstchg) {
+		pam_syslog(pamh, LOG_DEBUG,
+			 "account %s has password changed in future",
+			 spent->sp_namp);
+		return PAM_SUCCESS;
+	}
+	if ((curdays - spent->sp_lstchg > spent->sp_max)
+	    && (curdays - spent->sp_lstchg > spent->sp_inact)
+	    && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact)
+	    && (spent->sp_max != -1) && (spent->sp_inact != -1)) {
+		*daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
+		D(("authtok expired"));
+		return PAM_AUTHTOK_EXPIRED;
+	}
+	if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
+		D(("need a new password 2"));
+		return PAM_NEW_AUTHTOK_REQD;
+	}
+	if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn)
+	    && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
+		*daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
+		D(("warn before expiry"));
+	}
+	if ((curdays - spent->sp_lstchg < spent->sp_min)
+	    && (spent->sp_min != -1)) {
+		/*
+		 * The last password change was too recent. This error will be ignored
+		 * if no password change is attempted.
+		 */
+		D(("password change too recent"));
+		return PAM_AUTHTOK_ERR;
+	}
+	return PAM_SUCCESS;
+}
+
+/* passwd/salt conversion macros */
+
+#define PW_TMPFILE              "/var/lib/extrausers/npasswd"
+#define SH_TMPFILE              "/var/lib/extrausers/nshadow"
+#define OPW_TMPFILE             "/var/lib/extrausers/nopasswd"
+
+/*
+ * i64c - convert an integer to a radix 64 character
+ */
+static int
+i64c(int i)
+{
+        if (i < 0)
+                return ('.');
+        else if (i > 63)
+                return ('z');
+        if (i == 0)
+                return ('.');
+        if (i == 1)
+                return ('/');
+        if (i >= 2 && i <= 11)
+                return ('0' - 2 + i);
+        if (i >= 12 && i <= 37)
+                return ('A' - 12 + i);
+        if (i >= 38 && i <= 63)
+                return ('a' - 38 + i);
+        return ('\0');
+}
+
+/* <where> must point to a buffer of at least <length>+1 length */
+static void
+crypt_make_salt(char *where, int length)
+{
+        struct timeval tv;
+        MD5_CTX ctx;
+        unsigned char tmp[16];
+        unsigned char *src = (unsigned char *)where;
+        int i;
+#ifdef PAM_PATH_RANDOMDEV
+	int fd;
+	int rv;
+
+	if ((rv = fd = open(PAM_PATH_RANDOMDEV, O_RDONLY)) != -1) {
+		while ((rv = read(fd, where, length)) != length && errno == EINTR);
+		close (fd);
+	}
+	if (rv != length) {
+#endif
+        /*
+         * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
+         * removed use of static variables (AGM)
+         *
+	 * will work correctly only for length <= 16 */
+	src = tmp;
+        GoodMD5Init(&ctx);
+        gettimeofday(&tv, (struct timezone *) 0);
+        GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
+        i = getpid();
+        GoodMD5Update(&ctx, (void *) &i, sizeof i);
+        i = clock();
+        GoodMD5Update(&ctx, (void *) &i, sizeof i);
+        GoodMD5Update(&ctx, src, length);
+        GoodMD5Final(tmp, &ctx);
+#ifdef PAM_PATH_RANDOMDEV
+	}
+#endif
+        for (i = 0; i < length; i++)
+                *where++ = i64c(src[i] & 077);
+        *where = '\0';
+}
+
+char *
+crypt_md5_wrapper(const char *pass_new)
+{
+        unsigned char result[16];
+        char *cp = (char *) result;
+
+        cp = stpcpy(cp, "$1$");      /* magic for the MD5 */
+	crypt_make_salt(cp, 8);
+
+        /* no longer need cleartext */
+        cp = Goodcrypt_md5(pass_new, (const char *) result);
+	pass_new = NULL;
+
+        return cp;
+}
+
+PAMH_ARG_DECL(char * create_password_hash,
+	const char *password, unsigned int ctrl, int rounds)
+{
+	const char *algoid;
+	char salt[64]; /* contains rounds number + max 16 bytes of salt + algo id */
+	char *sp;
+
+	if (on(UNIX_MD5_PASS, ctrl)) {
+		/* algoid = "$1" */
+		return crypt_md5_wrapper(password);
+	} else if (on(UNIX_BLOWFISH_PASS, ctrl)) {
+		algoid = "$2a$";
+	} else if (on(UNIX_SHA256_PASS, ctrl)) {
+		algoid = "$5$";
+	} else if (on(UNIX_SHA512_PASS, ctrl)) {
+		algoid = "$6$";
+	} else { /* must be crypt/bigcrypt */
+		char tmppass[9];
+		char *crypted;
+
+		crypt_make_salt(salt, 2);
+		if (off(UNIX_BIGCRYPT, ctrl) && strlen(password) > 8) {
+			strncpy(tmppass, password, sizeof(tmppass)-1);
+			tmppass[sizeof(tmppass)-1] = '\0';
+			password = tmppass;
+		}
+		crypted = bigcrypt(password, salt);
+		memset(tmppass, '\0', sizeof(tmppass));
+		password = NULL;
+		return crypted;
+	}
+
+#ifdef HAVE_CRYPT_GENSALT_R
+	if (on(UNIX_BLOWFISH_PASS, ctrl)) {
+		char entropy[17];
+		crypt_make_salt(entropy, sizeof(entropy) - 1);
+		sp = crypt_gensalt_r (algoid, rounds,
+				      entropy, sizeof(entropy),
+				      salt, sizeof(salt));
+	} else {
+#endif
+		sp = stpcpy(salt, algoid);
+		if (on(UNIX_ALGO_ROUNDS, ctrl)) {
+			sp += snprintf(sp, sizeof(salt) - 3, "rounds=%u$", rounds);
+		}
+		crypt_make_salt(sp, 8);
+		/* For now be conservative so the resulting hashes
+		 * are not too long. 8 bytes of salt prevents dictionary
+		 * attacks well enough. */
+#ifdef HAVE_CRYPT_GENSALT_R
+	}
+#endif
+	sp = crypt(password, salt);
+	if (!sp || strncmp(algoid, sp, strlen(algoid)) != 0) {
+		/* libxcrypt/libc doesn't know the algorithm, use MD5 */
+		pam_syslog(pamh, LOG_ERR,
+			   "Algo %s not supported by the crypto backend, "
+			   "falling back to MD5\n",
+			   on(UNIX_BLOWFISH_PASS, ctrl) ? "blowfish" :
+			   on(UNIX_SHA256_PASS, ctrl) ? "sha256" :
+			   on(UNIX_SHA512_PASS, ctrl) ? "sha512" : algoid);
+		if(sp) {
+		   memset(sp, '\0', strlen(sp));
+		}
+		return crypt_md5_wrapper(password);
+	}
+
+	return x_strdup(sp);
+}
+
+#ifdef WITH_SELINUX
+int
+unix_selinux_confined(void)
+{
+    static int confined = -1;
+    int fd;
+    char tempfile[]="/var/lib/extrausers/.pwdXXXXXX";
+
+    if (confined != -1)
+	return confined;
+
+    /* cannot be confined without SELinux enabled */
+    if (!SELINUX_ENABLED){
+	confined = 0;
+	return confined;
+    }
+
+    /* let's try opening shadow read only */
+    if ((fd=open("/var/lib/extrausers/shadow", O_RDONLY)) != -1) {
+        close(fd);
+        confined = 0;
+        return confined;
+    }
+
+    if (errno == EACCES) {
+	confined = 1;
+	return confined;
+    }
+
+    /* shadow opening failed because of other reasons let's try
+       creating a file in /var/lib/extrausers */
+    if ((fd=mkstemp(tempfile)) != -1) {
+        unlink(tempfile);
+        close(fd);
+        confined = 0;
+        return confined;
+    }
+
+    confined = 1;
+    return confined;
+}
+
+#else
+int
+unix_selinux_confined(void)
+{
+    return 0;
+}
+#endif
+
+#ifdef USE_LCKPWDF
+int
+lock_pwdf(void)
+{
+        int i;
+        int retval;
+
+#ifndef HELPER_COMPILE
+        if (unix_selinux_confined()) {
+                return PAM_SUCCESS;
+        }
+#endif
+        /* These values for the number of attempts and the sleep time
+           are, of course, completely arbitrary.
+           My reading of the PAM docs is that, once pam_chauthtok() has been
+           called with PAM_UPDATE_AUTHTOK, we are obliged to take any
+           reasonable steps to make sure the token is updated; so retrying
+           for 1/10 sec. isn't overdoing it. */
+        i=0;
+        while((retval = extrausers_lckpwdf()) != 0 && i < 100) {
+                usleep(1000);
+                i++;
+        }
+        if(retval != 0) {
+                return PAM_AUTHTOK_LOCK_BUSY;
+        }
+        return PAM_SUCCESS;
+}
+
+void
+unlock_pwdf(void)
+{
+#ifndef HELPER_COMPILE
+        if (unix_selinux_confined()) {
+                return;
+        }
+#endif
+        extrausers_ulckpwdf();
+}
+#else
+int
+lock_pwdf(void)
+{
+	return PAM_SUCCESS;
+}
+
+void
+unlock_pwdf(void)
+{
+	return;
+}
+#endif
+
+#ifdef HELPER_COMPILE
+int
+save_old_password(const char *forwho, const char *oldpass,
+		  int howmany)
+#else
+int
+save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
+		  int howmany)
+#endif
+{
+    static char buf[16384];
+    static char nbuf[16384];
+    char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
+    int npas;
+    FILE *pwfile, *opwfile;
+    int err = 0;
+    int oldmask;
+    int found = 0;
+    struct passwd *pwd = NULL;
+    struct stat st;
+    size_t len = strlen(forwho);
+#ifdef WITH_SELINUX
+    security_context_t prev_context=NULL;
+#endif
+
+    if (howmany < 0) {
+	return PAM_SUCCESS;
+    }
+
+    if (oldpass == NULL) {
+	return PAM_SUCCESS;
+    }
+
+    oldmask = umask(077);
+
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      security_context_t passwd_context=NULL;
+      if (getfilecon("/var/lib/extrausers/passwd",&passwd_context)<0) {
+        return PAM_AUTHTOK_ERR;
+      };
+      if (getfscreatecon(&prev_context)<0) {
+        freecon(passwd_context);
+        return PAM_AUTHTOK_ERR;
+      }
+      if (setfscreatecon(passwd_context)) {
+        freecon(passwd_context);
+        freecon(prev_context);
+        return PAM_AUTHTOK_ERR;
+      }
+      freecon(passwd_context);
+    }
+#endif
+    pwfile = fopen(OPW_TMPFILE, "w");
+    umask(oldmask);
+    if (pwfile == NULL) {
+      err = 1;
+      goto done;
+    }
+
+    opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+    if (opwfile == NULL) {
+	fclose(pwfile);
+      err = 1;
+      goto done;
+    }
+
+    if (fstat(fileno(opwfile), &st) == -1) {
+	fclose(opwfile);
+	fclose(pwfile);
+	err = 1;
+	goto done;
+    }
+
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
+	fclose(opwfile);
+	fclose(pwfile);
+	err = 1;
+	goto done;
+    }
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
+	fclose(opwfile);
+	fclose(pwfile);
+	err = 1;
+	goto done;
+    }
+
+    while (fgets(buf, 16380, opwfile)) {
+	if (!strncmp(buf, forwho, len) && strchr(":,\n", buf[len]) != NULL) {
+	    char *sptr = NULL;
+	    found = 1;
+	    if (howmany == 0)
+		continue;
+	    buf[strlen(buf) - 1] = '\0';
+	    s_luser = strtok_r(buf, ":", &sptr);
+	    s_uid = strtok_r(NULL, ":", &sptr);
+	    s_npas = strtok_r(NULL, ":", &sptr);
+	    s_pas = strtok_r(NULL, ":", &sptr);
+	    npas = strtol(s_npas, NULL, 10) + 1;
+	    while (npas > howmany) {
+		s_pas = strpbrk(s_pas, ",");
+		if (s_pas != NULL)
+		    s_pas++;
+		npas--;
+	    }
+	    pass = crypt_md5_wrapper(oldpass);
+	    if (s_pas == NULL)
+		snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
+			 s_luser, s_uid, npas, pass);
+	    else
+		snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
+			 s_luser, s_uid, npas, s_pas, pass);
+	    _pam_delete(pass);
+	    if (fputs(nbuf, pwfile) < 0) {
+		err = 1;
+		break;
+	    }
+	} else if (fputs(buf, pwfile) < 0) {
+	    err = 1;
+	    break;
+	}
+    }
+    fclose(opwfile);
+
+    if (!found) {
+	pwd = pam_modutil_getpwnam(pamh, forwho);
+	if (pwd == NULL) {
+	    err = 1;
+	} else {
+	    pass = crypt_md5_wrapper(oldpass);
+	    snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
+		     forwho, (unsigned long)pwd->pw_uid, pass);
+	    _pam_delete(pass);
+	    if (fputs(nbuf, pwfile) < 0) {
+		err = 1;
+	    }
+	}
+    }
+
+    if (fflush(pwfile) || fsync(fileno(pwfile))) {
+	D(("fflush or fsync error writing entries to old passwords file: %m"));
+	err = 1;
+    }
+
+    if (fclose(pwfile)) {
+	D(("fclose error writing entries to old passwords file: %m"));
+	err = 1;
+    }
+
+done:
+    if (!err) {
+	if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
+	    err = 1;
+    }
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      if (setfscreatecon(prev_context)) {
+        err = 1;
+      }
+      if (prev_context)
+        freecon(prev_context);
+      prev_context=NULL;
+    }
+#endif
+    if (!err) {
+	return PAM_SUCCESS;
+    } else {
+	unlink(OPW_TMPFILE);
+	return PAM_AUTHTOK_ERR;
+    }
+}
+
+PAMH_ARG_DECL(int unix_update_passwd,
+	const char *forwho, const char *towhat)
+{
+    struct passwd *tmpent = NULL;
+    struct stat st;
+    FILE *pwfile, *opwfile;
+    int err = 1, found = 0;
+    int oldmask;
+#ifdef WITH_SELINUX
+    security_context_t prev_context=NULL;
+#endif
+
+    oldmask = umask(077);
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      security_context_t passwd_context=NULL;
+      if (getfilecon("/var/lib/extrausers/passwd",&passwd_context)<0) {
+	return PAM_AUTHTOK_ERR;
+      };
+      if (getfscreatecon(&prev_context)<0) {
+	freecon(passwd_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      if (setfscreatecon(passwd_context)) {
+	freecon(passwd_context);
+	freecon(prev_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      freecon(passwd_context);
+    }
+#endif
+    pwfile = fopen(PW_TMPFILE, "w");
+    umask(oldmask);
+    if (pwfile == NULL) {
+      err = 1;
+      goto done;
+    }
+
+    opwfile = fopen("/var/lib/extrausers/passwd", "r");
+    if (opwfile == NULL) {
+	fclose(pwfile);
+	err = 1;
+	goto done;
+    }
+
+    if (fstat(fileno(opwfile), &st) == -1) {
+	fclose(opwfile);
+	fclose(pwfile);
+	err = 1;
+	goto done;
+    }
+
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
+	fclose(opwfile);
+	fclose(pwfile);
+	err = 1;
+	goto done;
+    }
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
+	fclose(opwfile);
+	fclose(pwfile);
+	err = 1;
+	goto done;
+    }
+
+    tmpent = fgetpwent(opwfile);
+    while (tmpent) {
+	if (!strcmp(tmpent->pw_name, forwho)) {
+	    /* To shut gcc up */
+	    union {
+		const char *const_charp;
+		char *charp;
+	    } assigned_passwd;
+	    assigned_passwd.const_charp = towhat;
+
+	    tmpent->pw_passwd = assigned_passwd.charp;
+	    err = 0;
+	    found = 1;
+	}
+	if (putpwent(tmpent, pwfile)) {
+	    D(("error writing entry to password file: %m"));
+	    err = 1;
+	    break;
+	}
+	tmpent = fgetpwent(opwfile);
+    }
+    fclose(opwfile);
+
+    if (fflush(pwfile) || fsync(fileno(pwfile))) {
+	D(("fflush or fsync error writing entries to password file: %m"));
+	err = 1;
+    }
+
+    if (fclose(pwfile)) {
+	D(("fclose error writing entries to password file: %m"));
+	err = 1;
+    }
+
+done:
+    if (!err) {
+	if (!rename(PW_TMPFILE, "/var/lib/extrausers/passwd"))
+	    pam_syslog(pamh,
+		LOG_NOTICE, "password changed for %s", forwho);
+	else
+	    err = 1;
+    }
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      if (setfscreatecon(prev_context)) {
+	err = 1;
+      }
+      if (prev_context)
+	freecon(prev_context);
+      prev_context=NULL;
+    }
+#endif
+    if (!err) {
+	return PAM_SUCCESS;
+    } else {
+	unlink(PW_TMPFILE);
+	return found ? PAM_AUTHTOK_ERR : PAM_USER_UNKNOWN;
+    }
+}
+
+PAMH_ARG_DECL(int unix_update_shadow,
+	const char *forwho, char *towhat)
+{
+    struct spwd spwdent, *stmpent = NULL;
+    struct stat st;
+    FILE *pwfile, *opwfile;
+    int err = 0;
+    int oldmask;
+    int wroteentry = 0;
+#ifdef WITH_SELINUX
+    security_context_t prev_context=NULL;
+#endif
+
+    oldmask = umask(077);
+
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      security_context_t shadow_context=NULL;
+      if (getfilecon("/var/lib/extrausers/shadow",&shadow_context)<0) {
+	return PAM_AUTHTOK_ERR;
+      };
+      if (getfscreatecon(&prev_context)<0) {
+	freecon(shadow_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      if (setfscreatecon(shadow_context)) {
+	freecon(shadow_context);
+	freecon(prev_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      freecon(shadow_context);
+    }
+#endif
+    pwfile = fopen(SH_TMPFILE, "w");
+    umask(oldmask);
+    if (pwfile == NULL) {
+	err = 1;
+	goto done;
+    }
+
+    opwfile = fopen("/var/lib/extrausers/shadow", "r");
+    if (opwfile == NULL) {
+	fclose(pwfile);
+	err = 1;
+	goto done;
+    }
+
+    if (fstat(fileno(opwfile), &st) == -1) {
+	fclose(opwfile);
+	fclose(pwfile);
+	err = 1;
+	goto done;
+    }
+
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
+	fclose(opwfile);
+	fclose(pwfile);
+	err = 1;
+	goto done;
+    }
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
+	fclose(opwfile);
+	fclose(pwfile);
+	err = 1;
+	goto done;
+    }
+
+    stmpent = fgetspent(opwfile);
+    while (stmpent) {
+
+	if (!strcmp(stmpent->sp_namp, forwho)) {
+	    stmpent->sp_pwdp = towhat;
+	    stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
+	    if (stmpent->sp_lstchg == 0)
+	        stmpent->sp_lstchg = -1; /* Don't request passwort change
+					    only because time isn't set yet. */
+	    wroteentry = 1;
+	    D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
+	}
+
+	if (putspent(stmpent, pwfile)) {
+	    D(("error writing entry to shadow file: %m"));
+	    err = 1;
+	    break;
+	}
+
+	stmpent = fgetspent(opwfile);
+    }
+
+    fclose(opwfile);
+
+    if (!wroteentry && !err) {
+	spwdent.sp_namp = forwho;
+	spwdent.sp_pwdp = towhat;
+	spwdent.sp_lstchg = time(NULL) / (60 * 60 * 24);
+	if (spwdent.sp_lstchg == 0)
+	    spwdent.sp_lstchg = -1; /* Don't request passwort change
+				       only because time isn't set yet. */
+	spwdent.sp_min = spwdent.sp_max = spwdent.sp_warn = spwdent.sp_inact =
+	    spwdent.sp_expire = -1;
+	spwdent.sp_flag = (unsigned long)-1l;
+	if (putspent(&spwdent, pwfile)) {
+	    D(("error writing entry to shadow file: %m"));
+	    err = 1;
+	}
+    }
+
+    if (fflush(pwfile) || fsync(fileno(pwfile))) {
+	D(("fflush or fsync error writing entries to shadow file: %m"));
+	err = 1;
+    }
+
+    if (fclose(pwfile)) {
+	D(("fclose error writing entries to shadow file: %m"));
+	err = 1;
+    }
+
+ done:
+    if (!err) {
+	if (!rename(SH_TMPFILE, "/var/lib/extrausers/shadow"))
+	    pam_syslog(pamh,
+		LOG_NOTICE, "password changed for %s", forwho);
+	else
+	    err = 1;
+    }
+
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      if (setfscreatecon(prev_context)) {
+	err = 1;
+      }
+      if (prev_context)
+	freecon(prev_context);
+      prev_context=NULL;
+    }
+#endif
+
+    if (!err) {
+	return PAM_SUCCESS;
+    } else {
+	unlink(SH_TMPFILE);
+	return PAM_AUTHTOK_ERR;
+    }
+}
+
+#ifdef HELPER_COMPILE
+
+int
+helper_verify_password(const char *name, const char *p, int nullok)
+{
+	struct passwd *pwd = NULL;
+	char *salt = NULL;
+	int retval;
+
+	retval = get_pwd_hash(name, &pwd, &salt);
+
+	if (pwd == NULL || salt == NULL) {
+		helper_log_err(LOG_WARNING, "check pass; user unknown");
+		retval = PAM_USER_UNKNOWN;
+	} else {
+		retval = verify_pwd_hash(p, salt, nullok);
+	}
+
+	if (salt) {
+		_pam_overwrite(salt);
+		_pam_drop(salt);
+	}
+
+	p = NULL;		/* no longer needed here */
+
+	return retval;
+}
+
+void
+helper_log_err(int err, const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	openlog(HELPER_COMPILE, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
+	vsyslog(err, format, args);
+	va_end(args);
+	closelog();
+}
+
+static void
+su_sighandler(int sig)
+{
+#ifndef SA_RESETHAND
+        /* emulate the behaviour of the SA_RESETHAND flag */
+        if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV ) {
+		struct sigaction sa;
+		memset(&sa, '\0', sizeof(sa));
+		sa.sa_handler = SIG_DFL;
+                sigaction(sig, &sa, NULL);
+	}
+#endif
+        if (sig > 0) {
+                _exit(sig);
+        }
+}
+
+void
+setup_signals(void)
+{
+        struct sigaction action;        /* posix signal structure */
+
+        /*
+         * Setup signal handlers
+         */
+        (void) memset((void *) &action, 0, sizeof(action));
+        action.sa_handler = su_sighandler;
+#ifdef SA_RESETHAND
+        action.sa_flags = SA_RESETHAND;
+#endif
+        (void) sigaction(SIGILL, &action, NULL);
+        (void) sigaction(SIGTRAP, &action, NULL);
+        (void) sigaction(SIGBUS, &action, NULL);
+        (void) sigaction(SIGSEGV, &action, NULL);
+        action.sa_handler = SIG_IGN;
+        action.sa_flags = 0;
+        (void) sigaction(SIGTERM, &action, NULL);
+        (void) sigaction(SIGHUP, &action, NULL);
+        (void) sigaction(SIGINT, &action, NULL);
+        (void) sigaction(SIGQUIT, &action, NULL);
+}
+
+char *
+getuidname(uid_t uid)
+{
+        struct passwd *pw;
+        static char username[256];
+
+        pw = getpwuid(uid);
+        if (pw == NULL)
+                return NULL;
+
+        strncpy(username, pw->pw_name, sizeof(username));
+        username[sizeof(username) - 1] = '\0';
+
+        return username;
+}
+
+int
+read_passwords(int fd, int npass, char **passwords)
+{
+        int rbytes = 0;
+        int offset = 0;
+        int i = 0;
+        char *pptr;
+        while (npass > 0) {
+                rbytes = read(fd, passwords[i]+offset, MAXPASS-offset);
+
+                if (rbytes < 0) {
+                        if (errno == EINTR) continue;
+                        break;
+                }
+                if (rbytes == 0)
+                        break;
+
+                while (npass > 0 && (pptr=memchr(passwords[i]+offset, '\0', rbytes))
+                        != NULL) {
+                        rbytes -= pptr - (passwords[i]+offset) + 1;
+                        i++;
+                        offset = 0;
+                        npass--;
+                        if (rbytes > 0) {
+                                if (npass > 0)
+                                        memcpy(passwords[i], pptr+1, rbytes);
+                                memset(pptr+1, '\0', rbytes);
+                        }
+                }
+                offset += rbytes;
+        }
+
+        /* clear up */
+        if (offset > 0 && npass > 0) {
+                memset(passwords[i], '\0', offset);
+        }
+
+        return i;
+}
+
+#endif
+/* ****************************************************************** *
+ * Copyright (c) Jan Rêkorajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ * Copyright (c) Red Hat, Inc. 1996, 2007, 2008.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
Index: pam-1.1.8/modules/pam_extrausers/passverify.h
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/passverify.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright information at end of file.
+ */
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <security/pam_modules.h>
+
+#define PAM_UNIX_RUN_HELPER PAM_CRED_INSUFFICIENT
+
+#define MAXPASS		200	/* the maximum length of a password */
+
+#define OLD_PASSWORDS_FILE      "/var/lib/extrausers/opasswd"
+
+int
+verify_pwd_hash(const char *p, char *hash, unsigned int nullok);
+
+int
+is_pwd_shadowed(const struct passwd *pwd);
+
+char *
+crypt_md5_wrapper(const char *pass_new);
+
+int
+unix_selinux_confined(void);
+
+int
+lock_pwdf(void);
+
+void
+unlock_pwdf(void);
+
+#ifdef HELPER_COMPILE
+int
+save_old_password(const char *forwho, const char *oldpass,
+		  int howmany);
+#else
+int
+save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
+		  int howmany);
+#endif
+
+#ifdef HELPER_COMPILE
+void
+helper_log_err(int err, const char *format,...);
+
+int
+helper_verify_password(const char *name, const char *p, int nullok);
+
+void
+setup_signals(void);
+
+char *
+getuidname(uid_t uid);
+
+int
+read_passwords(int fd, int npass, char **passwords);
+#endif
+
+#ifdef HELPER_COMPILE
+#define PAMH_ARG_DECL(fname, ...)	fname(__VA_ARGS__)
+#define PAMH_ARG(...)			__VA_ARGS__
+#else
+#define PAMH_ARG_DECL(fname, ...)	fname(pam_handle_t *pamh, __VA_ARGS__)
+#define PAMH_ARG(...)			pamh, __VA_ARGS__
+#endif
+
+PAMH_ARG_DECL(char * create_password_hash,
+	const char *password, unsigned int ctrl, int rounds);
+
+PAMH_ARG_DECL(int get_account_info,
+	const char *name, struct passwd **pwd, struct spwd **spwdent);
+
+PAMH_ARG_DECL(int get_pwd_hash,
+	const char *name, struct passwd **pwd, char **hash);
+
+PAMH_ARG_DECL(int check_shadow_expiry,
+	struct spwd *spent, int *daysleft);
+
+PAMH_ARG_DECL(int unix_update_passwd,
+	const char *forwho, const char *towhat);
+
+PAMH_ARG_DECL(int unix_update_shadow,
+	const char *forwho, char *towhat);
+
+/* ****************************************************************** *
+ * Copyright (c) Red Hat, Inc. 2007.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
Index: pam-1.1.8/modules/pam_extrausers/support.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/support.c
@@ -0,0 +1,1083 @@
+/*
+ * Copyright information at end of file.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <limits.h>
+#include <utmp.h>
+#include <errno.h>
+#include <signal.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <sys/resource.h>
+#ifdef HAVE_RPCSVC_YPCLNT_H
+#include <rpcsvc/ypclnt.h>
+#endif
+
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+#include <security/pam_modutil.h>
+
+#include "support.h"
+#include "passverify.h"
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#define SELINUX_ENABLED is_selinux_enabled()>0
+#else
+#define SELINUX_ENABLED 0
+#endif
+
+static char *
+search_key (const char *key, const char *filename)
+{
+  FILE *fp;
+  char *buf = NULL;
+  size_t buflen = 0;
+  char *retval = NULL;
+
+  fp = fopen (filename, "r");
+  if (NULL == fp)
+    return NULL;
+
+  while (!feof (fp))
+    {
+      char *tmp, *cp;
+#if defined(HAVE_GETLINE)
+      ssize_t n = getline (&buf, &buflen, fp);
+#elif defined (HAVE_GETDELIM)
+      ssize_t n = getdelim (&buf, &buflen, '\n', fp);
+#else
+      ssize_t n;
+
+      if (buf == NULL)
+        {
+          buflen = BUF_SIZE;
+          buf = malloc (buflen);
+	  if (buf == NULL) {
+	    fclose (fp);
+	    return NULL;
+	  }
+        }
+      buf[0] = '\0';
+      if (fgets (buf, buflen - 1, fp) == NULL)
+        break;
+      else if (buf != NULL)
+        n = strlen (buf);
+      else
+        n = 0;
+#endif /* HAVE_GETLINE / HAVE_GETDELIM */
+      cp = buf;
+
+      if (n < 1)
+        break;
+
+      tmp = strchr (cp, '#');  /* remove comments */
+      if (tmp)
+        *tmp = '\0';
+      while (isspace ((int)*cp))    /* remove spaces and tabs */
+        ++cp;
+      if (*cp == '\0')        /* ignore empty lines */
+        continue;
+
+      if (cp[strlen (cp) - 1] == '\n')
+        cp[strlen (cp) - 1] = '\0';
+
+      tmp = strsep (&cp, " \t=");
+      if (cp != NULL)
+        while (isspace ((int)*cp) || *cp == '=')
+          ++cp;
+
+      if (strcasecmp (tmp, key) == 0)
+        {
+          retval = strdup (cp);
+          break;
+        }
+    }
+  fclose (fp);
+
+  free (buf);
+
+  return retval;
+}
+
+
+/* this is a front-end for module-application conversations */
+
+int _make_remark(pam_handle_t * pamh, unsigned int ctrl,
+		    int type, const char *text)
+{
+	int retval = PAM_SUCCESS;
+
+	if (off(UNIX__QUIET, ctrl)) {
+		retval = pam_prompt(pamh, type, NULL, "%s", text);
+	}
+	return retval;
+}
+
+/*
+ * set the control flags for the UNIX module.
+ */
+
+int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds,
+	      int *pass_min_len, int argc, const char **argv)
+{
+	unsigned int ctrl;
+	char *val;
+	int j;
+
+	D(("called."));
+
+	ctrl = UNIX_DEFAULTS;	/* the default selection of options */
+
+	/* set some flags manually */
+
+	if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
+		D(("IAMROOT"));
+		set(UNIX__IAMROOT, ctrl);
+	}
+	if (flags & PAM_UPDATE_AUTHTOK) {
+		D(("UPDATE_AUTHTOK"));
+		set(UNIX__UPDATE, ctrl);
+	}
+	if (flags & PAM_PRELIM_CHECK) {
+		D(("PRELIM_CHECK"));
+		set(UNIX__PRELIM, ctrl);
+	}
+	if (flags & PAM_SILENT) {
+		D(("SILENT"));
+		set(UNIX__QUIET, ctrl);
+	}
+
+	/* preset encryption method with value from /etc/login.defs */
+	val = search_key ("ENCRYPT_METHOD", LOGIN_DEFS);
+	if (val) {
+	  for (j = 0; j < UNIX_CTRLS_; ++j) {
+	    if (unix_args[j].token && unix_args[j].is_hash_algo
+		&& !strncasecmp(val, unix_args[j].token, strlen(unix_args[j].token))) {
+	      break;
+	    }
+	  }
+	  if (j >= UNIX_CTRLS_) {
+	    pam_syslog(pamh, LOG_WARNING, "unrecognized ENCRYPT_METHOD value [%s]", val);
+	  } else {
+	    ctrl &= unix_args[j].mask;	/* for turning things off */
+	    ctrl |= unix_args[j].flag;	/* for turning things on  */
+	  }
+	  free (val);
+
+	  /* read number of rounds for crypt algo */
+	  if (rounds && (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl))) {
+	    val=search_key ("SHA_CRYPT_MAX_ROUNDS", LOGIN_DEFS);
+
+	    if (val) {
+	      *rounds = strtol(val, NULL, 10);
+	      free (val);
+	    }
+	  }
+	}
+
+	/* now parse the arguments to this module */
+
+	for (; argc-- > 0; ++argv) {
+		int sl;
+
+		D(("pam_extrausers arg: %s", *argv));
+
+		for (j = 0; j < UNIX_CTRLS_; ++j) {
+			if (unix_args[j].token) {
+			    sl = strlen(unix_args[j].token);
+			    if (unix_args[j].token[sl-1] == '=') {
+				/* exclude argument from comparison */
+				if (!strncmp(*argv, unix_args[j].token, sl))
+				    break;
+			    } else {
+				/* compare full strings */
+				if (!strcmp(*argv, unix_args[j].token))
+				    break;
+			    }
+			}
+		}
+
+		if (j >= UNIX_CTRLS_) {
+			pam_syslog(pamh, LOG_ERR,
+			         "unrecognized option [%s]", *argv);
+		} else {
+			/* special cases */
+			if (j == UNIX_REMEMBER_PASSWD) {
+				if (remember == NULL) {
+					pam_syslog(pamh, LOG_ERR,
+					    "option remember not allowed for this module type");
+					continue;
+				}
+				*remember = strtol(*argv + 9, NULL, 10);
+				if ((*remember == INT_MIN) || (*remember == INT_MAX))
+					*remember = -1;
+				if (*remember > 400)
+					*remember = 400;
+			} else if (j == UNIX_MIN_PASS_LEN) {
+				if (pass_min_len == NULL) {
+					pam_syslog(pamh, LOG_ERR,
+					    "option minlen not allowed for this module type");
+					continue;
+				}
+				*pass_min_len = atoi(*argv + 7);
+			} else if (j == UNIX_ALGO_ROUNDS) {
+				if (rounds == NULL) {
+					pam_syslog(pamh, LOG_ERR,
+					    "option rounds not allowed for this module type");
+					continue;
+				}
+				*rounds = strtol(*argv + 7, NULL, 10);
+			}
+
+			ctrl &= unix_args[j].mask;	/* for turning things off */
+			ctrl |= unix_args[j].flag;	/* for turning things on  */
+		}
+	}
+
+	if (UNIX_DES_CRYPT(ctrl)
+	    && pass_min_len && *pass_min_len > 8)
+	  {
+	    pam_syslog (pamh, LOG_NOTICE, "Password minlen reset to 8 characters");
+	    *pass_min_len = 8;
+	  }
+
+	if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
+		D(("DISALLOW_NULL_AUTHTOK"));
+		set(UNIX__NONULL, ctrl);
+	}
+
+	/* Set default rounds for blowfish */
+	if (on(UNIX_BLOWFISH_PASS, ctrl) && off(UNIX_ALGO_ROUNDS, ctrl) && rounds != NULL) {
+		*rounds = 5;
+		set(UNIX_ALGO_ROUNDS, ctrl);
+	}
+
+	/* Enforce sane "rounds" values */
+	if (on(UNIX_ALGO_ROUNDS, ctrl)) {
+		if (on(UNIX_BLOWFISH_PASS, ctrl)) {
+			if (*rounds < 4 || *rounds > 31)
+				*rounds = 5;
+		} else if (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl)) {
+			if ((*rounds < 1000) || (*rounds == INT_MAX))
+				/* don't care about bogus values */
+				unset(UNIX_ALGO_ROUNDS, ctrl);
+			if (*rounds >= 10000000)
+				*rounds = 9999999;
+		}
+	}
+
+	/* auditing is a more sensitive version of debug */
+
+	if (on(UNIX_AUDIT, ctrl)) {
+		set(UNIX_DEBUG, ctrl);
+	}
+	/* return the set of flags */
+
+	D(("done."));
+	return ctrl;
+}
+
+static void _cleanup(pam_handle_t * pamh UNUSED, void *x, int error_status UNUSED)
+{
+	_pam_delete(x);
+}
+
+/* ************************************************************** *
+ * Useful non-trivial functions                                   *
+ * ************************************************************** */
+
+  /*
+   * the following is used to keep track of the number of times a user fails
+   * to authenticate themself.
+   */
+
+#define FAIL_PREFIX                   "-UN*X-FAIL-"
+#define UNIX_MAX_RETRIES              3
+
+struct _pam_failed_auth {
+	char *user;		/* user that's failed to be authenticated */
+	char *name;		/* attempt from user with name */
+	int uid;		/* uid of calling user */
+	int euid;		/* euid of calling process */
+	int count;		/* number of failures so far */
+};
+
+#ifndef PAM_DATA_REPLACE
+#error "Need to get an updated libpam 0.52 or better"
+#endif
+
+static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
+{
+	int quiet;
+	const void *service = NULL;
+	const void *ruser = NULL;
+	const void *rhost = NULL;
+	const void *tty = NULL;
+	struct _pam_failed_auth *failure;
+
+	D(("called"));
+
+	quiet = err & PAM_DATA_SILENT;	/* should we log something? */
+	err &= PAM_DATA_REPLACE;	/* are we just replacing data? */
+	failure = (struct _pam_failed_auth *) fl;
+
+	if (failure != NULL) {
+
+		if (!quiet && !err) {	/* under advisement from Sun,may go away */
+
+			/* log the number of authentication failures */
+			if (failure->count > 1) {
+				(void) pam_get_item(pamh, PAM_SERVICE,
+						    &service);
+				(void) pam_get_item(pamh, PAM_RUSER,
+						    &ruser);
+				(void) pam_get_item(pamh, PAM_RHOST,
+						    &rhost);
+				(void) pam_get_item(pamh, PAM_TTY,
+						    &tty);
+				pam_syslog(pamh, LOG_NOTICE,
+				         "%d more authentication failure%s; "
+				         "logname=%s uid=%d euid=%d "
+				         "tty=%s ruser=%s rhost=%s "
+				         "%s%s",
+				         failure->count - 1, failure->count == 2 ? "" : "s",
+				         failure->name, failure->uid, failure->euid,
+				         tty ? (const char *)tty : "", ruser ? (const char *)ruser : "",
+				         rhost ? (const char *)rhost : "",
+				         (failure->user && failure->user[0] != '\0')
+				          ? " user=" : "", failure->user
+				);
+
+				if (failure->count > UNIX_MAX_RETRIES) {
+					pam_syslog(pamh, LOG_ALERT,
+						 "service(%s) ignoring max retries; %d > %d",
+						 service == NULL ? "**unknown**" : (const char *)service,
+						 failure->count,
+						 UNIX_MAX_RETRIES);
+				}
+			}
+		}
+		_pam_delete(failure->user);	/* tidy up */
+		_pam_delete(failure->name);	/* tidy up */
+		free(failure);
+	}
+}
+
+/*
+ * _unix_getpwnam() searches only /var/lib/extrausers/passwd and NIS to find user information
+ */
+static void _unix_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED)
+{
+	free(data);
+}
+
+int _unix_getpwnam(pam_handle_t *pamh, const char *name,
+		   int files, int nis, struct passwd **ret)
+{
+	FILE *passwd;
+	char buf[16384];
+	int matched = 0, buflen;
+	char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (!matched && files) {
+		int userlen = strlen(name);
+		passwd = fopen("/var/lib/extrausers/passwd", "r");
+		if (passwd != NULL) {
+			while (fgets(buf, sizeof(buf), passwd) != NULL) {
+				if ((buf[userlen] == ':') &&
+				    (strncmp(name, buf, userlen) == 0)) {
+					p = buf + strlen(buf) - 1;
+					while (isspace(*p) && (p >= buf)) {
+						*p-- = '\0';
+					}
+					matched = 1;
+					break;
+				}
+			}
+			fclose(passwd);
+		}
+	}
+
+#if defined(HAVE_YP_GET_DEFAULT_DOMAIN) && defined (HAVE_YP_BIND) && defined (HAVE_YP_MATCH) && defined (HAVE_YP_UNBIND)
+	if (!matched && nis) {
+		char *userinfo = NULL, *domain = NULL;
+		int len = 0, i;
+		len = yp_get_default_domain(&domain);
+		if (len == YPERR_SUCCESS) {
+			len = yp_bind(domain);
+		}
+		if (len == YPERR_SUCCESS) {
+			i = yp_match(domain, "passwd.byname", name,
+				     strlen(name), &userinfo, &len);
+			yp_unbind(domain);
+			if ((i == YPERR_SUCCESS) && ((size_t)len < sizeof(buf))) {
+				strncpy(buf, userinfo, sizeof(buf) - 1);
+				buf[sizeof(buf) - 1] = '\0';
+				matched = 1;
+			}
+		}
+	}
+#else
+	/* we don't have NIS support, make compiler happy. */
+	nis = 0;
+#endif
+
+	if (matched && (ret != NULL)) {
+		*ret = NULL;
+
+		slogin = buf;
+
+		spasswd = strchr(slogin, ':');
+		if (spasswd == NULL) {
+			return matched;
+		}
+		*spasswd++ = '\0';
+
+		suid = strchr(spasswd, ':');
+		if (suid == NULL) {
+			return matched;
+		}
+		*suid++ = '\0';
+
+		sgid = strchr(suid, ':');
+		if (sgid == NULL) {
+			return matched;
+		}
+		*sgid++ = '\0';
+
+		sgecos = strchr(sgid, ':');
+		if (sgecos == NULL) {
+			return matched;
+		}
+		*sgecos++ = '\0';
+
+		shome = strchr(sgecos, ':');
+		if (shome == NULL) {
+			return matched;
+		}
+		*shome++ = '\0';
+
+		sshell = strchr(shome, ':');
+		if (sshell == NULL) {
+			return matched;
+		}
+		*sshell++ = '\0';
+
+		buflen = sizeof(struct passwd) +
+			 strlen(slogin) + 1 +
+			 strlen(spasswd) + 1 +
+			 strlen(sgecos) + 1 +
+			 strlen(shome) + 1 +
+			 strlen(sshell) + 1;
+		*ret = malloc(buflen);
+		if (*ret == NULL) {
+			return matched;
+		}
+		memset(*ret, '\0', buflen);
+
+		(*ret)->pw_uid = strtol(suid, &p, 10);
+		if ((strlen(suid) == 0) || (*p != '\0')) {
+			free(*ret);
+			*ret = NULL;
+			return matched;
+		}
+
+		(*ret)->pw_gid = strtol(sgid, &p, 10);
+		if ((strlen(sgid) == 0) || (*p != '\0')) {
+			free(*ret);
+			*ret = NULL;
+			return matched;
+		}
+
+		p = ((char*)(*ret)) + sizeof(struct passwd);
+		(*ret)->pw_name = strcpy(p, slogin);
+		p += strlen(p) + 1;
+		(*ret)->pw_passwd = strcpy(p, spasswd);
+		p += strlen(p) + 1;
+		(*ret)->pw_gecos = strcpy(p, sgecos);
+		p += strlen(p) + 1;
+		(*ret)->pw_dir = strcpy(p, shome);
+		p += strlen(p) + 1;
+		(*ret)->pw_shell = strcpy(p, sshell);
+
+		snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name);
+
+		if (pam_set_data(pamh, buf,
+				 *ret, _unix_cleanup) != PAM_SUCCESS) {
+			free(*ret);
+			*ret = NULL;
+		}
+	}
+
+	return matched;
+}
+
+/*
+ * _unix_comsefromsource() is a quick check to see if information about a given
+ * user comes from a particular source (just files and nis for now)
+ *
+ */
+int _unix_comesfromsource(pam_handle_t *pamh,
+			  const char *name, int files, int nis)
+{
+	return _unix_getpwnam(pamh, name, files, nis, NULL);
+}
+
+/*
+ * verify the password of a user
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
+				   unsigned int ctrl, const char *user)
+{
+    int retval, child, fds[2];
+    struct sigaction newsa, oldsa;
+
+    D(("called."));
+    /* create a pipe for the password */
+    if (pipe(fds) != 0) {
+	D(("could not make pipe"));
+	return PAM_AUTH_ERR;
+    }
+
+    if (off(UNIX_NOREAP, ctrl)) {
+	/*
+	 * This code arranges that the demise of the child does not cause
+	 * the application to receive a signal it is not expecting - which
+	 * may kill the application or worse.
+	 *
+	 * The "noreap" module argument is provided so that the admin can
+	 * override this behavior.
+	 */
+        memset(&newsa, '\0', sizeof(newsa));
+	newsa.sa_handler = SIG_DFL;
+	sigaction(SIGCHLD, &newsa, &oldsa);
+    }
+
+    /* fork */
+    child = fork();
+    if (child == 0) {
+        int i=0;
+        int nullok = off(UNIX__NONULL, ctrl);
+        struct rlimit rlim;
+	static char *envp[] = { NULL };
+	char *args[] = { NULL, NULL, NULL, NULL };
+
+	/* XXX - should really tidy up PAM here too */
+
+	/* reopen stdin as pipe */
+	dup2(fds[0], STDIN_FILENO);
+
+	if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
+          if (rlim.rlim_max >= MAX_FD_NO)
+                rlim.rlim_max = MAX_FD_NO;
+	  for (i=0; i < (int)rlim.rlim_max; i++) {
+		if (i != STDIN_FILENO)
+		  close(i);
+	  }
+	}
+
+	if (geteuid() == 0) {
+          /* must set the real uid to 0 so the helper will not error
+	     out if pam is called from setuid binary (su, sudo...) */
+	  if (setuid(0) == -1) {
+             D(("setuid failed"));
+	     _exit(PAM_AUTHINFO_UNAVAIL);
+          }
+	}
+
+	/* exec binary helper */
+	args[0] = strdup(CHKPWD_HELPER);
+	args[1] = x_strdup(user);
+
+	if (on(UNIX_NULLOK_SECURE, ctrl)) {
+	    const void *uttyname;
+	    retval = pam_get_item(pamh, PAM_TTY, &uttyname);
+	    if (retval != PAM_SUCCESS || uttyname == NULL
+	        || _pammodutil_tty_secure(pamh, (const char *)uttyname) != PAM_SUCCESS)
+	    {
+	        nullok = 0;
+	    }
+	}
+
+	if (nullok) {
+	  args[2]=strdup("nullok");
+	} else {
+	  args[2]=strdup("nonull");
+	}
+
+	execve(CHKPWD_HELPER, args, envp);
+
+	/* should not get here: exit with error */
+	D(("helper binary is not available"));
+	_exit(PAM_AUTHINFO_UNAVAIL);
+    } else if (child > 0) {
+	/* wait for child */
+	/* if the stored password is NULL */
+        int rc=0;
+	if (passwd != NULL) {            /* send the password to the child */
+	    if (write(fds[1], passwd, strlen(passwd)+1) == -1) {
+	      pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m");
+	      retval = PAM_AUTH_ERR;
+	    }
+	    passwd = NULL;
+	} else {                         /* blank password */
+	    if (write(fds[1], "", 1) == -1) {
+	      pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m");
+	      retval = PAM_AUTH_ERR;
+	    }
+	}
+	close(fds[0]);       /* close here to avoid possible SIGPIPE above */
+	close(fds[1]);
+	/* wait for helper to complete: */
+	while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
+	if (rc<0) {
+	  pam_syslog(pamh, LOG_ERR, "pam_extrausers_chkpwd waitpid returned %d: %m", rc);
+	  retval = PAM_AUTH_ERR;
+	} else if (!WIFEXITED(retval)) {
+	  pam_syslog(pamh, LOG_ERR, "pam_extrausers_chkpwd abnormal exit: %d", retval);
+	  retval = PAM_AUTH_ERR;
+	} else {
+	  retval = WEXITSTATUS(retval);
+	}
+    } else {
+	D(("fork failed"));
+	close(fds[0]);
+	close(fds[1]);
+	retval = PAM_AUTH_ERR;
+    }
+
+    if (off(UNIX_NOREAP, ctrl)) {
+        sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
+    }
+
+    D(("returning %d", retval));
+    return retval;
+}
+
+/*
+ * _unix_blankpasswd() is a quick check for a blank password
+ *
+ * returns TRUE if user does not have a password
+ * - to avoid prompting for one in such cases (CG)
+ */
+
+int
+_unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
+{
+	struct passwd *pwd = NULL;
+	char *salt = NULL;
+	int retval;
+
+	D(("called"));
+
+	/*
+	 * This function does not have to be too smart if something goes
+	 * wrong, return FALSE and let this case to be treated somewhere
+	 * else (CG)
+	 */
+
+	if (on(UNIX__NONULL, ctrl))
+		return 0;	/* will fail but don't let on yet */
+
+	if (on(UNIX_NULLOK_SECURE, ctrl)) {
+		int retval2;
+		const void *uttyname;
+		retval2 = pam_get_item(pamh, PAM_TTY, &uttyname);
+		if (retval2 != PAM_SUCCESS || uttyname == NULL)
+			return 0;
+
+		if (_pammodutil_tty_secure(pamh, (const char *)uttyname) != PAM_SUCCESS)
+			return 0;
+	}
+
+	/* UNIX passwords area */
+
+	retval = get_pwd_hash(pamh, name, &pwd, &salt);
+
+	if (retval == PAM_UNIX_RUN_HELPER) {
+		/* salt will not be set here so we can return immediately */
+		if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS)
+			return 1;
+		else
+			return 0;
+	}
+
+	/* Does this user have a password? */
+	if (salt == NULL) {
+		retval = 0;
+	} else {
+		if (strlen(salt) == 0)
+			retval = 1;
+		else
+			retval = 0;
+	}
+
+	/* tidy up */
+
+	if (salt)
+		_pam_delete(salt);
+
+	return retval;
+}
+
+int _unix_verify_password(pam_handle_t * pamh, const char *name
+			  ,const char *p, unsigned int ctrl)
+{
+	struct passwd *pwd = NULL;
+	char *salt = NULL;
+	char *data_name;
+	int retval;
+
+
+	D(("called"));
+
+#ifdef HAVE_PAM_FAIL_DELAY
+	if (off(UNIX_NODELAY, ctrl)) {
+		D(("setting delay"));
+		(void) pam_fail_delay(pamh, 2000000);	/* 2 sec delay for on failure */
+	}
+#endif
+
+	/* locate the entry for this user */
+
+	D(("locating user's record"));
+
+	retval = get_pwd_hash(pamh, name, &pwd, &salt);
+
+	data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
+	if (data_name == NULL) {
+		pam_syslog(pamh, LOG_CRIT, "no memory for data-name");
+	} else {
+		strcpy(data_name, FAIL_PREFIX);
+		strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
+	}
+
+	if (retval != PAM_SUCCESS) {
+		if (retval == PAM_UNIX_RUN_HELPER) {
+			D(("running helper binary"));
+			retval = _unix_run_helper_binary(pamh, p, ctrl, name);
+		} else {
+			D(("user's record unavailable"));
+			p = NULL;
+			if (on(UNIX_AUDIT, ctrl)) {
+				/* this might be a typo and the user has given a password
+				   instead of a username. Careful with this. */
+				pam_syslog(pamh, LOG_WARNING,
+				         "check pass; user (%s) unknown", name);
+			} else {
+				name = NULL;
+				if (on(UNIX_DEBUG, ctrl) || pwd == NULL) {
+				    pam_syslog(pamh, LOG_WARNING,
+				            "check pass; user unknown");
+				} else {
+				    /* don't log failure as another pam module can succeed */
+				    goto cleanup;
+				}
+			}
+		}
+	} else {
+		retval = verify_pwd_hash(p, salt,
+		                         _unix_blankpasswd(pamh, ctrl, name));
+	}
+
+	if (retval == PAM_SUCCESS) {
+		if (data_name)	/* reset failures */
+			pam_set_data(pamh, data_name, NULL, _cleanup_failures);
+	} else {
+		if (data_name != NULL) {
+			struct _pam_failed_auth *new = NULL;
+			const struct _pam_failed_auth *old = NULL;
+
+			/* get a failure recorder */
+
+			new = (struct _pam_failed_auth *)
+			    malloc(sizeof(struct _pam_failed_auth));
+
+			if (new != NULL) {
+
+			    const char *login_name;
+			    const void *void_old;
+
+
+			    login_name = pam_modutil_getlogin(pamh);
+			    if (login_name == NULL) {
+				login_name = "";
+			    }
+
+			        new->user = x_strdup(name ? name : "");
+				new->uid = getuid();
+				new->euid = geteuid();
+				new->name = x_strdup(login_name);
+
+				/* any previous failures for this user ? */
+				if (pam_get_data(pamh, data_name, &void_old)
+				    == PAM_SUCCESS)
+				        old = void_old;
+				else
+				        old = NULL;
+
+				if (old != NULL) {
+					new->count = old->count + 1;
+					if (new->count >= UNIX_MAX_RETRIES) {
+						retval = PAM_MAXTRIES;
+					}
+				} else {
+					const void *service=NULL;
+					const void *ruser=NULL;
+					const void *rhost=NULL;
+					const void *tty=NULL;
+
+					(void) pam_get_item(pamh, PAM_SERVICE,
+							    &service);
+					(void) pam_get_item(pamh, PAM_RUSER,
+							    &ruser);
+					(void) pam_get_item(pamh, PAM_RHOST,
+							    &rhost);
+					(void) pam_get_item(pamh, PAM_TTY,
+							    &tty);
+
+					pam_syslog(pamh, LOG_NOTICE,
+					         "authentication failure; "
+					         "logname=%s uid=%d euid=%d "
+					         "tty=%s ruser=%s rhost=%s "
+					         "%s%s",
+					         new->name, new->uid, new->euid,
+					         tty ? (const char *)tty : "",
+					         ruser ? (const char *)ruser : "",
+					         rhost ? (const char *)rhost : "",
+					         (new->user && new->user[0] != '\0')
+					          ? " user=" : "",
+					         new->user
+					);
+					new->count = 1;
+				}
+
+				pam_set_data(pamh, data_name, new, _cleanup_failures);
+
+			} else {
+				pam_syslog(pamh, LOG_CRIT,
+				         "no memory for failure recorder");
+			}
+		}
+	}
+
+cleanup:
+	if (data_name)
+		_pam_delete(data_name);
+	if (salt)
+		_pam_delete(salt);
+
+	D(("done [%d].", retval));
+
+	return retval;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+int _unix_read_password(pam_handle_t * pamh
+			,unsigned int ctrl
+			,const char *comment
+			,const char *prompt1
+			,const char *prompt2
+			,const char *data_name
+			,const void **pass)
+{
+	int authtok_flag;
+	int retval = PAM_SUCCESS;
+	char *token;
+
+	D(("called"));
+
+	/*
+	 * make sure nothing inappropriate gets returned
+	 */
+
+	*pass = token = NULL;
+
+	/*
+	 * which authentication token are we getting?
+	 */
+
+	authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
+
+	/*
+	 * should we obtain the password from a PAM item ?
+	 */
+
+	if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
+		retval = pam_get_item(pamh, authtok_flag, pass);
+		if (retval != PAM_SUCCESS) {
+			/* very strange. */
+			pam_syslog(pamh, LOG_ALERT,
+				 "pam_get_item returned error to unix-read-password"
+			    );
+			return retval;
+		} else if (*pass != NULL) {	/* we have a password! */
+			return PAM_SUCCESS;
+		} else if (on(UNIX_USE_AUTHTOK, ctrl)
+			   && off(UNIX__OLD_PASSWD, ctrl)) {
+			return PAM_AUTHTOK_ERR;
+		} else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
+			return PAM_AUTHTOK_RECOVERY_ERR;	  /* didn't work */
+		}
+	}
+	/*
+	 * getting here implies we will have to get the password from the
+	 * user directly.
+	 */
+
+	{
+		int replies=1;
+		char *resp[2] = { NULL, NULL };
+
+		if (comment != NULL && off(UNIX__QUIET, ctrl)) {
+			retval = pam_info(pamh, "%s", comment);
+		}
+
+		if (retval == PAM_SUCCESS) {
+			retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
+			    &resp[0], "%s", prompt1);
+
+			if (retval == PAM_SUCCESS && prompt2 != NULL) {
+				retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
+				    &resp[1], "%s", prompt2);
+				++replies;
+			}
+		}
+
+		if (resp[0] != NULL && resp[replies-1] != NULL) {
+			/* interpret the response */
+
+			if (retval == PAM_SUCCESS) {	/* a good conversation */
+
+				token = resp[0];
+				if (token != NULL) {
+					if (replies == 2) {
+						/* verify that password entered correctly */
+						if (strcmp(token, resp[replies - 1])) {
+							/* mistyped */
+							retval = PAM_AUTHTOK_RECOVERY_ERR;
+							_make_remark(pamh, ctrl,
+							    PAM_ERROR_MSG, MISTYPED_PASS);
+						}
+					}
+				} else {
+					pam_syslog(pamh, LOG_NOTICE,
+						    "could not recover authentication token");
+				}
+
+			}
+
+		} else {
+			retval = (retval == PAM_SUCCESS)
+			    ? PAM_AUTHTOK_RECOVERY_ERR : retval;
+		}
+
+		resp[0] = NULL;
+		if (replies > 1)
+			_pam_delete(resp[1]);
+	}
+
+	if (retval != PAM_SUCCESS) {
+		_pam_delete(token);
+
+		if (on(UNIX_DEBUG, ctrl))
+			pam_syslog(pamh, LOG_DEBUG,
+			         "unable to obtain a password");
+		return retval;
+	}
+	/* 'token' is the entered password */
+
+	if (off(UNIX_NOT_SET_PASS, ctrl)) {
+
+		/* we store this password as an item */
+
+		retval = pam_set_item(pamh, authtok_flag, token);
+		_pam_delete(token);	/* clean it up */
+		if (retval != PAM_SUCCESS
+		    || (retval = pam_get_item(pamh, authtok_flag, pass))
+		    != PAM_SUCCESS) {
+
+			*pass = NULL;
+			pam_syslog(pamh, LOG_CRIT, "error manipulating password");
+			return retval;
+
+		}
+	} else {
+		/*
+		 * then store it as data specific to this module. pam_end()
+		 * will arrange to clean it up.
+		 */
+
+		retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
+		if (retval != PAM_SUCCESS) {
+			pam_syslog(pamh, LOG_CRIT,
+			         "error manipulating password data [%s]",
+				 pam_strerror(pamh, retval));
+			_pam_delete(token);
+			return retval;
+		}
+		*pass = token;
+		token = NULL;	/* break link to password */
+	}
+
+	return PAM_SUCCESS;
+}
+
+/* ****************************************************************** *
+ * Copyright (c) Jan Rêkorajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ * Copyright (c) Red Hat, Inc. 2007.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
Index: pam-1.1.8/modules/pam_extrausers/support.h
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/support.h
@@ -0,0 +1,182 @@
+/*
+ * $Id$
+ */
+
+#ifndef _PAM_UNIX_SUPPORT_H
+#define _PAM_UNIX_SUPPORT_H
+
+#include <pwd.h>
+
+/*
+ * File to read value of ENCRYPT_METHOD from.
+ */
+#define LOGIN_DEFS "/etc/login.defs"
+
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+/* type definition for the control options */
+
+typedef struct {
+	const char *token;
+	unsigned int mask;	/* shall assume 32 bits of flags */
+	unsigned int flag;
+        unsigned int is_hash_algo;
+} UNIX_Ctrls;
+
+/*
+ * macro to determine if a given flag is on
+ */
+
+#define on(x,ctrl)  (unix_args[x].flag & ctrl)
+
+/*
+ * macro to determine that a given flag is NOT on
+ */
+
+#define off(x,ctrl) (!on(x,ctrl))
+
+/*
+ * macro to turn on/off a ctrl flag manually
+ */
+
+#define set(x,ctrl)   (ctrl = ((ctrl)&unix_args[x].mask)|unix_args[x].flag)
+#define unset(x,ctrl) (ctrl &= ~(unix_args[x].flag))
+
+/* the generic mask */
+
+#define _ALL_ON_  (~0U)
+
+/* end of macro definitions definitions for the control flags */
+
+/* ****************************************************************** *
+ * ctrl flags proper..
+ */
+
+/*
+ * here are the various options recognized by the unix module. They
+ * are enumerated here and then defined below. Internal arguments are
+ * given NULL tokens.
+ */
+
+#define UNIX__OLD_PASSWD          0	/* internal */
+#define UNIX__VERIFY_PASSWD       1	/* internal */
+#define UNIX__IAMROOT             2	/* internal */
+
+#define UNIX_AUDIT                3	/* print more things than debug..
+					   some information may be sensitive */
+#define UNIX_USE_FIRST_PASS       4
+#define UNIX_TRY_FIRST_PASS       5
+#define UNIX_NOT_SET_PASS         6	/* don't set the AUTHTOK items */
+
+#define UNIX__PRELIM              7	/* internal */
+#define UNIX__UPDATE              8	/* internal */
+#define UNIX__NONULL              9	/* internal */
+#define UNIX__QUIET              10	/* internal */
+#define UNIX_USE_AUTHTOK         11	/* insist on reading PAM_AUTHTOK */
+#define UNIX_SHADOW              12	/* signal shadow on */
+#define UNIX_MD5_PASS            13	/* force the use of MD5 passwords */
+#define UNIX__NULLOK             14	/* Null token ok */
+#define UNIX_DEBUG               15	/* send more info to syslog(3) */
+#define UNIX_NODELAY             16	/* admin does not want a fail-delay */
+#define UNIX_NIS                 17	/* wish to use NIS for pwd */
+#define UNIX_BIGCRYPT            18	/* use DEC-C2 crypt()^x function */
+#define UNIX_LIKE_AUTH           19	/* need to auth for setcred to work */
+#define UNIX_REMEMBER_PASSWD     20	/* Remember N previous passwords */
+#define UNIX_NOREAP              21     /* don't reap child process */
+#define UNIX_BROKEN_SHADOW       22     /* ignore errors reading password aging
+					 * information during acct management */
+#define UNIX_SHA256_PASS         23	/* new password hashes will use SHA256 */
+#define UNIX_SHA512_PASS         24	/* new password hashes will use SHA512 */
+#define UNIX_ALGO_ROUNDS         25	/* optional number of rounds for new
+					   password hash algorithms */
+#define UNIX_BLOWFISH_PASS       26	/* new password hashes will use blowfish */
+#define UNIX_MIN_PASS_LEN        27	/* min length for password */
+#define UNIX_OBSCURE_CHECKS      28	/* enable obscure checks on passwords */
+#define UNIX_NULLOK_SECURE       29	/* NULL passwords allowed only on secure ttys */
+/* -------------- */
+#define UNIX_CTRLS_              30	/* number of ctrl arguments defined */
+
+#define UNIX_DES_CRYPT(ctrl)	(off(UNIX_MD5_PASS,ctrl)&&off(UNIX_BIGCRYPT,ctrl)&&off(UNIX_SHA256_PASS,ctrl)&&off(UNIX_SHA512_PASS,ctrl)&&off(UNIX_BLOWFISH_PASS,ctrl))
+
+static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
+{
+/* symbol                  token name          ctrl mask             ctrl     *
+ * ----------------------- ------------------- --------------------- -------- */
+
+/* UNIX__OLD_PASSWD */     {NULL,              _ALL_ON_,                  0x1, 0},
+/* UNIX__VERIFY_PASSWD */  {NULL,              _ALL_ON_,                  0x2, 0},
+/* UNIX__IAMROOT */        {NULL,              _ALL_ON_,                  0x4, 0},
+/* UNIX_AUDIT */           {"audit",           _ALL_ON_,                  0x8, 0},
+/* UNIX_USE_FIRST_PASS */  {"use_first_pass",  _ALL_ON_^(0x30),          0x10, 0},
+/* UNIX_TRY_FIRST_PASS */  {"try_first_pass",  _ALL_ON_^(0x30),          0x20, 0},
+/* UNIX_NOT_SET_PASS */    {"not_set_pass",    _ALL_ON_,                 0x40, 0},
+/* UNIX__PRELIM */         {NULL,              _ALL_ON_^(0x180),         0x80, 0},
+/* UNIX__UPDATE */         {NULL,              _ALL_ON_^(0x180),        0x100, 0},
+/* UNIX__NONULL */         {NULL,              _ALL_ON_^(0x10000000),   0x200, 0},
+/* UNIX__QUIET */          {NULL,              _ALL_ON_,                0x400, 0},
+/* UNIX_USE_AUTHTOK */     {"use_authtok",     _ALL_ON_,                0x800, 0},
+/* UNIX_SHADOW */          {"shadow",          _ALL_ON_,               0x1000, 0},
+/* UNIX_MD5_PASS */        {"md5",             _ALL_ON_^(0x2C22000),   0x2000, 1},
+/* UNIX__NULLOK */         {"nullok",          _ALL_ON_^(0x200),            0, 0},
+/* UNIX_DEBUG */           {"debug",           _ALL_ON_,               0x4000, 0},
+/* UNIX_NODELAY */         {"nodelay",         _ALL_ON_,               0x8000, 0},
+/* UNIX_NIS */             {"nis",             _ALL_ON_,              0x10000, 0},
+/* UNIX_BIGCRYPT */        {"bigcrypt",        _ALL_ON_^(0x2C22000),  0x20000, 1},
+/* UNIX_LIKE_AUTH */       {"likeauth",        _ALL_ON_,              0x40000, 0},
+/* UNIX_REMEMBER_PASSWD */ {"remember=",       _ALL_ON_,              0x80000, 0},
+/* UNIX_NOREAP */          {"noreap",          _ALL_ON_,             0x100000, 0},
+/* UNIX_BROKEN_SHADOW */   {"broken_shadow",   _ALL_ON_,             0x200000, 0},
+/* UNIX_SHA256_PASS */     {"sha256",          _ALL_ON_^(0x2C22000), 0x400000, 1},
+/* UNIX_SHA512_PASS */     {"sha512",          _ALL_ON_^(0x2C22000), 0x800000, 1},
+/* UNIX_ALGO_ROUNDS */     {"rounds=",         _ALL_ON_,            0x1000000, 0},
+/* UNIX_BLOWFISH_PASS */   {"blowfish",        _ALL_ON_^(0x2C22000),0x2000000, 1},
+/* UNIX_MIN_PASS_LEN */    {"minlen=",         _ALL_ON_,            0x4000000, 0},
+/* UNIX_OBSCURE_CHECKS */  {"obscure",         _ALL_ON_,            0x8000000, 0},
+/* UNIX_NULLOK_SECURE */   {"nullok_secure",   _ALL_ON_^(0x200),   0x10000000, 0},
+};
+
+#define UNIX_DEFAULTS  (unix_args[UNIX__NONULL].flag)
+
+#define MAX_FD_NO 2000000
+
+/* use this to free strings. ESPECIALLY password strings */
+
+#define _pam_delete(xx)		\
+{				\
+	_pam_overwrite(xx);	\
+	_pam_drop(xx);		\
+}
+
+extern int _make_remark(pam_handle_t * pamh, unsigned int ctrl
+		       ,int type, const char *text);
+extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int *rounds,
+		     int *pass_min_len, int argc, const char **argv);
+extern int _unix_getpwnam (pam_handle_t *pamh,
+			   const char *name, int files, int nis,
+			   struct passwd **ret);
+extern int _unix_comesfromsource (pam_handle_t *pamh,
+				  const char *name, int files, int nis);
+extern int _unix_blankpasswd(pam_handle_t *pamh,unsigned int ctrl,
+			     const char *name);
+extern int _unix_verify_password(pam_handle_t * pamh, const char *name
+			  ,const char *p, unsigned int ctrl);
+extern int _unix_read_password(pam_handle_t * pamh
+			,unsigned int ctrl
+			,const char *comment
+			,const char *prompt1
+			,const char *prompt2
+			,const char *data_name
+			,const void **pass);
+
+extern int _pammodutil_tty_secure(const pam_handle_t *pamh,
+                                  const char *uttyname);
+
+extern int _unix_run_verify_binary(pam_handle_t *pamh,
+			unsigned int ctrl, const char *user, int *daysleft);
+#endif /* _PAM_UNIX_SUPPORT_H */
Index: pam-1.1.8/modules/pam_extrausers/unix_chkpwd.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/unix_chkpwd.c
@@ -0,0 +1,239 @@
+/*
+ * This program is designed to run setuid(root) or with sufficient
+ * privilege to read all of the unix password databases. It is designed
+ * to provide a mechanism for the current user (defined by this
+ * process' uid) to verify their own password.
+ *
+ * The password is read from the standard input. The exit status of
+ * this program indicates whether the user is authenticated or not.
+ *
+ * Copyright information is located at the end of the file.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#ifdef HAVE_LIBAUDIT
+#include <libaudit.h>
+#endif
+
+#include <security/_pam_types.h>
+#include <security/_pam_macros.h>
+
+#include "passverify.h"
+
+static int _check_expiry(const char *uname)
+{
+	struct spwd *spent;
+	struct passwd *pwent;
+	int retval;
+	int daysleft;
+
+	retval = get_account_info(uname, &pwent, &spent);
+	if (retval != PAM_SUCCESS) {
+		helper_log_err(LOG_ALERT, "could not obtain user info (%s)", uname);
+		printf("-1\n");
+		return retval;
+	}
+
+	if (spent == NULL) {
+		printf("-1\n");
+		return retval;
+	}
+
+	retval = check_shadow_expiry(spent, &daysleft);
+	printf("%d\n", daysleft);
+	return retval;
+}
+
+#ifdef HAVE_LIBAUDIT
+static int _audit_log(int type, const char *uname, int rc)
+{
+	int audit_fd;
+
+	audit_fd = audit_open();
+	if (audit_fd < 0) {
+		/* You get these error codes only when the kernel doesn't have
+		 * audit compiled in. */
+		if (errno == EINVAL || errno == EPROTONOSUPPORT ||
+			errno == EAFNOSUPPORT)
+			return PAM_SUCCESS;
+
+		helper_log_err(LOG_CRIT, "audit_open() failed: %m");
+		return PAM_AUTH_ERR;
+	}
+
+	rc = audit_log_acct_message(audit_fd, type, NULL, "PAM:pam_extrausers_chkpwd",
+		uname, -1, NULL, NULL, NULL, rc == PAM_SUCCESS);
+	if (rc == -EPERM && geteuid() != 0) {
+		rc = 0;
+	}
+
+	audit_close(audit_fd);
+
+	return rc < 0 ? PAM_AUTH_ERR : PAM_SUCCESS;
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+	char pass[MAXPASS + 1];
+	char *option;
+	int npass, nullok;
+	int blankpass = 0;
+	int retval = PAM_AUTH_ERR;
+	char *user;
+	char *passwords[] = { pass };
+
+	/*
+	 * Catch or ignore as many signal as possible.
+	 */
+	setup_signals();
+
+	/*
+	 * we establish that this program is running with non-tty stdin.
+	 * this is to discourage casual use. It does *NOT* prevent an
+	 * intruder from repeatadly running this program to determine the
+	 * password of the current user (brute force attack, but one for
+	 * which the attacker must already have gained access to the user's
+	 * account).
+	 */
+
+	if (isatty(STDIN_FILENO) || argc != 3 ) {
+		helper_log_err(LOG_NOTICE
+		      ,"inappropriate use of Unix helper binary [UID=%d]"
+			 ,getuid());
+#ifdef HAVE_LIBAUDIT
+		_audit_log(AUDIT_ANOM_EXEC, getuidname(getuid()), PAM_SYSTEM_ERR);
+#endif
+		fprintf(stderr
+		 ,"This binary is not designed for running in this way\n"
+		      "-- the system administrator has been informed\n");
+		sleep(10);	/* this should discourage/annoy the user */
+		return PAM_SYSTEM_ERR;
+	}
+
+	/*
+	 * Determine what the current user's name is.
+	 * We must thus skip the check if the real uid is 0.
+	 */
+	if (getuid() == 0) {
+	  user=argv[1];
+	}
+	else {
+	  user = getuidname(getuid());
+	  /* if the caller specifies the username, verify that user
+	     matches it */
+	  if (strcmp(user, argv[1])) {
+	    gid_t gid = getgid();
+	    user = argv[1];
+	    /* no match -> permanently change to the real user and proceed */
+	    if (setresgid(gid, gid, gid) != 0 || setuid(getuid()) != 0)
+		return PAM_AUTH_ERR;
+	  }
+	}
+
+	option=argv[2];
+
+	if (strcmp(option, "chkexpiry") == 0)
+	  /* Check account information from the shadow file */
+	  return _check_expiry(argv[1]);
+	/* read the nullok/nonull option */
+	else if (strcmp(option, "nullok") == 0)
+	  nullok = 1;
+	else if (strcmp(option, "nonull") == 0)
+	  nullok = 0;
+	else {
+#ifdef HAVE_LIBAUDIT
+	  _audit_log(AUDIT_ANOM_EXEC, getuidname(getuid()), PAM_SYSTEM_ERR);
+#endif
+	  return PAM_SYSTEM_ERR;
+	}
+	/* read the password from stdin (a pipe from the pam_unix module) */
+
+	npass = read_passwords(STDIN_FILENO, 1, passwords);
+
+	if (npass != 1) {	/* is it a valid password? */
+		helper_log_err(LOG_DEBUG, "no password supplied");
+		*pass = '\0';
+	}
+
+	if (*pass == '\0') {
+		blankpass = 1;
+	}
+
+	retval = helper_verify_password(user, pass, nullok);
+
+	memset(pass, '\0', MAXPASS);	/* clear memory of the password */
+
+	/* return pass or fail */
+
+	if (retval != PAM_SUCCESS) {
+		if (!nullok || !blankpass) {
+			/* no need to log blank pass test */
+#ifdef HAVE_LIBAUDIT
+			if (getuid() != 0)
+				_audit_log(AUDIT_USER_AUTH, user, PAM_AUTH_ERR);
+#endif
+			helper_log_err(LOG_NOTICE, "password check failed for user (%s)", user);
+		}
+		return PAM_AUTH_ERR;
+	} else {
+	        if (getuid() != 0) {
+#ifdef HAVE_LIBAUDIT
+			return _audit_log(AUDIT_USER_AUTH, user, PAM_SUCCESS);
+#else
+		        return PAM_SUCCESS;
+#endif
+	        }
+		return PAM_SUCCESS;
+	}
+}
+
+/*
+ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
+ * Copyright (c) Red Hat, Inc., 2007,2008. All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
Index: pam-1.1.8/modules/pam_extrausers/unix_update.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/unix_update.c
@@ -0,0 +1,191 @@
+/*
+ * This program is designed to run with sufficient privilege
+ * to read and write all of the unix password databases.
+ * Its purpose is to allow updating the databases when
+ * SELinux confinement of the caller domain prevents them to
+ * do that themselves.
+ *
+ * The password is read from the standard input. The exit status of
+ * this program indicates whether the password was updated or not.
+ *
+ * Copyright information is located at the end of the file.
+ *
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <security/_pam_types.h>
+#include <security/_pam_macros.h>
+
+#include "passverify.h"
+
+static int
+set_password(const char *forwho, const char *shadow, const char *remember)
+{
+    struct passwd *pwd = NULL;
+    int retval;
+    char pass[MAXPASS + 1];
+    char towhat[MAXPASS + 1];
+    int npass = 0;
+    /* we don't care about number format errors because the helper
+       should be called internally only */
+    int doshadow = atoi(shadow);
+    int nremember = atoi(remember);
+    char *passwords[] = { pass, towhat };
+
+    /* read the password from stdin (a pipe from the pam_unix module) */
+
+    npass = read_passwords(STDIN_FILENO, 2, passwords);
+
+    if (npass != 2) {	/* is it a valid password? */
+      if (npass == 1) {
+        helper_log_err(LOG_DEBUG, "no new password supplied");
+	memset(pass, '\0', MAXPASS);
+      } else {
+        helper_log_err(LOG_DEBUG, "no valid passwords supplied");
+      }
+      return PAM_AUTHTOK_ERR;
+    }
+
+    if (lock_pwdf() != PAM_SUCCESS)
+	return PAM_AUTHTOK_LOCK_BUSY;
+
+    pwd = getpwnam(forwho);
+
+    if (pwd == NULL) {
+        retval = PAM_USER_UNKNOWN;
+        goto done;
+    }
+
+    /* If real caller uid is not root we must verify that
+       received old pass agrees with the current one.
+       We always allow change from null pass. */
+    if (getuid()) {
+	retval = helper_verify_password(forwho, pass, 1);
+	if (retval != PAM_SUCCESS) {
+	    goto done;
+	}
+    }
+
+    /* first, save old password */
+    if (save_old_password(forwho, pass, nremember)) {
+	retval = PAM_AUTHTOK_ERR;
+	goto done;
+    }
+
+    if (doshadow || is_pwd_shadowed(pwd)) {
+	retval = unix_update_shadow(forwho, towhat);
+	if (retval == PAM_SUCCESS)
+	    if (!is_pwd_shadowed(pwd))
+		retval = unix_update_passwd(forwho, "x");
+    } else {
+	retval = unix_update_passwd(forwho, towhat);
+    }
+
+done:
+    memset(pass, '\0', MAXPASS);
+    memset(towhat, '\0', MAXPASS);
+
+    unlock_pwdf();
+
+    if (retval == PAM_SUCCESS) {
+	return PAM_SUCCESS;
+    } else {
+	return PAM_AUTHTOK_ERR;
+    }
+}
+
+int main(int argc, char *argv[])
+{
+	char *option;
+
+	/*
+	 * Catch or ignore as many signal as possible.
+	 */
+	setup_signals();
+
+	/*
+	 * we establish that this program is running with non-tty stdin.
+	 * this is to discourage casual use. It does *NOT* prevent an
+	 * intruder from repeatadly running this program to determine the
+	 * password of the current user (brute force attack, but one for
+	 * which the attacker must already have gained access to the user's
+	 * account).
+	 */
+
+	if (isatty(STDIN_FILENO) || argc != 5 ) {
+		helper_log_err(LOG_NOTICE
+		      ,"inappropriate use of Unix helper binary [UID=%d]"
+			 ,getuid());
+		fprintf(stderr
+		 ,"This binary is not designed for running in this way\n"
+		      "-- the system administrator has been informed\n");
+		sleep(10);	/* this should discourage/annoy the user */
+		return PAM_SYSTEM_ERR;
+	}
+
+	/* We must be root to read/update shadow.
+	 */
+	if (geteuid() != 0) {
+	    return PAM_CRED_INSUFFICIENT;
+	}
+
+	option = argv[2];
+
+	if (strcmp(option, "update") == 0) {
+	    /* Attempting to change the password */
+	    return set_password(argv[1], argv[3], argv[4]);
+	}
+
+	return PAM_SYSTEM_ERR;
+}
+
+/*
+ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
+ * Copyright (c) Red Hat, Inc., 2007, 2008. All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
Index: pam-1.1.8/modules/pam_extrausers/yppasswd.h
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/yppasswd.h
@@ -0,0 +1,51 @@
+/*
+ * yppasswdd
+ * Copyright 1994, 1995, 1996 Olaf Kirch, <okir@lst.de>
+ *
+ * This program is covered by the GNU General Public License, version 2
+ * or later. It is provided in the hope that it is useful. However, the author
+ * disclaims ALL WARRANTIES, expressed or implied. See the GPL for details.
+ *
+ * This file was generated automatically by rpcgen from yppasswd.x, and
+ * editied manually.
+ */
+
+#ifndef _YPPASSWD_H_
+#define _YPPASSWD_H_
+
+#define YPPASSWDPROG ((u_long)100009)
+#define YPPASSWDVERS ((u_long)1)
+#define YPPASSWDPROC_UPDATE ((u_long)1)
+
+/*
+ * The password struct passed by the update call. I renamed it to
+ * xpasswd to avoid a type clash with the one defined in <pwd.h>.
+ */
+#ifndef __sgi
+typedef struct xpasswd {
+	char *pw_name;
+	char *pw_passwd;
+	int pw_uid;
+	int pw_gid;
+	char *pw_gecos;
+	char *pw_dir;
+	char *pw_shell;
+} xpasswd;
+
+#else
+#include <pwd.h>
+typedef struct xpasswd xpasswd;
+#endif
+
+/* The updated password information, plus the old password.
+ */
+typedef struct yppasswd {
+	char *oldpass;
+	xpasswd newpw;
+} yppasswd;
+
+/* XDR encoding/decoding routines */
+bool_t xdr_xpasswd(XDR * xdrs, xpasswd * objp);
+bool_t xdr_yppasswd(XDR * xdrs, yppasswd * objp);
+
+#endif	/* _YPPASSWD_H_ */
Index: pam-1.1.8/modules/pam_extrausers/yppasswd_xdr.c
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/yppasswd_xdr.c
@@ -0,0 +1,40 @@
+/*
+ * yppasswdd
+ * Copyright 1994, 1995, 1996 Olaf Kirch, <okir@lst.de>
+ *
+ * This program is covered by the GNU General Public License, version 2
+ * or later. It is provided in the hope that it is useful. However, the author
+ * disclaims ALL WARRANTIES, expressed or implied. See the GPL for details.
+ *
+ * This file was generated automatically by rpcgen from yppasswd.x, and
+ * editied manually.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_RPC_RPC_H
+
+#include <rpc/rpc.h>
+#include "yppasswd.h"
+
+bool_t
+xdr_xpasswd(XDR * xdrs, xpasswd * objp)
+{
+	return xdr_string(xdrs, &objp->pw_name, ~0)
+	    && xdr_string(xdrs, &objp->pw_passwd, ~0)
+	    && xdr_int(xdrs, &objp->pw_uid)
+	    && xdr_int(xdrs, &objp->pw_gid)
+	    && xdr_string(xdrs, &objp->pw_gecos, ~0)
+	    && xdr_string(xdrs, &objp->pw_dir, ~0)
+	    && xdr_string(xdrs, &objp->pw_shell, ~0);
+}
+
+
+bool_t
+xdr_yppasswd(XDR * xdrs, yppasswd * objp)
+{
+	return xdr_string(xdrs, &objp->oldpass, ~0)
+	    && xdr_xpasswd(xdrs, &objp->newpw);
+}
+
+#endif
Index: pam-1.1.8/modules/Makefile.am
===================================================================
--- pam-1.1.8.orig/modules/Makefile.am
+++ pam-1.1.8/modules/Makefile.am
@@ -3,7 +3,7 @@
 #
 
 SUBDIRS = pam_access pam_cracklib pam_debug pam_deny pam_echo \
-	pam_env pam_exec pam_faildelay pam_filter pam_ftp \
+	pam_env pam_exec pam_extrausers pam_faildelay pam_filter pam_ftp \
 	pam_group pam_issue pam_keyinit pam_lastlog pam_limits \
 	pam_listfile pam_localuser pam_loginuid pam_mail \
 	pam_mkhomedir pam_motd pam_namespace pam_nologin \
Index: pam-1.1.8/configure.in
===================================================================
--- pam-1.1.8.orig/configure.in
+++ pam-1.1.8/configure.in
@@ -607,6 +607,7 @@ AC_CONFIG_FILES([Makefile libpam/Makefil
 	modules/pam_access/Makefile modules/pam_cracklib/Makefile \
         modules/pam_debug/Makefile modules/pam_deny/Makefile \
 	modules/pam_echo/Makefile modules/pam_env/Makefile \
+	modules/pam_extrausers/Makefile \
 	modules/pam_faildelay/Makefile \
 	modules/pam_filter/Makefile modules/pam_filter/upperLOWER/Makefile \
 	modules/pam_ftp/Makefile modules/pam_group/Makefile \
Index: pam-1.1.8/modules/pam_extrausers/tst-pam_extrausers
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/tst-pam_extrausers
@@ -0,0 +1,2 @@
+#!/bin/sh
+../../tests/tst-dlopen .libs/pam_extrausers.so
Index: pam-1.1.8/modules/pam_extrausers/pam_extrausers.8.xml
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/pam_extrausers.8.xml
@@ -0,0 +1,488 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+	"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+
+<refentry id="pam_extrausers">
+
+  <refmeta>
+    <refentrytitle>pam_extrausers</refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="pam_extrausers-name">
+    <refname>pam_extrausers</refname>
+    <refpurpose>Module for libnss-extrausers authentication</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis id="pam_extrausers-cmdsynopsis">
+      <command>pam_extrausers.so</command>
+      <arg choice="opt">
+        ...
+      </arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="pam_extrausers-description">
+
+    <title>DESCRIPTION</title>
+
+    <para>
+      This is similar to the standard Unix authentication module pam_unix.
+      But instead of using /etc/passwd and /etc/shadow, it uses
+      /var/lib/extrausers/passwd and /var/lib/extrausers/shadow.
+    </para>
+
+    <para>
+      The account component performs the task of establishing the status
+      of the user's account and password based on the following
+      <emphasis>shadow</emphasis> elements: expire, last_change, max_change,
+      min_change, warn_change. In the case of the latter, it may offer advice
+      to the user on changing their password or, through the
+      <emphasis remap='B'>PAM_AUTHTOKEN_REQD</emphasis> return, delay
+      giving service to the user until they have established a new password.
+      The entries listed above are documented in the <citerefentry>
+      <refentrytitle>shadow</refentrytitle><manvolnum>5</manvolnum>
+      </citerefentry> manual page. Should the user's record not contain
+      one or more of these entries, the corresponding
+      <emphasis>shadow</emphasis> check is not performed.
+    </para>
+
+    <para>
+      The authentication component performs the task of checking the
+      users credentials (password). The default action of this module
+      is to not permit the user access to a service if their official
+      password is blank.
+    </para>
+
+    <para>
+      The password component of this module performs the task of updating
+      the user's password. The default encryption hash is taken from the
+      <emphasis remap='B'>ENCRYPT_METHOD</emphasis> variable from
+      <emphasis>/etc/login.defs</emphasis>
+    </para>
+
+    <para>
+      The session component of this module logs when a user logins
+      or leave the system.
+    </para>
+
+    <para>
+      Remaining arguments, supported by others functions of this
+      module, are silently ignored. Other arguments are logged as
+      errors through <citerefentry>
+      <refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum>
+      </citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1 id="pam_extrausers-options">
+
+    <title>OPTIONS</title>
+    <variablelist>
+      <varlistentry>
+        <term>
+          <option>debug</option>
+        </term>
+        <listitem>
+          <para>
+	    Turns on debugging via
+            <citerefentry>
+              <refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum>
+            </citerefentry>.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>audit</option>
+        </term>
+        <listitem>
+          <para>
+            A little more extreme than debug.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>nullok</option>
+        </term>
+        <listitem>
+          <para>
+            The default action of this module is to not permit the
+            user access to a service if their official password is blank.
+            The <option>nullok</option> argument overrides this default
+            and allows any user with a blank password to access the
+            service.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>nullok_secure</option>
+        </term>
+        <listitem>
+          <para>
+            The default action of this module is to not permit the
+            user access to a service if their official password is blank.
+            The <option>nullok_secure</option> argument overrides this
+            default and allows any user with a blank password to access
+            the service as long as the value of PAM_TTY is set to one of
+	    the values found in /etc/securetty.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>try_first_pass</option>
+        </term>
+        <listitem>
+          <para>
+            Before prompting the user for their password, the module first
+            tries the previous stacked module's password in case that
+            satisfies this module as well.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>use_first_pass</option>
+        </term>
+        <listitem>
+          <para>
+            The argument <option>use_first_pass</option> forces the module
+            to use a previous stacked modules password and will never prompt
+            the user - if no password is available or the password is not
+            appropriate, the user will be denied access.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>nodelay</option>
+        </term>
+        <listitem>
+          <para>
+            This argument can be used to discourage the authentication
+            component from requesting a delay should the authentication
+            as a whole fail. The default action is for the module to
+            request a delay-on-failure of the order of two second.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>use_authtok</option>
+        </term>
+        <listitem>
+          <para>
+            When password changing enforce the module to set the new
+            password to the one provided by a previously stacked
+            <option>password</option> module (this is used in the
+            example of the stacking of the <command>pam_cracklib</command>
+            module documented below).
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>not_set_pass</option>
+        </term>
+        <listitem>
+          <para>
+            This argument is used to inform the module that it is not to
+            pay attention to/make available the old or new passwords from/to
+            other (stacked) password modules.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>nis</option>
+        </term>
+        <listitem>
+          <para>
+            NIS RPC is used for setting new passwords.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>remember=<replaceable>n</replaceable></option>
+        </term>
+        <listitem>
+          <para>
+            The last <replaceable>n</replaceable> passwords for each
+            user are saved in <filename>/etc/security/opasswd</filename>
+            in order to force password change history and keep the user
+            from alternating between the same password too frequently.
+            Instead of this option the <command>pam_pwhistory</command>
+            module should be used.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>shadow</option>
+        </term>
+        <listitem>
+          <para>
+            Try to maintain a shadow based system.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>md5</option>
+        </term>
+        <listitem>
+          <para>
+            When a user changes their password next, encrypt
+            it with the MD5 algorithm.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>bigcrypt</option>
+        </term>
+        <listitem>
+          <para>
+            When a user changes their password next,
+            encrypt it with the DEC C2 algorithm.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>sha256</option>
+        </term>
+        <listitem>
+          <para>
+            When a user changes their password next,
+            encrypt it with the SHA256 algorithm. If the
+            SHA256 algorithm is not known to the <citerefentry>
+	    <refentrytitle>crypt</refentrytitle><manvolnum>3</manvolnum>
+            </citerefentry> function,
+            fall back to MD5.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>sha512</option>
+        </term>
+        <listitem>
+          <para>
+            When a user changes their password next,
+            encrypt it with the SHA512 algorithm. If the
+            SHA512 algorithm is not known to the <citerefentry>
+	    <refentrytitle>crypt</refentrytitle><manvolnum>3</manvolnum>
+            </citerefentry> function,
+            fall back to MD5.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>blowfish</option>
+        </term>
+        <listitem>
+          <para>
+            When a user changes their password next,
+            encrypt it with the blowfish algorithm. If the
+            blowfish algorithm is not known to the <citerefentry>
+	    <refentrytitle>crypt</refentrytitle><manvolnum>3</manvolnum>
+            </citerefentry> function,
+            fall back to MD5.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>rounds=<replaceable>n</replaceable></option>
+        </term>
+        <listitem>
+          <para>
+            Set the optional number of rounds of the SHA256, SHA512
+            and blowfish password hashing algorithms to
+            <replaceable>n</replaceable>.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>broken_shadow</option>
+        </term>
+        <listitem>
+          <para>
+            Ignore errors reading shadow information for
+            users in the account management module.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>minlen=<replaceable>n</replaceable></option>
+        </term>
+        <listitem>
+          <para>
+            Set a minimum password length of <replaceable>n</replaceable>
+            characters.  The default value is 6.  The maximum for DES
+            crypt-based passwords is 8 characters.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>obscure</option>
+        </term>
+        <listitem>
+          <para>
+            Enable some extra checks on password strength.  These checks
+            are based on the "obscure" checks in the original shadow
+            package.  The behavior is similar to the pam_cracklib
+            module, but for non-dictionary-based checks.  The following
+            checks are implemented:
+            <variablelist>
+              <varlistentry>
+                <term>
+                  <option>Palindrome</option>
+                </term>
+                <listitem>
+                  <para>
+                    Verifies that the new password is not a palindrome
+                    of (i.e., the reverse of) the previous one.
+                  </para>
+                </listitem>
+              </varlistentry>
+              <varlistentry>
+                <term>
+                  <option>Case Change Only</option>
+                </term>
+                <listitem>
+                  <para>
+                    Verifies that the new password isn't the same as the
+                    old one with a change of case.
+                  </para>
+                </listitem>
+              </varlistentry>
+              <varlistentry>
+                <term>
+                  <option>Similar</option>
+                </term>
+                <listitem>
+                  <para>
+                    Verifies that the new password isn't too much like
+                    the previous one.
+                  </para>
+                </listitem>
+              </varlistentry>
+              <varlistentry>
+                <term>
+                  <option>Simple</option>
+                </term>
+                <listitem>
+                  <para>
+                    Is the new password too simple?  This is based on
+                    the length of the password and the number of
+                    different types of characters (alpha, numeric, etc.)
+                    used.
+                  </para>
+                </listitem>
+              </varlistentry>
+              <varlistentry>
+                <term>
+                  <option>Rotated</option>
+                </term>
+                <listitem>
+                  <para>
+                    Is the new password a rotated version of the old
+                    password?  (E.g., "billy" and "illyb")
+                  </para>
+                </listitem>
+              </varlistentry>
+            </variablelist>
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+    <para>
+      Invalid arguments are logged with  <citerefentry>
+      <refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum>
+      </citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1 id="pam_extrausers-types">
+    <title>MODULE TYPES PROVIDED</title>
+    <para>
+      All module types (<option>account</option>, <option>auth</option>,
+      <option>password</option> and <option>session</option>) are provided.
+    </para>
+  </refsect1>
+
+  <refsect1 id='pam_extrausers-return_values'>
+    <title>RETURN VALUES</title>
+    <variablelist>
+      <varlistentry>
+        <term>PAM_IGNORE</term>
+        <listitem>
+          <para>
+            Ignore this module.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+     </refsect1>
+
+  <refsect1 id='pam_extrausers-examples'>
+    <title>EXAMPLES</title>
+    <para>
+      An example usage for <filename>/etc/pam.d/common-password</filename>
+      might be:
+      <programlisting>
+password        [success=2 default=ignore]      pam_extrausers.so obscure sha512
+password        [success=1 default=ignore]      pam_unix.so obscure sha512
+# here's the fallback if no module succeeds
+password        requisite                       pam_deny.so
+# prime the stack with a positive return value if there isn't one already;
+# this avoids us returning an error just because nothing sets a success code
+# since the modules above will each just jump around
+password        required                        pam_permit.so
+# and here are more per-package modules (the "Additional" block)
+password        optional        pam_gnome_keyring.so 
+password        optional        pam_ecryptfs.so 
+      </programlisting>
+    </para>
+  </refsect1>
+
+  <refsect1 id='pam_extrausers-see_also'>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+	<refentrytitle>login.defs</refentrytitle><manvolnum>5</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+	<refentrytitle>pam.conf</refentrytitle><manvolnum>5</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+	<refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+	<refentrytitle>pam</refentrytitle><manvolnum>7</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+
+  <refsect1 id='pam_extrausers-author'>
+    <title>AUTHOR</title>
+      <para>
+        pam_extrausers was written by various people.
+      </para>
+  </refsect1>
+
+</refentry>
Index: pam-1.1.8/modules/pam_extrausers/pam_extrausers.8
===================================================================
--- /dev/null
+++ pam-1.1.8/modules/pam_extrausers/pam_extrausers.8
@@ -0,0 +1,269 @@
+'\" t
+.\"     Title: pam_extrausers
+.\"    Author: [see the "AUTHOR" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
+.\"      Date: 07/22/2014
+.\"    Manual: Linux-PAM Manual
+.\"    Source: Linux-PAM Manual
+.\"  Language: English
+.\"
+.TH "PAM_EXTRAUSERS" "8" "07/22/2014" "Linux-PAM Manual" "Linux\-PAM Manual"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+pam_extrausers \- Module for libnss\-extrausers authentication
+.SH "SYNOPSIS"
+.HP \w'\fBpam_extrausers\&.so\fR\ 'u
+\fBpam_extrausers\&.so\fR [\&.\&.\&.]
+.SH "DESCRIPTION"
+.PP
+This is similar to the standard Unix authentication module pam_unix\&. But instead of using /etc/passwd and /etc/shadow, it uses /var/lib/extrausers/passwd and /var/lib/extrausers/shadow\&.
+.PP
+The account component performs the task of establishing the status of the user\*(Aqs account and password based on the following
+\fIshadow\fR
+elements: expire, last_change, max_change, min_change, warn_change\&. In the case of the latter, it may offer advice to the user on changing their password or, through the
+\fBPAM_AUTHTOKEN_REQD\fR
+return, delay giving service to the user until they have established a new password\&. The entries listed above are documented in the
+\fBshadow\fR(5)
+manual page\&. Should the user\*(Aqs record not contain one or more of these entries, the corresponding
+\fIshadow\fR
+check is not performed\&.
+.PP
+The authentication component performs the task of checking the users credentials (password)\&. The default action of this module is to not permit the user access to a service if their official password is blank\&.
+.PP
+The password component of this module performs the task of updating the user\*(Aqs password\&. The default encryption hash is taken from the
+\fBENCRYPT_METHOD\fR
+variable from
+\fI/etc/login\&.defs\fR
+.PP
+The session component of this module logs when a user logins or leave the system\&.
+.PP
+Remaining arguments, supported by others functions of this module, are silently ignored\&. Other arguments are logged as errors through
+\fBsyslog\fR(3)\&.
+.SH "OPTIONS"
+.PP
+\fBdebug\fR
+.RS 4
+Turns on debugging via
+\fBsyslog\fR(3)\&.
+.RE
+.PP
+\fBaudit\fR
+.RS 4
+A little more extreme than debug\&.
+.RE
+.PP
+\fBnullok\fR
+.RS 4
+The default action of this module is to not permit the user access to a service if their official password is blank\&. The
+\fBnullok\fR
+argument overrides this default and allows any user with a blank password to access the service\&.
+.RE
+.PP
+\fBnullok_secure\fR
+.RS 4
+The default action of this module is to not permit the user access to a service if their official password is blank\&. The
+\fBnullok_secure\fR
+argument overrides this default and allows any user with a blank password to access the service as long as the value of PAM_TTY is set to one of the values found in /etc/securetty\&.
+.RE
+.PP
+\fBtry_first_pass\fR
+.RS 4
+Before prompting the user for their password, the module first tries the previous stacked module\*(Aqs password in case that satisfies this module as well\&.
+.RE
+.PP
+\fBuse_first_pass\fR
+.RS 4
+The argument
+\fBuse_first_pass\fR
+forces the module to use a previous stacked modules password and will never prompt the user \- if no password is available or the password is not appropriate, the user will be denied access\&.
+.RE
+.PP
+\fBnodelay\fR
+.RS 4
+This argument can be used to discourage the authentication component from requesting a delay should the authentication as a whole fail\&. The default action is for the module to request a delay\-on\-failure of the order of two second\&.
+.RE
+.PP
+\fBuse_authtok\fR
+.RS 4
+When password changing enforce the module to set the new password to the one provided by a previously stacked
+\fBpassword\fR
+module (this is used in the example of the stacking of the
+\fBpam_cracklib\fR
+module documented below)\&.
+.RE
+.PP
+\fBnot_set_pass\fR
+.RS 4
+This argument is used to inform the module that it is not to pay attention to/make available the old or new passwords from/to other (stacked) password modules\&.
+.RE
+.PP
+\fBnis\fR
+.RS 4
+NIS RPC is used for setting new passwords\&.
+.RE
+.PP
+\fBremember=\fR\fB\fIn\fR\fR
+.RS 4
+The last
+\fIn\fR
+passwords for each user are saved in
+/etc/security/opasswd
+in order to force password change history and keep the user from alternating between the same password too frequently\&. Instead of this option the
+\fBpam_pwhistory\fR
+module should be used\&.
+.RE
+.PP
+\fBshadow\fR
+.RS 4
+Try to maintain a shadow based system\&.
+.RE
+.PP
+\fBmd5\fR
+.RS 4
+When a user changes their password next, encrypt it with the MD5 algorithm\&.
+.RE
+.PP
+\fBbigcrypt\fR
+.RS 4
+When a user changes their password next, encrypt it with the DEC C2 algorithm\&.
+.RE
+.PP
+\fBsha256\fR
+.RS 4
+When a user changes their password next, encrypt it with the SHA256 algorithm\&. If the SHA256 algorithm is not known to the
+\fBcrypt\fR(3)
+function, fall back to MD5\&.
+.RE
+.PP
+\fBsha512\fR
+.RS 4
+When a user changes their password next, encrypt it with the SHA512 algorithm\&. If the SHA512 algorithm is not known to the
+\fBcrypt\fR(3)
+function, fall back to MD5\&.
+.RE
+.PP
+\fBblowfish\fR
+.RS 4
+When a user changes their password next, encrypt it with the blowfish algorithm\&. If the blowfish algorithm is not known to the
+\fBcrypt\fR(3)
+function, fall back to MD5\&.
+.RE
+.PP
+\fBrounds=\fR\fB\fIn\fR\fR
+.RS 4
+Set the optional number of rounds of the SHA256, SHA512 and blowfish password hashing algorithms to
+\fIn\fR\&.
+.RE
+.PP
+\fBbroken_shadow\fR
+.RS 4
+Ignore errors reading shadow information for users in the account management module\&.
+.RE
+.PP
+\fBminlen=\fR\fB\fIn\fR\fR
+.RS 4
+Set a minimum password length of
+\fIn\fR
+characters\&. The default value is 6\&. The maximum for DES crypt\-based passwords is 8 characters\&.
+.RE
+.PP
+\fBobscure\fR
+.RS 4
+Enable some extra checks on password strength\&. These checks are based on the "obscure" checks in the original shadow package\&. The behavior is similar to the pam_cracklib module, but for non\-dictionary\-based checks\&. The following checks are implemented:
+.PP
+\fBPalindrome\fR
+.RS 4
+Verifies that the new password is not a palindrome of (i\&.e\&., the reverse of) the previous one\&.
+.RE
+.PP
+\fBCase Change Only\fR
+.RS 4
+Verifies that the new password isn\*(Aqt the same as the old one with a change of case\&.
+.RE
+.PP
+\fBSimilar\fR
+.RS 4
+Verifies that the new password isn\*(Aqt too much like the previous one\&.
+.RE
+.PP
+\fBSimple\fR
+.RS 4
+Is the new password too simple? This is based on the length of the password and the number of different types of characters (alpha, numeric, etc\&.) used\&.
+.RE
+.PP
+\fBRotated\fR
+.RS 4
+Is the new password a rotated version of the old password? (E\&.g\&., "billy" and "illyb")
+.RE
+.sp
+.RE
+.PP
+Invalid arguments are logged with
+\fBsyslog\fR(3)\&.
+.SH "MODULE TYPES PROVIDED"
+.PP
+All module types (\fBaccount\fR,
+\fBauth\fR,
+\fBpassword\fR
+and
+\fBsession\fR) are provided\&.
+.SH "RETURN VALUES"
+.PP
+PAM_IGNORE
+.RS 4
+Ignore this module\&.
+.RE
+.SH "EXAMPLES"
+.PP
+An example usage for
+/etc/pam\&.d/common\-password
+would be:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+password        [success=2 default=ignore]      pam_extrausers\&.so obscure sha512
+password        [success=1 default=ignore]      pam_unix\&.so obscure sha512
+# here\*(Aqs the fallback if no module succeeds
+password        requisite                       pam_deny\&.so
+# prime the stack with a positive return value if there isn\*(Aqt one already;
+# this avoids us returning an error just because nothing sets a success code
+# since the modules above will each just jump around
+password        required                        pam_permit\&.so
+# and here are more per\-package modules (the "Additional" block)
+password        optional        pam_gnome_keyring\&.so 
+password        optional        pam_ecryptfs\&.so 
+      
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.SH "SEE ALSO"
+.PP
+\fBlogin.defs\fR(5),
+\fBpam.conf\fR(5),
+\fBpam.d\fR(5),
+\fBpam\fR(7)
+.SH "AUTHOR"
+.PP
+pam_extrausers was written by various people\&.
