193787Sdes/*
294691Sdes * Copyright (c) 2000-2002 by Solar Designer. See LICENSE.
393787Sdes */
493787Sdes
5254960Swill#define _XOPEN_SOURCE 600
693787Sdes#define _XOPEN_SOURCE_EXTENDED
7254960Swill#define _XOPEN_VERSION 600
893787Sdes#include <stdio.h>
993787Sdes#include <stdlib.h>
1093787Sdes#include <stdarg.h>
1193787Sdes#include <string.h>
1293787Sdes#include <limits.h>
1393787Sdes#include <unistd.h>
1493787Sdes#include <pwd.h>
1593787Sdes#ifdef HAVE_SHADOW
1693787Sdes#include <shadow.h>
1793787Sdes#endif
1893787Sdes
1993787Sdes#define PAM_SM_PASSWORD
2093787Sdes#ifndef LINUX_PAM
2193787Sdes#include <security/pam_appl.h>
2293787Sdes#endif
2393787Sdes#include <security/pam_modules.h>
2493787Sdes
2593787Sdes#include "pam_macros.h"
2693787Sdes
2793787Sdes#if !defined(PAM_EXTERN) && !defined(PAM_STATIC)
2893787Sdes#define PAM_EXTERN			extern
2993787Sdes#endif
3093787Sdes
3194691Sdes#if !defined(PAM_AUTHTOK_RECOVERY_ERR) && defined(PAM_AUTHTOK_RECOVER_ERR)
3294691Sdes#define PAM_AUTHTOK_RECOVERY_ERR	PAM_AUTHTOK_RECOVER_ERR
3393787Sdes#endif
3493787Sdes
3594691Sdes#if defined(__sun__) && !defined(LINUX_PAM) && !defined(_OPENPAM)
3694691Sdes/* Sun's PAM doesn't use const here */
3794691Sdes#define lo_const
3893787Sdes#else
3994691Sdes#define lo_const			const
4093787Sdes#endif
4194691Sdestypedef lo_const void *pam_item_t;
4293787Sdes
4393787Sdes#include "passwdqc.h"
4493787Sdes
4593787Sdes#define F_ENFORCE_MASK			0x00000003
4693787Sdes#define F_ENFORCE_USERS			0x00000001
4793787Sdes#define F_ENFORCE_ROOT			0x00000002
4893787Sdes#define F_ENFORCE_EVERYONE		F_ENFORCE_MASK
4993787Sdes#define F_NON_UNIX			0x00000004
5093787Sdes#define F_ASK_OLDAUTHTOK_MASK		0x00000030
5193787Sdes#define F_ASK_OLDAUTHTOK_PRELIM		0x00000010
5293787Sdes#define F_ASK_OLDAUTHTOK_UPDATE		0x00000020
5393787Sdes#define F_CHECK_OLDAUTHTOK		0x00000040
5493787Sdes#define F_USE_FIRST_PASS		0x00000100
5593787Sdes#define F_USE_AUTHTOK			0x00000200
5693787Sdes
5793787Sdestypedef struct {
5893787Sdes	passwdqc_params_t qc;
5993787Sdes	int flags;
6093787Sdes	int retry;
6193787Sdes} params_t;
6293787Sdes
6393787Sdesstatic params_t defaults = {
6493787Sdes	{
6593787Sdes		{INT_MAX, 24, 12, 8, 7},	/* min */
6693787Sdes		40,				/* max */
6793787Sdes		3,				/* passphrase_words */
6893787Sdes		4,				/* match_length */
6993787Sdes		1,				/* similar_deny */
7093787Sdes		42				/* random_bits */
7193787Sdes	},
7293787Sdes	F_ENFORCE_EVERYONE,			/* flags */
7393787Sdes	3					/* retry */
7493787Sdes};
7593787Sdes
7693787Sdes#define PROMPT_OLDPASS \
7793787Sdes	"Enter current password: "
7893787Sdes#define PROMPT_NEWPASS1 \
7993787Sdes	"Enter new password: "
8093787Sdes#define PROMPT_NEWPASS2 \
8193787Sdes	"Re-type new password: "
8293787Sdes
8393787Sdes#define MESSAGE_MISCONFIGURED \
8493787Sdes	"System configuration error.  Please contact your administrator."
8593787Sdes#define MESSAGE_INVALID_OPTION \
8693787Sdes	"pam_passwdqc: Invalid option: \"%s\"."
8793787Sdes#define MESSAGE_INTRO_PASSWORD \
8893787Sdes	"\nYou can now choose the new password.\n"
8993787Sdes#define MESSAGE_INTRO_BOTH \
9093787Sdes	"\nYou can now choose the new password or passphrase.\n"
9193787Sdes#define MESSAGE_EXPLAIN_PASSWORD_1 \
9293787Sdes	"A valid password should be a mix of upper and lower case letters,\n" \
9393787Sdes	"digits and other characters.  You can use a%s %d character long\n" \
9493787Sdes	"password with characters from at least 3 of these 4 classes.\n" \
9593787Sdes	"Characters that form a common pattern are discarded by the check.\n"
9693787Sdes#define MESSAGE_EXPLAIN_PASSWORD_2 \
9793787Sdes	"A valid password should be a mix of upper and lower case letters,\n" \
9893787Sdes	"digits and other characters.  You can use a%s %d character long\n" \
9993787Sdes	"password with characters from at least 3 of these 4 classes, or\n" \
10093787Sdes	"a%s %d character long password containing characters from all the\n" \
10193787Sdes	"classes.  Characters that form a common pattern are discarded by\n" \
10293787Sdes	"the check.\n"
10393787Sdes#define MESSAGE_EXPLAIN_PASSPHRASE \
10493787Sdes	"A passphrase should be of at least %d words, %d to %d characters\n" \
10593787Sdes	"long and contain enough different characters.\n"
10693787Sdes#define MESSAGE_RANDOM \
10793787Sdes	"Alternatively, if noone else can see your terminal now, you can\n" \
10893787Sdes	"pick this as your password: \"%s\".\n"
10993787Sdes#define MESSAGE_RANDOMONLY \
11093787Sdes	"This system is configured to permit randomly generated passwords\n" \
11193787Sdes	"only.  If noone else can see your terminal now, you can pick this\n" \
11293787Sdes	"as your password: \"%s\".  Otherwise, come back later.\n"
11393787Sdes#define MESSAGE_RANDOMFAILED \
11493787Sdes	"This system is configured to use randomly generated passwords\n" \
11593787Sdes	"only, but the attempt to generate a password has failed.  This\n" \
11693787Sdes	"could happen for a number of reasons: you could have requested\n" \
11793787Sdes	"an impossible password length, or the access to kernel random\n" \
11893787Sdes	"number pool could have failed."
11993787Sdes#define MESSAGE_TOOLONG \
12093787Sdes	"This password may be too long for some services.  Choose another."
12193787Sdes#define MESSAGE_TRUNCATED \
12293787Sdes	"Warning: your longer password will be truncated to 8 characters."
12393787Sdes#define MESSAGE_WEAKPASS \
12493787Sdes	"Weak password: %s."
12593787Sdes#define MESSAGE_NOTRANDOM \
12693787Sdes	"Sorry, you've mistyped the password that was generated for you."
12793787Sdes#define MESSAGE_MISTYPED \
12893787Sdes	"Sorry, passwords do not match."
12993787Sdes#define MESSAGE_RETRY \
13093787Sdes	"Try again."
13193787Sdes
13294691Sdesstatic int converse(pam_handle_t *pamh, int style, lo_const char *text,
13393787Sdes    struct pam_response **resp)
13493787Sdes{
135254960Swill	pam_item_t item;
136254960Swill	lo_const struct pam_conv *conv;
13793787Sdes	struct pam_message msg, *pmsg;
13893787Sdes	int status;
13993787Sdes
140254960Swill	status = pam_get_item(pamh, PAM_CONV, &item);
14193787Sdes	if (status != PAM_SUCCESS)
14293787Sdes		return status;
143254960Swill	conv = item;
14493787Sdes
14593787Sdes	pmsg = &msg;
14693787Sdes	msg.msg_style = style;
147254960Swill	msg.msg = (char *)text;
14893787Sdes
14993787Sdes	*resp = NULL;
15094691Sdes	return conv->conv(1, (lo_const struct pam_message **)&pmsg, resp,
15193787Sdes	    conv->appdata_ptr);
15293787Sdes}
15393787Sdes
15493787Sdes#ifdef __GNUC__
15593787Sdes__attribute__ ((format (printf, 3, 4)))
15693787Sdes#endif
15793787Sdesstatic int say(pam_handle_t *pamh, int style, const char *format, ...)
15893787Sdes{
15993787Sdes	va_list args;
16093787Sdes	char buffer[0x800];
16193787Sdes	int needed;
16293787Sdes	struct pam_response *resp;
16393787Sdes	int status;
16493787Sdes
16593787Sdes	va_start(args, format);
16693787Sdes	needed = vsnprintf(buffer, sizeof(buffer), format, args);
16793787Sdes	va_end(args);
16893787Sdes
16994691Sdes	if ((unsigned int)needed < sizeof(buffer)) {
17093787Sdes		status = converse(pamh, style, buffer, &resp);
17193787Sdes		_pam_overwrite(buffer);
17293787Sdes	} else {
17393787Sdes		status = PAM_ABORT;
17493787Sdes		memset(buffer, 0, sizeof(buffer));
17593787Sdes	}
17693787Sdes
17793787Sdes	return status;
17893787Sdes}
17993787Sdes
18094691Sdesstatic int check_max(params_t *params, pam_handle_t *pamh, const char *newpass)
18193787Sdes{
18294691Sdes	if ((int)strlen(newpass) > params->qc.max) {
18393787Sdes		if (params->qc.max != 8) {
18493787Sdes			say(pamh, PAM_ERROR_MSG, MESSAGE_TOOLONG);
18593787Sdes			return -1;
18693787Sdes		}
18793787Sdes		say(pamh, PAM_TEXT_INFO, MESSAGE_TRUNCATED);
18893787Sdes	}
18993787Sdes
19093787Sdes	return 0;
19193787Sdes}
19293787Sdes
19393787Sdesstatic int parse(params_t *params, pam_handle_t *pamh,
19493787Sdes    int argc, const char **argv)
19593787Sdes{
19693787Sdes	const char *p;
19794691Sdes	char *e;
19893787Sdes	int i;
19993787Sdes	unsigned long v;
20093787Sdes
20193787Sdes	while (argc) {
20293787Sdes		if (!strncmp(*argv, "min=", 4)) {
20393787Sdes			p = *argv + 4;
20493787Sdes			for (i = 0; i < 5; i++) {
20593787Sdes				if (!strncmp(p, "disabled", 8)) {
20693787Sdes					v = INT_MAX;
20793787Sdes					p += 8;
20894691Sdes				} else {
20994691Sdes					v = strtoul(p, &e, 10);
21094691Sdes					p = e;
21194691Sdes				}
21293787Sdes				if (i < 4 && *p++ != ',') break;
21393787Sdes				if (v > INT_MAX) break;
21494691Sdes				if (i && (int)v > params->qc.min[i - 1]) break;
21593787Sdes				params->qc.min[i] = v;
21693787Sdes			}
21793787Sdes			if (*p) break;
21893787Sdes		} else
21993787Sdes		if (!strncmp(*argv, "max=", 4)) {
22094691Sdes			v = strtoul(*argv + 4, &e, 10);
22194691Sdes			if (*e || v < 8 || v > INT_MAX) break;
22293787Sdes			params->qc.max = v;
22393787Sdes		} else
22493787Sdes		if (!strncmp(*argv, "passphrase=", 11)) {
22594691Sdes			v = strtoul(*argv + 11, &e, 10);
22694691Sdes			if (*e || v > INT_MAX) break;
22793787Sdes			params->qc.passphrase_words = v;
22893787Sdes		} else
22993787Sdes		if (!strncmp(*argv, "match=", 6)) {
23094691Sdes			v = strtoul(*argv + 6, &e, 10);
23194691Sdes			if (*e || v > INT_MAX) break;
23293787Sdes			params->qc.match_length = v;
23393787Sdes		} else
23493787Sdes		if (!strncmp(*argv, "similar=", 8)) {
23593787Sdes			if (!strcmp(*argv + 8, "permit"))
23693787Sdes				params->qc.similar_deny = 0;
23793787Sdes			else
23893787Sdes			if (!strcmp(*argv + 8, "deny"))
23993787Sdes				params->qc.similar_deny = 1;
24093787Sdes			else
24193787Sdes				break;
24293787Sdes		} else
24393787Sdes		if (!strncmp(*argv, "random=", 7)) {
24494691Sdes			v = strtoul(*argv + 7, &e, 10);
24594691Sdes			if (!strcmp(e, ",only")) {
24694691Sdes				e += 5;
24793787Sdes				params->qc.min[4] = INT_MAX;
24893787Sdes			}
24994691Sdes			if (*e || v > INT_MAX) break;
25093787Sdes			params->qc.random_bits = v;
25193787Sdes		} else
25293787Sdes		if (!strncmp(*argv, "enforce=", 8)) {
25393787Sdes			params->flags &= ~F_ENFORCE_MASK;
25493787Sdes			if (!strcmp(*argv + 8, "users"))
25593787Sdes				params->flags |= F_ENFORCE_USERS;
25693787Sdes			else
25793787Sdes			if (!strcmp(*argv + 8, "everyone"))
25893787Sdes				params->flags |= F_ENFORCE_EVERYONE;
25993787Sdes			else
26093787Sdes			if (strcmp(*argv + 8, "none"))
26193787Sdes				break;
26293787Sdes		} else
26393787Sdes		if (!strcmp(*argv, "non-unix")) {
26493787Sdes			if (params->flags & F_CHECK_OLDAUTHTOK) break;
26593787Sdes			params->flags |= F_NON_UNIX;
26693787Sdes		} else
26793787Sdes		if (!strncmp(*argv, "retry=", 6)) {
26894691Sdes			v = strtoul(*argv + 6, &e, 10);
26994691Sdes			if (*e || v > INT_MAX) break;
27093787Sdes			params->retry = v;
27193787Sdes		} else
27293787Sdes		if (!strncmp(*argv, "ask_oldauthtok", 14)) {
27393787Sdes			params->flags &= ~F_ASK_OLDAUTHTOK_MASK;
27493787Sdes			if (params->flags & F_USE_FIRST_PASS) break;
27593787Sdes			if (!strcmp(*argv + 14, "=update"))
27693787Sdes				params->flags |= F_ASK_OLDAUTHTOK_UPDATE;
27793787Sdes			else
27893787Sdes			if (!(*argv)[14])
27993787Sdes				params->flags |= F_ASK_OLDAUTHTOK_PRELIM;
28093787Sdes			else
28193787Sdes				break;
28293787Sdes		} else
28393787Sdes		if (!strcmp(*argv, "check_oldauthtok")) {
28493787Sdes			if (params->flags & F_NON_UNIX) break;
28593787Sdes			params->flags |= F_CHECK_OLDAUTHTOK;
28693787Sdes		} else
28793787Sdes		if (!strcmp(*argv, "use_first_pass")) {
28893787Sdes			if (params->flags & F_ASK_OLDAUTHTOK_MASK) break;
28993787Sdes			params->flags |= F_USE_FIRST_PASS | F_USE_AUTHTOK;
29093787Sdes		} else
29193787Sdes		if (!strcmp(*argv, "use_authtok")) {
29293787Sdes			params->flags |= F_USE_AUTHTOK;
29393787Sdes		} else
29493787Sdes			break;
29593787Sdes		argc--; argv++;
29693787Sdes	}
29793787Sdes
29893787Sdes	if (argc) {
299254960Swill		if (getuid() != 0) {
300254960Swill			say(pamh, PAM_ERROR_MSG, MESSAGE_MISCONFIGURED);
301254960Swill		} else {
302254960Swill			say(pamh, PAM_ERROR_MSG, MESSAGE_INVALID_OPTION, *argv);
303254960Swill		}
30493787Sdes		return PAM_ABORT;
30593787Sdes	}
30693787Sdes
30793787Sdes	return PAM_SUCCESS;
30893787Sdes}
30993787Sdes
31093787SdesPAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
31193787Sdes    int argc, const char **argv)
31293787Sdes{
31393787Sdes	params_t params;
31493787Sdes	struct pam_response *resp;
31593787Sdes	struct passwd *pw, fake_pw;
31693787Sdes#ifdef HAVE_SHADOW
31793787Sdes	struct spwd *spw;
31893787Sdes#endif
319254960Swill	pam_item_t item;
320254960Swill	lo_const char *user, *oldpass, *curpass;
321254960Swill	char *newpass, *randompass;
32294691Sdes	const char *reason;
32393787Sdes	int ask_oldauthtok;
32493787Sdes	int randomonly, enforce, retries_left, retry_wanted;
32593787Sdes	int status;
32693787Sdes
32793787Sdes	params = defaults;
32893787Sdes	status = parse(&params, pamh, argc, argv);
32993787Sdes	if (status != PAM_SUCCESS)
33093787Sdes		return status;
33193787Sdes
33293787Sdes	ask_oldauthtok = 0;
33393787Sdes	if (flags & PAM_PRELIM_CHECK) {
33493787Sdes		if (params.flags & F_ASK_OLDAUTHTOK_PRELIM)
33593787Sdes			ask_oldauthtok = 1;
33693787Sdes	} else
33793787Sdes	if (flags & PAM_UPDATE_AUTHTOK) {
33893787Sdes		if (params.flags & F_ASK_OLDAUTHTOK_UPDATE)
33993787Sdes			ask_oldauthtok = 1;
34093787Sdes	} else
34193787Sdes		return PAM_SERVICE_ERR;
34293787Sdes
34393787Sdes	if (ask_oldauthtok && getuid() != 0) {
34493787Sdes		status = converse(pamh, PAM_PROMPT_ECHO_OFF,
34593787Sdes		    PROMPT_OLDPASS, &resp);
34693787Sdes
34793787Sdes		if (status == PAM_SUCCESS) {
34893787Sdes			if (resp && resp->resp) {
34993787Sdes				status = pam_set_item(pamh,
35093787Sdes				    PAM_OLDAUTHTOK, resp->resp);
35193787Sdes				_pam_drop_reply(resp, 1);
35293787Sdes			} else
35394691Sdes				status = PAM_AUTHTOK_RECOVERY_ERR;
35493787Sdes		}
35593787Sdes
35693787Sdes		if (status != PAM_SUCCESS)
35793787Sdes			return status;
35893787Sdes	}
35993787Sdes
36093787Sdes	if (flags & PAM_PRELIM_CHECK)
36193787Sdes		return status;
36293787Sdes
363254960Swill	status = pam_get_item(pamh, PAM_USER, &item);
36493787Sdes	if (status != PAM_SUCCESS)
36593787Sdes		return status;
366254960Swill	user = item;
36793787Sdes
368254960Swill	status = pam_get_item(pamh, PAM_OLDAUTHTOK, &item);
36993787Sdes	if (status != PAM_SUCCESS)
37093787Sdes		return status;
371254960Swill	oldpass = item;
37293787Sdes
37393787Sdes	if (params.flags & F_NON_UNIX) {
37493787Sdes		pw = &fake_pw;
375254960Swill		pw->pw_name = (char *)user;
37693787Sdes		pw->pw_gecos = "";
37793787Sdes	} else {
37893787Sdes		pw = getpwnam(user);
37993787Sdes		endpwent();
38093787Sdes		if (!pw)
38193787Sdes			return PAM_USER_UNKNOWN;
38293787Sdes		if ((params.flags & F_CHECK_OLDAUTHTOK) && getuid() != 0) {
38393787Sdes			if (!oldpass)
38493787Sdes				status = PAM_AUTH_ERR;
38593787Sdes			else
38693787Sdes#ifdef HAVE_SHADOW
38793787Sdes			if (!strcmp(pw->pw_passwd, "x")) {
38893787Sdes				spw = getspnam(user);
38993787Sdes				endspent();
39093787Sdes				if (spw) {
39193787Sdes					if (strcmp(crypt(oldpass, spw->sp_pwdp),
39293787Sdes					    spw->sp_pwdp))
39393787Sdes						status = PAM_AUTH_ERR;
39493787Sdes					memset(spw->sp_pwdp, 0,
39593787Sdes					    strlen(spw->sp_pwdp));
39693787Sdes				} else
39793787Sdes					status = PAM_AUTH_ERR;
39893787Sdes			} else
39993787Sdes#endif
40093787Sdes			if (strcmp(crypt(oldpass, pw->pw_passwd),
40193787Sdes			    pw->pw_passwd))
40293787Sdes				status = PAM_AUTH_ERR;
40393787Sdes		}
40493787Sdes		memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
40593787Sdes		if (status != PAM_SUCCESS)
40693787Sdes			return status;
40793787Sdes	}
40893787Sdes
40993787Sdes	randomonly = params.qc.min[4] > params.qc.max;
41093787Sdes
41193787Sdes	if (getuid() != 0)
41293787Sdes		enforce = params.flags & F_ENFORCE_USERS;
41393787Sdes	else
41493787Sdes		enforce = params.flags & F_ENFORCE_ROOT;
41593787Sdes
41693787Sdes	if (params.flags & F_USE_AUTHTOK) {
417254960Swill		status = pam_get_item(pamh, PAM_AUTHTOK, &item);
41893787Sdes		if (status != PAM_SUCCESS)
41993787Sdes			return status;
420254960Swill		curpass = item;
421254960Swill		if (!curpass || (check_max(&params, pamh, curpass) && enforce))
42294889Sdes			return PAM_AUTHTOK_ERR;
423254960Swill		reason = _passwdqc_check(&params.qc, curpass, oldpass, pw);
42493787Sdes		if (reason) {
42593787Sdes			say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason);
42693787Sdes			if (enforce)
42794889Sdes				status = PAM_AUTHTOK_ERR;
42893787Sdes		}
42993787Sdes		return status;
43093787Sdes	}
43193787Sdes
43293787Sdes	retries_left = params.retry;
43393787Sdes
43493787Sdesretry:
43593787Sdes	retry_wanted = 0;
43693787Sdes
43793787Sdes	if (!randomonly &&
43893787Sdes	    params.qc.passphrase_words && params.qc.min[2] <= params.qc.max)
43993787Sdes		status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_BOTH);
44093787Sdes	else
44193787Sdes		status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_PASSWORD);
44293787Sdes	if (status != PAM_SUCCESS)
44393787Sdes		return status;
44493787Sdes
44593787Sdes	if (!randomonly && params.qc.min[3] <= params.qc.min[4])
44693787Sdes		status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_1,
44793787Sdes		    params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "",
44893787Sdes		    params.qc.min[3]);
44993787Sdes	else
45093787Sdes	if (!randomonly)
45193787Sdes		status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_2,
45293787Sdes		    params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "",
45393787Sdes		    params.qc.min[3],
45493787Sdes		    params.qc.min[4] == 8 || params.qc.min[4] == 11 ? "n" : "",
45593787Sdes		    params.qc.min[4]);
45693787Sdes	if (status != PAM_SUCCESS)
45793787Sdes		return status;
45893787Sdes
45993787Sdes	if (!randomonly &&
46093787Sdes	    params.qc.passphrase_words &&
46193787Sdes	    params.qc.min[2] <= params.qc.max) {
46293787Sdes		status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSPHRASE,
46393787Sdes		    params.qc.passphrase_words,
46493787Sdes		    params.qc.min[2], params.qc.max);
46593787Sdes		if (status != PAM_SUCCESS)
46693787Sdes			return status;
46793787Sdes	}
46893787Sdes
46993787Sdes	randompass = _passwdqc_random(&params.qc);
47093787Sdes	if (randompass) {
47193787Sdes		status = say(pamh, PAM_TEXT_INFO, randomonly ?
47293787Sdes		    MESSAGE_RANDOMONLY : MESSAGE_RANDOM, randompass);
47393787Sdes		if (status != PAM_SUCCESS) {
47493787Sdes			_pam_overwrite(randompass);
47593787Sdes			randompass = NULL;
47693787Sdes		}
47793787Sdes	} else
47893787Sdes	if (randomonly) {
47993787Sdes		say(pamh, PAM_ERROR_MSG, getuid() != 0 ?
48093787Sdes		    MESSAGE_MISCONFIGURED : MESSAGE_RANDOMFAILED);
48194889Sdes		return PAM_AUTHTOK_ERR;
48293787Sdes	}
48393787Sdes
48493787Sdes	status = converse(pamh, PAM_PROMPT_ECHO_OFF, PROMPT_NEWPASS1, &resp);
48593787Sdes	if (status == PAM_SUCCESS && (!resp || !resp->resp))
48694889Sdes		status = PAM_AUTHTOK_ERR;
48793787Sdes
48893787Sdes	if (status != PAM_SUCCESS) {
48993787Sdes		if (randompass) _pam_overwrite(randompass);
49093787Sdes		return status;
49193787Sdes	}
49293787Sdes
49393787Sdes	newpass = strdup(resp->resp);
49493787Sdes
49593787Sdes	_pam_drop_reply(resp, 1);
49693787Sdes
49793787Sdes	if (!newpass) {
49893787Sdes		if (randompass) _pam_overwrite(randompass);
499254960Swill		return status;
50093787Sdes	}
50193787Sdes
50293787Sdes	if (check_max(&params, pamh, newpass) && enforce) {
50394889Sdes		status = PAM_AUTHTOK_ERR;
50493787Sdes		retry_wanted = 1;
50593787Sdes	}
50693787Sdes
50793787Sdes	reason = NULL;
50893787Sdes	if (status == PAM_SUCCESS &&
50993787Sdes	    (!randompass || !strstr(newpass, randompass)) &&
51093787Sdes	    (randomonly ||
51193787Sdes	    (reason = _passwdqc_check(&params.qc, newpass, oldpass, pw)))) {
51293787Sdes		if (randomonly)
51393787Sdes			say(pamh, PAM_ERROR_MSG, MESSAGE_NOTRANDOM);
51493787Sdes		else
51593787Sdes			say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason);
51693787Sdes		if (enforce) {
51794889Sdes			status = PAM_AUTHTOK_ERR;
51893787Sdes			retry_wanted = 1;
51993787Sdes		}
52093787Sdes	}
52193787Sdes
52293787Sdes	if (status == PAM_SUCCESS)
52393787Sdes		status = converse(pamh, PAM_PROMPT_ECHO_OFF,
52493787Sdes		    PROMPT_NEWPASS2, &resp);
52593787Sdes	if (status == PAM_SUCCESS) {
52693787Sdes		if (resp && resp->resp) {
52793787Sdes			if (strcmp(newpass, resp->resp)) {
52893787Sdes				status = say(pamh,
52993787Sdes				    PAM_ERROR_MSG, MESSAGE_MISTYPED);
53093787Sdes				if (status == PAM_SUCCESS) {
53194889Sdes					status = PAM_AUTHTOK_ERR;
53293787Sdes					retry_wanted = 1;
53393787Sdes				}
53493787Sdes			}
53593787Sdes			_pam_drop_reply(resp, 1);
53693787Sdes		} else
53794889Sdes			status = PAM_AUTHTOK_ERR;
53893787Sdes	}
53993787Sdes
54093787Sdes	if (status == PAM_SUCCESS)
54193787Sdes		status = pam_set_item(pamh, PAM_AUTHTOK, newpass);
54293787Sdes
54393787Sdes	if (randompass) _pam_overwrite(randompass);
54493787Sdes	_pam_overwrite(newpass);
54593787Sdes	free(newpass);
54693787Sdes
54793787Sdes	if (retry_wanted && --retries_left > 0) {
54893787Sdes		status = say(pamh, PAM_TEXT_INFO, MESSAGE_RETRY);
54993787Sdes		if (status == PAM_SUCCESS)
55093787Sdes			goto retry;
55193787Sdes	}
55293787Sdes
55393787Sdes	return status;
55493787Sdes}
55593787Sdes
55694691Sdes#ifdef PAM_MODULE_ENTRY
55794691SdesPAM_MODULE_ENTRY("pam_passwdqc");
55894691Sdes#elif defined(PAM_STATIC)
55993787Sdesstruct pam_module _pam_passwdqc_modstruct = {
56093787Sdes	"pam_passwdqc",
56193787Sdes	NULL,
56293787Sdes	NULL,
56393787Sdes	NULL,
56493787Sdes	NULL,
56593787Sdes	NULL,
56693787Sdes	pam_sm_chauthtok
56793787Sdes};
56893787Sdes#endif
569