2 * This program is copyright Alec Muffett 1993. The author disclaims all
3 * responsibility or liability with respect to it's usage or its effect
4 * upon hardware or computer systems, and maintains copyright as set out
5 * in the "LICENCE" document which accompanies distributions of Crack v4.0
10 * Modified as part of the krb5-strength project as follows:
12 * 2016-08-17 Howard Guo <hguo@suse.com>
13 * - Double the length of buffers in Mangle to provide enough space to
14 * handle duplicating rules.
15 * 2007-03-22 Russ Allbery <eagle@eyrie.org>
16 * - Cap deletion of leading or trailing characters at one more than half
17 * the length of the password string and no more than five characters.
18 * This goes with a change to fascist.c that adds rules to delete more
19 * leading and trailing characters for longer passwords.
20 * - Additional system includes for other functions.
21 * 2009-10-14 Russ Allbery <eagle@eyrie.org>
22 * - Simplify Debug() function for how it's actually called.
23 * - Add ANSI C protototypes for all functions.
24 * - Tweaks for const cleanliness.
25 * - Make internal functions static.
26 * - Remove unused variables.
27 * - Changed a variable to unsigned to avoid gcc warnings.
28 * 2016-11-06 Russ Allbery <eagle@eyrie.org>
29 * - Remove unused vers_id to silence GCC warnings.
30 * - Added GCC __attribute__ marker on Debug() function.
43 static void __attribute__((__format__(printf, 2, 3)))
44 Debug(int val, const char *fmt, ...)
50 vfprintf(stderr, fmt, args);
60 #define RULE_PREPEND '^'
61 #define RULE_APPEND '$'
62 #define RULE_REVERSE 'r'
63 #define RULE_UPPERCASE 'u'
64 #define RULE_LOWERCASE 'l'
65 #define RULE_PLURALISE 'p'
66 #define RULE_CAPITALISE 'c'
67 #define RULE_DUPLICATE 'd'
68 #define RULE_REFLECT 'f'
69 #define RULE_SUBSTITUTE 's'
70 #define RULE_MATCH '/'
74 #define RULE_EXTRACT 'x'
75 #define RULE_OVERSTRIKE 'o'
76 #define RULE_INSERT 'i'
77 #define RULE_EQUALS '='
78 #define RULE_PURGE '@'
79 #define RULE_CLASS '?' /* class rule? socialist ethic in cracker? */
81 #define RULE_DFIRST '['
82 #define RULE_DLAST ']'
83 #define RULE_MFIRST '('
84 #define RULE_MLAST ')'
87 Suffix(const char *myword, const char *suffix)
96 return (STRCMP((myword + i - j), suffix));
103 /* return a pointer to a reversal */
105 Reverse(const char *str)
109 static char area[STRINGSIZE];
119 /* return a pointer to an uppercase */
121 Uppercase(const char *str)
124 static char area[STRINGSIZE];
128 *(ptr++) = CRACK_TOUPPER(*str);
136 /* return a pointer to an lowercase */
138 Lowercase(const char *str)
141 static char area[STRINGSIZE];
145 *(ptr++) = CRACK_TOLOWER(*str);
153 /* return a pointer to an capitalised */
155 Capitalise(const char *str)
158 static char area[STRINGSIZE];
163 *(ptr++) = CRACK_TOLOWER(*str);
168 area[0] = CRACK_TOUPPER(area[0]);
172 /* returns a pointer to a plural */
174 Pluralise(const char *string)
177 static char area[STRINGSIZE];
178 length = strlen(string);
179 strcpy(area, string);
181 if (!Suffix(string, "ch") ||
182 !Suffix(string, "ex") ||
183 !Suffix(string, "ix") ||
184 !Suffix(string, "sh") ||
185 !Suffix(string, "ss"))
187 /* bench -> benches */
189 } else if (length > 2 && string[length - 1] == 'y')
191 if (strchr("aeiou", string[length - 2]))
193 /* alloy -> alloys */
197 /* gully -> gullies */
198 strcpy(area + length - 1, "ies");
200 } else if (string[length - 1] == 's')
213 /* returns pointer to a swapped about copy */
215 Substitute(const char *string, char old, char new)
218 static char area[STRINGSIZE];
222 *(ptr++) = (*string == old ? new : *string);
229 /* returns pointer to a purged copy */
231 Purge(const char *string, char target)
234 static char area[STRINGSIZE];
238 if (*string != target)
247 /* -------- CHARACTER CLASSES START HERE -------- */
250 * this function takes two inputs, a class identifier and a character, and
251 * returns non-null if the given character is a member of the class, based
252 * upon restrictions set out below
256 MatchClass(char class, char input)
266 case '?': /* ?? -> ? */
273 /* ILLOGICAL GROUPINGS (ie: not in ctype.h) */
276 case 'v': /* vowels */
277 c = CRACK_TOLOWER(input);
278 if (strchr("aeiou", c))
285 case 'c': /* consonants */
286 c = CRACK_TOLOWER(input);
287 if (strchr("bcdfghjklmnpqrstvwxyz", c))
294 case 'w': /* whitespace */
295 if (strchr("\t ", input))
302 case 'p': /* punctuation */
303 if (strchr(".`,:;'!?\"", input))
310 case 's': /* symbols */
311 if (strchr("$%%^&*()-_+=|\\[]{}#@/~", input))
317 /* LOGICAL GROUPINGS */
320 case 'l': /* lowercase */
328 case 'u': /* uppercase */
336 case 'a': /* alphabetic */
344 case 'x': /* alphanumeric */
352 case 'd': /* digits */
360 Debug(1, "MatchClass: unknown class %c\n", class);
373 PolyStrchr(const char *string, char class)
377 if (MatchClass(class, *string))
386 /* returns pointer to a swapped about copy */
388 PolySubst(const char *string, char class, char new)
391 static char area[STRINGSIZE];
395 *(ptr++) = (MatchClass(class, *string) ? new : *string);
402 /* returns pointer to a purged copy */
404 PolyPurge(const char *string, const char class)
407 static char area[STRINGSIZE];
411 if (!MatchClass(class, *string))
420 /* -------- BACK TO NORMALITY -------- */
423 Char2Int(char character)
425 if (isdigit(character))
427 return (character - '0');
428 } else if (islower(character))
430 return (character - 'a' + 10);
431 } else if (isupper(character))
433 return (character - 'A' + 10);
438 /* returns a pointer to a controlled Mangle */
440 Mangle(const char *input, const char *control)
442 int limit, min_to_shift;
445 static char area[STRINGSIZE * 2] = "";
446 char area2[STRINGSIZE * 2] = "";
452 min_to_shift = (j + 1) / 2;
455 min_to_shift = j / 2;
458 if (min_to_shift > 5)
463 for (ptr = control; *ptr; ptr++)
470 strcpy(area, Reverse(area));
473 strcpy(area, Uppercase(area));
476 strcpy(area, Lowercase(area));
478 case RULE_CAPITALISE:
479 strcpy(area, Capitalise(area));
482 strcpy(area, Pluralise(area));
485 strcat(area, Reverse(area));
494 Debug(1, "Mangle: '>' missing argument in '%s'\n", control);
498 limit = Char2Int(*(++ptr));
501 Debug(1, "Mangle: '>' weird argument in '%s'\n", control);
504 if (strlen(area) <= (size_t) limit)
513 Debug(1, "Mangle: '<' missing argument in '%s'\n", control);
517 limit = Char2Int(*(++ptr));
520 Debug(1, "Mangle: '<' weird argument in '%s'\n", control);
523 if (strlen(area) >= (size_t) limit)
532 Debug(1, "Mangle: prepend missing argument in '%s'\n", control);
537 strcpy(area2 + 1, area);
544 Debug(1, "Mangle: append missing argument in '%s'\n", control);
548 register char *string;
551 string[-1] = *(++ptr);
556 if (!ptr[1] || !ptr[2])
558 Debug(1, "Mangle: extract missing argument in '%s'\n", control);
565 start = Char2Int(*(++ptr));
566 length = Char2Int(*(++ptr));
567 if (start < 0 || length < 0)
569 Debug(1, "Mangle: extract: weird argument in '%s'\n", control);
573 for (i = 0; length-- && area2[start + i]; i++)
575 area[i] = area2[start + i];
577 /* cant use strncpy() - no trailing NUL */
581 case RULE_OVERSTRIKE:
582 if (!ptr[1] || !ptr[2])
584 Debug(1, "Mangle: overstrike missing argument in '%s'\n", control);
589 i = Char2Int(*(++ptr));
592 Debug(1, "Mangle: overstrike weird argument in '%s'\n",
606 if (!ptr[1] || !ptr[2])
608 Debug(1, "Mangle: insert missing argument in '%s'\n", control);
615 i = Char2Int(*(++ptr));
618 Debug(1, "Mangle: insert weird argument in '%s'\n",
634 /* THE FOLLOWING RULES REQUIRE CLASS MATCHING */
636 case RULE_PURGE: /* @x or @?c */
637 if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
639 Debug(1, "Mangle: delete missing arguments in '%s'\n", control);
641 } else if (ptr[1] != RULE_CLASS)
643 strcpy(area, Purge(area, *(++ptr)));
646 strcpy(area, PolyPurge(area, ptr[2]));
650 case RULE_SUBSTITUTE: /* sxy || s?cy */
651 if (!ptr[1] || !ptr[2] || (ptr[1] == RULE_CLASS && !ptr[3]))
653 Debug(1, "Mangle: subst missing argument in '%s'\n", control);
655 } else if (ptr[1] != RULE_CLASS)
657 strcpy(area, Substitute(area, ptr[1], ptr[2]));
661 strcpy(area, PolySubst(area, ptr[2], ptr[3]));
665 case RULE_MATCH: /* /x || /?c */
666 if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
668 Debug(1, "Mangle: '/' missing argument in '%s'\n", control);
670 } else if (ptr[1] != RULE_CLASS)
672 if (!strchr(area, *(++ptr)))
678 if (!PolyStrchr(area, ptr[2]))
685 case RULE_NOT: /* !x || !?c */
686 if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
688 Debug(1, "Mangle: '!' missing argument in '%s'\n", control);
690 } else if (ptr[1] != RULE_CLASS)
692 if (strchr(area, *(++ptr)))
698 if (PolyStrchr(area, ptr[2]))
706 * alternative use for a boomerang, number 1: a standard throwing
707 * boomerang is an ideal thing to use to tuck the sheets under
708 * the mattress when making your bed. The streamlined shape of
709 * the boomerang allows it to slip easily 'twixt mattress and
710 * bedframe, and it's curve makes it very easy to hook sheets
714 case RULE_EQUALS: /* =nx || =n?c */
715 if (!ptr[1] || !ptr[2] || (ptr[2] == RULE_CLASS && !ptr[3]))
717 Debug(1, "Mangle: '=' missing argument in '%s'\n", control);
722 if ((i = Char2Int(ptr[1])) < 0)
724 Debug(1, "Mangle: '=' weird argument in '%s'\n", control);
727 if (ptr[2] != RULE_CLASS)
737 if (!MatchClass(*ptr, area[i]))
746 if (area[0] && strlen(area) > (size_t) min_to_shift)
749 for (i = 1; area[i]; i++)
751 area[i - 1] = area[i];
758 if (area[0] && strlen(area) > (size_t) min_to_shift)
761 for (i = 1; area[i]; i++);
767 if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
769 Debug(1, "Mangle: '(' missing argument in '%s'\n", control);
773 if (ptr[1] != RULE_CLASS)
783 if (!MatchClass(*ptr, area[0]))
790 if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
792 Debug(1, "Mangle: ')' missing argument in '%s'\n", control);
798 for (i = 0; area[i]; i++);
808 if (ptr[1] != RULE_CLASS)
818 if (!MatchClass(*ptr, area[i]))
826 Debug(1, "Mangle: unknown command %c in %s\n", *ptr, control);
831 if (!area[0]) /* have we deweted de poor widdle fing away? */
839 PMatch(const char *control, const char *string)
841 while (*string && *control)
843 if (!MatchClass(*control, *string))
852 if (*string || *control)