#! /bin/sh
#  ^^ may need to change this to "bash", "sh5", etc  on (e.g.) ultrix machines.
vers="brw0.00"

author="Piete.Brooks@cl.cam.ac.uk";
basename=`basename $0`
cmdnm=brwork
rc=.rc

helpproc() {
	cat <<EOF 1>&2
$0: Usage: [-a|-h|-i|-c configfile|-C configfile|-l loops]

-a	autoconfigure if no config file is found
-A ACK	return ACK info
-h	generate this message
-i	generate some info about configuring the system
-k keys	Just process the specified keys
-l loop	process "loop" chunks
-C file	configure saving output in file
-c file	read configuration info from file (see -i for details)

EOF
}

ask() {
	echo $n "$2: [$1] $c"
	read res || exit 1
	case "$res" in
	?*)     return;;
	*)      res="$1"; return;;
	esac
}

confproc() {
	echo ""; echo "See   $0 -i   if you need some background info"; echo ""
	case `echo -n "test\c"; echo ""` in
	-n*c*)	n="" c=""; echo " [[ odd echo you have ! ]]" 1>&2;;
	-n*)	n="" c="\\c";;
	*c*)	n="-n" c="";;
	*)	n="-n" c="";;
	esac
	case "$conffile" in
	'')	def="$HOME/.$basename$rc"
		echo $n "File into which to save info: [$def] $c"
		read res || exit 1
		case "$res" in
		?*)	conffile="$res";;
		*)	conffile="$def";;
		esac;;
	*)	if	test -s "$conffile"
		then	echo "$conffile already exists. Delete or move it and retry" 2>&1
			exit 1;
		fi
	esac
	echo Using "$conffile"
	ask "" "What ID would you like (usually your email address e.g. pb@cl.cam.ac.uk)"
	BRID="$res"
	ask "" "What NAME would you like (usually your full name e.g. Piete Brooks)"
	BRNAME="$res"
	ask "$HOME/$basename.keys" "Where should the log of the keys be kept"
	keyslog="$res"
	ask "1:00" "How long should it search for at a time (h:mm)"
	BRTIME="$res"

	if	( echo tailored=true
	case "$BRID" in ?*) echo "BRID=\"$BRID\";"; esac
	case "$BRNAME" in ?*) echo "BRNAME=\"$BRNAME\";"; esac
	case "$keyslog" in ?*) echo "keyslog=\"$keyslog\"";; esac
	case "$BRTIME" in ?*) echo "BRTIME=\"$BRTIME\""; esac
	) > $conffile
	then	echo "Config info save in $conffile"; echo ""
	else	echo "Failed to write $conffile" 1>&2; exit 1;
	fi
}
infoproc() {
	cat <<EOF 1>&2
This info relates to simple use of $0.
People interested in making it do other that the plain simple should read the
source or contact $author for help.

The objective of this script is to call brclient to get some work to do,
call brutessl (or bruterc4 or whatever) to do the work, and then report back
the result using brclient. It is done this way for security reasons.

If "-l loop" is given (where loop is a number) it will loop that many times.
Otherwise it will keep going until it find a file exit-brute in its current
directory (sleep-brute causes it to suspend processing until the file goes).
Note that this may take a while as it only looks after completing a chunk.
Killing the process will cause the allocated segments not to be ACKed.
[ If you are using a recent version of brutessl, you can type ^C to interupt
  it, and it will stop after the current chunk. ]

As it stands, it does no write any files (so that it can run in a padded cell)
but it is very useful to keep logs of what it has (and has not) done.
The crude way to do this is to send stderr and stdout to a log file, as in:

sh	$cmdnm >> $cmdnm.log 2>&1 &
csh	$cmdnm >>& $cmdnm.log &

but a more concise way is to set logkeys (and optionally scankeys) -- see below.

It tries to read a config file (typically .$basename$rc) to set tailor itself.
See  $0 -h  for the use of the -a and -C flags to create a simple config file.
There are listed in order of priority of being set -- you really SHOULD do the
first, whereas the last are unimportant.

tailored=true			<< Set it to the exact string "true"
	ALWAYS set this, so that $0 knows you have a tailor file

BRID="pb@cl.cam.ac.uk"		<< Replace pb@cl.cam.ac.uk by YOUR ID
	Set this to the ID which brclient should report when requesting and
	reporting seegments. This is typically your email address (as this is
	unique and helpful if I want to contact you!) but if you wish to remain
	anonymous, use a mailbox name from an anon remailer, or set it to some
	string (e.g. anon_12345) *WITHOUT* an "@" (so that I don't try to
	email it!)

BRNAME="Piete Brooks"		<< Replace Piete Brooks by YOUR ID
	This is a "friendly name" by which you would like to be known (some
	email addresses are random looking strings).

logkeys=$cmdnm.keys		<< Leave ASIS or set to another filename
	If set, a brief (cryptic !) log will be kept of which keys have been
	allocated and which have been scanned. If a system crashes, this file
	will indicate which have and have not been scanned.
	If the stats pags shows you as not having ACKed some segments, you can
	look at this log to see what happened. If you did scan them, you can
	re-ACK the segments. If not, you can REJECT them, or rescan them.
	*If you do not set it, the info will be sent to /dev/null (discarded)*
	If in doubt, mail the file to $author for help.
logscan=$cmdnm.scan		<< Leave ASIS or set to another filename
	If set, send scan info to a different file -- not normlly needed.

BRNUMBER=2			<< Replace as you want
	If set, specifies the number of segments to process at a time.
	The smaller the number, the less work is lost if something breaks,
	but the larger the load on the key doler server. If unset, it assumes
	a BRTIME of 1 hour.

BRTIME=0:30			<< Replace as you want
	If set and NRNUMBER is not, specifies the approximate time for which
	you want brtutessl to run. Note that if the result is 0, it is rounded
	up to 1. If you have a mix of machines, this is usually better than
	using BRNUMBER. DEfaults to an hour. The format is a number of hours,
	a colon and then a *TWO* digit number of minutes, e.g. 1:00 0:30 etc

SO: a sample $HOME/.$basename$c might be

tailored=true
BRID="pb@cl.cam.ac.uk"
BRNAME="Piete Brooks"
logkeys=\$HOME/$cmdnm.keys.\`uname -n\`
BRTIME=1:30

EOF
}

loops="${BRWORK_LOOPS-}"
while (test $# -gt 0)
do
	case "$1" in
	-a)	confproc || exit 1;;
	-C)	shift; conffile="$1"; confproc; exit 0;;
	-l)	shift; loops="$1"
		case "$loops" in
		[1-9]|[1-9][0-9]|[1-9][0-9][0-9]) : OK -- loops is $loops;;
		*)	echo "$0: invalid number of loops $loops" 1>&2;exit 1;;
		esac;;
	-c)	shift; conffile="$1";;
	-k)	shift; keys="$1";;
	-A)	shift; acki="$1";;
	-h)	helpproc; exit 0 ;;
	-i)	infoproc; exit 0 ;;
	-*)	echo "$0: unknown flag $1. See  $0 -h  for usage" 1>&2;exit 1;;
	*)	break;
	esac
	shift;
done


if	test ! -z "${conffile-}"
then	rcfile="$conffile"
elif	test ! -z "$BRWORKRC"
then	rcfile="$BRWORKRC"
elif	test -r $basename$rc
then	rcfile=$basename$rc
elif	test -r .$basename$rc
then	rcfile=.$basename$rc
elif	test -r $HOME/$basename$rc
then	rcfile=$basename$rc
elif	test -r $HOME/.$basename$rc
then	rcfile=.$basename$rc
elif	test -r $0.rc
then	rcfile=$0.rc
fi

norcfile() {
	cat <<EOF 1>&2

$0 was unable to find a configuration file ($basename$rc or .$basename$rc)
in the current directory, $HOME or /etc, nor was the environment variable
BRWORKRC set. As such it has been unable to tailor itself, so will be running
with "default" settings which may well be inappropriate. Try -h and -i.

EOF
}
# test1: BRNUMBER=3 type=test brwork	should stop
# test2: BRNUMBER=2 type=test brwork	should sleep
# test3: type=rc4ck checkcmds=no brloop	should do work
# :::::::::::::::::::::::::::::::::::::::::::::::::::::: TAILOR
#
# All of these tailorings apart from :1: can (I hope) be performed using the
# rcfile.  If not, please let me know ....
#

#:1: Add other locations to look for RC file here if you want.
case "$rcfile" in
?*)	if	test -r "$rcfile"
	then	. "$rcfile"
	else	(echo "";echo "	+++ Failed to read config file $rcfile";echo "") 1>&2
	fi;;
*)	norcfile;;
esac

#:2: An example scheme for terminating the loop -- if a file exists.
exitfile=${exitfile-exit-brute}
stopfile=${stopfile-stop-brute}
exitfile2=${exitfile2-$exitfile.$$}
stopfile2=${stopfile2-$stopfile.$$}
contfile2=${contfile2-$contfile.$$}

#:3: A command to get the seconds since 1970 -- maay need to set $perl
perl=${perl-perl}
case "$userdateproc" in
'') dateproc() { $perl -e 'print time'; } ;;
# or date -u "+%S %M 60 * + %H 3600 * + %j 86400 * + 788832000 + p" | dc
# modifiedd from suggestion by droelke@rdxsunhost.aud.alcatel.com
esac

#:4: Please include this string for log monitoring
BRCOMMENT="${BRCOMMENT-$vers p=$$}"

#:5: PLEASE set these two so that we can tie up all transactions from you.
# BRCLIENT="Piete Brooks"		# if needed
# BRID="pb@cl.cam.ac.uk"	# if needed

#:6: the required pathname for the "brclient" script
brclient=${brclient-brclient}

#:7: the *PREFIX* for the command to do the brute attack. The "type" will be
#:7: suffixed. e.g. to do an rc4 attempt, it will be ${brute}rc4
brute=${brute-brute}

#:8: the type of the brute atteck -- e.g. rc4, ssl, ...
type=${type-ssl}

export BRNUMBER BRCOMMENT BRCLIENT BRNAME BRID RUN_FROM_BRWORK BRWORKSTART

RUN_FROM_BRWORK=true

#:3: setting up how much work to do at once.
case "$BRNUMBER$keys$acki" in
'')	case "$type" in
	ssl)	case "${BRTIME-}" in
		[01][0-9]|[0-9]|[0-1][0-9]:[0-9][0-9]|[0-9]:[0-9][0-9]) : OK -- BRTIME $BRTIME;;
		'')	BRTIME=1:00;;
		*)	echo "	+++ Invalid BRTIME $BRTIME" 1>&2; exit 1;;
		esac
		val=`$brute$type -q -t $BRTIME`
		case $? in
		0)	: OK -- $brute$type -q -t $BRTIME gave $?;;
		*)	echo "	+++ $brute$type -w -t $BRTIME   gave  $val : $?" 1>&2;
			exit 2;;
		esac
		case "$val" in
		*" 0")	BRNUMBER=1;;
		*)	BRNUMBER=`echo $val | (read a b; echo $b)`;;
		esac
		case "$BRNUMBER" in
		[1-9]|[1-9][0-9])
			echo "#; Processing chunks of $BRNUMBER segments in about $BRTIME" 1>&2;;
		*)	echo "  +++ invalid BRNUMBER: $BRNUMBER" 1>&2;
			exit 1;;
		esac;;
	*)	BRNUMBER=4;;
	esac
esac

checkproj() {
	case "$BRPROJ" in
	[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]) export BRPROJ;;
	*)	echo "	++ FAILED to find a $type project" 1>&2
		exit 1;;
	esac
	echo "#; Working on project $BRPROJ of type $type" 1>&2
}

checkack() {
	case "$BRACKSERVERS" in
	'')	unset BRACKSERVERS;;
	*)	export BRACKSERVERS
		echo "#; using $BRACKSERVERS	for ACKing     keys" 1>&2;;
	esac
}

checkkey() {
	case "$BRKEYSERVERS" in
	'')	unset BRKEYSERVERS;;
	*)	export BRKEYSERVERS
		echo "#; using $BRKEYSERVERS	for allocating keys" 1>&2;;
	esac
}
checkdata() {
	case "$BRDATA" in
	'')	echo "	++ FAILED to find data for project $BRPROJ" 1>&2
		exit 1;;
	esac
	echo "#; using config data of:" 1>&2
	echo "$BRDATA" | sed 's/^/#;	/' 1>&2
}

case "$BRPROJ++$BRACKSERVERS++$BRACKSERVERS++$BRDATA" in
++++++)	raw=`BRNSERV=KEY $brclient -NACK -dTt$type`;
	BRPROJ=`echo "$raw" | (read a; echo $a | sed 's/:.*//')`
	BRACKSERVERS=`echo "$raw" | (read a; read a; echo $a)`
	BRKEYSERVERS=`echo "$raw" | (read a; read a; read a; echo $a)`
	BRDATA=`echo "$raw" | (read a; read a; read a; cat)`
	checkproj
	checkack
	checkkey
	checkdata
	;;
esac

case "$BRACKSERVERS++$BRACKSERVERS++$BRDATA" in
++++) 	raw=`BRNSERV=KEY $brclient -NACK -dP$BRPROJ`;
	BRACKSERVERS=`echo "$raw" | (read a; echo $a)`
	BRKEYSERVERS=`echo "$raw" | (read a; read a; echo $a)`
	BRDATA=`echo "$raw" | (read a; read a; cat)`
	checkack
	checkkey
	checkdata
	;;
esac
case "$BRPROJ" in
'')	BRPROJ=`$brclient -p | sed -n 's/\([^:]*\):'$type':.*/\1/p' | head -1`
	checkproj
esac
case "$BRACKSERVERS" in '') BRACKSERVERS=`$brclient -NACK -P$BRPROJ`; checkack;; esac
case "$BRKEYSERVERS" in '') BRKEYSERVERS=`$brclient -NKEY -P$BRPROJ`; checkkey;; esac
case "$BRDATA" in '') BRDATA=`$brclient -d`; checkdata;; esac

#:9: change this to avoid getting a warning message
tailored=${tailored-false} # ::::::: Set this to true when you tailor the script

#:10: Where to log info about keys tried and found
logkeys=${logkeys-/dev/null}
logscan=${logscan-$logkeys}

#:11: the actual command to do the work ....
case "$usercmd" in
defined): OK -- usercmd is $usercmd;;
*)	cmd () {
		case "$acki" in
		'') BRWORKSTART=`dateproc`
		    case "$keys" in
		    '')	res1=`$brclient -klLt$type`
			rc=$?;;
		    *)	res1="$keys"
			rc=0;;
		    esac
		    case $rc in
		    0)	: OK -- $brclient worked;;
		    6)	echo "exiting as told to stop"
			echo "exiting as told to stop" 1>&2
			exit 0;;
		    9)	echo "exiting as told to use a new server: $res1"
			echo "exiting as told to use a new server: $res1" 1>&2
			exit 0;;
		    *)	echo "$brclient -klLt$type failed $rc -- $res1"
		        echo "$brclient -klLt$type failed $rc -- $res1" 1>&2
			return $rc;;
		    esac
		    case "$res1" in
		    '') echo "$brclient -klLt$type failed will null result"
		        echo "$brclient -klLt$type failed will null result" 1>&2
		   	 return 1;;
		    esac
		    echo K:$$:$rc- $res1 `date` >> $logkeys
		    res2=`echo "$BRDATA" | nice -20 $brute$type - $res1`
		    rc=$?
		    case $rc in
		    0)	: OK -- $brute$type worked -- found the key ...
			echo K:$$:$rc: $res2 `date` >> $logkeys;;
		    1)	: OK? -- $brute$type MIGHT have worked -- no key ...
			echo K:$$:$rc: $res2 `date` >> $logkeys;;
		    *)	echo k:$$:$rc: $res2 `date` '!!!!!!!' >> $logkeys
			echo "nice -20 $brute$type - $res1 gave $rc - $res2"
			echo "nice -20 $brute$type - $res1 gave $rc - $res2" 1>&2
			return $rc;;
		    esac
		    case "$res2" in
		    '')	echo k:$$:-$rc: $res2 `date` '!!!!!!!' >> $logkeys
			return 1;;
		    esac;;
		*)  rc=0 res2="$acki";;
		esac
		echo S:$$:$rc- $res2 `date` >> $logscan
		echo '	'$res2 1>&2
		echo '	'$res2
		BRWORKSTART=`dateproc`
		$brclient -lLt$type -a "$res2"
		rc=$?
		case $rc in
		0)	echo S:$$:0: $res2 `date` >> $logscan;;
		*)	echo s:$$:$rc: $res2 `date` '!!!!!!!' >> $logscan
			return $rc;;
		esac
		return
	}
	;;
esac

#:12: Decide whether to exit, stop processing for a bit, or do some work
#:12: You may care to add some "number of active users" test, or some
#:12: "free CPU %" or "load average" checks. If so, note that as you may have
#:12: just run an attack, the machine may have been 100% busy foing that ..
#:12: If you declare your own testproc, set usertestproc=defined
case "$usertestproc" in
defined): OK ;;
*)	testproc () {
		test -r $contfile2 && return
		test "$stop" = "stop" -o -r $exitfile -o -r $exitfile2 && exit 0
		test ! -r $stopfile -a ! -r $stopfile2
	}
	trap 'stop=stop;' 2
	;;
esac
# :::::::::::::::::::::::::::::::::::::::::::::::::::::: TAILOR

# check that the programs can be found ....
case "$checkcmds$checkbrclient" in
'') if	$brclient -h 2>&1 | grep BRSERVERS > /dev/null
   then	: OK -- brclient is there
   else	(echo ""; echo " +++ Failed to find $brclient"; echo "") 1>&2; exit 1;
   fi
esac

case "$checkcmds$checkbrute" in
'') if	x=`($brclient -dLkt${type}ck | (read a; $brute$type - $a)) 2>&1`
   then	: OK -- $brute$type works
   else	(echo ""; echo " +++ Command failed -- $brute$type missing ?"; echo "$x"; echo "") 1>&2; exit 1;
   fi
esac

case "${BRID-}++${BRCLIENT-${BRNAME-}}" in
++*|*++)	echo ""; echo Your searches will be attributed to:
		$brclient -HV 2>&1 | sed -n 's/.*HELO 1 /	/p'
		echo Abort now if you want to tailor $0 ...
		sleep 5 || exit 1
		echo OK -- using it ....; echo ""
		;;
?*++?*)		echo "#; using id of ${BRCLIENT-${BRNAME-}} <$BRID>" 1>&2;;
esac

stop=go
sleeptime=0

case "$tailored" in
false)	(echo "";echo " +++ $0 running untailored -- non optimal";echo "") 1>&2
esac

case "$keys$acki" in ?*) cmd; exit;; esac

x=`echo X; testproc && echo Y; echo Z`

case "$x" in
X*Y*Z*)	: OK -- testproc says proceed;;
X*Z*)	echo "	++ WARNING: $0 is likely to sleep immediately !! ($stopfile exists ?)" 1>&2;;
X*)	echo "	++ WARNING: $0 is likely to exit immediately !! ($exitfile exists ?)" 1>&2;;
*)	echo "	++ ?? not sure what testproc will do ($x) !!" 1>&2;;
esac

echo "#; Starting the work on `date -u`" 1>&2

while	true
do	if testproc && cmd
	then	sleeptime=0
		case "$loops" in
		[1-9]*)	loops=`expr $loops - 1`;
			case "$loops" in 0|'')	break;; esac;;
		?*)	echo " ++ exiting as loops set to $loops" 2>&1;
			false; break;;
		esac
	else	rc=$?
		sleeptime=`expr $sleeptime + 60` || sleeptime=120
		case $sleeptime in [4-9]??|????*) sleeptime=300;; esac
		case "$stop" in
		go)	echo ... sleep $sleeptime as rc was $rc ... 2>&1
			sleep $sleeptime;;
		esac
	fi
done
