#!/bin/sh
# ~jhs/bin/.sh/gbde2.sh
# Copyright Julian H. Stacey jhs@@berklix.com	Free for public use.
# http://www.berklix.com/~jhs/bin/.sh/gbde2.sh
# See Also gbde.sh & gbde3.sh
# Installed to: /site/usr/local/bin/gbde2.sh by
#	cd ~/bin/.sh ; vi gbde2.sh ; xs make install
# Caution:
#	Do not format columns looking within exmh cos exmh uses
#	fonts that take unequal widhs etc. so first forward mail of output
#	& examine in an xterm.
# See Also (pre reboot):
#	/etc/rc.shutint < ~jhs/bin/.sh/rc.shutint
#	~/bin/.sh/gbde_build.sh
#	Background Reading:
#		man gbde
#		man geli (not that this script uses that, but it's another
#			disk encrypter some people may prefer)A
#		http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/disks-encrypting.html
#		http://wiki.freebsd.org/PEFS /usr/ports/sysutils/pefs-kmod
# Caution Before Changing: Check This script works on
#	All these hosts: laps, loft, lapr, laph.
#	Check:	config -x /boot/kernel/kernel | grep GEOM_BDE

# JJLATER add a trap, because adding brackets around gbde.sh in
#	/site/domain/js.berklix.net/host/lapr/etc/rc.local does not catch
#	a Control C (if I cant be bothered entering a password) & then the
#	rest of rc.local does not resume after gbde.sh.

if [ `whoami` = root ]; then
	true
else
	echo "$0: You must be root to run this."
	exit 1
fi

CFS=/crypt/fs
KEY_OWNER="jhs"	# Laptop owner where FS decrypt keys are.
echo "Listing any UFS already mounted on $CFS:"
mount -t ufs | grep $CFS

cd /crypt/image/gbde
echo "Listing any crypted file system image files in `/bin/pwd`:"
ls -l *
	# Not just ls as I dont want first summary line.
	# Might be symbolic links to an FS with space, eg:
	#	1700m@ -> ../../../s2crypt/1700m
	#	  99m@ -> ../../../s2crypt/99m

# Get $domain, outside 'for' loop so it is called just once.
xx=`hostname -s` ; domain=`hostname|sed -e "s/$xx.//"` ; unset xx

for i in *
	do	#{
	echo	# Blank line before each file system report.
	echo "About to vnode `/bin/pwd`/$i to mount on $CFS/$i"

	printf "Checking if already mounted:\\t\\t\\t\\t$CFS/$i\n"
	mount -t ufs | grep -q "on $CFS/$i (ufs, "
	# brackets.c: )
	#	/dev/md1.bde on $CFS/99m (ufs, local)
	#	......................... (ufs, NFS exported, local)
	case $? in
		# bracketc.c: (
		0)
			printf "Already mounted, so skipping:\\t$CFS/$i\n"
			# I would like to simply skip this iteration of $i
			# in the for loop & roll on to next $i in 'for' loop,
			# but not sure of syntax for that,
			# JJLATER 'continue' maybe ?
			# Anyway it's likely either none or
			# all are mounted, so for now, exit rather than skip.
			echo Exiting
			exit
			;;
		# bracketc.c: (
		*)
			printf "Not already mounted:\\t\\t\\t\\t$CFS/$i\n"
			;;
		esac
	printf "Seeing if vnode pre- open for:\\t\\t\\t\\t$CFS/$i\n"
	printf "Calling:\\t\\t\\t\\tmdconfig -l -v | grep $CFS/$i\n"
	mdconfig -l -v | grep $CFS/$i
		# "md0	vnode 1.5G /usr/crypt/image/gbde/1700m"
		# Note /crypt -> /usr/crypt, so the above grep works,
		# but it would not if eg /crypt -> /usr5/secret/
	case $? in
		# bracketc.c: (
		1)
			printf "Vnode not already open for:\\t\\t\\t\\t$CFS/$i\n"
			;;
		# bracketc.c: (
		0)
			echo "Vnode already open for $CFS/$i"
			echo Aborting
			exit 1
			;;
		esac

	printf "About to:\\t\\t\\t\\tmdconfig -a -t vnode -f $i\n"
	MD=`mdconfig -a -t vnode -f $i`
		# MD typical first value: "md0"
		# JJLATER ideally add if result processing else exit 1.
		# Not a big deal though, always seems to work.
	UNIT=`echo $MD | sed -e s/md//`

	printf "Vnode will be:\\t\\t\\t\\t/dev/$MD.bde\n"
	#	Announce above, so you know which pass phrase is
	#	appropriate, & what to hand mount if something breaks.

	# Help human by specifying which order to give multiple passwords.
	printf "Type passwords one per line in order, for: `cd /crypt/fs;echo [0-9]*`.\n"
		# /dev/md0.bde	/crypt/fs/1700m
		# /dev/md1.bde	/crypt/fs/99m

	# Find password for gbde FS.
	# Inside for loop allow different password per CFS.

	# If this is called by root, then HOME = / ,
	# but passwords not there, but here:
	GBDE_OWNER_SIZE=gbde/${KEY_OWNER}.$i

	echo "Seeing if any media is mounted"
	mount | grep /media

	PASSPATH=/home/${KEY_OWNER}/.DOTS/.passwd/${GBDE_OWNER_SIZE}
	# This path may reach across an AMD + NFS to another host.
	# echo "PASSPATH=${PASSPATH}"

	if [ "$domain" = "no.berklix.net" ]; then	# {
		ZONK=/media/sata1t/data/backup/${KEY_OWNER}_unencrypted/lower/.DOTS/.passwd/${GBDE_OWNER_SIZE}
		# This disk fails to fsck with sector errors, but leave code in for later.
		echo "Considering ${ZONK}"
		test -f ${ZONK} && PASSPATH=${ZONK}
		echo "PASSPATH=${PASSPATH}"

		ZONK=/media/sanblack.s2/backup/jhs/._dir/.DOTS/.passwd/${GBDE_OWNER_SIZE}
		echo "Considering ${ZONK}"
		test -f ${ZONK} && PASSPATH=${ZONK}
		echo "PASSPATH=${PASSPATH}"

		# Try the internal path last, cos after 1700m has mounted
		# using a password from either external media or keyboard,
		# the new 1700m FS gets mounted & can supply a password for 99m
		# without keyboard (unless I have removed the 99m password
		# from within my 1700m FS for supe paranoid customers).
		ZONK=/crypt/fs/1700m/home/jhs/._dir/txt/pri/passwd/${GBDE_OWNER_SIZE}
		echo "Considering ${ZONK}"
		test -f ${ZONK} && PASSPATH=${ZONK}
		echo "PASSPATH=${PASSPATH}"
	fi						# }

	printf "Password path:\\t\\t\\t\\t $PASSPATH\n"

	# At boot, I have checked: amd & nfs are already running,
	# before /etc/rc.local calls gbde.sh,
	# which allows passwords to be accessed from master host via local net,
	# That can be proven by uncommenting:
	#	wc -c $PASSPATH
	# Password is Not displayed on screen in case of alien eyes.
	# Ideally path & size might also best be avoided.

	if test -r $PASSPATH ; then	#{
		PASS=`cat $PASSPATH`
		PASSWORD="-p $PASS"
		printf "Password readable in:\\t\\t\\t\\t $PASSPATH\n"
			# EG from another already running AMD host.
		if [ "$domain" = "js.berklix.net" ]; then	# {
			# true	# Do nothing.
		else	# default }{
			P1="Caution: Possible Insecurity,"
			P2="Password file exists, As it does exist, using it: "
			P3="If path is on a previously decrypted FS, ignore this caution."
			# eg /crypt/fs/1700m/home/jhs/._dir/txt/pri/passwd/gbde/jhs.99m
			PASSLEAK="${P1} ${P2} $PASSPATH ${P3}"
			# A password file might get installed by a previous
			# rdist6 ccidentally burrowing underneath an amd
			# mount point ?
			echo "$PASSLEAK"
			echo "$PASSLEAK" | mail -s \
				"`hostname -s` $0 Warning" root
		fi	#}
	else							#}{
		echo "No pass phrase file: $PASSPATH"
		echo "so you will need to enter password yourself."
		PASSWORD=""
	fi							#}

	# My laptop ask for passwd for first 1700m FS, then as that
	# encrypted FS gets mounted & contains passwd for next 99m FS,
	# it does not ask again.
	# That is why I called it 99m, as if I had called it 100m,
	# 100m would alphabeticaly occur before 1700m in for loop.

	# echo "(If you get password wrong, it will not tell you.)"

	# man gbde does not seem to offer anything to see if eg
	# any /dev/md*.bde is associated with eg /crypt/fs/1700m
	ATTACHCMD="gbde attach /dev/$MD"
	printf "Calling:\\t\\t\\t\\t$ATTACHCMD\n"
	#	Above do not echo $PASSWORD

	# ATTACHRSLT=`$ATTACHCMD $PASSWORD`
	#	Fails to detect bad password:
	# if [ "X$ATTACHRSLT" = "X" ]; then
	#	....
	# if [ `$ATTACHCMD $PASSWORD` ]; then
	#	Fails to detect bad password:

	$ATTACHCMD $PASSWORD
	if [ $? ] ; then
		printf "Succeeded:\\t\\t\\t\\t$ATTACHCMD.\n"
	else
		printf "Failed:\\t\\t\\t\\t$ATTACHCMD\n"
		printf "So calling:\\tmdconfig -d -u $UNIT\n"
		mdconfig -d -u $UNIT
		exit 1
	fi

	# Test if gbde attach succeeded
	if [ -c /dev/$MD.bde ] ; then	#{
		printf "Created:\\t\\t\\t\\t/dev/$MD.bde\n"
		printf "For:\\t\\t\\t\\t$CFS/$i.\n"
		# ls -l /dev/$MD.bde
	else				#}{
		printf "Failed creating:\\t\\t\\t/dev/$MD.bde\n"
		echo "for	$CFS/$i."
		printf "So calling:\\t\\t\\t\\tmdconfig -d -u $UNIT\n"
		mdconfig -d -u $UNIT
		echo -n "If you get several of these failures ending"
		echo    " here: At holz, it is usually from"
		echo -n "laptop rebooting with no ethernet or with domain"
		echo    " no.berklix.net and"
		echo    "/etc/rc.local passing no stdin to gdbe."
		echo -n "Mails later get passed on "
		echo    "when laptop returns to home network."
		sleep 4	# In case called from rc.local, pause for human read.
		# While travelling just run gdbe.sh manually as root.
		echo Aborting
		exit 1
	fi				#}

	PASSWORD=""	# Forget the secret, but dont do it before
			# 'if' test, else it breaks the test.


	# The following test on fsck wrongly return a success value,
	# even when it should fail:
	#	fsck_ufs /dev/md2a
	#	if [ "X.$?" = "X.0" ] ; then		#{
	#		echo "Fsck returned zero: Success"
	#	else					#}{
	#		echo "Fsck returned not zero: Failed"
	#	fi					#}
	# But it is:
	# - Not the syntax failing,
	# The failure is that
	# - fsck fails to pass meaningful result, & always passes Zero back.
	# Proven by
	# - using instead of fsck, eg tail -f /etc/motd.impossible
	# ----
	# The following test on fsck wrongly return a success value,
	# even when it should fail:
	#	fsck_ufs /dev/md2a
	#	# Sample taken from results used in in rc.d/fsck
	#	case $? in
	#		# brackets.c (
	#	0)
	#		echo "Succeeded xx $? xx"
	#		;;
	#		# brackets.c (
	#	*)
	#		echo "Failed xx $? xx"
	#		;;
	#	esac
	# ----
	# The following test on fsck wrongly return a success value,
	# even when it should fail:
	#	FSCKCMD="fsck_ffs -p /dev/md2a"
	#	echo "Calling 8:	$FSCKCMD"
	#	# FSCKRSLT=`$FSCKCMD`
	#	# $FSCKCMD
	#	fsck_ffs /dev/md2a
	#	case $? in
	#		# brackets.c : (
	#	0)
	#		echo"Succeeded xx $? xx $FSCKRSLT xx"
	#		;;
	#		# brackets.c : (
	#	*)
	#		echo "Failed xx $? xx $FSCKRSLT xx"
	#		;;
	#	esac
	# ----
	# So do it an evil way, looking at text output:
	# FSCKCMD="fsck_ffs -p /dev/$MD.bde"
	# JJLATER add a preen test to make it faster.
	# FSCKCMD="fsck_ffs -p /dev/$MD.bde"
	#	2010.03.07 I changed -y to -p to avoid laptop boot delay.
	# Typically with -p, but not with -y:
		# /dev/md0.bde: FILE SYSTEM CLEAN; SKIPPING CHECKS
		# /dev/md0.bde: clean, 303796 free (13932 frags, \
		#	36233 blocks, 1.9% fragmentation)

	FSCKCMD="fsck_ffs -y /dev/$MD.bde"
	# 2012-06-25 reverted from -p to -y to do a full clean,
	# in case FS gets corrupt,
	# cos currently a PROBLEM: 2 laptops are crashing after mounting
	# a gbde'd USB stick, this got me to realising I should -
	# somewhere - add an automatic unmount of gbde'd FS's that are based
	# on images on other UFS mounted FS's,
	# Before the sys gets a halt on those other mounted FSs.

	printf "Calling:\\t\\t\\t\\t$FSCKCMD\n"
	FSCKRSLT=`$FSCKCMD`
	# echo "$FSCKRSLT" | grep -q "FILE SYSTEM CLEAN"

	case $? in	# $?: Ref: man sh: "Special Parameters"
		# bracketc.c: (
		0)
			printf "Succeeded:\\t\\t\\t\\t$FSCKCMD\n"
			;;
		# bracketc.c: (
		*)
			printf "Failed:\\t\\t\\t\\t\\t$FSCKCMD\n"
			printf "So calling:\\t\\t\\tgbde detach /dev/$MD\n"
			gbde detach /dev/$MD
			printf "& calling:\\t\\t\\tmdconfig -d -u $UNIT\n"
			mdconfig -d -u $UNIT
			echo Aborting
			exit 1
			;;
		esac

	mkdir -p $CFS/$i	# Ensure mount point exists.

	MOUNTCMD="mount /dev/$MD.bde $CFS/$i"
	printf "Calling:\\t\\t\\t\\t$MOUNTCMD\n"
	MOUNTRSLT=`$MOUNTCMD`
	case $? in	# $?: Ref: man sh: "Special Parameters"
		# bracketc.c: (
		0)
			printf "Succeeded:\\t\\t\\t\\t$MOUNTCMD\n"
			;;
		# bracketc.c: (
		*)
			printf "Failed:\\t\\t\\t\\t\\t$MOUNTCMD\n"
			printf "So calling:\\t\\t\\tgbde detach /dev/$MD\n"
			gbde detach /dev/$MD
			printf "& calling:\\t\\t\\tmdconfig -d -u $UNIT\n"
			mdconfig -d -u $UNIT
			echo Aborting
			exit 1
			;;
		esac

	done	#}
echo	# Blank line after last file system report.

echo "UFS mounts on $CFS:"
mount -t ufs | grep $CFS
	# The "-t nfs" usefuly stops this being listed:
	#	fire:$CFS/99m on
	#	/.amd_mnt/fire$CFS/99m (nfs)
echo "To unmount them: /etc/rc.shutint"

# There is currently nothing that on halt does:
#	umount		, presumably not needed.
#	gbde detach	, possibly needed ?
# See also /home/jhs/bin/.sh/umountusb
