#include "config.h"
#include <stdio.h>
-#ifdef HAVE_CRYPT_H
+#ifdef HAVE_LIBXCRYPT
+# include <xcrypt.h>
+#elif defined(HAVE_CRYPT_H)
# include <crypt.h>
#endif
#include <unistd.h>
#include <sys/stat.h>
#include <ctype.h>
#include <limits.h>
+#include <pwd.h>
+#include <security/pam_modutil.h>
#ifdef HAVE_CRACK_H
#include <crack.h>
extern char *FascistCheck(char *pw, const char *dictpath);
#endif
+#ifndef CRACKLIB_DICTS
+#define CRACKLIB_DICTS NULL
+#endif
+
/* For Translators: "%s%s" could be replaced with "<service> " or "". */
#define PROMPT1 _("New %s%spassword: ")
/* For Translators: "%s%s" could be replaced with "<service> " or "". */
struct cracklib_options {
int retry_times;
int diff_ok;
- int diff_ignore;
int min_length;
int dig_credit;
int up_credit;
int low_credit;
int oth_credit;
- int use_authtok;
- char prompt_type[BUFSIZ];
- char cracklib_dictpath[PATH_MAX];
+ int min_class;
+ int max_repeat;
+ int max_sequence;
+ int max_class_repeat;
+ int reject_user;
+ int gecos_check;
+ int enforce_for_root;
+ const char *cracklib_dictpath;
};
#define CO_RETRY_TIMES 1
#define CO_DIFF_OK 5
-#define CO_DIFF_IGNORE 23
#define CO_MIN_LENGTH 9
# define CO_MIN_LENGTH_BASE 5
#define CO_DIG_CREDIT 1
#define CO_UP_CREDIT 1
#define CO_LOW_CREDIT 1
#define CO_OTH_CREDIT 1
-#define CO_USE_AUTHTOK 0
+#define CO_MIN_WORD_LENGTH 4
static int
_pam_parse (pam_handle_t *pamh, struct cracklib_options *opt,
if (!strcmp(*argv,"debug"))
ctrl |= PAM_DEBUG_ARG;
else if (!strncmp(*argv,"type=",5))
- strncpy(opt->prompt_type, *argv+5, sizeof(opt->prompt_type) - 1);
+ pam_set_item (pamh, PAM_AUTHTOK_TYPE, *argv+5);
else if (!strncmp(*argv,"retry=",6)) {
opt->retry_times = strtol(*argv+6,&ep,10);
if (!ep || (opt->retry_times < 1))
if (!ep || (opt->diff_ok < 0))
opt->diff_ok = CO_DIFF_OK;
} else if (!strncmp(*argv,"difignore=",10)) {
- opt->diff_ignore = strtol(*argv+10,&ep,10);
- if (!ep || (opt->diff_ignore < 0))
- opt->diff_ignore = CO_DIFF_IGNORE;
+ /* just ignore */
} else if (!strncmp(*argv,"minlen=",7)) {
opt->min_length = strtol(*argv+7,&ep,10);
if (!ep || (opt->min_length < CO_MIN_LENGTH_BASE))
opt->oth_credit = strtol(*argv+8,&ep,10);
if (!ep)
opt->oth_credit = 0;
+ } else if (!strncmp(*argv,"minclass=",9)) {
+ opt->min_class = strtol(*argv+9,&ep,10);
+ if (!ep)
+ opt->min_class = 0;
+ 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,"maxsequence=",12)) {
+ opt->max_sequence = strtol(*argv+12,&ep,10);
+ if (!ep)
+ opt->max_sequence = 0;
+ } else if (!strncmp(*argv,"maxclassrepeat=",15)) {
+ opt->max_class_repeat = strtol(*argv+15,&ep,10);
+ if (!ep)
+ opt->max_class_repeat = 0;
+ } else if (!strncmp(*argv,"reject_username",15)) {
+ opt->reject_user = 1;
+ } else if (!strncmp(*argv,"gecoscheck",10)) {
+ opt->gecos_check = 1;
+ } else if (!strncmp(*argv,"enforce_for_root",16)) {
+ opt->enforce_for_root = 1;
+ } else if (!strncmp(*argv,"authtok_type",12)) {
+ /* for pam_get_authtok, ignore */;
} else if (!strncmp(*argv,"use_authtok",11)) {
- opt->use_authtok = 1;
+ /* for pam_get_authtok, ignore */;
+ } else if (!strncmp(*argv,"use_first_pass",14)) {
+ /* for pam_get_authtok, ignore */;
+ } else if (!strncmp(*argv,"try_first_pass",14)) {
+ /* for pam_get_authtok, ignore */;
} else if (!strncmp(*argv,"dictpath=",9)) {
- strncpy(opt->cracklib_dictpath, *argv+9,
- sizeof(opt->cracklib_dictpath) - 1);
+ opt->cracklib_dictpath = *argv+9;
+ if (!*(opt->cracklib_dictpath)) {
+ opt->cracklib_dictpath = CRACKLIB_DICTS;
+ }
} else {
pam_syslog(pamh,LOG_ERR,"pam_parse: unknown option; %s",*argv);
}
}
- opt->prompt_type[sizeof(opt->prompt_type) - 1] = '\0';
- opt->cracklib_dictpath[sizeof(opt->cracklib_dictpath) - 1] = '\0';
return ctrl;
}
/* Helper functions */
-/* use this to free strings. ESPECIALLY password strings */
-static char *_pam_delete(register char *xx)
-{
- _pam_overwrite(xx);
- free(xx);
- return NULL;
-}
-
/*
* can't be a palindrome - like `R A D A R' or `M A D A M'
*/
return 1;
}
+/*
+ * enough classes of charecters
+ */
+
+static int minclass (struct cracklib_options *opt,
+ const char *new)
+{
+ int digits = 0;
+ int uppers = 0;
+ int lowers = 0;
+ int others = 0;
+ int total_class;
+ int i;
+ int retval;
+
+ D(( "called" ));
+ for (i = 0; new[i]; i++)
+ {
+ if (isdigit (new[i]))
+ digits = 1;
+ else if (isupper (new[i]))
+ uppers = 1;
+ else if (islower (new[i]))
+ lowers = 1;
+ else
+ others = 1;
+ }
+
+ total_class = digits + uppers + lowers + others;
+
+ D (("total class: %d\tmin_class: %d", total_class, opt->min_class));
+
+ if (total_class >= opt->min_class)
+ retval = 0;
+ else
+ retval = 1;
+
+ return retval;
+}
+
+
/*
* a nice mix of characters.
*/
int others = 0;
int size;
int i;
+ enum { NONE, DIGIT, UCASE, LCASE, OTHER } prevclass = NONE;
+ int sameclass = 0;
for (i = 0;new[i];i++) {
- if (isdigit (new[i]))
+ if (isdigit (new[i])) {
digits++;
- else if (isupper (new[i]))
+ if (prevclass != DIGIT) {
+ prevclass = DIGIT;
+ sameclass = 1;
+ } else
+ sameclass++;
+ }
+ else if (isupper (new[i])) {
uppers++;
- else if (islower (new[i]))
+ if (prevclass != UCASE) {
+ prevclass = UCASE;
+ sameclass = 1;
+ } else
+ sameclass++;
+ }
+ else if (islower (new[i])) {
lowers++;
- else
+ if (prevclass != LCASE) {
+ prevclass = LCASE;
+ sameclass = 1;
+ } else
+ sameclass++;
+ }
+ else {
others++;
+ if (prevclass != OTHER) {
+ prevclass = OTHER;
+ sameclass = 1;
+ } else
+ sameclass++;
+ }
+ if (opt->max_class_repeat > 1 && sameclass > opt->max_class_repeat) {
+ return 1;
+ }
}
/*
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 sequence(struct cracklib_options *opt, const char *new)
+{
+ char c;
+ int i;
+ int sequp = 1;
+ int seqdown = 1;
+
+ if (opt->max_sequence == 0)
+ return 0;
+
+ if (new[0] == '\0')
+ return 0;
+
+ for (i = 1; new[i]; i++) {
+ c = new[i-1];
+ if (new[i] == c+1) {
+ ++sequp;
+ if (sequp > opt->max_sequence)
+ return 1;
+ seqdown = 1;
+ } else if (new[i] == c-1) {
+ ++seqdown;
+ if (seqdown > opt->max_sequence)
+ return 1;
+ sequp = 1;
+ } else {
+ sequp = 1;
+ seqdown = 1;
+ }
+ }
+ return 0;
+}
+
+static int wordcheck(const char *new, char *word)
+{
+ char *f, *b;
+
+ if (strstr(new, word) != NULL)
+ return 1;
+
+ /* now reverse the word, we can do that in place
+ as it is strdup-ed */
+ f = word;
+ b = word+strlen(word)-1;
+ while (f < b) {
+ char c;
+
+ c = *f;
+ *f = *b;
+ *b = c;
+ --b;
+ ++f;
+ }
+
+ if (strstr(new, word) != NULL)
+ return 1;
+ return 0;
+}
+
+static int usercheck(struct cracklib_options *opt, const char *new,
+ char *user)
+{
+ if (!opt->reject_user)
+ return 0;
+
+ return wordcheck(new, user);
+}
+
static char * str_lower(char *string)
{
char *cp;
+ if (!string)
+ return NULL;
+
for (cp = string; *cp; cp++)
*cp = tolower(*cp);
return string;
}
-static const char * password_check(struct cracklib_options *opt, const char *old, const char *new)
+static int gecoscheck(pam_handle_t *pamh, struct cracklib_options *opt, const char *new,
+ const char *user)
{
- const char *msg = NULL;
- char *oldmono, *newmono, *wrapped;
+ struct passwd *pwd;
+ char *list;
+ char *p;
+ char *next;
- if (strcmp(new, old) == 0) {
- msg = _("is the same as the old one");
- return msg;
+ if (!opt->gecos_check)
+ return 0;
+
+ if ((pwd = pam_modutil_getpwnam(pamh, user)) == NULL) {
+ return 0;
}
- newmono = str_lower(x_strdup(new));
- oldmono = str_lower(x_strdup(old));
- wrapped = malloc(strlen(oldmono) * 2 + 1);
- strcpy (wrapped, oldmono);
- strcat (wrapped, oldmono);
+ list = strdup(pwd->pw_gecos);
+
+ if (list == NULL || *list == '\0') {
+ free(list);
+ return 0;
+ }
+
+ for (p = list;;p = next + 1) {
+ next = strchr(p, ' ');
+ if (next)
+ *next = '\0';
+
+ if (strlen(p) >= CO_MIN_WORD_LENGTH) {
+ str_lower(p);
+ if (wordcheck(new, p)) {
+ free(list);
+ return 1;
+ }
+ }
+
+ if (!next)
+ break;
+ }
+
+ free(list);
+ return 0;
+}
+
+static const char *password_check(pam_handle_t *pamh, struct cracklib_options *opt,
+ 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");
+ return msg;
+ }
+
+ newmono = str_lower(strdup(new));
+ if (!newmono)
+ msg = _("memory allocation error");
+
+ usermono = str_lower(strdup(user));
+ if (!usermono)
+ msg = _("memory allocation error");
+
+ if (!msg && old) {
+ oldmono = str_lower(strdup(old));
+ if (oldmono)
+ wrapped = malloc(strlen(oldmono) * 2 + 1);
+ if (wrapped) {
+ strcpy (wrapped, oldmono);
+ strcat (wrapped, oldmono);
+ } else {
+ msg = _("memory allocation error");
+ }
+ }
- if (palindrome(newmono))
+ if (!msg && palindrome(newmono))
msg = _("is a palindrome");
- if (!msg && strcmp(oldmono, newmono) == 0)
+ if (!msg && oldmono && strcmp(oldmono, newmono) == 0)
msg = _("case changes only");
- if (!msg && similar(opt, oldmono, newmono))
+ if (!msg && oldmono && similar(opt, oldmono, newmono))
msg = _("is too similar to the old one");
if (!msg && simple(opt, new))
msg = _("is too simple");
- if (!msg && strstr(wrapped, newmono))
+ if (!msg && wrapped && strstr(wrapped, newmono))
msg = _("is rotated");
- memset(newmono, 0, strlen(newmono));
- memset(oldmono, 0, strlen(oldmono));
- memset(wrapped, 0, strlen(wrapped));
- free(newmono);
- free(oldmono);
- free(wrapped);
+ if (!msg && minclass (opt, new))
+ msg = _("not enough character classes");
- return msg;
-}
+ if (!msg && consecutive(opt, new))
+ msg = _("contains too many same characters consecutively");
+ if (!msg && sequence(opt, new))
+ msg = _("contains too long of a monotonic character sequence");
-#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
+ if (!msg && (usercheck(opt, newmono, usermono) || gecoscheck(pamh, opt, newmono, user)))
+ msg = _("contains the user name in some form");
-static const char * check_old_password(const char *forwho, const char *newpass)
-{
- static char buf[16384];
- char *s_luser, *s_uid, *s_npas, *s_pas;
- const char *msg = NULL;
- FILE *opwfile;
-
- opwfile = fopen(OLD_PASSWORDS_FILE, "r");
- if (opwfile == NULL)
- return NULL;
-
- while (fgets(buf, 16380, opwfile)) {
- if (!strncmp(buf, forwho, strlen(forwho))) {
- buf[strlen(buf)-1] = '\0';
- s_luser = strtok(buf, ":,");
- s_uid = strtok(NULL, ":,");
- s_npas = strtok(NULL, ":,");
- s_pas = strtok(NULL, ":,");
- while (s_pas != NULL) {
- if (!strcmp(crypt(newpass, s_pas), s_pas)) {
- msg = _("has been already used");
- break;
- }
- s_pas = strtok(NULL, ":,");
- }
- break;
- }
+ free(usermono);
+ if (newmono) {
+ memset(newmono, 0, strlen(newmono));
+ free(newmono);
+ }
+ if (oldmono) {
+ memset(oldmono, 0, strlen(oldmono));
+ free(oldmono);
+ }
+ if (wrapped) {
+ memset(wrapped, 0, strlen(wrapped));
+ free(wrapped);
}
- fclose(opwfile);
return msg;
}
const char *pass_new)
{
const char *msg = NULL;
- const void *user;
+ const char *user;
int retval;
if (pass_new == NULL || (pass_old && !strcmp(pass_old,pass_new))) {
return PAM_AUTHTOK_ERR;
}
+ retval = pam_get_user(pamh, &user, NULL);
+ 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);
- 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);
- }
+ msg = password_check(pamh, opt, pass_old, pass_new, user);
if (msg) {
if (ctrl & PAM_DEBUG_ARG)
memset(&options, 0, sizeof(options));
options.retry_times = CO_RETRY_TIMES;
options.diff_ok = CO_DIFF_OK;
- options.diff_ignore = CO_DIFF_IGNORE;
options.min_length = CO_MIN_LENGTH;
options.dig_credit = CO_DIG_CREDIT;
options.up_credit = CO_UP_CREDIT;
options.low_credit = CO_LOW_CREDIT;
options.oth_credit = CO_OTH_CREDIT;
- options.use_authtok = CO_USE_AUTHTOK;
- memset(options.prompt_type, 0, BUFSIZ);
- strcpy(options.prompt_type,"UNIX");
- memset(options.cracklib_dictpath, 0,
- sizeof (options.cracklib_dictpath));
+ options.cracklib_dictpath = CRACKLIB_DICTS;
ctrl = _pam_parse(pamh, &options, argc, argv);
return PAM_SUCCESS;
} else if (flags & PAM_UPDATE_AUTHTOK) {
int retval;
- char *token1, *token2, *resp;
const void *oldtoken;
+ int tries;
D(("do update"));
- retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &oldtoken);
+
+
+ retval = pam_get_item (pamh, PAM_OLDAUTHTOK, &oldtoken);
if (retval != PAM_SUCCESS) {
if (ctrl & PAM_DEBUG_ARG)
pam_syslog(pamh,LOG_ERR,"Can not get old passwd");
- oldtoken=NULL;
- retval = PAM_SUCCESS;
+ oldtoken = NULL;
}
- do {
- /*
- * make sure nothing inappropriate gets returned
- */
- token1 = token2 = NULL;
+ tries = 0;
+ while (tries < options.retry_times) {
+ const char *crack_msg;
+ const char *newtoken = NULL;
- if (!options.retry_times) {
- D(("returning %s because maxtries reached",
- pam_strerror(pamh, retval)));
- return retval;
- }
- /* Planned modus operandi:
- * Get a passwd.
- * Verify it against cracklib.
- * If okay get it a second time.
- * Check to be the same with the first one.
- * set PAM_AUTHTOK and return
- */
-
- if (options.use_authtok == 1) {
- const void *item = NULL;
-
- retval = pam_get_item(pamh, PAM_AUTHTOK, &item);
- if (retval != PAM_SUCCESS) {
- /* very strange. */
- pam_syslog(pamh, LOG_ALERT,
- "pam_get_item returned error to pam_cracklib");
- } else if (item != NULL) { /* we have a password! */
- token1 = x_strdup(item);
- item = NULL;
- } else {
- retval = PAM_AUTHTOK_RECOVERY_ERR; /* didn't work */
- }
+ tries++;
- } else {
- /* Prepare to ask the user for the first time */
- resp = NULL;
- retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp,
- PROMPT1, options.prompt_type,
- options.prompt_type[0]?" ":"");
-
- if (retval == PAM_SUCCESS) { /* a good conversation */
- token1 = x_strdup(resp);
- if (token1 == NULL) {
- pam_syslog(pamh, LOG_NOTICE,
- "could not recover authentication token 1");
- retval = PAM_AUTHTOK_RECOVERY_ERR;
- }
- /*
- * tidy up the conversation (resp_retcode) is ignored
- */
- _pam_drop(resp);
- } else {
- retval = (retval == PAM_SUCCESS) ?
- PAM_AUTHTOK_RECOVERY_ERR:retval ;
- }
- }
+ /* Planned modus operandi:
+ * Get a passwd.
+ * Verify it against cracklib.
+ * If okay get it a second time.
+ * Check to be the same with the first one.
+ * set PAM_AUTHTOK and return
+ */
- if (retval != PAM_SUCCESS) {
- if (ctrl & PAM_DEBUG_ARG)
- pam_syslog(pamh,LOG_DEBUG,"unable to obtain a password");
- continue;
- }
+ retval = pam_get_authtok_noverify (pamh, &newtoken, NULL);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "pam_get_authtok_noverify returned error: %s",
+ pam_strerror (pamh, retval));
+ continue;
+ } else if (newtoken == NULL) { /* user aborted password change, quit */
+ return PAM_AUTHTOK_ERR;
+ }
- D(("testing password, retval = %s", pam_strerror(pamh, retval)));
- /* now test this passwd against cracklib */
- {
- const char *crack_msg;
-
- D(("against cracklib"));
- if ((crack_msg = FascistCheck(token1,options.cracklib_dictpath[0] == '\0'?NULL:options.cracklib_dictpath))) {
- if (ctrl & PAM_DEBUG_ARG)
- pam_syslog(pamh,LOG_DEBUG,"bad password: %s",crack_msg);
- pam_error(pamh, _("BAD PASSWORD: %s"), crack_msg);
- if (getuid() || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
- retval = PAM_AUTHTOK_ERR;
- else
- retval = PAM_SUCCESS;
- } else {
- /* check it for strength too... */
- D(("for strength"));
- if (oldtoken) {
- retval = _pam_unix_approve_pass(pamh,ctrl,&options,
- oldtoken,token1);
- if (retval != PAM_SUCCESS) {
- if (getuid() || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
- retval = PAM_AUTHTOK_ERR;
- else
- retval = PAM_SUCCESS;
- }
- }
- }
- }
+ D(("testing password"));
+ /* now test this passwd against cracklib */
- D(("after testing: retval = %s", pam_strerror(pamh, retval)));
- /* if cracklib/strength check said it is a bad passwd... */
- if ((retval != PAM_SUCCESS) && (retval != PAM_IGNORE)) {
- int temp_unused;
+ D(("against cracklib"));
+ if ((crack_msg = FascistCheck (newtoken, options.cracklib_dictpath))) {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh,LOG_DEBUG,"bad password: %s",crack_msg);
+ pam_error (pamh, _("BAD PASSWORD: %s"), crack_msg);
+ if (getuid() || options.enforce_for_root || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
+ {
+ pam_set_item (pamh, PAM_AUTHTOK, NULL);
+ retval = PAM_AUTHTOK_ERR;
+ continue;
+ }
+ }
+
+ /* check it for strength too... */
+ D(("for strength"));
+ retval = _pam_unix_approve_pass (pamh, ctrl, &options,
+ oldtoken, newtoken);
+ if (retval != PAM_SUCCESS) {
+ if (getuid() || options.enforce_for_root || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
+ {
+ pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ retval = PAM_AUTHTOK_ERR;
+ continue;
+ }
+ }
+
+ retval = pam_get_authtok_verify (pamh, &newtoken, NULL);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "pam_get_authtok_verify returned error: %s",
+ pam_strerror (pamh, retval));
+ pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ continue;
+ } else if (newtoken == NULL) { /* user aborted password change, quit */
+ return PAM_AUTHTOK_ERR;
+ }
- temp_unused = pam_set_item(pamh, PAM_AUTHTOK, NULL);
- token1 = _pam_delete(token1);
- continue;
+ return PAM_SUCCESS;
}
- /* Now we have a good passwd. Ask for it once again */
-
- if (options.use_authtok == 0) {
- resp = NULL;
- retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp,
- PROMPT2, options.prompt_type,
- options.prompt_type[0]?" ":"");
- if (retval == PAM_SUCCESS) { /* a good conversation */
- token2 = x_strdup(resp);
- if (token2 == NULL) {
- pam_syslog(pamh,LOG_NOTICE,
- "could not recover authentication token 2");
- retval = PAM_AUTHTOK_RECOVERY_ERR;
- }
- /*
- * tidy up the conversation (resp_retcode) is ignored
- */
- _pam_drop(resp);
- }
-
- /* No else, the a retval == PAM_SUCCESS path can change retval
- to a failure code. */
- if (retval != PAM_SUCCESS) {
- if (ctrl & PAM_DEBUG_ARG)
- pam_syslog(pamh,LOG_DEBUG,"unable to obtain retyped password");
- continue;
- }
-
- /* Hopefully now token1 and token2 the same password ... */
- if (strcmp(token1,token2) != 0) {
- /* tell the user */
- pam_error(pamh, "%s", MISTYPED_PASS);
- token1 = _pam_delete(token1);
- token2 = _pam_delete(token2);
- pam_set_item(pamh, PAM_AUTHTOK, NULL);
- if (ctrl & PAM_DEBUG_ARG)
- pam_syslog(pamh,LOG_NOTICE,"Password mistyped");
- retval = PAM_AUTHTOK_RECOVERY_ERR;
- continue;
- }
-
- /* Yes, the password was typed correct twice
- * we store this password as an item
- */
-
- {
- const void *item = NULL;
-
- retval = pam_set_item(pamh, PAM_AUTHTOK, token1);
-
- /* clean up */
- token1 = _pam_delete(token1);
- token2 = _pam_delete(token2);
-
- if ( (retval != PAM_SUCCESS) ||
- ((retval = pam_get_item(pamh, PAM_AUTHTOK, &item)
- ) != PAM_SUCCESS) ) {
- pam_syslog(pamh, LOG_CRIT, "error manipulating password");
- continue;
- }
- item = NULL; /* break link to password */
- return PAM_SUCCESS;
- }
- }
+ D(("returning because maxtries reached"));
+
+ pam_set_item (pamh, PAM_AUTHTOK, NULL);
- } while (options.retry_times--);
+ /* if we have only one try, we can use the real reason,
+ else say that there were too many tries. */
+ if (options.retry_times > 1)
+ return PAM_MAXTRIES;
+ else
+ return retval;
} else {
if (ctrl & PAM_DEBUG_ARG)