summaryrefslogtreecommitdiffstats
path: root/debian/patches-applied/007_modules_pam_unix
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 12:01:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 12:01:37 +0000
commitdcd7a5748ef6820e3e0d386139d9dd946f0d71fa (patch)
tree66900ada5e764c3422a91836695cdef113bbb883 /debian/patches-applied/007_modules_pam_unix
parentAdding upstream version 1.4.0. (diff)
downloadpam-dcd7a5748ef6820e3e0d386139d9dd946f0d71fa.tar.xz
pam-dcd7a5748ef6820e3e0d386139d9dd946f0d71fa.zip
Adding debian version 1.4.0-9+deb11u1.debian/1.4.0-9+deb11u1debian
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'debian/patches-applied/007_modules_pam_unix')
-rw-r--r--debian/patches-applied/007_modules_pam_unix524
1 files changed, 524 insertions, 0 deletions
diff --git a/debian/patches-applied/007_modules_pam_unix b/debian/patches-applied/007_modules_pam_unix
new file mode 100644
index 0000000..218379c
--- /dev/null
+++ b/debian/patches-applied/007_modules_pam_unix
@@ -0,0 +1,524 @@
+Index: pam/modules/pam_unix/pam_unix_passwd.c
+===================================================================
+--- pam.orig/modules/pam_unix/pam_unix_passwd.c
++++ pam/modules/pam_unix/pam_unix_passwd.c
+@@ -95,6 +95,9 @@
+ # endif /* GNU libc 2.1 */
+ #endif
+
++extern const char *obscure_msg(const char *, const char *, const struct passwd *,
++ unsigned int);
++
+ /*
+ How it works:
+ Gets in username (has to be done) from the calling program
+@@ -593,6 +596,11 @@
+ return retval;
+ }
+ }
++ if (!remark && pass_old != NULL) { /* only check if we don't already have a failure */
++ struct passwd *pwd;
++ pwd = pam_modutil_getpwnam(pamh, user);
++ remark = (char *)obscure_msg(pass_old,pass_new,pwd,ctrl); /* do obscure checks */
++ }
+ }
+ if (remark) {
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
+@@ -608,7 +616,7 @@
+ int retval;
+ int remember = -1;
+ int rounds = 0;
+- int pass_min_len = 0;
++ int pass_min_len = 6;
+
+ /* <DO NOT free() THESE> */
+ const char *user;
+Index: pam/modules/pam_unix/support.h
+===================================================================
+--- pam.orig/modules/pam_unix/support.h
++++ pam/modules/pam_unix/support.h
+@@ -101,50 +101,52 @@
+ #define UNIX_GOST_YESCRYPT_PASS 31 /* new password hashes will use gost-yescrypt */
+ #define UNIX_YESCRYPT_PASS 32 /* new password hashes will use yescrypt */
+ #define UNIX_NULLRESETOK 33 /* allow empty password if password reset is enforced */
++#define UNIX_OBSCURE_CHECKS 34 /* enable obscure checks on passwords */
+ /* -------------- */
+-#define UNIX_CTRLS_ 34 /* number of ctrl arguments defined */
++#define UNIX_CTRLS_ 35 /* number of ctrl arguments defined */
+
+ #define UNIX_DES_CRYPT(ctrl) (off(UNIX_MD5_PASS,ctrl)&&off(UNIX_BIGCRYPT,ctrl)&&off(UNIX_SHA256_PASS,ctrl)&&off(UNIX_SHA512_PASS,ctrl)&&off(UNIX_BLOWFISH_PASS,ctrl)&&off(UNIX_GOST_YESCRYPT_PASS,ctrl)&&off(UNIX_YESCRYPT_PASS,ctrl))
+
+ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
+ {
+-/* symbol token name ctrl mask ctrl *
+- * --------------------------- -------------------- ------------------------- ---------------- */
++/* symbol token name ctrl mask ctrl *
++ * --------------------------- -------------------- ------------------------- ------------ */
+
+-/* UNIX__OLD_PASSWD */ {NULL, _ALL_ON_, 01, 0},
+-/* UNIX__VERIFY_PASSWD */ {NULL, _ALL_ON_, 02, 0},
+-/* UNIX__IAMROOT */ {NULL, _ALL_ON_, 04, 0},
+-/* UNIX_AUDIT */ {"audit", _ALL_ON_, 010, 0},
+-/* UNIX_USE_FIRST_PASS */ {"use_first_pass", _ALL_ON_^(060ULL), 020, 0},
+-/* UNIX_TRY_FIRST_PASS */ {"try_first_pass", _ALL_ON_^(060ULL), 040, 0},
+-/* UNIX_AUTHTOK_TYPE */ {"authtok_type=", _ALL_ON_, 0100, 0},
+-/* UNIX__PRELIM */ {NULL, _ALL_ON_^(0600ULL), 0200, 0},
+-/* UNIX__UPDATE */ {NULL, _ALL_ON_^(0600ULL), 0400, 0},
+-/* UNIX__NONULL */ {NULL, _ALL_ON_, 01000, 0},
+-/* UNIX__QUIET */ {NULL, _ALL_ON_, 02000, 0},
+-/* UNIX_USE_AUTHTOK */ {"use_authtok", _ALL_ON_, 04000, 0},
+-/* UNIX_SHADOW */ {"shadow", _ALL_ON_, 010000, 0},
+-/* UNIX_MD5_PASS */ {"md5", _ALL_ON_^(015660420000ULL), 020000, 1},
+-/* UNIX__NULLOK */ {"nullok", _ALL_ON_^(01000ULL), 0, 0},
+-/* UNIX_DEBUG */ {"debug", _ALL_ON_, 040000, 0},
+-/* UNIX_NODELAY */ {"nodelay", _ALL_ON_, 0100000, 0},
+-/* UNIX_NIS */ {"nis", _ALL_ON_, 0200000, 0},
+-/* UNIX_BIGCRYPT */ {"bigcrypt", _ALL_ON_^(015660420000ULL), 0400000, 1},
+-/* UNIX_LIKE_AUTH */ {"likeauth", _ALL_ON_, 01000000, 0},
+-/* UNIX_REMEMBER_PASSWD */ {"remember=", _ALL_ON_, 02000000, 0},
+-/* UNIX_NOREAP */ {"noreap", _ALL_ON_, 04000000, 0},
+-/* UNIX_BROKEN_SHADOW */ {"broken_shadow", _ALL_ON_, 010000000, 0},
+-/* UNIX_SHA256_PASS */ {"sha256", _ALL_ON_^(015660420000ULL), 020000000, 1},
+-/* UNIX_SHA512_PASS */ {"sha512", _ALL_ON_^(015660420000ULL), 040000000, 1},
+-/* UNIX_ALGO_ROUNDS */ {"rounds=", _ALL_ON_, 0100000000, 0},
+-/* UNIX_BLOWFISH_PASS */ {"blowfish", _ALL_ON_^(015660420000ULL), 0200000000, 1},
+-/* UNIX_MIN_PASS_LEN */ {"minlen=", _ALL_ON_, 0400000000, 0},
+-/* UNIX_QUIET */ {"quiet", _ALL_ON_, 01000000000, 0},
+-/* UNIX_NO_PASS_EXPIRY */ {"no_pass_expiry", _ALL_ON_, 02000000000, 0},
+-/* UNIX_DES */ {"des", _ALL_ON_^(015660420000ULL), 0, 1},
+-/* UNIX_GOST_YESCRYPT_PASS */ {"gost_yescrypt", _ALL_ON_^(015660420000ULL), 04000000000, 1},
+-/* UNIX_YESCRYPT_PASS */ {"yescrypt", _ALL_ON_^(015660420000ULL), 010000000000, 1},
+-/* UNIX_NULLRESETOK */ {"nullresetok", _ALL_ON_, 020000000000, 0},
++/* UNIX__OLD_PASSWD */ {NULL, _ALL_ON_, 0x1, 0},
++/* UNIX__VERIFY_PASSWD */ {NULL, _ALL_ON_, 0x2, 0},
++/* UNIX__IAMROOT */ {NULL, _ALL_ON_, 0x4, 0},
++/* UNIX_AUDIT */ {"audit", _ALL_ON_, 0x8, 0},
++/* UNIX_USE_FIRST_PASS */ {"use_first_pass", _ALL_ON_^(0x30ULL), 0x10, 0},
++/* UNIX_TRY_FIRST_PASS */ {"try_first_pass", _ALL_ON_^(0x30ULL), 0x20, 0},
++/* UNIX_AUTHTOK_TYPE */ {"authtok_type=", _ALL_ON_, 0x40, 0},
++/* UNIX__PRELIM */ {NULL, _ALL_ON_^(0x180ULL), 0x80, 0},
++/* UNIX__UPDATE */ {NULL, _ALL_ON_^(0x180ULL), 0x100, 0},
++/* UNIX__NONULL */ {NULL, _ALL_ON_, 0x200, 0},
++/* UNIX__QUIET */ {NULL, _ALL_ON_, 0x400, 0},
++/* UNIX_USE_AUTHTOK */ {"use_authtok", _ALL_ON_, 0x800, 0},
++/* UNIX_SHADOW */ {"shadow", _ALL_ON_, 0x1000, 0},
++/* UNIX_MD5_PASS */ {"md5", _ALL_ON_^(0x6EC22000ULL), 0x2000, 1},
++/* UNIX__NULLOK */ {"nullok", _ALL_ON_^(0x200ULL), 0, 0},
++/* UNIX_DEBUG */ {"debug", _ALL_ON_, 0x4000, 0},
++/* UNIX_NODELAY */ {"nodelay", _ALL_ON_, 0x8000, 0},
++/* UNIX_NIS */ {"nis", _ALL_ON_, 0x10000, 0},
++/* UNIX_BIGCRYPT */ {"bigcrypt", _ALL_ON_^(0x6EC22000ULL), 0x20000, 1},
++/* UNIX_LIKE_AUTH */ {"likeauth", _ALL_ON_, 0x40000, 0},
++/* UNIX_REMEMBER_PASSWD */ {"remember=", _ALL_ON_, 0x80000, 0},
++/* UNIX_NOREAP */ {"noreap", _ALL_ON_, 0x100000, 0},
++/* UNIX_BROKEN_SHADOW */ {"broken_shadow", _ALL_ON_, 0x200000, 0},
++/* UNIX_SHA256_PASS */ {"sha256", _ALL_ON_^(0x6EC22000ULL), 0x400000, 1},
++/* UNIX_SHA512_PASS */ {"sha512", _ALL_ON_^(0x6EC22000ULL), 0x800000, 1},
++/* UNIX_ALGO_ROUNDS */ {"rounds=", _ALL_ON_, 0x1000000, 0},
++/* UNIX_BLOWFISH_PASS */ {"blowfish", _ALL_ON_^(0x6EC22000ULL), 0x2000000, 1},
++/* UNIX_MIN_PASS_LEN */ {"minlen=", _ALL_ON_, 0x4000000, 0},
++/* UNIX_QUIET */ {"quiet", _ALL_ON_, 0x8000000, 0},
++/* UNIX_NO_PASS_EXPIRY */ {"no_pass_expiry", _ALL_ON_, 0x10000000, 0},
++/* UNIX_DES */ {"des", _ALL_ON_^(0x6EC22000ULL), 0, 1},
++/* UNIX_GOST_YESCRYPT_PASS */ {"gost_yescrypt", _ALL_ON_^(0x6EC22000ULL), 0x20000000, 1},
++/* UNIX_YESCRYPT_PASS */ {"yescrypt", _ALL_ON_^(0x6EC22000ULL), 0x40000000, 1},
++/* UNIX_NULLRESETOK */ {"nullresetok", _ALL_ON_, 0x80000000, 0},
++/* UNIX_OBSCURE_CHECKS */ {"obscure", _ALL_ON_, 0x100000000, 0},
+ };
+
+ #define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag)
+Index: pam/modules/pam_unix/pam_unix.8.xml
+===================================================================
+--- pam.orig/modules/pam_unix/pam_unix.8.xml
++++ pam/modules/pam_unix/pam_unix.8.xml
+@@ -400,8 +400,81 @@
+ <listitem>
+ <para>
+ Set a minimum password length of <replaceable>n</replaceable>
+- characters. The max. for DES crypt based passwords are 8
+- characters.
++ characters. The default value is 6. The maximum for DES
++ crypt-based passwords is 8 characters.
++ </para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>
++ <option>obscure</option>
++ </term>
++ <listitem>
++ <para>
++ Enable some extra checks on password strength. These checks
++ are based on the "obscure" checks in the original shadow
++ package. The behavior is similar to the pam_cracklib
++ module, but for non-dictionary-based checks. The following
++ checks are implemented:
++ <variablelist>
++ <varlistentry>
++ <term>
++ <option>Palindrome</option>
++ </term>
++ <listitem>
++ <para>
++ Verifies that the new password is not a palindrome
++ of (i.e., the reverse of) the previous one.
++ </para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>
++ <option>Case Change Only</option>
++ </term>
++ <listitem>
++ <para>
++ Verifies that the new password isn't the same as the
++ old one with a change of case.
++ </para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>
++ <option>Similar</option>
++ </term>
++ <listitem>
++ <para>
++ Verifies that the new password isn't too much like
++ the previous one.
++ </para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>
++ <option>Simple</option>
++ </term>
++ <listitem>
++ <para>
++ Is the new password too simple? This is based on
++ the length of the password and the number of
++ different types of characters (alpha, numeric, etc.)
++ used.
++ </para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>
++ <option>Rotated</option>
++ </term>
++ <listitem>
++ <para>
++ Is the new password a rotated version of the old
++ password? (E.g., "billy" and "illyb")
++ </para>
++ </listitem>
++ </varlistentry>
++ </variablelist>
+ </para>
+ </listitem>
+ </varlistentry>
+Index: pam/modules/pam_unix/obscure.c
+===================================================================
+--- /dev/null
++++ pam/modules/pam_unix/obscure.c
+@@ -0,0 +1,198 @@
++/*
++ * Copyright 1989 - 1994, Julianne Frances Haugh
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include "config.h"
++
++#include <ctype.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <string.h>
++#include <stdlib.h>
++#include <pwd.h>
++#include <security/pam_modules.h>
++#include <security/_pam_macros.h>
++
++
++#include "support.h"
++
++/* can't be a palindrome - like `R A D A R' or `M A D A M' */
++static int palindrome(const char *old, const char *new) {
++ int i, j;
++
++ i = strlen (new);
++
++ for (j = 0;j < i;j++)
++ if (new[i - j - 1] != new[j])
++ return 0;
++
++ return 1;
++}
++
++/* more than half of the characters are different ones. */
++static int similar(const char *old, const char *new) {
++ int i, j;
++
++ /*
++ * XXX - sometimes this fails when changing from a simple password
++ * to a really long one (MD5). For now, I just return success if
++ * the new password is long enough. Please feel free to suggest
++ * something better... --marekm
++ */
++ if (strlen(new) >= 8)
++ return 0;
++
++ for (i = j = 0; new[i] && old[i]; i++)
++ if (strchr(new, old[i]))
++ j++;
++
++ if (i >= j * 2)
++ return 0;
++
++ return 1;
++}
++
++/* a nice mix of characters. */
++static int simple(const char *old, const char *new) {
++ int digits = 0;
++ int uppers = 0;
++ int lowers = 0;
++ int others = 0;
++ int size;
++ int i;
++
++ for (i = 0;new[i];i++) {
++ if (isdigit (new[i]))
++ digits++;
++ else if (isupper (new[i]))
++ uppers++;
++ else if (islower (new[i]))
++ lowers++;
++ else
++ others++;
++ }
++
++ /*
++ * The scam is this - a password of only one character type
++ * must be 8 letters long. Two types, 7, and so on.
++ */
++
++ size = 9;
++ if (digits) size--;
++ if (uppers) size--;
++ if (lowers) size--;
++ if (others) size--;
++
++ if (size <= i)
++ return 0;
++
++ return 1;
++}
++
++static char *str_lower(char *string) {
++ char *cp;
++
++ for (cp = string; *cp; cp++)
++ *cp = tolower(*cp);
++ return string;
++}
++
++static const char * password_check(const char *old, const char *new,
++ const struct passwd *pwdp) {
++ const char *msg = NULL;
++ char *oldmono, *newmono, *wrapped;
++
++ if (strcmp(new, old) == 0)
++ return _("Bad: new password must be different than the old one");
++
++ newmono = str_lower(strdup(new));
++ oldmono = str_lower(strdup(old));
++ wrapped = (char *)malloc(strlen(oldmono) * 2 + 1);
++ strcpy (wrapped, oldmono);
++ strcat (wrapped, oldmono);
++
++ if (palindrome(oldmono, newmono)) {
++ msg = _("Bad: new password cannot be a palindrome");
++ } else if (strcmp(oldmono, newmono) == 0) {
++ msg = _("Bad: new and old password must differ by more than just case");
++ } else if (similar(oldmono, newmono)) {
++ msg = _("Bad: new and old password are too similar");
++ } else if (simple(old, new)) {
++ msg = _("Bad: new password is too simple");
++ } else if (strstr(wrapped, newmono)) {
++ msg = _("Bad: new password is just a wrapped version of the old one");
++ }
++
++ _pam_delete(newmono);
++ _pam_delete(oldmono);
++ _pam_delete(wrapped);
++
++ return msg;
++}
++
++const char *obscure_msg(const char *old, const char *new,
++ const struct passwd *pwdp, unsigned int ctrl) {
++ int oldlen, newlen;
++ char *new1, *old1;
++ const char *msg;
++
++ if (old == NULL)
++ return NULL; /* no check if old is NULL */
++
++ oldlen = strlen(old);
++ newlen = strlen(new);
++
++ /* Remaining checks are optional. */
++ if (off(UNIX_OBSCURE_CHECKS,ctrl))
++ return NULL;
++
++ if ((msg = password_check(old, new, pwdp)) != NULL)
++ return msg;
++
++ /* The traditional crypt() truncates passwords to 8 chars. It is
++ possible to circumvent the above checks by choosing an easy
++ 8-char password and adding some random characters to it...
++ Example: "password$%^&*123". So check it again, this time
++ truncated to the maximum length. Idea from npasswd. --marekm */
++
++ if (!UNIX_DES_CRYPT(ctrl))
++ return NULL; /* unlimited password length */
++
++ if (oldlen <= 8 && newlen <= 8)
++ return NULL;
++
++ new1 = strndup(new,8);
++ old1 = strndup(old,8);
++
++ msg = password_check(old1, new1, pwdp);
++
++ _pam_delete(new1);
++ _pam_delete(old1);
++
++ return msg;
++}
+Index: pam/modules/pam_unix/Makefile.am
+===================================================================
+--- pam.orig/modules/pam_unix/Makefile.am
++++ pam/modules/pam_unix/Makefile.am
+@@ -39,7 +39,7 @@
+
+ pam_unix_la_SOURCES = bigcrypt.c pam_unix_acct.c \
+ pam_unix_auth.c pam_unix_passwd.c pam_unix_sess.c support.c \
+- passverify.c yppasswd_xdr.c md5_good.c md5_broken.c
++ passverify.c yppasswd_xdr.c md5_good.c md5_broken.c obscure.c
+
+ bigcrypt_SOURCES = bigcrypt.c bigcrypt_main.c
+ bigcrypt_CFLAGS = $(AM_CFLAGS)
+Index: pam/modules/pam_unix/pam_unix.8
+===================================================================
+--- pam.orig/modules/pam_unix/pam_unix.8
++++ pam/modules/pam_unix/pam_unix.8
+@@ -216,7 +216,38 @@
+ .RS 4
+ Set a minimum password length of
+ \fIn\fR
+-characters\&. The max\&. for DES crypt based passwords are 8 characters\&.
++characters\&. The default value is 6\&. The maximum for DES crypt\-based passwords is 8 characters\&.
++.RE
++.PP
++\fBobscure\fR
++.RS 4
++Enable some extra checks on password strength\&. These checks are based on the "obscure" checks in the original shadow package\&. The behavior is similar to the pam_cracklib module, but for non\-dictionary\-based checks\&. The following checks are implemented:
++.PP
++\fBPalindrome\fR
++.RS 4
++Verifies that the new password is not a palindrome of (i\&.e\&., the reverse of) the previous one\&.
++.RE
++.PP
++\fBCase Change Only\fR
++.RS 4
++Verifies that the new password isn\*(Aqt the same as the old one with a change of case\&.
++.RE
++.PP
++\fBSimilar\fR
++.RS 4
++Verifies that the new password isn\*(Aqt too much like the previous one\&.
++.RE
++.PP
++\fBSimple\fR
++.RS 4
++Is the new password too simple? This is based on the length of the password and the number of different types of characters (alpha, numeric, etc\&.) used\&.
++.RE
++.PP
++\fBRotated\fR
++.RS 4
++Is the new password a rotated version of the old password? (E\&.g\&., "billy" and "illyb")
++.RE
++.sp
+ .RE
+ .PP
+ \fBno_pass_expiry\fR
+Index: pam/modules/pam_unix/README
+===================================================================
+--- pam.orig/modules/pam_unix/README
++++ pam/modules/pam_unix/README
+@@ -171,8 +171,40 @@
+
+ minlen=n
+
+- Set a minimum password length of n characters. The max. for DES crypt based
+- passwords are 8 characters.
++ Set a minimum password length of n characters. The default value is 6. The
++ maximum for DES crypt-based passwords is 8 characters.
++
++obscure
++
++ Enable some extra checks on password strength. These checks are based on
++ the "obscure" checks in the original shadow package. The behavior is
++ similar to the pam_cracklib module, but for non-dictionary-based checks.
++ The following checks are implemented:
++
++ Palindrome
++
++ Verifies that the new password is not a palindrome of (i.e., the
++ reverse of) the previous one.
++
++ Case Change Only
++
++ Verifies that the new password isn't the same as the old one with a
++ change of case.
++
++ Similar
++
++ Verifies that the new password isn't too much like the previous one.
++
++ Simple
++
++ Is the new password too simple? This is based on the length of the
++ password and the number of different types of characters (alpha,
++ numeric, etc.) used.
++
++ Rotated
++
++ Is the new password a rotated version of the old password? (E.g.,
++ "billy" and "illyb")
+
+ no_pass_expiry
+