Index: Linux-PAM/doc/modules/pam_unix.sgml
===================================================================
--- Linux-PAM/doc/modules/pam_unix.sgml	(revision 274)
+++ Linux-PAM/doc/modules/pam_unix.sgml	(working copy)
@@ -182,6 +182,9 @@
 <tt/bigcrypt/;
 <tt/shadow/;
 <tt/nis/;
+<tt/min/;
+<tt/max/;
+<tt/obscure/;
 <tt/remember/
 
 <tag><bf>Description:</bf></tag>
@@ -236,6 +239,45 @@
 <tt>/etc/security/opasswd</tt> in order to force password change history
 and keep the user from alternating between the same password too frequently.
 
+<p>
+The <tt/min/ and <tt/max/ options allow control over the length of the
+password. These have a hard coded default of 1 and 8. The values are
+inclusive.
+
+<p>
+The <tt/obscure/ option enables some extra checks on the password. These
+is taken after the same obscure checks enabled in the original shadow
+package. This works very similar to the pam_cracklib module and implements
+these checks (it does not implement dictionary checks):
+
+<itemize>
+
+<item> <bf/Palindrome/ -
+
+Is the new password a palindrome of the old one? A palindrome is where the
+words read the same backwards and forwards (eg. madam and radar).
+
+<item> <bf/Case Change Only/ -
+
+Is the new password the the old one with only a change of case?
+
+<item> <bf/Similar/ -
+
+Is the new password too much like the old one?
+
+<item> <bf/Simple/ -
+
+Is the new password too small? This is based on the length of the
+password and the number of different types of characters used (ie.alpha,
+numeric...).
+
+<item> <bf/Rotated/ -
+
+Is the new password a rotated version of the old password (eg. "billy" and
+"illyb")?
+
+</itemize>
+
 <tag><bf>Examples/suggested usage:</bf></tag>
 
 Standard usage:
Index: Linux-PAM/modules/pam_unix/pam_unix_passwd.c
===================================================================
--- Linux-PAM/modules/pam_unix/pam_unix_passwd.c	(revision 274)
+++ Linux-PAM/modules/pam_unix/pam_unix_passwd.c	(working copy)
@@ -122,6 +122,9 @@
 #define OPW_TMPFILE		"/etc/security/nopasswd"
 #define OLD_PASSWORDS_FILE	"/etc/security/opasswd"
 
+extern const char *obscure_msg(const char *, const char *, const struct passwd *,
+			       unsigned int);
+
 /*
  * i64c - convert an integer to a radix 64 character
  */
@@ -620,6 +623,8 @@
 #ifdef DEBUG
 		sleep(5);
 #endif
+		_log_err(LOG_NOTICE, pamh, "NIS Password for %s was changed on %s", forwho, master);
+
 		return retval;
 	}
 
@@ -628,6 +633,9 @@
 	ulckpwdf();
 #endif
 
+	if (retval == PAM_SUCCESS)
+	    _log_err(LOG_NOTICE, pamh, "Password for %s was changed", forwho);
+
 	return retval;
 }
 
@@ -732,12 +740,8 @@
 #ifdef USE_CRACKLIB
 		remark = FascistCheck(pass_new, CRACKLIB_DICTS);
 		D(("called cracklib [%s]", remark));
-#else
-		if (strlen(pass_new) < 6)
-			remark = "You must choose a longer password";
-		D(("length check [%s]", remark));
 #endif
-		if (on(UNIX_REMEMBER_PASSWD, ctrl)) {
+		if (!remark && on(UNIX_REMEMBER_PASSWD, ctrl)) {
 			if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR)
 				remark = "Password has been already used. Choose another.";
 			if (retval == PAM_ABORT) {
@@ -746,6 +750,11 @@
 				return retval;
 			}
 		}
+		if (!remark && pass_old != NULL) { /* only check if we don't already have a failure */
+			struct passwd *pwd;
+			pwd = _pammodutil_getpwnam(pamh, user);
+			remark = (char *)obscure_msg(pass_old,pass_new,pwd,ctrl); /* do obscure checks */
+		}
 	}
 	if (remark) {
 		_make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
Index: Linux-PAM/modules/pam_unix/pam_unix_acct.c
===================================================================
--- Linux-PAM/modules/pam_unix/pam_unix_acct.c	(revision 274)
+++ Linux-PAM/modules/pam_unix/pam_unix_acct.c	(working copy)
@@ -137,7 +137,8 @@
 
 	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)) {
+	if ((curdays > spent->sp_expire) && (spent->sp_expire != -1)
+            && (spent->sp_expire != 0)) {
 		_log_err(LOG_NOTICE, pamh
 			 ,"account %s has expired (account expired)"
 			 ,uname);
@@ -164,7 +165,8 @@
 	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)) {
+	    && (spent->sp_max != -1) && (spent->sp_max != 0) && (spent->sp_inact != -1)
+	    && (spent->sp_inact != 0)) {
 		_log_err(LOG_NOTICE, pamh
 		    ,"account %s has expired (failed to change password)"
 			 ,uname);
@@ -173,7 +175,8 @@
 		D(("account expired 2"));
 		return PAM_ACCT_EXPIRED;
 	}
-	if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
+	if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)
+	    && (spent->sp_max != 0)) {
 		_log_err(LOG_DEBUG, pamh
 			 ,"expired password for user %s (password aged)"
 			 ,uname);
@@ -183,7 +186,8 @@
 		return PAM_NEW_AUTHTOK_REQD;
 	}
 	if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn)
-	    && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
+	    && (spent->sp_max != -1) && (spent->sp_warn != -1)
+	    && (spent->sp_max != 0) && (spent->sp_warn != 0)) {
 		daysleft = (spent->sp_lstchg + spent->sp_max) - curdays;
 		_log_err(LOG_DEBUG, pamh
 			 ,"password for user %s will expire in %d days"
Index: Linux-PAM/modules/pam_unix/support.c
===================================================================
--- Linux-PAM/modules/pam_unix/support.c	(revision 274)
+++ Linux-PAM/modules/pam_unix/support.c	(working copy)
@@ -31,6 +31,9 @@
 extern char *crypt(const char *key, const char *salt);
 extern char *bigcrypt(const char *key, const char *salt);
 
+unsigned int pass_min_len = 1;
+unsigned int pass_max_len = 8;
+
 /* syslogging function for errors and other information */
 
 void _log_err(int err, pam_handle_t *pamh, const char *format,...)
@@ -142,6 +145,7 @@
 		D(("SILENT"));
 		set(UNIX__QUIET, ctrl);
 	}
+
 	/* now parse the arguments to this module */
 
 	while (argc-- > 0) {
@@ -150,10 +154,9 @@
 		D(("pam_unix arg: %s", *argv));
 
 		for (j = 0; j < UNIX_CTRLS_; ++j) {
-			if (unix_args[j].token
-			    && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
+			if (unix_args[j].token &&
+			    !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token)))
 				break;
-			}
 		}
 
 		if (j >= UNIX_CTRLS_) {
@@ -163,16 +166,21 @@
 			ctrl &= unix_args[j].mask;	/* for turning things off */
 			ctrl |= unix_args[j].flag;	/* for turning things on  */
 
-			if (remember != NULL) {
-				if (j == UNIX_REMEMBER_PASSWD) {
-					*remember = strtol(*argv + 9, NULL, 10);
-					if ((*remember == INT_MIN) || (*remember == INT_MAX))
-						*remember = -1;
-					if (*remember > 400)
-						*remember = 400;
-				}
+			/* special cases */
+			if (remember != NULL && j == UNIX_REMEMBER_PASSWD) {
+				*remember = strtol(*argv + 9, NULL, 10);
+				if ((*remember == INT_MIN) || (*remember == INT_MAX))
+					*remember = -1;
+				if (*remember > 400)
+					*remember = 400;
+			} else if (j == UNIX_MAX_PASS_LEN) {
+				pass_max_len = atoi(*argv + 4);
+			} else if (j == UNIX_MIN_PASS_LEN) {
+				pass_min_len = atoi(*argv + 4);
 			}
 		}
+		if (pass_min_len > pass_max_len)
+			pass_min_len = pass_max_len;
 
 		++argv;		/* step to next argument */
 	}
@@ -733,6 +741,8 @@
 	    } else if (!p || (*salt == '*') || (salt_len < 13)) {
 		retval = PAM_AUTH_ERR;
 	    } else {
+		/* Hack off sysv pw aging foo */
+		if (strrchr(salt, ',')) *(strrchr(salt, ',')) = '\0';
 		if (!strncmp(salt, "$1$", 3)) {
 		    pp = Goodcrypt_md5(p, salt);
 		    if (strcmp(pp, salt) != 0) {
Index: Linux-PAM/modules/pam_unix/support.h
===================================================================
--- Linux-PAM/modules/pam_unix/support.h	(revision 274)
+++ Linux-PAM/modules/pam_unix/support.h	(working copy)
@@ -84,8 +84,12 @@
 #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_MAX_PASS_LEN        23	/* Max length for password */
+#define UNIX_MIN_PASS_LEN        24     /* Min length for password */
+#define UNIX_NOOBSCURE_CHECKS    25	/* internal */
+#define UNIX_OBSCURE_CHECKS      26     /* enable obscure checks on passwords */
 /* -------------- */
-#define UNIX_CTRLS_              23	/* number of ctrl arguments defined */
+#define UNIX_CTRLS_              27	/* number of ctrl arguments defined */
 
 
 static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
@@ -93,32 +97,36 @@
 /* symbol                  token name          ctrl mask             ctrl     *
  * ----------------------- ------------------- --------------------- -------- */
 
-/* UNIX__OLD_PASSWD */     {NULL,              _ALL_ON_,                  01},
-/* UNIX__VERIFY_PASSWD */  {NULL,              _ALL_ON_,                  02},
-/* UNIX__IAMROOT */        {NULL,              _ALL_ON_,                  04},
-/* UNIX_AUDIT */           {"audit",           _ALL_ON_,                 010},
-/* UNIX_USE_FIRST_PASS */  {"use_first_pass",  _ALL_ON_^(060),           020},
-/* UNIX_TRY_FIRST_PASS */  {"try_first_pass",  _ALL_ON_^(060),           040},
-/* UNIX_NOT_SET_PASS */    {"not_set_pass",    _ALL_ON_,                0100},
-/* UNIX__PRELIM */         {NULL,              _ALL_ON_^(0600),         0200},
-/* UNIX__UPDATE */         {NULL,              _ALL_ON_^(0600),         0400},
-/* UNIX__NONULL */         {NULL,              _ALL_ON_,               01000},
-/* UNIX__QUIET */          {NULL,              _ALL_ON_,               02000},
-/* UNIX_USE_AUTHTOK */     {"use_authtok",     _ALL_ON_,               04000},
-/* UNIX_SHADOW */          {"shadow",          _ALL_ON_,              010000},
-/* UNIX_MD5_PASS */        {"md5",             _ALL_ON_^(0400000),    020000},
-/* UNIX__NULLOK */         {"nullok",          _ALL_ON_^(01000),           0},
-/* UNIX_DEBUG */           {"debug",           _ALL_ON_,              040000},
-/* UNIX_NODELAY */         {"nodelay",         _ALL_ON_,             0100000},
-/* UNIX_NIS */             {"nis",             _ALL_ON_^(010000),    0200000},
-/* UNIX_BIGCRYPT */        {"bigcrypt",        _ALL_ON_^(020000),    0400000},
-/* UNIX_LIKE_AUTH */       {"likeauth",        _ALL_ON_,            01000000},
-/* UNIX_REMEMBER_PASSWD */ {"remember=",       _ALL_ON_,            02000000},
-/* UNIX_NOREAP */          {"noreap",          _ALL_ON_,            04000000},
-/* UNIX_BROKEN_SHADOW */   {"broken_shadow",   _ALL_ON_,           010000000},
+/* UNIX__OLD_PASSWD */     {NULL,              _ALL_ON_,                  0x1},
+/* UNIX__VERIFY_PASSWD */  {NULL,              _ALL_ON_,                  0x2},
+/* UNIX__IAMROOT */        {NULL,              _ALL_ON_,                  0x4},
+/* UNIX_AUDIT */           {"audit",           _ALL_ON_,                  0x8},
+/* UNIX_USE_FIRST_PASS */  {"use_first_pass",  _ALL_ON_^(0x30),          0x10},
+/* UNIX_TRY_FIRST_PASS */  {"try_first_pass",  _ALL_ON_^(0x30),          0x20},
+/* UNIX_NOT_SET_PASS */    {"not_set_pass",    _ALL_ON_,                 0x40},
+/* UNIX__PRELIM */         {NULL,              _ALL_ON_^(0x180),         0x80},
+/* UNIX__UPDATE */         {NULL,              _ALL_ON_^(0x180),        0x100},
+/* UNIX__NONULL */         {NULL,              _ALL_ON_,                0x200},
+/* UNIX__QUIET */          {NULL,              _ALL_ON_,                0x400},
+/* UNIX_USE_AUTHTOK */     {"use_authtok",     _ALL_ON_,                0x800},
+/* UNIX_SHADOW */          {"shadow",          _ALL_ON_,               0x1000},
+/* UNIX_MD5_PASS */        {"md5",             _ALL_ON_^(0x20000),     0x2000},
+/* UNIX__NULLOK */         {"nullok",          _ALL_ON_^(0x200),            0},
+/* UNIX_DEBUG */           {"debug",           _ALL_ON_,               0x4000},
+/* UNIX_NODELAY */         {"nodelay",         _ALL_ON_,               0x8000},
+/* UNIX_NIS */             {"nis",             _ALL_ON_^(0x1000),     0x10000},
+/* UNIX_BIGCRYPT */        {"bigcrypt",        _ALL_ON_^(0x2000),     0x20000},
+/* UNIX_LIKE_AUTH */       {"likeauth",        _ALL_ON_,              0x40000},
+/* UNIX_REMEMBER_PASSWD */ {"remember=",       _ALL_ON_,              0x80000},
+/* UNIX_NOREAP */          {"noreap",          _ALL_ON_,             0x100000},
+/* UNIX_BROKEN_SHADOW */   {"broken_shadow",   _ALL_ON_,             0x200000},
+/* UNIX_MAX_PASS_LEN */    {"max=",            _ALL_ON_,             0x400000},
+/* UNIX_MIN_PASS_LEN */    {"min=",            _ALL_ON_,             0x800000},
+/* UNIX_NOOBSCURE_CHECKS */{NULL,              _ALL_ON_,            0x1000000},
+/* UNIX_OBSCURE_CHECKS */  {"obscure",         _ALL_ON_,            0x2000000},
 };
 
-#define UNIX_DEFAULTS  (unix_args[UNIX__NONULL].flag)
+#define UNIX_DEFAULTS  (unix_args[UNIX__NONULL].flag | unix_args[UNIX_NOOBSCURE_CHECKS].flag)
 
 
 /* use this to free strings. ESPECIALLY password strings */
@@ -152,4 +160,7 @@
 			,const char **pass);
 extern int _unix_shadowed(const struct passwd *pwd);
 
+extern unsigned int pass_min_len;
+extern unsigned int pass_max_len;
+
 #endif /* _PAM_UNIX_SUPPORT_H */
Index: Linux-PAM/modules/pam_unix/unix_chkpwd.c
===================================================================
--- Linux-PAM/modules/pam_unix/unix_chkpwd.c	(revision 274)
+++ Linux-PAM/modules/pam_unix/unix_chkpwd.c	(working copy)
@@ -165,6 +165,13 @@
 	else if (p == NULL || strlen(p) == 0)
 		return UNIX_FAILED;
 
+	/* Hack off SysVR4 password aging */
+	{
+	    char *tmp;
+
+	    if ((tmp = strrchr(salt, ',')) != NULL) *tmp = '\0';
+	}
+
 	/* the moment of truth -- do we agree with the password? */
 	retval = UNIX_FAILED;
 	if (!strncmp(salt, "$1$", 3)) {
@@ -275,7 +282,7 @@
 
 	npass = read(STDIN_FILENO, option, 8);
 
-	if (npass < 0) {
+	if (npass < 0 || option == NULL || option[0] == '\0') {
 		_log_err(LOG_DEBUG, "no option supplied");
 		return UNIX_FAILED;
 	} else {
@@ -290,7 +297,7 @@
 
 	npass = read(STDIN_FILENO, pass, MAXPASS);
 
-	if (npass < 0) {	/* is it a valid password? */
+	if (npass < 0 || pass == NULL || pass[0] == '\0') {	/* is it a valid password? */
 
 		_log_err(LOG_DEBUG, "no password supplied");
 
Index: Linux-PAM/modules/pam_unix/Makefile
===================================================================
--- Linux-PAM/modules/pam_unix/Makefile	(revision 274)
+++ Linux-PAM/modules/pam_unix/Makefile	(working copy)
@@ -77,7 +77,7 @@
 LIBOBJS = $(addprefix static/,$(LIBOBJ))
 
 PLUS = md5_good.o md5_broken.o md5_crypt_good.o md5_crypt_broken.o \
-		yppasswd_xdr.o bigcrypt.o
+		yppasswd_xdr.o bigcrypt.o obscure.o
 
 ifdef DYNAMIC
 LIBSHARED = pam_unix.so
--- /dev/null	1969-12-31 16:00:00.000000000 -0800
+++ Linux-PAM/modules/pam_unix/obscure.c	2005-07-13 01:56:42.000000000 -0700
@@ -0,0 +1,203 @@
+/*
+ * Copyright 1989 - 1994, Julianne Frances Haugh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include<security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+
+#include "support.h"
+
+/* can't be a palindrome - like `R A D A R' or `M A D A M' */
+static int palindrome(const char *old, const char *new) {
+	int	i, j;
+
+	i = strlen (new);
+
+	for (j = 0;j < i;j++)
+		if (new[i - j - 1] != new[j])
+			return 0;
+
+	return 1;
+}
+
+/* more than half of the characters are different ones. */
+static int similar(const char *old, const char *new) {
+	int i, j;
+
+	/*
+	 * XXX - sometimes this fails when changing from a simple password
+	 * to a really long one (MD5).  For now, I just return success if
+	 * the new password is long enough.  Please feel free to suggest
+	 * something better...  --marekm
+	 */
+	if (strlen(new) >= 8)
+		return 0;
+
+	for (i = j = 0; new[i] && old[i]; i++)
+		if (strchr(new, old[i]))
+			j++;
+
+	if (i >= j * 2)
+		return 0;
+
+	return 1;
+}
+
+/* a nice mix of characters. */
+static int simple(const char *old, const char *new) {
+	int	digits = 0;
+	int	uppers = 0;
+	int	lowers = 0;
+	int	others = 0;
+	int	size;
+	int	i;
+
+	for (i = 0;new[i];i++) {
+		if (isdigit (new[i]))
+			digits++;
+		else if (isupper (new[i]))
+			uppers++;
+		else if (islower (new[i]))
+			lowers++;
+		else
+			others++;
+	}
+
+	/*
+	 * The scam is this - a password of only one character type
+	 * must be 8 letters long.  Two types, 7, and so on.
+	 */
+
+	size = 9;
+	if (digits) size--;
+	if (uppers) size--;
+	if (lowers) size--;
+	if (others) size--;
+
+	if (size <= i)
+		return 0;
+
+	return 1;
+}
+
+static char *str_lower(char *string) {
+	char *cp;
+
+	for (cp = string; *cp; cp++)
+		*cp = tolower(*cp);
+	return string;
+}
+
+static const char * password_check(const char *old, const char *new,
+				   const struct passwd *pwdp) {
+	const char *msg = NULL;
+	char *oldmono, *newmono, *wrapped;
+
+	if (strcmp(new, old) == 0)
+		return "Bad: new password must be different than the old one";
+
+	newmono = str_lower(strdup(new));
+	oldmono = str_lower(strdup(old));
+	wrapped = (char *)malloc(strlen(oldmono) * 2 + 1);
+	strcpy (wrapped, oldmono);
+	strcat (wrapped, oldmono);
+
+	if (palindrome(oldmono, newmono)) {
+		msg = "Bad: new password cannot be a panlindrome";
+	} else if (strcmp(oldmono, newmono) == 0) {
+		msg = "Bad: new and old password must differ by more than just case";
+	} else if (similar(oldmono, newmono)) {
+		msg = "Bad: new and old password are too similar";
+	} else if (simple(old, new)) {
+		msg = "Bad: new password is too simple";
+	} else if (strstr(wrapped, newmono)) {
+		msg = "Bad: new password is just a wrapped version of the old one";
+	}
+
+	_pam_delete(newmono);
+	_pam_delete(oldmono);
+	_pam_delete(wrapped);
+
+	return msg;
+}
+
+const char *obscure_msg(const char *old, const char *new,
+			       const struct passwd *pwdp, unsigned int ctrl) {
+	int oldlen, newlen;
+	char *new1, *old1;
+	const char *msg;
+
+	if (old == NULL)
+		return NULL; /* no check if old is NULL */
+
+	oldlen = strlen(old);
+	newlen = strlen(new);
+
+	if ( newlen < pass_min_len )
+		return "Bad: new password is too short";
+
+	/* Remaining checks are optional. */
+	if (on(UNIX_NOOBSCURE_CHECKS,ctrl))
+		return NULL;
+
+	if ((msg = password_check(old, new, pwdp)) != NULL)
+		return msg;
+
+	/* The traditional crypt() truncates passwords to 8 chars.  It is
+	   possible to circumvent the above checks by choosing an easy
+	   8-char password and adding some random characters to it...
+	   Example: "password$%^&*123".  So check it again, this time
+	   truncated to the maximum length.  Idea from npasswd.  --marekm */
+
+	if (on(UNIX_MD5_PASS,ctrl))
+		return NULL;  /* unlimited password length */
+
+	if (oldlen <= pass_max_len && newlen <= pass_max_len)
+		return NULL;
+
+	new1 = strdup(new);
+	old1 = strdup(old);
+	if (newlen > pass_max_len)
+		new1[pass_max_len] = '\0';
+	if (oldlen > pass_max_len)
+		old1[pass_max_len] = '\0';
+
+	msg = password_check(old1, new1, pwdp);
+
+	_pam_delete(new1);
+	_pam_delete(old1);
+
+	return msg;
+}
