walters / rpms / pam

Forked from rpms/pam 5 years ago
Clone
Blob Blame History Raw
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c
--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c.update-helper	2006-06-17 18:44:58.000000000 +0200
+++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c	2008-01-07 16:39:07.000000000 +0100
@@ -73,7 +73,7 @@ PAM_EXTERN int pam_sm_open_session(pam_h
 
 	D(("called."));
 
-	ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
+	ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
 
 	retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
 	if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
@@ -107,7 +107,7 @@ PAM_EXTERN int pam_sm_close_session(pam_
 
 	D(("called."));
 
-	ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
+	ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
 
 	retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
 	if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c
--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper	2007-04-30 12:47:30.000000000 +0200
+++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c	2008-01-08 16:17:32.000000000 +0100
@@ -2,6 +2,7 @@
  * 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
@@ -63,7 +64,6 @@
 #ifdef WITH_SELINUX
 static int selinux_enabled=-1;
 #include <selinux/selinux.h>
-static security_context_t prev_context=NULL;
 #define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
 #endif
 
@@ -84,6 +84,7 @@ static security_context_t prev_context=N
 #include "yppasswd.h"
 #include "md5.h"
 #include "support.h"
+#include "passverify.h"
 #include "bigcrypt.h"
 
 #if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
@@ -92,15 +93,6 @@ extern int getrpcport(const char *host, 
 #endif				/* GNU libc 2.1 */
 
 /*
- * PAM framework looks for these entry-points to pass control to the
- * password changing module.
- */
-
-#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
-# include "./lckpwdf.-c"
-#endif
-
-/*
    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)
@@ -108,82 +100,15 @@ extern int getrpcport(const char *host, 
    Sets it.
  */
 
-/* passwd/salt conversion macros */
-
-#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
-#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
-
 /* data tokens */
 
 #define _UNIX_OLD_AUTHTOK	"-UN*X-OLD-PASS"
 #define _UNIX_NEW_AUTHTOK	"-UN*X-NEW-PASS"
 
 #define MAX_PASSWD_TRIES	3
-#define PW_TMPFILE		"/etc/npasswd"
-#define SH_TMPFILE		"/etc/nshadow"
 #ifndef CRACKLIB_DICTS
 #define CRACKLIB_DICTS		NULL
 #endif
-#define OPW_TMPFILE		"/etc/security/nopasswd"
-#define OLD_PASSWORDS_FILE	"/etc/security/opasswd"
-
-/*
- * 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');
-}
-
-static char *crypt_md5_wrapper(const char *pass_new)
-{
-	/*
-	 * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
-	 * removed use of static variables (AGM)
-	 */
-
-	struct timeval tv;
-	MD5_CTX ctx;
-	unsigned char result[16];
-	char *cp = (char *) result;
-	unsigned char tmp[16];
-	int i;
-	char *x = NULL;
-
-	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, result, sizeof result);
-	GoodMD5Final(tmp, &ctx);
-	strcpy(cp, "$1$");	/* magic for the MD5 */
-	cp += strlen(cp);
-	for (i = 0; i < 8; i++)
-		*cp++ = i64c(tmp[i] & 077);
-	*cp = '\0';
-
-	/* no longer need cleartext */
-	x = Goodcrypt_md5(pass_new, (const char *) result);
-
-	return x;
-}
 
 static char *getNISserver(pam_handle_t *pamh)
 {
@@ -217,7 +142,8 @@ static char *getNISserver(pam_handle_t *
 
 #ifdef WITH_SELINUX
 
-static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user, const char *fromwhat, const char *towhat)
+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];
     void (*sighandler)(int) = NULL;
@@ -247,7 +173,8 @@ static int _unix_run_shadow_binary(pam_h
         size_t i=0;
         struct rlimit rlim;
 	static char *envp[] = { NULL };
-	char *args[] = { NULL, NULL, NULL, NULL };
+	char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
+        char buffer[16];
 
 	/* XXX - should really tidy up PAM here too */
 
@@ -270,11 +197,18 @@ static int _unix_run_shadow_binary(pam_h
         }
 
 	/* exec binary helper */
-	args[0] = x_strdup(CHKPWD_HELPER);
+	args[0] = x_strdup(UPDATE_HELPER);
 	args[1] = x_strdup(user);
-	args[2] = x_strdup("shadow");
+	args[2] = x_strdup("update");
+	if (on(UNIX_SHADOW, ctrl))
+		args[3] = x_strdup("1");
+	else
+		args[3] = x_strdup("0");
 
-	execve(CHKPWD_HELPER, args, envp);
+        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"));
@@ -297,7 +231,7 @@ static int _unix_run_shadow_binary(pam_h
 	close(fds[1]);
 	rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
 	if (rc<0) {
-	  pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
+	  pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
 	  retval = PAM_AUTH_ERR;
 	} else {
 	  retval = WEXITSTATUS(retval);
@@ -354,393 +288,6 @@ static int check_old_password(const char
 	return retval;
 }
 
-static int save_old_password(pam_handle_t *pamh,
-			     const char *forwho, const char *oldpass,
-			     int howmany)
-{
-    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;
-
-    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("/etc/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, strlen(forwho))) {
-	    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);
-	    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;
-	    }
-	    found = 1;
-	} 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 (fclose(pwfile)) {
-	D(("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;
-    }
-}
-
-static int _update_passwd(pam_handle_t *pamh,
-			  const char *forwho, const char *towhat)
-{
-    struct passwd *tmpent = NULL;
-    struct stat st;
-    FILE *pwfile, *opwfile;
-    int err = 1;
-    int oldmask;
-
-    oldmask = umask(077);
-#ifdef WITH_SELINUX
-    if (SELINUX_ENABLED) {
-      security_context_t passwd_context=NULL;
-      if (getfilecon("/etc/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("/etc/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;
-	}
-	if (putpwent(tmpent, pwfile)) {
-	    D(("error writing entry to password file: %m"));
-	    err = 1;
-	    break;
-	}
-	tmpent = fgetpwent(opwfile);
-    }
-    fclose(opwfile);
-
-    if (fclose(pwfile)) {
-	D(("error writing entries to password file: %m"));
-	err = 1;
-    }
-
-done:
-    if (!err) {
-	if (!rename(PW_TMPFILE, "/etc/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 PAM_AUTHTOK_ERR;
-    }
-}
-
-static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
-{
-    struct spwd *spwdent = NULL, *stmpent = NULL;
-    struct stat st;
-    FILE *pwfile, *opwfile;
-    int err = 1;
-    int oldmask;
-
-    spwdent = getspnam(forwho);
-    if (spwdent == NULL) {
-	return PAM_USER_UNKNOWN;
-    }
-    oldmask = umask(077);
-
-#ifdef WITH_SELINUX
-    if (SELINUX_ENABLED) {
-      security_context_t shadow_context=NULL;
-      if (getfilecon("/etc/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("/etc/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);
-	    err = 0;
-	    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 (fclose(pwfile)) {
-	D(("error writing entries to shadow file: %m"));
-	err = 1;
-    }
-
- done:
-    if (!err) {
-	if (!rename(SH_TMPFILE, "/etc/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;
-    }
-}
-
 static int _do_setpass(pam_handle_t* pamh, const char *forwho,
 		       const char *fromwhat,
 		       char *towhat, unsigned int ctrl, int remember)
@@ -768,9 +315,7 @@ static int _do_setpass(pam_handle_t* pam
 		enum clnt_stat err;
 
 		/* Unlock passwd file to avoid deadlock */
-#ifdef USE_LCKPWDF
-		ulckpwdf();
-#endif
+		unlock_pwdf();
 		unlocked = 1;
 
 		/* Initialize password information */
@@ -830,129 +375,63 @@ static int _do_setpass(pam_handle_t* pam
 	}
 
 	if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
-#ifdef USE_LCKPWDF
 		if(unlocked) {
-			int i = 0;
-			/* 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. */
-			while((retval = lckpwdf()) != 0 && i < 100) {
-				usleep(1000);
-				i++;
-			}
-			if(retval != 0) {
+			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)) {
+		if (save_old_password(forwho, fromwhat, remember)) {
 			retval = PAM_AUTHTOK_ERR;
 			goto done;
 		}
-		if (on(UNIX_SHADOW, ctrl) || _unix_shadowed(pwd)) {
-			retval = _update_shadow(pamh, forwho, towhat);
-#ifdef WITH_SELINUX
- 		        if (retval != PAM_SUCCESS && SELINUX_ENABLED)
-			  retval = _unix_run_shadow_binary(pamh, ctrl, forwho, fromwhat, towhat);
-#endif
+		if (on(UNIX_SHADOW, ctrl) || is_pwd_shadowed(pwd)) {
+			retval = unix_update_shadow(pamh, forwho, towhat);
 			if (retval == PAM_SUCCESS)
-				if (!_unix_shadowed(pwd))
-					retval = _update_passwd(pamh, forwho, "x");
+				if (!is_pwd_shadowed(pwd))
+					retval = unix_update_passwd(pamh, forwho, "x");
 		} else {
-			retval = _update_passwd(pamh, forwho, towhat);
+			retval = unix_update_passwd(pamh, forwho, towhat);
 		}
 	}
 
 
 done:
-#ifdef USE_LCKPWDF
-	ulckpwdf();
-#endif
+	unlock_pwdf();
 
 	return retval;
 }
 
 static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl)
 {
-	struct passwd *pwd = NULL;	/* Password and shadow password */
-	struct spwd *spwdent = NULL;	/* file entries for the user */
-	time_t curdays;
-	int retval = PAM_SUCCESS;
+	struct passwd *pwent = NULL;	/* Password and shadow password */
+	struct spwd *spent = NULL;	/* file entries for the user */
+	int daysleft;
+	int retval;
 
-	/* UNIX passwords area */
-	pwd = getpwnam(user);	/* Get password file entry... */
-	if (pwd == NULL)
-		return PAM_AUTHINFO_UNAVAIL;	/* We don't need to do the rest... */
-
-	if (_unix_shadowed(pwd)) {
-		/* ...and shadow password file entry for this user, if shadowing
-		   is enabled */
-		setspent();
-		spwdent = getspnam(user);
-		endspent();
+	retval = get_account_info(pamh, user, &pwent, &spent);
+	if (retval == PAM_USER_UNKNOWN) {
+		return retval;
+	}
 
-#ifdef WITH_SELINUX
-		if (spwdent == NULL && SELINUX_ENABLED )
-		    spwdent = _unix_run_verify_binary(pamh, ctrl, user);
-#endif
-		if (spwdent == NULL)
-			return PAM_AUTHINFO_UNAVAIL;
-	} else {
-		if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */
-			uid_t save_uid;
+	if (retval == PAM_SUCCESS && spent == NULL)
+		return PAM_SUCCESS;
 
-			save_uid = geteuid();
-			seteuid (pwd->pw_uid);
-			spwdent = getspnam( user );
-			seteuid (save_uid);
-
-			if (spwdent == NULL)
-				return PAM_AUTHINFO_UNAVAIL;
-		} else
-			spwdent = NULL;
+	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;
 
-	if (spwdent != NULL) {
-		/* We have the user's information, now let's check if their account
-		   has expired (60 * 60 * 24 = number of seconds in a day) */
-
-		if (off(UNIX__IAMROOT, ctrl)) {
-			/* Get the current number of days since 1970 */
-			curdays = time(NULL) / (60 * 60 * 24);
-			if (curdays < spwdent->sp_lstchg) {
-				pam_syslog(pamh, LOG_DEBUG,
-					"account %s has password changed in future",
-					user);
-				curdays = spwdent->sp_lstchg;
-			}
-			if ((curdays - spwdent->sp_lstchg < spwdent->sp_min)
-				 && (spwdent->sp_min != -1))
-				/*
-				 * The last password change was too recent.
-				 */
-				retval = PAM_AUTHTOK_ERR;
-			else if ((curdays - spwdent->sp_lstchg > spwdent->sp_max)
-				 && (curdays - spwdent->sp_lstchg > spwdent->sp_inact)
-				 && (curdays - spwdent->sp_lstchg >
-				     spwdent->sp_max + spwdent->sp_inact)
-				 && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
-				 && (spwdent->sp_lstchg != 0))
-				/*
-				 * Their password change has been put off too long,
-				 */
-				retval = PAM_ACCT_EXPIRED;
-			else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
-				 && (spwdent->sp_lstchg != 0))
-				/*
-				 * OR their account has just plain expired
-				 */
-				retval = PAM_ACCT_EXPIRED;
-		}
-	}
 	return retval;
 }
 
@@ -1020,8 +499,9 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
 				int argc, const char **argv)
 {
 	unsigned int ctrl, lctrl;
-	int retval, i;
+	int retval;
 	int remember = -1;
+	int rounds = -1;
 
 	/* <DO NOT free() THESE> */
 	const char *user;
@@ -1030,7 +510,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
 
 	D(("called."));
 
-	ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);
+	ctrl = _set_ctrl(pamh, flags, &remember, &rounds, argc, argv);
 
 	/*
 	 * First get the name of a user
@@ -1239,40 +719,23 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
 			pass_new = pass_old = NULL;	/* tidy up */
 			return retval;
 		}
-#ifdef USE_LCKPWDF
-		/* 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 = lckpwdf()) != 0 && i < 100) {
-			usleep(1000);
-			i++;
-		}
-		if(retval != 0) {
+		if (lock_pwdf() != PAM_SUCCESS) {
 			return PAM_AUTHTOK_LOCK_BUSY;
 		}
-#endif
 
 		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");
-#ifdef USE_LCKPWDF
-				ulckpwdf();
-#endif
+				unlock_pwdf();
 				return retval;
 			}
 		}
 
 		retval = _unix_verify_shadow(pamh, user, ctrl);
 		if (retval != PAM_SUCCESS) {
-			pam_syslog(pamh, LOG_NOTICE, "user not authenticated 2");
-#ifdef USE_LCKPWDF
-			ulckpwdf();
-#endif
+			pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired");
+			unlock_pwdf();
 			return retval;
 		}
 
@@ -1281,9 +744,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
 			pam_syslog(pamh, LOG_NOTICE,
 			         "new password not acceptable 2");
 			pass_new = pass_old = NULL;	/* tidy up */
-#ifdef USE_LCKPWDF
-			ulckpwdf();
-#endif
+			unlock_pwdf();
 			return retval;
 		}
 
@@ -1296,51 +757,13 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
 		 * First we encrypt the new password.
 		 */
 
-		if (on(UNIX_MD5_PASS, ctrl)) {
-			tpass = crypt_md5_wrapper(pass_new);
-		} else {
-			/*
-			 * Salt manipulation is stolen from Rick Faith's passwd
-			 * program.  Sorry Rick :) -- alex
-			 */
-
-			time_t tm;
-			char salt[3];
-
-			time(&tm);
-			salt[0] = bin_to_ascii(tm & 0x3f);
-			salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
-			salt[2] = '\0';
-
-			if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
-				/*
-				 * to avoid using the _extensions_ of the bigcrypt()
-				 * function we truncate the newly entered password
-				 * [Problems that followed from this are fixed as per
-				 *  Bug 521314.]
-				 */
-				char *temp = malloc(9);
-
-				if (temp == NULL) {
-					pam_syslog(pamh, LOG_CRIT,
-					         "out of memory for password");
-					pass_new = pass_old = NULL;	/* tidy up */
-#ifdef USE_LCKPWDF
-					ulckpwdf();
-#endif
-					return PAM_BUF_ERR;
-				}
-				/* copy first 8 bytes of password */
-				strncpy(temp, pass_new, 8);
-				temp[8] = '\0';
-
-				/* no longer need cleartext */
-				tpass = bigcrypt(temp, salt);
-
-				_pam_delete(temp);	/* tidy up */
-			} else {
-				tpass = bigcrypt(pass_new, salt);
-			}
+		tpass = create_password_hash(pass_new, ctrl, rounds);
+		if (tpass == NULL) {
+			pam_syslog(pamh, LOG_CRIT,
+				"out of memory for password");
+			pass_new = pass_old = NULL;	/* tidy up */
+			unlock_pwdf();
+			return PAM_BUF_ERR;
 		}
 
 		D(("password processed"));
@@ -1349,7 +772,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
 
 		retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
 		                     remember);
-	        /* _do_setpass has called ulckpwdf for us */
+	        /* _do_setpass has called unlock_pwdf for us */
 
 		_pam_delete(tpass);
 		pass_old = pass_new = NULL;
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c
--- Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper	2007-03-12 15:35:14.000000000 +0100
+++ Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c	2008-01-08 16:17:32.000000000 +0100
@@ -13,7 +13,6 @@
 
 #include "config.h"
 
-#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -25,401 +24,34 @@
 #include <shadow.h>
 #include <signal.h>
 #include <time.h>
-#ifdef WITH_SELINUX
-#include <selinux/selinux.h>
-#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
-static security_context_t prev_context=NULL;
-static int selinux_enabled=-1;
-#else
-#define SELINUX_ENABLED 0
-#endif
-
-#define MAXPASS		200	/* the maximum length of a password */
 
 #include <security/_pam_types.h>
 #include <security/_pam_macros.h>
 
-#include "md5.h"
-#include "bigcrypt.h"
-
-/* syslogging function for errors and other information */
-
-static void _log_err(int err, const char *format,...)
-{
-	va_list args;
-
-	va_start(args, format);
-	openlog("unix_chkpwd", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
-	vsyslog(err, format, args);
-	va_end(args);
-	closelog();
-}
-
-static int _unix_shadowed(const struct passwd *pwd)
-{
-	char hashpass[1024];
-	if (pwd != NULL) {
-		if (strcmp(pwd->pw_passwd, "x") == 0) {
-			return 1;
-		}
-		if (strlen(pwd->pw_name) < sizeof(hashpass) - 2) {
-			strcpy(hashpass, "##");
-			strcpy(hashpass + 2, pwd->pw_name);
-			if (strcmp(pwd->pw_passwd, hashpass) == 0) {
-				return 1;
-			}
-		}
-	}
-	return 0;
-}
-
-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 )
-		signal(sig, SIG_DFL);
-#endif
-	if (sig > 0) {
-		_log_err(LOG_NOTICE, "caught signal %d.", sig);
-		exit(sig);
-	}
-}
-
-static 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);
-}
+#include "passverify.h"
 
-static int _verify_account(const char * const uname)
+static int _check_expiry(const char *uname)
 {
 	struct spwd *spent;
 	struct passwd *pwent;
+	int retval;
+	int daysleft;
 
-	pwent = getpwnam(uname);
-	if (!pwent) {
-		_log_err(LOG_ALERT, "could not identify user (from getpwnam(%s))", uname);
-		return PAM_USER_UNKNOWN;
-	}
-
-	spent = getspnam( uname );
-	if (!spent) {
-		_log_err(LOG_ALERT, "could not get username from shadow (%s))", uname);
-		return PAM_AUTHINFO_UNAVAIL;	/* Couldn't get username from shadow */
-	}
-	printf("%ld:%ld:%ld:%ld:%ld:%ld",
-		 spent->sp_lstchg, /* last password change */
-                 spent->sp_min, /* days until change allowed. */
-                 spent->sp_max, /* days before change required */
-                 spent->sp_warn, /* days warning for expiration */
-                 spent->sp_inact, /* days before account inactive */
-                 spent->sp_expire); /* date when account expires */
-
-	return PAM_SUCCESS;
-}
-
-static int _unix_verify_password(const char *name, const char *p, int nullok)
-{
-	struct passwd *pwd = NULL;
-	struct spwd *spwdent = NULL;
-	char *salt = NULL;
-	char *pp = NULL;
-	int retval = PAM_AUTH_ERR;
-	size_t salt_len;
-
-	/* UNIX passwords area */
-	setpwent();
-	pwd = getpwnam(name);	/* Get password file entry... */
-	endpwent();
-	if (pwd != NULL) {
-		if (_unix_shadowed(pwd)) {
-			/*
-			 * ...and shadow password file entry for this user,
-			 * if shadowing is enabled
-			 */
-			setspent();
-			spwdent = getspnam(name);
-			endspent();
-			if (spwdent != NULL)
-				salt = x_strdup(spwdent->sp_pwdp);
-			else
-				pwd = NULL;
-		} else {
-			if (strcmp(pwd->pw_passwd, "*NP*") == 0) {	/* NIS+ */
-				uid_t save_uid;
-
-				save_uid = geteuid();
-				seteuid(pwd->pw_uid);
-				spwdent = getspnam(name);
-				seteuid(save_uid);
-
-				salt = x_strdup(spwdent->sp_pwdp);
-			} else {
-				salt = x_strdup(pwd->pw_passwd);
-			}
-		}
-	}
-	if (pwd == NULL || salt == NULL) {
-		_log_err(LOG_ALERT, "check pass; user unknown");
-		p = NULL;
-		return PAM_USER_UNKNOWN;
-	}
-
-	salt_len = strlen(salt);
-	if (salt_len == 0) {
-		return (nullok == 0) ? PAM_AUTH_ERR : PAM_SUCCESS;
-	}
-	if (p == NULL || strlen(p) == 0) {
-		_pam_overwrite(salt);
-		_pam_drop(salt);
-		return PAM_AUTHTOK_ERR;
-	}
-
-	/* the moment of truth -- do we agree with the password? */
-	retval = PAM_AUTH_ERR;
-	if (!strncmp(salt, "$1$", 3)) {
-		pp = Goodcrypt_md5(p, salt);
-		if (pp && strcmp(pp, salt) == 0) {
-			retval = PAM_SUCCESS;
-		} else {
-			_pam_overwrite(pp);
-			_pam_drop(pp);
-			pp = Brokencrypt_md5(p, salt);
-			if (pp && strcmp(pp, salt) == 0)
-				retval = PAM_SUCCESS;
-		}
-	} else if (*salt == '$') {
-	        /*
-		 * Ok, we don't know the crypt algorithm, but maybe
-		 * libcrypt nows about it? We should try it.
-		 */
-	        pp = x_strdup (crypt(p, salt));
-		if (pp && strcmp(pp, salt) == 0) {
-			retval = PAM_SUCCESS;
-		}
-	} else if (*salt == '*' || *salt == '!' || salt_len < 13) {
-	    retval = PAM_AUTH_ERR;
-	} else {
-		pp = bigcrypt(p, salt);
-		/*
-		 * Note, we are comparing the bigcrypt of the password with
-		 * the contents of the password field. If the latter was
-		 * encrypted with regular crypt (and not bigcrypt) it will
-		 * have been truncated for storage relative to the output
-		 * of bigcrypt here. As such we need to compare only the
-		 * stored string with the subset of bigcrypt's result.
-		 * Bug 521314.
-		 */
-		if (pp && salt_len == 13 && strlen(pp) > salt_len) {
-		    _pam_overwrite(pp+salt_len);
-		}
-		
-		if (pp && strcmp(pp, salt) == 0) {
-			retval = PAM_SUCCESS;
-		}
+	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;
 	}
-	p = NULL;		/* no longer needed here */
 
-	/* clean up */
-	_pam_overwrite(pp);
-	_pam_drop(pp);
-
-	return retval;
-}
-
-static char *getuidname(uid_t uid)
-{
-	struct passwd *pw;
-	static char username[32];
-
-	pw = getpwuid(uid);
-	if (pw == NULL)
-		return NULL;
-
-	strncpy(username, pw->pw_name, sizeof(username));
-	username[sizeof(username) - 1] = '\0';
-
-	return username;
-}
-
-#define SH_TMPFILE		"/etc/nshadow"
-static int _update_shadow(const char *forwho)
-{
-    struct spwd *spwdent = NULL, *stmpent = NULL;
-    FILE *pwfile, *opwfile;
-    int err = 1;
-    int oldmask;
-    struct stat st;
-    char pass[MAXPASS + 1];
-    char towhat[MAXPASS + 1];
-    int npass=0;
-
-    /* read the password from stdin (a pipe from the pam_unix module) */
-
-    npass = read(STDIN_FILENO, pass, MAXPASS);
-
-    if (npass < 0) {	/* is it a valid password? */
-
-      _log_err(LOG_DEBUG, "no password supplied");
-      return PAM_AUTHTOK_ERR;
-
-    } else if (npass >= MAXPASS) {
-
-      _log_err(LOG_DEBUG, "password too long");
-      return PAM_AUTHTOK_ERR;
-
-    } else {
-      /* does pass agree with the official one? */
-      int retval=0;
-      pass[npass] = '\0';	/* NUL terminate */
-      retval = _unix_verify_password(forwho, pass, 0);
-      if (retval != PAM_SUCCESS) {
+	retval = check_shadow_expiry(spent, &daysleft);
+	printf("%d\n", daysleft);
 	return retval;
-      }
-    }
-
-    /* read the password from stdin (a pipe from the pam_unix module) */
-
-    npass = read(STDIN_FILENO, towhat, MAXPASS);
-
-    if (npass < 0) {	/* is it a valid password? */
-
-      _log_err(LOG_DEBUG, "no new password supplied");
-      return PAM_AUTHTOK_ERR;
-
-    } else if (npass >= MAXPASS) {
-
-      _log_err(LOG_DEBUG, "new password too long");
-      return PAM_AUTHTOK_ERR;
-
-    }
-
-    towhat[npass] = '\0';	/* NUL terminate */
-    spwdent = getspnam(forwho);
-    if (spwdent == NULL) {
-	return PAM_USER_UNKNOWN;
-    }
-    oldmask = umask(077);
-
-#ifdef WITH_SELINUX
-    if (SELINUX_ENABLED) {
-      security_context_t shadow_context=NULL;
-      if (getfilecon("/etc/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("/etc/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);
-	    err = 0;
-	    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 (fclose(pwfile)) {
-	D(("error writing entries to shadow file: %m"));
-	err = 1;
-    }
-
- done:
-    if (!err) {
-	if (rename(SH_TMPFILE, "/etc/shadow"))
-	    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;
-    }
 }
 
 int main(int argc, char *argv[])
@@ -427,9 +59,10 @@ int main(int argc, char *argv[])
 	char pass[MAXPASS + 1];
 	char *option;
 	int npass, nullok;
-	int force_failure = 0;
+	int blankpass = 0;
 	int retval = PAM_AUTH_ERR;
 	char *user;
+	char *passwords[] = { pass };
 
 	/*
 	 * Catch or ignore as many signal as possible.
@@ -446,7 +79,7 @@ int main(int argc, char *argv[])
 	 */
 
 	if (isatty(STDIN_FILENO) || argc != 3 ) {
-		_log_err(LOG_NOTICE
+		helper_log_err(LOG_NOTICE
 		      ,"inappropriate use of Unix helper binary [UID=%d]"
 			 ,getuid());
 		fprintf(stderr
@@ -458,11 +91,9 @@ int main(int argc, char *argv[])
 
 	/*
 	 * Determine what the current user's name is.
-	 * On a SELinux enabled system with a strict policy leaving the
-	 * existing check prevents shadow password authentication from working.
 	 * We must thus skip the check if the real uid is 0.
 	 */
-	if (SELINUX_ENABLED && getuid() == 0) {
+	if (getuid() == 0) {
 	  user=argv[1];
 	}
 	else {
@@ -476,63 +107,49 @@ int main(int argc, char *argv[])
 
 	option=argv[2];
 
-	if (strncmp(argv[2], "verify", 8) == 0) {
-	  /* Get the account information from the shadow file */
-	  return _verify_account(argv[1]);
-	}
-
-	if (strncmp(option, "shadow", 8) == 0) {
-	  /* Attempting to change the password */
-	  return _update_shadow(argv[1]);
-	}
-
+	if (strcmp(option, "chkexpiry") == 0)
+	  /* Check account information from the shadow file */
+	  return _check_expiry(argv[1]);	  
 	/* read the nullok/nonull option */
-	if (strncmp(option, "nullok", 8) == 0)
+	else if (strcmp(option, "nullok") == 0)
 	  nullok = 1;
-	else
+	else if (strcmp(option, "nonull") == 0)
 	  nullok = 0;
+	else
+	  return PAM_SYSTEM_ERR;
 
 	/* read the password from stdin (a pipe from the pam_unix module) */
 
-	npass = read(STDIN_FILENO, pass, MAXPASS);
-
-	if (npass < 0) {	/* is it a valid password? */
-
-		_log_err(LOG_DEBUG, "no password supplied");
+	npass = read_passwords(STDIN_FILENO, 1, passwords);
 
-	} else if (npass >= MAXPASS) {
-
-		_log_err(LOG_DEBUG, "password too long");
-
-	} else {
-		if (npass == 0) {
-			/* the password is NULL */
-
-			retval = _unix_verify_password(user, NULL, nullok);
-
-		} else {
-			/* does pass agree with the official one? */
-
-			pass[npass] = '\0';	/* NUL terminate */
-			retval = _unix_verify_password(user, pass, nullok);
+	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) || force_failure) {
-	    _log_err(LOG_NOTICE, "password check failed for user (%s)", user);
-	    return PAM_AUTH_ERR;
+	if (retval != PAM_SUCCESS) {
+		if (!nullok || !blankpass)
+			/* no need to log blank pass test */
+			helper_log_err(LOG_NOTICE, "password check failed for user (%s)", user);
+		return PAM_AUTH_ERR;
 	} else {
-	    return PAM_SUCCESS;
+		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
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/support.h.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/support.h
--- Linux-PAM-0.99.8.1/modules/pam_unix/support.h.update-helper	2007-01-23 10:30:23.000000000 +0100
+++ Linux-PAM-0.99.8.1/modules/pam_unix/support.h	2008-01-08 16:17:32.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: support.h,v 1.12 2005/09/26 14:27:09 t8m Exp $
+ * $Id: support.h,v 1.13.2.1 2008/01/04 14:41:06 t8m Exp $
  */
 
 #ifndef _PAM_UNIX_SUPPORT_H
@@ -84,8 +84,12 @@ typedef struct {
 #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_CTRLS_              23	/* number of ctrl arguments defined */
+#define UNIX_CTRLS_              26	/* number of ctrl arguments defined */
 
 
 static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
@@ -116,6 +120,9 @@ static const UNIX_Ctrls unix_args[UNIX_C
 /* UNIX_REMEMBER_PASSWD */ {"remember=",       _ALL_ON_,            02000000},
 /* UNIX_NOREAP */          {"noreap",          _ALL_ON_,            04000000},
 /* UNIX_BROKEN_SHADOW */   {"broken_shadow",   _ALL_ON_,           010000000},
+/* UNIX_SHA256_PASS */     {"sha256",        _ALL_ON_^(040420000), 020000000},
+/* UNIX_SHA512_PASS */     {"sha512",        _ALL_ON_^(020420000), 040000000},
+/* UNIX_ALGO_ROUNDS */     {"rounds=",         _ALL_ON_,          0100000000},
 };
 
 #define UNIX_DEFAULTS  (unix_args[UNIX__NONULL].flag)
@@ -131,8 +138,8 @@ static const UNIX_Ctrls unix_args[UNIX_C
 
 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 argc,
-		     const char **argv);
+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,
 			   const char *name, int files, int nis,
 			   struct passwd **ret);
@@ -149,7 +156,7 @@ extern int _unix_read_password(pam_handl
 			,const char *prompt2
 			,const char *data_name
 			,const void **pass);
-extern int _unix_shadowed(const struct passwd *pwd);
 
-extern struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user);
+extern int _unix_run_verify_binary(pam_handle_t *pamh,
+			unsigned int ctrl, const char *user, int *daysleft);
 #endif /* _PAM_UNIX_SUPPORT_H */
diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h
--- /dev/null	2008-01-05 20:21:31.621001512 +0100
+++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h	2008-01-08 16:17:32.000000000 +0100
@@ -0,0 +1,124 @@
+/*
+ * 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      "/etc/security/opasswd"
+
+int
+verify_pwd_hash(const char *p, const char *hash, unsigned int nullok);
+
+int
+is_pwd_shadowed(const struct passwd *pwd);
+
+char *
+crypt_md5_wrapper(const char *pass_new);
+
+char *
+create_password_hash(const char *password, unsigned int ctrl, int rounds);
+
+int
+unix_selinux_confined(void);
+
+int
+lock_pwdf(void);
+
+void
+unlock_pwdf(void);
+
+int
+save_old_password(const char *forwho, const char *oldpass,
+		  int howmany);
+
+#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);
+
+int
+get_account_info(const char *name,
+	struct passwd **pwd, struct spwd **spwdent);
+
+int
+get_pwd_hash(const char *name,
+	struct passwd **pwd, char **hash);
+
+int
+check_shadow_expiry(struct spwd *spent, int *daysleft);
+
+int
+unix_update_passwd(const char *forwho, const char *towhat);
+
+int
+unix_update_shadow(const char *forwho, char *towhat);
+#else
+int
+get_account_info(pam_handle_t *pamh, const char *name,
+	struct passwd **pwd,  struct spwd **spwdent);
+
+int
+get_pwd_hash(pam_handle_t *pamh, const char *name,
+	struct passwd **pwd, char **hash);
+
+int
+check_shadow_expiry(pam_handle_t *pamh, struct spwd *spent, int *daysleft);
+
+int
+unix_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat);
+
+int
+unix_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat);
+#endif
+
+/* ****************************************************************** *
+ * 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.
+ */
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c
--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c.update-helper	2006-06-27 10:38:14.000000000 +0200
+++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c	2008-01-08 16:17:32.000000000 +0100
@@ -47,10 +47,6 @@
 #include <time.h>		/* for time() */
 #include <errno.h>
 #include <sys/wait.h>
-#ifdef WITH_SELINUX
-#include <selinux/selinux.h>
-#define SELINUX_ENABLED is_selinux_enabled()>0
-#endif
 
 #include <security/_pam_macros.h>
 
@@ -63,12 +59,10 @@
 #include <security/pam_modutil.h>
 
 #include "support.h"
+#include "passverify.h"
 
-#ifdef WITH_SELINUX
-
-struct spwd spwd;
-
-struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user)
+int _unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl,
+	const char *user, int *daysleft)
 {
   int retval=0, child, fds[2];
   void (*sighandler)(int) = NULL;
@@ -78,7 +72,7 @@ struct spwd *_unix_run_verify_binary(pam
   if (pipe(fds) != 0) {
     D(("could not make pipe"));
     pam_syslog(pamh, LOG_ERR, "Could not make pipe: %m");
-    return NULL;
+    return PAM_AUTH_ERR;
   }
   D(("called."));
 
@@ -117,7 +111,7 @@ struct spwd *_unix_run_verify_binary(pam
       }
     }
 
-    if (SELINUX_ENABLED && geteuid() == 0) {
+    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...) */
       setuid(0);
@@ -126,7 +120,7 @@ struct spwd *_unix_run_verify_binary(pam
     /* exec binary helper */
     args[0] = x_strdup(CHKPWD_HELPER);
     args[1] = x_strdup(user);
-    args[2] = x_strdup("verify");
+    args[2] = x_strdup("chkexpiry");
 
     execve(CHKPWD_HELPER, args, envp);
 
@@ -134,11 +128,12 @@ struct spwd *_unix_run_verify_binary(pam
     /* should not get here: exit with error */
     close (fds[1]);
     D(("helper binary is not available"));
+    printf("-1\n");
     exit(PAM_AUTHINFO_UNAVAIL);
   } else {
     close(fds[1]);
     if (child > 0) {
-      char buf[1024];
+      char buf[32];
       int rc=0;
       rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
       if (rc<0) {
@@ -146,22 +141,16 @@ struct spwd *_unix_run_verify_binary(pam
 	retval = PAM_AUTH_ERR;
       } else {
 	retval = WEXITSTATUS(retval);
-	if (retval != PAM_AUTHINFO_UNAVAIL) {
-          rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
-	  if(rc > 0) {
+        rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
+	if(rc > 0) {
 	      buf[rc] = '\0';
-	      if (sscanf(buf,"%ld:%ld:%ld:%ld:%ld:%ld",
-		     &spwd.sp_lstchg, /* last password change */
-		     &spwd.sp_min, /* days until change allowed. */
-		     &spwd.sp_max, /* days before change required */
-		     &spwd.sp_warn, /* days warning for expiration */
-		     &spwd.sp_inact, /* days before account inactive */
-		     &spwd.sp_expire) /* date when account expires */ != 6 ) retval = PAM_AUTH_ERR;
+	      if (sscanf(buf,"%d", daysleft) != 1 )
+	        retval = PAM_AUTH_ERR;
 	    }
-	  else {
-	    pam_syslog(pamh, LOG_ERR, " ERROR %d: %m", rc); retval = PAM_AUTH_ERR;
+	else {
+	    pam_syslog(pamh, LOG_ERR, "read unix_chkpwd output error %d: %m", rc);
+	    retval = PAM_AUTH_ERR;
 	  }
-	}
       }
     } else {
       pam_syslog(pamh, LOG_ERR, "Fork failed: %m");
@@ -174,15 +163,9 @@ struct spwd *_unix_run_verify_binary(pam
     (void) signal(SIGCHLD, sighandler);   /* restore old signal handler */
   }
   D(("Returning %d",retval));
-  if (retval != PAM_SUCCESS) {
-    return NULL;
-  }
-  return &spwd;
+  return retval;
 }
 
-#endif
-
-
 /*
  * PAM framework looks for this entry-point to pass control to the
  * account management module.
@@ -195,14 +178,13 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_hand
 	const void *void_uname;
 	const char *uname;
 	int retval, daysleft;
-	time_t curdays;
 	struct spwd *spent;
 	struct passwd *pwent;
 	char buf[256];
 
 	D(("called."));
 
-	ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
+	ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
 
 	retval = pam_get_item(pamh, PAM_USER, &void_uname);
 	uname = void_uname;
@@ -214,134 +196,90 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_hand
 		return PAM_USER_UNKNOWN;
 	}
 
-	pwent = pam_modutil_getpwnam(pamh, uname);
-	if (!pwent) {
+	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 PAM_USER_UNKNOWN;
+		return retval;
 	}
 
-	if (!strcmp( pwent->pw_passwd, "*NP*" )) { /* NIS+ */
-		uid_t save_euid, save_uid;
-
-		save_euid = geteuid();
-		save_uid = getuid();
-		if (save_uid == pwent->pw_uid)
-			setreuid( save_euid, save_uid );
-		else  {
-			setreuid( 0, -1 );
-			if (setreuid( -1, pwent->pw_uid ) == -1) {
-				setreuid( -1, 0 );
-				setreuid( 0, -1 );
-				if(setreuid( -1, pwent->pw_uid ) == -1)
-					return PAM_CRED_INSUFFICIENT;
-			}
-		}
-		spent = pam_modutil_getspnam (pamh, uname);
-		if (save_uid == pwent->pw_uid)
-			setreuid( save_uid, save_euid );
-		else {
-			if (setreuid( -1, 0 ) == -1)
-			setreuid( save_uid, -1 );
-			setreuid( -1, save_euid );
-		}
-
-	} else if (_unix_shadowed (pwent))
-		spent = pam_modutil_getspnam (pamh, uname);
-	else
+	if (retval == PAM_SUCCESS && spent == NULL)
 		return PAM_SUCCESS;
 
-#ifdef WITH_SELINUX
-	if (!spent && SELINUX_ENABLED )
-	    spent = _unix_run_verify_binary(pamh, ctrl, uname);
-#endif
-
-	if (!spent)
+	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);
 
-	if (!spent)
-		return PAM_AUTHINFO_UNAVAIL;	/* Couldn't get username from shadow */
-
-	curdays = 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)) {
+	switch (retval) {
+	case PAM_ACCT_EXPIRED:
 		pam_syslog(pamh, LOG_NOTICE,
-			 "account %s has expired (account expired)",
-			 uname);
+			"account %s has expired (account expired)",
+			uname);
 		_make_remark(pamh, ctrl, PAM_ERROR_MSG,
-			     _("Your account has expired; please contact your system administrator"));
-		D(("account expired"));
-		return PAM_ACCT_EXPIRED;
-	}
-	if (spent->sp_lstchg == 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)"));
-		D(("need a new password"));
-		return PAM_NEW_AUTHTOK_REQD;
-	}
-	if (curdays < spent->sp_lstchg) {
-		pam_syslog(pamh, LOG_DEBUG,
-			 "account %s has password changed in future",
-			 uname);
-		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)) {
+			_("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"));
-		D(("account expired 2"));
-		return PAM_ACCT_EXPIRED;
-	}
-	if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
-		pam_syslog(pamh, LOG_DEBUG,
-			 "expired password for user %s (password aged)",
-			 uname);
+			"account %s has expired (failed to change password)",
+			uname);
 		_make_remark(pamh, ctrl, PAM_ERROR_MSG,
-			     _("You are required to change your password immediately (password aged)"));
-		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 = (spent->sp_lstchg + spent->sp_max) - curdays;
-		pam_syslog(pamh, LOG_DEBUG,
-			 "password for user %s will expire in %d days",
-			 uname, daysleft);
-#ifdef HAVE_DNGETTEXT
-		snprintf (buf, sizeof (buf),
-			  dngettext(PACKAGE,
-				    "Warning: your password will expire in %d day",
-				    "Warning: your password will expire in %d days",
-				    daysleft),
-			  daysleft);
+			_("Your account has expired; please contact your system administrator"));
+		break;
+	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 support
-ed */
-			   _("Warning: your password will expire in %d days"),
-			   daysleft);
+			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);
+			_make_remark(pamh, ctrl, PAM_TEXT_INFO, buf);
+		}
 	}
 
 	D(("all done"));
 
-	return PAM_SUCCESS;
+	return retval;
 }
 
 
diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/unix_update.c
--- /dev/null	2008-01-05 20:21:31.621001512 +0100
+++ Linux-PAM-0.99.8.1/modules/pam_unix/unix_update.c	2008-01-08 16:17:32.000000000 +0100
@@ -0,0 +1,194 @@
+/*
+ * 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 <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>
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
+static int selinux_enabled=-1;
+#else
+#define SELINUX_ENABLED 0
+#endif
+
+#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;
+    }
+
+    /* does pass agree with the official one? 
+       we always allow change from null pass */
+    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.
+ */
diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c
--- /dev/null	2008-01-05 20:21:31.621001512 +0100
+++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c	2008-01-08 16:17:32.000000000 +0100
@@ -0,0 +1,1083 @@
+/*
+ * 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>
+
+#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) && !defined(HAVE_LCKPWDF)
+# include "./lckpwdf.-c"
+#endif
+
+int
+verify_pwd_hash(const char *p, const char *hash, unsigned int nullok)
+{
+	size_t hash_len = strlen(hash);
+	char *pp = NULL;
+	int retval;
+	D(("called"));
+
+	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 nows about it? We should try it.
+			 */
+			pp = x_strdup(crypt(p, hash));
+		}
+		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));
+
+		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;
+}
+
+#ifdef HELPER_COMPILE
+int
+get_account_info(const char *name,
+	struct passwd **pwd, struct spwd **spwdent)
+#else
+int
+get_account_info(pam_handle_t *pamh, const char *name,
+	struct passwd **pwd,  struct spwd **spwdent)
+#endif
+{
+	/* 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
+			 */
+#ifndef HELPER_COMPILE
+			if (geteuid() || SELINUX_ENABLED)
+				return PAM_UNIX_RUN_HELPER;
+#endif
+			*spwdent = pam_modutil_getspnam(pamh, name);
+			if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
+				return PAM_AUTHINFO_UNAVAIL;
+		}
+	} else {
+		return PAM_USER_UNKNOWN;
+	}
+	return PAM_SUCCESS;
+}
+
+#ifdef HELPER_COMPILE
+int
+get_pwd_hash(const char *name,
+	struct passwd **pwd, char **hash)
+#else
+int
+get_pwd_hash(pam_handle_t *pamh, const char *name,
+	struct passwd **pwd, char **hash)
+#endif
+{
+	int retval;
+	struct spwd *spwdent = NULL;
+
+#ifdef HELPER_COMPILE
+	retval = get_account_info(name, pwd, &spwdent);
+#else
+	retval = get_account_info(pamh, name, pwd, &spwdent);
+#endif	
+	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;
+}
+
+#ifdef HELPER_COMPILE
+int
+check_shadow_expiry(struct spwd *spent, int *daysleft)
+#else
+int
+check_shadow_expiry(pam_handle_t *pamh, struct spwd *spent, int *daysleft)
+#endif
+{
+	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"));
+	}
+	return PAM_SUCCESS;
+
+}
+
+/* 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
+ */
+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');
+}
+
+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 PATH_RANDOMDEV
+	int fd;
+	int rv;
+
+	if ((rv = fd = open (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 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, 9);
+
+        /* no longer need cleartext */
+        cp = Goodcrypt_md5(pass_new, (const char *) result);
+	pass_new = NULL;
+
+        return cp;
+}
+
+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)) {
+		return crypt_md5_wrapper(password);
+	}
+	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;
+	}
+
+	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. */
+	return x_strdup(crypt(password, salt));
+}
+
+#ifdef WITH_SELINUX
+int
+unix_selinux_confined(void)
+{
+    static int confined = -1;
+    int fd;
+    char tempfile[]="/etc/.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("/etc/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 /etc */
+    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 = 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
+        ulckpwdf();
+}
+#else
+int
+lock_pwdf(void)
+{
+	return PAM_SUCCESS;
+}
+
+void
+unlock_pwdf(void)
+{
+	return;
+}
+#endif
+
+int
+save_old_password(const char *forwho, const char *oldpass,
+		  int howmany)
+{
+    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;
+    security_context_t prev_context=NULL;
+
+    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("/etc/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, strlen(forwho))) {
+	    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 = getpwnam(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 (fclose(pwfile)) {
+	D(("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;
+    }
+}
+
+#ifdef HELPER_COMPILE
+int
+unix_update_passwd(const char *forwho, const char *towhat)
+#else
+int
+unix_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat)
+#endif
+{
+    struct passwd *tmpent = NULL;
+    struct stat st;
+    FILE *pwfile, *opwfile;
+    int err = 1;
+    int oldmask;
+    security_context_t prev_context=NULL;
+
+    oldmask = umask(077);
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      security_context_t passwd_context=NULL;
+      if (getfilecon("/etc/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("/etc/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;
+	}
+	if (putpwent(tmpent, pwfile)) {
+	    D(("error writing entry to password file: %m"));
+	    err = 1;
+	    break;
+	}
+	tmpent = fgetpwent(opwfile);
+    }
+    fclose(opwfile);
+
+    if (fclose(pwfile)) {
+	D(("error writing entries to password file: %m"));
+	err = 1;
+    }
+
+done:
+    if (!err) {
+	if (!rename(PW_TMPFILE, "/etc/passwd"))
+#ifdef HELPER_COMPILE
+	    helper_log_err(
+#else
+	    pam_syslog(pamh, 
+#endif
+		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 PAM_AUTHTOK_ERR;
+    }
+}
+
+#ifdef HELPER_COMPILE
+int
+unix_update_shadow(const char *forwho, char *towhat)
+#else
+int
+unix_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
+#endif
+{
+    struct spwd *spwdent = NULL, *stmpent = NULL;
+    struct stat st;
+    FILE *pwfile, *opwfile;
+    int err = 1;
+    int oldmask;
+    security_context_t prev_context=NULL;
+
+    spwdent = getspnam(forwho);
+    if (spwdent == NULL) {
+	return PAM_USER_UNKNOWN;
+    }
+    oldmask = umask(077);
+
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      security_context_t shadow_context=NULL;
+      if (getfilecon("/etc/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("/etc/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);
+	    err = 0;
+	    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 (fclose(pwfile)) {
+	D(("error writing entries to shadow file: %m"));
+	err = 1;
+    }
+
+ done:
+    if (!err) {
+	if (!rename(SH_TMPFILE, "/etc/shadow"))
+#ifdef HELPER_COMPILE
+	    helper_log_err(
+#else
+	    pam_syslog(pamh, 
+#endif
+		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 )
+                signal(sig, SIG_DFL);
+#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.
+ */
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/support.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/support.c
--- Linux-PAM-0.99.8.1/modules/pam_unix/support.c.update-helper	2007-02-06 17:06:45.000000000 +0100
+++ Linux-PAM-0.99.8.1/modules/pam_unix/support.c	2008-01-08 16:17:32.000000000 +0100
@@ -26,9 +26,8 @@
 #include <security/pam_ext.h>
 #include <security/pam_modutil.h>
 
-#include "md5.h"
 #include "support.h"
-#include "bigcrypt.h"
+#include "passverify.h"
 #ifdef WITH_SELINUX
 #include <selinux/selinux.h>
 #define SELINUX_ENABLED is_selinux_enabled()>0
@@ -53,8 +52,8 @@ int _make_remark(pam_handle_t * pamh, un
  * set the control flags for the UNIX module.
  */
 
-int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
-              const char **argv)
+int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds,
+	      int argc, const char **argv)
 {
 	unsigned int ctrl;
 
@@ -110,6 +109,16 @@ int _set_ctrl(pam_handle_t *pamh, int fl
 						*remember = 400;
 				}
 			}
+			if (rounds != NULL) {
+				if (j == UNIX_ALGO_ROUNDS) {
+					*rounds = strtol(*argv + 7, NULL, 10);
+					if ((*rounds < 1000) || (*rounds == INT_MAX))
+						/* don't care about bogus values */
+						unset(UNIX_ALGO_ROUNDS, ctrl);
+					if (*rounds >= 10000000)
+						*rounds = 9999999;
+				}
+			}
 		}
 
 		++argv;		/* step to next argument */
@@ -377,95 +386,6 @@ int _unix_comesfromsource(pam_handle_t *
 }
 
 /*
- * _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;
-	struct spwd *spwdent = 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 */
-
-	/* UNIX passwords area */
-
-	/* Get password file entry... */
-	pwd = pam_modutil_getpwnam (pamh, name);
-
-	if (pwd != NULL) {
-		if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
-		{ /* NIS+ */
-			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)
-						/* Will fail elsewhere. */
-						return 0;
-				}
-			}
-
-			spwdent = pam_modutil_getspnam (pamh, name);
-			if (save_uid == pwd->pw_uid)
-				setreuid( save_uid, save_euid );
-			else {
-				if (setreuid( -1, 0 ) == -1)
-					setreuid( save_uid, -1 );
-				setreuid( -1, save_euid );
-			}
-		} else if (_unix_shadowed(pwd)) {
-			/*
-			 * ...and shadow password file entry for this user,
-			 * if shadowing is enabled
-			 */
-			spwdent = pam_modutil_getspnam(pamh, name);
-		}
-		if (spwdent)
-			salt = x_strdup(spwdent->sp_pwdp);
-		else
-			salt = x_strdup(pwd->pw_passwd);
-	}
-	/* 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;
-}
-
-/*
  * verify the password of a user
  */
 
@@ -519,7 +439,7 @@ static int _unix_run_helper_binary(pam_h
 	  }
 	}
 
-	if (SELINUX_ENABLED && geteuid() == 0) {
+	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...) */
 	  setuid(0);
@@ -573,13 +493,66 @@ static int _unix_run_helper_binary(pam_h
     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 */
+
+	/* 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;
-	struct spwd *spwdent = NULL;
 	char *salt = NULL;
-	char *pp = NULL;
 	char *data_name;
 	int retval;
 
@@ -597,48 +570,7 @@ int _unix_verify_password(pam_handle_t *
 
 	D(("locating user's record"));
 
-	/* UNIX passwords area */
-	pwd = pam_modutil_getpwnam (pamh, name);	/* Get password file entry... */
-
-	if (pwd != NULL) {
-		if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
-		{ /* NIS+ */
-			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 {
-				if (setreuid( -1, 0 ) == -1)
-				setreuid( save_uid, -1 );
-				setreuid( -1, save_euid );
-			}
-		} else if (_unix_shadowed(pwd)) {
-			/*
-			 * ...and shadow password file entry for this user,
-			 * if shadowing is enabled
-			 */
-			spwdent = pam_modutil_getspnam (pamh, name);
-		}
-		if (spwdent)
-			salt = x_strdup(spwdent->sp_pwdp);
-		else
-			salt = x_strdup(pwd->pw_passwd);
-	}
+	retval = get_pwd_hash(pamh, name, &pwd, &salt);
 
 	data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
 	if (data_name == NULL) {
@@ -648,29 +580,22 @@ int _unix_verify_password(pam_handle_t *
 		strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
 	}
 
-	retval = PAM_SUCCESS;
-	if (pwd == NULL || salt == NULL || !strcmp(salt, "x") || ((salt[0] == '#') && (salt[1] == '#') && !strcmp(salt + 2, name))) {
-
-		if (pwd != NULL && (geteuid() || SELINUX_ENABLED)) {
-			/* we are not root perhaps this is the reason? Run helper */
+	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 (pwd == NULL)
-				retval = PAM_USER_UNKNOWN;
-			else
-				retval = PAM_AUTHINFO_UNAVAIL;
 			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_ALERT,
+				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_ALERT,
+				    pam_syslog(pamh, LOG_WARNING,
 				            "check pass; user unknown");
 				} else {
 				    /* don't log failure as another pam module can succeed */
@@ -679,48 +604,7 @@ int _unix_verify_password(pam_handle_t *
 			}
 		}
 	} else {
-	    size_t salt_len = strlen(salt);
-	    if (!salt_len) {
-		/* the stored password is NULL */
-		if (off(UNIX__NONULL, ctrl)) {/* 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 || *salt == '*' || *salt == '!') {
-		retval = PAM_AUTH_ERR;
-	    } else {
-		if (!strncmp(salt, "$1$", 3)) {
-		    pp = Goodcrypt_md5(p, salt);
-		    if (pp && strcmp(pp, salt) != 0) {
-			_pam_delete(pp);
-			pp = Brokencrypt_md5(p, salt);
-		    }
-		} else if (*salt != '$' && salt_len >= 13) {
-		    pp = bigcrypt(p, salt);
-		    if (pp && salt_len == 13 && strlen(pp) > salt_len) {
-			_pam_overwrite(pp + salt_len);
-		    }
-		} else {
-                    /*
-		     * Ok, we don't know the crypt algorithm, but maybe
-		     * libcrypt nows about it? We should try it.
-		     */
-		    pp = x_strdup (crypt(p, salt));
-		}
-		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));
-
-		if (pp && strcmp(pp, salt) == 0) {
-		    retval = PAM_SUCCESS;
-		} else {
-		    retval = PAM_AUTH_ERR;
-		}
-	    }
+		retval = verify_pwd_hash(p, salt, off(UNIX__NONULL, ctrl));
 	}
 
 	if (retval == PAM_SUCCESS) {
@@ -809,8 +693,6 @@ cleanup:
 		_pam_delete(data_name);
 	if (salt)
 		_pam_delete(salt);
-	if (pp)
-		_pam_delete(pp);
 
 	D(("done [%d].", retval));
 
@@ -971,26 +853,12 @@ int _unix_read_password(pam_handle_t * p
 	return PAM_SUCCESS;
 }
 
-int _unix_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;
-}
-
 /* ****************************************************************** *
  * 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
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am
--- Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am.update-helper	2006-12-18 19:50:50.000000000 +0100
+++ Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am	2008-01-08 16:17:32.000000000 +0100
@@ -16,7 +16,9 @@ securelibdir = $(SECUREDIR)
 secureconfdir = $(SCONFIGDIR)
 
 AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
-	-DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\"
+	-DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\" \
+	-DUPDATE_HELPER=\"$(sbindir)/unix_update\" \
+	-DPATH_RANDOMDEV=\"/dev/urandom\"
 
 if HAVE_LIBSELINUX
   AM_CFLAGS += -D"WITH_SELINUX"
@@ -25,33 +27,40 @@ if HAVE_LIBCRACK
   AM_CFLAGS += -D"USE_CRACKLIB"
 endif
 
-pam_unix_la_LDFLAGS = -no-undefined -avoid-version -module \
-	@LIBCRACK@ @LIBNSL@ -L$(top_builddir)/libpam -lpam \
-	@LIBCRYPT@ @LIBSELINUX@
+pam_unix_la_LDFLAGS = -no-undefined -avoid-version -module
 if HAVE_VERSIONING
   pam_unix_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
 endif
+pam_unix_la_LIBADD = @LIBCRACK@ @LIBNSL@ -L$(top_builddir)/libpam -lpam \
+	@LIBCRYPT@ @LIBSELINUX@
 
 securelib_LTLIBRARIES = pam_unix.la
 
-noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h
+noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h passverify.h
 
-sbin_PROGRAMS = unix_chkpwd
+sbin_PROGRAMS = unix_chkpwd unix_update
 
 noinst_PROGRAMS = bigcrypt
 
 pam_unix_la_SOURCES = bigcrypt.c pam_unix_acct.c \
 	pam_unix_auth.c pam_unix_passwd.c pam_unix_sess.c support.c \
-	yppasswd_xdr.c md5_good.c md5_broken.c
+	passverify.c yppasswd_xdr.c md5_good.c md5_broken.c
 
 bigcrypt_SOURCES = bigcrypt.c bigcrypt_main.c
 bigcrypt_CFLAGS = $(AM_CFLAGS)
-bigcrypt_LDFLAGS = @LIBCRYPT@
+bigcrypt_LDADD = @LIBCRYPT@
 
-unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c
-unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
-unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@ -L$(top_builddir)/libpam -lpam \
-	@LIBCRYPT@ @LIBSELINUX@
+unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c \
+	passverify.c
+unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_chkpwd\"
+unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@ 
+unix_chkpwd_LDADD = @LIBCRYPT@ @LIBSELINUX@
+
+unix_update_SOURCES = unix_update.c md5_good.c md5_broken.c bigcrypt.c \
+	passverify.c
+unix_update_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_update\"
+unix_update_LDFLAGS = @PIE_LDFLAGS@ 
+unix_update_LDADD = @LIBCRYPT@ @LIBSELINUX@
 
 if ENABLE_REGENERATE_MAN
 noinst_DATA = README
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c
--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c.update-helper	2006-12-20 15:52:55.000000000 +0100
+++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c	2008-01-07 16:38:50.000000000 +0100
@@ -111,7 +111,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_h
 
 	D(("called."));
 
-	ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
+	ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
 
 	/* Get a few bytes so we can pass our return value to
 	   pam_sm_setcred(). */