xref: /freebsd/contrib/pam_modules/pam_passwdqc/passwdqc_check.c (revision 402783abd11a9f734cc08c6d9120347f316bdb3c)
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 
50b0ecb56SDag-Erling Smørgrav #include <stdlib.h>
60b0ecb56SDag-Erling Smørgrav #include <string.h>
70b0ecb56SDag-Erling Smørgrav #include <ctype.h>
80b0ecb56SDag-Erling Smørgrav #include <pwd.h>
90b0ecb56SDag-Erling Smørgrav 
100b0ecb56SDag-Erling Smørgrav #include "passwdqc.h"
110b0ecb56SDag-Erling Smørgrav 
120b0ecb56SDag-Erling Smørgrav #define REASON_ERROR \
130b0ecb56SDag-Erling Smørgrav 	"check failed"
140b0ecb56SDag-Erling Smørgrav 
150b0ecb56SDag-Erling Smørgrav #define REASON_SAME \
160b0ecb56SDag-Erling Smørgrav 	"is the same as the old one"
170b0ecb56SDag-Erling Smørgrav #define REASON_SIMILAR \
180b0ecb56SDag-Erling Smørgrav 	"is based on the old one"
190b0ecb56SDag-Erling Smørgrav 
200b0ecb56SDag-Erling Smørgrav #define REASON_SHORT \
210b0ecb56SDag-Erling Smørgrav 	"too short"
220b0ecb56SDag-Erling Smørgrav #define REASON_LONG \
230b0ecb56SDag-Erling Smørgrav 	"too long"
240b0ecb56SDag-Erling Smørgrav 
250b0ecb56SDag-Erling Smørgrav #define REASON_SIMPLESHORT \
260b0ecb56SDag-Erling Smørgrav 	"not enough different characters or classes for this length"
270b0ecb56SDag-Erling Smørgrav #define REASON_SIMPLE \
280b0ecb56SDag-Erling Smørgrav 	"not enough different characters or classes"
290b0ecb56SDag-Erling Smørgrav 
300b0ecb56SDag-Erling Smørgrav #define REASON_PERSONAL \
310b0ecb56SDag-Erling Smørgrav 	"based on personal login information"
320b0ecb56SDag-Erling Smørgrav 
330b0ecb56SDag-Erling Smørgrav #define REASON_WORD \
340b0ecb56SDag-Erling Smørgrav 	"based on a dictionary word and not a passphrase"
350b0ecb56SDag-Erling Smørgrav 
360b0ecb56SDag-Erling Smørgrav #define FIXED_BITS			15
370b0ecb56SDag-Erling Smørgrav 
380b0ecb56SDag-Erling Smørgrav typedef unsigned long fixed;
390b0ecb56SDag-Erling Smørgrav 
400b0ecb56SDag-Erling Smørgrav /*
410b0ecb56SDag-Erling Smørgrav  * Calculates the expected number of different characters for a random
420b0ecb56SDag-Erling Smørgrav  * password of a given length. The result is rounded down. We use this
430b0ecb56SDag-Erling Smørgrav  * with the _requested_ minimum length (so longer passwords don't have
440b0ecb56SDag-Erling Smørgrav  * to meet this strict requirement for their length).
450b0ecb56SDag-Erling Smørgrav  */
expected_different(int charset,int length)460b0ecb56SDag-Erling Smørgrav static int expected_different(int charset, int length)
470b0ecb56SDag-Erling Smørgrav {
480b0ecb56SDag-Erling Smørgrav 	fixed x, y, z;
490b0ecb56SDag-Erling Smørgrav 
500b0ecb56SDag-Erling Smørgrav 	x = ((fixed)(charset - 1) << FIXED_BITS) / charset;
510b0ecb56SDag-Erling Smørgrav 	y = x;
520b0ecb56SDag-Erling Smørgrav 	while (--length > 0) y = (y * x) >> FIXED_BITS;
530b0ecb56SDag-Erling Smørgrav 	z = (fixed)charset * (((fixed)1 << FIXED_BITS) - y);
540b0ecb56SDag-Erling Smørgrav 
550b0ecb56SDag-Erling Smørgrav 	return (int)(z >> FIXED_BITS);
560b0ecb56SDag-Erling Smørgrav }
570b0ecb56SDag-Erling Smørgrav 
580b0ecb56SDag-Erling Smørgrav /*
590b0ecb56SDag-Erling Smørgrav  * A password is too simple if it is too short for its class, or doesn't
600b0ecb56SDag-Erling Smørgrav  * contain enough different characters for its class, or doesn't contain
610b0ecb56SDag-Erling Smørgrav  * enough words for a passphrase.
620b0ecb56SDag-Erling Smørgrav  */
is_simple(passwdqc_params_t * params,const char * newpass)63402783abSDag-Erling Smørgrav static int is_simple(passwdqc_params_t *params, const char *newpass)
640b0ecb56SDag-Erling Smørgrav {
650b0ecb56SDag-Erling Smørgrav 	int length, classes, words, chars;
660b0ecb56SDag-Erling Smørgrav 	int digits, lowers, uppers, others, unknowns;
670b0ecb56SDag-Erling Smørgrav 	int c, p;
680b0ecb56SDag-Erling Smørgrav 
690b0ecb56SDag-Erling Smørgrav 	length = classes = words = chars = 0;
700b0ecb56SDag-Erling Smørgrav 	digits = lowers = uppers = others = unknowns = 0;
710b0ecb56SDag-Erling Smørgrav 	p = ' ';
720b0ecb56SDag-Erling Smørgrav 	while ((c = (unsigned char)newpass[length])) {
730b0ecb56SDag-Erling Smørgrav 		length++;
740b0ecb56SDag-Erling Smørgrav 
750b0ecb56SDag-Erling Smørgrav 		if (!isascii(c)) unknowns++; else
760b0ecb56SDag-Erling Smørgrav 		if (isdigit(c)) digits++; else
770b0ecb56SDag-Erling Smørgrav 		if (islower(c)) lowers++; else
780b0ecb56SDag-Erling Smørgrav 		if (isupper(c)) uppers++; else
790b0ecb56SDag-Erling Smørgrav 			others++;
800b0ecb56SDag-Erling Smørgrav 
810b0ecb56SDag-Erling Smørgrav 		if (isascii(c) && isalpha(c) && isascii(p) && !isalpha(p))
820b0ecb56SDag-Erling Smørgrav 			words++;
830b0ecb56SDag-Erling Smørgrav 		p = c;
840b0ecb56SDag-Erling Smørgrav 
850b0ecb56SDag-Erling Smørgrav 		if (!strchr(&newpass[length], c))
860b0ecb56SDag-Erling Smørgrav 			chars++;
870b0ecb56SDag-Erling Smørgrav 	}
880b0ecb56SDag-Erling Smørgrav 
890b0ecb56SDag-Erling Smørgrav 	if (!length) return 1;
900b0ecb56SDag-Erling Smørgrav 
910b0ecb56SDag-Erling Smørgrav /* Upper case characters and digits used in common ways don't increase the
920b0ecb56SDag-Erling Smørgrav  * strength of a password */
930b0ecb56SDag-Erling Smørgrav 	c = (unsigned char)newpass[0];
940b0ecb56SDag-Erling Smørgrav 	if (uppers && isascii(c) && isupper(c)) uppers--;
950b0ecb56SDag-Erling Smørgrav 	c = (unsigned char)newpass[length - 1];
960b0ecb56SDag-Erling Smørgrav 	if (digits && isascii(c) && isdigit(c)) digits--;
970b0ecb56SDag-Erling Smørgrav 
980b0ecb56SDag-Erling Smørgrav /* Count the number of different character classes we've seen. We assume
990b0ecb56SDag-Erling Smørgrav  * that there're no non-ASCII characters for digits. */
1000b0ecb56SDag-Erling Smørgrav 	classes = 0;
1010b0ecb56SDag-Erling Smørgrav 	if (digits) classes++;
1020b0ecb56SDag-Erling Smørgrav 	if (lowers) classes++;
1030b0ecb56SDag-Erling Smørgrav 	if (uppers) classes++;
1040b0ecb56SDag-Erling Smørgrav 	if (others) classes++;
1050b0ecb56SDag-Erling Smørgrav 	if (unknowns && (!classes || (digits && classes == 1))) classes++;
1060b0ecb56SDag-Erling Smørgrav 
1070b0ecb56SDag-Erling Smørgrav 	for (; classes > 0; classes--)
1080b0ecb56SDag-Erling Smørgrav 	switch (classes) {
1090b0ecb56SDag-Erling Smørgrav 	case 1:
1100b0ecb56SDag-Erling Smørgrav 		if (length >= params->min[0] &&
1110b0ecb56SDag-Erling Smørgrav 		    chars >= expected_different(10, params->min[0]) - 1)
1120b0ecb56SDag-Erling Smørgrav 			return 0;
1130b0ecb56SDag-Erling Smørgrav 		return 1;
1140b0ecb56SDag-Erling Smørgrav 
1150b0ecb56SDag-Erling Smørgrav 	case 2:
1160b0ecb56SDag-Erling Smørgrav 		if (length >= params->min[1] &&
1170b0ecb56SDag-Erling Smørgrav 		    chars >= expected_different(36, params->min[1]) - 1)
1180b0ecb56SDag-Erling Smørgrav 			return 0;
1190b0ecb56SDag-Erling Smørgrav 		if (!params->passphrase_words ||
1200b0ecb56SDag-Erling Smørgrav 		    words < params->passphrase_words)
1210b0ecb56SDag-Erling Smørgrav 			continue;
1220b0ecb56SDag-Erling Smørgrav 		if (length >= params->min[2] &&
1230b0ecb56SDag-Erling Smørgrav 		    chars >= expected_different(27, params->min[2]) - 1)
1240b0ecb56SDag-Erling Smørgrav 			return 0;
1250b0ecb56SDag-Erling Smørgrav 		continue;
1260b0ecb56SDag-Erling Smørgrav 
1270b0ecb56SDag-Erling Smørgrav 	case 3:
1280b0ecb56SDag-Erling Smørgrav 		if (length >= params->min[3] &&
1290b0ecb56SDag-Erling Smørgrav 		    chars >= expected_different(62, params->min[3]) - 1)
1300b0ecb56SDag-Erling Smørgrav 			return 0;
1310b0ecb56SDag-Erling Smørgrav 		continue;
1320b0ecb56SDag-Erling Smørgrav 
1330b0ecb56SDag-Erling Smørgrav 	case 4:
1340b0ecb56SDag-Erling Smørgrav 		if (length >= params->min[4] &&
1350b0ecb56SDag-Erling Smørgrav 		    chars >= expected_different(95, params->min[4]) - 1)
1360b0ecb56SDag-Erling Smørgrav 			return 0;
1370b0ecb56SDag-Erling Smørgrav 		continue;
1380b0ecb56SDag-Erling Smørgrav 	}
1390b0ecb56SDag-Erling Smørgrav 
1400b0ecb56SDag-Erling Smørgrav 	return 1;
1410b0ecb56SDag-Erling Smørgrav }
1420b0ecb56SDag-Erling Smørgrav 
unify(const char * src)143402783abSDag-Erling Smørgrav static char *unify(const char *src)
1440b0ecb56SDag-Erling Smørgrav {
145402783abSDag-Erling Smørgrav 	const char *sptr;
146402783abSDag-Erling Smørgrav 	char *dst, *dptr;
1470b0ecb56SDag-Erling Smørgrav 	int c;
1480b0ecb56SDag-Erling Smørgrav 
1490b0ecb56SDag-Erling Smørgrav 	if (!(dst = malloc(strlen(src) + 1)))
1500b0ecb56SDag-Erling Smørgrav 		return NULL;
1510b0ecb56SDag-Erling Smørgrav 
1520b0ecb56SDag-Erling Smørgrav 	sptr = src;
1530b0ecb56SDag-Erling Smørgrav 	dptr = dst;
1540b0ecb56SDag-Erling Smørgrav 	do {
1550b0ecb56SDag-Erling Smørgrav 		c = (unsigned char)*sptr;
1560b0ecb56SDag-Erling Smørgrav 		if (isascii(c) && isupper(c))
1570b0ecb56SDag-Erling Smørgrav 			*dptr++ = tolower(c);
1580b0ecb56SDag-Erling Smørgrav 		else
1590b0ecb56SDag-Erling Smørgrav 			*dptr++ = *sptr;
1600b0ecb56SDag-Erling Smørgrav 	} while (*sptr++);
1610b0ecb56SDag-Erling Smørgrav 
1620b0ecb56SDag-Erling Smørgrav 	return dst;
1630b0ecb56SDag-Erling Smørgrav }
1640b0ecb56SDag-Erling Smørgrav 
reverse(const char * src)165402783abSDag-Erling Smørgrav static char *reverse(const char *src)
1660b0ecb56SDag-Erling Smørgrav {
167402783abSDag-Erling Smørgrav 	const char *sptr;
168402783abSDag-Erling Smørgrav 	char *dst, *dptr;
1690b0ecb56SDag-Erling Smørgrav 
1700b0ecb56SDag-Erling Smørgrav 	if (!(dst = malloc(strlen(src) + 1)))
1710b0ecb56SDag-Erling Smørgrav 		return NULL;
1720b0ecb56SDag-Erling Smørgrav 
1730b0ecb56SDag-Erling Smørgrav 	sptr = &src[strlen(src)];
1740b0ecb56SDag-Erling Smørgrav 	dptr = dst;
1750b0ecb56SDag-Erling Smørgrav 	while (sptr > src)
1760b0ecb56SDag-Erling Smørgrav 		*dptr++ = *--sptr;
1770b0ecb56SDag-Erling Smørgrav 	*dptr = '\0';
1780b0ecb56SDag-Erling Smørgrav 
1790b0ecb56SDag-Erling Smørgrav 	return dst;
1800b0ecb56SDag-Erling Smørgrav }
1810b0ecb56SDag-Erling Smørgrav 
clean(char * dst)1820b0ecb56SDag-Erling Smørgrav static void clean(char *dst)
1830b0ecb56SDag-Erling Smørgrav {
1840b0ecb56SDag-Erling Smørgrav 	if (dst) {
1850b0ecb56SDag-Erling Smørgrav 		memset(dst, 0, strlen(dst));
1860b0ecb56SDag-Erling Smørgrav 		free(dst);
1870b0ecb56SDag-Erling Smørgrav 	}
1880b0ecb56SDag-Erling Smørgrav }
1890b0ecb56SDag-Erling Smørgrav 
1900b0ecb56SDag-Erling Smørgrav /*
1910b0ecb56SDag-Erling Smørgrav  * Needle is based on haystack if both contain a long enough common
1920b0ecb56SDag-Erling Smørgrav  * substring and needle would be too simple for a password with the
1930b0ecb56SDag-Erling Smørgrav  * substring removed.
1940b0ecb56SDag-Erling Smørgrav  */
is_based(passwdqc_params_t * params,const char * haystack,const char * needle,const char * original)1950b0ecb56SDag-Erling Smørgrav static int is_based(passwdqc_params_t *params,
196402783abSDag-Erling Smørgrav     const char *haystack, const char *needle, const char *original)
1970b0ecb56SDag-Erling Smørgrav {
1980b0ecb56SDag-Erling Smørgrav 	char *scratch;
1990b0ecb56SDag-Erling Smørgrav 	int length;
2000b0ecb56SDag-Erling Smørgrav 	int i, j;
201402783abSDag-Erling Smørgrav 	const char *p;
2020b0ecb56SDag-Erling Smørgrav 	int match;
2030b0ecb56SDag-Erling Smørgrav 
2040b0ecb56SDag-Erling Smørgrav 	if (!params->match_length)	/* disabled */
2050b0ecb56SDag-Erling Smørgrav 		return 0;
2060b0ecb56SDag-Erling Smørgrav 
2070b0ecb56SDag-Erling Smørgrav 	if (params->match_length < 0)	/* misconfigured */
2080b0ecb56SDag-Erling Smørgrav 		return 1;
2090b0ecb56SDag-Erling Smørgrav 
2100b0ecb56SDag-Erling Smørgrav 	if (strstr(haystack, needle))	/* based on haystack entirely */
2110b0ecb56SDag-Erling Smørgrav 		return 1;
2120b0ecb56SDag-Erling Smørgrav 
2130b0ecb56SDag-Erling Smørgrav 	scratch = NULL;
2140b0ecb56SDag-Erling Smørgrav 
2150b0ecb56SDag-Erling Smørgrav 	length = strlen(needle);
2160b0ecb56SDag-Erling Smørgrav 	for (i = 0; i <= length - params->match_length; i++)
2170b0ecb56SDag-Erling Smørgrav 	for (j = params->match_length; i + j <= length; j++) {
2180b0ecb56SDag-Erling Smørgrav 		match = 0;
2190b0ecb56SDag-Erling Smørgrav 		for (p = haystack; *p; p++)
2200b0ecb56SDag-Erling Smørgrav 		if (*p == needle[i] && !strncmp(p, &needle[i], j)) {
2210b0ecb56SDag-Erling Smørgrav 			match = 1;
2220b0ecb56SDag-Erling Smørgrav 			if (!scratch) {
2230b0ecb56SDag-Erling Smørgrav 				if (!(scratch = malloc(length + 1)))
2240b0ecb56SDag-Erling Smørgrav 					return 1;
2250b0ecb56SDag-Erling Smørgrav 			}
2260b0ecb56SDag-Erling Smørgrav 			memcpy(scratch, original, i);
2270b0ecb56SDag-Erling Smørgrav 			memcpy(&scratch[i], &original[i + j],
2280b0ecb56SDag-Erling Smørgrav 			    length + 1 - (i + j));
2290b0ecb56SDag-Erling Smørgrav 			if (is_simple(params, scratch)) {
2300b0ecb56SDag-Erling Smørgrav 				clean(scratch);
2310b0ecb56SDag-Erling Smørgrav 				return 1;
2320b0ecb56SDag-Erling Smørgrav 			}
2330b0ecb56SDag-Erling Smørgrav 		}
2340b0ecb56SDag-Erling Smørgrav 		if (!match) break;
2350b0ecb56SDag-Erling Smørgrav 	}
2360b0ecb56SDag-Erling Smørgrav 
2370b0ecb56SDag-Erling Smørgrav 	clean(scratch);
2380b0ecb56SDag-Erling Smørgrav 
2390b0ecb56SDag-Erling Smørgrav 	return 0;
2400b0ecb56SDag-Erling Smørgrav }
2410b0ecb56SDag-Erling Smørgrav 
2420b0ecb56SDag-Erling Smørgrav /*
2430b0ecb56SDag-Erling Smørgrav  * This wordlist check is now the least important given the checks above
2440b0ecb56SDag-Erling Smørgrav  * and the support for passphrases (which are based on dictionary words,
2450b0ecb56SDag-Erling Smørgrav  * and checked by other means). It is still useful to trap simple short
2460b0ecb56SDag-Erling Smørgrav  * passwords (if short passwords are allowed) that are word-based, but
2470b0ecb56SDag-Erling Smørgrav  * passed the other checks due to uncommon capitalization, digits, and
2480b0ecb56SDag-Erling Smørgrav  * special characters. We (mis)use the same set of words that are used
2490b0ecb56SDag-Erling Smørgrav  * to generate random passwords. This list is much smaller than those
2500b0ecb56SDag-Erling Smørgrav  * used for password crackers, and it doesn't contain common passwords
2510b0ecb56SDag-Erling Smørgrav  * that aren't short English words. Perhaps support for large wordlists
2520b0ecb56SDag-Erling Smørgrav  * should still be added, even though this is now of little importance.
2530b0ecb56SDag-Erling Smørgrav  */
is_word_based(passwdqc_params_t * params,const char * needle,const char * original)2540b0ecb56SDag-Erling Smørgrav static int is_word_based(passwdqc_params_t *params,
255402783abSDag-Erling Smørgrav     const char *needle, const char *original)
2560b0ecb56SDag-Erling Smørgrav {
2570b0ecb56SDag-Erling Smørgrav 	char word[7];
2580b0ecb56SDag-Erling Smørgrav 	char *unified;
259402783abSDag-Erling Smørgrav 	int i;
2600b0ecb56SDag-Erling Smørgrav 
2610b0ecb56SDag-Erling Smørgrav 	word[6] = '\0';
262402783abSDag-Erling Smørgrav 	for (i = 0; i < 0x1000; i++) {
263402783abSDag-Erling Smørgrav 		memcpy(word, _passwdqc_wordset_4k[i], 6);
264402783abSDag-Erling Smørgrav 		if ((int)strlen(word) < params->match_length) continue;
2650b0ecb56SDag-Erling Smørgrav 		unified = unify(word);
2660b0ecb56SDag-Erling Smørgrav 		if (is_based(params, unified, needle, original)) {
2670b0ecb56SDag-Erling Smørgrav 			clean(unified);
2680b0ecb56SDag-Erling Smørgrav 			return 1;
2690b0ecb56SDag-Erling Smørgrav 		}
2700b0ecb56SDag-Erling Smørgrav 		clean(unified);
2710b0ecb56SDag-Erling Smørgrav 	}
2720b0ecb56SDag-Erling Smørgrav 
2730b0ecb56SDag-Erling Smørgrav 	return 0;
2740b0ecb56SDag-Erling Smørgrav }
2750b0ecb56SDag-Erling Smørgrav 
_passwdqc_check(passwdqc_params_t * params,const char * newpass,const char * oldpass,struct passwd * pw)276402783abSDag-Erling Smørgrav const char *_passwdqc_check(passwdqc_params_t *params,
277402783abSDag-Erling Smørgrav     const char *newpass, const char *oldpass, struct passwd *pw)
2780b0ecb56SDag-Erling Smørgrav {
2790b0ecb56SDag-Erling Smørgrav 	char truncated[9], *reversed;
2800b0ecb56SDag-Erling Smørgrav 	char *u_newpass, *u_reversed;
2810b0ecb56SDag-Erling Smørgrav 	char *u_oldpass;
2820b0ecb56SDag-Erling Smørgrav 	char *u_name, *u_gecos;
283402783abSDag-Erling Smørgrav 	const char *reason;
2840b0ecb56SDag-Erling Smørgrav 	int length;
2850b0ecb56SDag-Erling Smørgrav 
2860b0ecb56SDag-Erling Smørgrav 	reversed = NULL;
2870b0ecb56SDag-Erling Smørgrav 	u_newpass = u_reversed = NULL;
2880b0ecb56SDag-Erling Smørgrav 	u_oldpass = NULL;
2890b0ecb56SDag-Erling Smørgrav 	u_name = u_gecos = NULL;
2900b0ecb56SDag-Erling Smørgrav 
2910b0ecb56SDag-Erling Smørgrav 	reason = NULL;
2920b0ecb56SDag-Erling Smørgrav 
2930b0ecb56SDag-Erling Smørgrav 	if (oldpass && !strcmp(oldpass, newpass))
2940b0ecb56SDag-Erling Smørgrav 		reason = REASON_SAME;
2950b0ecb56SDag-Erling Smørgrav 
2960b0ecb56SDag-Erling Smørgrav 	length = strlen(newpass);
2970b0ecb56SDag-Erling Smørgrav 
2980b0ecb56SDag-Erling Smørgrav 	if (!reason && length < params->min[4])
2990b0ecb56SDag-Erling Smørgrav 		reason = REASON_SHORT;
3000b0ecb56SDag-Erling Smørgrav 
3010b0ecb56SDag-Erling Smørgrav 	if (!reason && length > params->max) {
3020b0ecb56SDag-Erling Smørgrav 		if (params->max == 8) {
3030b0ecb56SDag-Erling Smørgrav 			truncated[0] = '\0';
3040b0ecb56SDag-Erling Smørgrav 			strncat(truncated, newpass, 8);
3050b0ecb56SDag-Erling Smørgrav 			newpass = truncated;
3060b0ecb56SDag-Erling Smørgrav 			if (oldpass && !strncmp(oldpass, newpass, 8))
3070b0ecb56SDag-Erling Smørgrav 				reason = REASON_SAME;
3080b0ecb56SDag-Erling Smørgrav 		} else
3090b0ecb56SDag-Erling Smørgrav 			reason = REASON_LONG;
3100b0ecb56SDag-Erling Smørgrav 	}
3110b0ecb56SDag-Erling Smørgrav 
3120b0ecb56SDag-Erling Smørgrav 	if (!reason && is_simple(params, newpass)) {
3130b0ecb56SDag-Erling Smørgrav 		if (length < params->min[1] && params->min[1] <= params->max)
3140b0ecb56SDag-Erling Smørgrav 			reason = REASON_SIMPLESHORT;
3150b0ecb56SDag-Erling Smørgrav 		else
3160b0ecb56SDag-Erling Smørgrav 			reason = REASON_SIMPLE;
3170b0ecb56SDag-Erling Smørgrav 	}
3180b0ecb56SDag-Erling Smørgrav 
3190b0ecb56SDag-Erling Smørgrav 	if (!reason) {
3200b0ecb56SDag-Erling Smørgrav 		if ((reversed = reverse(newpass))) {
3210b0ecb56SDag-Erling Smørgrav 			u_newpass = unify(newpass);
3220b0ecb56SDag-Erling Smørgrav 			u_reversed = unify(reversed);
3230b0ecb56SDag-Erling Smørgrav 			if (oldpass)
3240b0ecb56SDag-Erling Smørgrav 				u_oldpass = unify(oldpass);
3250b0ecb56SDag-Erling Smørgrav 			if (pw) {
3260b0ecb56SDag-Erling Smørgrav 				u_name = unify(pw->pw_name);
3270b0ecb56SDag-Erling Smørgrav 				u_gecos = unify(pw->pw_gecos);
3280b0ecb56SDag-Erling Smørgrav 			}
3290b0ecb56SDag-Erling Smørgrav 		}
3300b0ecb56SDag-Erling Smørgrav 		if (!reversed ||
3310b0ecb56SDag-Erling Smørgrav 		    !u_newpass || !u_reversed ||
3320b0ecb56SDag-Erling Smørgrav 		    (oldpass && !u_oldpass) ||
3330b0ecb56SDag-Erling Smørgrav 		    (pw && (!u_name || !u_gecos)))
3340b0ecb56SDag-Erling Smørgrav 			reason = REASON_ERROR;
3350b0ecb56SDag-Erling Smørgrav 	}
3360b0ecb56SDag-Erling Smørgrav 
3370b0ecb56SDag-Erling Smørgrav 	if (!reason && oldpass && params->similar_deny &&
3380b0ecb56SDag-Erling Smørgrav 	    (is_based(params, u_oldpass, u_newpass, newpass) ||
3390b0ecb56SDag-Erling Smørgrav 	    is_based(params, u_oldpass, u_reversed, reversed)))
3400b0ecb56SDag-Erling Smørgrav 		reason = REASON_SIMILAR;
3410b0ecb56SDag-Erling Smørgrav 
3420b0ecb56SDag-Erling Smørgrav 	if (!reason && pw &&
3430b0ecb56SDag-Erling Smørgrav 	    (is_based(params, u_name, u_newpass, newpass) ||
3440b0ecb56SDag-Erling Smørgrav 	    is_based(params, u_name, u_reversed, reversed) ||
3450b0ecb56SDag-Erling Smørgrav 	    is_based(params, u_gecos, u_newpass, newpass) ||
3460b0ecb56SDag-Erling Smørgrav 	    is_based(params, u_gecos, u_reversed, reversed)))
3470b0ecb56SDag-Erling Smørgrav 		reason = REASON_PERSONAL;
3480b0ecb56SDag-Erling Smørgrav 
349402783abSDag-Erling Smørgrav 	if (!reason && (int)strlen(newpass) < params->min[2] &&
3500b0ecb56SDag-Erling Smørgrav 	    (is_word_based(params, u_newpass, newpass) ||
3510b0ecb56SDag-Erling Smørgrav 	    is_word_based(params, u_reversed, reversed)))
3520b0ecb56SDag-Erling Smørgrav 		reason = REASON_WORD;
3530b0ecb56SDag-Erling Smørgrav 
3540b0ecb56SDag-Erling Smørgrav 	memset(truncated, 0, sizeof(truncated));
3550b0ecb56SDag-Erling Smørgrav 	clean(reversed);
3560b0ecb56SDag-Erling Smørgrav 	clean(u_newpass); clean(u_reversed);
3570b0ecb56SDag-Erling Smørgrav 	clean(u_oldpass);
3580b0ecb56SDag-Erling Smørgrav 	clean(u_name); clean(u_gecos);
3590b0ecb56SDag-Erling Smørgrav 
3600b0ecb56SDag-Erling Smørgrav 	return reason;
3610b0ecb56SDag-Erling Smørgrav }
362