Index: pam.deb/modules/pam_unix/pam_unix_passwd.c =================================================================== --- pam.deb.orig/modules/pam_unix/pam_unix_passwd.c +++ pam.deb/modules/pam_unix/pam_unix_passwd.c @@ -88,6 +88,9 @@ unsigned long versnum, unsigned int proto); #endif /* GNU libc 2.1 */ +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 @@ -430,7 +433,8 @@ static int _pam_unix_approve_pass(pam_handle_t * pamh ,unsigned int ctrl ,const char *pass_old - ,const char *pass_new) + ,const char *pass_new, + int pass_min_len) { const void *user; const char *remark = NULL; @@ -461,7 +465,7 @@ } } if (off(UNIX__IAMROOT, ctrl)) { - if (strlen(pass_new) < 6) + if (strlen(pass_new) < pass_min_len) remark = _("You must choose a longer password"); D(("length check [%s]", remark)); if (on(UNIX_REMEMBER_PASSWD, ctrl)) { @@ -473,6 +477,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); @@ -489,6 +498,7 @@ int retval; int remember = -1; int rounds = -1; + int pass_min_len = 6; /* */ const char *user; @@ -497,7 +507,8 @@ D(("called.")); - ctrl = _set_ctrl(pamh, flags, &remember, &rounds, argc, argv); + ctrl = _set_ctrl(pamh, flags, &remember, &rounds, &pass_min_len, + argc, argv); /* * First get the name of a user @@ -697,7 +708,8 @@ if (*(const char *)pass_new == '\0') { /* "\0" password = NULL */ pass_new = NULL; } - retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new); + retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, + pass_new, pass_min_len); if (retval != PAM_SUCCESS && off(UNIX_NOT_SET_PASS, ctrl)) { pam_set_item(pamh, PAM_AUTHTOK, NULL); @@ -730,7 +742,8 @@ return retval; } - retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new); + retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new, + pass_min_len); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_NOTICE, "new password not acceptable 2"); Index: pam.deb/modules/pam_unix/pam_unix_acct.c =================================================================== --- pam.deb.orig/modules/pam_unix/pam_unix_acct.c +++ pam.deb/modules/pam_unix/pam_unix_acct.c @@ -191,7 +191,7 @@ D(("called.")); - ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv); + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); retval = pam_get_item(pamh, PAM_USER, &void_uname); uname = void_uname; Index: pam.deb/modules/pam_unix/support.c =================================================================== --- pam.deb.orig/modules/pam_unix/support.c +++ pam.deb/modules/pam_unix/support.c @@ -53,7 +53,7 @@ */ int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds, - int argc, const char **argv) + int *pass_min_len, int argc, const char **argv) { unsigned int ctrl; @@ -79,6 +79,7 @@ D(("SILENT")); set(UNIX__QUIET, ctrl); } + /* now parse the arguments to this module */ while (argc-- > 0) { @@ -88,7 +89,8 @@ for (j = 0; j < UNIX_CTRLS_; ++j) { if (unix_args[j].token - && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) { + && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) + { break; } } @@ -100,15 +102,16 @@ 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 (pass_min_len && j == UNIX_MIN_PASS_LEN) { + *pass_min_len = atoi(*argv + 4); + } if (rounds != NULL && j == UNIX_ALGO_ROUNDS) *rounds = strtol(*argv + 7, NULL, 10); } @@ -116,6 +119,11 @@ ++argv; /* step to next argument */ } + if (off(UNIX_BIGCRYPT|UNIX_MD5_PASS|UNIX_SHA256_PASS|UNIX_SHA512_PASS, + ctrl) + && pass_min_len && *pass_min_len > 8) + *pass_min_len = 8; + if (flags & PAM_DISALLOW_NULL_AUTHTOK) { D(("DISALLOW_NULL_AUTHTOK")); set(UNIX__NONULL, ctrl); Index: pam.deb/modules/pam_unix/support.h =================================================================== --- pam.deb.orig/modules/pam_unix/support.h +++ pam.deb/modules/pam_unix/support.h @@ -89,41 +89,47 @@ #define UNIX_ALGO_ROUNDS 25 /* optional number of rounds for new password hash algorithms */ #define UNIX_BLOWFISH_PASS 26 /* new password hashes will use blowfish */ +#define UNIX_MAX_PASS_LEN 27 /* internal, for compatibility only */ +#define UNIX_MIN_PASS_LEN 28 /* min length for password */ +#define UNIX_OBSCURE_CHECKS 29 /* enable obscure checks on passwords */ /* -------------- */ -#define UNIX_CTRLS_ 27 /* number of ctrl arguments defined */ +#define UNIX_CTRLS_ 30 /* number of ctrl arguments defined */ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = { /* 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_^(0260420000), 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_, 0200000}, -/* UNIX_BIGCRYPT */ {"bigcrypt", _ALL_ON_^(0260420000), 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_SHA256_PASS */ {"sha256", _ALL_ON_^(0260420000), 020000000}, -/* UNIX_SHA512_PASS */ {"sha512", _ALL_ON_^(0260420000), 040000000}, -/* UNIX_ALGO_ROUNDS */ {"rounds=", _ALL_ON_, 0100000000}, -/* UNIX_BLOWFISH_PASS */ {"blowfish", _ALL_ON_^(0260420000), 0200000000}, +/* 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_^(0x2C22000), 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_, 0x10000}, +/* UNIX_BIGCRYPT */ {"bigcrypt", _ALL_ON_^(0x2C22000), 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_SHA256_PASS */ {"sha256", _ALL_ON_^(0x2C22000), 0x400000}, +/* UNIX_SHA512_PASS */ {"sha512", _ALL_ON_^(0x2C22000), 0x800000}, +/* UNIX_ALGO_ROUNDS */ {"rounds=", _ALL_ON_, 0x1000000}, +/* UNIX_BLOWFISH_PASS */ {"blowfish", _ALL_ON_^(0x2C22000),0x2000000}, +/* UNIX_MAX_PASS_LEN */ {"max=", _ALL_ON_, 0}, +/* UNIX_MIN_PASS_LEN */ {"min=", _ALL_ON_, 0x4000000}, +/* UNIX_OBSCURE_CHECKS */ {"obscure", _ALL_ON_, 0x8000000}, }; #define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag) @@ -141,7 +147,7 @@ extern int _make_remark(pam_handle_t * pamh, unsigned int ctrl ,int type, const char *text); extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int *rounds, - int argc, const char **argv); + int *pass_min_len, int argc, const char **argv); extern int _unix_getpwnam (pam_handle_t *pamh, const char *name, int files, int nis, struct passwd **ret); Index: pam.deb/modules/pam_unix/pam_unix.8.xml =================================================================== --- pam.deb.orig/modules/pam_unix/pam_unix.8.xml +++ pam.deb/modules/pam_unix/pam_unix.8.xml @@ -326,6 +326,90 @@ + + + + + + + Set a minimum password length of n + characters. The default value is 6. + + + + + + + + + + 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: + + + + + + + + Verifies that the new password is not a palindrome + of (i.e., the reverse of) the previous one. + + + + + + + + + + Verifies that the new password isn't the same as the + old one with a change of case. + + + + + + + + + + Verifies that the new password isn't too much like + the previous one. + + + + + + + + + + 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. + + + + + + + + + + Is the new password a rotated version of the old + password? (E.g., "billy" and "illyb") + + + + + + + Invalid arguments are logged with Index: pam.deb/modules/pam_unix/obscure.c =================================================================== --- /dev/null +++ pam.deb/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 +#include +#include +#include +#include +#include +#include +#include + + +#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 (on(UNIX_MD5_PASS,ctrl) || on(UNIX_BIGCRYPT,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.deb/modules/pam_unix/Makefile.am =================================================================== --- pam.deb.orig/modules/pam_unix/Makefile.am +++ pam.deb/modules/pam_unix/Makefile.am @@ -40,7 +40,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.deb/modules/pam_unix/pam_unix_auth.c =================================================================== --- pam.deb.orig/modules/pam_unix/pam_unix_auth.c +++ pam.deb/modules/pam_unix/pam_unix_auth.c @@ -111,7 +111,7 @@ D(("called.")); - ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv); + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); /* Get a few bytes so we can pass our return value to pam_sm_setcred(). */ Index: pam.deb/modules/pam_unix/pam_unix_sess.c =================================================================== --- pam.deb.orig/modules/pam_unix/pam_unix_sess.c +++ pam.deb/modules/pam_unix/pam_unix_sess.c @@ -73,7 +73,7 @@ D(("called.")); - ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv); + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); retval = pam_get_item(pamh, PAM_USER, (void *) &user_name); if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) { @@ -107,7 +107,7 @@ D(("called.")); - ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv); + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); retval = pam_get_item(pamh, PAM_USER, (void *) &user_name); if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) { Index: pam.deb/modules/pam_unix/pam_unix.8 =================================================================== --- pam.deb.orig/modules/pam_unix/pam_unix.8 +++ pam.deb/modules/pam_unix/pam_unix.8 @@ -1,161 +1,13 @@ +'\" t .\" Title: pam_unix .\" Author: [see the "AUTHOR" section] -.\" Generator: DocBook XSL Stylesheets v1.74.0 -.\" Date: 06/16/2009 +.\" Generator: DocBook XSL Stylesheets v1.75.2 +.\" Date: 08/24/2009 .\" Manual: Linux-PAM Manual .\" Source: Linux-PAM Manual .\" Language: English .\" -.TH "PAM_UNIX" "8" "06/16/2009" "Linux-PAM Manual" "Linux\-PAM Manual" -.\" ----------------------------------------------------------------- -.\" * (re)Define some macros -.\" ----------------------------------------------------------------- -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" toupper - uppercase a string (locale-aware) -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de toupper -.tr aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ -\\$* -.tr aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz -.. -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" SH-xref - format a cross-reference to an SH section -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de SH-xref -.ie n \{\ -.\} -.toupper \\$* -.el \{\ -\\$* -.\} -.. -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" SH - level-one heading that works better for non-TTY output -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de1 SH -.\" put an extra blank line of space above the head in non-TTY output -.if t \{\ -.sp 1 -.\} -.sp \\n[PD]u -.nr an-level 1 -.set-an-margin -.nr an-prevailing-indent \\n[IN] -.fi -.in \\n[an-margin]u -.ti 0 -.HTML-TAG ".NH \\n[an-level]" -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -\." make the size of the head bigger -.ps +3 -.ft B -.ne (2v + 1u) -.ie n \{\ -.\" if n (TTY output), use uppercase -.toupper \\$* -.\} -.el \{\ -.nr an-break-flag 0 -.\" if not n (not TTY), use normal case (not uppercase) -\\$1 -.in \\n[an-margin]u -.ti 0 -.\" if not n (not TTY), put a border/line under subheading -.sp -.6 -\l'\n(.lu' -.\} -.. -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" SS - level-two heading that works better for non-TTY output -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de1 SS -.sp \\n[PD]u -.nr an-level 1 -.set-an-margin -.nr an-prevailing-indent \\n[IN] -.fi -.in \\n[IN]u -.ti \\n[SN]u -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -.ps \\n[PS-SS]u -\." make the size of the head bigger -.ps +2 -.ft B -.ne (2v + 1u) -.if \\n[.$] \&\\$* -.. -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" BB/BE - put background/screen (filled box) around block of text -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de BB -.if t \{\ -.sp -.5 -.br -.in +2n -.ll -2n -.gcolor red -.di BX -.\} -.. -.de EB -.if t \{\ -.if "\\$2"adjust-for-leading-newline" \{\ -.sp -1 -.\} -.br -.di -.in -.ll -.gcolor -.nr BW \\n(.lu-\\n(.i -.nr BH \\n(dn+.5v -.ne \\n(BHu+.5v -.ie "\\$2"adjust-for-leading-newline" \{\ -\M[\\$1]\h'1n'\v'+.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[] -.\} -.el \{\ -\M[\\$1]\h'1n'\v'-.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[] -.\} -.in 0 -.sp -.5v -.nf -.BX -.in -.sp .5v -.fi -.\} -.. -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" BM/EM - put colored marker in margin next to block of text -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de BM -.if t \{\ -.br -.ll -2n -.gcolor red -.di BX -.\} -.. -.de EM -.if t \{\ -.br -.di -.ll -.gcolor -.nr BH \\n(dn -.ne \\n(BHu -\M[\\$1]\D'P -.75n 0 0 \\n(BHu -(\\n[.i]u - \\n(INu - .75n) 0 0 -\\n(BHu'\M[] -.in 0 -.nf -.BX -.in -.fi -.\} -.. +.TH "PAM_UNIX" "8" "08/24/2009" "Linux-PAM Manual" "Linux\-PAM Manual" .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- @@ -166,13 +18,11 @@ .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- -.SH "Name" +.SH "NAME" pam_unix \- Module for traditional password authentication -.SH "Synopsis" -.fam C +.SH "SYNOPSIS" .HP \w'\fBpam_unix\&.so\fR\ 'u \fBpam_unix\&.so\fR [\&.\&.\&.] -.fam .SH "DESCRIPTION" .PP This is the standard Unix authentication module\&. It uses standard calls from the system\'s libraries to retrieve and set account information as well as authentication\&. Usually this is obtained from the /etc/passwd and the /etc/shadow file as well if shadow is enabled\&. @@ -264,7 +114,7 @@ The last \fIn\fR passwords for each user are saved in -\FC/etc/security/opasswd\F[] +/etc/security/opasswd in order to force password change history and keep the user from alternating between the same password too frequently\&. .RE .PP @@ -315,6 +165,44 @@ Ignore errors reading shadow information for users in the account management module\&. .RE .PP +\fBmin=\fR\fB\fIn\fR\fR +.RS 4 +Set a minimum password length of +\fIn\fR +characters\&. The default value is 6\&. +.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\'t the same as the old one with a change of case\&. +.RE +.PP +\fBSimilar\fR +.RS 4 +Verifies that the new password isn\'t too much like the previous one\&. +.RE +.PP +\fBSimple\fR +.RS 4 +Is the new password too simple? This is based on the length of the password and the number of different types of characters (alpha, numeric, etc\&.) used\&. +.RE +.PP +\fBRotated\fR +.RS 4 +Is the new password a rotated version of the old password? (E\&.g\&., "billy" and "illyb") +.RE +.sp +.RE +.PP Invalid arguments are logged with \fBsyslog\fR(3)\&. .SH "MODULE TYPES PROVIDED" @@ -333,21 +221,13 @@ .SH "EXAMPLES" .PP An example usage for -\FC/etc/pam\&.d/login\F[] +/etc/pam\&.d/login would be: .sp .if n \{\ .RS 4 .\} -.fam C -.ps -1 .nf -.if t \{\ -.sp -1 -.\} -.BB lightgray adjust-for-leading-newline -.sp -1 - # Authenticate the user auth required pam_unix\&.so # Ensure users account and password are still active @@ -358,13 +238,7 @@ password required pam_unix\&.so use_authtok nullok md5 session required pam_unix\&.so -.EB lightgray adjust-for-leading-newline -.if t \{\ -.sp 1 -.\} .fi -.fam -.ps +1 .if n \{\ .RE .\}