diff options
Diffstat (limited to 'contrib/pam_modules/pam_passwdqc/pam_passwdqc.c')
-rw-r--r-- | contrib/pam_modules/pam_passwdqc/pam_passwdqc.c | 601 |
1 files changed, 303 insertions, 298 deletions
diff --git a/contrib/pam_modules/pam_passwdqc/pam_passwdqc.c b/contrib/pam_modules/pam_passwdqc/pam_passwdqc.c index 7c3973105cd2..6be3d9e0499f 100644 --- a/contrib/pam_modules/pam_passwdqc/pam_passwdqc.c +++ b/contrib/pam_modules/pam_passwdqc/pam_passwdqc.c @@ -1,10 +1,19 @@ /* - * Copyright (c) 2000-2002 by Solar Designer. See LICENSE. + * Copyright (c) 2000-2003,2005,2012,2016,2019,2021 by Solar Designer + * Copyright (c) 2017,2018 by Dmitry V. Levin + * Copyright (c) 2017,2018 by Oleg Solovyov + * See LICENSE */ +#ifdef __FreeBSD__ +/* For vsnprintf(3) */ +#define _XOPEN_SOURCE 600 +#else #define _XOPEN_SOURCE 500 #define _XOPEN_SOURCE_EXTENDED #define _XOPEN_VERSION 500 +#define _DEFAULT_SOURCE +#endif #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -15,6 +24,10 @@ #ifdef HAVE_SHADOW #include <shadow.h> #endif +#ifdef HAVE_LIBAUDIT +#include <security/pam_modutil.h> +#include <libaudit.h> +#endif #define PAM_SM_PASSWORD #ifndef LINUX_PAM @@ -32,119 +45,164 @@ #define PAM_AUTHTOK_RECOVERY_ERR PAM_AUTHTOK_RECOVER_ERR #endif -#if defined(__sun__) && !defined(LINUX_PAM) && !defined(_OPENPAM) -/* Sun's PAM doesn't use const here */ +#if (defined(__sun) || defined(__hpux)) && \ + !defined(LINUX_PAM) && !defined(_OPENPAM) +/* Sun's PAM doesn't use const here, while Linux-PAM and OpenPAM do */ #define lo_const #else #define lo_const const #endif +#ifdef _OPENPAM +/* OpenPAM doesn't use const here, while Linux-PAM does */ +#define l_const +#else +#define l_const lo_const +#endif typedef lo_const void *pam_item_t; #include "passwdqc.h" -#define F_ENFORCE_MASK 0x00000003 -#define F_ENFORCE_USERS 0x00000001 -#define F_ENFORCE_ROOT 0x00000002 -#define F_ENFORCE_EVERYONE F_ENFORCE_MASK -#define F_NON_UNIX 0x00000004 -#define F_ASK_OLDAUTHTOK_MASK 0x00000030 -#define F_ASK_OLDAUTHTOK_PRELIM 0x00000010 -#define F_ASK_OLDAUTHTOK_UPDATE 0x00000020 -#define F_CHECK_OLDAUTHTOK 0x00000040 -#define F_USE_FIRST_PASS 0x00000100 -#define F_USE_AUTHTOK 0x00000200 - -typedef struct { - passwdqc_params_t qc; - int flags; - int retry; -} params_t; - -static params_t defaults = { - { - {INT_MAX, 24, 12, 8, 7}, /* min */ - 40, /* max */ - 3, /* passphrase_words */ - 4, /* match_length */ - 1, /* similar_deny */ - 42 /* random_bits */ - }, - F_ENFORCE_EVERYONE, /* flags */ - 3 /* retry */ -}; +#include "passwdqc_i18n.h" #define PROMPT_OLDPASS \ - "Enter current password: " + _("Enter current password: ") #define PROMPT_NEWPASS1 \ - "Enter new password: " + _("Enter new password: ") #define PROMPT_NEWPASS2 \ - "Re-type new password: " + _("Re-type new password: ") #define MESSAGE_MISCONFIGURED \ - "System configuration error. Please contact your administrator." + _("System configuration error. Please contact your administrator.") #define MESSAGE_INVALID_OPTION \ - "pam_passwdqc: Invalid option: \"%s\"." + "pam_passwdqc: %s." #define MESSAGE_INTRO_PASSWORD \ - "\nYou can now choose the new password.\n" + _("\nYou can now choose the new password.\n") #define MESSAGE_INTRO_BOTH \ - "\nYou can now choose the new password or passphrase.\n" -#define MESSAGE_EXPLAIN_PASSWORD_1 \ - "A valid password should be a mix of upper and lower case letters,\n" \ - "digits and other characters. You can use a%s %d character long\n" \ - "password with characters from at least 3 of these 4 classes.\n" \ - "Characters that form a common pattern are discarded by the check.\n" -#define MESSAGE_EXPLAIN_PASSWORD_2 \ - "A valid password should be a mix of upper and lower case letters,\n" \ - "digits and other characters. You can use a%s %d character long\n" \ - "password with characters from at least 3 of these 4 classes, or\n" \ - "a%s %d character long password containing characters from all the\n" \ - "classes. Characters that form a common pattern are discarded by\n" \ - "the check.\n" -#define MESSAGE_EXPLAIN_PASSPHRASE \ - "A passphrase should be of at least %d words, %d to %d characters\n" \ - "long and contain enough different characters.\n" + _("\nYou can now choose the new password or passphrase.\n") + +#define MESSAGE_EXPLAIN_PASSWORD_1_CLASS(count) \ + P3_( \ + "A good password should be a mix of upper and lower case letters, digits, and\n" \ + "other characters. You can use a password containing at least %d character.\n", \ + \ + "A good password should be a mix of upper and lower case letters, digits, and\n" \ + "other characters. You can use a password containing at least %d characters.\n", \ + count), (count) + +#define MESSAGE_EXPLAIN_PASSWORD_N_CLASSES(count) \ + P3_( \ + "A valid password should be a mix of upper and lower case letters, digits, and\n" \ + "other characters. You can use a password containing at least %d character\n" \ + "from at least %d of these 4 classes.\n" \ + "An upper case letter that begins the password and a digit that ends it do not\n" \ + "count towards the number of character classes used.\n", \ + \ + "A valid password should be a mix of upper and lower case letters, digits, and\n" \ + "other characters. You can use a password containing at least %d characters\n" \ + "from at least %d of these 4 classes.\n" \ + "An upper case letter that begins the password and a digit that ends it do not\n" \ + "count towards the number of character classes used.\n", \ + count), (count) + +#define MESSAGE_EXPLAIN_PASSWORD_ALL_CLASSES(count) \ + P3_( \ + "A valid password should be a mix of upper and lower case letters, digits, and\n" \ + "other characters. You can use a password containing at least %d character\n" \ + "from all of these classes.\n" \ + "An upper case letter that begins the password and a digit that ends it do not\n" \ + "count towards the number of character classes used.\n", \ + \ + "A valid password should be a mix of upper and lower case letters, digits, and\n" \ + "other characters. You can use a password containing at least %d characters\n" \ + "from all of these classes.\n" \ + "An upper case letter that begins the password and a digit that ends it do not\n" \ + "count towards the number of character classes used.\n", \ + count), (count) + +#define MESSAGE_EXPLAIN_PASSWORD_ALL_OR_3_CLASSES(count) \ + P3_( \ + "A valid password should be a mix of upper and lower case letters, digits, and\n" \ + "other characters. You can use a password containing at least %d character\n" \ + "from all of these classes, or a password containing at least %d characters\n" \ + "from just 3 of these 4 classes.\n" \ + "An upper case letter that begins the password and a digit that ends it do not\n" \ + "count towards the number of character classes used.\n", \ + \ + "A valid password should be a mix of upper and lower case letters, digits, and\n" \ + "other characters. You can use a password containing at least %d characters\n" \ + "from all of these classes, or a password containing at least %d characters\n" \ + "from just 3 of these 4 classes.\n" \ + "An upper case letter that begins the password and a digit that ends it do not\n" \ + "count towards the number of character classes used.\n", \ + count), (count) + +#define MESSAGE_EXPLAIN_PASSPHRASE(count) \ + P3_(\ + "A passphrase should be of at least %d word, %d to %d characters long, and\n" \ + "contain enough different characters.\n", \ + \ + "A passphrase should be of at least %d words, %d to %d characters long, and\n" \ + "contain enough different characters.\n", \ + count), (count) + #define MESSAGE_RANDOM \ - "Alternatively, if noone else can see your terminal now, you can\n" \ - "pick this as your password: \"%s\".\n" + _("Alternatively, if no one else can see your terminal now, you can pick this as\n" \ + "your password: \"%s\".\n") #define MESSAGE_RANDOMONLY \ - "This system is configured to permit randomly generated passwords\n" \ - "only. If noone else can see your terminal now, you can pick this\n" \ - "as your password: \"%s\". Otherwise, come back later.\n" + _("This system is configured to permit randomly generated passwords only.\n" \ + "If no one else can see your terminal now, you can pick this as your\n" \ + "password: \"%s\". Otherwise come back later.\n") #define MESSAGE_RANDOMFAILED \ - "This system is configured to use randomly generated passwords\n" \ - "only, but the attempt to generate a password has failed. This\n" \ - "could happen for a number of reasons: you could have requested\n" \ - "an impossible password length, or the access to kernel random\n" \ - "number pool could have failed." + _("This system is configured to use randomly generated passwords only,\n" \ + "but the attempt to generate a password has failed. This could happen\n" \ + "for a number of reasons: you could have requested an impossible password\n" \ + "length, or the access to kernel random number pool could have failed.") #define MESSAGE_TOOLONG \ - "This password may be too long for some services. Choose another." + _("This password may be too long for some services. Choose another.") #define MESSAGE_TRUNCATED \ - "Warning: your longer password will be truncated to 8 characters." + _("Warning: your longer password will be truncated to 8 characters.") #define MESSAGE_WEAKPASS \ - "Weak password: %s." + _("Weak password: %s.") #define MESSAGE_NOTRANDOM \ - "Sorry, you've mistyped the password that was generated for you." + _("Sorry, you've mistyped the password that was generated for you.") #define MESSAGE_MISTYPED \ - "Sorry, passwords do not match." + _("Sorry, passwords do not match.") #define MESSAGE_RETRY \ - "Try again." + _("Try again.") -static int converse(pam_handle_t *pamh, int style, lo_const char *text, +static int logaudit(pam_handle_t *pamh, int status, passwdqc_params_t *params) +{ +#ifdef HAVE_LIBAUDIT + if (!(params->pam.flags & F_NO_AUDIT)) { + int rc = pam_modutil_audit_write(pamh, AUDIT_USER_CHAUTHTOK, "pam_passwdqc", status); + if (status == PAM_SUCCESS) + status = rc; + } +#else /* !HAVE_LIBAUDIT */ + (void) pamh; +#endif + passwdqc_params_free(params); + return status; +} + +static int converse(pam_handle_t *pamh, int style, l_const char *text, struct pam_response **resp) { - struct pam_conv *conv; + pam_item_t item; + const struct pam_conv *conv; struct pam_message msg, *pmsg; int status; - status = pam_get_item(pamh, PAM_CONV, (pam_item_t *)&conv); + *resp = NULL; + status = pam_get_item(pamh, PAM_CONV, &item); if (status != PAM_SUCCESS) return status; + conv = item; pmsg = &msg; msg.msg_style = style; msg.msg = text; - *resp = NULL; return conv->conv(1, (lo_const struct pam_message **)&pmsg, resp, conv->appdata_ptr); } @@ -166,19 +224,20 @@ static int say(pam_handle_t *pamh, int style, const char *format, ...) if ((unsigned int)needed < sizeof(buffer)) { status = converse(pamh, style, buffer, &resp); - _pam_overwrite(buffer); + pwqc_drop_pam_reply(resp, 1); } else { status = PAM_ABORT; - memset(buffer, 0, sizeof(buffer)); } + _passwdqc_memzero(buffer, sizeof(buffer)); return status; } -static int check_max(params_t *params, pam_handle_t *pamh, const char *newpass) +static int check_max(passwdqc_params_qc_t *qc, pam_handle_t *pamh, + const char *newpass) { - if ((int)strlen(newpass) > params->qc.max) { - if (params->qc.max != 8) { + if (strlen(newpass) > (size_t)qc->max) { + if (qc->max != 8) { say(pamh, PAM_ERROR_MSG, MESSAGE_TOOLONG); return -1; } @@ -188,152 +247,96 @@ static int check_max(params_t *params, pam_handle_t *pamh, const char *newpass) return 0; } -static int parse(params_t *params, pam_handle_t *pamh, - int argc, const char **argv) +static int check_pass(struct passwd *pw, const char *pass) { - const char *p; - char *e; - int i; - unsigned long v; - - while (argc) { - if (!strncmp(*argv, "min=", 4)) { - p = *argv + 4; - for (i = 0; i < 5; i++) { - if (!strncmp(p, "disabled", 8)) { - v = INT_MAX; - p += 8; - } else { - v = strtoul(p, &e, 10); - p = e; - } - if (i < 4 && *p++ != ',') break; - if (v > INT_MAX) break; - if (i && (int)v > params->qc.min[i - 1]) break; - params->qc.min[i] = v; - } - if (*p) break; - } else - if (!strncmp(*argv, "max=", 4)) { - v = strtoul(*argv + 4, &e, 10); - if (*e || v < 8 || v > INT_MAX) break; - params->qc.max = v; - } else - if (!strncmp(*argv, "passphrase=", 11)) { - v = strtoul(*argv + 11, &e, 10); - if (*e || v > INT_MAX) break; - params->qc.passphrase_words = v; - } else - if (!strncmp(*argv, "match=", 6)) { - v = strtoul(*argv + 6, &e, 10); - if (*e || v > INT_MAX) break; - params->qc.match_length = v; - } else - if (!strncmp(*argv, "similar=", 8)) { - if (!strcmp(*argv + 8, "permit")) - params->qc.similar_deny = 0; - else - if (!strcmp(*argv + 8, "deny")) - params->qc.similar_deny = 1; - else - break; - } else - if (!strncmp(*argv, "random=", 7)) { - v = strtoul(*argv + 7, &e, 10); - if (!strcmp(e, ",only")) { - e += 5; - params->qc.min[4] = INT_MAX; - } - if (*e || v > INT_MAX) break; - params->qc.random_bits = v; - } else - if (!strncmp(*argv, "enforce=", 8)) { - params->flags &= ~F_ENFORCE_MASK; - if (!strcmp(*argv + 8, "users")) - params->flags |= F_ENFORCE_USERS; - else - if (!strcmp(*argv + 8, "everyone")) - params->flags |= F_ENFORCE_EVERYONE; - else - if (strcmp(*argv + 8, "none")) - break; - } else - if (!strcmp(*argv, "non-unix")) { - if (params->flags & F_CHECK_OLDAUTHTOK) break; - params->flags |= F_NON_UNIX; - } else - if (!strncmp(*argv, "retry=", 6)) { - v = strtoul(*argv + 6, &e, 10); - if (*e || v > INT_MAX) break; - params->retry = v; - } else - if (!strncmp(*argv, "ask_oldauthtok", 14)) { - params->flags &= ~F_ASK_OLDAUTHTOK_MASK; - if (params->flags & F_USE_FIRST_PASS) break; - if (!strcmp(*argv + 14, "=update")) - params->flags |= F_ASK_OLDAUTHTOK_UPDATE; - else - if (!(*argv)[14]) - params->flags |= F_ASK_OLDAUTHTOK_PRELIM; - else - break; - } else - if (!strcmp(*argv, "check_oldauthtok")) { - if (params->flags & F_NON_UNIX) break; - params->flags |= F_CHECK_OLDAUTHTOK; - } else - if (!strcmp(*argv, "use_first_pass")) { - if (params->flags & F_ASK_OLDAUTHTOK_MASK) break; - params->flags |= F_USE_FIRST_PASS | F_USE_AUTHTOK; - } else - if (!strcmp(*argv, "use_authtok")) { - params->flags |= F_USE_AUTHTOK; - } else - break; - argc--; argv++; - } + const char *hash; + int retval; - if (argc) { - say(pamh, PAM_ERROR_MSG, getuid() != 0 ? - MESSAGE_MISCONFIGURED : MESSAGE_INVALID_OPTION, *argv); - return PAM_ABORT; +#ifdef HAVE_SHADOW +#ifdef __hpux + if (iscomsec()) { +#else + if (!strcmp(pw->pw_passwd, "x")) { +#endif + struct spwd *spw = getspnam(pw->pw_name); + endspent(); + if (!spw) + return -1; + hash = NULL; + if (strlen(spw->sp_pwdp) >= 13) { +#ifdef __hpux + hash = bigcrypt(pass, spw->sp_pwdp); +#else + hash = crypt(pass, spw->sp_pwdp); +#endif + } + retval = (hash && !strcmp(hash, spw->sp_pwdp)) ? 0 : -1; + _passwdqc_memzero(spw->sp_pwdp, strlen(spw->sp_pwdp)); + return retval; } +#endif - return PAM_SUCCESS; + hash = NULL; + if (strlen(pw->pw_passwd) >= 13) + hash = crypt(pass, pw->pw_passwd); + retval = (hash && !strcmp(hash, pw->pw_passwd)) ? 0 : -1; + _passwdqc_memzero(pw->pw_passwd, strlen(pw->pw_passwd)); + return retval; +} + +static int am_root(pam_handle_t *pamh) +{ + pam_item_t item; + const char *service; + + if (getuid() != 0) + return 0; + + if (pam_get_item(pamh, PAM_SERVICE, &item) != PAM_SUCCESS) + return 0; + service = item; + + return !strcmp(service, "passwd") || !strcmp(service, "chpasswd"); } PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) { - params_t params; + passwdqc_params_t params; struct pam_response *resp; struct passwd *pw, fake_pw; -#ifdef HAVE_SHADOW - struct spwd *spw; -#endif - char *user, *oldpass, *newpass, *randompass; - const char *reason; + pam_item_t item; + const char *user, *oldpass, *newpass; + char *trypass, *randompass; + char *parse_reason; + const char *check_reason; int ask_oldauthtok; int randomonly, enforce, retries_left, retry_wanted; int status; - params = defaults; - status = parse(¶ms, pamh, argc, argv); - if (status != PAM_SUCCESS) - return status; + passwdqc_params_reset(¶ms); + if (passwdqc_params_parse(¶ms, &parse_reason, argc, argv)) { + say(pamh, PAM_ERROR_MSG, am_root(pamh) ? + MESSAGE_INVALID_OPTION : MESSAGE_MISCONFIGURED, + parse_reason); + free(parse_reason); + return PAM_ABORT; + } + status = PAM_SUCCESS; ask_oldauthtok = 0; if (flags & PAM_PRELIM_CHECK) { - if (params.flags & F_ASK_OLDAUTHTOK_PRELIM) + if (params.pam.flags & F_ASK_OLDAUTHTOK_PRELIM) ask_oldauthtok = 1; - } else - if (flags & PAM_UPDATE_AUTHTOK) { - if (params.flags & F_ASK_OLDAUTHTOK_UPDATE) + } else if (flags & PAM_UPDATE_AUTHTOK) { + if (params.pam.flags & F_ASK_OLDAUTHTOK_UPDATE) ask_oldauthtok = 1; - } else + } else { + passwdqc_params_free(¶ms); return PAM_SERVICE_ERR; + } - if (ask_oldauthtok && getuid() != 0) { + if (ask_oldauthtok && !am_root(pamh)) { status = converse(pamh, PAM_PROMPT_ECHO_OFF, PROMPT_OLDPASS, &resp); @@ -341,86 +344,80 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, if (resp && resp->resp) { status = pam_set_item(pamh, PAM_OLDAUTHTOK, resp->resp); - _pam_drop_reply(resp, 1); + pwqc_drop_pam_reply(resp, 1); } else status = PAM_AUTHTOK_RECOVERY_ERR; } if (status != PAM_SUCCESS) - return status; + return logaudit(pamh, status, ¶ms); } - if (flags & PAM_PRELIM_CHECK) + if (flags & PAM_PRELIM_CHECK) { + passwdqc_params_free(¶ms); return status; + } - status = pam_get_item(pamh, PAM_USER, (pam_item_t *)&user); + status = pam_get_item(pamh, PAM_USER, &item); if (status != PAM_SUCCESS) - return status; + return logaudit(pamh, status, ¶ms); + user = item; - status = pam_get_item(pamh, PAM_OLDAUTHTOK, (pam_item_t *)&oldpass); + status = pam_get_item(pamh, PAM_OLDAUTHTOK, &item); if (status != PAM_SUCCESS) - return status; + return logaudit(pamh, status, ¶ms); + oldpass = item; - if (params.flags & F_NON_UNIX) { + if (params.pam.flags & F_NON_UNIX) { pw = &fake_pw; - pw->pw_name = user; + memset(pw, 0, sizeof(*pw)); + pw->pw_name = (char *)user; pw->pw_gecos = ""; + pw->pw_dir = ""; } else { +/* As currently implemented, we don't avoid timing leaks for valid vs. not + * usernames and hashes. Normally, the username would have already been + * checked and determined valid, and the check_oldauthtok option is only needed + * on systems that happen to have similar timing leaks all over the place. */ pw = getpwnam(user); endpwent(); if (!pw) - return PAM_USER_UNKNOWN; - if ((params.flags & F_CHECK_OLDAUTHTOK) && getuid() != 0) { - if (!oldpass) - status = PAM_AUTH_ERR; - else -#ifdef HAVE_SHADOW - if (!strcmp(pw->pw_passwd, "x")) { - spw = getspnam(user); - endspent(); - if (spw) { - if (strcmp(crypt(oldpass, spw->sp_pwdp), - spw->sp_pwdp)) - status = PAM_AUTH_ERR; - memset(spw->sp_pwdp, 0, - strlen(spw->sp_pwdp)); - } else - status = PAM_AUTH_ERR; - } else -#endif - if (strcmp(crypt(oldpass, pw->pw_passwd), - pw->pw_passwd)) - status = PAM_AUTH_ERR; - } - memset(pw->pw_passwd, 0, strlen(pw->pw_passwd)); + return logaudit(pamh, PAM_USER_UNKNOWN, ¶ms); + if ((params.pam.flags & F_CHECK_OLDAUTHTOK) && !am_root(pamh) + && (!oldpass || check_pass(pw, oldpass))) + status = PAM_AUTH_ERR; + _passwdqc_memzero(pw->pw_passwd, strlen(pw->pw_passwd)); if (status != PAM_SUCCESS) - return status; + return logaudit(pamh, status, ¶ms); } randomonly = params.qc.min[4] > params.qc.max; - if (getuid() != 0) - enforce = params.flags & F_ENFORCE_USERS; + if (am_root(pamh)) + enforce = params.pam.flags & F_ENFORCE_ROOT; else - enforce = params.flags & F_ENFORCE_ROOT; + enforce = params.pam.flags & F_ENFORCE_USERS; - if (params.flags & F_USE_AUTHTOK) { - status = pam_get_item(pamh, PAM_AUTHTOK, - (pam_item_t *)&newpass); + if (params.pam.flags & F_USE_AUTHTOK) { + status = pam_get_item(pamh, PAM_AUTHTOK, &item); if (status != PAM_SUCCESS) - return status; - if (!newpass || (check_max(¶ms, pamh, newpass) && enforce)) - return PAM_AUTHTOK_ERR; - reason = _passwdqc_check(¶ms.qc, newpass, oldpass, pw); - if (reason) { - say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason); + return logaudit(pamh, status, ¶ms); + newpass = item; + if (!newpass || + (check_max(¶ms.qc, pamh, newpass) && enforce)) + return logaudit(pamh, PAM_AUTHTOK_ERR, ¶ms); + check_reason = + passwdqc_check(¶ms.qc, newpass, oldpass, pw); + if (check_reason) { + say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, + check_reason); if (enforce) status = PAM_AUTHTOK_ERR; } - return status; + return logaudit(pamh, status, ¶ms); } - retries_left = params.retry; + retries_left = params.pam.retry; retry: retry_wanted = 0; @@ -431,45 +428,48 @@ retry: else status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_PASSWORD); if (status != PAM_SUCCESS) - return status; - - if (!randomonly && params.qc.min[3] <= params.qc.min[4]) - status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_1, - params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "", + return logaudit(pamh, status, ¶ms); + + if (!randomonly && params.qc.min[0] == params.qc.min[4]) + status = say(pamh, PAM_TEXT_INFO, + MESSAGE_EXPLAIN_PASSWORD_1_CLASS(params.qc.min[4])); + + else if (!randomonly && params.qc.min[3] == params.qc.min[4]) + status = say(pamh, PAM_TEXT_INFO, + MESSAGE_EXPLAIN_PASSWORD_N_CLASSES(params.qc.min[4]), + params.qc.min[1] != params.qc.min[3] ? 3 : 2); + else if (!randomonly && params.qc.min[3] == INT_MAX) + status = say(pamh, PAM_TEXT_INFO, + MESSAGE_EXPLAIN_PASSWORD_ALL_CLASSES(params.qc.min[4])); + else if (!randomonly) { + status = say(pamh, PAM_TEXT_INFO, + MESSAGE_EXPLAIN_PASSWORD_ALL_OR_3_CLASSES(params.qc.min[4]), params.qc.min[3]); - else - if (!randomonly) - status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_2, - params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "", - params.qc.min[3], - params.qc.min[4] == 8 || params.qc.min[4] == 11 ? "n" : "", - params.qc.min[4]); + } if (status != PAM_SUCCESS) - return status; + return logaudit(pamh, status, ¶ms); if (!randomonly && - params.qc.passphrase_words && - params.qc.min[2] <= params.qc.max) { - status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSPHRASE, - params.qc.passphrase_words, + params.qc.passphrase_words && params.qc.min[2] <= params.qc.max) { + status = say(pamh, PAM_TEXT_INFO, + MESSAGE_EXPLAIN_PASSPHRASE(params.qc.passphrase_words), params.qc.min[2], params.qc.max); if (status != PAM_SUCCESS) - return status; + return logaudit(pamh, status, ¶ms); } - randompass = _passwdqc_random(¶ms.qc); + randompass = passwdqc_random(¶ms.qc); if (randompass) { status = say(pamh, PAM_TEXT_INFO, randomonly ? MESSAGE_RANDOMONLY : MESSAGE_RANDOM, randompass); if (status != PAM_SUCCESS) { - _pam_overwrite(randompass); - randompass = NULL; + pwqc_overwrite_string(randompass); + pwqc_drop_mem(randompass); } - } else - if (randomonly) { - say(pamh, PAM_ERROR_MSG, getuid() != 0 ? - MESSAGE_MISCONFIGURED : MESSAGE_RANDOMFAILED); - return PAM_AUTHTOK_ERR; + } else if (randomonly) { + say(pamh, PAM_ERROR_MSG, am_root(pamh) ? + MESSAGE_RANDOMFAILED : MESSAGE_MISCONFIGURED); + return logaudit(pamh, PAM_AUTHTOK_ERR, ¶ms); } status = converse(pamh, PAM_PROMPT_ECHO_OFF, PROMPT_NEWPASS1, &resp); @@ -477,33 +477,36 @@ retry: status = PAM_AUTHTOK_ERR; if (status != PAM_SUCCESS) { - if (randompass) _pam_overwrite(randompass); - return status; + pwqc_overwrite_string(randompass); + pwqc_drop_mem(randompass); + return logaudit(pamh, status, ¶ms); } - newpass = strdup(resp->resp); + trypass = strdup(resp->resp); - _pam_drop_reply(resp, 1); + pwqc_drop_pam_reply(resp, 1); - if (!newpass) { - if (randompass) _pam_overwrite(randompass); - return PAM_AUTHTOK_ERR; + if (!trypass) { + pwqc_overwrite_string(randompass); + pwqc_drop_mem(randompass); + return logaudit(pamh, PAM_AUTHTOK_ERR, ¶ms); } - if (check_max(¶ms, pamh, newpass) && enforce) { + if (check_max(¶ms.qc, pamh, trypass) && enforce) { status = PAM_AUTHTOK_ERR; retry_wanted = 1; } - reason = NULL; + check_reason = NULL; /* unused */ if (status == PAM_SUCCESS && - (!randompass || !strstr(newpass, randompass)) && + (!randompass || !strstr(trypass, randompass)) && (randomonly || - (reason = _passwdqc_check(¶ms.qc, newpass, oldpass, pw)))) { + (check_reason = passwdqc_check(¶ms.qc, trypass, oldpass, pw)))) { if (randomonly) say(pamh, PAM_ERROR_MSG, MESSAGE_NOTRANDOM); else - say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason); + say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, + check_reason); if (enforce) { status = PAM_AUTHTOK_ERR; retry_wanted = 1; @@ -515,7 +518,7 @@ retry: PROMPT_NEWPASS2, &resp); if (status == PAM_SUCCESS) { if (resp && resp->resp) { - if (strcmp(newpass, resp->resp)) { + if (strcmp(trypass, resp->resp)) { status = say(pamh, PAM_ERROR_MSG, MESSAGE_MISTYPED); if (status == PAM_SUCCESS) { @@ -523,17 +526,19 @@ retry: retry_wanted = 1; } } - _pam_drop_reply(resp, 1); + pwqc_drop_pam_reply(resp, 1); } else status = PAM_AUTHTOK_ERR; } if (status == PAM_SUCCESS) - status = pam_set_item(pamh, PAM_AUTHTOK, newpass); + status = pam_set_item(pamh, PAM_AUTHTOK, trypass); - if (randompass) _pam_overwrite(randompass); - _pam_overwrite(newpass); - free(newpass); + pwqc_overwrite_string(randompass); + pwqc_drop_mem(randompass); + + pwqc_overwrite_string(trypass); + pwqc_drop_mem(trypass); if (retry_wanted && --retries_left > 0) { status = say(pamh, PAM_TEXT_INFO, MESSAGE_RETRY); @@ -541,13 +546,13 @@ retry: goto retry; } - return status; + return logaudit(pamh, status, ¶ms); } #ifdef PAM_MODULE_ENTRY PAM_MODULE_ENTRY("pam_passwdqc"); #elif defined(PAM_STATIC) -struct pam_module _pam_passwdqc_modstruct = { +const struct pam_module _pam_passwdqc_modstruct = { "pam_passwdqc", NULL, NULL, |