From c83099a8f01f25d9c8bb9c176abe26fcac36c01b Mon Sep 17 00:00:00 2001
From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Date: Fri, 19 Sep 2008 17:26:19 +0200
Subject: [PATCH] Cluster support to pam_unix and pam_cracklib added.

One can use an alternate root directory, under which /etc/passwd etc.
looked up, checked and updated. Assuming that the alternate root directory
is on a shared filesystem, the "cluster" option and functionality can be
used to build up a cluster of machines with PAM authentication which
uses plain files only.
---
 configure.in                        |   11 ++-
 modules/pam_cracklib/pam_cracklib.c |   33 ++++-
 modules/pam_unix/cluster.c          |  104 +++++++++++++
 modules/pam_unix/cluster.h          |   56 +++++++
 modules/pam_unix/lckpwdf.-c         |    9 +-
 modules/pam_unix/pam_unix_acct.c    |    5 +-
 modules/pam_unix/pam_unix_passwd.c  |   54 ++++----
 modules/pam_unix/passverify.c       |  277 +++++++++++++++++++++++++++++------
 modules/pam_unix/passverify.h       |   22 ++--
 modules/pam_unix/support.c          |   40 +++--
 modules/pam_unix/support.h          |    8 +-
 modules/pam_unix/unix_chkpwd.c      |   27 +++-
 modules/pam_unix/unix_update.c      |   32 +++--
 13 files changed, 545 insertions(+), 133 deletions(-)
 create mode 100644 modules/pam_unix/cluster.c
 create mode 100644 modules/pam_unix/cluster.h

diff --git a/configure.in b/configure.in
index 0b929a4..6591b0d 100644
--- a/configure.in
+++ b/configure.in
@@ -326,6 +326,15 @@ AC_ARG_ENABLE([cracklib],
 if test x"$WITH_CRACKLIB" != xno ; then
         AC_CHECK_HEADERS([crack.h],
               AC_CHECK_LIB([crack], [FascistCheck], LIBCRACK="-lcrack", LIBCRACK=""))
+if test x"$ac_cv_header_crack_h" != xno; then
+	AC_TRY_RUN([
+#include <crack.h>
+int main() {
+const char *path = CRACKLIB_DICTPATH;
+exit(0);
+}],
+[AC_DEFINE(HAVE_CRACKLIB_DICTPATH, 1, [Enable if CRACKLIB_DICTPATH exists in crack.h])])
+fi
 else
 	LIBCRACK=""
 fi
@@ -454,7 +463,7 @@ AC_FUNC_MEMCMP
 AC_FUNC_VPRINTF
 AC_CHECK_FUNCS(fseeko gethostname gettimeofday lckpwdf mkdir select)
 AC_CHECK_FUNCS(strcspn strdup strspn strstr strtol uname)
-AC_CHECK_FUNCS(getpwnam_r getpwuid_r getgrnam_r getgrgid_r getspnam_r)
+AC_CHECK_FUNCS(getpwnam_r getpwuid_r getgrnam_r getgrgid_r getspnam_r fgetpwent_r fgetspent_r)
 AC_CHECK_FUNCS(getgrouplist getline getdelim)
 AC_CHECK_FUNCS(inet_ntop inet_pton ruserok_af)
 
diff --git a/modules/pam_cracklib/pam_cracklib.c b/modules/pam_cracklib/pam_cracklib.c
index 63a49ff..ba9e337 100644
--- a/modules/pam_cracklib/pam_cracklib.c
+++ b/modules/pam_cracklib/pam_cracklib.c
@@ -51,6 +51,7 @@
 #include <sys/stat.h>
 #include <ctype.h>
 #include <limits.h>
+#include <errno.h>
 
 #ifdef HAVE_CRACK_H
 #include <crack.h>
@@ -59,8 +60,12 @@ extern char *FascistCheck(char *pw, const char *dictpath);
 #endif
 
 #ifndef CRACKLIB_DICTS
+#ifdef HAVE_CRACKLIB_DICTPATH
+#define CRACKLIB_DICTS CRACKLIB_DICTPATH
+#else
 #define CRACKLIB_DICTS NULL
 #endif
+#endif
 
 /* For Translators: "%s%s" could be replaced with "<service> " or "". */
 #define PROMPT1 _("New %s%spassword: ")
@@ -100,6 +105,7 @@ struct cracklib_options {
 	int oth_credit;
         int min_class;
 	int use_authtok;
+	int cluster;
 	char prompt_type[BUFSIZ];
         const char *cracklib_dictpath;
 };
@@ -114,6 +120,7 @@ struct cracklib_options {
 #define CO_LOW_CREDIT   1
 #define CO_OTH_CREDIT   1
 #define CO_USE_AUTHTOK  0
+#define CO_CLUSTER	0
 
 static int
 _pam_parse (pam_handle_t *pamh, struct cracklib_options *opt,
@@ -171,6 +178,8 @@ _pam_parse (pam_handle_t *pamh, struct cracklib_options *opt,
                      opt->min_class = 4 ;
 	 } else if (!strncmp(*argv,"use_authtok",11)) {
 		 opt->use_authtok = 1;
+	 } else if (!strncmp(*argv,"cluster",7)) {
+		 opt->cluster = 1;
 	 } else if (!strncmp(*argv,"dictpath=",9)) {
 	     opt->cracklib_dictpath = *argv+9;
 	     if (!*(opt->cracklib_dictpath)) {
@@ -475,15 +484,32 @@ static const char *password_check(struct cracklib_options *opt,
 
 
 #define OLD_PASSWORDS_FILE	"/etc/security/opasswd"
+#define CLUSTER_ROOTDIR		"/etc/cluster_rootdir"
 
-static const char * check_old_password(const char *forwho, const char *newpass)
+static const char * check_old_password(pam_handle_t *pamh,
+				       struct cracklib_options *opt,
+				       const char *forwho, const char *newpass)
 {
 	static char buf[16384];
+	char opasswd[1024+21] = "";
+	int pos;
 	char *s_luser, *s_uid, *s_npas, *s_pas;
 	const char *msg = NULL;
 	FILE *opwfile;
 
-	opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+	if (opt->cluster) {
+		pos = readlink(CLUSTER_ROOTDIR, opasswd, 1024 - 1);
+		if (pos > 0)
+			opasswd[pos] = '\0';
+		else {
+			pam_syslog(pamh, LOG_ERR, "Invalid " CLUSTER_ROOTDIR ": %s",
+				   strerror(errno));
+			return NULL;
+		}
+	}
+	strcat(opasswd, OLD_PASSWORDS_FILE);
+	
+	opwfile = fopen(opasswd, "r");
 	if (opwfile == NULL)
 		return NULL;
 
@@ -541,7 +567,7 @@ static int _pam_unix_approve_pass(pam_handle_t *pamh,
 		pam_syslog(pamh,LOG_ERR,"Can not get username");
 	    return PAM_AUTHTOK_ERR;
 	}
-	msg = check_old_password(user, pass_new);
+	msg = check_old_password(pamh, opt, user, pass_new);
     }
 
     if (msg) {
@@ -576,6 +602,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
     options.low_credit = CO_LOW_CREDIT;
     options.oth_credit = CO_OTH_CREDIT;
     options.use_authtok = CO_USE_AUTHTOK;
+    options.cluster = CO_CLUSTER;
     memset(options.prompt_type, 0, BUFSIZ);
     strcpy(options.prompt_type,"UNIX");
     options.cracklib_dictpath = CRACKLIB_DICTS;
diff --git a/modules/pam_unix/cluster.c b/modules/pam_unix/cluster.c
new file mode 100644
index 0000000..6263b20
--- /dev/null
+++ b/modules/pam_unix/cluster.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright information at end of file.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <security/pam_ext.h>
+#include "passverify.h"
+#include "support.h"
+#include "cluster.h"
+
+/* The actual filenames */
+static char unix_files[UNIX_FILE_TYPES][UNIX_FILES_MAX][UNIX_FILES_MAXLEN];
+
+/* The cluster rootdir */
+static char cluster_rootdir[CLUSTER_ROOTDIRLEN] = "";
+
+#ifdef HELPER_COMPILE
+#define report_error(format, args...)	\
+	helper_log_err(LOG_ERR, format , ## args)
+#else
+#define report_error(format, args...)	\
+	pam_syslog(pamh, LOG_ERR, format , ## args)
+#endif
+
+#ifdef HELPER_COMPILE
+int read_cluster_rootdir(void)
+#else
+int read_cluster_rootdir(pam_handle_t *pamh)
+#endif
+{
+	int pos = readlink(CLUSTER_ROOTDIR,
+			   cluster_rootdir, sizeof(cluster_rootdir) - 1);
+			
+	if (pos < 0)
+		report_error("Invalid " CLUSTER_ROOTDIR ": %s",
+			     strerror(errno));
+	else
+		cluster_rootdir[pos] = '\0';
+	
+	return pos;
+}
+
+#define STRCAT(type)	\
+	strcpy(unix_files[DEFAULT_UNIX_FILES][type], DEFAULT ## type); \
+	strcat(unix_files[CLUSTER_UNIX_FILES][type], DEFAULT ## type)
+
+void init_unix_files(void)
+{
+	unsigned int i;
+	
+	for (i = 0; i < UNIX_FILES_MAX; i++)
+		strcpy(unix_files[CLUSTER_UNIX_FILES][i], cluster_rootdir);
+	
+	STRCAT(_PASSWD_FILE);
+	STRCAT(_PASSWD_TMPFILE);
+	STRCAT(_SHADOW_FILE);
+	STRCAT(_SHADOW_TMPFILE);
+	STRCAT(_OLD_PASSWORDS_FILE);
+	STRCAT(_OLD_PASSWORDS_TMPFILE);
+	STRCAT(_LOCKFILE);
+	STRCAT(_TEMPFILE);
+}
+
+char * map_unix_files(unsigned int ctrl, int type)
+{	
+	return unix_files[!!on(UNIX_CLUSTER,ctrl)][type];
+}	
+
+/* ****************************************************************** *
+ * Copyright (c) Jozsef Kadlecsik, 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.
+ */
diff --git a/modules/pam_unix/cluster.h b/modules/pam_unix/cluster.h
new file mode 100644
index 0000000..6ec36ae
--- /dev/null
+++ b/modules/pam_unix/cluster.h
@@ -0,0 +1,56 @@
+#ifndef _PAM_UNIX_CLUSTER_H
+#define _PAM_UNIX_CLUSTER_H
+
+/* Files we may open */
+#define DEFAULT_PASSWD_FILE		"/etc/passwd"
+#define	DEFAULT_PASSWD_TMPFILE		"/etc/npasswd"
+#define DEFAULT_SHADOW_FILE		"/etc/shadow"
+#define DEFAULT_SHADOW_TMPFILE		"/etc/nshadow"
+#define DEFAULT_OLD_PASSWORDS_FILE	"/etc/security/opasswd"
+#define DEFAULT_OLD_PASSWORDS_TMPFILE	"/etc/security/nopasswd"
+#define DEFAULT_LOCKFILE		"/etc/.pwd.lock"
+#define DEFAULT_TEMPFILE		"/etc/.pwdXXXXXX"
+
+enum {
+	DEFAULT_UNIX_FILES = 0,
+	CLUSTER_UNIX_FILES,
+	UNIX_FILE_TYPES,
+};
+
+enum {
+	_PASSWD_FILE = 0,
+	_PASSWD_TMPFILE,
+	_SHADOW_FILE,
+	_SHADOW_TMPFILE,
+	_OLD_PASSWORDS_FILE,
+	_OLD_PASSWORDS_TMPFILE,
+	_LOCKFILE,
+	_TEMPFILE,
+	UNIX_FILES_MAX,
+};
+
+#define	CLUSTER_ROOTDIR			"/etc/cluster_rootdir"
+#define CLUSTER_ROOTDIRLEN		1024
+#define UNIX_FILENAMELEN		22	/* "/etc/security/nopasswd" */
+#define UNIX_FILES_MAXLEN		CLUSTER_ROOTDIRLEN+UNIX_FILENAMELEN
+
+extern char *map_unix_files(unsigned int ctrl, int type);
+
+/* Map file type to filename */
+#define PASSWD_FILE		map_unix_files(ctrl,_PASSWD_FILE)
+#define PASSWD_TMPFILE		map_unix_files(ctrl,_PASSWD_TMPFILE)
+#define SHADOW_FILE		map_unix_files(ctrl,_SHADOW_FILE)
+#define SHADOW_TMPFILE		map_unix_files(ctrl,_SHADOW_TMPFILE)
+#define OLD_PASSWORDS_FILE	map_unix_files(ctrl,_OLD_PASSWORDS_FILE)
+#define OLD_PASSWORDS_TMPFILE	map_unix_files(ctrl,_OLD_PASSWORDS_TMPFILE)
+#define LOCKFILE		map_unix_files(ctrl,_LOCKFILE)
+#define TEMPFILE		map_unix_files(ctrl,_TEMPFILE)
+
+#ifdef HELPER_COMPILE
+extern int read_cluster_rootdir(void);
+#else
+extern int read_cluster_rootdir(pam_handle_t *pamh);
+#endif
+extern void init_unix_files(void);
+
+#endif /* _PAM_UNIX_CLUSTER_H */
diff --git a/modules/pam_unix/lckpwdf.-c b/modules/pam_unix/lckpwdf.-c
index 7145617..8eabe7b 100644
--- a/modules/pam_unix/lckpwdf.-c
+++ b/modules/pam_unix/lckpwdf.-c
@@ -30,7 +30,6 @@
 #include <selinux/selinux.h>
 #endif
 
-#define LOCKFILE "/etc/.pwd.lock"
 #define TIMEOUT 15
 
 static int lockfd = -1;
@@ -54,12 +53,12 @@ static int do_lock(int fd)
 	return fcntl(fd, F_SETLKW, &fl);
 }
 
-static void alarm_catch(int sig)
+static void alarm_catch(int sig UNUSED)
 {
 /* does nothing, but fcntl F_SETLKW will fail with EINTR */
 }
 
-static int lckpwdf(void)
+static int __lckpwdf(unsigned int ctrl)
 {
 	struct sigaction act, oldact;
 	sigset_t set, oldset;
@@ -76,7 +75,7 @@ static int lckpwdf(void)
 			security_context_t create_context;
 			int rc;
 
-			if(getfilecon("/etc/passwd", &create_context))
+			if(getfilecon(PASSWD_FILE, &create_context))
 				return -1;
 			rc = setfscreatecon(create_context);
 			freecon(create_context);
@@ -126,7 +125,7 @@ static int lckpwdf(void)
 	return -1;
 }
 
-static int ulckpwdf(void)
+static int __ulckpwdf(unsigned int ctrl)
 {
 	unlink(LOCKFILE);
 	if (lockfd == -1)
diff --git a/modules/pam_unix/pam_unix_acct.c b/modules/pam_unix/pam_unix_acct.c
index c09bc17..24e04e6 100644
--- a/modules/pam_unix/pam_unix_acct.c
+++ b/modules/pam_unix/pam_unix_acct.c
@@ -94,7 +94,7 @@ int _unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl,
     size_t i=0;
     struct rlimit rlim;
     static char *envp[] = { NULL };
-    char *args[] = { NULL, NULL, NULL, NULL };
+    char *args[] = { NULL, NULL, NULL, NULL, NULL };
 
     close(0); close(1);
     /* reopen stdin as pipe */
@@ -121,6 +121,7 @@ int _unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl,
     args[0] = x_strdup(CHKPWD_HELPER);
     args[1] = x_strdup(user);
     args[2] = x_strdup("chkexpiry");
+    args[3] = x_strdup(on(UNIX_CLUSTER, ctrl) ? "cluster" : "local");
 
     execve(CHKPWD_HELPER, args, envp);
 
@@ -196,7 +197,7 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags,
 		return PAM_USER_UNKNOWN;
 	}
 
-	retval = get_account_info(pamh, uname, &pwent, &spent);
+	retval = get_account_info(pamh, ctrl, uname, &pwent, &spent);
 	if (retval == PAM_USER_UNKNOWN) {
 		pam_syslog(pamh, LOG_ALERT,
 			 "could not identify user (from getpwnam(%s))",
diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c
index ea39408..b411cc8 100644
--- a/modules/pam_unix/pam_unix_passwd.c
+++ b/modules/pam_unix/pam_unix_passwd.c
@@ -82,6 +82,7 @@ static int selinux_enabled=-1;
 #include "support.h"
 #include "passverify.h"
 #include "bigcrypt.h"
+#include "cluster.h"
 
 #if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
 extern int getrpcport(const char *host, unsigned long prognum,
@@ -166,7 +167,7 @@ static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const
         size_t i=0;
         struct rlimit rlim;
 	static char *envp[] = { NULL };
-	char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
+	char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
         char buffer[16];
 
 	/* XXX - should really tidy up PAM here too */
@@ -193,10 +194,8 @@ static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const
 	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");
+	args[3] = x_strdup(on(UNIX_SHADOW, ctrl) ? "1" : "0");
+	args[5] = x_strdup(on(UNIX_CLUSTER, ctrl) ? "cluster" : "local");
 
         snprintf(buffer, sizeof(buffer), "%d", remember);
         args[4] = x_strdup(buffer);
@@ -244,7 +243,8 @@ static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const
 }
 #endif
 
-static int check_old_password(const char *forwho, const char *newpass)
+static int check_old_password(unsigned int ctrl,
+			      const char *forwho, const char *newpass)
 {
 	static char buf[16384];
 	char *s_luser, *s_uid, *s_npas, *s_pas;
@@ -299,7 +299,7 @@ static int _do_setpass(pam_handle_t* pamh, const char *forwho,
 		goto done;
 	}
 
-	if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) {
+	if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, ctrl, forwho, 0, 1)) {
 	    if ((master=getNISserver(pamh)) != NULL) {
 		struct timeval timeout;
 		struct yppasswd yppwd;
@@ -308,7 +308,7 @@ static int _do_setpass(pam_handle_t* pamh, const char *forwho,
 		enum clnt_stat err;
 
 		/* Unlock passwd file to avoid deadlock */
-		unlock_pwdf();
+		unlock_pwdf(ctrl);
 		unlocked = 1;
 
 		/* Initialize password information */
@@ -367,34 +367,34 @@ static int _do_setpass(pam_handle_t* pamh, const char *forwho,
 	    }
 	}
 
-	if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
+	if (_unix_comesfromsource(pamh, ctrl, forwho, 1, 0)) {
 		if(unlocked) {
-			if (lock_pwdf() != PAM_SUCCESS) {
+			if (lock_pwdf(ctrl) != PAM_SUCCESS) {
 				return PAM_AUTHTOK_LOCK_BUSY;
 			}
 		}
 #ifdef WITH_SELINUX
-	        if (unix_selinux_confined())
+	        if (unix_selinux_confined(ctrl))
 			  return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember);
 #endif
 		/* first, save old password */
-		if (save_old_password(forwho, fromwhat, remember)) {
+		if (save_old_password(ctrl, 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);
+			retval = unix_update_shadow(pamh, ctrl, forwho, towhat);
 			if (retval == PAM_SUCCESS)
 				if (!is_pwd_shadowed(pwd))
-					retval = unix_update_passwd(pamh, forwho, "x");
+					retval = unix_update_passwd(pamh, ctrl, forwho, "x");
 		} else {
-			retval = unix_update_passwd(pamh, forwho, towhat);
+			retval = unix_update_passwd(pamh, ctrl, forwho, towhat);
 		}
 	}
 
 
 done:
-	unlock_pwdf();
+	unlock_pwdf(ctrl);
 
 	return retval;
 }
@@ -406,7 +406,7 @@ static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned in
 	int daysleft;
 	int retval;
 
-	retval = get_account_info(pamh, user, &pwent, &spent);
+	retval = get_account_info(pamh, ctrl, user, &pwent, &spent);
 	if (retval == PAM_USER_UNKNOWN) {
 		return retval;
 	}
@@ -466,7 +466,7 @@ static int _pam_unix_approve_pass(pam_handle_t * pamh
 		  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)
+			if ((retval = check_old_password(ctrl, 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",
@@ -533,14 +533,14 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 	 * 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) {
+	if (_unix_comesfromsource(pamh, ctrl, user, 1, on(UNIX_NIS, ctrl)) == 0) {
 		pam_syslog(pamh, LOG_DEBUG,
-			 "user \"%s\" does not exist in /etc/passwd%s",
-			 user, on(UNIX_NIS, ctrl) ? " or NIS" : "");
+			 "user \"%s\" does not exist in %s%s",
+			 user, PASSWD_FILE, on(UNIX_NIS, ctrl) ? " or NIS" : "");
 		return PAM_USER_UNKNOWN;
 	} else {
 		struct passwd *pwd;
-		_unix_getpwnam(pamh, user, 1, 1, &pwd);
+		_unix_getpwnam(pamh, ctrl, user, 1, 1, &pwd);
 		if (pwd == NULL) {
 			pam_syslog(pamh, LOG_DEBUG,
 				"user \"%s\" has corrupted passwd entry",
@@ -707,7 +707,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 			pass_new = pass_old = NULL;	/* tidy up */
 			return retval;
 		}
-		if (lock_pwdf() != PAM_SUCCESS) {
+		if (lock_pwdf(ctrl) != PAM_SUCCESS) {
 			return PAM_AUTHTOK_LOCK_BUSY;
 		}
 
@@ -715,7 +715,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 			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();
+				unlock_pwdf(ctrl);
 				return retval;
 			}
 		}
@@ -723,7 +723,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 		retval = _unix_verify_shadow(pamh, user, ctrl);
 		if (retval != PAM_SUCCESS) {
 			pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired");
-			unlock_pwdf();
+			unlock_pwdf(ctrl);
 			return retval;
 		}
 
@@ -732,7 +732,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 			pam_syslog(pamh, LOG_NOTICE,
 			         "new password not acceptable 2");
 			pass_new = pass_old = NULL;	/* tidy up */
-			unlock_pwdf();
+			unlock_pwdf(ctrl);
 			return retval;
 		}
 
@@ -750,7 +750,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 			pam_syslog(pamh, LOG_CRIT,
 				"out of memory for password");
 			pass_new = pass_old = NULL;	/* tidy up */
-			unlock_pwdf();
+			unlock_pwdf(ctrl);
 			return PAM_BUF_ERR;
 		}
 
diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c
index 9563ca6..51af3c1 100644
--- a/modules/pam_unix/passverify.c
+++ b/modules/pam_unix/passverify.c
@@ -9,6 +9,7 @@
 #include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <limits.h>
 #include <pwd.h>
 #include <shadow.h>
 #include <syslog.h>
@@ -28,6 +29,7 @@
 #include "md5.h"
 #include "bigcrypt.h"
 #include "passverify.h"
+#include "cluster.h"
 
 #ifdef WITH_SELINUX
 #include <selinux/selinux.h>
@@ -37,17 +39,18 @@
 #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)
+#define fgetpwnam(h,f,n) _fgetpwnam(f,n)
+#define fgetspnam(h,f,n) _fgetspnam(f,n)
 #else
 #include <security/pam_modutil.h>
 #include <security/pam_ext.h>
+#define fgetpwnam(h,f,n) _fgetpwnam_r(h,f,n)
+#define fgetspnam(h,f,n) _fgetspnam_r(h,f,n)
 #endif
 
-#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
-# include "./lckpwdf.-c"
-#endif
+/* We have to include lckpwdf.-c as lckpwdf works hardcoded in /etc only */
+#include "./lckpwdf.-c"
 
 static void
 strip_hpux_aging(char *hash)
@@ -119,7 +122,7 @@ verify_pwd_hash(const char *p, char *hash, unsigned int nullok)
 		p = NULL;		/* no longer needed here */
 
 		/* the moment of truth -- do we agree with the password? */
-		D(("comparing state of pp[%s] and salt[%s]", pp, salt));
+		D(("comparing state of pp[%s] and hash[%s]", pp, hash));
 
 		if (pp && strcmp(pp, hash) == 0) {
 			retval = PAM_SUCCESS;
@@ -151,11 +154,191 @@ is_pwd_shadowed(const struct passwd *pwd)
 	return 0;
 }
 
+#define PWD_INITIAL_LENGTH	0x100
+#define PWD_ABSURD_PWD_LENGTH	0x8000
+
+#if defined(HAVE_FGETPWENT_R) || defined(HAVE_FGETSPENT_R)
+#ifndef HELPER_COMPILE
+static void
+_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED)
+{
+	free(data);
+}
+
+static int intlen(int number)
+{
+	int len = 2;
+	while (number != 0) {
+		number /= 10;
+		len++;
+	}
+	return len;
+}
+
+static int
+_pam_add_getnam_data(pam_handle_t *pamh, void *data,
+		     const char *modname, const char *user)
+{
+	char *data_name;
+	const void *ignore;
+	int i;
+	size_t len;
+	int status = PAM_NO_MODULE_DATA;
+
+	len = strlen(modname) + strlen(user) + 1 + intlen(INT_MAX) + 1;
+	data_name = malloc(len);
+	if ((pamh != NULL) && (data_name == NULL)) {
+ 		D(("was unable to register the data item [%s]",
+		   pam_strerror(pamh, status)));
+		return PAM_BUF_ERR;
+	}
+	if (pamh != NULL) {
+		for (i = 0; i < INT_MAX; i++) {
+			snprintf(data_name, len, "%s%s_%d", modname, user, i);
+			status = PAM_NO_MODULE_DATA;
+			if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) {
+				status = pam_set_data(pamh, data_name, data, _cleanup);
+			}
+			if (status == PAM_SUCCESS) {
+				break;
+			}
+		}
+	} else {
+		status = PAM_SUCCESS;
+	}
+
+	free(data_name);
+	return status;
+}
+#endif
+#endif
+
+/* Macros to avoid copy & paste. Alas, not much nicer. */
+
+#define FNAME(pre, kind, post)	pre##kind##post
+
+#define DEFINE_FGETXNAM(TYPE, KIND, PWNAME, FILENAME)				\
+static TYPE *									\
+FNAME(_fget, KIND, nam)(const char *filename, const char *user)			\
+{										\
+	FILE *pwfile;								\
+	TYPE *result = NULL;							\
+										\
+	if (strcmp(FILENAME, filename) == 0)					\
+		return FNAME(get, KIND, nam)(user);				\
+	else {									\
+		pwfile = fopen(filename, "r");					\
+		if (pwfile != NULL) {						\
+			while ((result = FNAME(fget, KIND, ent)(pwfile)) != NULL) \
+				if (strcmp(result->PWNAME, user) == 0)		\
+					break;					\
+			fclose(pwfile);						\
+		}								\
+	}									\
+	return result;								\
+}
+
+#define DEFINE_FGETXNAM_R(TYPE, KIND, PWNAME, FILENAME, MODNAME)		\
+static TYPE *									\
+FNAME(_fget, KIND, nam_r)(pam_handle_t *pamh, const char *filename, const char *user) \
+{										\
+	FILE *pwfile;								\
+	TYPE *result = NULL;							\
+	void *buffer = NULL;							\
+	size_t length = PWD_INITIAL_LENGTH;					\
+										\
+	if (strcmp(FILENAME, filename) == 0)					\
+		return FNAME(pam_modutil_get, KIND, nam)(pamh, user);		\
+										\
+	D(("file: %s", filename));						\
+	pwfile = fopen(filename, "r");						\
+	if (pwfile == NULL) {							\
+		D(("Cannot open file %s", filename));				\
+		return NULL;							\
+	}									\
+										\
+	do {									\
+		int status;							\
+		void *new_buffer;						\
+										\
+		new_buffer = realloc(buffer, sizeof(TYPE) + length);		\
+		if (new_buffer == NULL) {					\
+			D(("out of memory"));					\
+			goto finish;						\
+		}								\
+		buffer = new_buffer;						\
+										\
+		/* make the re-entrant call to get the structure */		\
+		errno = 0;							\
+		status = FNAME(fget, KIND, ent_r)(pwfile, buffer,		\
+				     (char *) buffer + sizeof(TYPE), length,	\
+				     &result);					\
+		if (!status) {							\
+			D(("comparing username %s to %s", user, result->PWNAME));\
+			if (strcmp(result->PWNAME, user) != 0)			\
+				continue;					\
+										\
+			status = _pam_add_getnam_data(pamh, buffer, MODNAME, user);\
+			if (status == PAM_SUCCESS) {				\
+				fclose(pwfile);					\
+				return result;					\
+			}							\
+			goto finish;						\
+		}								\
+		D(("fgetXent_r failed with status code %d (bufsize %d)",	\
+		   errno, sizeof(TYPE) + length));				\
+		   								\
+		length <<= 2;							\
+										\
+	} while (length < PWD_ABSURD_PWD_LENGTH);				\
+										\
+	D(("structure took %u bytes or so of memory",				\
+	   length+sizeof(TYPE)));						\
+finish:										\
+	if (buffer != NULL)							\
+    		free(buffer);							\
+	fclose(pwfile);								\
+	return NULL;								\
+}
+
+
+#if defined(HELPER_COMPILE) || !defined(HAVE_FGETPWENT_R)
+DEFINE_FGETXNAM(struct passwd, pw, pw_name, "/etc/passwd")
+#endif
+
+#ifndef HELPER_COMPILE
+#ifdef HAVE_FGETPWENT_R
+DEFINE_FGETXNAM_R(struct passwd, pw, pw_name, "/etc/passwd", "_pam_unix_fgetpwnam_")
+#else
+static struct passwd *
+_fgetpwnam_r(pam_handle_t *pamh UNUSED, const char *filename, const char *user)
+{
+	return _fgetpwnam(filename, user);
+}
+#endif 
+#endif
+
+#if defined(HELPER_COMPILE) || !defined(HAVE_FGETSPENT_R)
+DEFINE_FGETXNAM(struct spwd, sp, sp_namp, "/etc/shadow")
+#endif /* ! HAVE_FGETSPENT_R */
+
+#ifndef HELPER_COMPILE
+#ifdef HAVE_FGETSPENT_R
+DEFINE_FGETXNAM_R(struct spwd, sp, sp_namp, "/etc/shadow", "_pam_unix_fgetspnam_")
+#else /* ! HAVE_FGETSPENT_R */
+static struct spwd *
+_fgetspnam_r(pam_handle_t *pamh UNUSED, const char *filename, const char *user)
+{
+	return _fgetspnam(filename, user);
+}
+#endif 
+#endif /* ! HELPER_COMPILE */
+
 PAMH_ARG_DECL(int get_account_info,
-	const char *name, struct passwd **pwd, struct spwd **spwdent)
+	unsigned int ctrl, const char *name, struct passwd **pwd, struct spwd **spwdent)
 {
 	/* UNIX passwords area */
-	*pwd = pam_modutil_getpwnam(pamh, name);	/* Get password file entry... */
+	*pwd = fgetpwnam(pamh, PASSWD_FILE, name);	/* Get password file entry... */
 	*spwdent = NULL;
 
 	if (*pwd != NULL) {
@@ -178,7 +361,7 @@ PAMH_ARG_DECL(int get_account_info,
 				}
 			}
 
-			*spwdent = pam_modutil_getspnam(pamh, name);
+			*spwdent = fgetspnam(pamh, SHADOW_FILE, name);
 			if (save_uid == (*pwd)->pw_uid)
 				setreuid(save_uid, save_euid);
 			else {
@@ -202,7 +385,7 @@ PAMH_ARG_DECL(int get_account_info,
 			if (geteuid() || SELINUX_ENABLED)
 				return PAM_UNIX_RUN_HELPER;
 #endif
-			*spwdent = pam_modutil_getspnam(pamh, name);
+			*spwdent = fgetspnam(pamh, SHADOW_FILE, name);
 			if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
 				return PAM_AUTHINFO_UNAVAIL;
 		}
@@ -213,12 +396,12 @@ PAMH_ARG_DECL(int get_account_info,
 }
 
 PAMH_ARG_DECL(int get_pwd_hash,
-	const char *name, struct passwd **pwd, char **hash)
+	unsigned int ctrl, const char *name, struct passwd **pwd, char **hash)
 {
 	int retval;
 	struct spwd *spwdent = NULL;
 
-	retval = get_account_info(PAMH_ARG(name, pwd, &spwdent));
+	retval = get_account_info(PAMH_ARG(ctrl, name, pwd, &spwdent));
 	if (retval != PAM_SUCCESS) {
 		return retval;
 	}
@@ -239,7 +422,7 @@ PAMH_ARG_DECL(int check_shadow_expiry,
 	long int curdays;
 	*daysleft = -1;
 	curdays = (long int)(time(NULL) / (60 * 60 * 24));
-	D(("today is %d, last change %d", curdays, spent->sp_lstchg));
+	D(("today is %ld, last change %ld", curdays, spent->sp_lstchg));
 	if ((curdays > spent->sp_expire) && (spent->sp_expire != -1)) {
 		D(("account expired"));
 		return PAM_ACCT_EXPIRED;
@@ -278,10 +461,6 @@ PAMH_ARG_DECL(int check_shadow_expiry,
 
 /* passwd/salt conversion macros */
 
-#define PW_TMPFILE              "/etc/npasswd"
-#define SH_TMPFILE              "/etc/nshadow"
-#define OPW_TMPFILE             "/etc/security/nopasswd"
-
 /*
  * i64c - convert an integer to a radix 64 character
  */
@@ -433,11 +612,11 @@ PAMH_ARG_DECL(char * create_password_hash,
 
 #ifdef WITH_SELINUX
 int
-unix_selinux_confined(void)
+unix_selinux_confined(unsigned int ctrl)
 {
     static int confined = -1;
     int fd;
-    char tempfile[]="/etc/.pwdXXXXXX";
+    char *tempfile = TEMPFILE;
 
     if (confined != -1)
     	return confined;
@@ -449,7 +628,7 @@ unix_selinux_confined(void)
     }
 
     /* let's try opening shadow read only */
-    if ((fd=open("/etc/shadow", O_RDONLY)) != -1) {
+    if ((fd=open(SHADOW_FILE, O_RDONLY)) != -1) {
         close(fd);
         confined = 0;
         return confined;
@@ -475,7 +654,7 @@ unix_selinux_confined(void)
 
 #else
 int
-unix_selinux_confined(void)
+unix_selinux_confined(unsigned int ctrl UNUSED)
 {
     return 0;
 }
@@ -483,13 +662,13 @@ unix_selinux_confined(void)
 
 #ifdef USE_LCKPWDF
 int
-lock_pwdf(void)
+lock_pwdf(unsigned int ctrl)
 {
         int i;
         int retval;
 
 #ifndef HELPER_COMPILE
-        if (unix_selinux_confined()) {
+        if (unix_selinux_confined(ctrl)) {
                 return PAM_SUCCESS;
         }
 #endif
@@ -500,7 +679,7 @@ lock_pwdf(void)
            reasonable steps to make sure the token is updated; so retrying
            for 1/10 sec. isn't overdoing it. */
         i=0;
-        while((retval = lckpwdf()) != 0 && i < 100) {
+        while((retval = __lckpwdf(ctrl)) != 0 && i < 100) {
                 usleep(1000);
                 i++;
         }
@@ -511,31 +690,32 @@ lock_pwdf(void)
 }
 
 void
-unlock_pwdf(void)
+unlock_pwdf(unsigned int ctrl)
 {
 #ifndef HELPER_COMPILE
-        if (unix_selinux_confined()) {
+        if (unix_selinux_confined(ctrl)) {
                 return;
         }
 #endif
-        ulckpwdf();
+        __ulckpwdf(ctrl);
 }
 #else
 int
-lock_pwdf(void)
+lock_pwdf(unsigned int ctrl UNUSED)
 {
 	return PAM_SUCCESS;
 }
 
 void
-unlock_pwdf(void)
+unlock_pwdf(unsigned int ctrl UNUSED)
 {
 	return;
 }
 #endif
 
 int
-save_old_password(const char *forwho, const char *oldpass,
+save_old_password(unsigned int ctrl,
+		  const char *forwho, const char *oldpass,
 		  int howmany)
 {
     static char buf[16384];
@@ -565,7 +745,7 @@ save_old_password(const char *forwho, const char *oldpass,
 #ifdef WITH_SELINUX
     if (SELINUX_ENABLED) {
       security_context_t passwd_context=NULL;
-      if (getfilecon("/etc/passwd",&passwd_context)<0) {
+      if (getfilecon(PASSWD_FILE,&passwd_context)<0) {
         return PAM_AUTHTOK_ERR;
       };
       if (getfscreatecon(&prev_context)<0) {
@@ -580,7 +760,7 @@ save_old_password(const char *forwho, const char *oldpass,
       freecon(passwd_context);
     }
 #endif
-    pwfile = fopen(OPW_TMPFILE, "w");
+    pwfile = fopen(OLD_PASSWORDS_TMPFILE, "w");
     umask(oldmask);
     if (pwfile == NULL) {
       err = 1;
@@ -673,7 +853,7 @@ save_old_password(const char *forwho, const char *oldpass,
 
 done:
     if (!err) {
-	if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
+	if (rename(OLD_PASSWORDS_TMPFILE, OLD_PASSWORDS_FILE))
 	    err = 1;
     }
 #ifdef WITH_SELINUX
@@ -689,13 +869,13 @@ done:
     if (!err) {
 	return PAM_SUCCESS;
     } else {
-	unlink(OPW_TMPFILE);
+	unlink(OLD_PASSWORDS_TMPFILE);
 	return PAM_AUTHTOK_ERR;
     }
 }
 
 PAMH_ARG_DECL(int unix_update_passwd,
-	const char *forwho, const char *towhat)
+	unsigned int ctrl, const char *forwho, const char *towhat)
 {
     struct passwd *tmpent = NULL;
     struct stat st;
@@ -710,7 +890,7 @@ PAMH_ARG_DECL(int unix_update_passwd,
 #ifdef WITH_SELINUX
     if (SELINUX_ENABLED) {
       security_context_t passwd_context=NULL;
-      if (getfilecon("/etc/passwd",&passwd_context)<0) {
+      if (getfilecon(PASSWD_FILE,&passwd_context)<0) {
 	return PAM_AUTHTOK_ERR;
       };
       if (getfscreatecon(&prev_context)<0) {
@@ -725,14 +905,14 @@ PAMH_ARG_DECL(int unix_update_passwd,
       freecon(passwd_context);
     }
 #endif
-    pwfile = fopen(PW_TMPFILE, "w");
+    pwfile = fopen(PASSWD_TMPFILE, "w");
     umask(oldmask);
     if (pwfile == NULL) {
       err = 1;
       goto done;
     }
 
-    opwfile = fopen("/etc/passwd", "r");
+    opwfile = fopen(PASSWD_FILE, "r");
     if (opwfile == NULL) {
 	fclose(pwfile);
 	err = 1;
@@ -788,7 +968,7 @@ PAMH_ARG_DECL(int unix_update_passwd,
 
 done:
     if (!err) {
-	if (!rename(PW_TMPFILE, "/etc/passwd"))
+	if (!rename(PASSWD_TMPFILE, PASSWD_FILE))
 	    pam_syslog(pamh,
 		LOG_NOTICE, "password changed for %s", forwho);
 	else
@@ -807,13 +987,13 @@ done:
     if (!err) {
 	return PAM_SUCCESS;
     } else {
-	unlink(PW_TMPFILE);
+	unlink(PASSWD_TMPFILE);
 	return PAM_AUTHTOK_ERR;
     }
 }
 
 PAMH_ARG_DECL(int unix_update_shadow,
-	const char *forwho, char *towhat)
+	unsigned int ctrl, const char *forwho, char *towhat)
 {
     struct spwd *spwdent = NULL, *stmpent = NULL;
     struct stat st;
@@ -833,7 +1013,7 @@ PAMH_ARG_DECL(int unix_update_shadow,
 #ifdef WITH_SELINUX
     if (SELINUX_ENABLED) {
       security_context_t shadow_context=NULL;
-      if (getfilecon("/etc/shadow",&shadow_context)<0) {
+      if (getfilecon(SHADOW_FILE,&shadow_context)<0) {
 	return PAM_AUTHTOK_ERR;
       };
       if (getfscreatecon(&prev_context)<0) {
@@ -848,14 +1028,14 @@ PAMH_ARG_DECL(int unix_update_shadow,
       freecon(shadow_context);
     }
 #endif
-    pwfile = fopen(SH_TMPFILE, "w");
+    pwfile = fopen(SHADOW_TMPFILE, "w");
     umask(oldmask);
     if (pwfile == NULL) {
 	err = 1;
 	goto done;
     }
 
-    opwfile = fopen("/etc/shadow", "r");
+    opwfile = fopen(SHADOW_FILE, "r");
     if (opwfile == NULL) {
 	fclose(pwfile);
 	err = 1;
@@ -909,7 +1089,7 @@ PAMH_ARG_DECL(int unix_update_shadow,
 
  done:
     if (!err) {
-	if (!rename(SH_TMPFILE, "/etc/shadow"))
+	if (!rename(SHADOW_TMPFILE, SHADOW_FILE))
 	    pam_syslog(pamh,
 		LOG_NOTICE, "password changed for %s", forwho);
 	else
@@ -930,7 +1110,7 @@ PAMH_ARG_DECL(int unix_update_shadow,
     if (!err) {
 	return PAM_SUCCESS;
     } else {
-	unlink(SH_TMPFILE);
+	unlink(SHADOW_TMPFILE);
 	return PAM_AUTHTOK_ERR;
     }
 }
@@ -938,13 +1118,14 @@ PAMH_ARG_DECL(int unix_update_shadow,
 #ifdef HELPER_COMPILE
 
 int
-helper_verify_password(const char *name, const char *p, int nullok)
+helper_verify_password(unsigned int ctrl,
+		       const char *name, const char *p, int nullok)
 {
 	struct passwd *pwd = NULL;
 	char *salt = NULL;
 	int retval;
 
-	retval = get_pwd_hash(name, &pwd, &salt);
+	retval = get_pwd_hash(ctrl, name, &pwd, &salt);
 
 	if (pwd == NULL || salt == NULL) {
 		helper_log_err(LOG_WARNING, "check pass; user unknown");
diff --git a/modules/pam_unix/passverify.h b/modules/pam_unix/passverify.h
index 888548d..e121518 100644
--- a/modules/pam_unix/passverify.h
+++ b/modules/pam_unix/passverify.h
@@ -10,8 +10,6 @@
 
 #define MAXPASS		200	/* the maximum length of a password */
 
-#define OLD_PASSWORDS_FILE      "/etc/security/opasswd"
-
 int
 verify_pwd_hash(const char *p, char *hash, unsigned int nullok);
 
@@ -22,16 +20,17 @@ char *
 crypt_md5_wrapper(const char *pass_new);
 
 int
-unix_selinux_confined(void);
+unix_selinux_confined(unsigned int ctrl);
 
 int
-lock_pwdf(void);
+lock_pwdf(unsigned int ctrl);
 
 void
-unlock_pwdf(void);
+unlock_pwdf(unsigned int ctrl);
 
 int
-save_old_password(const char *forwho, const char *oldpass,
+save_old_password(unsigned int ctrl,
+		  const char *forwho, const char *oldpass,
 		  int howmany);
 
 #ifdef HELPER_COMPILE
@@ -39,7 +38,8 @@ void
 helper_log_err(int err, const char *format,...);
 
 int
-helper_verify_password(const char *name, const char *p, int nullok);
+helper_verify_password(unsigned int ctrl,
+		       const char *name, const char *p, int nullok);
 
 void
 setup_signals(void);
@@ -63,19 +63,19 @@ 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);
+	unsigned int ctrl, const char *name, struct passwd **pwd, struct spwd **spwdent);
 
 PAMH_ARG_DECL(int get_pwd_hash,
-	const char *name, struct passwd **pwd, char **hash);
+	unsigned int ctrl, 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);
+	unsigned int ctrl, const char *forwho, const char *towhat);
 
 PAMH_ARG_DECL(int unix_update_shadow,
-	const char *forwho, char *towhat);
+	unsigned int ctrl, const char *forwho, char *towhat);
 
 /* ****************************************************************** *
  * Copyright (c) Red Hat, Inc. 2007.
diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c
index 400b1b9..56ef5cd 100644
--- a/modules/pam_unix/support.c
+++ b/modules/pam_unix/support.c
@@ -28,6 +28,7 @@
 
 #include "support.h"
 #include "passverify.h"
+#include "cluster.c"
 #ifdef WITH_SELINUX
 #include <selinux/selinux.h>
 #define SELINUX_ENABLED is_selinux_enabled()>0
@@ -140,6 +141,11 @@ int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds,
 				*rounds = 9999999;
 		}
 	}
+	
+	/* Set unix filename paths */
+	if (on(UNIX_CLUSTER, ctrl))
+		read_cluster_rootdir(pamh);
+	init_unix_files();
 
 	/* auditing is a more sensitive version of debug */
 
@@ -246,7 +252,7 @@ static void _unix_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_statu
 	free(data);
 }
 
-int _unix_getpwnam(pam_handle_t *pamh, const char *name,
+int _unix_getpwnam(pam_handle_t *pamh, unsigned int ctrl, const char *name,
 		   int files, int nis, struct passwd **ret)
 {
 	FILE *passwd;
@@ -258,7 +264,7 @@ int _unix_getpwnam(pam_handle_t *pamh, const char *name,
 
 	if (!matched && files) {
 		int userlen = strlen(name);
-		passwd = fopen("/etc/passwd", "r");
+		passwd = fopen(PASSWD_FILE, "r");
 		if (passwd != NULL) {
 			while (fgets(buf, sizeof(buf), passwd) != NULL) {
 				if ((buf[userlen] == ':') &&
@@ -392,9 +398,9 @@ int _unix_getpwnam(pam_handle_t *pamh, const char *name,
  *
  */
 int _unix_comesfromsource(pam_handle_t *pamh,
-			  const char *name, int files, int nis)
+			  unsigned int ctrl, const char *name, int files, int nis)
 {
-	return _unix_getpwnam(pamh, name, files, nis, NULL);
+	return _unix_getpwnam(pamh, ctrl, name, files, nis, NULL);
 }
 
 /*
@@ -435,7 +441,7 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
         int i=0;
         struct rlimit rlim;
 	static char *envp[] = { NULL };
-	char *args[] = { NULL, NULL, NULL, NULL };
+	char *args[] = { NULL, NULL, NULL, NULL, NULL };
 
 	/* XXX - should really tidy up PAM here too */
 
@@ -460,11 +466,9 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
 	/* exec binary helper */
 	args[0] = strdup(CHKPWD_HELPER);
 	args[1] = x_strdup(user);
-	if (off(UNIX__NONULL, ctrl)) {	/* this means we've succeeded */
-	  args[2]=strdup("nullok");
-	} else {
-	  args[2]=strdup("nonull");
-	}
+	/* off(UNIX__NONULL, ctrl) means we've succeeded */
+	args[2]=strdup(off(UNIX__NONULL, ctrl) ? "nullok" : "nonull");
+	args[3]=strdup(on(UNIX_CLUSTER, ctrl) ? "cluster" : "local");
 
 	execve(CHKPWD_HELPER, args, envp);
 
@@ -532,7 +536,7 @@ _unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
 
 	/* UNIX passwords area */
 
-	retval = get_pwd_hash(pamh, name, &pwd, &salt);
+	retval = get_pwd_hash(pamh, ctrl, name, &pwd, &salt);
 
 	if (retval == PAM_UNIX_RUN_HELPER) {
 		/* salt will not be set here so we can return immediately */
@@ -582,7 +586,7 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name
 
 	D(("locating user's record"));
 
-	retval = get_pwd_hash(pamh, name, &pwd, &salt);
+	retval = get_pwd_hash(pamh, ctrl, name, &pwd, &salt);
 
 	data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
 	if (data_name == NULL) {
@@ -676,17 +680,19 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name
 							    &tty);
 
 					pam_syslog(pamh, LOG_NOTICE,
-					         "authentication failure; "
-					         "logname=%s uid=%d euid=%d "
-					         "tty=%s ruser=%s rhost=%s "
-					         "%s%s",
+					         "authentication failure;"
+					         " logname=%s uid=%d euid=%d"
+					         " tty=%s ruser=%s rhost=%s"
+					         "%s%s"
+					         " pwfile=%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->user,
+					         PASSWD_FILE
 					);
 					new->count = 1;
 				}
diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h
index fadd978..3c9c587 100644
--- a/modules/pam_unix/support.h
+++ b/modules/pam_unix/support.h
@@ -89,8 +89,9 @@ typedef struct {
 #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_CLUSTER		 27	/* Use alternate passwd/shadow/etc. files */
 /* -------------- */
-#define UNIX_CTRLS_              27	/* number of ctrl arguments defined */
+#define UNIX_CTRLS_              28	/* number of ctrl arguments defined */
 
 
 static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
@@ -125,6 +126,7 @@ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
 /* UNIX_SHA512_PASS */     {"sha512",        _ALL_ON_^(020420000), 040000000},
 /* UNIX_ALGO_ROUNDS */     {"rounds=",         _ALL_ON_,          0100000000},
 /* UNIX_BLOWFISH_PASS */   {"blowfish",      _ALL_ON_^(060420000),0200000000},
+/* UNIX_CLUSTER */	   {"cluster",	       _ALL_ON_,	  0400000000},
 };
 
 #define UNIX_DEFAULTS  (unix_args[UNIX__NONULL].flag)
@@ -142,10 +144,10 @@ 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 argc, const char **argv);
-extern int _unix_getpwnam (pam_handle_t *pamh,
+extern int _unix_getpwnam (pam_handle_t *pamh, unsigned int ctrl,
 			   const char *name, int files, int nis,
 			   struct passwd **ret);
-extern int _unix_comesfromsource (pam_handle_t *pamh,
+extern int _unix_comesfromsource (pam_handle_t *pamh, unsigned int ctrl,
 				  const char *name, int files, int nis);
 extern int _unix_blankpasswd(pam_handle_t *pamh,unsigned int ctrl,
 			     const char *name);
diff --git a/modules/pam_unix/unix_chkpwd.c b/modules/pam_unix/unix_chkpwd.c
index 5f872d2..34f3c29 100644
--- a/modules/pam_unix/unix_chkpwd.c
+++ b/modules/pam_unix/unix_chkpwd.c
@@ -29,15 +29,16 @@
 #include <security/_pam_macros.h>
 
 #include "passverify.h"
+#include "cluster.c"
 
-static int _check_expiry(const char *uname)
+static int _check_expiry(unsigned int ctrl, const char *uname)
 {
 	struct spwd *spent;
 	struct passwd *pwent;
 	int retval;
 	int daysleft;
 
-	retval = get_account_info(uname, &pwent, &spent);
+	retval = get_account_info(ctrl, uname, &pwent, &spent);
 	if (retval != PAM_SUCCESS) {
 		helper_log_err(LOG_ALERT, "could not obtain user info (%s)", uname);
 		printf("-1\n");
@@ -58,8 +59,10 @@ int main(int argc, char *argv[])
 {
 	char pass[MAXPASS + 1];
 	char *option;
-	int npass, nullok;
+	int npass, nullok = 0;
 	int blankpass = 0;
+	int chkexpiry = 0;
+	unsigned int ctrl = 0;
 	int retval = PAM_AUTH_ERR;
 	char *user;
 	char *passwords[] = { pass };
@@ -78,7 +81,7 @@ int main(int argc, char *argv[])
 	 * account).
 	 */
 
-	if (isatty(STDIN_FILENO) || argc != 3 ) {
+	if (isatty(STDIN_FILENO) || argc != 4 ) {
 		helper_log_err(LOG_NOTICE
 		      ,"inappropriate use of Unix helper binary [UID=%d]"
 			 ,getuid());
@@ -112,7 +115,7 @@ int main(int argc, char *argv[])
 
 	if (strcmp(option, "chkexpiry") == 0)
 	  /* Check account information from the shadow file */
-	  return _check_expiry(argv[1]);	  
+	  chkexpiry = 1;
 	/* read the nullok/nonull option */
 	else if (strcmp(option, "nullok") == 0)
 	  nullok = 1;
@@ -120,6 +123,18 @@ int main(int argc, char *argv[])
 	  nullok = 0;
 	else
 	  return PAM_SYSTEM_ERR;
+	
+	if (strcmp(argv[3], "cluster") == 0) {
+		ctrl = UNIX_CLUSTER;
+		if (read_cluster_rootdir() < 0)
+			return PAM_SYSTEM_ERR;
+	} if (strcmp(argv[3], "local") != 0)
+		return PAM_SYSTEM_ERR;
+	
+	init_unix_files();
+	
+	if (chkexpiry)
+		return _check_expiry(ctrl, argv[1]);
 
 	/* read the password from stdin (a pipe from the pam_unix module) */
 
@@ -134,7 +149,7 @@ int main(int argc, char *argv[])
 		blankpass = 1;
 	}
 
-	retval = helper_verify_password(user, pass, nullok);
+	retval = helper_verify_password(ctrl, user, pass, nullok);
 
 	memset(pass, '\0', MAXPASS);	/* clear memory of the password */
 
diff --git a/modules/pam_unix/unix_update.c b/modules/pam_unix/unix_update.c
index f54a59c..5af3d23 100644
--- a/modules/pam_unix/unix_update.c
+++ b/modules/pam_unix/unix_update.c
@@ -32,9 +32,11 @@
 #include <security/_pam_macros.h>
 
 #include "passverify.h"
+#include "cluster.c"
 
 static int
-set_password(const char *forwho, const char *shadow, const char *remember)
+set_password(unsigned int ctrl,
+	     const char *forwho, const char *shadow, const char *remember)
 {
     struct passwd *pwd = NULL;
     int retval;
@@ -61,7 +63,7 @@ set_password(const char *forwho, const char *shadow, const char *remember)
       return PAM_AUTHTOK_ERR;
     }
 
-    if (lock_pwdf() != PAM_SUCCESS)
+    if (lock_pwdf(ctrl) != PAM_SUCCESS)
     	return PAM_AUTHTOK_LOCK_BUSY;
 
     pwd = getpwnam(forwho);
@@ -73,31 +75,31 @@ set_password(const char *forwho, const char *shadow, const char *remember)
 
     /* does pass agree with the official one?
        we always allow change from null pass */
-    retval = helper_verify_password(forwho, pass, 1);
+    retval = helper_verify_password(ctrl, forwho, pass, 1);
     if (retval != PAM_SUCCESS) {
 	goto done;
     }
 
     /* first, save old password */
-    if (save_old_password(forwho, pass, nremember)) {
+    if (save_old_password(ctrl, forwho, pass, nremember)) {
 	retval = PAM_AUTHTOK_ERR;
 	goto done;
     }
 
     if (doshadow || is_pwd_shadowed(pwd)) {
-	retval = unix_update_shadow(forwho, towhat);
+	retval = unix_update_shadow(ctrl, forwho, towhat);
 	if (retval == PAM_SUCCESS)
 	    if (!is_pwd_shadowed(pwd))
-		retval = unix_update_passwd(forwho, "x");
+		retval = unix_update_passwd(ctrl, forwho, "x");
     } else {
-	retval = unix_update_passwd(forwho, towhat);
+	retval = unix_update_passwd(ctrl, forwho, towhat);
     }
 
 done:
     memset(pass, '\0', MAXPASS);
     memset(towhat, '\0', MAXPASS);
 
-    unlock_pwdf();
+    unlock_pwdf(ctrl);
 
     if (retval == PAM_SUCCESS) {
 	return PAM_SUCCESS;
@@ -109,6 +111,7 @@ done:
 int main(int argc, char *argv[])
 {
 	char *option;
+	unsigned int ctrl = 0;
 
 	/*
 	 * Catch or ignore as many signal as possible.
@@ -124,7 +127,7 @@ int main(int argc, char *argv[])
 	 * account).
 	 */
 
-	if (isatty(STDIN_FILENO) || argc != 5 ) {
+	if (isatty(STDIN_FILENO) || argc != 6 ) {
 		helper_log_err(LOG_NOTICE
 		      ,"inappropriate use of Unix helper binary [UID=%d]"
 			 ,getuid());
@@ -142,10 +145,19 @@ int main(int argc, char *argv[])
 	}
 
 	option = argv[2];
+	
+	if (strcmp(argv[5], "cluster") == 0) {
+		ctrl = UNIX_CLUSTER;
+		if (read_cluster_rootdir() < 0)
+			return PAM_SYSTEM_ERR;
+	} else if (strcmp(argv[5], "local") != 0)
+		return PAM_SYSTEM_ERR;
+	
+	init_unix_files();
 
 	if (strcmp(option, "update") == 0) {
 	    /* Attempting to change the password */
-	    return set_password(argv[1], argv[3], argv[4]);
+	    return set_password(ctrl, argv[1], argv[3], argv[4]);
 	}
 
 	return PAM_SYSTEM_ERR;
-- 
1.5.3.4

