xref: /illumos-gate/usr/src/lib/pam_modules/authtok_check/rules.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * This program is copyright Alec Muffett 1993. The author disclaims all
8  * responsibility or liability with respect to it's usage or its effect
9  * upon hardware or computer systems, and maintains copyright as set out
10  * in the "LICENCE" document which accompanies distributions of Crack v4.0
11  * and upwards.
12  */
13 
14 #include "packer.h"
15 
16 
17 #define	RULE_NOOP	':'
18 #define	RULE_PREPEND	'^'
19 #define	RULE_APPEND	'$'
20 #define	RULE_REVERSE	'r'
21 #define	RULE_UPPERCASE	'u'
22 #define	RULE_LOWERCASE	'l'
23 #define	RULE_PLURALISE	'p'
24 #define	RULE_CAPITALISE	'c'
25 #define	RULE_DUPLICATE	'd'
26 #define	RULE_REFLECT	'f'
27 #define	RULE_SUBSTITUTE	's'
28 #define	RULE_MATCH	'/'
29 #define	RULE_NOT	'!'
30 #define	RULE_LT		'<'
31 #define	RULE_GT		'>'
32 #define	RULE_EXTRACT	'x'
33 #define	RULE_OVERSTRIKE	'o'
34 #define	RULE_INSERT	'i'
35 #define	RULE_EQUALS	'='
36 #define	RULE_PURGE	'@'
37 #define	RULE_CLASS	'?'	/* class rule? socialist ethic in cracker? */
38 #define	RULE_DFIRST	'['
39 #define	RULE_DLAST	']'
40 #define	RULE_MFIRST	'('
41 #define	RULE_MLAST	')'
42 
43 int
44 Suffix(char *myword, char *suffix)
45 {
46 	register int i;
47 	register int j;
48 
49 	i = strlen(myword);
50 	j = strlen(suffix);
51 
52 	if (i > j) {
53 		return (STRCMP((myword + i - j), suffix));
54 	} else {
55 		return (-1);
56 	}
57 }
58 
59 char *
60 Reverse(register char *str)		/* return a pointer to a reversal */
61 {
62 	register int i;
63 	register int j;
64 	static char area[PATH_MAX];
65 
66 	j = i = strlen(str);
67 	while (*str) {
68 		area[--i] = *str++;
69 	}
70 	area[j] = '\0';
71 	return (area);
72 }
73 
74 char *
75 Uppercase(register char *str)		/* return a pointer to an uppercase */
76 {
77 	register char *ptr;
78 	static char area[PATH_MAX];
79 
80 	ptr = area;
81 	while (*str) {
82 		*(ptr++) = CRACK_TOUPPER(*str);
83 		str++;
84 	}
85 	*ptr = '\0';
86 
87 	return (area);
88 }
89 
90 char *
91 Lowercase(register char *str)		/* return a pointer to an lowercase */
92 {
93 	register char *ptr;
94 	static char area[PATH_MAX];
95 
96 	ptr = area;
97 	while (*str) {
98 		*(ptr++) = CRACK_TOLOWER(*str);
99 		str++;
100 	}
101 	*ptr = '\0';
102 
103 	return (area);
104 }
105 
106 char *
107 Capitalise(register char *str)		/* return a pointer to an capitalised */
108 {
109 	register char *ptr;
110 	static char area[PATH_MAX];
111 
112 	ptr = area;
113 
114 	while (*str) {
115 		*(ptr++) = CRACK_TOLOWER(*str);
116 		str++;
117 	}
118 
119 	*ptr = '\0';
120 	area[0] = CRACK_TOUPPER(area[0]);
121 	return (area);
122 }
123 
124 char *
125 Pluralise(register char *string)	/* returns a pointer to a plural */
126 {
127 	register int length;
128 	static char area[PATH_MAX];
129 
130 	length = strlen(string);
131 	(void) strlcpy(area, string, PATH_MAX);
132 
133 	if (!Suffix(string, "ch") ||
134 	    !Suffix(string, "ex") ||
135 	    !Suffix(string, "ix") ||
136 	    !Suffix(string, "sh") ||
137 	    !Suffix(string, "ss")) {
138 		/* bench -> benches */
139 		(void) strcat(area, "es");
140 	} else if (length > 2 && string[length - 1] == 'y') {
141 		if (strchr("aeiou", string[length - 2])) {
142 			/* alloy -> alloys */
143 			(void) strcat(area, "s");
144 		} else {
145 			/* gully -> gullies */
146 			(void) strcpy(area + length - 1, "ies");
147 		}
148 	} else if (string[length - 1] == 's') {
149 		/* bias -> biases */
150 		(void) strcat(area, "es");
151 	} else {
152 		/* catchall */
153 		(void) strcat(area, "s");
154 	}
155 
156 	return (area);
157 }
158 
159 char *
160 Substitute(register char *string, register char old,
161 	register char new)	/* returns pointer to a swapped about copy */
162 {
163 	register char *ptr;
164 	static char area[PATH_MAX];
165 
166 	ptr = area;
167 	while (*string) {
168 		*(ptr++) = (*string == old ? new : *string);
169 		string++;
170 	}
171 	*ptr = '\0';
172 	return (area);
173 }
174 
175 /* returns pointer to a purged copy */
176 char *
177 Purge(register char *string, register char target)
178 {
179 	register char *ptr;
180 	static char area[PATH_MAX];
181 	ptr = area;
182 	while (*string) {
183 		if (*string != target) {
184 			*(ptr++) = *string;
185 		}
186 		string++;
187 	}
188 	*ptr = '\0';
189 	return (area);
190 }
191 /* -------- CHARACTER CLASSES START HERE -------- */
192 
193 /*
194  * this function takes two inputs, a class identifier and a character, and
195  * returns non-null if the given character is a member of the class, based
196  * upon restrictions set out below
197  */
198 
199 int
200 MatchClass(register char class, register char input)
201 {
202 	register char c;
203 	register int retval;
204 
205 	retval = 0;
206 
207 	switch (class) {
208 	/* ESCAPE */
209 
210 		case '?':			/* ?? -> ? */
211 			if (input == '?') {
212 				retval = 1;
213 			}
214 			break;
215 
216 	/* ILLOGICAL GROUPINGS (ie: not in ctype.h) */
217 
218 		case 'V':
219 		case 'v':			/* vowels */
220 			c = CRACK_TOLOWER(input);
221 			if (strchr("aeiou", c)) {
222 				retval = 1;
223 			}
224 			break;
225 
226 		case 'C':
227 		case 'c':			/* consonants */
228 			c = CRACK_TOLOWER(input);
229 			if (strchr("bcdfghjklmnpqrstvwxyz", c)) {
230 				retval = 1;
231 			}
232 			break;
233 
234 		case 'W':
235 		case 'w':			/* whitespace */
236 			if (strchr("\t ", input)) {
237 				retval = 1;
238 			}
239 			break;
240 
241 		case 'P':
242 		case 'p':			/* punctuation */
243 			if (strchr(".`,:;'!?\"", input)) {
244 				retval = 1;
245 			}
246 			break;
247 
248 		case 'S':
249 		case 's':			/* symbols */
250 			if (strchr("$%%^&*()-_+=|\\[]{}#@/~", input)) {
251 				retval = 1;
252 			}
253 			break;
254 
255 		/* LOGICAL GROUPINGS */
256 
257 		case 'L':
258 		case 'l':			/* lowercase */
259 			if (islower(input)) {
260 				retval = 1;
261 			}
262 			break;
263 
264 		case 'U':
265 		case 'u':			/* uppercase */
266 			if (isupper(input)) {
267 				retval = 1;
268 			}
269 			break;
270 
271 		case 'A':
272 		case 'a':			/* alphabetic */
273 			if (isalpha(input)) {
274 				retval = 1;
275 			}
276 			break;
277 
278 		case 'X':
279 		case 'x':			/* alphanumeric */
280 			if (isalnum(input)) {
281 				retval = 1;
282 			}
283 			break;
284 
285 		case 'D':
286 		case 'd':			/* digits */
287 			if (isdigit(input)) {
288 				retval = 1;
289 			}
290 			break;
291 	}
292 
293 	if (isupper(class)) {
294 		return (!retval);
295 	}
296 	return (retval);
297 }
298 
299 char *
300 PolyStrchr(register char *string, register char class)
301 {
302 	while (*string) {
303 		if (MatchClass(class, *string)) {
304 			return (string);
305 		}
306 		string++;
307 	}
308 	return ((char *)0);
309 }
310 
311 /* returns pointer to a swapped about copy */
312 char *
313 PolySubst(register char *string, register char class, register char new)
314 {
315 	register char *ptr;
316 	static char area[PATH_MAX];
317 
318 	ptr = area;
319 	while (*string) {
320 		*(ptr++) = (MatchClass(class, *string) ? new : *string);
321 		string++;
322 	}
323 	*ptr = '\0';
324 	return (area);
325 }
326 
327 /* returns pointer to a purged copy */
328 char *
329 PolyPurge(register char *string, register char class)
330 {
331 	register char *ptr;
332 	static char area[PATH_MAX];
333 
334 	ptr = area;
335 	while (*string) {
336 		if (!MatchClass(class, *string)) {
337 			*(ptr++) = *string;
338 		}
339 		string++;
340 	}
341 	*ptr = '\0';
342 	return (area);
343 }
344 /* -------- BACK TO NORMALITY -------- */
345 
346 int
347 Char2Int(char character)
348 {
349 	if (isdigit(character)) {
350 		return (character - '0');
351 	} else if (islower(character)) {
352 		return (character - 'a' + 10);
353 	} else if (isupper(character)) {
354 		return (character - 'A' + 10);
355 	}
356 	return (-1);
357 }
358 
359 /* returns a pointer to a controlled Mangle */
360 char *
361 Mangle(char *input, char *control)
362 {
363 	int limit;
364 	register char *ptr;
365 	static char area[PATH_MAX];
366 	char area2[PATH_MAX];
367 
368 	area[0] = '\0';
369 	(void) strlcpy(area, input, PATH_MAX);
370 
371 	for (ptr = control; *ptr; ptr++) {
372 		switch (*ptr) {
373 			case RULE_NOOP:
374 				break;
375 			case RULE_REVERSE:
376 				(void) strlcpy(area, Reverse(area), PATH_MAX);
377 				break;
378 			case RULE_UPPERCASE:
379 				(void) strlcpy(area, Uppercase(area), PATH_MAX);
380 				break;
381 			case RULE_LOWERCASE:
382 				(void) strlcpy(area, Lowercase(area), PATH_MAX);
383 				break;
384 			case RULE_CAPITALISE:
385 				(void) strlcpy(area, Capitalise(area),
386 				    PATH_MAX);
387 				break;
388 			case RULE_PLURALISE:
389 				(void) strlcpy(area, Pluralise(area), PATH_MAX);
390 				break;
391 			case RULE_REFLECT:
392 				(void) strlcat(area, Reverse(area), PATH_MAX);
393 				break;
394 			case RULE_DUPLICATE:
395 				(void) strlcpy(area2, area, PATH_MAX);
396 				(void) strlcat(area, area2, PATH_MAX);
397 				break;
398 			case RULE_GT:
399 				if (!ptr[1]) {
400 					return ((char *)0);
401 				} else {
402 					limit = Char2Int(*(++ptr));
403 					if (limit < 0) {
404 						return ((char *)0);
405 					}
406 					if (strlen(area) <= limit) {
407 						return ((char *)0);
408 					}
409 				}
410 				break;
411 			case RULE_LT:
412 				if (!ptr[1]) {
413 					return ((char *)0);
414 				} else {
415 					limit = Char2Int(*(++ptr));
416 					if (limit < 0) {
417 						return ((char *)0);
418 					}
419 					if (strlen(area) >= limit) {
420 						return ((char *)0);
421 					}
422 				}
423 				break;
424 			case RULE_PREPEND:
425 				if (!ptr[1]) {
426 					return ((char *)0);
427 				} else {
428 					area2[0] = *(++ptr);
429 					(void) strlcpy(area2 + 1, area,
430 					    PATH_MAX);
431 					(void) strlcpy(area, area2, PATH_MAX);
432 				}
433 				break;
434 			case RULE_APPEND:
435 				if (!ptr[1]) {
436 					return ((char *)0);
437 				} else {
438 					register char *string;
439 
440 					string = area;
441 					while (*(string++));
442 					string[-1] = *(++ptr);
443 					*string = '\0';
444 				}
445 				break;
446 			case RULE_EXTRACT:
447 				if (!ptr[1] || !ptr[2]) {
448 					return ((char *)0);
449 				} else {
450 					register int i;
451 					int start;
452 					int length;
453 
454 					start = Char2Int(*(++ptr));
455 					length = Char2Int(*(++ptr));
456 					if (start < 0 || length < 0) {
457 						return ((char *)0);
458 					}
459 					(void) strlcpy(area2, area, PATH_MAX);
460 					for (i = 0; length-- &&
461 					    area2[start + i]; i++) {
462 						area[i] = area2[start + i];
463 					}
464 					/* cant use strncpy()-no trailing NUL */
465 					area[i] = '\0';
466 				}
467 				break;
468 			case RULE_OVERSTRIKE:
469 				if (!ptr[1] || !ptr[2]) {
470 					return ((char *)0);
471 				} else {
472 					register int i;
473 
474 					i = Char2Int(*(++ptr));
475 					if (i < 0) {
476 						return ((char *)0);
477 					} else {
478 						++ptr;
479 						if (area[i]) {
480 							area[i] = *ptr;
481 						}
482 					}
483 				}
484 				break;
485 			case RULE_INSERT:
486 				if (!ptr[1] || !ptr[2]) {
487 					return ((char *)0);
488 				} else {
489 					register int i;
490 					register char *p1;
491 					register char *p2;
492 
493 					i = Char2Int(*(++ptr));
494 					if (i < 0) {
495 						return ((char *)0);
496 					}
497 					p1 = area;
498 					p2 = area2;
499 					while (i && *p1) {
500 						i--;
501 						*(p2++) = *(p1++);
502 					}
503 					*(p2++) = *(++ptr);
504 					(void) strlcpy(p2, p1, PATH_MAX);
505 					(void) strlcpy(area, area2, PATH_MAX);
506 				}
507 				break;
508 	    /* THE FOLLOWING RULES REQUIRE CLASS MATCHING */
509 
510 			case RULE_PURGE:	/* @x or @?c */
511 				if (!ptr[1] || (ptr[1] ==
512 				    RULE_CLASS && !ptr[2])) {
513 					return ((char *)0);
514 				} else if (ptr[1] != RULE_CLASS) {
515 					(void) strlcpy(area, Purge(area,
516 					    *(++ptr)), PATH_MAX);
517 				} else {
518 					(void) strlcpy(area, PolyPurge(area,
519 					    ptr[2]), PATH_MAX);
520 					ptr += 2;
521 				}
522 				break;
523 			case RULE_SUBSTITUTE:	/* sxy || s?cy */
524 				if (!ptr[1] || !ptr[2] ||
525 				    (ptr[1] == RULE_CLASS && !ptr[3])) {
526 					return ((char *)0);
527 				} else if (ptr[1] != RULE_CLASS) {
528 					ptr += 2;
529 				} else {
530 					(void) strlcpy(area, PolySubst(area,
531 					    ptr[2], ptr[3]), PATH_MAX);
532 					ptr += 3;
533 				}
534 				break;
535 			case RULE_MATCH:	/* /x || /?c */
536 				if (!ptr[1] ||
537 				    (ptr[1] == RULE_CLASS && !ptr[2])) {
538 					return ((char *)0);
539 				} else if (ptr[1] != RULE_CLASS) {
540 					if (!strchr(area, *(++ptr))) {
541 						return ((char *)0);
542 					}
543 				} else {
544 					if (!PolyStrchr(area, ptr[2])) {
545 						return ((char *)0);
546 					}
547 					ptr += 2;
548 				}
549 				break;
550 			case RULE_NOT:		/* !x || !?c */
551 				if (!ptr[1] ||
552 				    (ptr[1] == RULE_CLASS && !ptr[2])) {
553 					return ((char *)0);
554 				} else if (ptr[1] != RULE_CLASS) {
555 					if (strchr(area, *(++ptr))) {
556 						return ((char *)0);
557 					}
558 				} else {
559 					if (PolyStrchr(area, ptr[2])) {
560 						return ((char *)0);
561 					}
562 					ptr += 2;
563 				}
564 				break;
565 	/*
566 	 * alternative use for a boomerang, number 1: a standard throwing
567 	 * boomerang is an ideal thing to use to tuck the sheets under
568 	 * the mattress when making your bed.  The streamlined shape of
569 	 * the boomerang allows it to slip easily 'twixt mattress and
570 	 * bedframe, and it's curve makes it very easy to hook sheets
571 	 * into the gap.
572 	 */
573 
574 			case RULE_EQUALS:	/* =nx || =n?c */
575 				if (!ptr[1] || !ptr[2] ||
576 				    (ptr[2] == RULE_CLASS && !ptr[3])) {
577 					return ((char *)0);
578 				} else {
579 					register int i;
580 
581 					if ((i = Char2Int(ptr[1])) < 0) {
582 						return ((char *)0);
583 					}
584 					if (ptr[2] != RULE_CLASS) {
585 						ptr += 2;
586 						if (area[i] != *ptr) {
587 							return ((char *)0);
588 						}
589 					} else {
590 						ptr += 3;
591 						if (!MatchClass(*ptr,
592 						    area[i])) {
593 							return ((char *)0);
594 						}
595 					}
596 				}
597 				break;
598 
599 			case RULE_DFIRST:
600 				if (area[0]) {
601 					register int i;
602 
603 					for (i = 1; area[i]; i++) {
604 						area[i - 1] = area[i];
605 					}
606 					area[i - 1] = '\0';
607 				}
608 				break;
609 
610 			case RULE_DLAST:
611 				if (area[0]) {
612 					register int i;
613 
614 					for (i = 1; area[i]; i++);
615 					area[i - 1] = '\0';
616 				}
617 				break;
618 
619 			case RULE_MFIRST:
620 				if (!ptr[1] ||
621 				    (ptr[1] == RULE_CLASS && !ptr[2])) {
622 					return ((char *)0);
623 				} else {
624 					if (ptr[1] != RULE_CLASS) {
625 						ptr++;
626 						if (area[0] != *ptr) {
627 							return ((char *)0);
628 						}
629 					} else {
630 						ptr += 2;
631 						if (!MatchClass(*ptr,
632 						    area[0])) {
633 							return ((char *)0);
634 						}
635 					}
636 				}
637 				break;
638 			case RULE_MLAST:
639 				if (!ptr[1] ||
640 				    (ptr[1] == RULE_CLASS && !ptr[2])) {
641 					return ((char *)0);
642 				} else {
643 					register int i;
644 
645 					for (i = 0; area[i]; i++);
646 
647 					if (i > 0) {
648 						i--;
649 					} else {
650 						return ((char *)0);
651 					}
652 					if (ptr[1] != RULE_CLASS) {
653 						ptr++;
654 						if (area[i] != *ptr) {
655 							return ((char *)0);
656 						}
657 					} else {
658 						ptr += 2;
659 						if (!MatchClass(*ptr,
660 						    area[i])) {
661 							return ((char *)0);
662 						}
663 					}
664 				}
665 				break;
666 		}
667 	}
668 	if (!area[0]) {		/* have we deweted de poor widdle fing away? */
669 		return ((char *)0);
670 	}
671 	return (area);
672 }
673 /*
674  * int
675  * PMatch(register char *control, register char *string)
676  * {
677  * 	while (*string && *control) {
678  * 		if (!MatchClass(*control, *string)) {
679  * 			return (0);
680  * 		}
681  *
682  * 		string++;
683  * 		control++;
684  * 	}
685  *
686  * 	if (*string || *control) {
687  * 		return (0);
688  * 	}
689  *
690  * 	return (1);
691  * }
692  */
693