/*
VERSION: 0.01 by Sudog Feb '97
So what if I have a non-standard version?
*/

#include <stdio.h>
#include <string.h>
#ifdef AMIGA
#include <dos/dos.h>
#include <exec/memory.h>
#endif

/* duh */
#define TRUE        1  /* generic true/false because I don't want to waste time */
#define FALSE       0  /* including exec/types.h so there. */

/* the following for passwd structure traversal */
#define USER        1  /* at user field in line */
#define PASSWD      2  /* at passwd field in passwd entry */
#define SAVE        3  /* after storing user & password, save it */
#define DONE        4  /* and after saving it for later, skip over rest */

/* maximum values for internal passwd structure */
#define NAMESIZE   15  /* for struct passwd and for parsepass routine */
#define SALTSIZE    3
#define PASSWDSIZE 14
#define WORDSIZE   20  /* maximum size of password in command line or password in dictfile */

/* values for return() from getnextdict */
#define DICT_DONE  1   /* dictionary has been exhausted */
#define DICT_ERROR 2   /* dictionary file is corrupt or something else just as fucked up */


#define SEPARATOR  ':' /* passwd field separator.. : for unix, | for AmiTCP */

/*
** This is merely a small affectation because I am sometimes curious
** to have regular status updates..  uncomment this #define if you like
** something happening everytime the dictionary file has been entirely
** searched through..
*/
#define ANIMSIZE   4

#ifdef AMIGA
int CXBRK(void) {return(0);}    /* sorry user we're gonna make SURE that you can't */
int chkabort(void) {return(0);} /* mess us up without giving us chance to clean up */
#endif

struct passwd
  {
  char name[NAMESIZE];
  char salt[SALTSIZE];
  char password[PASSWDSIZE];
  struct passwd *nextitem;
  struct passwd *lastitem;
  short cracked;
  };

FILE *dictionary;  /* global because we want getnextdict() to have an open-and-ready */
                   /* file handle from which to read words from the dictionary when in */
                   /* dictionary mode */

char *crypt(char *, char *);
struct passwd *parsepass(char *);
void cleanup(struct passwd *);
void log(char *, char *, char *);
long getnextdict(char *, char *);

int main(int argc, char *argv[])
{
unsigned int counter;
unsigned int c=FALSE;
unsigned char commandword[WORDSIZE];

struct passwd *firstitem=NULL;
struct passwd *currentitem=NULL;

long status; /* for returns from various functions..  scratchpad */

#ifdef ANIMSIZE
char anim[] = "\\|/-";
int animcounter = 0;
#endif

#ifdef AMIGA
long actualsigs;
long waitmask = SIGBREAKF_CTRL_C;
#endif

dictionary=NULL; /* i don't want to do this more than once */

/* do some command line argument parsing */
/* this is hereby hard-coded.*/
for (counter = 0; counter < argc; counter++)
  {
  if (argv[counter][0] == '-')
    {
/*
** The following accepts the - as an indicator, and then it copies the
** rest of this particular argument as a word to search a passwd file for.
** The reason this isn't hard-coded as argv[2] is for future expandability--
** sort of a functional work-in-progress.
*/
    c = TRUE;
    for (counter=1; counter < WORDSIZE; counter++)
      {
      commandword[counter-1] = argv[2][counter];
      if (commandword[counter-1] == 0) break;
      }
    break;
    }
  if (argv[counter][0] == '?')
    {
/*
** I could never really see the harm in goto statements within programs.
** "A Book On C" says that goto statements are bad programming pratice.
** Whatever..
*/
usage:
    printf("Usage: %s <passwdfile> -<word> [outputfile]\n", argv[0]);
    printf("       %s <passwdfile> <dictfile> [outputfile]\n", argv[0]);
    printf("       %s ?\n", argv[0]);
    printf("This program written in Feb, '97 by sudog.\n");
    printf("So there.\n");
    exit(0);
    }
  }

counter = 0;

if (argc > 2)
  {
  if (firstitem = parsepass(argv[1]))
    {
    currentitem = firstitem;
/*
** This section deals with a command-line supplied password.
*/
    if (c)
      {
      while (currentitem != NULL)
        {
        if (!strcmp(currentitem->password, crypt(commandword, currentitem->salt)))
          {
          printf("%s %s\n", currentitem->name, commandword);
          if (argc > 3)
             {
            log(argv[3], currentitem->name, commandword);
            }
          }
/* This section is amiga-only--it allows the user to break the program,
** CLEANLY! (Ie: We trap SIGBREAKF_CTRL_C and clean up the memory we allocated.)
*/
#ifdef AMIGA
        actualsigs = CheckSignal(waitmask);
        if (actualsigs)
          {
          cleanup(firstitem);
          }
#endif
        currentitem = currentitem->nextitem;
        }
      }
/*
** This section deals with a dictionary file!
** TODO:
** Add place holder so a system crash can be survived and iterations can
** continue from last point...  VERY cool. let's do som background cracking!
*/
/*
** First priority is to check the entire dictionary against the 
** current passwd entry. It's MUCH MUCH INCREDIBLY FASTER then.
*/
    else
      {
      currentitem=firstitem;
/*
** A simple while loop to search an entire dictionary on a single
** password entry. Trust me, this saves a SHITLOAD of time, since
** for each new salt which is used, crypt() has to re-set all its
** damned data structure and shit. VERY crappy. Otherwise it zips
** through things in like, 500 keys per second. At least, on my
** machine, compared with maybe 10 keys/sec otherwise.
*/
      while ( currentitem != NULL )
        {
        while (((status=getnextdict(commandword, argv[2])) != DICT_DONE) && (status != DICT_ERROR))
          {
          if (!currentitem->cracked)
            {
            if (!strcmp(currentitem->password, crypt(commandword, currentitem->salt)))
              {
              printf("%s %s\n", currentitem->name, commandword);
              currentitem->cracked = TRUE;
              if (argc > 3)
                {
                log(argv[3], currentitem->name, commandword);
                }
              }
            }
#ifdef AMIGA
          actualsigs = CheckSignal(waitmask);
          if (actualsigs)
            {
            cleanup(firstitem);
            }
#endif
          }
        if (status == DICT_ERROR)
          {
          printf("Error: Unable to access dictionary file..\n");
          break;
          }
        if (status == DICT_DONE)
          {
#ifdef ANIMSIZE
          printf("%c%c", anim[animcounter], 8);
          animcounter++;
          if (animcounter >= ANIMSIZE)
            {
            animcounter = 0;
            }
#endif
          }
        currentitem=currentitem->nextitem;
        }
      }
    }
  else
    {
    printf("Error in parsing password file.\n");
    }
  }
else
  {
  printf("Error. You don't have a clue. Here. Have one.\n");
  goto usage;
  }

cleanup(firstitem);

}

/*
** In order that we get dictionary words, we have to 
** return words in cyclical order. Ie: We have to 
** get the first word after we got the last word.
** So we aren't returning any end-of-file conditions.
** We're just returning words unless an error
** condition exists.
*/

long getnextdict(char *commandword, char *filename)
{
long counter=0;
int gotone=FALSE;
char temp;

if (commandword == NULL && filename == NULL)
  {
  if (dictionary)
    {
    fclose(dictionary);
    }
  return(0);
  }
if (!dictionary)
  {
  if (!(dictionary = fopen(filename, "r")))
    {
    return(DICT_ERROR);
    }
  }
if (dictionary)
  {
  while ( 1 )
    {
    temp = fgetc(dictionary);
    if (feof(dictionary))
      {
      if (gotone)
        {
        commandword[counter] = 0;
        return(0);
        }
      else
        {
/*
** We're going to re-use the dictionary file again and again and
** again until we're TOLD to close it with getnextdict(NULL, NULL);
*/
        rewind(dictionary);
        return(DICT_DONE);
        }
      }
    else if (temp > 31 && temp < 127)
      {
      if (gotone == FALSE)
        {
        gotone = TRUE;
        }
      if (counter < WORDSIZE)
        {
        commandword[counter++] = temp;
        }
      }
    else
      {
      if (gotone == TRUE)
        {
        commandword[counter] = 0;
        return(0);
        }
      }
    } /* end of while loop */
  }
}

/*
** Excellently enough, calloc() remembers how big the memory chunks
** it allocated are, so a call to free() doesn't have to specify anything
** but the pointer!!  ANSI-C rules!
*/
void cleanup(struct passwd *firstitem)
{
struct passwd *currentitem;
while (firstitem != NULL)
  {
  currentitem = firstitem->nextitem;
  free(firstitem);
  firstitem = currentitem;
  }
getnextdict(NULL, NULL);
exit(0);
}
  
void log (char *filename, char *username, char *password)
{
FILE *output;

if (output = fopen(filename, "a"))
  {
  fprintf(output, "%s %s\n", username, password);
  fclose(output);
  }
else
  {
  printf("Error: Unable to open output file!\n");
  }
}

/*
** This routine opens a passwd file, and parses it according to the
** following rules:
**
** {user}{separator}{passwd field entry}{separator}
** There MUST be two separators per line..  the separator should be defined as
** SEPARATOR in the preprocessor.. usually ':' for UNIX-style passwd files.
*/

/*
** We use a state variable to define which part of the passwd entry we
** are currently processing..  it's more used as a sort of directive..
** if it's USER, store it in the user field of the struct passwd..
** etc etc..
*/
struct passwd *parsepass(char *filename)
{
struct passwd *currentitem=NULL;
struct passwd *firstitem=NULL;
struct passwd *floatitem=NULL;

unsigned int counter = 0;
unsigned int state;       /* the part of the field we're parsing right now */
unsigned int ff = FALSE;

FILE *input;
char temp;


if (input = fopen(filename, "r"))
  {
  while ( 1 )
    {
    temp = fgetc(input);
    if (feof(input)) break;
    if (temp > 31 && temp < 127)
      {
/*
** ff here is being used as a sort of flip-flop switch. It is used for
** QUICKLY skipping over non-printable characters.
*/
      if (ff == FALSE)
        {
        ff = TRUE;
        if (!(floatitem = (struct passwd *)calloc(1, sizeof(struct passwd))))
          {
          printf("Error: Unable to allocate %d bytes. Exiting..\n", sizeof(struct passwd));
          cleanup(firstitem);
          }
        state = USER;
        }
      if (state == USER)
        {
        if (temp != SEPARATOR && counter < NAMESIZE)
          {
          floatitem->name[counter++] = temp;
          }
        if (temp == SEPARATOR)
          {
          state = PASSWD;
          counter = 0;
          }
        }
      else if (state == PASSWD)
        {
        if (temp != SEPARATOR && counter < PASSWDSIZE)
          {
          if (counter < 2)
            {
            floatitem->salt[counter] = temp;
            }
          floatitem->password[counter++] = temp;
          }
        if (temp == SEPARATOR)
          {
          state = SAVE;
          counter = 0;
          }
        }
      if (state == SAVE)
        {
        if (!firstitem)
          {
          firstitem=floatitem;
          }
        else
          {
          currentitem=firstitem;
/*
** We are using a doubly-linked list to store our list of passwd
** entries..  doubly-linked lists are cool! buhuhuhuhuhuhuhuhuh hehe
*/
          while ( 1 )
            {
            if ((strcmp(floatitem->salt, currentitem->salt)) <= 0)
              {
              floatitem->nextitem=currentitem;
              floatitem->lastitem=currentitem->lastitem;
              currentitem->lastitem = floatitem;
              if (floatitem->lastitem != NULL)
                {
                floatitem->lastitem->nextitem = floatitem;
                }
              else
                {
                firstitem = floatitem;
                }
              break;
              }
            if (currentitem->nextitem == NULL)
              {
              currentitem->nextitem = floatitem;
              floatitem->lastitem = currentitem;
              break;
              }
            currentitem=currentitem->nextitem;
            }
          }
        state = DONE;
        }
      }
    else
      {
      state = USER;
      ff = FALSE;
      }
    }
  fclose(input);
  }
else
  {
  printf("Error: Unable to open specified passwd file.\n");
  }
return(firstitem);
}
