/* attempts to crack a password by trying every possible combination   */
/*                                                                     */
/* this probably won't be terribly useful to you, as it takes an       */
/* obscenely long time to find any decent passwords.                   */
/*                                                                     */
/* requires structural get_opts.  to compile:                          */
/*    gcc -o pwcr pwcr.c -I<get_opts_dir> -L<get_opts_dir> -lget_opts  */
/*                                                                     */
/* ari edelkind (06/20/2001)                                           */
/* last modified 06/20/2001                                            */


#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "get_opts.h"

add_opt(opt_d,   "d",   "default-passwd", OPT_STRING, (opt_string_t)0, 0);
add_opt(opt_min, "min", "minimum-length", OPT_USHORT, (opt_ushort_t)0, 0);
add_opt(opt_max, "max", "maximum-length", OPT_USHORT, (opt_ushort_t)8, 0);

put_opts(options, &opt_d, &opt_min, &opt_max);

extern char *crypt();

char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()_+-=|\\/?<>.,[]{};:'\"";
/*
char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!$*(.";
*/

#define SECLEN 10000

void usage();
void run_crypto();
void cycle_pass();
void set_positions();
void die_badchar();
void success();

unsigned int section = 1; /* show first entry */

int main(argc, argv)
	int argc;
	char **argv;
{
	char **args;

	if (get_opts_errormatic(&args, argv+1, argc-1, options) != 1) usage();

	if (opt_d.v_opt.opt_string) {
		int tmpn;
		tmpn = strlen(opt_d.v_opt.opt_string);
		if (tmpn > opt_max.v_opt.opt_ushort ||
				tmpn < opt_min.v_opt.opt_ushort) {
			fprintf (stderr,
				"min/max password length criteria not met\n");
			usage();
		}
	}

	run_crypto(args[0]);

	return 0;
}

void run_crypto(char *e_passwd) {
	char passwd[opt_max.v_opt.opt_ushort + 1];
	char passwn[opt_max.v_opt.opt_ushort + 1];
	unsigned short pwlen = opt_min.v_opt.opt_ushort;
	char done1 = 0;
	char nchars = sizeof(chars) - 2; /* char 0 counts, \0 doesn't */

	memset (passwd, 0, opt_max.v_opt.opt_ushort+1);
	memset (passwn, 0, opt_max.v_opt.opt_ushort+1);

	if (opt_d.v_opt.opt_string) {
		pwlen = strlen(opt_d.v_opt.opt_string);
		memcpy(passwd, opt_d.v_opt.opt_string, (size_t)pwlen);

	} else  memset(passwd, chars[0], pwlen);

	while (pwlen <= opt_max.v_opt.opt_ushort) {
		if (done1) memset (passwd, chars[0], pwlen);
		else done1 = 1;
		set_positions (passwn, passwd, chars);
		cycle_pass (e_passwd, passwn, passwd, chars, nchars, pwlen);
		pwlen++;
	}

	printf ("\npassword not found.\n");
	_exit(1);

}

void cycle_pass (e_passwd, passwn, passwd, chars, nchars, pwlen)
	char *e_passwd;
	char *passwn;
	char *passwd;
	char *chars;
	char nchars;
	unsigned short pwlen;
{
	register unsigned int i1 = 0;

	if (!pwlen) return;

	for (;;) {

		if (!--section) {
			printf ("\rcurrent: %s", passwd);
			fflush(stdout);
			section = SECLEN;
		}
		if (!strcmp(crypt(passwd, e_passwd), e_passwd)) success(passwd);
		i1 = 0;
		for (;;) {
			if (passwn[i1] != nchars) break;
			if (i1 == (pwlen - 1)) return;
			passwd[i1] = chars[0];
			passwn[i1++] = 0;
		}

		passwd[i1] = chars[(int)++(passwn[i1])];

	}

}

void set_positions (sp, s, cs)
	char *sp;
	char *s;
	char *cs;
{
	char *idx;
	char c;

	while ((c = *s)) {
		if (!(idx = index (cs, (int)c))) die_badchar(c);
		*sp = (char)(idx - cs);
		s++; sp++;
	}
}


void usage(void) {
	fprintf (stderr, "usage: pwcr [-d default] [-min minlen] [-max maxlen] encrypted_string\n");
	_exit(1);
}

void die_badchar(c)
	char c;
{
	fprintf (stderr, "character %c is in character list\n", c);
	_exit(1);
}

void success(passwd)
	char *passwd;
{
	printf ("\npassword: %s\n", passwd);
	_exit(0);
}
