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