From e15dee8abc38058cae9259a3aa3b625ea8febbfd Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Fri, 19 Sep 2008 13:38:32 +0000 Subject: Relevant BUGIDs: Purpose of commit: new feature Commit summary: --------------- 2008-09-19 Tomas Mraz * modules/pam_cracklib/pam_cracklib.8.xml: Fix description of the palindrome test. Document new options maxrepeat and reject_username. * modules/pam_cracklib/pam_cracklib.c(_pam_parse): Parse the maxrepeat and reject_username options. (password_check): Call the new tests usercheck() and consecutive(). (_pam_unix_approve_pass): Pass user name to the password_check(). --- ChangeLog | 11 ++++ NEWS | 5 +- modules/pam_cracklib/pam_cracklib.8.xml | 45 ++++++++++++++++- modules/pam_cracklib/pam_cracklib.c | 90 +++++++++++++++++++++++++++++---- 4 files changed, 138 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5da1343c..5a6c2bba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2008-09-19 Tomas Mraz + + * modules/pam_cracklib/pam_cracklib.8.xml: Fix description + of the palindrome test. Document new options maxrepeat and + reject_username. + * modules/pam_cracklib/pam_cracklib.c(_pam_parse): Parse + the maxrepeat and reject_username options. + (password_check): Call the new tests usercheck() and + consecutive(). + (_pam_unix_approve_pass): Pass user name to the password_check(). + 2008-09-16 Thorsten Kukuk * modules/pam_cracklib/pam_cracklib.8.xml: Fix typo. diff --git a/NEWS b/NEWS index e4e2b743..5d9927b7 100644 --- a/NEWS +++ b/NEWS @@ -6,11 +6,12 @@ Release 1.0.90 * Supply hostname of the machine to netgroup match call in pam_access * Make pam_namespace to work safe on child directories of parent directories owned by users -* Redifine LOCAL keyword of pam_access configuration file +* Redefine LOCAL keyword of pam_access configuration file * Add support fro try_first_pass and use_first_pass to pam_cracklib * Print informative messages for rejected login and add silent and no_log_info options to pam_tally - +* Add support for passing PAM_AUTHTOK to stdin of helpers from pam_exec +* New password quality tests in pam_cracklib Release 1.0.1 diff --git a/modules/pam_cracklib/pam_cracklib.8.xml b/modules/pam_cracklib/pam_cracklib.8.xml index 19b74d27..3d061c43 100644 --- a/modules/pam_cracklib/pam_cracklib.8.xml +++ b/modules/pam_cracklib/pam_cracklib.8.xml @@ -59,7 +59,7 @@ Palindrome - Is the new password a palindrome of the old one? + Is the new password a palindrome? @@ -120,6 +120,23 @@ + + Same consecutive characters + + + Optional check for same consecutive characters. + + + + + Contains user name + + + Optional check whether the password contains the user's name + in some form. + + + This module with no arguments will work well for standard unix @@ -347,6 +364,32 @@ + + + + + + + Reject passwords which contain more than N same consecutive + characters. The default is 0 which means that this check + is disabled. + + + + + + + + + + + Check whether the name of the user in straight or reversed + form is contained in the new password. If it is found the + new password is rejected. + + + + diff --git a/modules/pam_cracklib/pam_cracklib.c b/modules/pam_cracklib/pam_cracklib.c index 12cbcf3c..3dcc4729 100644 --- a/modules/pam_cracklib/pam_cracklib.c +++ b/modules/pam_cracklib/pam_cracklib.c @@ -99,6 +99,8 @@ struct cracklib_options { int min_class; int use_authtok; int try_first_pass; + int max_repeat; + int reject_user; char prompt_type[BUFSIZ]; const char *cracklib_dictpath; }; @@ -166,8 +168,14 @@ _pam_parse (pam_handle_t *pamh, struct cracklib_options *opt, opt->min_class = strtol(*argv+9,&ep,10); if (!ep) opt->min_class = 0; - if (opt->min_class > 4) - opt->min_class = 4 ; + if (opt->min_class > 4) + opt->min_class = 4; + } else if (!strncmp(*argv,"maxrepeat=",10)) { + opt->max_repeat = strtol(*argv+10,&ep,10); + if (!ep) + opt->max_repeat = 0; + } else if (!strncmp(*argv,"reject_username",15)) { + opt->reject_user = 1; } else if (!strncmp(*argv,"use_authtok",11)) { opt->use_authtok = 1; } else if (!strncmp(*argv,"use_first_pass",14)) { @@ -418,6 +426,58 @@ static int simple(struct cracklib_options *opt, const char *new) return 1; } +static int consecutive(struct cracklib_options *opt, const char *new) +{ + char c; + int i; + int same; + + if (opt->max_repeat == 0) + return 0; + + for (i = 0; new[i]; i++) { + if (i > 0 && new[i] == c) { + ++same; + if (same > opt->max_repeat) + return 1; + } else { + c = new[i]; + same = 1; + } + } + return 0; +} + +static int usercheck(struct cracklib_options *opt, const char *new, + char *user) +{ + char *f, *b; + + if (!opt->reject_user) + return 0; + + if (strstr(new, user) != NULL) + return 1; + + /* now reverse the username, we can do that in place + as it is strdup-ed */ + f = user; + b = user+strlen(user)-1; + while (f < b) { + char c; + + c = *f; + *f = *b; + *b = c; + --b; + ++f; + } + + if (strstr(new, user) != NULL) + return 1; + return 0; +} + static char * str_lower(char *string) { char *cp; @@ -428,10 +488,12 @@ static char * str_lower(char *string) } static const char *password_check(struct cracklib_options *opt, - const char *old, const char *new) + const char *old, const char *new, + const char *user) { const char *msg = NULL; char *oldmono = NULL, *newmono, *wrapped = NULL; + char *usermono = NULL; if (old && strcmp(new, old) == 0) { msg = _("is the same as the old one"); @@ -439,6 +501,7 @@ static const char *password_check(struct cracklib_options *opt, } newmono = str_lower(x_strdup(new)); + usermono = str_lower(x_strdup(user)); if (old) { oldmono = str_lower(x_strdup(old)); wrapped = malloc(strlen(oldmono) * 2 + 1); @@ -464,8 +527,15 @@ static const char *password_check(struct cracklib_options *opt, if (!msg && minclass (opt, new)) msg = _("not enough character classes"); + if (!msg && consecutive(opt, new)) + msg = _("contains too many same characters consecutively"); + + if (!msg && usercheck(opt, newmono, usermono)) + msg = _("contains the user name in some form"); + memset(newmono, 0, strlen(newmono)); free(newmono); + free(usermono); if (old) { memset(oldmono, 0, strlen(oldmono)); memset(wrapped, 0, strlen(wrapped)); @@ -532,18 +602,18 @@ static int _pam_unix_approve_pass(pam_handle_t *pamh, return PAM_AUTHTOK_ERR; } + retval = pam_get_item(pamh, PAM_USER, &user); + if (retval != PAM_SUCCESS || user == NULL) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh,LOG_ERR,"Can not get username"); + return PAM_AUTHTOK_ERR; + } /* * if one wanted to hardwire authentication token strength * checking this would be the place */ - msg = password_check(opt, pass_old, pass_new); + msg = password_check(opt, pass_old, pass_new, user); if (!msg) { - retval = pam_get_item(pamh, PAM_USER, &user); - if (retval != PAM_SUCCESS || user == NULL) { - if (ctrl & PAM_DEBUG_ARG) - pam_syslog(pamh,LOG_ERR,"Can not get username"); - return PAM_AUTHTOK_ERR; - } msg = check_old_password(user, pass_new); } -- cgit v1.2.3