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.
31 * 2020-05-16 Russ Allbery <eagle@eyrie.org>
32 * - Change variables from int to size_t to silence warnings.
33 * - Add missing break to RULE_MFIRST and RULE_MLAST handling.
34 * - Remove break after return to silence Clang warnings.
47 static void __attribute__((__format__(printf, 2, 3)))
48 Debug(int val, const char *fmt, ...)
54 vfprintf(stderr, fmt, args);
64 #define RULE_PREPEND '^'
65 #define RULE_APPEND '$'
66 #define RULE_REVERSE 'r'
67 #define RULE_UPPERCASE 'u'
68 #define RULE_LOWERCASE 'l'
69 #define RULE_PLURALISE 'p'
70 #define RULE_CAPITALISE 'c'
71 #define RULE_DUPLICATE 'd'
72 #define RULE_REFLECT 'f'
73 #define RULE_SUBSTITUTE 's'
74 #define RULE_MATCH '/'
78 #define RULE_EXTRACT 'x'
79 #define RULE_OVERSTRIKE 'o'
80 #define RULE_INSERT 'i'
81 #define RULE_EQUALS '='
82 #define RULE_PURGE '@'
83 #define RULE_CLASS '?' /* class rule? socialist ethic in cracker? */
85 #define RULE_DFIRST '['
86 #define RULE_DLAST ']'
87 #define RULE_MFIRST '('
88 #define RULE_MLAST ')'
91 Suffix(const char *myword, const char *suffix)
100 return (STRCMP((myword + i - j), suffix));
107 /* return a pointer to a reversal */
109 Reverse(const char *str)
113 static char area[STRINGSIZE];
123 /* return a pointer to an uppercase */
125 Uppercase(const char *str)
128 static char area[STRINGSIZE];
132 *(ptr++) = CRACK_TOUPPER(*str);
140 /* return a pointer to an lowercase */
142 Lowercase(const char *str)
145 static char area[STRINGSIZE];
149 *(ptr++) = CRACK_TOLOWER(*str);
157 /* return a pointer to an capitalised */
159 Capitalise(const char *str)
162 static char area[STRINGSIZE];
167 *(ptr++) = CRACK_TOLOWER(*str);
172 area[0] = CRACK_TOUPPER(area[0]);
176 /* returns a pointer to a plural */
178 Pluralise(const char *string)
180 register size_t length;
181 static char area[STRINGSIZE];
182 length = strlen(string);
183 strcpy(area, string);
185 if (!Suffix(string, "ch") ||
186 !Suffix(string, "ex") ||
187 !Suffix(string, "ix") ||
188 !Suffix(string, "sh") ||
189 !Suffix(string, "ss"))
191 /* bench -> benches */
193 } else if (length > 2 && string[length - 1] == 'y')
195 if (strchr("aeiou", string[length - 2]))
197 /* alloy -> alloys */
201 /* gully -> gullies */
202 strcpy(area + length - 1, "ies");
204 } else if (string[length - 1] == 's')
217 /* returns pointer to a swapped about copy */
219 Substitute(const char *string, char old, char new)
222 static char area[STRINGSIZE];
226 *(ptr++) = (*string == old ? new : *string);
233 /* returns pointer to a purged copy */
235 Purge(const char *string, char target)
238 static char area[STRINGSIZE];
242 if (*string != target)
251 /* -------- CHARACTER CLASSES START HERE -------- */
254 * this function takes two inputs, a class identifier and a character, and
255 * returns non-null if the given character is a member of the class, based
256 * upon restrictions set out below
260 MatchClass(char class, char input)
270 case '?': /* ?? -> ? */
277 /* ILLOGICAL GROUPINGS (ie: not in ctype.h) */
280 case 'v': /* vowels */
281 c = CRACK_TOLOWER(input);
282 if (strchr("aeiou", c))
289 case 'c': /* consonants */
290 c = CRACK_TOLOWER(input);
291 if (strchr("bcdfghjklmnpqrstvwxyz", c))
298 case 'w': /* whitespace */
299 if (strchr("\t ", input))
306 case 'p': /* punctuation */
307 if (strchr(".`,:;'!?\"", input))
314 case 's': /* symbols */
315 if (strchr("$%%^&*()-_+=|\\[]{}#@/~", input))
321 /* LOGICAL GROUPINGS */
324 case 'l': /* lowercase */
332 case 'u': /* uppercase */
340 case 'a': /* alphabetic */
348 case 'x': /* alphanumeric */
356 case 'd': /* digits */
364 Debug(1, "MatchClass: unknown class %c\n", class);
376 PolyStrchr(const char *string, char class)
380 if (MatchClass(class, *string))
389 /* returns pointer to a swapped about copy */
391 PolySubst(const char *string, char class, char new)
394 static char area[STRINGSIZE];
398 *(ptr++) = (MatchClass(class, *string) ? new : *string);
405 /* returns pointer to a purged copy */
407 PolyPurge(const char *string, const char class)
410 static char area[STRINGSIZE];
414 if (!MatchClass(class, *string))
423 /* -------- BACK TO NORMALITY -------- */
426 Char2Int(char character)
428 if (isdigit(character))
430 return (character - '0');
431 } else if (islower(character))
433 return (character - 'a' + 10);
434 } else if (isupper(character))
436 return (character - 'A' + 10);
441 /* returns a pointer to a controlled Mangle */
443 Mangle(const char *input, const char *control)
449 static char area[STRINGSIZE * 2] = "";
450 char area2[STRINGSIZE * 2] = "";
456 min_to_shift = (j + 1) / 2;
459 min_to_shift = j / 2;
462 if (min_to_shift > 5)
467 for (ptr = control; *ptr; ptr++)
474 strcpy(area, Reverse(area));
477 strcpy(area, Uppercase(area));
480 strcpy(area, Lowercase(area));
482 case RULE_CAPITALISE:
483 strcpy(area, Capitalise(area));
486 strcpy(area, Pluralise(area));
489 strcat(area, Reverse(area));
498 Debug(1, "Mangle: '>' missing argument in '%s'\n", control);
502 limit = Char2Int(*(++ptr));
505 Debug(1, "Mangle: '>' weird argument in '%s'\n", control);
508 if (strlen(area) <= (size_t) limit)
517 Debug(1, "Mangle: '<' missing argument in '%s'\n", control);
521 limit = Char2Int(*(++ptr));
524 Debug(1, "Mangle: '<' weird argument in '%s'\n", control);
527 if (strlen(area) >= (size_t) limit)
536 Debug(1, "Mangle: prepend missing argument in '%s'\n", control);
541 strcpy(area2 + 1, area);
548 Debug(1, "Mangle: append missing argument in '%s'\n", control);
552 register char *string;
555 string[-1] = *(++ptr);
560 if (!ptr[1] || !ptr[2])
562 Debug(1, "Mangle: extract missing argument in '%s'\n", control);
569 start = Char2Int(*(++ptr));
570 length = Char2Int(*(++ptr));
571 if (start < 0 || length < 0)
573 Debug(1, "Mangle: extract: weird argument in '%s'\n", control);
577 for (i = 0; length-- && area2[start + i]; i++)
579 area[i] = area2[start + i];
581 /* cant use strncpy() - no trailing NUL */
585 case RULE_OVERSTRIKE:
586 if (!ptr[1] || !ptr[2])
588 Debug(1, "Mangle: overstrike missing argument in '%s'\n", control);
593 i = Char2Int(*(++ptr));
596 Debug(1, "Mangle: overstrike weird argument in '%s'\n",
610 if (!ptr[1] || !ptr[2])
612 Debug(1, "Mangle: insert missing argument in '%s'\n", control);
619 i = Char2Int(*(++ptr));
622 Debug(1, "Mangle: insert weird argument in '%s'\n",
638 /* THE FOLLOWING RULES REQUIRE CLASS MATCHING */
640 case RULE_PURGE: /* @x or @?c */
641 if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
643 Debug(1, "Mangle: delete missing arguments in '%s'\n", control);
645 } else if (ptr[1] != RULE_CLASS)
647 strcpy(area, Purge(area, *(++ptr)));
650 strcpy(area, PolyPurge(area, ptr[2]));
654 case RULE_SUBSTITUTE: /* sxy || s?cy */
655 if (!ptr[1] || !ptr[2] || (ptr[1] == RULE_CLASS && !ptr[3]))
657 Debug(1, "Mangle: subst missing argument in '%s'\n", control);
659 } else if (ptr[1] != RULE_CLASS)
661 strcpy(area, Substitute(area, ptr[1], ptr[2]));
665 strcpy(area, PolySubst(area, ptr[2], ptr[3]));
669 case RULE_MATCH: /* /x || /?c */
670 if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
672 Debug(1, "Mangle: '/' missing argument in '%s'\n", control);
674 } else if (ptr[1] != RULE_CLASS)
676 if (!strchr(area, *(++ptr)))
682 if (!PolyStrchr(area, ptr[2]))
689 case RULE_NOT: /* !x || !?c */
690 if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
692 Debug(1, "Mangle: '!' missing argument in '%s'\n", control);
694 } else if (ptr[1] != RULE_CLASS)
696 if (strchr(area, *(++ptr)))
702 if (PolyStrchr(area, ptr[2]))
710 * alternative use for a boomerang, number 1: a standard throwing
711 * boomerang is an ideal thing to use to tuck the sheets under
712 * the mattress when making your bed. The streamlined shape of
713 * the boomerang allows it to slip easily 'twixt mattress and
714 * bedframe, and it's curve makes it very easy to hook sheets
718 case RULE_EQUALS: /* =nx || =n?c */
719 if (!ptr[1] || !ptr[2] || (ptr[2] == RULE_CLASS && !ptr[3]))
721 Debug(1, "Mangle: '=' missing argument in '%s'\n", control);
726 if ((i = Char2Int(ptr[1])) < 0)
728 Debug(1, "Mangle: '=' weird argument in '%s'\n", control);
731 if (ptr[2] != RULE_CLASS)
741 if (!MatchClass(*ptr, area[i]))
750 if (area[0] && strlen(area) > (size_t) min_to_shift)
753 for (i = 1; area[i]; i++)
755 area[i - 1] = area[i];
762 if (area[0] && strlen(area) > (size_t) min_to_shift)
765 for (i = 1; area[i]; i++);
771 if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
773 Debug(1, "Mangle: '(' missing argument in '%s'\n", control);
777 if (ptr[1] != RULE_CLASS)
787 if (!MatchClass(*ptr, area[0]))
796 if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
798 Debug(1, "Mangle: ')' missing argument in '%s'\n", control);
804 for (i = 0; area[i]; i++);
814 if (ptr[1] != RULE_CLASS)
824 if (!MatchClass(*ptr, area[i]))
833 Debug(1, "Mangle: unknown command %c in %s\n", *ptr, control);
837 if (!area[0]) /* have we deweted de poor widdle fing away? */
845 PMatch(const char *control, const char *string)
847 while (*string && *control)
849 if (!MatchClass(*control, *string))
858 if (*string || *control)