/*
 * This program is copyright Alec Muffett 1991 except for some portions of
 * code in "crack-fcrypt.c" which are copyright Robert Baldwin, Icarus Sparry
 * and Alec Muffett.  The author(s) disclaims all responsibility or liability
 * with respect to it's usage or its effect upon hardware or computer
 * systems, and maintain copyright as set out in the "LICENCE" document which
 * accompanies distributions of Crack v4.0 and upwards.
 */

#include "crack.h"

#define DOTFILESIZE	1024
#define WORDSTACKSIZE	512

/*
 * crack-pwc.c - an optimised password cracker. (c) ADE Muffett, Feb 1992. If
 * this won't break your password file, it's unlikely that anything else
 * will.
 */

/* trap a signal on shutdown */

void
CatchTERM ()
{
    /* bury magnets */
    Log ("Caught a SIGTERM! Commiting suicide...\n");
    /* swallow the rapture */
    Log ("<argh!>\n");
    /* let's gather feathers */
    sync ();
    /* don't fall on me */
    exit (0);
    /* 'Fall on Me' by R.E.M. */
}
/* jump ':' separated fields in an input */

char *
PWSkip (p)
    register char *p;
{
    while (*p && *p != ':')
    {
	p++;
    }
    if (*p)
    {
	*p++ = '\0';
    }
    return (p);
}

char *
Archive (myword)
    register char *myword;
{
    register int i;
    register struct DICT *ptr;
    static struct DICT *arch_root;

    for (ptr = arch_root; ptr; ptr = ptr -> next)
    {
	if (!STRCMP (ptr -> word, myword))
	{
	    return (ptr -> word);
	}
    }

    i = strlen (myword);

    ptr = (struct DICT *) malloc (sizeof (struct DICT) + i);

    if (ptr)
    {
	strcpy (ptr -> word, myword);
	ptr -> word[i] = '\0';
	ptr -> next = arch_root;
	arch_root = ptr;
    } else
    {
	Log ("Archive/malloc() failed! Fatal lack of memory!\n");
	exit (1);
    }

    return (ptr -> word);
}
/* parse and store a password entry */

struct USER *
Parse (buffer)
    register char *buffer;
{
    register char *p;
    register struct USER *retval;

    retval = (struct USER *) malloc (sizeof (struct USER));

    if (!retval)
    {
	Log ("Parse/malloc() failed! Fatal lack of memory!\n");
	exit (1);
    }
    retval -> next = retval -> across = NULL;
    retval -> passwd_txt = NULL;
    retval -> done = 0;
    Trim (buffer);

    p = PWSkip (buffer);
    retval -> filename = Archive (buffer);

    p = Clone (p);
    if (!p)
    {
	Log ("Parse/Clone() failed! Fatal lack of memory!\n");
	exit (1);
    }
    retval -> passwd.pw_name = p;

    p = PWSkip (p);
    retval -> passwd.pw_passwd = p;

    p = PWSkip (p);
    retval -> passwd.pw_uid = atoi (p);

    p = PWSkip (p);
    retval -> passwd.pw_gid = atoi (p);

    p = PWSkip (p);
    retval -> passwd.pw_gecos = p;

    p = PWSkip (p);
    retval -> passwd.pw_dir = p;

    p = PWSkip (p);
    retval -> passwd.pw_shell = p;

    return (retval);
}
/* load pre-formatted password entries off stdin into linked list */

int
LoadData ()
{
    int i;
    char *ptr;
    char salt[2];
    char buffer[STRINGSIZE];
    long int numlines;
    long int numentries;
    register struct USER *new_element;
    register struct USER *current_line;

    numlines = 0L;
    numentries = 0L;
    current_line = NULL;
    salt[0] = salt[1] = '*';

    while (fgets (buffer, STRINGSIZE, stdin))
    {
	if (!*buffer || isspace (*buffer))
	{
	    continue;
	}
	new_element = Parse (buffer);
	ptr = new_element -> passwd.pw_passwd;

	if (!ptr[0])
	{
	    Log ("Warning! %s (%s in %s) has a NULL password!\n",
		 new_element -> passwd.pw_name,
		 new_element -> passwd.pw_shell,
		 new_element -> filename);
	    continue;
	}
	if (strchr (ptr, '*') ||
	    strchr (ptr, '!') ||
	    strchr (ptr, ' '))
	{
	    Log ("User %s (in %s) has a locked password:- %s\n",
		 new_element -> passwd.pw_name,
		 new_element -> filename,
		 new_element -> passwd.pw_passwd);
	    continue;
	}
	i = strlen (ptr);

	if (i < 13)
	{
	    Log ("User %s (in %s) has a short pw_passwd field - skipping.\n",
		 new_element -> passwd.pw_name,
		 new_element -> filename);
	    continue;
	}
	if (i > 13)
	{
	    Log ("User %s (in %s) has a long pw_passwd field - truncating.\n",
		 new_element -> passwd.pw_name,
		 new_element -> filename);
	    ptr[13] = '\0';
	}
	numentries++;

	if (ptr[0] == salt[0] && ptr[1] == salt[1])
	{
	    new_element -> across = current_line;
	    current_line = new_element;
	} else
	{
	    if (current_line)
	    {
		current_line -> next = userroot;
	    }
	    userroot = current_line;
	    current_line = new_element;
	    numlines++;
	    salt[0] = ptr[0];
	    salt[1] = ptr[1];
	}
    }

    if (current_line)		/* last one tends to hang about */
    {
	current_line -> next = userroot;
	userroot = current_line;
	numlines++;
    }
    --numlines;

    if (numentries)
    {
	Log ("Loaded %ld password entries with %ld different salts: %d%%\n",
	     numentries,
	     numlines,
	     ((numlines * 100) / numentries));
    } else
    {
	Log ("No input supplied: everything removed by feedback ?\n");
    }
    return (numentries);
}
/* and load rules from a standard file into a similar list */

int
LoadRules (file, rootpos)
    char *file;
    struct RULE **rootpos;
{
    FILE *fp;
    int numrules;
    struct RULE fencepost;
    register struct RULE *addinto;
    register struct RULE *scratch;
    char buffer[STRINGSIZE];

    if (!(fp = fopen (file, "r")))
    {
	Log ("cannot open rulefile %s\n", file);
	perror (file);
	return (-1);
    }
    numrules = 0;
    addinto = &fencepost;
    addinto -> next = (struct RULE *) 0;

    while (fgets (buffer, STRINGSIZE, fp))
    {
	Trim (buffer);

	if (!buffer[0] || buffer[0] == '#')
	{
	    continue;
	}
	scratch = (struct RULE *) malloc (sizeof (struct RULE));

	if (!scratch)
	{
	    Log ("LoadRules/malloc() failed! Fatal lack of memory!\n");
	    exit (1);
	}
	scratch -> rule = Clone (buffer);

	if (!scratch -> rule)
	{
	    Log ("LoadRules/Clone() failed! Fatal lack of memory!\n");
	    exit (1);
	}
	scratch -> next = (struct RULE *) 0;
	addinto -> next = scratch;
	addinto = scratch;
	numrules++;
    }

    fclose (fp);
    Log ("Loaded %d rules from '%s'.\n", numrules, file);
    *rootpos = fencepost.next;
    return (numrules);
}
/* load a dictionary into a linked list, and sort it */

long int
LoadDict (file, rule, contdict)
    char *file;
    char *rule;
    int contdict;
{
    int i;
    int memfilled;
    long int nelem;
    long int rejected;
    register char *mangle;
    register struct DICT *scratch;
    char pipebuff[STRINGSIZE];

    static FILE *fp;
    char buffer[STRINGSIZE];

    if (contdict && fp)
    {
	goto files_open;
    }
#ifdef COMPRESSION
    if (!Suffix (file, ".Z"))
    {
	sprintf (pipebuff, "%s %s", zcat, file);
	if (!(fp = (FILE *) popen (pipebuff, "r")))
	{
	    perror (pipebuff);
	    return (0);
	}
    } else if (!Suffix (file, ".z"))
    {
	sprintf (pipebuff, "%s %s", pcat, file);
	if (!(fp = (FILE *) popen (pipebuff, "r")))
	{
	    perror (pipebuff);
	    return (0);
	}
    } else
#endif	/* COMPRESSION */
    {
	pipebuff[0] = '\0';
	if (!(fp = fopen (file, "r")))
	{
	    perror (file);
	    return (0);
	}
    }

  files_open:

    nelem = 0;
    rejected = 0;
    memfilled = 0;
    dictroot = (struct DICT *) 0;

    Log ("%s rule '%s' to file '%s'\n",
	 contdict ? "Continuing" : "Applying",
	 rule,
	 file);

    while (fgets (buffer, STRINGSIZE, fp))
    {
	Trim (buffer);

	if (!buffer[0] || buffer[0] == '#')
	{
	    continue;
	}
	mangle = Mangle (buffer, rule);

	if (!mangle)
	{
	    rejected++;

	    if (verbose_bool)
	    {
		Log ("Rejected '%s' due to rule specs.\n", buffer);
	    }
	    continue;
	}
	if (dictroot && !strncmp (mangle, dictroot -> word, pwlength))
	{
	    rejected++;

	    if (verbose_bool)
	    {
		Log ("Rejected '%s'; duplicated to %d chars.\n", buffer,
		     pwlength);
	    }
	    continue;
	}
	i = strlen (mangle);

	if (i > pwlength)
	{
	    i = pwlength;
	}
	scratch = (struct DICT *) malloc (sizeof (struct DICT) + i);

	if (!scratch)
	{
	    Log ("LoadDict/malloc() failed! Shameful lack of memory!\n");
	    memfilled = 1;
	    goto words_loaded;
	}
	strncpy (scratch -> word, mangle, i);
	scratch -> word[i] = '\0';
	scratch -> next = dictroot;
	dictroot = scratch;
	nelem++;

	if (verbose_bool)
	{
	    Log ("Loaded '%s' as '%s' using '%s'\n", buffer,
		 scratch -> word, rule);
	}
    }

    if (pipebuff[0])
    {
	pclose (fp);
    } else
    {
	fclose (fp);
    }

    fp = (FILE *) 0;

  words_loaded:

    if (nelem == 0)
    {
	return (0);
    }
    Log ("Rejected %ld words on loading, %ld words left to sort\n",
	 rejected, nelem);

    dictroot = (struct DICT *) SortDict (dictroot, nelem);

    if (memfilled)
    {
	nelem = -nelem;
    }
    return (nelem);		/* not strict number anymore... */
}
/* lose the current dictionary */

int
DropDict ()
{
    register struct DICT *scratch1;
    register struct DICT *scratch2;

    scratch1 = dictroot;
    while (scratch1)
    {
	scratch2 = scratch1 -> next;
	free (scratch1);
	scratch1 = scratch2;
    }
    return (0);
}
/*
 * write a feedback file if there is anything to save - return number
 * uncracked users
 */

int
FeedBack (log_notdone)
    int log_notdone;
{
    register FILE *fp;
    static char fmt[] = "%s:%s:%s:%s\n";
    register struct USER *head;
    register struct USER *arm;
    int done;
    int notdone;

    notdone = done = 0;

    if (verbose_bool)
    {
	Log ("Sweeping data looking for feedback.\n");
    }
    fp = (FILE *) 0;

    for (head = userroot; head; head = head -> next)
    {
	for (arm = head; arm; arm = arm -> across)
	{
	    if (arm -> done)
	    {
		done++;
		/* horrible little hack, vile, sick, I love it */
		if (!fp)
		{
		    if (!(fp = fopen (feedbackfile, "w")))
		    {
			perror (feedbackfile);
			return (-1);
		    }
		    if (verbose_bool)
		    {
			Log ("Feedback file opened for writing.\n");
		    }
		}
		fprintf (fp, fmt, feedback_string,
			 arm -> passwd.pw_passwd, "Y", arm -> passwd_txt);
	    } else
	    {
		notdone++;
		if (log_notdone)
		{
		    if (!fp)	/* and again !!! heheheheheheh */
		    {
			if (!(fp = fopen (feedbackfile, "w")))
			{
			    perror (feedbackfile);
			    return (-1);
			}
			if (verbose_bool)
			{
			    Log ("Feedback file opened for writing.\n");
			}
		    }
		    /* I think I'm going slightly warped */
		    fprintf (fp, fmt, feedback_string,
			     arm -> passwd.pw_passwd, "N", "");
		}
	    }

	}
    }
    if (fp)
    {
	fclose (fp);
	Log ("Closing feedback file.\n");
    }
    Log ("FeedBack: %d users done, %d users left to crack.\n", done, notdone);
    return (notdone);
}
/* try a chain of users with the same salt */

int
TryManyUsers (eptr, guess)	/* returns 0 if all done this chain */
    register struct USER *eptr;
    char *guess;
{
    register int retval;
    char guess_crypted[STRINGSIZE];

    if (eptr -> done && !eptr -> across)
    {
	return (0);
    }
    strcpy (guess_crypted, crypt (guess, eptr -> passwd.pw_passwd));

    retval = 0;

    while (eptr)
    {
	if (verbose_bool)
	{
	    Log ("Trying '%s' on %s from line %s\n",
		 guess,
		 eptr -> passwd.pw_name,
		 eptr -> filename);
	}
	if (!eptr -> done && !STRCMP (guess_crypted, eptr -> passwd.pw_passwd))
	{
	    PrintGuess (eptr, guess);
	}
	retval += (!(eptr -> done));
	eptr = eptr -> across;
    }

    return (retval);
}
/* try a word on an individual */

int
TryOneUser (eptr, guess)	/* returns non-null on guessed user */
    register struct USER *eptr;
    register char *guess;
{
    if (!guess || !*guess || eptr -> done)
    {
	return (0);
    }
    if (verbose_bool)
    {
	Log ("Trying '%s' on %s from %s\n",
	     guess,
	     eptr -> passwd.pw_name,
	     eptr -> filename);
    }
    if (strcmp (crypt (guess, eptr -> passwd.pw_passwd),
		eptr -> passwd.pw_passwd))
    {
	return (0);
    }
    PrintGuess (eptr, guess);

    return (1);
}
/* frontend to TryOneUser() to save hassle */

int
WordTry (entry_ptr, guess)
    register struct USER *entry_ptr;
    register char *guess;
{
    struct RULE *ruleptr;
    register char *mangle;

    if (!guess[0] || !guess[1])
    {
	return (0);
    }
    for (ruleptr = gecosroot; ruleptr; ruleptr = ruleptr -> next)
    {
	if (mangle = Mangle (guess, ruleptr -> rule))
	{
	    if (TryOneUser (entry_ptr, mangle))
	    {
		return (1);
	    }
	}
    }
    return (0);
}
/* Special manipulations for the GECOS field and dotfiles */

int
ParseBuffer (entry_ptr, buffer, advanced)
    register struct USER *entry_ptr;
    char *buffer;
    int advanced;
{
    int wordcount;
    register int i;
    register int j;
    register char *ptr;
    char junk[STRINGSIZE];
    char *words[WORDSTACKSIZE];

    /* zap all punctuation */
    for (ptr = buffer; *ptr; ptr++)
    {
	if (ispunct (*ptr) || isspace (*ptr))
	{
	    *ptr = ' ';
	}
    }

    /* break up all individual words */
    wordcount = 0;
    ptr = buffer;
    while (*ptr)
    {
	while (*ptr && isspace (*ptr))
	{
	    ptr++;
	}

	if (*ptr)
	{
	    words[wordcount++] = ptr;
	    if (wordcount >= WORDSTACKSIZE)
	    {
		Log ("ParseBuffer: Abort: Stack Full !\n");
		return (0);
	    }
	}
	while (*ptr && !isspace (*ptr))
	{
	    ptr++;
	}

	if (*ptr)
	{
	    *(ptr++) = '\0';
	}
    }

    words[wordcount] = (char *) 0;

    /* try all the words individually */
    if (verbose_bool)
    {
	Log ("Trying individual words\n");
    }
    for (i = 0; i < wordcount; i++)
    {
	if (WordTry (entry_ptr, words[i]))
	{
	    return (1);
	}
    }

    if (!advanced)
    {
	return (0);
    }
    /* try pairings of words */
    if (verbose_bool)
    {
	Log ("Trying paired words\n");
    }
    for (j = 1; j < wordcount; j++)
    {
	for (i = 0; i < j; i++)
	{
	    /* Skip initials for next pass */
	    if (!words[i][1] || !words[j][1])
	    {
		continue;
	    }
	    strcpy (junk, words[i]);
	    strcat (junk, words[j]);

	    if (WordTry (entry_ptr, junk))
	    {
		return (1);
	    }
	    strcpy (junk, words[j]);
	    strcat (junk, words[i]);

	    if (WordTry (entry_ptr, junk))
	    {
		return (1);
	    }
	}
    }

    /* try initials + words */
    if (verbose_bool)
    {
	Log ("Trying initial'ed words\n");
    }
    for (j = 1; j < wordcount; j++)
    {
	for (i = 0; i < j; i++)
	{
	    junk[0] = words[i][0];
	    junk[0] = CRACK_TOUPPER (junk[0]);
	    strcpy (junk + 1, words[j]);
	    if (WordTry (entry_ptr, junk))
	    {
		return (1);
	    }
	}
    }

    return (0);
}
/* run over password entries looking for passwords */

void
Pass1 ()
{
    struct USER *head;
    char junk[DOTFILESIZE];
    register struct USER *this;

#ifdef CRACK_DOTFILES
#ifdef CRACK_DOTSANE
#include <sys/types.h>
#include <sys/stat.h>
    struct stat sb;
#endif	/* CRACK_DOTSANE */
    int i;
    int j;
    FILE *fp;
    char filename[STRINGSIZE];
    static char *dotfiles[] =
    {
	".plan",
	".project",
	".signature",
	(char *) 0
    };
#endif	/* CRACK_DOTFILES */

    Log ("Starting pass 1 - password information\n");

    for (head = userroot; head; head = head -> next)
    {
	for (this = head; this; this = this -> across)
	{
	    strcpy (junk, this -> passwd.pw_gecos);

	    if (WordTry (this, this -> passwd.pw_name) ||
		ParseBuffer (this, junk, 1))
	    {
		continue;
	    }
#ifdef CRACK_DOTFILES
	    for (i = 0; dotfiles[i]; i++)
	    {
		sprintf (filename, "%s/%s", this -> passwd.pw_dir, dotfiles[i]);
#ifdef CRACK_DOTSANE
		if (stat (filename, &sb) < 0)
		{
		    continue;
		}
		if ((!(sb.st_mode & S_IFREG))
#ifdef S_IFSOCK
		    || ((sb.st_mode & S_IFSOCK) == S_IFSOCK)
#endif	/* S_IFSOCK */
		    )
		{
		    continue;
		}
#endif	/* CRACK_DOTSANE */

		if (!(fp = fopen (filename, "r")))
		{
		    continue;
		}
		j = fread (junk, 1, DOTFILESIZE, fp);
		fclose (fp);

		if (j <= 2)
		{
		    continue;
		}
		junk[j - 1] = '\0';	/* definite terminator */

		if (verbose_bool)
		{
		    Log ("DOTFILES: Checking %d bytes of %s\n", j, filename);
		}
		if (ParseBuffer (this, junk, 0))
		{
		    continue;
		}
	    }
#endif	/* CRACK_DOTFILES */
	}
    }
    return;
}

void
Pass2 (dictfile)
    char *dictfile;
{
    int pointuser;
    struct USER *headptr;
    struct RULE *ruleptr;
    struct DICT *dictptr;

    Log ("Starting pass 2 - dictionary words\n");
    headptr = (struct USER *) 0;
    ruleptr = (struct RULE *) 0;

    /* check if we are recovering from a crash */
    if (recover_bool)
    {
	recover_bool = 0;	/* switch off */

	for (ruleptr = ruleroot;
	     ruleptr && strcmp (ruleptr -> rule, old_rule);
	     ruleptr = ruleptr -> next);

	if (!ruleptr)
	{
	    Log ("Fatal: Ran off end of list looking for rule '%s'\n",
		 old_rule);
	    exit (1);
	}
	for (headptr = userroot;/* skip right number of users */
	     headptr && old_usernum--;
	     headptr = headptr -> next);

	if (!headptr)
	{
	    Log ("Fatal: Ran off end of list looking for user '%s'\n",
		 old_username);
	    exit (1);
	}
    }
    /* start iterating here */
    for (ruleptr = (ruleptr ? ruleptr : ruleroot);
	 ruleptr;
	 ruleptr = ruleptr -> next)
    {
	long int rval;
	int continue_dict;

	continue_dict = 0;

      load_dict:
	rval = LoadDict (dictfile, ruleptr -> rule, continue_dict);

	if (rval == 0)
	{
	    Log ("Oops! I got an empty dictionary! Skipping rule '%s'!\n",
		 ruleptr -> rule);
	    continue;
	}
	pointuser = 0;

	/* iterate all the users */
	for (headptr = (headptr ? headptr : userroot);
	     headptr;
	     headptr = headptr -> next)
	{
	    SetPoint (dictfile,
		      ruleptr -> rule,
		      pointuser++,
		      headptr -> passwd.pw_name);

	    /* iterate all the words */
	    for (dictptr = dictroot;
		 dictptr;
		 dictptr = dictptr -> next)
	    {
		/* skip repeated words... */
		if (!TryManyUsers (headptr, dictptr -> word))
		{
		    break;
		}
	    }
	}

	/* free up memory */
	DropDict ();

	/* write feedback file */
	if (!FeedBack (0))
	{
	    Log ("FeedBack: All Users Are Cracked! Bloody Hell!\n");
	    return;
	}
	/* on next pass, start from top of user list */
	headptr = (struct USER *) 0;

	/* did we REALLY finish this dictionary ? */
	if (rval < 0)
	{
	    continue_dict = 1;
	    goto load_dict;
	}
    }
}

int
main (argc, argv)
    int argc;
    char *argv[];
{
    int i;
    long t;
    int uerr;
    int die_bool = 0;
    FILE *fp;
    char *crack_out;
    extern int optind;
    extern char *optarg;
    static char getopt_string[] = "i:fX:n:r:vml:";

    uerr = 0;

    if (argc == 1)
    {
	uerr++;
    }
    while ((i = getopt (argc, argv, getopt_string)) != EOF)
    {
	switch (i)
	{
	case 'i':
	    strcpy (input_file, optarg);
	    if (!freopen (input_file, "r", stdin))
	    {
		perror (input_file);
		exit (1);
	    }
	    if (!strncmp (input_file, "/tmp/pw.", 7))
	    {
		unlink (input_file);
	    }
	    break;
	case 'm':
	    mail_bool = 1;
	    break;
	case 'f':
	    foreground_bool = 1;
	    break;
	case 'X':
	    remote_bool = 1;
	    strcpy (supplied_name, optarg);
	    break;
	case 'l':
	    pwlength = atoi (optarg);
	    break;
	case 'n':
	    nice_value = atoi (optarg);
	    nice (nice_value);
	    break;
	case 'r':
	    recover_bool = 1;
	    strcpy (recover_file, optarg);
	    break;
	case 'v':
	    verbose_bool = 1;
	    break;
	default:
	case '?':
	    uerr++;
	    break;
	}
    }

    if (optind >= argc)
    {
	uerr++;
    }
    if (uerr)
    {
	fprintf (stderr,
		 "Usage:\t%s -%s dictfile [dictfile...]\n",
		 argv[0],
		 getopt_string);
	exit (1);
    }
    pid = getpid ();

    if (gethostname (this_hostname, STRINGSIZE))
    {
	perror ("gethostname");
    }
    if (!(crack_out = (char *) getenv ("CRACK_OUT")))
    {
	crack_out = ".";
    }
    sprintf (opfile, "%s/out.%s%d", crack_out, this_hostname, pid);

    if (remote_bool)
    {
	sprintf (diefile, "%s", supplied_name);
    } else
    {
	sprintf (diefile, "%s/D%s%d", runtime, this_hostname, pid);
    }
    sprintf (pointfile, "%s/P%s%d", runtime, this_hostname, pid);
    sprintf (feedbackfile, "%s/F%s%d", runtime, this_hostname, pid);

    if (!foreground_bool)
    {
	if (!freopen (opfile, "w", stdout))
	{
	    perror ("freopen(stdout)");
	    exit (1);
	}
	if (!freopen (opfile, "a", stderr))
	{
	    perror ("freopen(stderr)");
	    exit (1);
	}
    }
    /*
     * don't generate a die file unless we are not 'attached' to a
     * terminal...  except when we are remote as well...
     */

    time (&t);

    if (!foreground_bool || (foreground_bool && remote_bool))
    {
	if (!(fp = fopen (diefile, "w")))
	{
	    perror (diefile);
	    exit (1);
	}
	die_bool = 1;
	fprintf (fp, "#!/bin/sh\n");
	fprintf (fp, "# ID=%s.%d start=%s", this_hostname, pid, ctime (&t));
	fprintf (fp, "kill -TERM %d && rm $0", pid);
	fclose (fp);
	chmod (diefile, 0700);
    }
    Log ("Crack v%s: The Password Cracker, (c) Alec D.E. Muffett, 1992\n",
	 version);

#ifdef FCRYPT
    init_des ();
#endif

    /* Quick, verify that we are sane ! */

    if (strcmp (crypt ("fredfred", "fredfred"), "frxWbx4IRuBBA"))
    {
	Log ("Version of crypt() being used internally is not compatible with standard.\n");
	Log ("This could be due to byte ordering problems - see the comments in Sources/conf.h\n");
	Log ("If there is another reason for this, edit the source to remove this assertion.\n");
	Log ("Terminating...\n");
	exit (0);
    }
#ifndef AMIGA
    signal (SIGTERM, CatchTERM);
#endif

    Log ("Loading Data, host=%s pid=%d\n", this_hostname, pid);

    if (LoadData () <= 0)
    {
	Log ("Nothing to Crack. Exiting...\n");
	exit (0);
    }
    if (LoadRules (rulefile, &ruleroot) < 0 ||
	LoadRules (gecosfile, &gecosroot) < 0)
    {
	exit (1);
    }
    if (!recover_bool)
    {
	/* We are starting afresh ! Ah, the birds in May ! */
	Pass1 ();

	if (!FeedBack (0))
	{
	    Log ("FeedBack: information: all users are cracked after gecos pass\n");
	    goto finish_crack;
	}
    } else
    {
	int rval;

	if (rval = GetPoint (recover_file))
	{
	    Log ("Recovery from file %s not permitted on this host [code %d]\n",
		 recover_file,
		 rval);
	    exit (0);

	}
	/* Some spodulous creep pulled our plug... */
	while ((optind < argc) && strcmp (argv[optind], old_dictname))
	{
	    optind++;
	}
    }

    for (i = optind; i < argc; i++)
    {
	Pass2 (argv[i]);
    }

    Log ("Tidying up files...\n");
    FeedBack (1);

  finish_crack:

    if (die_bool)
    {
	unlink (diefile);
    }
    unlink (pointfile);

    Log ("Done.\n");

    return (0);
}
