#!/bin/bash
###
# This program was written by and is copyright Alec Muffett 1991,
# 1992, 1993, 1994, 1995, and 1996, and is provided as part of the
# Crack v5.0 Password Cracking package.
#
# The copyright holder disclaims all responsibility or liability with
# respect to its usage or its effect upon hardware or computer
# systems, and maintains copyright as set out in the "LICENCE"
# document which accompanies distributions of Crack v5.0 and upwards.
###
# User-configurable junk for Crack
###

###
# security
###

umask 077

###
# Defaults
###

usage="Usage: Crack [options] [bindir] [[-fmt format] files]..."

version="5.0a"                          # version string
deffmt=trad                             # for default trad2spf
dodie=""                                # for verbose usage/die
debug="false"                           # ...guess...
node=`uname -n`                         # more portable then `hostname`

###
# home base
###

old_wd=`pwd`

CRACK_HOME=${CRACK_HOME:-"/usr/share/crack"}
CRACK_BIN_HOME=${CRACK_BIN_HOME:-"/usr/libexec/crack/"}
CRACK_STATE_DIR=${CRACK_STATE_DIR:-"/var/lib/crack"}

if cd $CRACK_HOME
then
	CRACK_HOME=`pwd`
else
	echo "Fatal Error: $CRACK_HOME: cannot chdir" 1>&2
	exit 1
fi

export CRACK_HOME
export CRACK_BIN_HOME

###
# Flagwaving
###

echo "Crack $version: The Password Cracker."
echo "(c) Alec Muffett, 1991, 1992, 1993, 1994, 1995, 1996"
echo "System:" `uname -a 2>/dev/null`
echo "Home: $CRACK_HOME"
echo "Script Home: $CRACK_BIN_HOME"
echo "Invoked: $0 $*"

###
# Parse Arguments
###

while [ "x$1" != "x" ]
do
	case $1 in
		-fmt)
			$debug && echo "beginning of filespecs detected"
			break
			;;

		-recover)
			echo "Option: $1 enabled"
			recover=true
			shift
			;;

		-keep)
			echo "Option: $1 enabled"
			keep=true
			shift
			;;

		-debug)
			echo "Option: $1 enabled"
			debug=true
			set -x
			shift
			;;

		-network)
			echo "Option: $1 enabled"
			networkflag=$1
			shift
			;;

		-remote)
			echo "Option: $1 enabled"
			remoteflag=$1
			shift
			;;

		-makeonly)
			echo "Option: $1 enabled"
			makeonlyflag=$1
			shift
			;;

		-makedict*)
			echo "Option: $1 enabled"
			makedictflag="-makedict"
			shift
			;;

		-fgnd)
			echo "Option: $1 enabled"
			fgndflag=$1
			shift
			;;

		-mail)                                  # pass to cracker
			echo "Option: $1 enabled"
			mailflag=$1
			shift
			;;

		-nice)                                  # pass to cracker
			echo "Option: $1 enabled"
			if [ "$2" = "" ]
			then
				echo "Crack: -nice needs an argument" 1>&2
				exit 1
			fi
			niceflag="$1 $2"
			shift 2
			;;

		-from)                                  # pass to cracker
			echo "Option: $1 enabled"
			if [ "$2" = "" ]
			then
				echo "Crack: -from needs an argument" 1>&2
				exit 1
			fi
			fromflag="$1 $2"
			shift 2
			;;

		-kill)                                  # pass to cracker
			echo "Option: $1 enabled"
			if [ "$2" = "" ]
			then
				echo "Crack: -kill needs an argument" 1>&2
				exit 1
			fi
			killflag="$2"
			shift 2
			;;

		-*)
			echo "Crack: unrecognised argument $1" 1>&2
			dodie=yes
			shift
			;;

		*)
			$debug && echo "End of options detected."
			break
			;;
	esac
done

if [ "x$dodie" != "x" ]
then
	echo $usage 1>&2
	exit 1
fi

echo ""


if [ "x$1" != "x" ]
then
	bindir=bin
else                                    # supporting "-makeonly"
	if [ "$makeonlyflag" != "" ]
	then
		bindir=bin

	elif [ "$makedictflag" != "" ]
	then
		bindir=bin

	elif [ "$remoteflag" != "" ]
	then
		bindir=bin
	else
		echo $usage 1>&2
		exit 1
	fi
fi

###
# Reset PATH in advance
###

PATH=$CRACK_HOME/scripts:$CRACK_BIN_HOME/$bindir:$PATH
export PATH

###
# Make Only ?
###

if [ "x$makeonlyflag" != "x" ]
then
	echo "Crack: makeonly done"
	exit 0
fi

###
# Make the dictionary passes
###

dp=$CRACK_STATE_DIR/run/dict
dplf=$dp/.dictmade

if [ ! -f $dplf ]
then
	test -d $dp && rm -rf $dp

	echo "Crack: making dictionary groups, please be patient..."
	mkdictgrps $dp || exit 1

	echo "Crack: Created new dictionaries..."
	date > $dplf
else
	echo "Crack: The dictionaries seem up to date..."
fi

###
# Make Dict Only ?
###

if [ "x$makedictflag" != "x" ]
then
	echo "Crack: makedict done"
	exit 0
fi

###
# We're on a roll...
###

# F-files are feedback
crackin=$CRACK_STATE_DIR/run/I$node.$$           # I-files are input to the cracker
crackfb=$CRACK_STATE_DIR/run/D.boot.$$           # D-files are data from the cracker/fb
crackout=$CRACK_STATE_DIR/run/D$node.$$
crackerr=$CRACK_STATE_DIR/run/E$node.$$          # E-files are errors from the cracker

if [ "x$killflag" = "x" ]
then
	crackkf=$CRACK_STATE_DIR/run/K$node.$$   # K-files are kill file for 'plaster'
else
	crackkf=$killflag
fi

if [ "x$mailflag" != "x" ]
then
	crackmf=$CRACK_STATE_DIR/run/M$node.$$   # M-files are for mail commands
fi


###
# SPF
###

# The SPF scripts are frontend processors for creating "Single
# Password Format" files which Crack can work on, from any of a
# variety of possible inputs.

# if your target system uses a non-traditional passwd-file format (eg:
# /etc/master.passwd in FreeBSD) then you can use/write a spf script
# (eg: freebsd2spf) to convert the data, and invoke it thusly:

# Crack -fmt freebsd /etc/master.passwd

# If your system has shadow passwords but the core of the password
# data is held in a "traditional" format and uses the standard
# crypt(), function it is recommended that you coerce your data into a
# BSD format file to feed into Crack, by using one of the supplied
# "shadmrg" scripts, or by your own means.

# if your target system is running a non-traditional crypt()
# algorithm, you will have to modify the ELCID code, too.

if [ "x$remoteflag" = "x" ]
then
	echo "Crack: Sorting out and merging feedback, please be patient..."
	fbmerge

	echo "Crack: Merging password files..."

	(
		cat $CRACK_STATE_DIR/run/F-merged        # first the feedback

		while [ "x$1" != "x" ]
		do
			if [ "$1" = "-fmt" ]
			then
				shift || exit 1
				deffmt=$1

			else
			  theFile=$1
			  if [ ! -r $theFile ]; then
			    theFile="$old_wd/$theFile"
			  fi
			  if [ "$deffmt" = "spf" ]
			  then
			  	  cat $theFile 1>&3 # skip the sort
			  else
			  	  ${deffmt}2spf $theFile
			  fi
			fi

			shift
		done |
		crack-sort -t: -k 2
	) 3>&1 |
	fbfilt $crackfb $crackmf > $crackin # remove feedback-guessable users

	if [ "x$crackmf" != "x" ]
	then
		if [ -s $crackmf ]
		then
			echo "Crack: mailing nastygrams..."
			sh -x $crackmf
		fi
		rm -f $crackmf
	fi

	if [ "x$recover" = "x" ]
	then
	    echo "Crack: Creating gecos-derived dictionaries"
	    mkgecosd $dp $crackin
	else
	    echo "Crack: -recover: using existing gecos-derived dictionaries"
	fi
else
	echo "Crack: reading data from stdin..."
	cat > $crackin # has already been thru fbfilt on master
	ls -l $crackin
fi

###
# Launch it...
###

flags="$fromflag $niceflag $mailflag"

if [ "x$networkflag" != "x" ]
then
	if [ "x$remoteflag" != "x" ]
	then
		echo "Error: cannot -network AND -remote" 1>&2
		exit 1
	elif [ "x$fgndflag" != "x" ]
	then
		echo "Error: cannot -network AND -foreground" 1>&2
		exit 1
	fi

	echo "Crack: launching: netcrack $flags"
	netcrack $flags <$crackin

elif [ "x$fgndflag" != "x" ]
then
	echo "Crack: exec: cracker -kill $crackkf $flags"
	exec cracker $flags <$crackin
else
	echo "Crack: launching: cracker -kill $crackkf $flags"
	exec 3>&- 4>&- 5>&- 6>&- 7>&- 8>&- 9>&-
	nohup cracker -kill $crackkf $flags <$crackin >$crackout 2>$crackerr &
fi

if [ "x$keep" = "x" ]
then
	sleep 3
	rm $crackin             # Aye, some things never change...
fi

###
# Exit
###

echo "Done"

exit 0
