/*
 * rpasswd.c
 * Copyright 2006, Brian Baker bab72@yahoo.com
 *
 * Changes a user password to a random generated password.
 * Requires 'net user' command included with Windows XP.
 *
 * Known issues:
 *  - Username cannot contain spaces
 *  - Username must contain at least one non numeric character
 *  - Command line parameters cannot be quoted
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define VERSION "0.2"
#define DEF_PASS_LENGTH 14
#define MAX_PASS_LENGTH 127
#define USER_LENGTH     20
#define CMD_LENGTH      25+MAX_PASS_LENGTH+USER_LENGTH
#define BOOL unsigned char
#define TRUE            1
#define FALSE           0
#define DEF_HELP        FALSE
#define DEF_VERBOSE     FALSE
#define DEF_TEST        FALSE
#define DEF_INCUPPERS   TRUE
#define DEF_INCLOWERS   TRUE
#define DEF_INCNUMBERS  FALSE
#define DEF_INCSPECIALS FALSE
#define DEF_INCEXTRAS   FALSE


void usage()
{
  printf("Random Password Generator - Version %s\n", VERSION);
  printf("Copyright (C) 2006, Brian Baker <bab72@yahoo.com>\n\n");
  printf("Changes a user password to a random string.\n");
  printf("Requires net command and system rights to change passwords.\n\n");
  printf("Usage: rpasswd [-[EhlLnNsStuUv]] [num] <username>\n");
  printf("Parameters:                                            Default Setting\n");
  printf("  h    : Show this help message                      : %s\n", (DEF_HELP==TRUE ? "Enabled" : "Disabled"));
  printf("  t    : Test mode - Just print password             : %s\n", (DEF_TEST==TRUE ? "Enabled" : "Disabled"));
  printf("  v    : Verbose mode (Shows PASSWORD!!)             : %s\n", (DEF_VERBOSE==TRUE ? "Enabled" : "Disabled"));
  printf("  u    : Min Required uppercase letters  *           : 0\n");
  printf("  l    : Min Required lowercase letters  *           : 0\n");
  printf("  n    : Min Required numbers  *                     : 0\n");
  printf("  s    : Min Required special characters  *          : 0\n");
  printf("  U    : Permit uppercase letters in password        : %s\n", (DEF_INCUPPERS==TRUE ? "Enabled" : "Disabled"));
  printf("  L    : Permit lowercase letters in password        : %s\n", (DEF_INCLOWERS==TRUE ? "Enabled" : "Disabled"));
  printf("  N    : Permit numbers in password                  : %s\n", (DEF_INCNUMBERS==TRUE ? "Enabled" : "Disabled"));
  printf("  S    : Permit special characters in password       : %s\n", (DEF_INCSPECIALS==TRUE ? "Enabled" : "Disabled"));
  printf("  E    : Permit extra-special characters in password : %s\n", (DEF_INCEXTRAS==TRUE ? "Enabled" : "Disabled"));
  printf("  num  : Minimum number of characters in password    : %d\n", DEF_PASS_LENGTH);
  printf("  user : Username to change paswsword                : NULL\n");
  printf("   * May be used multiple times to require multiple characters\n");
  //printf("\nSee source code for more detailed usage and copyright information.\n");
  exit(0);
}

void bad_arg(const char str[])
{
  printf("Argument %s ignored...\n", str);
}

int main(int argc, char **argv)
{
  char Password[MAX_PASS_LENGTH + 1] = "";
  char Username[USER_LENGTH + 1] = "";
  char buffer[USER_LENGTH + 1] = "";
  char UpperPool[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  char LowerPool[] = "abcdefghijklmnopqrstuvwxyz";
  char NumberPool[] = "0123456789";
  char SpecialPool[] = "~@#$%^&*(),./?;:-_=+";
  char ExtraPool[] = "`'\"";
  char CharacterPool[256] = "";
  char cmdline[CMD_LENGTH + 1] = "";
  //char *ptr;
  int PoolSize = 0;
  int PassLength = 0;
  int i=0;
  int n=0;
  int nbuffer = 0;

  /* Commandline parameters passed to program */
  BOOL help=DEF_HELP;
  BOOL verbose=DEF_VERBOSE;
  BOOL test=DEF_TEST;

  /* Characters used to generate password */
  BOOL IncUppers=DEF_INCUPPERS; /* Default */
  BOOL IncLowers=DEF_INCLOWERS;
  BOOL IncNumbers=DEF_INCNUMBERS;
  BOOL IncSpecials=DEF_INCSPECIALS;
  BOOL IncExtras=DEF_INCEXTRAS;

  /* Number of characters of each type required in password */
  int ReqUppers=0;
  int ReqLowers=0;
  int ReqNumbers=0;
  int ReqSpecials=0;

  /* Count characters of each type used in password */
  int Uppers=0;
  int Lowers=0;
  int Numbers=0;
  int Specials=0;

  while(*++argv)
  {
    if(argv[0][0]=='-' || argv[0][0]=='/')
    {
      for(i=1;i<strlen(argv[0]);i++)
      {
        switch(argv[0][i])
        {
          case 'E' :  IncExtras = TRUE;
                      break;
          case 'h' :  help=TRUE;
                      break;
          case 'v' :  verbose=TRUE;
                      break;
          case 'u' :  ReqUppers += 1;
          case 'U' :  IncUppers = TRUE;
                      break;
          case 'l' :  ReqLowers += 1;
          case 'L' :  IncLowers = TRUE;
                      break;
          case 'n' :  ReqNumbers += 1;
          case 'N' :  IncNumbers = TRUE;
                      break;
          case 's' :  ReqSpecials += 1;
          case 'S' :  IncSpecials = TRUE;
                      break;
          case 't' :  test=TRUE;
                      break;
          default  :  strncpy(buffer, argv[0]+i, 1);
                      bad_arg(buffer);
        }
      }
    }
    else
    {
      strncpy(buffer, argv[0], USER_LENGTH);

       /* 
        * Check to see if we have a number (Password length)
        * or a username next on the command line.
        */

      if(sscanf(buffer, "%d", &nbuffer)==1)  /* Valid number! */
      {
        if(PassLength==0 && nbuffer>0)
          PassLength = nbuffer;
        else
          bad_arg(argv[0]);
      }
      else                                   /* Not a number! */
      {
        if(strlen(Username)==0)
          strncpy(Username, buffer, USER_LENGTH);
        else
          bad_arg(argv[0]);
      }          
    }
  }

  /* Check options for conflicts */
  if(Username[0]==0 && test==FALSE) help=TRUE;
  if(PassLength==0) PassLength = DEF_PASS_LENGTH;
  if(ReqUppers + ReqLowers + ReqNumbers + ReqSpecials > PassLength)
    PassLength = ReqUppers + ReqLowers + ReqNumbers + ReqSpecials;
  if(PassLength > MAX_PASS_LENGTH)
  {
    printf("Password Length cannot exceed %d\n", MAX_PASS_LENGTH);
    exit(1);
  }
  if(help==TRUE) usage();

  /* Build Character Pool according to options */
  if(IncUppers==TRUE) strcat(CharacterPool, UpperPool);
  if(IncLowers==TRUE) strcat(CharacterPool, LowerPool);
  if(IncNumbers==TRUE) strcat(CharacterPool, NumberPool);
  if(IncSpecials==TRUE) strcat(CharacterPool, SpecialPool);
  if(IncExtras==TRUE) strcat(CharacterPool, ExtraPool);

  PoolSize=strlen(CharacterPool);

  if(verbose==TRUE)
  {
    printf("Username=%s\n", Username);
    printf("PassLength=%d\n", PassLength);
    printf("Using %d possible characters\n", PoolSize);
    if( ReqUppers>0 )
      printf("Requires %d uppercase character(s)\n", ReqUppers);
    if( ReqLowers>0 )
      printf("Requires %d lowercase character(s)\n", ReqLowers);
    if( ReqNumbers>0 )
      printf("Requires %d number(s)\n", ReqNumbers);
    if( ReqSpecials>0 )
      printf("Requires %d special character(s)\n", ReqSpecials);
    //printf("Require Number=%d\n", number);
    //printf("Require Special Char=%d\n", special);
  }

  srand(time(NULL));

  do
  {
    if(verbose==TRUE) printf("Generating password...\n");
    Uppers = Lowers = Numbers = Specials = 0;
    for(i=0;i<PassLength;i++)
    {
      while( PoolSize <= ( n = rand() / (RAND_MAX/PoolSize)));
      Password[i] = CharacterPool[n];
      //if(verbose==TRUE) printf("Password[%d] = %c\n", i, Password[i]);
      if(Password[i]>=65 && Password[i] <=90) Uppers += 1;
      else if (Password[i]>=97 && Password[i]<=122) Lowers += 1;
      else if (Password[i]>=48 && Password[i]<=57) Numbers += 1;
      else Specials += 1;
    }
  } while( Uppers < ReqUppers || Lowers < ReqLowers ||
           Numbers < ReqNumbers || Specials < ReqSpecials );

  /* Reset counters to zero for security */
  Uppers = Lowers = Numbers = Specials = 0;

  if(verbose==TRUE) printf("Password=%s\n", Password);

  if(test==TRUE)
    printf("%s\n", Password);
  else
  {
    sprintf(cmdline, "net user %s \"%s\"", Username, Password);

    if(verbose==TRUE) printf("Calling: %s\n", cmdline);
    system(cmdline);
  }

  /* Overwrite Password[] in memory for security */
  for(i=0;i<MAX_PASS_LENGTH;i++)
    Password[i] = 0;
  if(verbose==TRUE) printf("Password=%s\n", Password);

  /* Overwrite Username[] in memory for security */
  for(i=0;i<USER_LENGTH;i++)
    Username[i] = 0;
  if(verbose==TRUE) printf("Username=%s\n", Username);

  /* Overwrite cmdline[] in memory for security */
  for(i=0;i<CMD_LENGTH;i++)
    cmdline[i] = 0;
  if(verbose==TRUE) printf("Command Line=%s\n", cmdline);
}
