/* * This file is part of John the Ripper password cracker, * Copyright (c) 1996-98 by Solar Designer */ #include #include #include #include #include #include "config.h" #include "rpp.h" #include "rules.h" char *rules_errors[] = { NULL, /* No error */ "Unexpected end of rule", "Unknown command", "Invalid position code", "Unknown character class code", "Unknown rule reject flag" }; int rules_errno, rules_line; int rules_debug; char *rules_classes[0x100], rules_length[0x100]; int rules_max_length = 0; #define CONV_SOURCE \ "`1234567890-=\\qwertyuiop[]asdfghjkl;'zxcvbnm,./" \ "~!@#$%^&*()_+|QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?" #define CONV_SHIFT \ "~!@#$%^&*()_+|QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?" \ "`1234567890-=\\qwertyuiop[]asdfghjkl;'zxcvbnm,./" #define CONV_INVERT \ "`1234567890-=\\QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./" \ "~!@#$%^&*()_+|qwertyuiop{}asdfghjkl:\"zxcvbnm<>?" #define CONV_VOWELS \ "`1234567890-=\\QWeRTYuioP[]aSDFGHJKL;'ZXCVBNM,./" \ "~!@#$%^&*()_+|QWeRTYuioP{}aSDFGHJKL:\"ZXCVBNM<>?" #define CONV_RIGHT \ "1234567890-=\\\\wertyuiop[]]sdfghjkl;''xcvbnm,./\\" \ "!@#$%^&*()_+||WERTYUIOP{}}SDFGHJKL:\"\"XCVBNM<>?|" #define CONV_LEFT \ "``1234567890-=qqwertyuiop[aasdfghjkl;zzxcvbnm,." \ "~~!@#$%^&*()_+QQWERTYUIOP{AASDFGHJKL:ZZXCVBNM<>" #define CHARS_LOWER \ "abcdefghijklmnopqrstuvwxyz" #define CHARS_UPPER \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" #define CHARS_DIGITS \ "0123456789" char *conv_source = CONV_SOURCE; char *conv_shift, *conv_invert, *conv_vowels, *conv_right, *conv_left; char *conv_tolower, *conv_toupper; #define INVALID_LENGTH 0x7F #define RULE (*rule++) #define LAST (*(rule - 1)) #define NEXT (*rule) #define REJECT { \ if (!rules_debug) return (NULL); \ } #define VALUE { \ if (!(value = RULE)) { \ rules_errno = RULES_ERROR_END; \ return (NULL); \ } \ } #define POSITION { \ if ((pos = rules_length[(ARCH_INDEX)RULE]) == INVALID_LENGTH) { \ if (LAST) \ rules_errno = RULES_ERROR_POSITION; \ else \ rules_errno = RULES_ERROR_END; \ return (NULL); \ } \ } #define CLASS(start, true, false) { \ if ((value = RULE) == '?') { \ if (!(class = rules_classes[(ARCH_INDEX)RULE])) { \ if (LAST) \ rules_errno = RULES_ERROR_CLASS; \ else \ rules_errno = RULES_ERROR_END; \ return (NULL); \ } \ for (pos = (start); (ARCH_INDEX)in[pos]; pos++) \ if (class[(ARCH_INDEX)in[pos]]) { \ true; \ } else { \ false; \ } \ } else { \ if (!value) { \ rules_errno = RULES_ERROR_END; \ return (NULL); \ } \ for (pos = (start); (ARCH_INDEX)in[pos]; pos++) \ if (in[pos] == value) { \ true; \ } else { \ false; \ } \ } \ } #define SKIP_CLASS { \ VALUE \ if (value == '?') VALUE \ } #define CONV(conv) { \ for (pos = 0; (out[pos] = (conv)[(ARCH_INDEX)in[pos]]); pos++); \ } void rules_init_class(char name, char *valid) { char *pos, inv, *tmp; if ((tmp = malloc(0x100)) == NULL) err(1, "malloc"); rules_classes[(ARCH_INDEX)name] = tmp; memset(rules_classes[(ARCH_INDEX)name], 0, 0x100); for (pos = valid; (ARCH_INDEX)*pos; pos++) rules_classes[(ARCH_INDEX)name][(ARCH_INDEX)*pos] = 1; if (name >= 'a' && name <= 'z') { inv = name & ~0x20; if ((tmp = malloc(0x100)) == NULL) err(1, "malloc"); rules_classes[(ARCH_INDEX)inv] = tmp; memset(rules_classes[(ARCH_INDEX)inv], 1, 0x100); for (pos = valid; (ARCH_INDEX)*pos; pos++) rules_classes[(ARCH_INDEX)inv][(ARCH_INDEX)*pos] = 0; } } void rules_init_classes() { memset(rules_classes, 0, sizeof(rules_classes)); rules_init_class('?', "?"); rules_init_class('v', "aeiouAEIOU"); rules_init_class('c', "bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ"); rules_init_class('w', " \t"); rules_init_class('p', ".,:;'\"?!`"); rules_init_class('s', "$%^&*()-_+=|\\<>[]{}#@/~"); rules_init_class('l', CHARS_LOWER); rules_init_class('u', CHARS_UPPER); rules_init_class('d', CHARS_DIGITS); rules_init_class('a', CHARS_LOWER CHARS_UPPER); rules_init_class('x', CHARS_LOWER CHARS_UPPER CHARS_DIGITS); } char *rules_init_conv(char *src, char *dst) { char *conv; int pos; conv = malloc(0x100); if (conv == NULL) err(1, "malloc"); for (pos = 0; pos < 0x100; pos++) conv[pos] = pos; while (*src) conv[(ARCH_INDEX)*src++] = *dst++; return (conv); } void rules_init_convs() { conv_shift = rules_init_conv(conv_source, CONV_SHIFT); conv_invert = rules_init_conv(conv_source, CONV_INVERT); conv_vowels = rules_init_conv(conv_source, CONV_VOWELS); conv_right = rules_init_conv(conv_source, CONV_RIGHT); conv_left = rules_init_conv(conv_source, CONV_LEFT); conv_tolower = rules_init_conv(CHARS_UPPER, CHARS_LOWER); conv_toupper = rules_init_conv(CHARS_LOWER, CHARS_UPPER); } void rules_init_length(int max_length) { int c; memset(rules_length, INVALID_LENGTH, sizeof(rules_length)); for (c = '0'; c <= '9'; c++) rules_length[c] = c - '0'; for (c = 'a'; c <= 'z'; c++) rules_length[c] = c - ('a' - 10); for (c = 'A'; c <= 'Z'; c++) rules_length[c] = c - ('A' - 10); rules_length['*'] = rules_max_length = max_length; rules_length['-'] = max_length - 1; rules_length['+'] = max_length + 1; } void rules_init(int max_length) { if (rules_max_length) return; rules_init_classes(); rules_init_convs(); rules_init_length(max_length); rules_debug = 0; rules_errno = RULES_ERROR_NONE; } char *rules_reject(char *rule, void *db) { while (RULE) switch (LAST) { case ':': case ' ': case '\t': break; case '-': switch (RULE) { case 'c': /* if (!db) continue; if (db->format->params.flags & FMT_CASE) continue; return (NULL); */ case '8': /* if (!db) continue; if (db->format->params.flags & FMT_8_BIT) continue; return (NULL); */ case 's': /* if (!db) continue; if (db->options->flags & DB_SPLIT) continue; return (NULL); */ continue; case '\0': rules_errno = RULES_ERROR_END; return (NULL); default: rules_errno = RULES_ERROR_REJECT; return (NULL); } default: return (rule - 1); } return (rule - 1); } char * rules_apply(char *word, char *rule, int split) { static char buffer[3][RULE_WORD_SIZE * 2]; char *in = buffer[0], *out = buffer[1]; char memory[RULE_WORD_SIZE]; int memory_empty, which; char value, *class; int pos, out_pos; int count, required; strncpy(in, word, RULE_WORD_SIZE); memory_empty = 1; which = 0; if (NEXT != ':' || *(rule + 1)) while (RULE) { if (!in[0]) REJECT in[RULE_WORD_SIZE - 1] = 0; switch (LAST) { /* Crack v4.1 rules */ case ':': case ' ': case '\t': out = in; break; case '<': POSITION if ((int)strlen(in) < pos) out = in; else REJECT break; case '>': POSITION if ((int)strlen(in) > pos) out = in; else REJECT break; case 'l': CONV(conv_tolower) break; case 'u': CONV(conv_toupper) break; case 'c': pos = 0; if ((out[0] = conv_toupper[(ARCH_INDEX)in[0]])) while (in[++pos]) out[pos] = conv_tolower[(ARCH_INDEX)in[pos]]; out[pos] = 0; if (out[0] == 'M' && out[1] == 'c') out[2] = conv_toupper[(ARCH_INDEX)out[2]]; break; case 'r': *(out += strlen(in)) = 0; while (*in) *--out = *in++; break; case 'd': strcpy(out, in); strcat(out, in); break; case 'f': out = in; out[pos = strlen(out) << 1] = 0; while (*in) out[--pos] = *in++; break; case 'p': out = in; if (!out[0] || !out[1]) break; if (strchr("hsx", out[pos = strlen(out) - 1])) strcat(out, "es"); else if (out[pos] == 'f' && out[pos - 1] != 'f') strcpy(&out[pos], "ves"); else if (pos > 1 && out[pos] == 'e' && out[pos - 1] == 'f') strcpy(&out[pos - 1], "ves"); else if (pos > 1 && out[pos] == 'y') { if (strchr("aeiou", out[pos - 1])) strcat(out, "s"); else strcpy(&out[pos], "ies"); } else strcat(out, "s"); break; case '$': VALUE out = in; out[pos = strlen(out)] = value; out[pos + 1] = 0; break; case '^': VALUE out[0] = value; strcpy(&out[1], in); break; case 'x': POSITION if (pos < (int)strlen(in)) { in += pos; POSITION strlcpy(out, in, pos + 1); } else { POSITION out[0] = 0; } break; case 'i': POSITION VALUE if (pos < (out_pos = strlen(in))) { memcpy(out, in, pos); out[pos] = value; strcpy(&out[pos + 1], &in[pos]); } else { out = in; out[out_pos] = value; out[out_pos + 1] = 0; } break; case 'o': POSITION VALUE out = in; if (out[pos]) out[pos] = value; break; case 's': out = in; CLASS(0, out[pos] = NEXT, {}) VALUE break; case '@': out_pos = 0; CLASS(0, {}, out[out_pos++] = in[pos]) out[out_pos] = 0; break; case '!': CLASS(0, REJECT, {}) out = in; break; case '/': CLASS(0, break, {}) if (!in[pos]) REJECT out = in; break; case '=': POSITION if (pos >= (int)strlen(in)) { SKIP_CLASS REJECT } else CLASS(pos, break, REJECT) out = in; break; /* Crack v5.0 rules */ case '[': if (in[0]) strcpy(out, &in[1]); else out[0] = 0; break; case ']': out = in; if (out[0]) out[strlen(out) - 1] = 0; break; case 'C': pos = 0; if ((out[0] = conv_tolower[(ARCH_INDEX)in[0]])) while (in[++pos]) out[pos] = conv_toupper[(ARCH_INDEX)in[pos]]; out[pos] = 0; if (out[0] == 'm' && out[1] == 'C') out[2] = conv_tolower[(ARCH_INDEX)out[2]]; break; case 't': CONV(conv_invert) break; case '(': CLASS(0, break, REJECT) out = in; break; case ')': if (!in[0]) { SKIP_CLASS REJECT } else CLASS(strlen(in) - 1, break, REJECT) out = in; break; case '\'': POSITION (out = in)[pos] = 0; break; case '%': POSITION count = 0; required = pos; CLASS(0, if (++count >= required) break, {}) if (count < required) REJECT out = in; break; /* Rules added in John */ case 'T': POSITION out = in; out[pos] = conv_invert[(ARCH_INDEX)out[pos]]; break; case 'D': POSITION if (pos >= (int)strlen(in)) out = in; else { memcpy(out, in, pos); strcpy(&out[pos], &in[pos + 1]); } break; case '{': if (in[0]) { strcpy(out, &in[1]); in[1] = 0; strcat(out, in); } else out[0] = 0; break; case '}': if (in[0]) { out[0] = in[pos = strlen(in) - 1]; in[pos] = 0; strcpy(&out[1], in); } else out[0] = 0; break; case 'S': CONV(conv_shift); break; case 'V': CONV(conv_vowels); break; case 'R': CONV(conv_right); break; case 'L': CONV(conv_left); break; case 'P': out = in; if ((pos = strlen(out) - 1) < 2) break; if (out[pos] == 'd' && out[pos - 1] == 'e') break; if (out[pos] == 'y') out[pos] = 'i'; else if (strchr("bgp", out[pos]) && !strchr("bgp", out[pos - 1])) { out[pos + 1] = out[pos]; out[pos + 2] = 0; } if (out[pos] == 'e') strcat(out, "d"); else strcat(out, "ed"); break; case 'I': out = in; if ((pos = strlen(out) - 1) < 2) break; if (out[pos] == 'g' && out[pos - 1] == 'n' && out[pos - 2] == 'i') break; if (strchr("aeiou", out[pos])) strcpy(&out[pos], "ing"); else { if (strchr("bgp", out[pos]) && !strchr("bgp", out[pos - 1])) { out[pos + 1] = out[pos]; out[pos + 2] = 0; } strcat(out, "ing"); } break; case 'M': strncpy(memory, (out = in), rules_max_length); memory_empty = 0; break; case 'Q': if (memory_empty) { if (!strncmp(word, in, rules_max_length)) REJECT } else if (!strncmp(memory, in, rules_max_length)) REJECT out = in; break; /* Additional "single crack" mode rules */ case '1': if (split < 0) { rules_errno = RULES_ERROR_UNKNOWN; return (NULL); } if (!split) REJECT if (which) strcpy(buffer[2], in); else strlcpy(buffer[2], &word[split], RULE_WORD_SIZE); strlcpy(out, word, split + 1); which = 1; break; case '2': if (split < 0) { rules_errno = RULES_ERROR_UNKNOWN; return (NULL); } if (!split) REJECT if (which) strcpy(buffer[2], in); else strlcpy(buffer[2], word, split + 1); strlcpy(out, &word[split], RULE_WORD_SIZE); which = 2; break; case '+': switch (which) { case 1: strcat(out = in, buffer[2]); break; case 2: strcpy(out, buffer[2]); strcat(out, in); break; default: rules_errno = RULES_ERROR_UNKNOWN; return (NULL); } which = 0; break; default: rules_errno = RULES_ERROR_UNKNOWN; return (NULL); } if (!out[0]) REJECT if ((in = out) == buffer[1]) out = buffer[0]; else out = buffer[1]; } switch (which) { case 1: strcat(in, buffer[2]); break; case 2: strcpy(out, buffer[2]); strcat(out, in); in = out; } in[rules_max_length] = 0; return (in); } int rules_check(struct rpp_context *start, int split) { struct rpp_context ctx; char *rule; int count; rules_errno = RULES_ERROR_NONE; memcpy(&ctx, start, sizeof(ctx)); rules_line = ctx.input->number; count = 0; rules_debug = 1; while ((rule = rpp_next(&ctx))) if ((rule = rules_reject(rule, NULL))) { rules_apply("", rule, split); if (rules_errno) break; if (ctx.input) rules_line = ctx.input->number; count++; } rules_debug = 0; return (rules_errno ? 0 : count); } int rules_count(struct rpp_context *start, int split) { int count; if (!(count = rules_check(start, split))) { fprintf(stderr, "Invalid rule in %s at line %d: %s\n", RULES_NAME, rules_line, rules_errors[rules_errno]); exit(1); } return (count); }