diff --git a/src/dynamic.h b/src/dynamic.h index 873b6f3..136e132 100644 --- a/src/dynamic.h +++ b/src/dynamic.h @@ -149,6 +149,9 @@ typedef struct DYNAMIC_Setup_t int SaltLenX86; // if zero, then use salt len of SSE } DYNAMIC_Setup; +/* See dynamic_fmt.c for description */ +extern int dynamic_allow_rawhash_fixup; + int dynamic_SETUP(DYNAMIC_Setup *, struct fmt_main *pFmt); int dynamic_IS_VALID(int i, int force); int dynamic_real_salt_length(struct fmt_main *pFmt); diff --git a/src/dynamic_fmt.c b/src/dynamic_fmt.c index ac97e23..9baf973 100644 --- a/src/dynamic_fmt.c +++ b/src/dynamic_fmt.c @@ -303,7 +303,7 @@ unsigned int m_count; // that if set to true, we will perform a 1 time check within the valid function. If at // that time we find out that we are cracking (or showing, etc) that we will accept lines // that are either format of $dynamic_0$hhhhhh...32 or simply in the format of hhhhhhh..32 -static int m_allow_rawhash_fixup = 0; +int dynamic_allow_rawhash_fixup = 0; // this one IS in the private_dat, but since it is accessed SO much, we pull it // out prior to 'internal' processing. The others are accessed right from @@ -7478,13 +7478,13 @@ int dynamic_Register_formats(struct fmt_main **ptr) if (options.format && options.subformat && !strcmp(options.format, "dynamic") && !strncmp(options.subformat, "dynamic_", 8)) sscanf(options.subformat, "dynamic_%d", &single); if (options.dynamic_bare_hashes_always_valid == 'Y') - m_allow_rawhash_fixup = 1; + dynamic_allow_rawhash_fixup = 1; else if (options.dynamic_bare_hashes_always_valid != 'N' && cfg_get_bool(SECTION_OPTIONS, NULL, "DynamicAlwaysUseBareHashes", 1)) - m_allow_rawhash_fixup = 1; + dynamic_allow_rawhash_fixup = 1; if (single != -1) { // user wanted only a 'specific' format. Simply load that one. - m_allow_rawhash_fixup = 1; + dynamic_allow_rawhash_fixup = 1; if (dynamic_IS_VALID(single, 1) == 0) return 0; pFmts = mem_alloc_tiny(sizeof(pFmts[0]), MEM_ALIGN_WORD); @@ -7568,7 +7568,7 @@ struct fmt_main *dynamic_THIN_FORMAT_LINK(struct fmt_main *pFmt, char *ciphertex struct fmt_main *pFmtLocal; static char subformat[17], *cp; - m_allow_rawhash_fixup = 0; + dynamic_allow_rawhash_fixup = 0; strncpy(subformat, ciphertext, 16); subformat[16] = 0; cp = strchr(&subformat[9], '$'); @@ -7683,7 +7683,7 @@ static char *FixupIfNeeded(char *ciphertext, private_subformat_data *pPriv) { if (!ciphertext || *ciphertext == 0 || *ciphertext == '*') return ciphertext; - if (m_allow_rawhash_fixup && strncmp(ciphertext, "$dynamic_", 9) && looks_like_raw_hash(ciphertext, pPriv)) + if (dynamic_allow_rawhash_fixup && strncmp(ciphertext, "$dynamic_", 9) && looks_like_raw_hash(ciphertext, pPriv)) { static char __ciphertext[512+24]; if (pPriv->pSetup->flags & MGF_SALTED) { diff --git a/src/john.c b/src/john.c index a5dbd97..2570cf4 100644 --- a/src/john.c +++ b/src/john.c @@ -967,7 +967,7 @@ static void john_load(void) ldr_show_pw_file(&database, current->data); } while ((current = current->next)); - if (john_main_process) + if (john_main_process && !options.loader.showtypes) printf("%s%d password hash%s cracked, %d left\n", database.guess_count ? "\n" : "", database.guess_count, diff --git a/src/loader.c b/src/loader.c index 777cece..988ef57 100644 --- a/src/loader.c +++ b/src/loader.c @@ -339,8 +339,17 @@ static int ldr_split_line(char **login, char **ciphertext, /* Check for NIS stuff */ if ((!strcmp(*login, "+") || !strncmp(*login, "+@", 2)) && strlen(*ciphertext) < 10 && strncmp(*ciphertext, "$dummy$", 7) - && strncmp(*ciphertext, "$0$", 3)) + && strncmp(*ciphertext, "$0$", 3)) { + if (db_opts->showtypes) { + int fs = db_opts->field_sep_char; + printf("%s%c%s%c2%c\n", + *login, + fs, *ciphertext, + fs, /* 2, */ + fs); + } return 0; + } if (!**ciphertext && !line) { /* Possible hash on a line on its own (no colons) */ @@ -363,8 +372,17 @@ static int ldr_split_line(char **login, char **ciphertext, (*ciphertext)--; if (p - *ciphertext == 12 && *ciphertext - *login == 1) (*ciphertext)--; - if (p - *ciphertext < 13) + if (p - *ciphertext < 13) { + if (db_opts->showtypes) { + int fs = db_opts->field_sep_char; + printf("%c%s%c3%c\n", + /* empty, */ + fs, *ciphertext, + fs, /* 3, */ + fs); + } return 0; + } } *p = 0; fields[0] = *login = no_username; @@ -446,6 +464,152 @@ static int ldr_split_line(char **login, char **ciphertext, if (ldr_check_list(db_opts->groups, gid, gid)) return 0; if (ldr_check_shells(db_opts->shells, shell)) return 0; + /* TODO: Should john_main_process be checked? */ + if (db_opts->showtypes) { + /* TODO: Do we need to print 'source' var? */ + /* TODO: Forbid --salt= and --cost= selectors. */ + int fs = db_opts->field_sep_char; + /* Flag: no formats can load the hash. */ + int not_valid = 1; + /* Flag: the format is the first valid format. The first + * format should not print the first field separator. */ + int first_format = 1; + /* \0 and \n are not possible in any field. */ + /* field_sep_char should not be possible in any field too. + * But it is possible with --field-separator-char=/ due to + * default value "/" for home field. Also ? may come from + * empty login. */ + /* Flag: no fields contain \n or field separator. */ + int bad_char = 0; + int bare_always_valid = 0; + /* TODO: The check was copied from dynamic_fmt.c. Make a function? */ + if ((options.dynamic_bare_hashes_always_valid == 'Y') + || (options.dynamic_bare_hashes_always_valid != 'N' + && cfg_get_bool(SECTION_OPTIONS, NULL, "DynamicAlwaysUseBareHashes", 1))) + bare_always_valid = 1; + /* We output 1 or 0, so 0 and 1 are bad field separators. */ + /* TODO: Maybe fail right now? */ + if (fs == '0' || fs == '1') + bad_char = 1; +#define check_field_separator(str) bad_char |= strchr((str), fs) || strchr((str), '\n') + /* To suppress warnings, particularly from + * generic crypt's valid() (c3_fmt.c). */ + /* TODO: Should a separate var be made? */ + ldr_in_pot = 1; + /* The format: + * Once for each hash even if it can't be loaded (7 fields): + * login, + * ciphertext, + * uid, + * gid, + * gecos, + * home, + * shell. + * For each valid format (may be nothing): + * label, + * is format disabled? (1/0), + * is format dynamic? (1/0), + * does format use the ciphertext field as is? (1/0), + * canonical hash or hashes (if there are several parts). + * All fields above are separated by field_sep_char. + * Formats are separated by empty field. + * Additionally on the end: + * separator, + * line consistency mark (0/1/2/3): + * 0 - the line is consistent and can be parsed easily, + * 1 - field separator char occurs in fields, line can't + * be parsed easily, + * 2 - the line was skipped as bad NIS stuff, only login + * and ciphertext are shown, + * 3 - the line was skipped parsing descrypt with + * invalid salt, only ciphertext is shown (together + * with empty login); empty lines fall here, + * separator. + * The additional field_sep_char at the end of line does not + * break numeration of fields but allows parser to get + * field_sep_char from the line. + * A parser have to check the last 3 chars. + * If the format does not use the ciphertext field as is, + * then a parser have to match input line with output line + * by number of line. + * TODO: do we guarantee that input lines and output lines matches 1 to 1? + * TODO: field_sep_char == '\n' should be banned. + * TODO: make examples. + */ + printf("%s%c%s%c%s%c%s%c%s%c%s%c%s", + *login, + fs, *ciphertext, + fs, *uid, + fs, gid, + fs, *gecos, + fs, *home, + fs, shell); + check_field_separator(*login); + check_field_separator(*ciphertext); + check_field_separator(*uid); + check_field_separator(gid); + check_field_separator(*gecos); + check_field_separator(*home); + check_field_separator(shell); + /* Fields above may be empty. */ + /* Empty fields are separators after this point. */ + alt = fmt_list; + do { + char *prepared; + int disabled = 0; + int prepared_eq_ciphertext; + int valid; + int part; + int is_dynamic = ((alt->params.flags & FMT_DYNAMIC) == FMT_DYNAMIC); +/* We enforce DynamicAlwaysUseBareHashes for each format. By default + * dynamics do that only if a bare hash occurs on the first line. */ + if (bare_always_valid) + dynamic_allow_rawhash_fixup = 1; + /* We don't skip generic crypt. */ + /* Format disabled in john.conf */ + if (cfg_get_bool(SECTION_DISABLED, SUBSECTION_FORMATS, + alt->params.label, 0)) { + /* TODO: should not I skip disabled formats? */ + disabled = 1; + } + /* TODO: May methods modify given fields/strings? Is there a selftest against that? Though it may depend on data, it'd be good to check it here. */ + /* prepared is not equal to *ciphertext for nt in pwdump format */ + prepared = alt->methods.prepare(fields, alt); + if (!prepared) + continue; + prepared_eq_ciphertext = (*ciphertext == prepared || !strcmp(*ciphertext, prepared)); + valid = alt->methods.valid(prepared, alt); + if (!valid) + continue; + not_valid = 0; + ldr_set_encoding(alt); + /* TODO: alt->params.label is the name of format? */ + /* TODO: Is it guaranteed that format's label does not contain spaces? */ + /* Empty field between valid formats */ + if (first_format) { + printf("%c", fs); + first_format = 0; + } + printf("%c%s%c%d%c%d%c%d", + fs, alt->params.label, + fs, disabled, + fs, is_dynamic, + fs, prepared_eq_ciphertext); + check_field_separator(alt->params.label); + /* Canonical hash or hashes (like halves of LM) */ + for (part = 0; part < valid; part++) { + char *splitted = alt->methods.split(prepared, part, alt); + /* TODO: handle splitted == 0; it should be a bug then */ + printf("%c%s", fs, splitted); + check_field_separator(splitted); + } + } while ((alt = alt->next)); + printf("%c%d%c\n", fs, bad_char, fs); + return 0; +#undef check_field_separator + } + + if (*format) { char *prepared; int valid; diff --git a/src/loader.h b/src/loader.h index c74e844..ef2463d 100644 --- a/src/loader.h +++ b/src/loader.h @@ -198,6 +198,9 @@ struct db_options { /* if --show=left is used, john dumps the non-cracked hashes */ int showuncracked; +/* if --show=types is used, john shows all hashes in machine readable form */ + int showtypes; + /* Field separator (normally ':') */ char field_sep_char; diff --git a/src/options.c b/src/options.c index 03ee4bc..8ed8527 100644 --- a/src/options.c +++ b/src/options.c @@ -412,6 +412,7 @@ void opt_print_hidden_usage(void) puts("--mkv-stats=FILE \"Markov\" stats file (see doc/MARKOV)"); puts("--reject-printable reject printable binaries"); puts("--verbosity=N change verbosity (1-5, default 3)"); + puts("--show=types show some information about hashes in file (machine readable)"); puts("--skip-self-tests skip self tests"); puts("--stress-test[=TIME] loop self tests forever"); puts("--input-encoding=NAME input encoding (alias for --encoding)"); @@ -905,8 +906,11 @@ void opt_init(char *name, int argc, char **argv, int show_usage) // instead of normal loading if we are in 'normal' show mode) options.flags &= ~FLG_SHOW_CHK; } + else if (!strcasecmp(show_uncracked_str, "types")) { + options.loader.showtypes = 1; + } else { - fprintf(stderr, "Invalid option in --show switch.\nOnly --show or --show=left are valid\n"); + fprintf(stderr, "Invalid option in --show switch.\nOnly --show , --show=left or --show=types are valid\n"); error(); } }