xref: /freebsd/contrib/pam_modules/pam_passwdqc/pam_passwdqc.c (revision 0bfd163f522701b486e066fa2e56624c02f5081a)
10b0ecb56SDag-Erling Smørgrav /*
2402783abSDag-Erling Smørgrav  * Copyright (c) 2000-2002 by Solar Designer. See LICENSE.
30b0ecb56SDag-Erling Smørgrav  */
40b0ecb56SDag-Erling Smørgrav 
5*caf6fbd8SWill Andrews #define _XOPEN_SOURCE 600
60b0ecb56SDag-Erling Smørgrav #define _XOPEN_SOURCE_EXTENDED
7*caf6fbd8SWill Andrews #define _XOPEN_VERSION 600
80b0ecb56SDag-Erling Smørgrav #include <stdio.h>
90b0ecb56SDag-Erling Smørgrav #include <stdlib.h>
100b0ecb56SDag-Erling Smørgrav #include <stdarg.h>
110b0ecb56SDag-Erling Smørgrav #include <string.h>
120b0ecb56SDag-Erling Smørgrav #include <limits.h>
130b0ecb56SDag-Erling Smørgrav #include <unistd.h>
140b0ecb56SDag-Erling Smørgrav #include <pwd.h>
150b0ecb56SDag-Erling Smørgrav #ifdef HAVE_SHADOW
160b0ecb56SDag-Erling Smørgrav #include <shadow.h>
170b0ecb56SDag-Erling Smørgrav #endif
180b0ecb56SDag-Erling Smørgrav 
190b0ecb56SDag-Erling Smørgrav #define PAM_SM_PASSWORD
200b0ecb56SDag-Erling Smørgrav #ifndef LINUX_PAM
210b0ecb56SDag-Erling Smørgrav #include <security/pam_appl.h>
220b0ecb56SDag-Erling Smørgrav #endif
230b0ecb56SDag-Erling Smørgrav #include <security/pam_modules.h>
240b0ecb56SDag-Erling Smørgrav 
250b0ecb56SDag-Erling Smørgrav #include "pam_macros.h"
260b0ecb56SDag-Erling Smørgrav 
270b0ecb56SDag-Erling Smørgrav #if !defined(PAM_EXTERN) && !defined(PAM_STATIC)
280b0ecb56SDag-Erling Smørgrav #define PAM_EXTERN			extern
290b0ecb56SDag-Erling Smørgrav #endif
300b0ecb56SDag-Erling Smørgrav 
31402783abSDag-Erling Smørgrav #if !defined(PAM_AUTHTOK_RECOVERY_ERR) && defined(PAM_AUTHTOK_RECOVER_ERR)
32402783abSDag-Erling Smørgrav #define PAM_AUTHTOK_RECOVERY_ERR	PAM_AUTHTOK_RECOVER_ERR
330b0ecb56SDag-Erling Smørgrav #endif
340b0ecb56SDag-Erling Smørgrav 
35402783abSDag-Erling Smørgrav #if defined(__sun__) && !defined(LINUX_PAM) && !defined(_OPENPAM)
36402783abSDag-Erling Smørgrav /* Sun's PAM doesn't use const here */
37402783abSDag-Erling Smørgrav #define lo_const
380b0ecb56SDag-Erling Smørgrav #else
39402783abSDag-Erling Smørgrav #define lo_const			const
400b0ecb56SDag-Erling Smørgrav #endif
41402783abSDag-Erling Smørgrav typedef lo_const void *pam_item_t;
420b0ecb56SDag-Erling Smørgrav 
430b0ecb56SDag-Erling Smørgrav #include "passwdqc.h"
440b0ecb56SDag-Erling Smørgrav 
450b0ecb56SDag-Erling Smørgrav #define F_ENFORCE_MASK			0x00000003
460b0ecb56SDag-Erling Smørgrav #define F_ENFORCE_USERS			0x00000001
470b0ecb56SDag-Erling Smørgrav #define F_ENFORCE_ROOT			0x00000002
480b0ecb56SDag-Erling Smørgrav #define F_ENFORCE_EVERYONE		F_ENFORCE_MASK
490b0ecb56SDag-Erling Smørgrav #define F_NON_UNIX			0x00000004
500b0ecb56SDag-Erling Smørgrav #define F_ASK_OLDAUTHTOK_MASK		0x00000030
510b0ecb56SDag-Erling Smørgrav #define F_ASK_OLDAUTHTOK_PRELIM		0x00000010
520b0ecb56SDag-Erling Smørgrav #define F_ASK_OLDAUTHTOK_UPDATE		0x00000020
530b0ecb56SDag-Erling Smørgrav #define F_CHECK_OLDAUTHTOK		0x00000040
540b0ecb56SDag-Erling Smørgrav #define F_USE_FIRST_PASS		0x00000100
550b0ecb56SDag-Erling Smørgrav #define F_USE_AUTHTOK			0x00000200
560b0ecb56SDag-Erling Smørgrav 
570b0ecb56SDag-Erling Smørgrav typedef struct {
580b0ecb56SDag-Erling Smørgrav 	passwdqc_params_t qc;
590b0ecb56SDag-Erling Smørgrav 	int flags;
600b0ecb56SDag-Erling Smørgrav 	int retry;
610b0ecb56SDag-Erling Smørgrav } params_t;
620b0ecb56SDag-Erling Smørgrav 
630b0ecb56SDag-Erling Smørgrav static params_t defaults = {
640b0ecb56SDag-Erling Smørgrav 	{
650b0ecb56SDag-Erling Smørgrav 		{INT_MAX, 24, 12, 8, 7},	/* min */
660b0ecb56SDag-Erling Smørgrav 		40,				/* max */
670b0ecb56SDag-Erling Smørgrav 		3,				/* passphrase_words */
680b0ecb56SDag-Erling Smørgrav 		4,				/* match_length */
690b0ecb56SDag-Erling Smørgrav 		1,				/* similar_deny */
700b0ecb56SDag-Erling Smørgrav 		42				/* random_bits */
710b0ecb56SDag-Erling Smørgrav 	},
720b0ecb56SDag-Erling Smørgrav 	F_ENFORCE_EVERYONE,			/* flags */
730b0ecb56SDag-Erling Smørgrav 	3					/* retry */
740b0ecb56SDag-Erling Smørgrav };
750b0ecb56SDag-Erling Smørgrav 
760b0ecb56SDag-Erling Smørgrav #define PROMPT_OLDPASS \
770b0ecb56SDag-Erling Smørgrav 	"Enter current password: "
780b0ecb56SDag-Erling Smørgrav #define PROMPT_NEWPASS1 \
790b0ecb56SDag-Erling Smørgrav 	"Enter new password: "
800b0ecb56SDag-Erling Smørgrav #define PROMPT_NEWPASS2 \
810b0ecb56SDag-Erling Smørgrav 	"Re-type new password: "
820b0ecb56SDag-Erling Smørgrav 
830b0ecb56SDag-Erling Smørgrav #define MESSAGE_MISCONFIGURED \
840b0ecb56SDag-Erling Smørgrav 	"System configuration error.  Please contact your administrator."
850b0ecb56SDag-Erling Smørgrav #define MESSAGE_INVALID_OPTION \
860b0ecb56SDag-Erling Smørgrav 	"pam_passwdqc: Invalid option: \"%s\"."
870b0ecb56SDag-Erling Smørgrav #define MESSAGE_INTRO_PASSWORD \
880b0ecb56SDag-Erling Smørgrav 	"\nYou can now choose the new password.\n"
890b0ecb56SDag-Erling Smørgrav #define MESSAGE_INTRO_BOTH \
900b0ecb56SDag-Erling Smørgrav 	"\nYou can now choose the new password or passphrase.\n"
910b0ecb56SDag-Erling Smørgrav #define MESSAGE_EXPLAIN_PASSWORD_1 \
920b0ecb56SDag-Erling Smørgrav 	"A valid password should be a mix of upper and lower case letters,\n" \
930b0ecb56SDag-Erling Smørgrav 	"digits and other characters.  You can use a%s %d character long\n" \
940b0ecb56SDag-Erling Smørgrav 	"password with characters from at least 3 of these 4 classes.\n" \
950b0ecb56SDag-Erling Smørgrav 	"Characters that form a common pattern are discarded by the check.\n"
960b0ecb56SDag-Erling Smørgrav #define MESSAGE_EXPLAIN_PASSWORD_2 \
970b0ecb56SDag-Erling Smørgrav 	"A valid password should be a mix of upper and lower case letters,\n" \
980b0ecb56SDag-Erling Smørgrav 	"digits and other characters.  You can use a%s %d character long\n" \
990b0ecb56SDag-Erling Smørgrav 	"password with characters from at least 3 of these 4 classes, or\n" \
1000b0ecb56SDag-Erling Smørgrav 	"a%s %d character long password containing characters from all the\n" \
1010b0ecb56SDag-Erling Smørgrav 	"classes.  Characters that form a common pattern are discarded by\n" \
1020b0ecb56SDag-Erling Smørgrav 	"the check.\n"
1030b0ecb56SDag-Erling Smørgrav #define MESSAGE_EXPLAIN_PASSPHRASE \
1040b0ecb56SDag-Erling Smørgrav 	"A passphrase should be of at least %d words, %d to %d characters\n" \
1050b0ecb56SDag-Erling Smørgrav 	"long and contain enough different characters.\n"
1060b0ecb56SDag-Erling Smørgrav #define MESSAGE_RANDOM \
1070b0ecb56SDag-Erling Smørgrav 	"Alternatively, if noone else can see your terminal now, you can\n" \
1080b0ecb56SDag-Erling Smørgrav 	"pick this as your password: \"%s\".\n"
1090b0ecb56SDag-Erling Smørgrav #define MESSAGE_RANDOMONLY \
1100b0ecb56SDag-Erling Smørgrav 	"This system is configured to permit randomly generated passwords\n" \
1110b0ecb56SDag-Erling Smørgrav 	"only.  If noone else can see your terminal now, you can pick this\n" \
1120b0ecb56SDag-Erling Smørgrav 	"as your password: \"%s\".  Otherwise, come back later.\n"
1130b0ecb56SDag-Erling Smørgrav #define MESSAGE_RANDOMFAILED \
1140b0ecb56SDag-Erling Smørgrav 	"This system is configured to use randomly generated passwords\n" \
1150b0ecb56SDag-Erling Smørgrav 	"only, but the attempt to generate a password has failed.  This\n" \
1160b0ecb56SDag-Erling Smørgrav 	"could happen for a number of reasons: you could have requested\n" \
1170b0ecb56SDag-Erling Smørgrav 	"an impossible password length, or the access to kernel random\n" \
1180b0ecb56SDag-Erling Smørgrav 	"number pool could have failed."
1190b0ecb56SDag-Erling Smørgrav #define MESSAGE_TOOLONG \
1200b0ecb56SDag-Erling Smørgrav 	"This password may be too long for some services.  Choose another."
1210b0ecb56SDag-Erling Smørgrav #define MESSAGE_TRUNCATED \
1220b0ecb56SDag-Erling Smørgrav 	"Warning: your longer password will be truncated to 8 characters."
1230b0ecb56SDag-Erling Smørgrav #define MESSAGE_WEAKPASS \
1240b0ecb56SDag-Erling Smørgrav 	"Weak password: %s."
1250b0ecb56SDag-Erling Smørgrav #define MESSAGE_NOTRANDOM \
1260b0ecb56SDag-Erling Smørgrav 	"Sorry, you've mistyped the password that was generated for you."
1270b0ecb56SDag-Erling Smørgrav #define MESSAGE_MISTYPED \
1280b0ecb56SDag-Erling Smørgrav 	"Sorry, passwords do not match."
1290b0ecb56SDag-Erling Smørgrav #define MESSAGE_RETRY \
1300b0ecb56SDag-Erling Smørgrav 	"Try again."
1310b0ecb56SDag-Erling Smørgrav 
converse(pam_handle_t * pamh,int style,lo_const char * text,struct pam_response ** resp)132402783abSDag-Erling Smørgrav static int converse(pam_handle_t *pamh, int style, lo_const char *text,
1330b0ecb56SDag-Erling Smørgrav     struct pam_response **resp)
1340b0ecb56SDag-Erling Smørgrav {
135*caf6fbd8SWill Andrews 	pam_item_t item;
136*caf6fbd8SWill Andrews 	lo_const struct pam_conv *conv;
1370b0ecb56SDag-Erling Smørgrav 	struct pam_message msg, *pmsg;
1380b0ecb56SDag-Erling Smørgrav 	int status;
1390b0ecb56SDag-Erling Smørgrav 
140*caf6fbd8SWill Andrews 	status = pam_get_item(pamh, PAM_CONV, &item);
1410b0ecb56SDag-Erling Smørgrav 	if (status != PAM_SUCCESS)
1420b0ecb56SDag-Erling Smørgrav 		return status;
143*caf6fbd8SWill Andrews 	conv = item;
1440b0ecb56SDag-Erling Smørgrav 
1450b0ecb56SDag-Erling Smørgrav 	pmsg = &msg;
1460b0ecb56SDag-Erling Smørgrav 	msg.msg_style = style;
147*caf6fbd8SWill Andrews 	msg.msg = (char *)text;
1480b0ecb56SDag-Erling Smørgrav 
1490b0ecb56SDag-Erling Smørgrav 	*resp = NULL;
150402783abSDag-Erling Smørgrav 	return conv->conv(1, (lo_const struct pam_message **)&pmsg, resp,
1510b0ecb56SDag-Erling Smørgrav 	    conv->appdata_ptr);
1520b0ecb56SDag-Erling Smørgrav }
1530b0ecb56SDag-Erling Smørgrav 
1540b0ecb56SDag-Erling Smørgrav #ifdef __GNUC__
1550b0ecb56SDag-Erling Smørgrav __attribute__ ((format (printf, 3, 4)))
1560b0ecb56SDag-Erling Smørgrav #endif
say(pam_handle_t * pamh,int style,const char * format,...)1570b0ecb56SDag-Erling Smørgrav static int say(pam_handle_t *pamh, int style, const char *format, ...)
1580b0ecb56SDag-Erling Smørgrav {
1590b0ecb56SDag-Erling Smørgrav 	va_list args;
1600b0ecb56SDag-Erling Smørgrav 	char buffer[0x800];
1610b0ecb56SDag-Erling Smørgrav 	int needed;
1620b0ecb56SDag-Erling Smørgrav 	struct pam_response *resp;
1630b0ecb56SDag-Erling Smørgrav 	int status;
1640b0ecb56SDag-Erling Smørgrav 
1650b0ecb56SDag-Erling Smørgrav 	va_start(args, format);
1660b0ecb56SDag-Erling Smørgrav 	needed = vsnprintf(buffer, sizeof(buffer), format, args);
1670b0ecb56SDag-Erling Smørgrav 	va_end(args);
1680b0ecb56SDag-Erling Smørgrav 
169402783abSDag-Erling Smørgrav 	if ((unsigned int)needed < sizeof(buffer)) {
1700b0ecb56SDag-Erling Smørgrav 		status = converse(pamh, style, buffer, &resp);
1710b0ecb56SDag-Erling Smørgrav 		_pam_overwrite(buffer);
1720b0ecb56SDag-Erling Smørgrav 	} else {
1730b0ecb56SDag-Erling Smørgrav 		status = PAM_ABORT;
1740b0ecb56SDag-Erling Smørgrav 		memset(buffer, 0, sizeof(buffer));
1750b0ecb56SDag-Erling Smørgrav 	}
1760b0ecb56SDag-Erling Smørgrav 
1770b0ecb56SDag-Erling Smørgrav 	return status;
1780b0ecb56SDag-Erling Smørgrav }
1790b0ecb56SDag-Erling Smørgrav 
check_max(params_t * params,pam_handle_t * pamh,const char * newpass)180402783abSDag-Erling Smørgrav static int check_max(params_t *params, pam_handle_t *pamh, const char *newpass)
1810b0ecb56SDag-Erling Smørgrav {
182402783abSDag-Erling Smørgrav 	if ((int)strlen(newpass) > params->qc.max) {
1830b0ecb56SDag-Erling Smørgrav 		if (params->qc.max != 8) {
1840b0ecb56SDag-Erling Smørgrav 			say(pamh, PAM_ERROR_MSG, MESSAGE_TOOLONG);
1850b0ecb56SDag-Erling Smørgrav 			return -1;
1860b0ecb56SDag-Erling Smørgrav 		}
1870b0ecb56SDag-Erling Smørgrav 		say(pamh, PAM_TEXT_INFO, MESSAGE_TRUNCATED);
1880b0ecb56SDag-Erling Smørgrav 	}
1890b0ecb56SDag-Erling Smørgrav 
1900b0ecb56SDag-Erling Smørgrav 	return 0;
1910b0ecb56SDag-Erling Smørgrav }
1920b0ecb56SDag-Erling Smørgrav 
parse(params_t * params,pam_handle_t * pamh,int argc,const char ** argv)1930b0ecb56SDag-Erling Smørgrav static int parse(params_t *params, pam_handle_t *pamh,
1940b0ecb56SDag-Erling Smørgrav     int argc, const char **argv)
1950b0ecb56SDag-Erling Smørgrav {
1960b0ecb56SDag-Erling Smørgrav 	const char *p;
197402783abSDag-Erling Smørgrav 	char *e;
1980b0ecb56SDag-Erling Smørgrav 	int i;
1990b0ecb56SDag-Erling Smørgrav 	unsigned long v;
2000b0ecb56SDag-Erling Smørgrav 
2010b0ecb56SDag-Erling Smørgrav 	while (argc) {
2020b0ecb56SDag-Erling Smørgrav 		if (!strncmp(*argv, "min=", 4)) {
2030b0ecb56SDag-Erling Smørgrav 			p = *argv + 4;
2040b0ecb56SDag-Erling Smørgrav 			for (i = 0; i < 5; i++) {
2050b0ecb56SDag-Erling Smørgrav 				if (!strncmp(p, "disabled", 8)) {
2060b0ecb56SDag-Erling Smørgrav 					v = INT_MAX;
2070b0ecb56SDag-Erling Smørgrav 					p += 8;
208402783abSDag-Erling Smørgrav 				} else {
209402783abSDag-Erling Smørgrav 					v = strtoul(p, &e, 10);
210402783abSDag-Erling Smørgrav 					p = e;
211402783abSDag-Erling Smørgrav 				}
2120b0ecb56SDag-Erling Smørgrav 				if (i < 4 && *p++ != ',') break;
2130b0ecb56SDag-Erling Smørgrav 				if (v > INT_MAX) break;
214402783abSDag-Erling Smørgrav 				if (i && (int)v > params->qc.min[i - 1]) break;
2150b0ecb56SDag-Erling Smørgrav 				params->qc.min[i] = v;
2160b0ecb56SDag-Erling Smørgrav 			}
2170b0ecb56SDag-Erling Smørgrav 			if (*p) break;
2180b0ecb56SDag-Erling Smørgrav 		} else
2190b0ecb56SDag-Erling Smørgrav 		if (!strncmp(*argv, "max=", 4)) {
220402783abSDag-Erling Smørgrav 			v = strtoul(*argv + 4, &e, 10);
221402783abSDag-Erling Smørgrav 			if (*e || v < 8 || v > INT_MAX) break;
2220b0ecb56SDag-Erling Smørgrav 			params->qc.max = v;
2230b0ecb56SDag-Erling Smørgrav 		} else
2240b0ecb56SDag-Erling Smørgrav 		if (!strncmp(*argv, "passphrase=", 11)) {
225402783abSDag-Erling Smørgrav 			v = strtoul(*argv + 11, &e, 10);
226402783abSDag-Erling Smørgrav 			if (*e || v > INT_MAX) break;
2270b0ecb56SDag-Erling Smørgrav 			params->qc.passphrase_words = v;
2280b0ecb56SDag-Erling Smørgrav 		} else
2290b0ecb56SDag-Erling Smørgrav 		if (!strncmp(*argv, "match=", 6)) {
230402783abSDag-Erling Smørgrav 			v = strtoul(*argv + 6, &e, 10);
231402783abSDag-Erling Smørgrav 			if (*e || v > INT_MAX) break;
2320b0ecb56SDag-Erling Smørgrav 			params->qc.match_length = v;
2330b0ecb56SDag-Erling Smørgrav 		} else
2340b0ecb56SDag-Erling Smørgrav 		if (!strncmp(*argv, "similar=", 8)) {
2350b0ecb56SDag-Erling Smørgrav 			if (!strcmp(*argv + 8, "permit"))
2360b0ecb56SDag-Erling Smørgrav 				params->qc.similar_deny = 0;
2370b0ecb56SDag-Erling Smørgrav 			else
2380b0ecb56SDag-Erling Smørgrav 			if (!strcmp(*argv + 8, "deny"))
2390b0ecb56SDag-Erling Smørgrav 				params->qc.similar_deny = 1;
2400b0ecb56SDag-Erling Smørgrav 			else
2410b0ecb56SDag-Erling Smørgrav 				break;
2420b0ecb56SDag-Erling Smørgrav 		} else
2430b0ecb56SDag-Erling Smørgrav 		if (!strncmp(*argv, "random=", 7)) {
244402783abSDag-Erling Smørgrav 			v = strtoul(*argv + 7, &e, 10);
245402783abSDag-Erling Smørgrav 			if (!strcmp(e, ",only")) {
246402783abSDag-Erling Smørgrav 				e += 5;
2470b0ecb56SDag-Erling Smørgrav 				params->qc.min[4] = INT_MAX;
2480b0ecb56SDag-Erling Smørgrav 			}
249402783abSDag-Erling Smørgrav 			if (*e || v > INT_MAX) break;
2500b0ecb56SDag-Erling Smørgrav 			params->qc.random_bits = v;
2510b0ecb56SDag-Erling Smørgrav 		} else
2520b0ecb56SDag-Erling Smørgrav 		if (!strncmp(*argv, "enforce=", 8)) {
2530b0ecb56SDag-Erling Smørgrav 			params->flags &= ~F_ENFORCE_MASK;
2540b0ecb56SDag-Erling Smørgrav 			if (!strcmp(*argv + 8, "users"))
2550b0ecb56SDag-Erling Smørgrav 				params->flags |= F_ENFORCE_USERS;
2560b0ecb56SDag-Erling Smørgrav 			else
2570b0ecb56SDag-Erling Smørgrav 			if (!strcmp(*argv + 8, "everyone"))
2580b0ecb56SDag-Erling Smørgrav 				params->flags |= F_ENFORCE_EVERYONE;
2590b0ecb56SDag-Erling Smørgrav 			else
2600b0ecb56SDag-Erling Smørgrav 			if (strcmp(*argv + 8, "none"))
2610b0ecb56SDag-Erling Smørgrav 				break;
2620b0ecb56SDag-Erling Smørgrav 		} else
2630b0ecb56SDag-Erling Smørgrav 		if (!strcmp(*argv, "non-unix")) {
2640b0ecb56SDag-Erling Smørgrav 			if (params->flags & F_CHECK_OLDAUTHTOK) break;
2650b0ecb56SDag-Erling Smørgrav 			params->flags |= F_NON_UNIX;
2660b0ecb56SDag-Erling Smørgrav 		} else
2670b0ecb56SDag-Erling Smørgrav 		if (!strncmp(*argv, "retry=", 6)) {
268402783abSDag-Erling Smørgrav 			v = strtoul(*argv + 6, &e, 10);
269402783abSDag-Erling Smørgrav 			if (*e || v > INT_MAX) break;
2700b0ecb56SDag-Erling Smørgrav 			params->retry = v;
2710b0ecb56SDag-Erling Smørgrav 		} else
2720b0ecb56SDag-Erling Smørgrav 		if (!strncmp(*argv, "ask_oldauthtok", 14)) {
2730b0ecb56SDag-Erling Smørgrav 			params->flags &= ~F_ASK_OLDAUTHTOK_MASK;
2740b0ecb56SDag-Erling Smørgrav 			if (params->flags & F_USE_FIRST_PASS) break;
2750b0ecb56SDag-Erling Smørgrav 			if (!strcmp(*argv + 14, "=update"))
2760b0ecb56SDag-Erling Smørgrav 				params->flags |= F_ASK_OLDAUTHTOK_UPDATE;
2770b0ecb56SDag-Erling Smørgrav 			else
2780b0ecb56SDag-Erling Smørgrav 			if (!(*argv)[14])
2790b0ecb56SDag-Erling Smørgrav 				params->flags |= F_ASK_OLDAUTHTOK_PRELIM;
2800b0ecb56SDag-Erling Smørgrav 			else
2810b0ecb56SDag-Erling Smørgrav 				break;
2820b0ecb56SDag-Erling Smørgrav 		} else
2830b0ecb56SDag-Erling Smørgrav 		if (!strcmp(*argv, "check_oldauthtok")) {
2840b0ecb56SDag-Erling Smørgrav 			if (params->flags & F_NON_UNIX) break;
2850b0ecb56SDag-Erling Smørgrav 			params->flags |= F_CHECK_OLDAUTHTOK;
2860b0ecb56SDag-Erling Smørgrav 		} else
2870b0ecb56SDag-Erling Smørgrav 		if (!strcmp(*argv, "use_first_pass")) {
2880b0ecb56SDag-Erling Smørgrav 			if (params->flags & F_ASK_OLDAUTHTOK_MASK) break;
2890b0ecb56SDag-Erling Smørgrav 			params->flags |= F_USE_FIRST_PASS | F_USE_AUTHTOK;
2900b0ecb56SDag-Erling Smørgrav 		} else
2910b0ecb56SDag-Erling Smørgrav 		if (!strcmp(*argv, "use_authtok")) {
2920b0ecb56SDag-Erling Smørgrav 			params->flags |= F_USE_AUTHTOK;
2930b0ecb56SDag-Erling Smørgrav 		} else
2940b0ecb56SDag-Erling Smørgrav 			break;
2950b0ecb56SDag-Erling Smørgrav 		argc--; argv++;
2960b0ecb56SDag-Erling Smørgrav 	}
2970b0ecb56SDag-Erling Smørgrav 
2980b0ecb56SDag-Erling Smørgrav 	if (argc) {
299*caf6fbd8SWill Andrews 		if (getuid() != 0) {
300*caf6fbd8SWill Andrews 			say(pamh, PAM_ERROR_MSG, MESSAGE_MISCONFIGURED);
301*caf6fbd8SWill Andrews 		} else {
302*caf6fbd8SWill Andrews 			say(pamh, PAM_ERROR_MSG, MESSAGE_INVALID_OPTION, *argv);
303*caf6fbd8SWill Andrews 		}
3040b0ecb56SDag-Erling Smørgrav 		return PAM_ABORT;
3050b0ecb56SDag-Erling Smørgrav 	}
3060b0ecb56SDag-Erling Smørgrav 
3070b0ecb56SDag-Erling Smørgrav 	return PAM_SUCCESS;
3080b0ecb56SDag-Erling Smørgrav }
3090b0ecb56SDag-Erling Smørgrav 
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)3100b0ecb56SDag-Erling Smørgrav PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
3110b0ecb56SDag-Erling Smørgrav     int argc, const char **argv)
3120b0ecb56SDag-Erling Smørgrav {
3130b0ecb56SDag-Erling Smørgrav 	params_t params;
3140b0ecb56SDag-Erling Smørgrav 	struct pam_response *resp;
3150b0ecb56SDag-Erling Smørgrav 	struct passwd *pw, fake_pw;
3160b0ecb56SDag-Erling Smørgrav #ifdef HAVE_SHADOW
3170b0ecb56SDag-Erling Smørgrav 	struct spwd *spw;
3180b0ecb56SDag-Erling Smørgrav #endif
319*caf6fbd8SWill Andrews 	pam_item_t item;
320*caf6fbd8SWill Andrews 	lo_const char *user, *oldpass, *curpass;
321*caf6fbd8SWill Andrews 	char *newpass, *randompass;
322402783abSDag-Erling Smørgrav 	const char *reason;
3230b0ecb56SDag-Erling Smørgrav 	int ask_oldauthtok;
3240b0ecb56SDag-Erling Smørgrav 	int randomonly, enforce, retries_left, retry_wanted;
3250b0ecb56SDag-Erling Smørgrav 	int status;
3260b0ecb56SDag-Erling Smørgrav 
3270b0ecb56SDag-Erling Smørgrav 	params = defaults;
3280b0ecb56SDag-Erling Smørgrav 	status = parse(&params, pamh, argc, argv);
3290b0ecb56SDag-Erling Smørgrav 	if (status != PAM_SUCCESS)
3300b0ecb56SDag-Erling Smørgrav 		return status;
3310b0ecb56SDag-Erling Smørgrav 
3320b0ecb56SDag-Erling Smørgrav 	ask_oldauthtok = 0;
3330b0ecb56SDag-Erling Smørgrav 	if (flags & PAM_PRELIM_CHECK) {
3340b0ecb56SDag-Erling Smørgrav 		if (params.flags & F_ASK_OLDAUTHTOK_PRELIM)
3350b0ecb56SDag-Erling Smørgrav 			ask_oldauthtok = 1;
3360b0ecb56SDag-Erling Smørgrav 	} else
3370b0ecb56SDag-Erling Smørgrav 	if (flags & PAM_UPDATE_AUTHTOK) {
3380b0ecb56SDag-Erling Smørgrav 		if (params.flags & F_ASK_OLDAUTHTOK_UPDATE)
3390b0ecb56SDag-Erling Smørgrav 			ask_oldauthtok = 1;
3400b0ecb56SDag-Erling Smørgrav 	} else
3410b0ecb56SDag-Erling Smørgrav 		return PAM_SERVICE_ERR;
3420b0ecb56SDag-Erling Smørgrav 
3430b0ecb56SDag-Erling Smørgrav 	if (ask_oldauthtok && getuid() != 0) {
3440b0ecb56SDag-Erling Smørgrav 		status = converse(pamh, PAM_PROMPT_ECHO_OFF,
3450b0ecb56SDag-Erling Smørgrav 		    PROMPT_OLDPASS, &resp);
3460b0ecb56SDag-Erling Smørgrav 
3470b0ecb56SDag-Erling Smørgrav 		if (status == PAM_SUCCESS) {
3480b0ecb56SDag-Erling Smørgrav 			if (resp && resp->resp) {
3490b0ecb56SDag-Erling Smørgrav 				status = pam_set_item(pamh,
3500b0ecb56SDag-Erling Smørgrav 				    PAM_OLDAUTHTOK, resp->resp);
3510b0ecb56SDag-Erling Smørgrav 				_pam_drop_reply(resp, 1);
3520b0ecb56SDag-Erling Smørgrav 			} else
353402783abSDag-Erling Smørgrav 				status = PAM_AUTHTOK_RECOVERY_ERR;
3540b0ecb56SDag-Erling Smørgrav 		}
3550b0ecb56SDag-Erling Smørgrav 
3560b0ecb56SDag-Erling Smørgrav 		if (status != PAM_SUCCESS)
3570b0ecb56SDag-Erling Smørgrav 			return status;
3580b0ecb56SDag-Erling Smørgrav 	}
3590b0ecb56SDag-Erling Smørgrav 
3600b0ecb56SDag-Erling Smørgrav 	if (flags & PAM_PRELIM_CHECK)
3610b0ecb56SDag-Erling Smørgrav 		return status;
3620b0ecb56SDag-Erling Smørgrav 
363*caf6fbd8SWill Andrews 	status = pam_get_item(pamh, PAM_USER, &item);
3640b0ecb56SDag-Erling Smørgrav 	if (status != PAM_SUCCESS)
3650b0ecb56SDag-Erling Smørgrav 		return status;
366*caf6fbd8SWill Andrews 	user = item;
3670b0ecb56SDag-Erling Smørgrav 
368*caf6fbd8SWill Andrews 	status = pam_get_item(pamh, PAM_OLDAUTHTOK, &item);
3690b0ecb56SDag-Erling Smørgrav 	if (status != PAM_SUCCESS)
3700b0ecb56SDag-Erling Smørgrav 		return status;
371*caf6fbd8SWill Andrews 	oldpass = item;
3720b0ecb56SDag-Erling Smørgrav 
3730b0ecb56SDag-Erling Smørgrav 	if (params.flags & F_NON_UNIX) {
3740b0ecb56SDag-Erling Smørgrav 		pw = &fake_pw;
375*caf6fbd8SWill Andrews 		pw->pw_name = (char *)user;
3760b0ecb56SDag-Erling Smørgrav 		pw->pw_gecos = "";
3770b0ecb56SDag-Erling Smørgrav 	} else {
3780b0ecb56SDag-Erling Smørgrav 		pw = getpwnam(user);
3790b0ecb56SDag-Erling Smørgrav 		endpwent();
3800b0ecb56SDag-Erling Smørgrav 		if (!pw)
3810b0ecb56SDag-Erling Smørgrav 			return PAM_USER_UNKNOWN;
3820b0ecb56SDag-Erling Smørgrav 		if ((params.flags & F_CHECK_OLDAUTHTOK) && getuid() != 0) {
3830b0ecb56SDag-Erling Smørgrav 			if (!oldpass)
3840b0ecb56SDag-Erling Smørgrav 				status = PAM_AUTH_ERR;
3850b0ecb56SDag-Erling Smørgrav 			else
3860b0ecb56SDag-Erling Smørgrav #ifdef HAVE_SHADOW
3870b0ecb56SDag-Erling Smørgrav 			if (!strcmp(pw->pw_passwd, "x")) {
3880b0ecb56SDag-Erling Smørgrav 				spw = getspnam(user);
3890b0ecb56SDag-Erling Smørgrav 				endspent();
3900b0ecb56SDag-Erling Smørgrav 				if (spw) {
3910b0ecb56SDag-Erling Smørgrav 					if (strcmp(crypt(oldpass, spw->sp_pwdp),
3920b0ecb56SDag-Erling Smørgrav 					    spw->sp_pwdp))
3930b0ecb56SDag-Erling Smørgrav 						status = PAM_AUTH_ERR;
3940b0ecb56SDag-Erling Smørgrav 					memset(spw->sp_pwdp, 0,
3950b0ecb56SDag-Erling Smørgrav 					    strlen(spw->sp_pwdp));
3960b0ecb56SDag-Erling Smørgrav 				} else
3970b0ecb56SDag-Erling Smørgrav 					status = PAM_AUTH_ERR;
3980b0ecb56SDag-Erling Smørgrav 			} else
3990b0ecb56SDag-Erling Smørgrav #endif
4000b0ecb56SDag-Erling Smørgrav 			if (strcmp(crypt(oldpass, pw->pw_passwd),
4010b0ecb56SDag-Erling Smørgrav 			    pw->pw_passwd))
4020b0ecb56SDag-Erling Smørgrav 				status = PAM_AUTH_ERR;
4030b0ecb56SDag-Erling Smørgrav 		}
4040b0ecb56SDag-Erling Smørgrav 		memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
4050b0ecb56SDag-Erling Smørgrav 		if (status != PAM_SUCCESS)
4060b0ecb56SDag-Erling Smørgrav 			return status;
4070b0ecb56SDag-Erling Smørgrav 	}
4080b0ecb56SDag-Erling Smørgrav 
4090b0ecb56SDag-Erling Smørgrav 	randomonly = params.qc.min[4] > params.qc.max;
4100b0ecb56SDag-Erling Smørgrav 
4110b0ecb56SDag-Erling Smørgrav 	if (getuid() != 0)
4120b0ecb56SDag-Erling Smørgrav 		enforce = params.flags & F_ENFORCE_USERS;
4130b0ecb56SDag-Erling Smørgrav 	else
4140b0ecb56SDag-Erling Smørgrav 		enforce = params.flags & F_ENFORCE_ROOT;
4150b0ecb56SDag-Erling Smørgrav 
4160b0ecb56SDag-Erling Smørgrav 	if (params.flags & F_USE_AUTHTOK) {
417*caf6fbd8SWill Andrews 		status = pam_get_item(pamh, PAM_AUTHTOK, &item);
4180b0ecb56SDag-Erling Smørgrav 		if (status != PAM_SUCCESS)
4190b0ecb56SDag-Erling Smørgrav 			return status;
420*caf6fbd8SWill Andrews 		curpass = item;
421*caf6fbd8SWill Andrews 		if (!curpass || (check_max(&params, pamh, curpass) && enforce))
42200d65bdcSDag-Erling Smørgrav 			return PAM_AUTHTOK_ERR;
423*caf6fbd8SWill Andrews 		reason = _passwdqc_check(&params.qc, curpass, oldpass, pw);
4240b0ecb56SDag-Erling Smørgrav 		if (reason) {
4250b0ecb56SDag-Erling Smørgrav 			say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason);
4260b0ecb56SDag-Erling Smørgrav 			if (enforce)
42700d65bdcSDag-Erling Smørgrav 				status = PAM_AUTHTOK_ERR;
4280b0ecb56SDag-Erling Smørgrav 		}
4290b0ecb56SDag-Erling Smørgrav 		return status;
4300b0ecb56SDag-Erling Smørgrav 	}
4310b0ecb56SDag-Erling Smørgrav 
4320b0ecb56SDag-Erling Smørgrav 	retries_left = params.retry;
4330b0ecb56SDag-Erling Smørgrav 
4340b0ecb56SDag-Erling Smørgrav retry:
4350b0ecb56SDag-Erling Smørgrav 	retry_wanted = 0;
4360b0ecb56SDag-Erling Smørgrav 
4370b0ecb56SDag-Erling Smørgrav 	if (!randomonly &&
4380b0ecb56SDag-Erling Smørgrav 	    params.qc.passphrase_words && params.qc.min[2] <= params.qc.max)
4390b0ecb56SDag-Erling Smørgrav 		status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_BOTH);
4400b0ecb56SDag-Erling Smørgrav 	else
4410b0ecb56SDag-Erling Smørgrav 		status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_PASSWORD);
4420b0ecb56SDag-Erling Smørgrav 	if (status != PAM_SUCCESS)
4430b0ecb56SDag-Erling Smørgrav 		return status;
4440b0ecb56SDag-Erling Smørgrav 
4450b0ecb56SDag-Erling Smørgrav 	if (!randomonly && params.qc.min[3] <= params.qc.min[4])
4460b0ecb56SDag-Erling Smørgrav 		status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_1,
4470b0ecb56SDag-Erling Smørgrav 		    params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "",
4480b0ecb56SDag-Erling Smørgrav 		    params.qc.min[3]);
4490b0ecb56SDag-Erling Smørgrav 	else
4500b0ecb56SDag-Erling Smørgrav 	if (!randomonly)
4510b0ecb56SDag-Erling Smørgrav 		status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_2,
4520b0ecb56SDag-Erling Smørgrav 		    params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "",
4530b0ecb56SDag-Erling Smørgrav 		    params.qc.min[3],
4540b0ecb56SDag-Erling Smørgrav 		    params.qc.min[4] == 8 || params.qc.min[4] == 11 ? "n" : "",
4550b0ecb56SDag-Erling Smørgrav 		    params.qc.min[4]);
4560b0ecb56SDag-Erling Smørgrav 	if (status != PAM_SUCCESS)
4570b0ecb56SDag-Erling Smørgrav 		return status;
4580b0ecb56SDag-Erling Smørgrav 
4590b0ecb56SDag-Erling Smørgrav 	if (!randomonly &&
4600b0ecb56SDag-Erling Smørgrav 	    params.qc.passphrase_words &&
4610b0ecb56SDag-Erling Smørgrav 	    params.qc.min[2] <= params.qc.max) {
4620b0ecb56SDag-Erling Smørgrav 		status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSPHRASE,
4630b0ecb56SDag-Erling Smørgrav 		    params.qc.passphrase_words,
4640b0ecb56SDag-Erling Smørgrav 		    params.qc.min[2], params.qc.max);
4650b0ecb56SDag-Erling Smørgrav 		if (status != PAM_SUCCESS)
4660b0ecb56SDag-Erling Smørgrav 			return status;
4670b0ecb56SDag-Erling Smørgrav 	}
4680b0ecb56SDag-Erling Smørgrav 
4690b0ecb56SDag-Erling Smørgrav 	randompass = _passwdqc_random(&params.qc);
4700b0ecb56SDag-Erling Smørgrav 	if (randompass) {
4710b0ecb56SDag-Erling Smørgrav 		status = say(pamh, PAM_TEXT_INFO, randomonly ?
4720b0ecb56SDag-Erling Smørgrav 		    MESSAGE_RANDOMONLY : MESSAGE_RANDOM, randompass);
4730b0ecb56SDag-Erling Smørgrav 		if (status != PAM_SUCCESS) {
4740b0ecb56SDag-Erling Smørgrav 			_pam_overwrite(randompass);
4750b0ecb56SDag-Erling Smørgrav 			randompass = NULL;
4760b0ecb56SDag-Erling Smørgrav 		}
4770b0ecb56SDag-Erling Smørgrav 	} else
4780b0ecb56SDag-Erling Smørgrav 	if (randomonly) {
4790b0ecb56SDag-Erling Smørgrav 		say(pamh, PAM_ERROR_MSG, getuid() != 0 ?
4800b0ecb56SDag-Erling Smørgrav 		    MESSAGE_MISCONFIGURED : MESSAGE_RANDOMFAILED);
48100d65bdcSDag-Erling Smørgrav 		return PAM_AUTHTOK_ERR;
4820b0ecb56SDag-Erling Smørgrav 	}
4830b0ecb56SDag-Erling Smørgrav 
4840b0ecb56SDag-Erling Smørgrav 	status = converse(pamh, PAM_PROMPT_ECHO_OFF, PROMPT_NEWPASS1, &resp);
4850b0ecb56SDag-Erling Smørgrav 	if (status == PAM_SUCCESS && (!resp || !resp->resp))
48600d65bdcSDag-Erling Smørgrav 		status = PAM_AUTHTOK_ERR;
4870b0ecb56SDag-Erling Smørgrav 
4880b0ecb56SDag-Erling Smørgrav 	if (status != PAM_SUCCESS) {
4890b0ecb56SDag-Erling Smørgrav 		if (randompass) _pam_overwrite(randompass);
4900b0ecb56SDag-Erling Smørgrav 		return status;
4910b0ecb56SDag-Erling Smørgrav 	}
4920b0ecb56SDag-Erling Smørgrav 
4930b0ecb56SDag-Erling Smørgrav 	newpass = strdup(resp->resp);
4940b0ecb56SDag-Erling Smørgrav 
4950b0ecb56SDag-Erling Smørgrav 	_pam_drop_reply(resp, 1);
4960b0ecb56SDag-Erling Smørgrav 
4970b0ecb56SDag-Erling Smørgrav 	if (!newpass) {
4980b0ecb56SDag-Erling Smørgrav 		if (randompass) _pam_overwrite(randompass);
499*caf6fbd8SWill Andrews 		return status;
5000b0ecb56SDag-Erling Smørgrav 	}
5010b0ecb56SDag-Erling Smørgrav 
5020b0ecb56SDag-Erling Smørgrav 	if (check_max(&params, pamh, newpass) && enforce) {
50300d65bdcSDag-Erling Smørgrav 		status = PAM_AUTHTOK_ERR;
5040b0ecb56SDag-Erling Smørgrav 		retry_wanted = 1;
5050b0ecb56SDag-Erling Smørgrav 	}
5060b0ecb56SDag-Erling Smørgrav 
5070b0ecb56SDag-Erling Smørgrav 	reason = NULL;
5080b0ecb56SDag-Erling Smørgrav 	if (status == PAM_SUCCESS &&
5090b0ecb56SDag-Erling Smørgrav 	    (!randompass || !strstr(newpass, randompass)) &&
5100b0ecb56SDag-Erling Smørgrav 	    (randomonly ||
5110b0ecb56SDag-Erling Smørgrav 	    (reason = _passwdqc_check(&params.qc, newpass, oldpass, pw)))) {
5120b0ecb56SDag-Erling Smørgrav 		if (randomonly)
5130b0ecb56SDag-Erling Smørgrav 			say(pamh, PAM_ERROR_MSG, MESSAGE_NOTRANDOM);
5140b0ecb56SDag-Erling Smørgrav 		else
5150b0ecb56SDag-Erling Smørgrav 			say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason);
5160b0ecb56SDag-Erling Smørgrav 		if (enforce) {
51700d65bdcSDag-Erling Smørgrav 			status = PAM_AUTHTOK_ERR;
5180b0ecb56SDag-Erling Smørgrav 			retry_wanted = 1;
5190b0ecb56SDag-Erling Smørgrav 		}
5200b0ecb56SDag-Erling Smørgrav 	}
5210b0ecb56SDag-Erling Smørgrav 
5220b0ecb56SDag-Erling Smørgrav 	if (status == PAM_SUCCESS)
5230b0ecb56SDag-Erling Smørgrav 		status = converse(pamh, PAM_PROMPT_ECHO_OFF,
5240b0ecb56SDag-Erling Smørgrav 		    PROMPT_NEWPASS2, &resp);
5250b0ecb56SDag-Erling Smørgrav 	if (status == PAM_SUCCESS) {
5260b0ecb56SDag-Erling Smørgrav 		if (resp && resp->resp) {
5270b0ecb56SDag-Erling Smørgrav 			if (strcmp(newpass, resp->resp)) {
5280b0ecb56SDag-Erling Smørgrav 				status = say(pamh,
5290b0ecb56SDag-Erling Smørgrav 				    PAM_ERROR_MSG, MESSAGE_MISTYPED);
5300b0ecb56SDag-Erling Smørgrav 				if (status == PAM_SUCCESS) {
53100d65bdcSDag-Erling Smørgrav 					status = PAM_AUTHTOK_ERR;
5320b0ecb56SDag-Erling Smørgrav 					retry_wanted = 1;
5330b0ecb56SDag-Erling Smørgrav 				}
5340b0ecb56SDag-Erling Smørgrav 			}
5350b0ecb56SDag-Erling Smørgrav 			_pam_drop_reply(resp, 1);
5360b0ecb56SDag-Erling Smørgrav 		} else
53700d65bdcSDag-Erling Smørgrav 			status = PAM_AUTHTOK_ERR;
5380b0ecb56SDag-Erling Smørgrav 	}
5390b0ecb56SDag-Erling Smørgrav 
5400b0ecb56SDag-Erling Smørgrav 	if (status == PAM_SUCCESS)
5410b0ecb56SDag-Erling Smørgrav 		status = pam_set_item(pamh, PAM_AUTHTOK, newpass);
5420b0ecb56SDag-Erling Smørgrav 
5430b0ecb56SDag-Erling Smørgrav 	if (randompass) _pam_overwrite(randompass);
5440b0ecb56SDag-Erling Smørgrav 	_pam_overwrite(newpass);
5450b0ecb56SDag-Erling Smørgrav 	free(newpass);
5460b0ecb56SDag-Erling Smørgrav 
5470b0ecb56SDag-Erling Smørgrav 	if (retry_wanted && --retries_left > 0) {
5480b0ecb56SDag-Erling Smørgrav 		status = say(pamh, PAM_TEXT_INFO, MESSAGE_RETRY);
5490b0ecb56SDag-Erling Smørgrav 		if (status == PAM_SUCCESS)
5500b0ecb56SDag-Erling Smørgrav 			goto retry;
5510b0ecb56SDag-Erling Smørgrav 	}
5520b0ecb56SDag-Erling Smørgrav 
5530b0ecb56SDag-Erling Smørgrav 	return status;
5540b0ecb56SDag-Erling Smørgrav }
5550b0ecb56SDag-Erling Smørgrav 
556402783abSDag-Erling Smørgrav #ifdef PAM_MODULE_ENTRY
557402783abSDag-Erling Smørgrav PAM_MODULE_ENTRY("pam_passwdqc");
558402783abSDag-Erling Smørgrav #elif defined(PAM_STATIC)
5590b0ecb56SDag-Erling Smørgrav struct pam_module _pam_passwdqc_modstruct = {
5600b0ecb56SDag-Erling Smørgrav 	"pam_passwdqc",
5610b0ecb56SDag-Erling Smørgrav 	NULL,
5620b0ecb56SDag-Erling Smørgrav 	NULL,
5630b0ecb56SDag-Erling Smørgrav 	NULL,
5640b0ecb56SDag-Erling Smørgrav 	NULL,
5650b0ecb56SDag-Erling Smørgrav 	NULL,
5660b0ecb56SDag-Erling Smørgrav 	pam_sm_chauthtok
5670b0ecb56SDag-Erling Smørgrav };
5680b0ecb56SDag-Erling Smørgrav #endif
569